tldraw 3.16.0-canary.ba3bc37d4418 → 3.16.0-canary.bf7be9dad88b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +1 -0
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +3 -3
- package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/OverflowingToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js +6 -2
- package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js +3 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +7 -6
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-esm/index.d.mts +1 -0
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs +2 -2
- package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +3 -3
- package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs +1 -1
- package/dist-esm/lib/ui/components/Toolbar/OverflowingToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs +6 -2
- package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs +3 -1
- package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +7 -6
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/package.json +3 -3
- package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +3 -3
- package/src/lib/ui/components/Toolbar/LinkEditor.tsx +5 -5
- package/src/lib/ui/components/Toolbar/OverflowingToolbar.tsx +1 -1
- package/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx +9 -2
- package/src/lib/ui/components/primitives/TldrawUiInput.tsx +3 -0
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +7 -6
- package/src/lib/ui/version.ts +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/Toolbar/AltTextEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["import { preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport {
|
|
5
|
-
"mappings": "AAiEE,mBACC,KADD;AAjEF,SAAS,gBAAoC,iBAAiB;AAC9D,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AACzD,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,
|
|
4
|
+
"sourcesContent": ["import { preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface AltTextEditorProps {\n\tshapeId: TLShapeId\n\tonClose(): void\n\tsource: 'image-toolbar' | 'video-toolbar'\n}\n\n/** @public @react */\nexport function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps) {\n\tconst editor = useEditor()\n\tconst [altText, setAltText] = useState(() => {\n\t\tconst shape = editor.getShape<TLShape>(shapeId)\n\t\tif (!shape) return ''\n\t\tif (!('altText' in shape.props)) throw Error('Shape does not have altText property')\n\t\treturn shape.props.altText || ''\n\t})\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst isReadonly = editor.getIsReadonly()\n\n\tconst handleValueChange = (value: string) => setAltText(value)\n\n\tconst handleComplete = () => {\n\t\ttrackEvent('set-alt-text', { source })\n\t\tconst shape = editor.getShape<TLShape & { props: { altText: string } }>(shapeId)\n\t\tif (!shape) return\n\t\teditor.updateShapes([\n\t\t\t{\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tprops: { altText },\n\t\t\t},\n\t\t])\n\t\tonClose()\n\t}\n\n\tconst handleConfirm = () => handleComplete()\n\tconst handleAltTextCancel = useCallback(() => onClose(), [onClose])\n\n\tuseEffect(() => {\n\t\tref.current?.select()\n\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (event.key === 'Escape') {\n\t\t\t\tevent.stopPropagation()\n\t\t\t\thandleAltTextCancel()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown, { capture: true })\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown, { capture: true })\n\t\t}\n\t}, [handleAltTextCancel])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tclassName=\"tlui-media__toolbar-alt-text-input\"\n\t\t\t\tdata-testid=\"media-toolbar.alt-text-input\"\n\t\t\t\tvalue={altText}\n\t\t\t\tplaceholder={msg('tool.media-alt-text-desc')}\n\t\t\t\taria-label={msg('tool.media-alt-text-desc')}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleComplete}\n\t\t\t\tonCancel={handleAltTextCancel}\n\t\t\t\tdisabled={isReadonly}\n\t\t\t/>\n\t\t\t{!isReadonly && (\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttitle={msg('tool.media-alt-text-confirm')}\n\t\t\t\t\tdata-testid=\"tool.media-alt-text-confirm\"\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\t\tonClick={handleConfirm}\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiButtonIcon small icon=\"check\" />\n\t\t\t\t</TldrawUiButton>\n\t\t\t)}\n\t\t</>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AAiEE,mBACC,KADD;AAjEF,SAAS,gBAAoC,iBAAiB;AAC9D,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AACzD,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,qBAAqB;AAUvB,SAAS,cAAc,EAAE,SAAS,SAAS,OAAO,GAAuB;AAC/E,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,MAAM;AAC5C,UAAM,QAAQ,OAAO,SAAkB,OAAO;AAC9C,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,EAAE,aAAa,MAAM,OAAQ,OAAM,MAAM,sCAAsC;AACnF,WAAO,MAAM,MAAM,WAAW;AAAA,EAC/B,CAAC;AACD,QAAM,MAAM,eAAe;AAC3B,QAAM,MAAM,OAAyB,IAAI;AACzC,QAAM,aAAa,YAAY;AAC/B,QAAM,aAAa,OAAO,cAAc;AAExC,QAAM,oBAAoB,CAAC,UAAkB,WAAW,KAAK;AAE7D,QAAM,iBAAiB,MAAM;AAC5B,eAAW,gBAAgB,EAAE,OAAO,CAAC;AACrC,UAAM,QAAQ,OAAO,SAAmD,OAAO;AAC/E,QAAI,CAAC,MAAO;AACZ,WAAO,aAAa;AAAA,MACnB;AAAA,QACC,IAAI,MAAM;AAAA,QACV,MAAM,MAAM;AAAA,QACZ,OAAO,EAAE,QAAQ;AAAA,MAClB;AAAA,IACD,CAAC;AACD,YAAQ;AAAA,EACT;AAEA,QAAM,gBAAgB,MAAM,eAAe;AAC3C,QAAM,sBAAsB,YAAY,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC;AAElE,YAAU,MAAM;AACf,QAAI,SAAS,OAAO;AAEpB,aAAS,cAAc,OAAsB;AAC5C,UAAI,MAAM,QAAQ,UAAU;AAC3B,cAAM,gBAAgB;AACtB,4BAAoB;AAAA,MACrB;AAAA,IACD;AAEA,aAAS,iBAAiB,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AACrE,WAAO,MAAM;AACZ,eAAS,oBAAoB,WAAW,eAAe,EAAE,SAAS,KAAK,CAAC;AAAA,IACzE;AAAA,EACD,GAAG,CAAC,mBAAmB,CAAC;AAExB,SACC,iCACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,WAAU;AAAA,QACV,eAAY;AAAA,QACZ,OAAO;AAAA,QACP,aAAa,IAAI,0BAA0B;AAAA,QAC3C,cAAY,IAAI,0BAA0B;AAAA,QAC1C,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,UAAU;AAAA;AAAA,IACX;AAAA,IACC,CAAC,cACD;AAAA,MAAC;AAAA;AAAA,QACA,OAAO,IAAI,6BAA6B;AAAA,QACxC,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,8BAAC,sBAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KAEF;AAEF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,9 +3,9 @@ import { preventDefault, useEditor } from "@tldraw/editor";
|
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { useUiEvents } from "../../context/events.mjs";
|
|
5
5
|
import { useTranslation } from "../../hooks/useTranslation/useTranslation.mjs";
|
|
6
|
+
import { TldrawUiButton } from "../primitives/Button/TldrawUiButton.mjs";
|
|
6
7
|
import { TldrawUiButtonIcon } from "../primitives/Button/TldrawUiButtonIcon.mjs";
|
|
7
8
|
import { TldrawUiInput } from "../primitives/TldrawUiInput.mjs";
|
|
8
|
-
import { TldrawUiToolbarButton } from "../primitives/TldrawUiToolbar.mjs";
|
|
9
9
|
function LinkEditor({ textEditor, value: initialValue, onClose }) {
|
|
10
10
|
const editor = useEditor();
|
|
11
11
|
const [value, setValue] = useState(initialValue);
|
|
@@ -61,7 +61,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
|
|
|
61
61
|
}
|
|
62
62
|
),
|
|
63
63
|
/* @__PURE__ */ jsx(
|
|
64
|
-
|
|
64
|
+
TldrawUiButton,
|
|
65
65
|
{
|
|
66
66
|
className: "tlui-rich-text__toolbar-link-visit",
|
|
67
67
|
title: msg("tool.rich-text-link-visit"),
|
|
@@ -73,7 +73,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
|
|
|
73
73
|
}
|
|
74
74
|
),
|
|
75
75
|
/* @__PURE__ */ jsx(
|
|
76
|
-
|
|
76
|
+
TldrawUiButton,
|
|
77
77
|
{
|
|
78
78
|
className: "tlui-rich-text__toolbar-link-remove",
|
|
79
79
|
title: msg("tool.rich-text-link-remove"),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/Toolbar/LinkEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport {
|
|
5
|
-
"mappings": "AAmEE,mBACC,KADD;AAnEF,SAAS,gBAA8B,iBAAiB;AACxD,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,
|
|
4
|
+
"sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = (link: string) => {\n\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\tlink = `https://${link}`\n\t\t}\n\n\t\ttextEditor.commands.setLink({ href: link })\n\t\t// N.B. We shouldn't focus() on mobile because it causes the\n\t\t// Return key to replace the link with a newline :facepalm:\n\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\ttextEditor.commands.blur()\n\t\t} else {\n\t\t\ttextEditor.commands.focus()\n\t\t}\n\t\tonClose()\n\t}\n\n\tconst handleVisitLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-visit', source })\n\t\twindow.open(linkifiedValue, '_blank', 'noopener, noreferrer')\n\t\tonClose()\n\t}\n\n\tconst handleRemoveLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-remove', source })\n\t\ttextEditor.chain().unsetLink().focus().run()\n\t\tonClose()\n\t}\n\n\tconst handleLinkCancel = () => onClose()\n\n\tuseEffect(() => {\n\t\tref.current?.focus()\n\t}, [value])\n\n\tuseEffect(() => {\n\t\tsetValue(initialValue)\n\t}, [initialValue])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tdata-testid=\"rich-text.link-input\"\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-input\"\n\t\t\t\tvalue={value}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleLinkComplete}\n\t\t\t\tonCancel={handleLinkCancel}\n\t\t\t\tplaceholder=\"example.com\"\n\t\t\t\taria-label=\"example.com\"\n\t\t\t/>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-visit\"\n\t\t\t\ttitle={msg('tool.rich-text-link-visit')}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleVisitLink}\n\t\t\t\tdisabled={!value}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"external-link\" />\n\t\t\t</TldrawUiButton>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-remove\"\n\t\t\t\ttitle={msg('tool.rich-text-link-remove')}\n\t\t\t\tdata-testid=\"rich-text.link-remove\"\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleRemoveLink}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"trash\" />\n\t\t\t</TldrawUiButton>\n\t\t</>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AAmEE,mBACC,KADD;AAnEF,SAAS,gBAA8B,iBAAiB;AACxD,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,qBAAqB;AAUvB,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,YAAY;AAC/C,QAAM,MAAM,eAAe;AAC3B,QAAM,MAAM,OAAyB,IAAI;AACzC,QAAM,aAAa,YAAY;AAC/B,QAAM,SAAS;AACf,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAAI,QAAQ,WAAW,KAAK;AAE1E,QAAM,oBAAoB,CAACA,WAAkB,SAASA,MAAK;AAE3D,QAAM,qBAAqB,CAAC,SAAiB;AAC5C,eAAW,aAAa,EAAE,WAAW,aAAa,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAK,WAAW,SAAS,KAAK,CAAC,KAAK,WAAW,UAAU,GAAG;AAChE,aAAO,WAAW,IAAI;AAAA,IACvB;AAEA,eAAW,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;AAG1C,QAAI,OAAO,iBAAiB,EAAE,iBAAiB;AAC9C,iBAAW,SAAS,KAAK;AAAA,IAC1B,OAAO;AACN,iBAAW,SAAS,MAAM;AAAA,IAC3B;AACA,YAAQ;AAAA,EACT;AAEA,QAAM,kBAAkB,MAAM;AAC7B,eAAW,aAAa,EAAE,WAAW,cAAc,OAAO,CAAC;AAC3D,WAAO,KAAK,gBAAgB,UAAU,sBAAsB;AAC5D,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM;AAC9B,eAAW,aAAa,EAAE,WAAW,eAAe,OAAO,CAAC;AAC5D,eAAW,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI;AAC3C,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM,QAAQ;AAEvC,YAAU,MAAM;AACf,QAAI,SAAS,MAAM;AAAA,EACpB,GAAG,CAAC,KAAK,CAAC;AAEV,YAAU,MAAM;AACf,aAAS,YAAY;AAAA,EACtB,GAAG,CAAC,YAAY,CAAC;AAEjB,SACC,iCACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,eAAY;AAAA,QACZ,WAAU;AAAA,QACV;AAAA,QACA,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,aAAY;AAAA,QACZ,cAAW;AAAA;AAAA,IACZ;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,2BAA2B;AAAA,QACtC,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QACT,UAAU,CAAC;AAAA,QAEX,8BAAC,sBAAmB,OAAK,MAAC,MAAK,iBAAgB;AAAA;AAAA,IAChD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,4BAA4B;AAAA,QACvC,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,8BAAC,sBAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KACD;AAEF;",
|
|
6
6
|
"names": ["value"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/Toolbar/OverflowingToolbar.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\tactiveElementShouldCaptureKeys,\n\tassert,\n\tmodulate,\n\tpreventDefault,\n\ttlmenus,\n\tuseEditor,\n\tuseEvent,\n\tuseUniqueSafeId,\n} from '@tldraw/editor'\nimport classNames from 'classnames'\nimport { createContext, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { PORTRAIT_BREAKPOINT } from '../../constants'\nimport { useBreakpoint } from '../../context/breakpoints'\nimport { areShortcutsDisabled } from '../../hooks/useKeyboardShortcuts'\nimport { TLUiToolItem } from '../../hooks/useTools'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport {\n\tTldrawUiPopover,\n\tTldrawUiPopoverContent,\n\tTldrawUiPopoverTrigger,\n} from '../primitives/TldrawUiPopover'\nimport { TldrawUiToolbar, TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'\nimport { TldrawUiColumn, TldrawUiRow } from '../primitives/layout'\nimport { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'\n\nexport const IsInOverflowContext = createContext(false)\n\nconst NUMBERED_SHORTCUT_KEYS: Record<string, number> = {\n\t'1': 0,\n\t'2': 1,\n\t'3': 2,\n\t'4': 3,\n\t'5': 4,\n\t'6': 5,\n\t'7': 6,\n\t'8': 7,\n\t'9': 8,\n\t'0': 9,\n}\n\n/** @public */\nexport interface OverflowingToolbarProps {\n\tchildren: React.ReactNode\n\torientation: 'horizontal' | 'vertical'\n\tsizingParentClassName: string\n\tminItems: number\n\tminSizePx: number\n\tmaxItems: number\n\tmaxSizePx: number\n}\n\n/** @public @react */\nexport function OverflowingToolbar({\n\tchildren,\n\torientation,\n\tsizingParentClassName,\n\tminItems,\n\tminSizePx,\n\tmaxItems,\n\tmaxSizePx,\n}: OverflowingToolbarProps) {\n\tconst editor = useEditor()\n\tconst id = useUniqueSafeId()\n\tconst breakpoint = useBreakpoint()\n\tconst msg = useTranslation()\n\tconst rButtons = useRef<HTMLElement[]>([])\n\tconst [isOpen, setIsOpen] = useState(false)\n\n\tconst mainToolsRef = useRef<HTMLDivElement>(null)\n\n\t// we have to use state instead of a ref here so that we get\n\t// an update when the overflow popover mounts / unmounts\n\tconst [overflowTools, setOverflowTools] = useState<HTMLDivElement | null>(null)\n\tconst [lastActiveOverflowItem, setLastActiveOverflowItem] = useState<string | null>(null)\n\tconst [shouldShowOverflow, setShouldShowOverflow] = useState(false)\n\n\tconst onDomUpdate = useEvent(() => {\n\t\tif (!mainToolsRef.current) return\n\n\t\t// whenever we get an update, we need to re-calculate the number of items to show and update\n\t\t// the component accordingly.\n\t\tconst sizeProp = orientation === 'horizontal' ? 'offsetWidth' : 'offsetHeight'\n\n\t\t// toolbars can contain both single items and groups. we need to keep track of both.\n\t\ttype Items = (\n\t\t\t| { type: 'item'; element: HTMLElement }\n\t\t\t| { type: 'group'; items: Items; element: HTMLElement }\n\t\t)[]\n\n\t\t// walk through the dom and collect items so we can calculate what to show/hide\n\t\tconst mainItems = collectItems(mainToolsRef.current.children)\n\t\tconst overflowItems = overflowTools ? collectItems(overflowTools.children) : null\n\t\tfunction collectItems(collection: HTMLCollection) {\n\t\t\tconst items: Items = []\n\t\t\tfor (const child of collection) {\n\t\t\t\tif (child.classList.contains('tlui-main-toolbar__group')) {\n\t\t\t\t\titems.push({\n\t\t\t\t\t\ttype: 'group',\n\t\t\t\t\t\titems: collectItems(child.children),\n\t\t\t\t\t\telement: child as HTMLElement,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\titems.push({ type: 'item', element: child as HTMLElement })\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn items\n\t\t}\n\n\t\t// the number of items to show is based on the space available to the toolbar.\n\t\tconst sizingParent = findParentWithClassName(mainToolsRef.current, sizingParentClassName)\n\t\tconst size = sizingParent[sizeProp]\n\t\tconst itemsToShow = Math.floor(\n\t\t\tmodulate(size, [minSizePx, maxSizePx], [minItems, maxItems], true)\n\t\t)\n\n\t\t// now we know how many items to show, we need to walk through the items we found and show /\n\t\t// hide them accordingly. We need to keep track of:\n\t\t// the number of item's we've shown in the main content so far\n\t\tlet mainItemCount = 0\n\t\t// the item that is currently active in the overflow content (if any)\n\t\tlet newActiveOverflowItem: string | null = null\n\t\t// whether the last active overflow item is actually still in the overflow content\n\t\tlet shouldInvalidateLastActiveOverflowItem = false\n\t\t// the buttons visible in the main content\n\t\tconst numberedButtons: HTMLButtonElement[] = []\n\t\tfunction visitItems(\n\t\t\tmainItems: Items,\n\t\t\toverflowItems: Items | null\n\t\t): {\n\t\t\t// for each group of items we visit, we need to know whether we showed anything in\n\t\t\t// either section\n\t\t\tdidShowAnyInMain: boolean\n\t\t\tdidShowAnyInOverflow: boolean\n\t\t} {\n\t\t\tif (overflowItems) assert(mainItems.length === overflowItems.length)\n\n\t\t\tlet didShowAnyInMain = false\n\t\t\tlet didShowAnyInOverflow = false\n\n\t\t\tfor (let i = 0; i < mainItems.length; i++) {\n\t\t\t\tconst mainItem = mainItems[i]\n\t\t\t\tconst overflowItem = overflowItems?.[i]\n\n\t\t\t\tif (mainItem.type === 'item') {\n\t\t\t\t\tconst isLastActiveOverflowItem =\n\t\t\t\t\t\tmainItem.element.getAttribute('data-value') === lastActiveOverflowItem\n\n\t\t\t\t\t// for single items, we show them in main if we have space, or if they're the\n\t\t\t\t\t// last-used item from the overflow.\n\t\t\t\t\tlet shouldShowInMain\n\t\t\t\t\tif (lastActiveOverflowItem) {\n\t\t\t\t\t\tshouldShowInMain = mainItemCount < itemsToShow || isLastActiveOverflowItem\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// we use <= here because if there is no last active overflow item, we want\n\t\t\t\t\t\t// to show at least one item in the main toolbar.\n\t\t\t\t\t\tshouldShowInMain = mainItemCount <= itemsToShow\n\t\t\t\t\t}\n\t\t\t\t\tconst shouldShowInOverflow = mainItemCount >= itemsToShow\n\n\t\t\t\t\tdidShowAnyInMain ||= shouldShowInMain\n\t\t\t\t\tdidShowAnyInOverflow ||= shouldShowInOverflow\n\n\t\t\t\t\tsetAttribute(\n\t\t\t\t\t\tmainItem.element,\n\t\t\t\t\t\t'data-toolbar-visible',\n\t\t\t\t\t\tshouldShowInMain ? 'true' : 'false'\n\t\t\t\t\t)\n\t\t\t\t\tif (overflowItem) {\n\t\t\t\t\t\tassert(overflowItem.type === 'item')\n\t\t\t\t\t\tsetAttribute(\n\t\t\t\t\t\t\toverflowItem.element,\n\t\t\t\t\t\t\t'data-toolbar-visible',\n\t\t\t\t\t\t\tshouldShowInOverflow ? 'true' : 'false'\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tif (shouldShowInOverflow && mainItem.element.getAttribute('aria-pressed') === 'true') {\n\t\t\t\t\t\tnewActiveOverflowItem = mainItem.element.getAttribute('data-value')\n\t\t\t\t\t}\n\t\t\t\t\tif (shouldShowInMain && mainItem.element.tagName === 'BUTTON') {\n\t\t\t\t\t\tnumberedButtons.push(mainItem.element as HTMLButtonElement)\n\t\t\t\t\t}\n\t\t\t\t\tif (!shouldShowInOverflow && isLastActiveOverflowItem) {\n\t\t\t\t\t\tshouldInvalidateLastActiveOverflowItem = true\n\t\t\t\t\t}\n\t\t\t\t\tmainItemCount++\n\t\t\t\t} else {\n\t\t\t\t\t// for groups, we show them in main if we have space, or if they're the\n\t\t\t\t\t// last-used item from the overflow.\n\t\t\t\t\tlet result, overflowGroup\n\t\t\t\t\tif (overflowItem) {\n\t\t\t\t\t\tassert(overflowItem.type === 'group')\n\t\t\t\t\t\toverflowGroup = overflowItem\n\t\t\t\t\t\tresult = visitItems(mainItem.items, overflowGroup.items)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = visitItems(mainItem.items, null)\n\t\t\t\t\t}\n\n\t\t\t\t\tdidShowAnyInMain ||= result.didShowAnyInMain\n\t\t\t\t\tdidShowAnyInOverflow ||= result.didShowAnyInOverflow\n\n\t\t\t\t\tsetAttribute(\n\t\t\t\t\t\tmainItem.element,\n\t\t\t\t\t\t'data-toolbar-visible',\n\t\t\t\t\t\tresult.didShowAnyInMain ? 'true' : 'false'\n\t\t\t\t\t)\n\t\t\t\t\tif (overflowGroup) {\n\t\t\t\t\t\tsetAttribute(\n\t\t\t\t\t\t\toverflowGroup.element,\n\t\t\t\t\t\t\t'data-toolbar-visible',\n\t\t\t\t\t\t\tresult.didShowAnyInOverflow ? 'true' : 'false'\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { didShowAnyInMain, didShowAnyInOverflow }\n\t\t}\n\n\t\tconst { didShowAnyInOverflow } = visitItems(mainItems, overflowItems)\n\t\tsetShouldShowOverflow(didShowAnyInOverflow)\n\t\tif (newActiveOverflowItem) {\n\t\t\tsetLastActiveOverflowItem(newActiveOverflowItem)\n\t\t} else if (shouldInvalidateLastActiveOverflowItem) {\n\t\t\tsetLastActiveOverflowItem(null)\n\t\t}\n\n\t\trButtons.current = numberedButtons\n\t})\n\n\tuseLayoutEffect(() => {\n\t\tonDomUpdate()\n\t})\n\n\tuseLayoutEffect(() => {\n\t\tif (!mainToolsRef.current) return\n\n\t\tconst mutationObserver = new MutationObserver(onDomUpdate)\n\t\tmutationObserver.observe(mainToolsRef.current, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\tattributes: true,\n\t\t\tcharacterData: true,\n\t\t})\n\n\t\tconst sizingParent = findParentWithClassName(mainToolsRef.current, sizingParentClassName)\n\t\tconst resizeObserver = new ResizeObserver(onDomUpdate)\n\t\tresizeObserver.observe(sizingParent)\n\n\t\treturn () => {\n\t\t\tmutationObserver.disconnect()\n\t\t\tresizeObserver.disconnect()\n\t\t}\n\t}, [onDomUpdate, sizingParentClassName])\n\n\tuseEffect(() => {\n\t\tif (!editor.options.enableToolbarKeyboardShortcuts) return\n\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (\n\t\t\t\tareShortcutsDisabled(editor) ||\n\t\t\t\tactiveElementShouldCaptureKeys(true /* allow buttons */)\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// no accelerator keys\n\t\t\tif (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return\n\t\t\tconst index = NUMBERED_SHORTCUT_KEYS[event.key]\n\t\t\tif (typeof index === 'number') {\n\t\t\t\tpreventDefault(event)\n\t\t\t\trButtons.current[index]?.click()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown)\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown)\n\t\t}\n\t}, [editor])\n\n\tconst popoverId = 'toolbar overflow'\n\n\tconst Layout = orientation === 'horizontal' ? TldrawUiRow : TldrawUiColumn\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiToolbar\n\t\t\t\torientation={orientation}\n\t\t\t\tclassName={classNames('tlui-main-toolbar__tools', {\n\t\t\t\t\t'tlui-main-toolbar__tools__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,\n\t\t\t\t})}\n\t\t\t\tlabel={msg('tool-panel.title')}\n\t\t\t>\n\t\t\t\t<Layout id={`${id}_main`} ref={mainToolsRef}>\n\t\t\t\t\t<TldrawUiMenuContextProvider type=\"toolbar\" sourceId=\"toolbar\">\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t\t</Layout>\n\t\t\t\t{shouldShowOverflow && (\n\t\t\t\t\t<IsInOverflowContext.Provider value={true}>\n\t\t\t\t\t\t<TldrawUiPopover id={popoverId} open={isOpen} onOpenChange={setIsOpen}>\n\t\t\t\t\t\t\t<TldrawUiPopoverTrigger>\n\t\t\t\t\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\t\t\t\t\ttitle={msg('tool-panel.more')}\n\t\t\t\t\t\t\t\t\ttype=\"tool\"\n\t\t\t\t\t\t\t\t\tclassName=\"tlui-main-toolbar__overflow\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"tools.more-button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<TldrawUiButtonIcon\n\t\t\t\t\t\t\t\t\t\ticon={orientation === 'horizontal' ? 'chevron-up' : 'chevron-right'}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t\t\t\t\t</TldrawUiPopoverTrigger>\n\t\t\t\t\t\t\t<TldrawUiPopoverContent\n\t\t\t\t\t\t\t\tside={orientation === 'horizontal' ? 'top' : 'right'}\n\t\t\t\t\t\t\t\talign={orientation === 'horizontal' ? 'center' : 'end'}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<TldrawUiToolbar\n\t\t\t\t\t\t\t\t\torientation=\"grid\"\n\t\t\t\t\t\t\t\t\tclassName=\"tlui-main-toolbar__overflow-content\"\n\t\t\t\t\t\t\t\t\tref={setOverflowTools}\n\t\t\t\t\t\t\t\t\tdata-testid=\"tools.more-content\"\n\t\t\t\t\t\t\t\t\tlabel={msg('tool-panel.more')}\n\t\t\t\t\t\t\t\t\tid={`${id}_more`}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\ttlmenus.deleteOpenMenu(popoverId, editor.contextId)\n\t\t\t\t\t\t\t\t\t\tsetIsOpen(false)\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<TldrawUiMenuContextProvider type=\"toolbar-overflow\" sourceId=\"toolbar\">\n\t\t\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t\t\t\t\t\t</TldrawUiToolbar>\n\t\t\t\t\t\t\t</TldrawUiPopoverContent>\n\t\t\t\t\t\t</TldrawUiPopover>\n\t\t\t\t\t</IsInOverflowContext.Provider>\n\t\t\t\t)}\n\t\t\t</TldrawUiToolbar>\n\t\t</>\n\t)\n}\n\nexport const isActiveTLUiToolItem = (\n\titem: TLUiToolItem,\n\tactiveToolId: string | undefined,\n\tgeoState: string | null | undefined\n) => {\n\treturn item.meta?.geo\n\t\t? activeToolId === 'geo' && geoState === item.meta?.geo\n\t\t: activeToolId === item.id\n}\n\nfunction findParentWithClassName(startingElement: HTMLElement, className: string): HTMLElement {\n\tlet element: HTMLElement | null = startingElement\n\twhile (element) {\n\t\tif (element.classList.contains(className)) {\n\t\t\treturn element\n\t\t}\n\t\telement = element.parentElement\n\t}\n\tthrow new Error('Could not find parent with class name ' + className)\n}\n\nfunction setAttribute(element: HTMLElement, name: string, value: string) {\n\tif (element.getAttribute(name) === value) return\n\telement.setAttribute(name, value)\n}\n"],
|
|
5
|
-
"mappings": "AA8RE,mBASG,KAMC,YAfJ;AA9RF;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,OAAO,gBAAgB;AACvB,SAAS,eAAe,WAAW,iBAAiB,QAAQ,gBAAgB;AAC5E,SAAS,2BAA2B;AACpC,SAAS,qBAAqB;AAC9B,SAAS,4BAA4B;AAErC,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,iBAAiB,6BAA6B;AACvD,SAAS,gBAAgB,mBAAmB;AAC5C,SAAS,mCAAmC;AAErC,MAAM,sBAAsB,cAAc,KAAK;AAEtD,MAAM,yBAAiD;AAAA,EACtD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACN;AAcO,SAAS,mBAAmB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAA4B;AAC3B,QAAM,SAAS,UAAU;AACzB,QAAM,KAAK,gBAAgB;AAC3B,QAAM,aAAa,cAAc;AACjC,QAAM,MAAM,eAAe;AAC3B,QAAM,WAAW,OAAsB,CAAC,CAAC;AACzC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAE1C,QAAM,eAAe,OAAuB,IAAI;AAIhD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAgC,IAAI;AAC9E,QAAM,CAAC,wBAAwB,yBAAyB,IAAI,SAAwB,IAAI;AACxF,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAS,KAAK;AAElE,QAAM,cAAc,SAAS,MAAM;AAClC,QAAI,CAAC,aAAa,QAAS;AAI3B,UAAM,WAAW,gBAAgB,eAAe,gBAAgB;AAShE,UAAM,YAAY,aAAa,aAAa,QAAQ,QAAQ;AAC5D,UAAM,gBAAgB,gBAAgB,aAAa,cAAc,QAAQ,IAAI;AAC7E,aAAS,aAAa,YAA4B;AACjD,YAAM,QAAe,CAAC;AACtB,iBAAW,SAAS,YAAY;AAC/B,YAAI,MAAM,UAAU,SAAS,0BAA0B,GAAG;AACzD,gBAAM,KAAK;AAAA,YACV,MAAM;AAAA,YACN,OAAO,aAAa,MAAM,QAAQ;AAAA,YAClC,SAAS;AAAA,UACV,CAAC;AAAA,QACF,
|
|
4
|
+
"sourcesContent": ["import {\n\tactiveElementShouldCaptureKeys,\n\tassert,\n\tmodulate,\n\tpreventDefault,\n\ttlmenus,\n\tuseEditor,\n\tuseEvent,\n\tuseUniqueSafeId,\n} from '@tldraw/editor'\nimport classNames from 'classnames'\nimport { createContext, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { PORTRAIT_BREAKPOINT } from '../../constants'\nimport { useBreakpoint } from '../../context/breakpoints'\nimport { areShortcutsDisabled } from '../../hooks/useKeyboardShortcuts'\nimport { TLUiToolItem } from '../../hooks/useTools'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport {\n\tTldrawUiPopover,\n\tTldrawUiPopoverContent,\n\tTldrawUiPopoverTrigger,\n} from '../primitives/TldrawUiPopover'\nimport { TldrawUiToolbar, TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'\nimport { TldrawUiColumn, TldrawUiRow } from '../primitives/layout'\nimport { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'\n\nexport const IsInOverflowContext = createContext(false)\n\nconst NUMBERED_SHORTCUT_KEYS: Record<string, number> = {\n\t'1': 0,\n\t'2': 1,\n\t'3': 2,\n\t'4': 3,\n\t'5': 4,\n\t'6': 5,\n\t'7': 6,\n\t'8': 7,\n\t'9': 8,\n\t'0': 9,\n}\n\n/** @public */\nexport interface OverflowingToolbarProps {\n\tchildren: React.ReactNode\n\torientation: 'horizontal' | 'vertical'\n\tsizingParentClassName: string\n\tminItems: number\n\tminSizePx: number\n\tmaxItems: number\n\tmaxSizePx: number\n}\n\n/** @public @react */\nexport function OverflowingToolbar({\n\tchildren,\n\torientation,\n\tsizingParentClassName,\n\tminItems,\n\tminSizePx,\n\tmaxItems,\n\tmaxSizePx,\n}: OverflowingToolbarProps) {\n\tconst editor = useEditor()\n\tconst id = useUniqueSafeId()\n\tconst breakpoint = useBreakpoint()\n\tconst msg = useTranslation()\n\tconst rButtons = useRef<HTMLElement[]>([])\n\tconst [isOpen, setIsOpen] = useState(false)\n\n\tconst mainToolsRef = useRef<HTMLDivElement>(null)\n\n\t// we have to use state instead of a ref here so that we get\n\t// an update when the overflow popover mounts / unmounts\n\tconst [overflowTools, setOverflowTools] = useState<HTMLDivElement | null>(null)\n\tconst [lastActiveOverflowItem, setLastActiveOverflowItem] = useState<string | null>(null)\n\tconst [shouldShowOverflow, setShouldShowOverflow] = useState(false)\n\n\tconst onDomUpdate = useEvent(() => {\n\t\tif (!mainToolsRef.current) return\n\n\t\t// whenever we get an update, we need to re-calculate the number of items to show and update\n\t\t// the component accordingly.\n\t\tconst sizeProp = orientation === 'horizontal' ? 'offsetWidth' : 'offsetHeight'\n\n\t\t// toolbars can contain both single items and groups. we need to keep track of both.\n\t\ttype Items = (\n\t\t\t| { type: 'item'; element: HTMLElement }\n\t\t\t| { type: 'group'; items: Items; element: HTMLElement }\n\t\t)[]\n\n\t\t// walk through the dom and collect items so we can calculate what to show/hide\n\t\tconst mainItems = collectItems(mainToolsRef.current.children)\n\t\tconst overflowItems = overflowTools ? collectItems(overflowTools.children) : null\n\t\tfunction collectItems(collection: HTMLCollection) {\n\t\t\tconst items: Items = []\n\t\t\tfor (const child of collection) {\n\t\t\t\tif (child.classList.contains('tlui-main-toolbar__group')) {\n\t\t\t\t\titems.push({\n\t\t\t\t\t\ttype: 'group',\n\t\t\t\t\t\titems: collectItems(child.children),\n\t\t\t\t\t\telement: child as HTMLElement,\n\t\t\t\t\t})\n\t\t\t\t} else if (!child.hasAttribute('data-radix-popper-content-wrapper')) {\n\t\t\t\t\titems.push({ type: 'item', element: child as HTMLElement })\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn items\n\t\t}\n\n\t\t// the number of items to show is based on the space available to the toolbar.\n\t\tconst sizingParent = findParentWithClassName(mainToolsRef.current, sizingParentClassName)\n\t\tconst size = sizingParent[sizeProp]\n\t\tconst itemsToShow = Math.floor(\n\t\t\tmodulate(size, [minSizePx, maxSizePx], [minItems, maxItems], true)\n\t\t)\n\n\t\t// now we know how many items to show, we need to walk through the items we found and show /\n\t\t// hide them accordingly. We need to keep track of:\n\t\t// the number of item's we've shown in the main content so far\n\t\tlet mainItemCount = 0\n\t\t// the item that is currently active in the overflow content (if any)\n\t\tlet newActiveOverflowItem: string | null = null\n\t\t// whether the last active overflow item is actually still in the overflow content\n\t\tlet shouldInvalidateLastActiveOverflowItem = false\n\t\t// the buttons visible in the main content\n\t\tconst numberedButtons: HTMLButtonElement[] = []\n\t\tfunction visitItems(\n\t\t\tmainItems: Items,\n\t\t\toverflowItems: Items | null\n\t\t): {\n\t\t\t// for each group of items we visit, we need to know whether we showed anything in\n\t\t\t// either section\n\t\t\tdidShowAnyInMain: boolean\n\t\t\tdidShowAnyInOverflow: boolean\n\t\t} {\n\t\t\tif (overflowItems) assert(mainItems.length === overflowItems.length)\n\n\t\t\tlet didShowAnyInMain = false\n\t\t\tlet didShowAnyInOverflow = false\n\n\t\t\tfor (let i = 0; i < mainItems.length; i++) {\n\t\t\t\tconst mainItem = mainItems[i]\n\t\t\t\tconst overflowItem = overflowItems?.[i]\n\n\t\t\t\tif (mainItem.type === 'item') {\n\t\t\t\t\tconst isLastActiveOverflowItem =\n\t\t\t\t\t\tmainItem.element.getAttribute('data-value') === lastActiveOverflowItem\n\n\t\t\t\t\t// for single items, we show them in main if we have space, or if they're the\n\t\t\t\t\t// last-used item from the overflow.\n\t\t\t\t\tlet shouldShowInMain\n\t\t\t\t\tif (lastActiveOverflowItem) {\n\t\t\t\t\t\tshouldShowInMain = mainItemCount < itemsToShow || isLastActiveOverflowItem\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// we use <= here because if there is no last active overflow item, we want\n\t\t\t\t\t\t// to show at least one item in the main toolbar.\n\t\t\t\t\t\tshouldShowInMain = mainItemCount <= itemsToShow\n\t\t\t\t\t}\n\t\t\t\t\tconst shouldShowInOverflow = mainItemCount >= itemsToShow\n\n\t\t\t\t\tdidShowAnyInMain ||= shouldShowInMain\n\t\t\t\t\tdidShowAnyInOverflow ||= shouldShowInOverflow\n\n\t\t\t\t\tsetAttribute(\n\t\t\t\t\t\tmainItem.element,\n\t\t\t\t\t\t'data-toolbar-visible',\n\t\t\t\t\t\tshouldShowInMain ? 'true' : 'false'\n\t\t\t\t\t)\n\t\t\t\t\tif (overflowItem) {\n\t\t\t\t\t\tassert(overflowItem.type === 'item')\n\t\t\t\t\t\tsetAttribute(\n\t\t\t\t\t\t\toverflowItem.element,\n\t\t\t\t\t\t\t'data-toolbar-visible',\n\t\t\t\t\t\t\tshouldShowInOverflow ? 'true' : 'false'\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tif (shouldShowInOverflow && mainItem.element.getAttribute('aria-pressed') === 'true') {\n\t\t\t\t\t\tnewActiveOverflowItem = mainItem.element.getAttribute('data-value')\n\t\t\t\t\t}\n\t\t\t\t\tif (shouldShowInMain && mainItem.element.tagName === 'BUTTON') {\n\t\t\t\t\t\tnumberedButtons.push(mainItem.element as HTMLButtonElement)\n\t\t\t\t\t}\n\t\t\t\t\tif (!shouldShowInOverflow && isLastActiveOverflowItem) {\n\t\t\t\t\t\tshouldInvalidateLastActiveOverflowItem = true\n\t\t\t\t\t}\n\t\t\t\t\tmainItemCount++\n\t\t\t\t} else {\n\t\t\t\t\t// for groups, we show them in main if we have space, or if they're the\n\t\t\t\t\t// last-used item from the overflow.\n\t\t\t\t\tlet result, overflowGroup\n\t\t\t\t\tif (overflowItem) {\n\t\t\t\t\t\tassert(overflowItem.type === 'group')\n\t\t\t\t\t\toverflowGroup = overflowItem\n\t\t\t\t\t\tresult = visitItems(mainItem.items, overflowGroup.items)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = visitItems(mainItem.items, null)\n\t\t\t\t\t}\n\n\t\t\t\t\tdidShowAnyInMain ||= result.didShowAnyInMain\n\t\t\t\t\tdidShowAnyInOverflow ||= result.didShowAnyInOverflow\n\n\t\t\t\t\tsetAttribute(\n\t\t\t\t\t\tmainItem.element,\n\t\t\t\t\t\t'data-toolbar-visible',\n\t\t\t\t\t\tresult.didShowAnyInMain ? 'true' : 'false'\n\t\t\t\t\t)\n\t\t\t\t\tif (overflowGroup) {\n\t\t\t\t\t\tsetAttribute(\n\t\t\t\t\t\t\toverflowGroup.element,\n\t\t\t\t\t\t\t'data-toolbar-visible',\n\t\t\t\t\t\t\tresult.didShowAnyInOverflow ? 'true' : 'false'\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { didShowAnyInMain, didShowAnyInOverflow }\n\t\t}\n\n\t\tconst { didShowAnyInOverflow } = visitItems(mainItems, overflowItems)\n\t\tsetShouldShowOverflow(didShowAnyInOverflow)\n\t\tif (newActiveOverflowItem) {\n\t\t\tsetLastActiveOverflowItem(newActiveOverflowItem)\n\t\t} else if (shouldInvalidateLastActiveOverflowItem) {\n\t\t\tsetLastActiveOverflowItem(null)\n\t\t}\n\n\t\trButtons.current = numberedButtons\n\t})\n\n\tuseLayoutEffect(() => {\n\t\tonDomUpdate()\n\t})\n\n\tuseLayoutEffect(() => {\n\t\tif (!mainToolsRef.current) return\n\n\t\tconst mutationObserver = new MutationObserver(onDomUpdate)\n\t\tmutationObserver.observe(mainToolsRef.current, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\tattributes: true,\n\t\t\tcharacterData: true,\n\t\t})\n\n\t\tconst sizingParent = findParentWithClassName(mainToolsRef.current, sizingParentClassName)\n\t\tconst resizeObserver = new ResizeObserver(onDomUpdate)\n\t\tresizeObserver.observe(sizingParent)\n\n\t\treturn () => {\n\t\t\tmutationObserver.disconnect()\n\t\t\tresizeObserver.disconnect()\n\t\t}\n\t}, [onDomUpdate, sizingParentClassName])\n\n\tuseEffect(() => {\n\t\tif (!editor.options.enableToolbarKeyboardShortcuts) return\n\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (\n\t\t\t\tareShortcutsDisabled(editor) ||\n\t\t\t\tactiveElementShouldCaptureKeys(true /* allow buttons */)\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// no accelerator keys\n\t\t\tif (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return\n\t\t\tconst index = NUMBERED_SHORTCUT_KEYS[event.key]\n\t\t\tif (typeof index === 'number') {\n\t\t\t\tpreventDefault(event)\n\t\t\t\trButtons.current[index]?.click()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown)\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown)\n\t\t}\n\t}, [editor])\n\n\tconst popoverId = 'toolbar overflow'\n\n\tconst Layout = orientation === 'horizontal' ? TldrawUiRow : TldrawUiColumn\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiToolbar\n\t\t\t\torientation={orientation}\n\t\t\t\tclassName={classNames('tlui-main-toolbar__tools', {\n\t\t\t\t\t'tlui-main-toolbar__tools__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,\n\t\t\t\t})}\n\t\t\t\tlabel={msg('tool-panel.title')}\n\t\t\t>\n\t\t\t\t<Layout id={`${id}_main`} ref={mainToolsRef}>\n\t\t\t\t\t<TldrawUiMenuContextProvider type=\"toolbar\" sourceId=\"toolbar\">\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t\t</Layout>\n\t\t\t\t{shouldShowOverflow && (\n\t\t\t\t\t<IsInOverflowContext.Provider value={true}>\n\t\t\t\t\t\t<TldrawUiPopover id={popoverId} open={isOpen} onOpenChange={setIsOpen}>\n\t\t\t\t\t\t\t<TldrawUiPopoverTrigger>\n\t\t\t\t\t\t\t\t<TldrawUiToolbarButton\n\t\t\t\t\t\t\t\t\ttitle={msg('tool-panel.more')}\n\t\t\t\t\t\t\t\t\ttype=\"tool\"\n\t\t\t\t\t\t\t\t\tclassName=\"tlui-main-toolbar__overflow\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"tools.more-button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<TldrawUiButtonIcon\n\t\t\t\t\t\t\t\t\t\ticon={orientation === 'horizontal' ? 'chevron-up' : 'chevron-right'}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</TldrawUiToolbarButton>\n\t\t\t\t\t\t\t</TldrawUiPopoverTrigger>\n\t\t\t\t\t\t\t<TldrawUiPopoverContent\n\t\t\t\t\t\t\t\tside={orientation === 'horizontal' ? 'top' : 'right'}\n\t\t\t\t\t\t\t\talign={orientation === 'horizontal' ? 'center' : 'end'}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<TldrawUiToolbar\n\t\t\t\t\t\t\t\t\torientation=\"grid\"\n\t\t\t\t\t\t\t\t\tclassName=\"tlui-main-toolbar__overflow-content\"\n\t\t\t\t\t\t\t\t\tref={setOverflowTools}\n\t\t\t\t\t\t\t\t\tdata-testid=\"tools.more-content\"\n\t\t\t\t\t\t\t\t\tlabel={msg('tool-panel.more')}\n\t\t\t\t\t\t\t\t\tid={`${id}_more`}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\ttlmenus.deleteOpenMenu(popoverId, editor.contextId)\n\t\t\t\t\t\t\t\t\t\tsetIsOpen(false)\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<TldrawUiMenuContextProvider type=\"toolbar-overflow\" sourceId=\"toolbar\">\n\t\t\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t\t\t\t\t\t</TldrawUiToolbar>\n\t\t\t\t\t\t\t</TldrawUiPopoverContent>\n\t\t\t\t\t\t</TldrawUiPopover>\n\t\t\t\t\t</IsInOverflowContext.Provider>\n\t\t\t\t)}\n\t\t\t</TldrawUiToolbar>\n\t\t</>\n\t)\n}\n\nexport const isActiveTLUiToolItem = (\n\titem: TLUiToolItem,\n\tactiveToolId: string | undefined,\n\tgeoState: string | null | undefined\n) => {\n\treturn item.meta?.geo\n\t\t? activeToolId === 'geo' && geoState === item.meta?.geo\n\t\t: activeToolId === item.id\n}\n\nfunction findParentWithClassName(startingElement: HTMLElement, className: string): HTMLElement {\n\tlet element: HTMLElement | null = startingElement\n\twhile (element) {\n\t\tif (element.classList.contains(className)) {\n\t\t\treturn element\n\t\t}\n\t\telement = element.parentElement\n\t}\n\tthrow new Error('Could not find parent with class name ' + className)\n}\n\nfunction setAttribute(element: HTMLElement, name: string, value: string) {\n\tif (element.getAttribute(name) === value) return\n\telement.setAttribute(name, value)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["mainItems", "overflowItems", "didShowAnyInOverflow"]
|
|
7
7
|
}
|
|
@@ -2,8 +2,10 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEditor, useValue } from "@tldraw/editor";
|
|
3
3
|
import classNames from "classnames";
|
|
4
4
|
import { PORTRAIT_BREAKPOINT } from "../../constants.mjs";
|
|
5
|
+
import { useActions } from "../../context/actions.mjs";
|
|
5
6
|
import { useBreakpoint } from "../../context/breakpoints.mjs";
|
|
6
7
|
import { useTranslation } from "../../hooks/useTranslation/useTranslation.mjs";
|
|
8
|
+
import { kbdStr } from "../../kbd-utils.mjs";
|
|
7
9
|
import { TldrawUiButton } from "../primitives/Button/TldrawUiButton.mjs";
|
|
8
10
|
import { TldrawUiButtonIcon } from "../primitives/Button/TldrawUiButtonIcon.mjs";
|
|
9
11
|
import { TldrawUiTooltip } from "../primitives/TldrawUiTooltip.mjs";
|
|
@@ -11,16 +13,18 @@ function ToggleToolLockedButton({ activeToolId }) {
|
|
|
11
13
|
const editor = useEditor();
|
|
12
14
|
const breakpoint = useBreakpoint();
|
|
13
15
|
const msg = useTranslation();
|
|
16
|
+
const actions = useActions();
|
|
14
17
|
const isToolLocked = useValue("is tool locked", () => editor.getInstanceState().isToolLocked, [
|
|
15
18
|
editor
|
|
16
19
|
]);
|
|
17
20
|
const tool = useValue("current tool", () => editor.getCurrentTool(), [editor]);
|
|
18
21
|
if (!activeToolId || !tool.isLockable) return null;
|
|
19
|
-
|
|
22
|
+
const toggleLockAction = actions["toggle-tool-lock"];
|
|
23
|
+
const tooltipContent = toggleLockAction?.kbd ? `${msg("action.toggle-tool-lock")} ${kbdStr(toggleLockAction.kbd)}` : msg("action.toggle-tool-lock");
|
|
24
|
+
return /* @__PURE__ */ jsx(TldrawUiTooltip, { content: tooltipContent, children: /* @__PURE__ */ jsx(
|
|
20
25
|
TldrawUiButton,
|
|
21
26
|
{
|
|
22
27
|
type: "normal",
|
|
23
|
-
title: msg("action.toggle-tool-lock"),
|
|
24
28
|
"data-testid": "tool-lock",
|
|
25
29
|
className: classNames("tlui-main-toolbar__lock-button", {
|
|
26
30
|
"tlui-main-toolbar__lock-button__mobile": breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx"],
|
|
4
|
-
"sourcesContent": ["import { useEditor, useValue } from '@tldraw/editor'\nimport classNames from 'classnames'\nimport { PORTRAIT_BREAKPOINT } from '../../constants'\nimport { useBreakpoint } from '../../context/breakpoints'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiTooltip } from '../primitives/TldrawUiTooltip'\n\n/** @public */\nexport interface ToggleToolLockedButtonProps {\n\tactiveToolId?: string\n}\n\n/** @public @react */\nexport function ToggleToolLockedButton({ activeToolId }: ToggleToolLockedButtonProps) {\n\tconst editor = useEditor()\n\tconst breakpoint = useBreakpoint()\n\tconst msg = useTranslation()\n\n\tconst isToolLocked = useValue('is tool locked', () => editor.getInstanceState().isToolLocked, [\n\t\teditor,\n\t])\n\tconst tool = useValue('current tool', () => editor.getCurrentTool(), [editor])\n\n\tif (!activeToolId || !tool.isLockable) return null\n\n\
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { useEditor, useValue } from '@tldraw/editor'\nimport classNames from 'classnames'\nimport { PORTRAIT_BREAKPOINT } from '../../constants'\nimport { useActions } from '../../context/actions'\nimport { useBreakpoint } from '../../context/breakpoints'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { kbdStr } from '../../kbd-utils'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiTooltip } from '../primitives/TldrawUiTooltip'\n\n/** @public */\nexport interface ToggleToolLockedButtonProps {\n\tactiveToolId?: string\n}\n\n/** @public @react */\nexport function ToggleToolLockedButton({ activeToolId }: ToggleToolLockedButtonProps) {\n\tconst editor = useEditor()\n\tconst breakpoint = useBreakpoint()\n\tconst msg = useTranslation()\n\tconst actions = useActions()\n\n\tconst isToolLocked = useValue('is tool locked', () => editor.getInstanceState().isToolLocked, [\n\t\teditor,\n\t])\n\tconst tool = useValue('current tool', () => editor.getCurrentTool(), [editor])\n\n\tif (!activeToolId || !tool.isLockable) return null\n\n\tconst toggleLockAction = actions['toggle-tool-lock']\n\tconst tooltipContent = toggleLockAction?.kbd\n\t\t? `${msg('action.toggle-tool-lock')} ${kbdStr(toggleLockAction.kbd)}`\n\t\t: msg('action.toggle-tool-lock')\n\n\treturn (\n\t\t<TldrawUiTooltip content={tooltipContent}>\n\t\t\t<TldrawUiButton\n\t\t\t\ttype=\"normal\"\n\t\t\t\tdata-testid=\"tool-lock\"\n\t\t\t\tclassName={classNames('tlui-main-toolbar__lock-button', {\n\t\t\t\t\t'tlui-main-toolbar__lock-button__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,\n\t\t\t\t})}\n\t\t\t\tonClick={() => editor.updateInstanceState({ isToolLocked: !isToolLocked })}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon icon={isToolLocked ? 'lock' : 'unlock'} small />\n\t\t\t</TldrawUiButton>\n\t\t</TldrawUiTooltip>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AA6CI;AA7CJ,SAAS,WAAW,gBAAgB;AACpC,OAAO,gBAAgB;AACvB,SAAS,2BAA2B;AACpC,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAC9B,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AACvB,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAQzB,SAAS,uBAAuB,EAAE,aAAa,GAAgC;AACrF,QAAM,SAAS,UAAU;AACzB,QAAM,aAAa,cAAc;AACjC,QAAM,MAAM,eAAe;AAC3B,QAAM,UAAU,WAAW;AAE3B,QAAM,eAAe,SAAS,kBAAkB,MAAM,OAAO,iBAAiB,EAAE,cAAc;AAAA,IAC7F;AAAA,EACD,CAAC;AACD,QAAM,OAAO,SAAS,gBAAgB,MAAM,OAAO,eAAe,GAAG,CAAC,MAAM,CAAC;AAE7E,MAAI,CAAC,gBAAgB,CAAC,KAAK,WAAY,QAAO;AAE9C,QAAM,mBAAmB,QAAQ,kBAAkB;AACnD,QAAM,iBAAiB,kBAAkB,MACtC,GAAG,IAAI,yBAAyB,CAAC,IAAI,OAAO,iBAAiB,GAAG,CAAC,KACjE,IAAI,yBAAyB;AAEhC,SACC,oBAAC,mBAAgB,SAAS,gBACzB;AAAA,IAAC;AAAA;AAAA,MACA,MAAK;AAAA,MACL,eAAY;AAAA,MACZ,WAAW,WAAW,kCAAkC;AAAA,QACvD,0CAA0C,aAAa,oBAAoB;AAAA,MAC5E,CAAC;AAAA,MACD,SAAS,MAAM,OAAO,oBAAoB,EAAE,cAAc,CAAC,aAAa,CAAC;AAAA,MAEzE,8BAAC,sBAAmB,MAAM,eAAe,SAAS,UAAU,OAAK,MAAC;AAAA;AAAA,EACnE,GACD;AAEF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -24,7 +24,8 @@ const TldrawUiInput = React.forwardRef(
|
|
|
24
24
|
children,
|
|
25
25
|
value,
|
|
26
26
|
"data-testid": dataTestId,
|
|
27
|
-
disabled
|
|
27
|
+
disabled,
|
|
28
|
+
"aria-label": ariaLabel
|
|
28
29
|
}, ref) {
|
|
29
30
|
const editor = useMaybeEditor();
|
|
30
31
|
const rInputRef = React.useRef(null);
|
|
@@ -145,6 +146,7 @@ const TldrawUiInput = React.forwardRef(
|
|
|
145
146
|
onCompositionStart: handleCompositionStart,
|
|
146
147
|
onCompositionEnd: handleCompositionEnd,
|
|
147
148
|
autoFocus,
|
|
149
|
+
"aria-label": ariaLabel,
|
|
148
150
|
placeholder,
|
|
149
151
|
value,
|
|
150
152
|
"data-testid": dataTestId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiInput.tsx"],
|
|
4
|
-
"sourcesContent": ["import { tlenv, tltime, useMaybeEditor } from '@tldraw/editor'\nimport classNames from 'classnames'\nimport * as React from 'react'\nimport { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TLUiIconType } from '../../icon-types'\nimport { TldrawUiIcon } from './TldrawUiIcon'\n\n/** @public */\nexport interface TLUiInputProps {\n\tdisabled?: boolean\n\tlabel?: TLUiTranslationKey | Exclude<string, TLUiTranslationKey>\n\ticon?: TLUiIconType | Exclude<string, TLUiIconType>\n\ticonLeft?: TLUiIconType | Exclude<string, TLUiIconType>\n\ticonLabel?: TLUiTranslationKey | Exclude<string, TLUiTranslationKey>\n\tautoFocus?: boolean\n\tautoSelect?: boolean\n\tchildren?: React.ReactNode\n\tdefaultValue?: string\n\tplaceholder?: string\n\tonComplete?(value: string): void\n\tonValueChange?(value: string): void\n\tonCancel?(value: string): void\n\tonBlur?(value: string): void\n\tonFocus?(): void\n\tclassName?: string\n\t/**\n\t * Usually on iOS when you focus an input, the browser will adjust the viewport to bring the input\n\t * into view. Sometimes this doesn't work properly though - for example, if the input is newly\n\t * created, iOS seems to have a hard time adjusting the viewport for it. This prop allows you to\n\t * opt-in to some extra code to manually bring the input into view when the visual viewport of the\n\t * browser changes, but we don't want to use it everywhere because generally the native behavior\n\t * looks nicer in scenarios where it's sufficient.\n\t */\n\tshouldManuallyMaintainScrollPositionWhenFocused?: boolean\n\tvalue?: string\n\t'data-testid'?: string\n}\n\n/** @public @react */\nexport const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(\n\tfunction TldrawUiInput(\n\t\t{\n\t\t\tclassName,\n\t\t\tlabel,\n\t\t\ticon,\n\t\t\ticonLeft,\n\t\t\ticonLabel,\n\t\t\tautoSelect = false,\n\t\t\tautoFocus = false,\n\t\t\tdefaultValue,\n\t\t\tplaceholder,\n\t\t\tonComplete,\n\t\t\tonValueChange,\n\t\t\tonCancel,\n\t\t\tonFocus,\n\t\t\tonBlur,\n\t\t\tshouldManuallyMaintainScrollPositionWhenFocused = false,\n\t\t\tchildren,\n\t\t\tvalue,\n\t\t\t'data-testid': dataTestId,\n\t\t\tdisabled,\n\t\t},\n\t\tref\n\t) {\n\t\tconst editor = useMaybeEditor()\n\t\tconst rInputRef = React.useRef<HTMLInputElement>(null)\n\n\t\t// combine rInputRef and ref\n\t\tReact.useImperativeHandle(ref, () => rInputRef.current as HTMLInputElement)\n\n\t\tconst msg = useTranslation()\n\t\tconst rInitialValue = React.useRef<string>(defaultValue ?? '')\n\t\tconst rCurrentValue = React.useRef<string>(defaultValue ?? '')\n\n\t\tconst isComposing = React.useRef(false)\n\n\t\tconst [isFocused, setIsFocused] = React.useState(false)\n\t\tconst handleFocus = React.useCallback(\n\t\t\t(e: React.FocusEvent<HTMLInputElement>) => {\n\t\t\t\tsetIsFocused(true)\n\t\t\t\tconst elm = e.currentTarget as HTMLInputElement\n\t\t\t\trCurrentValue.current = elm.value\n\t\t\t\tif (editor) {\n\t\t\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\t\t\tif (autoSelect) {\n\t\t\t\t\t\t\telm.select()\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\ttltime.requestAnimationFrame('anon', () => {\n\t\t\t\t\t\tif (autoSelect) {\n\t\t\t\t\t\t\telm.select()\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tonFocus?.()\n\t\t\t},\n\t\t\t[autoSelect, editor, onFocus]\n\t\t)\n\n\t\tconst handleChange = React.useCallback(\n\t\t\t(e: React.ChangeEvent<HTMLInputElement>) => {\n\t\t\t\tconst value = e.currentTarget.value\n\t\t\t\trCurrentValue.current = value\n\t\t\t\tonValueChange?.(value)\n\t\t\t},\n\t\t\t[onValueChange]\n\t\t)\n\n\t\t// We use keydown capture, because we want to get the escape key event.\n\t\tconst handleKeyDownCapture = React.useCallback(\n\t\t\t(e: React.KeyboardEvent<HTMLInputElement>) => {\n\t\t\t\tswitch (e.key) {\n\t\t\t\t\tcase 'Enter': {\n\t\t\t\t\t\t// In Chrome, if the user presses the Enter key while using the IME and calls\n\t\t\t\t\t\t// `e.currentTarget.blur()` in the event callback here, it will trigger an\n\t\t\t\t\t\t// `onChange` with a duplicated text value.\n\t\t\t\t\t\tif (isComposing.current) return\n\t\t\t\t\t\te.currentTarget.blur()\n\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\tonComplete?.(e.currentTarget.value)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcase 'Escape': {\n\t\t\t\t\t\te.currentTarget.value = rInitialValue.current\n\t\t\t\t\t\tonCancel?.(e.currentTarget.value)\n\t\t\t\t\t\te.currentTarget.blur()\n\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t[onComplete, onCancel]\n\t\t)\n\n\t\tconst handleBlur = React.useCallback(\n\t\t\t(e: React.FocusEvent<HTMLInputElement>) => {\n\t\t\t\tsetIsFocused(false)\n\t\t\t\tconst value = e.currentTarget.value\n\t\t\t\tonBlur?.(value)\n\t\t\t},\n\t\t\t[onBlur]\n\t\t)\n\n\t\tconst handleCompositionStart = React.useCallback(() => (isComposing.current = true), [])\n\t\tconst handleCompositionEnd = React.useCallback(() => (isComposing.current = false), [])\n\n\t\tReact.useEffect(() => {\n\t\t\tif (!tlenv.isIos) return\n\n\t\t\tconst visualViewport = window.visualViewport\n\t\t\tif (isFocused && shouldManuallyMaintainScrollPositionWhenFocused && visualViewport) {\n\t\t\t\tconst onViewportChange = () => {\n\t\t\t\t\trInputRef.current?.scrollIntoView({ block: 'center' })\n\t\t\t\t}\n\t\t\t\tvisualViewport.addEventListener('resize', onViewportChange)\n\t\t\t\tvisualViewport.addEventListener('scroll', onViewportChange)\n\n\t\t\t\tif (editor) {\n\t\t\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\t\t\trInputRef.current?.scrollIntoView({ block: 'center' })\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\ttltime.requestAnimationFrame('anon', () => {\n\t\t\t\t\t\trInputRef.current?.scrollIntoView({ block: 'center' })\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\treturn () => {\n\t\t\t\t\tvisualViewport.removeEventListener('resize', onViewportChange)\n\t\t\t\t\tvisualViewport.removeEventListener('scroll', onViewportChange)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [isFocused, editor, shouldManuallyMaintainScrollPositionWhenFocused])\n\n\t\treturn (\n\t\t\t<div draggable={false} className=\"tlui-input__wrapper\">\n\t\t\t\t{children}\n\t\t\t\t{label && <label>{msg(label)}</label>}\n\t\t\t\t{iconLeft && (\n\t\t\t\t\t<TldrawUiIcon\n\t\t\t\t\t\tlabel={iconLabel ? msg(iconLabel) : ''}\n\t\t\t\t\t\ticon={iconLeft}\n\t\t\t\t\t\tclassName=\"tlui-icon-left\"\n\t\t\t\t\t\tsmall\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t<input\n\t\t\t\t\tref={rInputRef}\n\t\t\t\t\tclassName={classNames('tlui-input', className)}\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tdefaultValue={defaultValue}\n\t\t\t\t\tonKeyDownCapture={handleKeyDownCapture}\n\t\t\t\t\tonChange={handleChange}\n\t\t\t\t\tonFocus={handleFocus}\n\t\t\t\t\tonBlur={handleBlur}\n\t\t\t\t\tonCompositionStart={handleCompositionStart}\n\t\t\t\t\tonCompositionEnd={handleCompositionEnd}\n\t\t\t\t\tautoFocus={autoFocus}\n\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\tvalue={value}\n\t\t\t\t\tdata-testid={dataTestId}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t/>\n\t\t\t\t{icon && (\n\t\t\t\t\t<TldrawUiIcon label={iconLabel ? msg(iconLabel) : ''} icon={icon} small={!!label} />\n\t\t\t\t)}\n\t\t\t</div>\n\t\t)\n\t}\n)\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { tlenv, tltime, useMaybeEditor } from '@tldraw/editor'\nimport classNames from 'classnames'\nimport * as React from 'react'\nimport { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TLUiIconType } from '../../icon-types'\nimport { TldrawUiIcon } from './TldrawUiIcon'\n\n/** @public */\nexport interface TLUiInputProps {\n\tdisabled?: boolean\n\tlabel?: TLUiTranslationKey | Exclude<string, TLUiTranslationKey>\n\ticon?: TLUiIconType | Exclude<string, TLUiIconType>\n\ticonLeft?: TLUiIconType | Exclude<string, TLUiIconType>\n\ticonLabel?: TLUiTranslationKey | Exclude<string, TLUiTranslationKey>\n\tautoFocus?: boolean\n\tautoSelect?: boolean\n\tchildren?: React.ReactNode\n\tdefaultValue?: string\n\tplaceholder?: string\n\tonComplete?(value: string): void\n\tonValueChange?(value: string): void\n\tonCancel?(value: string): void\n\tonBlur?(value: string): void\n\tonFocus?(): void\n\tclassName?: string\n\t/**\n\t * Usually on iOS when you focus an input, the browser will adjust the viewport to bring the input\n\t * into view. Sometimes this doesn't work properly though - for example, if the input is newly\n\t * created, iOS seems to have a hard time adjusting the viewport for it. This prop allows you to\n\t * opt-in to some extra code to manually bring the input into view when the visual viewport of the\n\t * browser changes, but we don't want to use it everywhere because generally the native behavior\n\t * looks nicer in scenarios where it's sufficient.\n\t */\n\tshouldManuallyMaintainScrollPositionWhenFocused?: boolean\n\tvalue?: string\n\t'data-testid'?: string\n\t'aria-label'?: string\n}\n\n/** @public @react */\nexport const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(\n\tfunction TldrawUiInput(\n\t\t{\n\t\t\tclassName,\n\t\t\tlabel,\n\t\t\ticon,\n\t\t\ticonLeft,\n\t\t\ticonLabel,\n\t\t\tautoSelect = false,\n\t\t\tautoFocus = false,\n\t\t\tdefaultValue,\n\t\t\tplaceholder,\n\t\t\tonComplete,\n\t\t\tonValueChange,\n\t\t\tonCancel,\n\t\t\tonFocus,\n\t\t\tonBlur,\n\t\t\tshouldManuallyMaintainScrollPositionWhenFocused = false,\n\t\t\tchildren,\n\t\t\tvalue,\n\t\t\t'data-testid': dataTestId,\n\t\t\tdisabled,\n\t\t\t'aria-label': ariaLabel,\n\t\t},\n\t\tref\n\t) {\n\t\tconst editor = useMaybeEditor()\n\t\tconst rInputRef = React.useRef<HTMLInputElement>(null)\n\n\t\t// combine rInputRef and ref\n\t\tReact.useImperativeHandle(ref, () => rInputRef.current as HTMLInputElement)\n\n\t\tconst msg = useTranslation()\n\t\tconst rInitialValue = React.useRef<string>(defaultValue ?? '')\n\t\tconst rCurrentValue = React.useRef<string>(defaultValue ?? '')\n\n\t\tconst isComposing = React.useRef(false)\n\n\t\tconst [isFocused, setIsFocused] = React.useState(false)\n\t\tconst handleFocus = React.useCallback(\n\t\t\t(e: React.FocusEvent<HTMLInputElement>) => {\n\t\t\t\tsetIsFocused(true)\n\t\t\t\tconst elm = e.currentTarget as HTMLInputElement\n\t\t\t\trCurrentValue.current = elm.value\n\t\t\t\tif (editor) {\n\t\t\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\t\t\tif (autoSelect) {\n\t\t\t\t\t\t\telm.select()\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\ttltime.requestAnimationFrame('anon', () => {\n\t\t\t\t\t\tif (autoSelect) {\n\t\t\t\t\t\t\telm.select()\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tonFocus?.()\n\t\t\t},\n\t\t\t[autoSelect, editor, onFocus]\n\t\t)\n\n\t\tconst handleChange = React.useCallback(\n\t\t\t(e: React.ChangeEvent<HTMLInputElement>) => {\n\t\t\t\tconst value = e.currentTarget.value\n\t\t\t\trCurrentValue.current = value\n\t\t\t\tonValueChange?.(value)\n\t\t\t},\n\t\t\t[onValueChange]\n\t\t)\n\n\t\t// We use keydown capture, because we want to get the escape key event.\n\t\tconst handleKeyDownCapture = React.useCallback(\n\t\t\t(e: React.KeyboardEvent<HTMLInputElement>) => {\n\t\t\t\tswitch (e.key) {\n\t\t\t\t\tcase 'Enter': {\n\t\t\t\t\t\t// In Chrome, if the user presses the Enter key while using the IME and calls\n\t\t\t\t\t\t// `e.currentTarget.blur()` in the event callback here, it will trigger an\n\t\t\t\t\t\t// `onChange` with a duplicated text value.\n\t\t\t\t\t\tif (isComposing.current) return\n\t\t\t\t\t\te.currentTarget.blur()\n\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\tonComplete?.(e.currentTarget.value)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcase 'Escape': {\n\t\t\t\t\t\te.currentTarget.value = rInitialValue.current\n\t\t\t\t\t\tonCancel?.(e.currentTarget.value)\n\t\t\t\t\t\te.currentTarget.blur()\n\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t[onComplete, onCancel]\n\t\t)\n\n\t\tconst handleBlur = React.useCallback(\n\t\t\t(e: React.FocusEvent<HTMLInputElement>) => {\n\t\t\t\tsetIsFocused(false)\n\t\t\t\tconst value = e.currentTarget.value\n\t\t\t\tonBlur?.(value)\n\t\t\t},\n\t\t\t[onBlur]\n\t\t)\n\n\t\tconst handleCompositionStart = React.useCallback(() => (isComposing.current = true), [])\n\t\tconst handleCompositionEnd = React.useCallback(() => (isComposing.current = false), [])\n\n\t\tReact.useEffect(() => {\n\t\t\tif (!tlenv.isIos) return\n\n\t\t\tconst visualViewport = window.visualViewport\n\t\t\tif (isFocused && shouldManuallyMaintainScrollPositionWhenFocused && visualViewport) {\n\t\t\t\tconst onViewportChange = () => {\n\t\t\t\t\trInputRef.current?.scrollIntoView({ block: 'center' })\n\t\t\t\t}\n\t\t\t\tvisualViewport.addEventListener('resize', onViewportChange)\n\t\t\t\tvisualViewport.addEventListener('scroll', onViewportChange)\n\n\t\t\t\tif (editor) {\n\t\t\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\t\t\trInputRef.current?.scrollIntoView({ block: 'center' })\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\ttltime.requestAnimationFrame('anon', () => {\n\t\t\t\t\t\trInputRef.current?.scrollIntoView({ block: 'center' })\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\treturn () => {\n\t\t\t\t\tvisualViewport.removeEventListener('resize', onViewportChange)\n\t\t\t\t\tvisualViewport.removeEventListener('scroll', onViewportChange)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [isFocused, editor, shouldManuallyMaintainScrollPositionWhenFocused])\n\n\t\treturn (\n\t\t\t<div draggable={false} className=\"tlui-input__wrapper\">\n\t\t\t\t{children}\n\t\t\t\t{label && <label>{msg(label)}</label>}\n\t\t\t\t{iconLeft && (\n\t\t\t\t\t<TldrawUiIcon\n\t\t\t\t\t\tlabel={iconLabel ? msg(iconLabel) : ''}\n\t\t\t\t\t\ticon={iconLeft}\n\t\t\t\t\t\tclassName=\"tlui-icon-left\"\n\t\t\t\t\t\tsmall\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t<input\n\t\t\t\t\tref={rInputRef}\n\t\t\t\t\tclassName={classNames('tlui-input', className)}\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tdefaultValue={defaultValue}\n\t\t\t\t\tonKeyDownCapture={handleKeyDownCapture}\n\t\t\t\t\tonChange={handleChange}\n\t\t\t\t\tonFocus={handleFocus}\n\t\t\t\t\tonBlur={handleBlur}\n\t\t\t\t\tonCompositionStart={handleCompositionStart}\n\t\t\t\t\tonCompositionEnd={handleCompositionEnd}\n\t\t\t\t\tautoFocus={autoFocus}\n\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\tvalue={value}\n\t\t\t\t\tdata-testid={dataTestId}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t/>\n\t\t\t\t{icon && (\n\t\t\t\t\t<TldrawUiIcon label={iconLabel ? msg(iconLabel) : ''} icon={icon} small={!!label} />\n\t\t\t\t)}\n\t\t\t</div>\n\t\t)\n\t}\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["TldrawUiInput", "value"]
|
|
7
7
|
}
|
|
@@ -107,15 +107,16 @@ function TooltipSingleton() {
|
|
|
107
107
|
}, [cameraState, isOpen, currentTooltip, editor]);
|
|
108
108
|
useEffect(() => {
|
|
109
109
|
function handleKeyDown(event) {
|
|
110
|
-
if (event.key === "Escape" && currentTooltip) {
|
|
110
|
+
if (event.key === "Escape" && currentTooltip && isOpen) {
|
|
111
111
|
tooltipManager.hideTooltip(editor, currentTooltip.id);
|
|
112
|
+
event.stopPropagation();
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
115
|
+
document.addEventListener("keydown", handleKeyDown, { capture: true });
|
|
115
116
|
return () => {
|
|
116
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
117
|
+
document.removeEventListener("keydown", handleKeyDown, { capture: true });
|
|
117
118
|
};
|
|
118
|
-
}, [editor, currentTooltip]);
|
|
119
|
+
}, [editor, currentTooltip, isOpen]);
|
|
119
120
|
useEffect(() => {
|
|
120
121
|
let timer = null;
|
|
121
122
|
if (currentTooltip && triggerRef.current) {
|
|
@@ -212,8 +213,8 @@ const TldrawUiTooltip = forwardRef(
|
|
|
212
213
|
} else {
|
|
213
214
|
delayDurationToUse = delayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS);
|
|
214
215
|
}
|
|
215
|
-
if (!hasProvider) {
|
|
216
|
-
return /* @__PURE__ */ jsxs(_Tooltip.Root, { delayDuration: delayDurationToUse, disableHoverableContent:
|
|
216
|
+
if (!hasProvider || showUiLabels) {
|
|
217
|
+
return /* @__PURE__ */ jsxs(_Tooltip.Root, { delayDuration: delayDurationToUse, disableHoverableContent: !showUiLabels, children: [
|
|
217
218
|
/* @__PURE__ */ jsx(_Tooltip.Trigger, { asChild: true, ref, children }),
|
|
218
219
|
/* @__PURE__ */ jsxs(
|
|
219
220
|
_Tooltip.Content,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiTooltip.tsx"],
|
|
4
|
-
"sourcesContent": ["import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\ninterface CurrentTooltip {\n\tid: string\n\tcontent: ReactNode\n\tside: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset: number\n\tshowOnMobile: boolean\n\ttargetElement: HTMLElement\n\tdelayDuration: number\n}\n\n// Singleton tooltip manager\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate currentTooltip = atom<CurrentTooltip | null>('current tooltip', null)\n\tprivate destroyTimeoutId: number | null = null\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\tshowTooltip(\n\t\ttooltipId: string,\n\t\tcontent: string | React.ReactNode,\n\t\ttargetElement: HTMLElement,\n\t\tside: 'top' | 'right' | 'bottom' | 'left',\n\t\tsideOffset: number,\n\t\tshowOnMobile: boolean,\n\t\tdelayDuration: number\n\t) {\n\t\t// Clear any existing destroy timeout\n\t\tif (this.destroyTimeoutId) {\n\t\t\tclearTimeout(this.destroyTimeoutId)\n\t\t\tthis.destroyTimeoutId = null\n\t\t}\n\n\t\t// Update current tooltip\n\t\tthis.currentTooltip.set({\n\t\t\tid: tooltipId,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset,\n\t\t\tshowOnMobile,\n\t\t\ttargetElement,\n\t\t\tdelayDuration,\n\t\t})\n\t}\n\n\tupdateCurrentTooltip(tooltipId: string, update: (tooltip: CurrentTooltip) => CurrentTooltip) {\n\t\tthis.currentTooltip.update((tooltip) => {\n\t\t\tif (tooltip?.id === tooltipId) {\n\t\t\t\treturn update(tooltip)\n\t\t\t}\n\t\t\treturn tooltip\n\t\t})\n\t}\n\n\thideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {\n\t\tconst hide = () => {\n\t\t\t// Only hide if this is the current tooltip\n\t\t\tif (this.currentTooltip.get()?.id === tooltipId) {\n\t\t\t\tthis.currentTooltip.set(null)\n\t\t\t\tthis.destroyTimeoutId = null\n\t\t\t}\n\t\t}\n\n\t\tif (editor && !instant) {\n\t\t\t// Start destroy timeout (1 second)\n\t\t\tthis.destroyTimeoutId = editor.timers.setTimeout(hide, 300)\n\t\t} else {\n\t\t\thide()\n\t\t}\n\t}\n\n\thideAllTooltips() {\n\t\tthis.currentTooltip.set(null)\n\t\tthis.destroyTimeoutId = null\n\t}\n\n\tgetCurrentTooltipData() {\n\t\tconst currentTooltip = this.currentTooltip.get()\n\t\tif (!currentTooltip) return null\n\t\tif (!this.supportsHover() && !currentTooltip.showOnMobile) return null\n\t\treturn currentTooltip\n\t}\n\n\tprivate supportsHoverAtom: Atom<boolean> | null = null\n\tsupportsHover() {\n\t\tif (!this.supportsHoverAtom) {\n\t\t\tconst mediaQuery = window.matchMedia('(hover: hover)')\n\t\t\tconst supportsHover = atom('has hover', mediaQuery.matches)\n\t\t\tthis.supportsHoverAtom = supportsHover\n\t\t\tmediaQuery.addEventListener('change', (e) => {\n\t\t\t\tsupportsHover.set(e.matches)\n\t\t\t})\n\t\t}\n\t\treturn this.supportsHoverAtom.get()\n\t}\n}\n\nexport const tooltipManager = TooltipManager.getInstance()\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\tconst editor = useMaybeEditor()\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\tconst cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])\n\n\t// Hide tooltip when camera is moving (panning/zooming)\n\tuseEffect(() => {\n\t\tif (cameraState === 'moving' && isOpen && currentTooltip) {\n\t\t\ttooltipManager.hideTooltip(editor, currentTooltip.id, true)\n\t\t}\n\t}, [cameraState, isOpen, currentTooltip, editor])\n\n\tuseEffect(() => {\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (event.key === 'Escape' && currentTooltip) {\n\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltip.id)\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown)\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown)\n\t\t}\n\t}, [editor, currentTooltip])\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t}\n\t\t}\n\t}, [currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\t\tconst showUiLabels = useValue('showUiLabels', () => editor?.user.getShowUiLabels(), [editor])\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltipId, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\tuseLayoutEffect(() => {\n\t\t\tif (hasProvider && tooltipManager.getCurrentTooltipData()?.id === tooltipId.current) {\n\t\t\t\ttooltipManager.updateCurrentTooltip(tooltipId.current, (tooltip) => ({\n\t\t\t\t\t...tooltip,\n\t\t\t\t\tcontent,\n\t\t\t\t\tside: sideToUse,\n\t\t\t\t\tsideOffset,\n\t\t\t\t\tshowOnMobile,\n\t\t\t\t}))\n\t\t\t}\n\t\t}, [content, sideToUse, sideOffset, showOnMobile, hasProvider])\n\n\t\t// Don't show tooltip if disabled, no content, or UI labels are disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\tlet delayDurationToUse\n\t\tif (showUiLabels) {\n\t\t\tdelayDurationToUse = 0\n\t\t} else {\n\t\t\tdelayDurationToUse =\n\t\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\t\t}\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
|
|
5
|
-
"mappings": "AA+IG,
|
|
4
|
+
"sourcesContent": ["import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\ninterface CurrentTooltip {\n\tid: string\n\tcontent: ReactNode\n\tside: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset: number\n\tshowOnMobile: boolean\n\ttargetElement: HTMLElement\n\tdelayDuration: number\n}\n\n// Singleton tooltip manager\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate currentTooltip = atom<CurrentTooltip | null>('current tooltip', null)\n\tprivate destroyTimeoutId: number | null = null\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\tshowTooltip(\n\t\ttooltipId: string,\n\t\tcontent: string | React.ReactNode,\n\t\ttargetElement: HTMLElement,\n\t\tside: 'top' | 'right' | 'bottom' | 'left',\n\t\tsideOffset: number,\n\t\tshowOnMobile: boolean,\n\t\tdelayDuration: number\n\t) {\n\t\t// Clear any existing destroy timeout\n\t\tif (this.destroyTimeoutId) {\n\t\t\tclearTimeout(this.destroyTimeoutId)\n\t\t\tthis.destroyTimeoutId = null\n\t\t}\n\n\t\t// Update current tooltip\n\t\tthis.currentTooltip.set({\n\t\t\tid: tooltipId,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset,\n\t\t\tshowOnMobile,\n\t\t\ttargetElement,\n\t\t\tdelayDuration,\n\t\t})\n\t}\n\n\tupdateCurrentTooltip(tooltipId: string, update: (tooltip: CurrentTooltip) => CurrentTooltip) {\n\t\tthis.currentTooltip.update((tooltip) => {\n\t\t\tif (tooltip?.id === tooltipId) {\n\t\t\t\treturn update(tooltip)\n\t\t\t}\n\t\t\treturn tooltip\n\t\t})\n\t}\n\n\thideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {\n\t\tconst hide = () => {\n\t\t\t// Only hide if this is the current tooltip\n\t\t\tif (this.currentTooltip.get()?.id === tooltipId) {\n\t\t\t\tthis.currentTooltip.set(null)\n\t\t\t\tthis.destroyTimeoutId = null\n\t\t\t}\n\t\t}\n\n\t\tif (editor && !instant) {\n\t\t\t// Start destroy timeout (1 second)\n\t\t\tthis.destroyTimeoutId = editor.timers.setTimeout(hide, 300)\n\t\t} else {\n\t\t\thide()\n\t\t}\n\t}\n\n\thideAllTooltips() {\n\t\tthis.currentTooltip.set(null)\n\t\tthis.destroyTimeoutId = null\n\t}\n\n\tgetCurrentTooltipData() {\n\t\tconst currentTooltip = this.currentTooltip.get()\n\t\tif (!currentTooltip) return null\n\t\tif (!this.supportsHover() && !currentTooltip.showOnMobile) return null\n\t\treturn currentTooltip\n\t}\n\n\tprivate supportsHoverAtom: Atom<boolean> | null = null\n\tsupportsHover() {\n\t\tif (!this.supportsHoverAtom) {\n\t\t\tconst mediaQuery = window.matchMedia('(hover: hover)')\n\t\t\tconst supportsHover = atom('has hover', mediaQuery.matches)\n\t\t\tthis.supportsHoverAtom = supportsHover\n\t\t\tmediaQuery.addEventListener('change', (e) => {\n\t\t\t\tsupportsHover.set(e.matches)\n\t\t\t})\n\t\t}\n\t\treturn this.supportsHoverAtom.get()\n\t}\n}\n\nexport const tooltipManager = TooltipManager.getInstance()\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\tconst editor = useMaybeEditor()\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\tconst cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])\n\n\t// Hide tooltip when camera is moving (panning/zooming)\n\tuseEffect(() => {\n\t\tif (cameraState === 'moving' && isOpen && currentTooltip) {\n\t\t\ttooltipManager.hideTooltip(editor, currentTooltip.id, true)\n\t\t}\n\t}, [cameraState, isOpen, currentTooltip, editor])\n\n\tuseEffect(() => {\n\t\tfunction handleKeyDown(event: KeyboardEvent) {\n\t\t\tif (event.key === 'Escape' && currentTooltip && isOpen) {\n\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltip.id)\n\t\t\t\tevent.stopPropagation()\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener('keydown', handleKeyDown, { capture: true })\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', handleKeyDown, { capture: true })\n\t\t}\n\t}, [editor, currentTooltip, isOpen])\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t}\n\t\t}\n\t}, [currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\t\tconst showUiLabels = useValue('showUiLabels', () => editor?.user.getShowUiLabels(), [editor])\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltipId, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\tuseLayoutEffect(() => {\n\t\t\tif (hasProvider && tooltipManager.getCurrentTooltipData()?.id === tooltipId.current) {\n\t\t\t\ttooltipManager.updateCurrentTooltip(tooltipId.current, (tooltip) => ({\n\t\t\t\t\t...tooltip,\n\t\t\t\t\tcontent,\n\t\t\t\t\tside: sideToUse,\n\t\t\t\t\tsideOffset,\n\t\t\t\t\tshowOnMobile,\n\t\t\t\t}))\n\t\t\t}\n\t\t}, [content, sideToUse, sideOffset, showOnMobile, hasProvider])\n\n\t\t// Don't show tooltip if disabled, no content, or UI labels are disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\tlet delayDurationToUse\n\t\tif (showUiLabels) {\n\t\t\tdelayDurationToUse = 0\n\t\t} else {\n\t\t\tdelayDurationToUse =\n\t\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\t\t}\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider || showUiLabels) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent={!showUiLabels}>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "3.16.0-canary.
|
|
1
|
+
const version = "3.16.0-canary.bf7be9dad88b";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2024-09-13T14:36:29.063Z",
|
|
4
|
-
minor: "2025-09-
|
|
5
|
-
patch: "2025-09-
|
|
4
|
+
minor: "2025-09-17T15:01:00.589Z",
|
|
5
|
+
patch: "2025-09-17T15:01:00.589Z"
|
|
6
6
|
};
|
|
7
7
|
export {
|
|
8
8
|
publishDates,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/ui/version.ts"],
|
|
4
|
-
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.16.0-canary.
|
|
4
|
+
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.16.0-canary.bf7be9dad88b'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-09-17T15:01:00.589Z',\n\tpatch: '2025-09-17T15:01:00.589Z',\n}\n"],
|
|
5
5
|
"mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tldraw",
|
|
3
3
|
"description": "A tiny little drawing editor.",
|
|
4
|
-
"version": "3.16.0-canary.
|
|
4
|
+
"version": "3.16.0-canary.bf7be9dad88b",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"@tiptap/pm": "^2.9.1",
|
|
56
56
|
"@tiptap/react": "^2.9.1",
|
|
57
57
|
"@tiptap/starter-kit": "^2.9.1",
|
|
58
|
-
"@tldraw/editor": "3.16.0-canary.
|
|
59
|
-
"@tldraw/store": "3.16.0-canary.
|
|
58
|
+
"@tldraw/editor": "3.16.0-canary.bf7be9dad88b",
|
|
59
|
+
"@tldraw/store": "3.16.0-canary.bf7be9dad88b",
|
|
60
60
|
"classnames": "^2.5.1",
|
|
61
61
|
"hotkeys-js": "^3.13.9",
|
|
62
62
|
"idb": "^7.1.1",
|
|
@@ -2,9 +2,9 @@ import { preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'
|
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
3
3
|
import { useUiEvents } from '../../context/events'
|
|
4
4
|
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
|
5
|
+
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
|
5
6
|
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
|
6
7
|
import { TldrawUiInput } from '../primitives/TldrawUiInput'
|
|
7
|
-
import { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
|
|
8
8
|
|
|
9
9
|
/** @public */
|
|
10
10
|
export interface AltTextEditorProps {
|
|
@@ -77,7 +77,7 @@ export function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps)
|
|
|
77
77
|
disabled={isReadonly}
|
|
78
78
|
/>
|
|
79
79
|
{!isReadonly && (
|
|
80
|
-
<
|
|
80
|
+
<TldrawUiButton
|
|
81
81
|
title={msg('tool.media-alt-text-confirm')}
|
|
82
82
|
data-testid="tool.media-alt-text-confirm"
|
|
83
83
|
type="icon"
|
|
@@ -85,7 +85,7 @@ export function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps)
|
|
|
85
85
|
onClick={handleConfirm}
|
|
86
86
|
>
|
|
87
87
|
<TldrawUiButtonIcon small icon="check" />
|
|
88
|
-
</
|
|
88
|
+
</TldrawUiButton>
|
|
89
89
|
)}
|
|
90
90
|
</>
|
|
91
91
|
)
|
|
@@ -2,9 +2,9 @@ import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'
|
|
|
2
2
|
import { useEffect, useRef, useState } from 'react'
|
|
3
3
|
import { useUiEvents } from '../../context/events'
|
|
4
4
|
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
|
5
|
+
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
|
5
6
|
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
|
6
7
|
import { TldrawUiInput } from '../primitives/TldrawUiInput'
|
|
7
|
-
import { TldrawUiToolbarButton } from '../primitives/TldrawUiToolbar'
|
|
8
8
|
|
|
9
9
|
/** @public */
|
|
10
10
|
export interface LinkEditorProps {
|
|
@@ -77,7 +77,7 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
|
|
|
77
77
|
placeholder="example.com"
|
|
78
78
|
aria-label="example.com"
|
|
79
79
|
/>
|
|
80
|
-
<
|
|
80
|
+
<TldrawUiButton
|
|
81
81
|
className="tlui-rich-text__toolbar-link-visit"
|
|
82
82
|
title={msg('tool.rich-text-link-visit')}
|
|
83
83
|
type="icon"
|
|
@@ -86,8 +86,8 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
|
|
|
86
86
|
disabled={!value}
|
|
87
87
|
>
|
|
88
88
|
<TldrawUiButtonIcon small icon="external-link" />
|
|
89
|
-
</
|
|
90
|
-
<
|
|
89
|
+
</TldrawUiButton>
|
|
90
|
+
<TldrawUiButton
|
|
91
91
|
className="tlui-rich-text__toolbar-link-remove"
|
|
92
92
|
title={msg('tool.rich-text-link-remove')}
|
|
93
93
|
data-testid="rich-text.link-remove"
|
|
@@ -96,7 +96,7 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
|
|
|
96
96
|
onClick={handleRemoveLink}
|
|
97
97
|
>
|
|
98
98
|
<TldrawUiButtonIcon small icon="trash" />
|
|
99
|
-
</
|
|
99
|
+
</TldrawUiButton>
|
|
100
100
|
</>
|
|
101
101
|
)
|
|
102
102
|
}
|
|
@@ -101,7 +101,7 @@ export function OverflowingToolbar({
|
|
|
101
101
|
items: collectItems(child.children),
|
|
102
102
|
element: child as HTMLElement,
|
|
103
103
|
})
|
|
104
|
-
} else {
|
|
104
|
+
} else if (!child.hasAttribute('data-radix-popper-content-wrapper')) {
|
|
105
105
|
items.push({ type: 'item', element: child as HTMLElement })
|
|
106
106
|
}
|
|
107
107
|
}
|