tldraw 3.16.0-canary.1e59e27864bb → 3.16.0-canary.1efac4751756
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/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 +5 -4
- 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/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 +5 -4
- 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/primitives/TldrawUiInput.tsx +3 -0
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +5 -4
- package/src/lib/ui/version.ts +3 -3
package/dist-cjs/index.d.ts
CHANGED
package/dist-cjs/index.js
CHANGED
|
@@ -575,7 +575,7 @@ var import_buildFromV1Document = require("./lib/utils/tldr/buildFromV1Document")
|
|
|
575
575
|
var import_file = require("./lib/utils/tldr/file");
|
|
576
576
|
(0, import_editor.registerTldrawLibraryVersion)(
|
|
577
577
|
"tldraw",
|
|
578
|
-
"3.16.0-canary.
|
|
578
|
+
"3.16.0-canary.1efac4751756",
|
|
579
579
|
"cjs"
|
|
580
580
|
);
|
|
581
581
|
//# sourceMappingURL=index.js.map
|
|
@@ -26,9 +26,9 @@ var import_editor = require("@tldraw/editor");
|
|
|
26
26
|
var import_react = require("react");
|
|
27
27
|
var import_events = require("../../context/events");
|
|
28
28
|
var import_useTranslation = require("../../hooks/useTranslation/useTranslation");
|
|
29
|
+
var import_TldrawUiButton = require("../primitives/Button/TldrawUiButton");
|
|
29
30
|
var import_TldrawUiButtonIcon = require("../primitives/Button/TldrawUiButtonIcon");
|
|
30
31
|
var import_TldrawUiInput = require("../primitives/TldrawUiInput");
|
|
31
|
-
var import_TldrawUiToolbar = require("../primitives/TldrawUiToolbar");
|
|
32
32
|
function AltTextEditor({ shapeId, onClose, source }) {
|
|
33
33
|
const editor = (0, import_editor.useEditor)();
|
|
34
34
|
const [altText, setAltText] = (0, import_react.useState)(() => {
|
|
@@ -87,7 +87,7 @@ function AltTextEditor({ shapeId, onClose, source }) {
|
|
|
87
87
|
}
|
|
88
88
|
),
|
|
89
89
|
!isReadonly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
90
|
-
|
|
90
|
+
import_TldrawUiButton.TldrawUiButton,
|
|
91
91
|
{
|
|
92
92
|
title: msg("tool.media-alt-text-confirm"),
|
|
93
93
|
"data-testid": "tool.media-alt-text-confirm",
|
|
@@ -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": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiEE;AAjEF,oBAA8D;AAC9D,mBAAyD;AACzD,oBAA4B;AAC5B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;
|
|
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": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiEE;AAjEF,oBAA8D;AAC9D,mBAAyD;AACzD,oBAA4B;AAC5B,4BAA+B;AAC/B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;AAUvB,SAAS,cAAc,EAAE,SAAS,SAAS,OAAO,GAAuB;AAC/E,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,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,UAAM,sCAAe;AAC3B,QAAM,UAAM,qBAAyB,IAAI;AACzC,QAAM,iBAAa,2BAAY;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,0BAAsB,0BAAY,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC;AAElE,8BAAU,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,4EACC;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,sDAAC,gDAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KAEF;AAEF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -26,9 +26,9 @@ var import_editor = require("@tldraw/editor");
|
|
|
26
26
|
var import_react = require("react");
|
|
27
27
|
var import_events = require("../../context/events");
|
|
28
28
|
var import_useTranslation = require("../../hooks/useTranslation/useTranslation");
|
|
29
|
+
var import_TldrawUiButton = require("../primitives/Button/TldrawUiButton");
|
|
29
30
|
var import_TldrawUiButtonIcon = require("../primitives/Button/TldrawUiButtonIcon");
|
|
30
31
|
var import_TldrawUiInput = require("../primitives/TldrawUiInput");
|
|
31
|
-
var import_TldrawUiToolbar = require("../primitives/TldrawUiToolbar");
|
|
32
32
|
function LinkEditor({ textEditor, value: initialValue, onClose }) {
|
|
33
33
|
const editor = (0, import_editor.useEditor)();
|
|
34
34
|
const [value, setValue] = (0, import_react.useState)(initialValue);
|
|
@@ -84,7 +84,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
|
|
|
84
84
|
}
|
|
85
85
|
),
|
|
86
86
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
87
|
-
|
|
87
|
+
import_TldrawUiButton.TldrawUiButton,
|
|
88
88
|
{
|
|
89
89
|
className: "tlui-rich-text__toolbar-link-visit",
|
|
90
90
|
title: msg("tool.rich-text-link-visit"),
|
|
@@ -96,7 +96,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
|
|
|
96
96
|
}
|
|
97
97
|
),
|
|
98
98
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
99
|
-
|
|
99
|
+
import_TldrawUiButton.TldrawUiButton,
|
|
100
100
|
{
|
|
101
101
|
className: "tlui-rich-text__toolbar-link-remove",
|
|
102
102
|
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": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmEE;AAnEF,oBAAwD;AACxD,mBAA4C;AAC5C,oBAA4B;AAC5B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;
|
|
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": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmEE;AAnEF,oBAAwD;AACxD,mBAA4C;AAC5C,oBAA4B;AAC5B,4BAA+B;AAC/B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;AAUvB,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,YAAY;AAC/C,QAAM,UAAM,sCAAe;AAC3B,QAAM,UAAM,qBAAyB,IAAI;AACzC,QAAM,iBAAa,2BAAY;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,8BAAU,MAAM;AACf,QAAI,SAAS,MAAM;AAAA,EACpB,GAAG,CAAC,KAAK,CAAC;AAEV,8BAAU,MAAM;AACf,aAAS,YAAY;AAAA,EACtB,GAAG,CAAC,YAAY,CAAC;AAEjB,SACC,4EACC;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,sDAAC,gDAAmB,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,sDAAC,gDAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KACD;AAEF;",
|
|
6
6
|
"names": ["value"]
|
|
7
7
|
}
|
|
@@ -57,7 +57,8 @@ const TldrawUiInput = React.forwardRef(
|
|
|
57
57
|
children,
|
|
58
58
|
value,
|
|
59
59
|
"data-testid": dataTestId,
|
|
60
|
-
disabled
|
|
60
|
+
disabled,
|
|
61
|
+
"aria-label": ariaLabel
|
|
61
62
|
}, ref) {
|
|
62
63
|
const editor = (0, import_editor.useMaybeEditor)();
|
|
63
64
|
const rInputRef = React.useRef(null);
|
|
@@ -178,6 +179,7 @@ const TldrawUiInput = React.forwardRef(
|
|
|
178
179
|
onCompositionStart: handleCompositionStart,
|
|
179
180
|
onCompositionEnd: handleCompositionEnd,
|
|
180
181
|
autoFocus,
|
|
182
|
+
"aria-label": ariaLabel,
|
|
181
183
|
placeholder,
|
|
182
184
|
value,
|
|
183
185
|
"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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmLG;AAnLH,oBAA8C;AAC9C,wBAAuB;AACvB,YAAuB;AAEvB,4BAA+B;AAE/B,0BAA6B;AAmCtB,MAAM,gBAAgB,MAAM;AAAA,EAClC,SAASA,eACR;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,kDAAkD;AAAA,IAClD;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA,cAAc;AAAA,EACf,GACA,KACC;AACD,UAAM,aAAS,8BAAe;AAC9B,UAAM,YAAY,MAAM,OAAyB,IAAI;AAGrD,UAAM,oBAAoB,KAAK,MAAM,UAAU,OAA2B;AAE1E,UAAM,UAAM,sCAAe;AAC3B,UAAM,gBAAgB,MAAM,OAAe,gBAAgB,EAAE;AAC7D,UAAM,gBAAgB,MAAM,OAAe,gBAAgB,EAAE;AAE7D,UAAM,cAAc,MAAM,OAAO,KAAK;AAEtC,UAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,KAAK;AACtD,UAAM,cAAc,MAAM;AAAA,MACzB,CAAC,MAA0C;AAC1C,qBAAa,IAAI;AACjB,cAAM,MAAM,EAAE;AACd,sBAAc,UAAU,IAAI;AAC5B,YAAI,QAAQ;AACX,iBAAO,OAAO,sBAAsB,MAAM;AACzC,gBAAI,YAAY;AACf,kBAAI,OAAO;AAAA,YACZ;AAAA,UACD,CAAC;AAAA,QACF,OAAO;AACN,+BAAO,sBAAsB,QAAQ,MAAM;AAC1C,gBAAI,YAAY;AACf,kBAAI,OAAO;AAAA,YACZ;AAAA,UACD,CAAC;AAAA,QACF;AACA,kBAAU;AAAA,MACX;AAAA,MACA,CAAC,YAAY,QAAQ,OAAO;AAAA,IAC7B;AAEA,UAAM,eAAe,MAAM;AAAA,MAC1B,CAAC,MAA2C;AAC3C,cAAMC,SAAQ,EAAE,cAAc;AAC9B,sBAAc,UAAUA;AACxB,wBAAgBA,MAAK;AAAA,MACtB;AAAA,MACA,CAAC,aAAa;AAAA,IACf;AAGA,UAAM,uBAAuB,MAAM;AAAA,MAClC,CAAC,MAA6C;AAC7C,gBAAQ,EAAE,KAAK;AAAA,UACd,KAAK,SAAS;AAIb,gBAAI,YAAY,QAAS;AACzB,cAAE,cAAc,KAAK;AACrB,cAAE,gBAAgB;AAClB,yBAAa,EAAE,cAAc,KAAK;AAClC;AAAA,UACD;AAAA,UACA,KAAK,UAAU;AACd,cAAE,cAAc,QAAQ,cAAc;AACtC,uBAAW,EAAE,cAAc,KAAK;AAChC,cAAE,cAAc,KAAK;AACrB,cAAE,gBAAgB;AAClB;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,MACA,CAAC,YAAY,QAAQ;AAAA,IACtB;AAEA,UAAM,aAAa,MAAM;AAAA,MACxB,CAAC,MAA0C;AAC1C,qBAAa,KAAK;AAClB,cAAMA,SAAQ,EAAE,cAAc;AAC9B,iBAASA,MAAK;AAAA,MACf;AAAA,MACA,CAAC,MAAM;AAAA,IACR;AAEA,UAAM,yBAAyB,MAAM,YAAY,MAAO,YAAY,UAAU,MAAO,CAAC,CAAC;AACvF,UAAM,uBAAuB,MAAM,YAAY,MAAO,YAAY,UAAU,OAAQ,CAAC,CAAC;AAEtF,UAAM,UAAU,MAAM;AACrB,UAAI,CAAC,oBAAM,MAAO;AAElB,YAAM,iBAAiB,OAAO;AAC9B,UAAI,aAAa,mDAAmD,gBAAgB;AACnF,cAAM,mBAAmB,MAAM;AAC9B,oBAAU,SAAS,eAAe,EAAE,OAAO,SAAS,CAAC;AAAA,QACtD;AACA,uBAAe,iBAAiB,UAAU,gBAAgB;AAC1D,uBAAe,iBAAiB,UAAU,gBAAgB;AAE1D,YAAI,QAAQ;AACX,iBAAO,OAAO,sBAAsB,MAAM;AACzC,sBAAU,SAAS,eAAe,EAAE,OAAO,SAAS,CAAC;AAAA,UACtD,CAAC;AAAA,QACF,OAAO;AACN,+BAAO,sBAAsB,QAAQ,MAAM;AAC1C,sBAAU,SAAS,eAAe,EAAE,OAAO,SAAS,CAAC;AAAA,UACtD,CAAC;AAAA,QACF;AAEA,eAAO,MAAM;AACZ,yBAAe,oBAAoB,UAAU,gBAAgB;AAC7D,yBAAe,oBAAoB,UAAU,gBAAgB;AAAA,QAC9D;AAAA,MACD;AAAA,IACD,GAAG,CAAC,WAAW,QAAQ,+CAA+C,CAAC;AAEvE,WACC,6CAAC,SAAI,WAAW,OAAO,WAAU,uBAC/B;AAAA;AAAA,MACA,SAAS,4CAAC,WAAO,cAAI,KAAK,GAAE;AAAA,MAC5B,YACA;AAAA,QAAC;AAAA;AAAA,UACA,OAAO,YAAY,IAAI,SAAS,IAAI;AAAA,UACpC,MAAM;AAAA,UACN,WAAU;AAAA,UACV,OAAK;AAAA;AAAA,MACN;AAAA,MAED;AAAA,QAAC;AAAA;AAAA,UACA,KAAK;AAAA,UACL,eAAW,kBAAAC,SAAW,cAAc,SAAS;AAAA,UAC7C,MAAK;AAAA,UACL;AAAA,UACA,kBAAkB;AAAA,UAClB,UAAU;AAAA,UACV,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,oBAAoB;AAAA,UACpB,kBAAkB;AAAA,UAClB;AAAA,UACA,cAAY;AAAA,UACZ;AAAA,UACA;AAAA,UACA,eAAa;AAAA,UACb;AAAA;AAAA,MACD;AAAA,MACC,QACA,4CAAC,oCAAa,OAAO,YAAY,IAAI,SAAS,IAAI,IAAI,MAAY,OAAO,CAAC,CAAC,OAAO;AAAA,OAEpF;AAAA,EAEF;AACD;",
|
|
6
6
|
"names": ["TldrawUiInput", "value", "classNames"]
|
|
7
7
|
}
|
|
@@ -134,15 +134,16 @@ function TooltipSingleton() {
|
|
|
134
134
|
}, [cameraState, isOpen, currentTooltip, editor]);
|
|
135
135
|
(0, import_react.useEffect)(() => {
|
|
136
136
|
function handleKeyDown(event) {
|
|
137
|
-
if (event.key === "Escape" && currentTooltip) {
|
|
137
|
+
if (event.key === "Escape" && currentTooltip && isOpen) {
|
|
138
138
|
tooltipManager.hideTooltip(editor, currentTooltip.id);
|
|
139
|
+
event.stopPropagation();
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
142
|
+
document.addEventListener("keydown", handleKeyDown, { capture: true });
|
|
142
143
|
return () => {
|
|
143
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
144
|
+
document.removeEventListener("keydown", handleKeyDown, { capture: true });
|
|
144
145
|
};
|
|
145
|
-
}, [editor, currentTooltip]);
|
|
146
|
+
}, [editor, currentTooltip, isOpen]);
|
|
146
147
|
(0, import_react.useEffect)(() => {
|
|
147
148
|
let timer = null;
|
|
148
149
|
if (currentTooltip && triggerRef.current) {
|
|
@@ -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": "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
|
|
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) {\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": "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",
|
|
6
6
|
"names": ["_Tooltip", "React"]
|
|
7
7
|
}
|
|
@@ -22,10 +22,10 @@ __export(version_exports, {
|
|
|
22
22
|
version: () => version
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(version_exports);
|
|
25
|
-
const version = "3.16.0-canary.
|
|
25
|
+
const version = "3.16.0-canary.1efac4751756";
|
|
26
26
|
const publishDates = {
|
|
27
27
|
major: "2024-09-13T14:36:29.063Z",
|
|
28
|
-
minor: "2025-09-
|
|
29
|
-
patch: "2025-09-
|
|
28
|
+
minor: "2025-09-16T22:07:50.660Z",
|
|
29
|
+
patch: "2025-09-16T22:07:50.660Z"
|
|
30
30
|
};
|
|
31
31
|
//# sourceMappingURL=version.js.map
|
|
@@ -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.1efac4751756'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-09-16T22:07:50.660Z',\n\tpatch: '2025-09-16T22:07:50.660Z',\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/index.d.mts
CHANGED
package/dist-esm/index.mjs
CHANGED
|
@@ -3,9 +3,9 @@ import { preventDefault, useEditor } from "@tldraw/editor";
|
|
|
3
3
|
import { useCallback, 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 AltTextEditor({ shapeId, onClose, source }) {
|
|
10
10
|
const editor = useEditor();
|
|
11
11
|
const [altText, setAltText] = useState(() => {
|
|
@@ -64,7 +64,7 @@ function AltTextEditor({ shapeId, onClose, source }) {
|
|
|
64
64
|
}
|
|
65
65
|
),
|
|
66
66
|
!isReadonly && /* @__PURE__ */ jsx(
|
|
67
|
-
|
|
67
|
+
TldrawUiButton,
|
|
68
68
|
{
|
|
69
69
|
title: msg("tool.media-alt-text-confirm"),
|
|
70
70
|
"data-testid": "tool.media-alt-text-confirm",
|
|
@@ -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
|
}
|
|
@@ -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) {
|
|
@@ -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) {\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": "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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.1efac4751756";
|
|
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-16T22:07:50.660Z",
|
|
5
|
+
patch: "2025-09-16T22:07:50.660Z"
|
|
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.1efac4751756'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-09-16T22:07:50.660Z',\n\tpatch: '2025-09-16T22:07:50.660Z',\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.1efac4751756",
|
|
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.1efac4751756",
|
|
59
|
+
"@tldraw/store": "3.16.0-canary.1efac4751756",
|
|
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
|
}
|
|
@@ -35,6 +35,7 @@ export interface TLUiInputProps {
|
|
|
35
35
|
shouldManuallyMaintainScrollPositionWhenFocused?: boolean
|
|
36
36
|
value?: string
|
|
37
37
|
'data-testid'?: string
|
|
38
|
+
'aria-label'?: string
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
/** @public @react */
|
|
@@ -60,6 +61,7 @@ export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
|
|
|
60
61
|
value,
|
|
61
62
|
'data-testid': dataTestId,
|
|
62
63
|
disabled,
|
|
64
|
+
'aria-label': ariaLabel,
|
|
63
65
|
},
|
|
64
66
|
ref
|
|
65
67
|
) {
|
|
@@ -198,6 +200,7 @@ export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
|
|
|
198
200
|
onCompositionStart={handleCompositionStart}
|
|
199
201
|
onCompositionEnd={handleCompositionEnd}
|
|
200
202
|
autoFocus={autoFocus}
|
|
203
|
+
aria-label={ariaLabel}
|
|
201
204
|
placeholder={placeholder}
|
|
202
205
|
value={value}
|
|
203
206
|
data-testid={dataTestId}
|
|
@@ -173,16 +173,17 @@ function TooltipSingleton() {
|
|
|
173
173
|
|
|
174
174
|
useEffect(() => {
|
|
175
175
|
function handleKeyDown(event: KeyboardEvent) {
|
|
176
|
-
if (event.key === 'Escape' && currentTooltip) {
|
|
176
|
+
if (event.key === 'Escape' && currentTooltip && isOpen) {
|
|
177
177
|
tooltipManager.hideTooltip(editor, currentTooltip.id)
|
|
178
|
+
event.stopPropagation()
|
|
178
179
|
}
|
|
179
180
|
}
|
|
180
181
|
|
|
181
|
-
document.addEventListener('keydown', handleKeyDown)
|
|
182
|
+
document.addEventListener('keydown', handleKeyDown, { capture: true })
|
|
182
183
|
return () => {
|
|
183
|
-
document.removeEventListener('keydown', handleKeyDown)
|
|
184
|
+
document.removeEventListener('keydown', handleKeyDown, { capture: true })
|
|
184
185
|
}
|
|
185
|
-
}, [editor, currentTooltip])
|
|
186
|
+
}, [editor, currentTooltip, isOpen])
|
|
186
187
|
|
|
187
188
|
// Update open state and trigger position
|
|
188
189
|
useEffect(() => {
|
package/src/lib/ui/version.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// This file is automatically generated by internal/scripts/refresh-assets.ts.
|
|
2
2
|
// Do not edit manually. Or do, I'm a comment, not a cop.
|
|
3
3
|
|
|
4
|
-
export const version = '3.16.0-canary.
|
|
4
|
+
export const version = '3.16.0-canary.1efac4751756'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '2025-09-
|
|
8
|
-
patch: '2025-09-
|
|
7
|
+
minor: '2025-09-16T22:07:50.660Z',
|
|
8
|
+
patch: '2025-09-16T22:07:50.660Z',
|
|
9
9
|
}
|