tldraw 5.4.0 → 5.4.1
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/DOCS.md +2 -1
- package/RELEASE_NOTES.md +20 -2
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js +2 -1
- package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js.map +3 -3
- package/dist-cjs/lib/ui/version.js +2 -2
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs +9 -2
- package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +2 -2
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/package.json +6 -6
- package/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx +20 -3
- package/src/lib/ui/version.ts +2 -2
- package/src/test/ui/ContextMenu.test.tsx +29 -0
package/DOCS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# tldraw Documentation
|
|
2
2
|
|
|
3
|
-
Version: `5.4.
|
|
3
|
+
Version: `5.4.1`
|
|
4
4
|
|
|
5
5
|
This file is generated during package publishing from the tldraw docs content for this exact package version.
|
|
6
6
|
|
|
@@ -383,6 +383,7 @@ We ship an experimental `tldraw-migrate` agent skill that helps a coding agent u
|
|
|
383
383
|
|
|
384
384
|
#### v5.x
|
|
385
385
|
|
|
386
|
+
- [v5.4](https://tldraw.dev/releases/v5.4.0) - A center align operation, a tleditors registry of mounted editors, faster signals and store layers, and a broad reliability pass across the SDK
|
|
386
387
|
- [v5.3](https://tldraw.dev/releases/v5.3.0) - Commenting with anchored threads on the canvas, new collaboration packages, geo shape flipping, crop snapping, and faster viewport culling
|
|
387
388
|
- [v5.2](https://tldraw.dev/releases/v5.2.0) - Faster freehand ink, a frame selection action, SDK-wide performance improvements, and breaking changes to the collaborator user-id types and Node support
|
|
388
389
|
- [v5.1](https://tldraw.dev/releases/v5.1.0) - Page menu redesign, copy-styles shortcut, selectable locked shapes, public translation APIs, and performance improvements
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
-
Version: `5.4.
|
|
3
|
+
Version: `5.4.1`
|
|
4
4
|
|
|
5
5
|
This file is generated during package publishing from the tldraw release notes content for this exact package version.
|
|
6
6
|
|
|
7
|
+
## v5.4.1
|
|
8
|
+
|
|
9
|
+
So far this release is a set of fixes: camera coasting and edge scrolling now move at the same rate on high-refresh displays, rotated images stay put when a crop changes their size, and embed hostname matching no longer trusts lookalike hosts.
|
|
10
|
+
|
|
11
|
+
### Bug fixes
|
|
12
|
+
|
|
13
|
+
- Fix camera coasting after a fling, and edge scrolling, running roughly twice as fast on a 120 Hz display as on a 60 Hz one. Both now scale by the elapsed tick time, so motion per second is the same at any refresh rate. ([#10501](https://github.com/tldraw/tldraw/pull/10501))
|
|
14
|
+
- Fix rotated images jumping on the canvas when their aspect ratio or crop zoom changed, or when the image was replaced. ([#10233](https://github.com/tldraw/tldraw/pull/10233))
|
|
15
|
+
- Fix embed hostname matching trusting lookalike hosts such as `tldraw.com.evil.io`, which were rendered with the matched definition's relaxed iframe permissions. A host must now be the pattern itself or a subdomain of it. ([#10196](https://github.com/tldraw/tldraw/pull/10196))
|
|
16
|
+
- Fix geometry hit tests, nearest-point, and length calculations losing precision after a shape's SVG path data was generated. `getSvgPathData` rounded the geometry's stored points to two decimals in place, and geometries are cached per shape, so every later calculation used the rounded points. ([#10600](https://github.com/tldraw/tldraw/pull/10600))
|
|
17
|
+
- Fix keyboard shape navigation, and `Editor.selectAdjacentShape('prev')`, selecting the second-to-last shape instead of the last one when nothing is selected. ([#10601](https://github.com/tldraw/tldraw/pull/10601))
|
|
18
|
+
- Fix `Editor.getCurrentTheme()` returning `undefined` after `setCurrentTheme()` was called with an unregistered theme id. The call is now rejected and the current theme is kept. ([#10602](https://github.com/tldraw/tldraw/pull/10602))
|
|
19
|
+
- Fix `Group2d.transform` dropping its ignored children, resetting the `isInternal`, `excludeFromShapeBounds`, and `isEmptyLabel` flags, and discarding its `opts` argument. It now matches the base `Geometry2d.transform` signature. ([#10603](https://github.com/tldraw/tldraw/pull/10603))
|
|
20
|
+
|
|
7
21
|
## v5.4.0
|
|
8
22
|
|
|
23
|
+
[View on GitHub](https://github.com/tldraw/tldraw/releases/tag/v5.4.0)
|
|
24
|
+
|
|
9
25
|
This release adds a `center` operation to `Editor.alignShapes`, a `tleditors` registry of mounted editors, `iterateGraphemes` to `@tldraw/utils`, and two new reactive methods on `AtomMap`. It also deprecates `useViewportHeight`, speeds up the signals and store layers, and lands a broad reliability pass across the SDK, with over a hundred fixes to undo history, selection and tool interactions, cropping, arrows, the signals and store layers, sync, and local persistence.
|
|
10
26
|
|
|
11
27
|
### API changes
|
|
@@ -32,6 +48,7 @@ This release adds a `center` operation to `Editor.alignShapes`, a `tleditors` re
|
|
|
32
48
|
- Reduce per-pointer-event allocations in `Group2d` and `Editor` hit-testing. ([#8600](https://github.com/tldraw/tldraw/pull/8600))
|
|
33
49
|
- Speed up signal reads in `@tldraw/state`. Capturing a parent used to scan the capturing signal's parent set twice, once to check for the parent and again to add it. ([#9690](https://github.com/tldraw/tldraw/pull/9690))
|
|
34
50
|
- Reduce per-signal memory use and per-flush allocations in `@tldraw/state`. ([#10155](https://github.com/tldraw/tldraw/pull/10155))
|
|
51
|
+
- Speed up editor hot paths: set lookups replace array scans in the selection side effect and the erasing check, `getCollaborators` makes a single pass over the presence records, and `getShapeUtil` only builds its assert message when the lookup fails. ([#10358](https://github.com/tldraw/tldraw/pull/10358))
|
|
35
52
|
- Reduce the work and allocations in `@tldraw/store` history bookkeeping, so squashing record diffs and filtering query history no longer build intermediate copies on every tick. ([#9689](https://github.com/tldraw/tldraw/pull/9689), [#9869](https://github.com/tldraw/tldraw/pull/9869))
|
|
36
53
|
- Move the `exportBackground` default (`true`) into the `TLInstance` record type in `@tldraw/tlschema`, so every code path that creates the instance record gets the same value. ([#10534](https://github.com/tldraw/tldraw/pull/10534))
|
|
37
54
|
|
|
@@ -150,7 +167,8 @@ This release adds a `center` operation to `Editor.alignShapes`, a `tleditors` re
|
|
|
150
167
|
- Fix the spacing around the cursor chat item in the context menu. ([#9921](https://github.com/tldraw/tldraw/pull/9921))
|
|
151
168
|
- Fix the people menu's collaborator rows not lining up with your own row above them: the follow icon sat too close to the right edge, and collaborator names sat slightly left of your own. ([#9900](https://github.com/tldraw/tldraw/pull/9900))
|
|
152
169
|
- Fix the comment thread popover briefly rendering with a doubled shadow the first time a thread is opened. ([#9886](https://github.com/tldraw/tldraw/pull/9886))
|
|
153
|
-
|
|
170
|
+
|
|
171
|
+
#### Persistence and sync
|
|
154
172
|
|
|
155
173
|
- Fix edits made in the last 350 ms before closing or reloading a tab being lost from local persistence. ([#10526](https://github.com/tldraw/tldraw/pull/10526))
|
|
156
174
|
- Fix background tabs repeatedly dropping and re-establishing their sync connection when the browser throttles timers. ([#9964](https://github.com/tldraw/tldraw/pull/9964))
|
package/dist-cjs/index.js
CHANGED
|
@@ -653,7 +653,7 @@ var import_buildFromV1Document = require("./lib/utils/tldr/buildFromV1Document")
|
|
|
653
653
|
var import_file = require("./lib/utils/tldr/file");
|
|
654
654
|
(0, import_editor.registerTldrawLibraryVersion)(
|
|
655
655
|
"tldraw",
|
|
656
|
-
"5.4.
|
|
656
|
+
"5.4.1",
|
|
657
657
|
"cjs"
|
|
658
658
|
);
|
|
659
659
|
//# sourceMappingURL=index.js.map
|
|
@@ -30,6 +30,7 @@ var import_useMenuIsOpen = require("../../hooks/useMenuIsOpen");
|
|
|
30
30
|
var import_useTranslation = require("../../hooks/useTranslation/useTranslation");
|
|
31
31
|
var import_TldrawUiMenuContext = require("../primitives/menus/TldrawUiMenuContext");
|
|
32
32
|
var import_DefaultContextMenuContent = require("./DefaultContextMenuContent");
|
|
33
|
+
const ContextMenuRoot = import_radix_ui.ContextMenu.Root;
|
|
33
34
|
const DefaultContextMenu = (0, import_react.memo)(function DefaultContextMenu2({
|
|
34
35
|
children,
|
|
35
36
|
disabled = false
|
|
@@ -105,7 +106,7 @@ const DefaultContextMenu = (0, import_react.memo)(function DefaultContextMenu2({
|
|
|
105
106
|
const dir = (0, import_useTranslation.useDirection)();
|
|
106
107
|
const [isOpen, handleOpenChange] = (0, import_useMenuIsOpen.useMenuIsOpen)("context menu", cb);
|
|
107
108
|
const content = children ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_DefaultContextMenuContent.DefaultContextMenuContent, {});
|
|
108
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
109
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ContextMenuRoot, { dir, open: isOpen, onOpenChange: handleOpenChange, modal: false, children: [
|
|
109
110
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
110
111
|
import_radix_ui.ContextMenu.Trigger,
|
|
111
112
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\tpreventDefault,\n\tuseContainer,\n\tuseEditor,\n\tuseEditorComponents,\n\tuseValue,\n} from '@tldraw/editor'\nimport { ContextMenu as _ContextMenu } from 'radix-ui'\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
6
|
-
"names": ["
|
|
4
|
+
"sourcesContent": ["import {\n\tpreventDefault,\n\tuseContainer,\n\tuseEditor,\n\tuseEditorComponents,\n\tuseValue,\n} from '@tldraw/editor'\nimport { ContextMenu as _ContextMenu } from 'radix-ui'\nimport {\n\tComponentProps,\n\tComponentType,\n\tReactNode,\n\tmemo,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n} from 'react'\nimport { ContextMenuPagePointContext } from '../../context/actions'\nimport { useMenuIsOpen } from '../../hooks/useMenuIsOpen'\nimport { useDirection, useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'\nimport { DefaultContextMenuContent } from './DefaultContextMenuContent'\n\n// Controlled `open`: tldraw can close the menu itself (MenuClickCapture's\n// clearOpenMenus), and radix >=2.3.0 only fires onOpenChange on real state\n// changes \u2014 a stale internal `open` would swallow every later right-click.\n// radix <2.3 ignores the prop and omits it from its types, hence the widening.\nconst ContextMenuRoot = _ContextMenu.Root as ComponentType<\n\tComponentProps<typeof _ContextMenu.Root> & { open?: boolean }\n>\n\n/** @public */\nexport interface TLUiContextMenuProps {\n\tchildren?: ReactNode\n\tdisabled?: boolean\n}\n\n/** @public @react */\nexport const DefaultContextMenu = memo(function DefaultContextMenu({\n\tchildren,\n\tdisabled = false,\n}: TLUiContextMenuProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\n\tconst { Canvas } = useEditorComponents()\n\n\t// The context menu opens from a right-click in any tool, and from a touch\n\t// long-press only in the select tool (where it acts on the selection). A\n\t// right-click (fine pointer) is routed through the select tool by the editor; a\n\t// long-press in any other tool belongs to that tool's gesture (creating, erasing,\n\t// drawing, panning), so it opens nothing.\n\t//\n\t// The right-click case keys off the pointer type, not the tool, on purpose: a\n\t// right-click switches to select synchronously within the same gesture, so gating\n\t// it on the tool would race the React render and wrongly suppress the menu. The\n\t// select-tool long-press case is safe to gate on the tool \u2014 a long-press never\n\t// switches tools, so there is nothing to race.\n\tconst menuCanOpen = useValue(\n\t\t'context menu can open',\n\t\t() => !editor.getInstanceState().isCoarsePointer || editor.isIn('select'),\n\t\t[editor]\n\t)\n\n\t// When hitting `Escape` while the context menu is open, we want to prevent\n\t// the default behavior of losing focus on the shape. Otherwise,\n\t// it's pretty annoying from an accessibility perspective.\n\tconst preventEscapeFromLosingShapeFocus = useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\te.stopPropagation()\n\t\t\t\teditor.getContainer().focus()\n\t\t\t}\n\t\t},\n\t\t[editor]\n\t)\n\n\tuseEffect(() => {\n\t\tconst body = editor.getContainerDocument().body\n\t\treturn () => {\n\t\t\tbody.removeEventListener('keydown', preventEscapeFromLosingShapeFocus, {\n\t\t\t\tcapture: true,\n\t\t\t})\n\t\t}\n\t}, [editor, preventEscapeFromLosingShapeFocus])\n\n\t// On touch devices, the same touch that triggers Radix's long-press open is still\n\t// down when the menu mounts. The release fires events the dismissable layer treats\n\t// as an outside interaction and closes the menu. We swallow dismissals during a\n\t// short grace window after open so the menu stays put until the user actually\n\t// interacts again.\n\tconst suppressDismissUntilRef = useRef(0)\n\n\tconst rContextMenuPagePoint = useContext(ContextMenuPagePointContext)\n\n\tconst cb = useCallback(\n\t\t(isOpen: boolean) => {\n\t\t\tconst body = editor.getContainerDocument().body\n\t\t\tif (rContextMenuPagePoint) {\n\t\t\t\trContextMenuPagePoint.current = isOpen ? editor.inputs.getCurrentPagePoint().clone() : null\n\t\t\t}\n\t\t\tif (!isOpen) {\n\t\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\n\t\t\t\tif (onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)) {\n\t\t\t\t\teditor.setSelectedShapes([])\n\t\t\t\t}\n\n\t\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\t\tbody.removeEventListener('keydown', preventEscapeFromLosingShapeFocus, {\n\t\t\t\t\t\tcapture: true,\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tbody.addEventListener('keydown', preventEscapeFromLosingShapeFocus, {\n\t\t\t\t\tcapture: true,\n\t\t\t\t})\n\n\t\t\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\t\t\tsuppressDismissUntilRef.current = Date.now() + 500\n\n\t\t\t\t\t// Weird route: selecting locked shapes on long press\n\t\t\t\t\tconst selectedShapes = editor.getSelectedShapes()\n\t\t\t\t\tconst currentPagePoint = editor.inputs.getCurrentPagePoint()\n\n\t\t\t\t\t// get all of the shapes under the current pointer\n\t\t\t\t\tconst shapesAtPoint = editor.getShapesAtPoint(currentPagePoint)\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t// if there are no selected shapes\n\t\t\t\t\t\t!editor.getSelectedShapes().length ||\n\t\t\t\t\t\t// OR if none of the shapes at the point include the selected shape\n\t\t\t\t\t\t!shapesAtPoint.some((s) => selectedShapes.includes(s))\n\t\t\t\t\t) {\n\t\t\t\t\t\t// then are there any locked shapes under the current pointer?\n\t\t\t\t\t\tconst lockedShapes = shapesAtPoint.filter((s) => editor.isShapeOrAncestorLocked(s))\n\n\t\t\t\t\t\tif (lockedShapes.length) {\n\t\t\t\t\t\t\t// nice, let's select them\n\t\t\t\t\t\t\teditor.select(...lockedShapes.map((s) => s.id))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor, preventEscapeFromLosingShapeFocus, rContextMenuPagePoint]\n\t)\n\n\tconst container = useContainer()\n\tconst dir = useDirection()\n\tconst [isOpen, handleOpenChange] = useMenuIsOpen('context menu', cb)\n\n\t// Get the context menu content, either the default component or the user's\n\t// override. If there's no menu content, then the user has set it to null,\n\t// so skip rendering the menu.\n\tconst content = children ?? <DefaultContextMenuContent />\n\n\treturn (\n\t\t<ContextMenuRoot dir={dir} open={isOpen} onOpenChange={handleOpenChange} modal={false}>\n\t\t\t<_ContextMenu.Trigger\n\t\t\t\t// When suppressed, disabling the trigger stops Radix from opening the\n\t\t\t\t// menu, but it also stops Radix from preventing the native contextmenu \u2014\n\t\t\t\t// so prevent the browser's own menu ourselves in that case.\n\t\t\t\tonContextMenu={menuCanOpen ? undefined : preventDefault}\n\t\t\t\tdir=\"ltr\"\n\t\t\t\tdisabled={disabled || !menuCanOpen}\n\t\t\t>\n\t\t\t\t{Canvas ? <Canvas /> : null}\n\t\t\t</_ContextMenu.Trigger>\n\t\t\t{isOpen && (\n\t\t\t\t<_ContextMenu.Portal container={container}>\n\t\t\t\t\t<_ContextMenu.Content\n\t\t\t\t\t\tclassName=\"tlui-menu tlui-scrollable\"\n\t\t\t\t\t\tdata-testid=\"context-menu\"\n\t\t\t\t\t\taria-label={msg('context-menu.title')}\n\t\t\t\t\t\talignOffset={-4}\n\t\t\t\t\t\tcollisionPadding={4}\n\t\t\t\t\t\tonContextMenu={preventDefault}\n\t\t\t\t\t\tonPointerDownOutside={(e) => {\n\t\t\t\t\t\t\tif (Date.now() < suppressDismissUntilRef.current) e.preventDefault()\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonInteractOutside={(e) => {\n\t\t\t\t\t\t\tif (Date.now() < suppressDismissUntilRef.current) e.preventDefault()\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonFocusOutside={(e) => {\n\t\t\t\t\t\t\tif (Date.now() < suppressDismissUntilRef.current) e.preventDefault()\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<TldrawUiMenuContextProvider type=\"context-menu\" sourceId=\"context-menu\">\n\t\t\t\t\t\t\t{content}\n\t\t\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t\t\t</_ContextMenu.Content>\n\t\t\t\t</_ContextMenu.Portal>\n\t\t\t)}\n\t\t</ContextMenuRoot>\n\t)\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["_ContextMenu", "DefaultContextMenu", "isOpen"]
|
|
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 = "5.4.
|
|
25
|
+
const version = "5.4.1";
|
|
26
26
|
const publishDates = {
|
|
27
27
|
major: "2026-05-06T16:28:18.473Z",
|
|
28
28
|
minor: "2026-09-02T11:04:02.901Z",
|
|
29
|
-
patch: "2026-09-
|
|
29
|
+
patch: "2026-09-08T10:52:09.132Z"
|
|
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 = '5.4.
|
|
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 = '5.4.1'\nexport const publishDates = {\n\tmajor: '2026-05-06T16:28:18.473Z',\n\tminor: '2026-09-02T11:04:02.901Z',\n\tpatch: '2026-09-08T10:52:09.132Z',\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.mjs
CHANGED
|
@@ -7,12 +7,19 @@ import {
|
|
|
7
7
|
useValue
|
|
8
8
|
} from "@tldraw/editor";
|
|
9
9
|
import { ContextMenu as _ContextMenu } from "radix-ui";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
memo,
|
|
12
|
+
useCallback,
|
|
13
|
+
useContext,
|
|
14
|
+
useEffect,
|
|
15
|
+
useRef
|
|
16
|
+
} from "react";
|
|
11
17
|
import { ContextMenuPagePointContext } from "../../context/actions.mjs";
|
|
12
18
|
import { useMenuIsOpen } from "../../hooks/useMenuIsOpen.mjs";
|
|
13
19
|
import { useDirection, useTranslation } from "../../hooks/useTranslation/useTranslation.mjs";
|
|
14
20
|
import { TldrawUiMenuContextProvider } from "../primitives/menus/TldrawUiMenuContext.mjs";
|
|
15
21
|
import { DefaultContextMenuContent } from "./DefaultContextMenuContent.mjs";
|
|
22
|
+
const ContextMenuRoot = _ContextMenu.Root;
|
|
16
23
|
const DefaultContextMenu = memo(function DefaultContextMenu2({
|
|
17
24
|
children,
|
|
18
25
|
disabled = false
|
|
@@ -88,7 +95,7 @@ const DefaultContextMenu = memo(function DefaultContextMenu2({
|
|
|
88
95
|
const dir = useDirection();
|
|
89
96
|
const [isOpen, handleOpenChange] = useMenuIsOpen("context menu", cb);
|
|
90
97
|
const content = children ?? /* @__PURE__ */ jsx(DefaultContextMenuContent, {});
|
|
91
|
-
return /* @__PURE__ */ jsxs(
|
|
98
|
+
return /* @__PURE__ */ jsxs(ContextMenuRoot, { dir, open: isOpen, onOpenChange: handleOpenChange, modal: false, children: [
|
|
92
99
|
/* @__PURE__ */ jsx(
|
|
93
100
|
_ContextMenu.Trigger,
|
|
94
101
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\tpreventDefault,\n\tuseContainer,\n\tuseEditor,\n\tuseEditorComponents,\n\tuseValue,\n} from '@tldraw/editor'\nimport { ContextMenu as _ContextMenu } from 'radix-ui'\nimport {
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import {\n\tpreventDefault,\n\tuseContainer,\n\tuseEditor,\n\tuseEditorComponents,\n\tuseValue,\n} from '@tldraw/editor'\nimport { ContextMenu as _ContextMenu } from 'radix-ui'\nimport {\n\tComponentProps,\n\tComponentType,\n\tReactNode,\n\tmemo,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n} from 'react'\nimport { ContextMenuPagePointContext } from '../../context/actions'\nimport { useMenuIsOpen } from '../../hooks/useMenuIsOpen'\nimport { useDirection, useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'\nimport { DefaultContextMenuContent } from './DefaultContextMenuContent'\n\n// Controlled `open`: tldraw can close the menu itself (MenuClickCapture's\n// clearOpenMenus), and radix >=2.3.0 only fires onOpenChange on real state\n// changes \u2014 a stale internal `open` would swallow every later right-click.\n// radix <2.3 ignores the prop and omits it from its types, hence the widening.\nconst ContextMenuRoot = _ContextMenu.Root as ComponentType<\n\tComponentProps<typeof _ContextMenu.Root> & { open?: boolean }\n>\n\n/** @public */\nexport interface TLUiContextMenuProps {\n\tchildren?: ReactNode\n\tdisabled?: boolean\n}\n\n/** @public @react */\nexport const DefaultContextMenu = memo(function DefaultContextMenu({\n\tchildren,\n\tdisabled = false,\n}: TLUiContextMenuProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\n\tconst { Canvas } = useEditorComponents()\n\n\t// The context menu opens from a right-click in any tool, and from a touch\n\t// long-press only in the select tool (where it acts on the selection). A\n\t// right-click (fine pointer) is routed through the select tool by the editor; a\n\t// long-press in any other tool belongs to that tool's gesture (creating, erasing,\n\t// drawing, panning), so it opens nothing.\n\t//\n\t// The right-click case keys off the pointer type, not the tool, on purpose: a\n\t// right-click switches to select synchronously within the same gesture, so gating\n\t// it on the tool would race the React render and wrongly suppress the menu. The\n\t// select-tool long-press case is safe to gate on the tool \u2014 a long-press never\n\t// switches tools, so there is nothing to race.\n\tconst menuCanOpen = useValue(\n\t\t'context menu can open',\n\t\t() => !editor.getInstanceState().isCoarsePointer || editor.isIn('select'),\n\t\t[editor]\n\t)\n\n\t// When hitting `Escape` while the context menu is open, we want to prevent\n\t// the default behavior of losing focus on the shape. Otherwise,\n\t// it's pretty annoying from an accessibility perspective.\n\tconst preventEscapeFromLosingShapeFocus = useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\te.stopPropagation()\n\t\t\t\teditor.getContainer().focus()\n\t\t\t}\n\t\t},\n\t\t[editor]\n\t)\n\n\tuseEffect(() => {\n\t\tconst body = editor.getContainerDocument().body\n\t\treturn () => {\n\t\t\tbody.removeEventListener('keydown', preventEscapeFromLosingShapeFocus, {\n\t\t\t\tcapture: true,\n\t\t\t})\n\t\t}\n\t}, [editor, preventEscapeFromLosingShapeFocus])\n\n\t// On touch devices, the same touch that triggers Radix's long-press open is still\n\t// down when the menu mounts. The release fires events the dismissable layer treats\n\t// as an outside interaction and closes the menu. We swallow dismissals during a\n\t// short grace window after open so the menu stays put until the user actually\n\t// interacts again.\n\tconst suppressDismissUntilRef = useRef(0)\n\n\tconst rContextMenuPagePoint = useContext(ContextMenuPagePointContext)\n\n\tconst cb = useCallback(\n\t\t(isOpen: boolean) => {\n\t\t\tconst body = editor.getContainerDocument().body\n\t\t\tif (rContextMenuPagePoint) {\n\t\t\t\trContextMenuPagePoint.current = isOpen ? editor.inputs.getCurrentPagePoint().clone() : null\n\t\t\t}\n\t\t\tif (!isOpen) {\n\t\t\t\tconst onlySelectedShape = editor.getOnlySelectedShape()\n\n\t\t\t\tif (onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)) {\n\t\t\t\t\teditor.setSelectedShapes([])\n\t\t\t\t}\n\n\t\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\t\tbody.removeEventListener('keydown', preventEscapeFromLosingShapeFocus, {\n\t\t\t\t\t\tcapture: true,\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tbody.addEventListener('keydown', preventEscapeFromLosingShapeFocus, {\n\t\t\t\t\tcapture: true,\n\t\t\t\t})\n\n\t\t\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\t\t\tsuppressDismissUntilRef.current = Date.now() + 500\n\n\t\t\t\t\t// Weird route: selecting locked shapes on long press\n\t\t\t\t\tconst selectedShapes = editor.getSelectedShapes()\n\t\t\t\t\tconst currentPagePoint = editor.inputs.getCurrentPagePoint()\n\n\t\t\t\t\t// get all of the shapes under the current pointer\n\t\t\t\t\tconst shapesAtPoint = editor.getShapesAtPoint(currentPagePoint)\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t// if there are no selected shapes\n\t\t\t\t\t\t!editor.getSelectedShapes().length ||\n\t\t\t\t\t\t// OR if none of the shapes at the point include the selected shape\n\t\t\t\t\t\t!shapesAtPoint.some((s) => selectedShapes.includes(s))\n\t\t\t\t\t) {\n\t\t\t\t\t\t// then are there any locked shapes under the current pointer?\n\t\t\t\t\t\tconst lockedShapes = shapesAtPoint.filter((s) => editor.isShapeOrAncestorLocked(s))\n\n\t\t\t\t\t\tif (lockedShapes.length) {\n\t\t\t\t\t\t\t// nice, let's select them\n\t\t\t\t\t\t\teditor.select(...lockedShapes.map((s) => s.id))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor, preventEscapeFromLosingShapeFocus, rContextMenuPagePoint]\n\t)\n\n\tconst container = useContainer()\n\tconst dir = useDirection()\n\tconst [isOpen, handleOpenChange] = useMenuIsOpen('context menu', cb)\n\n\t// Get the context menu content, either the default component or the user's\n\t// override. If there's no menu content, then the user has set it to null,\n\t// so skip rendering the menu.\n\tconst content = children ?? <DefaultContextMenuContent />\n\n\treturn (\n\t\t<ContextMenuRoot dir={dir} open={isOpen} onOpenChange={handleOpenChange} modal={false}>\n\t\t\t<_ContextMenu.Trigger\n\t\t\t\t// When suppressed, disabling the trigger stops Radix from opening the\n\t\t\t\t// menu, but it also stops Radix from preventing the native contextmenu \u2014\n\t\t\t\t// so prevent the browser's own menu ourselves in that case.\n\t\t\t\tonContextMenu={menuCanOpen ? undefined : preventDefault}\n\t\t\t\tdir=\"ltr\"\n\t\t\t\tdisabled={disabled || !menuCanOpen}\n\t\t\t>\n\t\t\t\t{Canvas ? <Canvas /> : null}\n\t\t\t</_ContextMenu.Trigger>\n\t\t\t{isOpen && (\n\t\t\t\t<_ContextMenu.Portal container={container}>\n\t\t\t\t\t<_ContextMenu.Content\n\t\t\t\t\t\tclassName=\"tlui-menu tlui-scrollable\"\n\t\t\t\t\t\tdata-testid=\"context-menu\"\n\t\t\t\t\t\taria-label={msg('context-menu.title')}\n\t\t\t\t\t\talignOffset={-4}\n\t\t\t\t\t\tcollisionPadding={4}\n\t\t\t\t\t\tonContextMenu={preventDefault}\n\t\t\t\t\t\tonPointerDownOutside={(e) => {\n\t\t\t\t\t\t\tif (Date.now() < suppressDismissUntilRef.current) e.preventDefault()\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonInteractOutside={(e) => {\n\t\t\t\t\t\t\tif (Date.now() < suppressDismissUntilRef.current) e.preventDefault()\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonFocusOutside={(e) => {\n\t\t\t\t\t\t\tif (Date.now() < suppressDismissUntilRef.current) e.preventDefault()\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<TldrawUiMenuContextProvider type=\"context-menu\" sourceId=\"context-menu\">\n\t\t\t\t\t\t\t{content}\n\t\t\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t\t\t</_ContextMenu.Content>\n\t\t\t\t</_ContextMenu.Portal>\n\t\t\t)}\n\t\t</ContextMenuRoot>\n\t)\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["DefaultContextMenu", "isOpen"]
|
|
7
7
|
}
|
|
@@ -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 = '5.4.
|
|
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 = '5.4.1'\nexport const publishDates = {\n\tmajor: '2026-05-06T16:28:18.473Z',\n\tminor: '2026-09-02T11:04:02.901Z',\n\tpatch: '2026-09-08T10:52:09.132Z',\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": "5.4.
|
|
4
|
+
"version": "5.4.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
"@tiptap/pm": "^3.12.1",
|
|
70
70
|
"@tiptap/react": "^3.12.1",
|
|
71
71
|
"@tiptap/starter-kit": "^3.12.1",
|
|
72
|
-
"@tldraw/driver": "5.4.
|
|
73
|
-
"@tldraw/editor": "5.4.
|
|
74
|
-
"@tldraw/state": "5.4.
|
|
75
|
-
"@tldraw/store": "5.4.
|
|
76
|
-
"@tldraw/tlschema": "5.4.
|
|
72
|
+
"@tldraw/driver": "5.4.1",
|
|
73
|
+
"@tldraw/editor": "5.4.1",
|
|
74
|
+
"@tldraw/state": "5.4.1",
|
|
75
|
+
"@tldraw/store": "5.4.1",
|
|
76
|
+
"@tldraw/tlschema": "5.4.1",
|
|
77
77
|
"classnames": "^2.5.1",
|
|
78
78
|
"idb": "^7.1.1",
|
|
79
79
|
"lz-string": "^1.5.0",
|
|
@@ -6,13 +6,30 @@ import {
|
|
|
6
6
|
useValue,
|
|
7
7
|
} from '@tldraw/editor'
|
|
8
8
|
import { ContextMenu as _ContextMenu } from 'radix-ui'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
ComponentProps,
|
|
11
|
+
ComponentType,
|
|
12
|
+
ReactNode,
|
|
13
|
+
memo,
|
|
14
|
+
useCallback,
|
|
15
|
+
useContext,
|
|
16
|
+
useEffect,
|
|
17
|
+
useRef,
|
|
18
|
+
} from 'react'
|
|
10
19
|
import { ContextMenuPagePointContext } from '../../context/actions'
|
|
11
20
|
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
|
|
12
21
|
import { useDirection, useTranslation } from '../../hooks/useTranslation/useTranslation'
|
|
13
22
|
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
|
14
23
|
import { DefaultContextMenuContent } from './DefaultContextMenuContent'
|
|
15
24
|
|
|
25
|
+
// Controlled `open`: tldraw can close the menu itself (MenuClickCapture's
|
|
26
|
+
// clearOpenMenus), and radix >=2.3.0 only fires onOpenChange on real state
|
|
27
|
+
// changes — a stale internal `open` would swallow every later right-click.
|
|
28
|
+
// radix <2.3 ignores the prop and omits it from its types, hence the widening.
|
|
29
|
+
const ContextMenuRoot = _ContextMenu.Root as ComponentType<
|
|
30
|
+
ComponentProps<typeof _ContextMenu.Root> & { open?: boolean }
|
|
31
|
+
>
|
|
32
|
+
|
|
16
33
|
/** @public */
|
|
17
34
|
export interface TLUiContextMenuProps {
|
|
18
35
|
children?: ReactNode
|
|
@@ -140,7 +157,7 @@ export const DefaultContextMenu = memo(function DefaultContextMenu({
|
|
|
140
157
|
const content = children ?? <DefaultContextMenuContent />
|
|
141
158
|
|
|
142
159
|
return (
|
|
143
|
-
<
|
|
160
|
+
<ContextMenuRoot dir={dir} open={isOpen} onOpenChange={handleOpenChange} modal={false}>
|
|
144
161
|
<_ContextMenu.Trigger
|
|
145
162
|
// When suppressed, disabling the trigger stops Radix from opening the
|
|
146
163
|
// menu, but it also stops Radix from preventing the native contextmenu —
|
|
@@ -176,6 +193,6 @@ export const DefaultContextMenu = memo(function DefaultContextMenu({
|
|
|
176
193
|
</_ContextMenu.Content>
|
|
177
194
|
</_ContextMenu.Portal>
|
|
178
195
|
)}
|
|
179
|
-
</
|
|
196
|
+
</ContextMenuRoot>
|
|
180
197
|
)
|
|
181
198
|
})
|
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 = '5.4.
|
|
4
|
+
export const version = '5.4.1'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2026-05-06T16:28:18.473Z',
|
|
7
7
|
minor: '2026-09-02T11:04:02.901Z',
|
|
8
|
-
patch: '2026-09-
|
|
8
|
+
patch: '2026-09-08T10:52:09.132Z',
|
|
9
9
|
}
|
|
@@ -38,6 +38,35 @@ it('opens on right-click', async () => {
|
|
|
38
38
|
expect(screen.queryByTestId('context-menu')).toBeNull()
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
+
it('reopens after tldraw closes the menu itself', async () => {
|
|
42
|
+
const { editor } = await renderTldrawComponentWithEditor(
|
|
43
|
+
(onMount) => (
|
|
44
|
+
<Tldraw
|
|
45
|
+
onMount={(editor) => {
|
|
46
|
+
editor.createShape({ id: createShapeId(), type: 'geo' })
|
|
47
|
+
onMount(editor)
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
),
|
|
51
|
+
{ waitForPatterns: false }
|
|
52
|
+
)
|
|
53
|
+
const canvas = await screen.findByTestId('canvas')
|
|
54
|
+
|
|
55
|
+
fireEvent.contextMenu(canvas)
|
|
56
|
+
await screen.findByTestId('context-menu')
|
|
57
|
+
|
|
58
|
+
// A left-click on the canvas closes the menu through tldraw's own state:
|
|
59
|
+
// MenuClickCapture clears the open menus and unmounts the content before
|
|
60
|
+
// radix's dismissable layer ever sees the outside pointerdown.
|
|
61
|
+
act(() => editor.menus.clearOpenMenus())
|
|
62
|
+
expect(screen.queryByTestId('context-menu')).toBeNull()
|
|
63
|
+
|
|
64
|
+
// Regression for #10566: radix >=2.3.0 fires onOpenChange only on real state
|
|
65
|
+
// changes, so a stale internal `open` swallowed every later right-click.
|
|
66
|
+
fireEvent.contextMenu(canvas)
|
|
67
|
+
await screen.findByTestId('context-menu')
|
|
68
|
+
})
|
|
69
|
+
|
|
41
70
|
// A touch long-press (coarse pointer) opens the menu only in the select tool. In any
|
|
42
71
|
// other tool the long-press belongs to that tool's gesture, so the menu stays closed.
|
|
43
72
|
// The instance's isCoarsePointer is synced from tlenv, so flip that to simulate touch.
|