diff --git a/assets/icons/icon/clipboard-copied.svg b/assets/icons/icon/clipboard-copied.svg new file mode 100644 index 000000000..1860e1516 --- /dev/null +++ b/assets/icons/icon/clipboard-copied.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/assets/imports.d.ts b/packages/assets/imports.d.ts index ca1c79da7..75c5d0dfa 100644 --- a/packages/assets/imports.d.ts +++ b/packages/assets/imports.d.ts @@ -50,6 +50,7 @@ export function getAssetUrlsByImport(opts?: AssetUrlOptions): { 'chevron-up': string 'chevrons-ne': string 'chevrons-sw': string + 'clipboard-copied': string 'clipboard-copy': string code: string collab: string diff --git a/packages/assets/imports.js b/packages/assets/imports.js index 907ad4003..251df116c 100644 --- a/packages/assets/imports.js +++ b/packages/assets/imports.js @@ -61,6 +61,7 @@ import iconsChevronRight from './icons/icon/chevron-right.svg' import iconsChevronUp from './icons/icon/chevron-up.svg' import iconsChevronsNe from './icons/icon/chevrons-ne.svg' import iconsChevronsSw from './icons/icon/chevrons-sw.svg' +import iconsClipboardCopied from './icons/icon/clipboard-copied.svg' import iconsClipboardCopy from './icons/icon/clipboard-copy.svg' import iconsCode from './icons/icon/code.svg' import iconsCollab from './icons/icon/collab.svg' @@ -287,6 +288,7 @@ export function getAssetUrlsByImport(opts) { 'chevron-up': formatAssetUrl(iconsChevronUp, opts), 'chevrons-ne': formatAssetUrl(iconsChevronsNe, opts), 'chevrons-sw': formatAssetUrl(iconsChevronsSw, opts), + 'clipboard-copied': formatAssetUrl(iconsClipboardCopied, opts), 'clipboard-copy': formatAssetUrl(iconsClipboardCopy, opts), code: formatAssetUrl(iconsCode, opts), collab: formatAssetUrl(iconsCollab, opts), diff --git a/packages/assets/urls.d.ts b/packages/assets/urls.d.ts index efebf8ced..1db9cb6a4 100644 --- a/packages/assets/urls.d.ts +++ b/packages/assets/urls.d.ts @@ -50,6 +50,7 @@ export function getAssetUrlsByMetaUrl(opts?: AssetUrlOptions): { 'chevron-up': string 'chevrons-ne': string 'chevrons-sw': string + 'clipboard-copied': string 'clipboard-copy': string code: string collab: string diff --git a/packages/assets/urls.js b/packages/assets/urls.js index 1d8016d4b..947ac2005 100644 --- a/packages/assets/urls.js +++ b/packages/assets/urls.js @@ -193,6 +193,10 @@ export function getAssetUrlsByMetaUrl(opts) { new URL('./icons/icon/chevrons-sw.svg', import.meta.url).href, opts ), + 'clipboard-copied': formatAssetUrl( + new URL('./icons/icon/clipboard-copied.svg', import.meta.url).href, + opts + ), 'clipboard-copy': formatAssetUrl( new URL('./icons/icon/clipboard-copy.svg', import.meta.url).href, opts diff --git a/packages/ui/api-report.md b/packages/ui/api-report.md index 0fc7ade29..85c9e3685 100644 --- a/packages/ui/api-report.md +++ b/packages/ui/api-report.md @@ -551,10 +551,9 @@ export const NavigationZone: NamedExoticComponent; function RadioItem({ children, onSelect, ...rest }: DropdownMenuCheckboxItemProps): JSX.Element; // @public (undocumented) -function Root({ id, open, children, modal, }: { +function Root({ id, children, modal, }: { id: string; children: any; - open?: boolean; modal?: boolean; }): JSX.Element; @@ -584,10 +583,9 @@ export interface SliderProps { export const StylePanel: ({ isMobile }: StylePanelProps) => JSX.Element | null; // @public (undocumented) -function Sub({ id, children, open }: { +function Sub({ id, children }: { id: string; children: any; - open?: boolean; }): JSX.Element; // @public (undocumented) @@ -743,10 +741,10 @@ export type TLUiEventHandler export type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'toolbar' | 'unknown' | 'zoom-menu'; // @public (undocumented) -export type TLUiIconType = 'align-bottom-center' | 'align-bottom-left' | 'align-bottom-right' | 'align-bottom' | 'align-center-center' | 'align-center-horizontal' | 'align-center-left' | 'align-center-right' | 'align-center-vertical' | 'align-left' | 'align-right' | 'align-top-center' | 'align-top-left' | 'align-top-right' | 'align-top' | 'arrow-left' | 'arrowhead-arrow' | 'arrowhead-bar' | 'arrowhead-diamond' | 'arrowhead-dot' | 'arrowhead-none' | 'arrowhead-square' | 'arrowhead-triangle-inverted' | 'arrowhead-triangle' | 'aspect-ratio' | 'avatar' | 'blob' | 'bring-forward' | 'bring-to-front' | 'check' | 'checkbox-checked' | 'checkbox-empty' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' | 'clipboard-copy' | 'code' | 'collab' | 'color' | 'comment' | 'cross-2' | 'cross' | 'dash-dashed' | 'dash-dotted' | 'dash-draw' | 'dash-solid' | 'discord' | 'distribute-horizontal' | 'distribute-vertical' | 'dot' | 'dots-horizontal' | 'dots-vertical' | 'drag-handle-dots' | 'duplicate' | 'edit' | 'external-link' | 'file' | 'fill-none' | 'fill-pattern' | 'fill-semi' | 'fill-solid' | 'follow' | 'following' | 'font-draw' | 'font-mono' | 'font-sans' | 'font-serif' | 'geo-arrow-down' | 'geo-arrow-left' | 'geo-arrow-right' | 'geo-arrow-up' | 'geo-check-box' | 'geo-diamond' | 'geo-ellipse' | 'geo-hexagon' | 'geo-octagon' | 'geo-oval' | 'geo-pentagon' | 'geo-rectangle' | 'geo-rhombus-2' | 'geo-rhombus' | 'geo-star' | 'geo-trapezoid' | 'geo-triangle' | 'geo-x-box' | 'github' | 'group' | 'hidden' | 'image' | 'info-circle' | 'leading' | 'link' | 'lock-small' | 'lock' | 'menu' | 'minus' | 'mixed' | 'pack' | 'page' | 'plus' | 'question-mark-circle' | 'question-mark' | 'redo' | 'reset-zoom' | 'rotate-ccw' | 'rotate-cw' | 'ruler' | 'search' | 'send-backward' | 'send-to-back' | 'settings-horizontal' | 'settings-vertical-1' | 'settings-vertical' | 'share-1' | 'share-2' | 'size-extra-large' | 'size-large' | 'size-medium' | 'size-small' | 'spline-cubic' | 'spline-line' | 'stack-horizontal' | 'stack-vertical' | 'stretch-horizontal' | 'stretch-vertical' | 'text-align-center' | 'text-align-justify' | 'text-align-left' | 'text-align-right' | 'tool-arrow' | 'tool-embed' | 'tool-eraser' | 'tool-frame' | 'tool-hand' | 'tool-highlighter' | 'tool-line' | 'tool-media' | 'tool-note' | 'tool-pencil' | 'tool-pointer' | 'tool-text' | 'trash' | 'triangle-down' | 'triangle-up' | 'twitter' | 'undo' | 'ungroup' | 'unlock-small' | 'unlock' | 'visible' | 'warning-triangle' | 'zoom-in' | 'zoom-out'; +export type TLUiIconType = 'align-bottom-center' | 'align-bottom-left' | 'align-bottom-right' | 'align-bottom' | 'align-center-center' | 'align-center-horizontal' | 'align-center-left' | 'align-center-right' | 'align-center-vertical' | 'align-left' | 'align-right' | 'align-top-center' | 'align-top-left' | 'align-top-right' | 'align-top' | 'arrow-left' | 'arrowhead-arrow' | 'arrowhead-bar' | 'arrowhead-diamond' | 'arrowhead-dot' | 'arrowhead-none' | 'arrowhead-square' | 'arrowhead-triangle-inverted' | 'arrowhead-triangle' | 'aspect-ratio' | 'avatar' | 'blob' | 'bring-forward' | 'bring-to-front' | 'check' | 'checkbox-checked' | 'checkbox-empty' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' | 'clipboard-copied' | 'clipboard-copy' | 'code' | 'collab' | 'color' | 'comment' | 'cross-2' | 'cross' | 'dash-dashed' | 'dash-dotted' | 'dash-draw' | 'dash-solid' | 'discord' | 'distribute-horizontal' | 'distribute-vertical' | 'dot' | 'dots-horizontal' | 'dots-vertical' | 'drag-handle-dots' | 'duplicate' | 'edit' | 'external-link' | 'file' | 'fill-none' | 'fill-pattern' | 'fill-semi' | 'fill-solid' | 'follow' | 'following' | 'font-draw' | 'font-mono' | 'font-sans' | 'font-serif' | 'geo-arrow-down' | 'geo-arrow-left' | 'geo-arrow-right' | 'geo-arrow-up' | 'geo-check-box' | 'geo-diamond' | 'geo-ellipse' | 'geo-hexagon' | 'geo-octagon' | 'geo-oval' | 'geo-pentagon' | 'geo-rectangle' | 'geo-rhombus-2' | 'geo-rhombus' | 'geo-star' | 'geo-trapezoid' | 'geo-triangle' | 'geo-x-box' | 'github' | 'group' | 'hidden' | 'image' | 'info-circle' | 'leading' | 'link' | 'lock-small' | 'lock' | 'menu' | 'minus' | 'mixed' | 'pack' | 'page' | 'plus' | 'question-mark-circle' | 'question-mark' | 'redo' | 'reset-zoom' | 'rotate-ccw' | 'rotate-cw' | 'ruler' | 'search' | 'send-backward' | 'send-to-back' | 'settings-horizontal' | 'settings-vertical-1' | 'settings-vertical' | 'share-1' | 'share-2' | 'size-extra-large' | 'size-large' | 'size-medium' | 'size-small' | 'spline-cubic' | 'spline-line' | 'stack-horizontal' | 'stack-vertical' | 'stretch-horizontal' | 'stretch-vertical' | 'text-align-center' | 'text-align-justify' | 'text-align-left' | 'text-align-right' | 'tool-arrow' | 'tool-embed' | 'tool-eraser' | 'tool-frame' | 'tool-hand' | 'tool-highlighter' | 'tool-line' | 'tool-media' | 'tool-note' | 'tool-pencil' | 'tool-pointer' | 'tool-text' | 'trash' | 'triangle-down' | 'triangle-up' | 'twitter' | 'undo' | 'ungroup' | 'unlock-small' | 'unlock' | 'visible' | 'warning-triangle' | 'zoom-in' | 'zoom-out'; // @public (undocumented) -export const TLUiIconTypes: readonly ["align-bottom-center", "align-bottom-left", "align-bottom-right", "align-bottom", "align-center-center", "align-center-horizontal", "align-center-left", "align-center-right", "align-center-vertical", "align-left", "align-right", "align-top-center", "align-top-left", "align-top-right", "align-top", "arrow-left", "arrowhead-arrow", "arrowhead-bar", "arrowhead-diamond", "arrowhead-dot", "arrowhead-none", "arrowhead-square", "arrowhead-triangle-inverted", "arrowhead-triangle", "aspect-ratio", "avatar", "blob", "bring-forward", "bring-to-front", "check", "checkbox-checked", "checkbox-empty", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-ne", "chevrons-sw", "clipboard-copy", "code", "collab", "color", "comment", "cross-2", "cross", "dash-dashed", "dash-dotted", "dash-draw", "dash-solid", "discord", "distribute-horizontal", "distribute-vertical", "dot", "dots-horizontal", "dots-vertical", "drag-handle-dots", "duplicate", "edit", "external-link", "file", "fill-none", "fill-pattern", "fill-semi", "fill-solid", "follow", "following", "font-draw", "font-mono", "font-sans", "font-serif", "geo-arrow-down", "geo-arrow-left", "geo-arrow-right", "geo-arrow-up", "geo-check-box", "geo-diamond", "geo-ellipse", "geo-hexagon", "geo-octagon", "geo-oval", "geo-pentagon", "geo-rectangle", "geo-rhombus-2", "geo-rhombus", "geo-star", "geo-trapezoid", "geo-triangle", "geo-x-box", "github", "group", "hidden", "image", "info-circle", "leading", "link", "lock-small", "lock", "menu", "minus", "mixed", "pack", "page", "plus", "question-mark-circle", "question-mark", "redo", "reset-zoom", "rotate-ccw", "rotate-cw", "ruler", "search", "send-backward", "send-to-back", "settings-horizontal", "settings-vertical-1", "settings-vertical", "share-1", "share-2", "size-extra-large", "size-large", "size-medium", "size-small", "spline-cubic", "spline-line", "stack-horizontal", "stack-vertical", "stretch-horizontal", "stretch-vertical", "text-align-center", "text-align-justify", "text-align-left", "text-align-right", "tool-arrow", "tool-embed", "tool-eraser", "tool-frame", "tool-hand", "tool-highlighter", "tool-line", "tool-media", "tool-note", "tool-pencil", "tool-pointer", "tool-text", "trash", "triangle-down", "triangle-up", "twitter", "undo", "ungroup", "unlock-small", "unlock", "visible", "warning-triangle", "zoom-in", "zoom-out"]; +export const TLUiIconTypes: readonly ["align-bottom-center", "align-bottom-left", "align-bottom-right", "align-bottom", "align-center-center", "align-center-horizontal", "align-center-left", "align-center-right", "align-center-vertical", "align-left", "align-right", "align-top-center", "align-top-left", "align-top-right", "align-top", "arrow-left", "arrowhead-arrow", "arrowhead-bar", "arrowhead-diamond", "arrowhead-dot", "arrowhead-none", "arrowhead-square", "arrowhead-triangle-inverted", "arrowhead-triangle", "aspect-ratio", "avatar", "blob", "bring-forward", "bring-to-front", "check", "checkbox-checked", "checkbox-empty", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-ne", "chevrons-sw", "clipboard-copied", "clipboard-copy", "code", "collab", "color", "comment", "cross-2", "cross", "dash-dashed", "dash-dotted", "dash-draw", "dash-solid", "discord", "distribute-horizontal", "distribute-vertical", "dot", "dots-horizontal", "dots-vertical", "drag-handle-dots", "duplicate", "edit", "external-link", "file", "fill-none", "fill-pattern", "fill-semi", "fill-solid", "follow", "following", "font-draw", "font-mono", "font-sans", "font-serif", "geo-arrow-down", "geo-arrow-left", "geo-arrow-right", "geo-arrow-up", "geo-check-box", "geo-diamond", "geo-ellipse", "geo-hexagon", "geo-octagon", "geo-oval", "geo-pentagon", "geo-rectangle", "geo-rhombus-2", "geo-rhombus", "geo-star", "geo-trapezoid", "geo-triangle", "geo-x-box", "github", "group", "hidden", "image", "info-circle", "leading", "link", "lock-small", "lock", "menu", "minus", "mixed", "pack", "page", "plus", "question-mark-circle", "question-mark", "redo", "reset-zoom", "rotate-ccw", "rotate-cw", "ruler", "search", "send-backward", "send-to-back", "settings-horizontal", "settings-vertical-1", "settings-vertical", "share-1", "share-2", "size-extra-large", "size-large", "size-medium", "size-small", "spline-cubic", "spline-line", "stack-horizontal", "stack-vertical", "stretch-horizontal", "stretch-vertical", "text-align-center", "text-align-justify", "text-align-left", "text-align-right", "tool-arrow", "tool-embed", "tool-eraser", "tool-frame", "tool-hand", "tool-highlighter", "tool-line", "tool-media", "tool-note", "tool-pencil", "tool-pointer", "tool-text", "trash", "triangle-down", "triangle-up", "twitter", "undo", "ungroup", "unlock-small", "unlock", "visible", "warning-triangle", "zoom-in", "zoom-out"]; // @public (undocumented) export const ToastsContext: Context; @@ -1043,7 +1041,7 @@ export function useMenuClipboardEvents(source: TLUiEventSource): { }; // @public (undocumented) -export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void): (isOpen: boolean) => void; +export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void): readonly [boolean, (isOpen: boolean) => void]; // @public (undocumented) export function useMenuSchema(): MenuSchema; diff --git a/packages/ui/src/lib/components/ContextMenu.tsx b/packages/ui/src/lib/components/ContextMenu.tsx index fe9c11957..0b2dd0722 100644 --- a/packages/ui/src/lib/components/ContextMenu.tsx +++ b/packages/ui/src/lib/components/ContextMenu.tsx @@ -25,7 +25,7 @@ export const ContextMenu = function ContextMenu({ children }: { children: any }) const app = useApp() const contextMenuSchema = useContextMenuSchema() - const handleOpenChange = useMenuIsOpen('context menu') + const [_, handleOpenChange] = useMenuIsOpen('context menu') // If every item in the menu is readonly, then we don't want to show the menu const isReadonly = useReadonly() @@ -58,7 +58,7 @@ function ContextMenuContent() { const app = useApp() const msg = useTranslation() const menuSchema = useContextMenuSchema() - const handleSubOpenChange = useMenuIsOpen('context menu sub') + const [_, handleSubOpenChange] = useMenuIsOpen('context menu sub') const isReadonly = useReadonly() const { paste } = useMenuClipboardEvents('context-menu') diff --git a/packages/ui/src/lib/components/HelpMenu.tsx b/packages/ui/src/lib/components/HelpMenu.tsx index c06c38d62..41ee3e61f 100644 --- a/packages/ui/src/lib/components/HelpMenu.tsx +++ b/packages/ui/src/lib/components/HelpMenu.tsx @@ -27,11 +27,11 @@ export interface HelpMenuProps { export const HelpMenu = React.memo(function HelpMenu() { const container = useContainer() const msg = useTranslation() - const onOpenChange = useMenuIsOpen('help menu') + const [isOpen, onOpenChange] = useMenuIsOpen('help menu') return (
- + @@ -68,8 +66,8 @@ export function Content({ } /** @public */ -export function Sub({ id, children, open }: { id: string; children: any; open?: boolean }) { - const onOpenChange = useMenuIsOpen(id) +export function Sub({ id, children }: { id: string; children: any }) { + const [open, onOpenChange] = useMenuIsOpen(id) return ( diff --git a/packages/ui/src/lib/components/primitives/Popover.tsx b/packages/ui/src/lib/components/primitives/Popover.tsx index 91acee587..aa9a9e7cb 100644 --- a/packages/ui/src/lib/components/primitives/Popover.tsx +++ b/packages/ui/src/lib/components/primitives/Popover.tsx @@ -10,11 +10,11 @@ type PopoverProps = { onOpenChange?: (isOpen: boolean) => void } -export const Popover: FC = ({ id, open, children, onOpenChange }) => { - const handleOpenChange = useMenuIsOpen(id, onOpenChange) +export const Popover: FC = ({ id, children, onOpenChange }) => { + const [isOpen, handleOpenChange] = useMenuIsOpen(id, onOpenChange) return ( - +
{children}
) diff --git a/packages/ui/src/lib/hooks/useMenuIsOpen.ts b/packages/ui/src/lib/hooks/useMenuIsOpen.ts index 13d1c2599..608bb0e4b 100644 --- a/packages/ui/src/lib/hooks/useMenuIsOpen.ts +++ b/packages/ui/src/lib/hooks/useMenuIsOpen.ts @@ -1,5 +1,6 @@ import { useApp } from '@tldraw/editor' import { useCallback, useEffect, useRef } from 'react' +import { useValue } from 'signia-react' import { useEvents } from './useEventsProvider' /** @public */ @@ -62,5 +63,7 @@ export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void) { } }, [app, id, trackEvent]) - return onOpenChange + const isOpen = useValue('is menu open', () => app.openMenus.includes(id), [app, id]) + + return [isOpen, onOpenChange] as const } diff --git a/packages/ui/src/lib/icon-types.ts b/packages/ui/src/lib/icon-types.ts index e8e00e084..bef1bab9e 100644 --- a/packages/ui/src/lib/icon-types.ts +++ b/packages/ui/src/lib/icon-types.ts @@ -41,6 +41,7 @@ export type TLUiIconType = | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' + | 'clipboard-copied' | 'clipboard-copy' | 'code' | 'collab' @@ -200,6 +201,7 @@ export const TLUiIconTypes = [ 'chevron-up', 'chevrons-ne', 'chevrons-sw', + 'clipboard-copied', 'clipboard-copy', 'code', 'collab',