tldraw 3.15.0-next.39f008bfb627 → 3.15.0-next.6032a20bb23f
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 +16 -8
- package/dist-cjs/index.js +2 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawImage.js +5 -2
- package/dist-cjs/lib/TldrawImage.js.map +3 -3
- package/dist-cjs/lib/canvas/TldrawCropHandles.js +1 -1
- package/dist-cjs/lib/canvas/TldrawCropHandles.js.map +2 -2
- package/dist-cjs/lib/canvas/TldrawHandles.js +1 -1
- package/dist-cjs/lib/canvas/TldrawHandles.js.map +2 -2
- package/dist-cjs/lib/canvas/TldrawOverlays.js +1 -1
- package/dist-cjs/lib/canvas/TldrawOverlays.js.map +2 -2
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +279 -271
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/PathBuilder.js +21 -3
- package/dist-cjs/lib/shapes/shared/PathBuilder.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -0
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js +1 -0
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
- package/dist-cjs/lib/styles.js.map +2 -2
- package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js +3 -4
- package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js.map +2 -2
- package/dist-cjs/lib/ui/components/Spinner.js +2 -25
- package/dist-cjs/lib/ui/components/Spinner.js.map +2 -2
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +2 -1
- package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/Button/TldrawUiButtonIcon.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiDialog.js +1 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiDialog.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js +35 -1
- package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +5 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +1 -0
- package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
- package/dist-cjs/lib/ui/context/actions.js.map +1 -1
- package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +24 -7
- package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +2 -2
- package/dist-cjs/lib/ui/hooks/useTools.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 +16 -8
- package/dist-esm/index.mjs +6 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawImage.mjs +5 -2
- package/dist-esm/lib/TldrawImage.mjs.map +2 -2
- package/dist-esm/lib/canvas/TldrawCropHandles.mjs +1 -1
- package/dist-esm/lib/canvas/TldrawCropHandles.mjs.map +2 -2
- package/dist-esm/lib/canvas/TldrawHandles.mjs +1 -1
- package/dist-esm/lib/canvas/TldrawHandles.mjs.map +2 -2
- package/dist-esm/lib/canvas/TldrawOverlays.mjs +1 -1
- package/dist-esm/lib/canvas/TldrawOverlays.mjs.map +2 -2
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +279 -271
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/PathBuilder.mjs +22 -3
- package/dist-esm/lib/shapes/shared/PathBuilder.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -0
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +1 -0
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
- package/dist-esm/lib/styles.mjs.map +2 -2
- package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs +3 -4
- package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Spinner.mjs +3 -26
- package/dist-esm/lib/ui/components/Spinner.mjs.map +2 -2
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +2 -1
- package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/Button/TldrawUiButtonIcon.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiDialog.mjs +1 -1
- package/dist-esm/lib/ui/components/primitives/TldrawUiDialog.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs +36 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +5 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +1 -0
- package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
- package/dist-esm/lib/ui/context/actions.mjs.map +1 -1
- package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +24 -7
- package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +2 -2
- package/dist-esm/lib/ui/hooks/useTools.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 +4 -3
- package/src/index.ts +6 -1
- package/src/lib/TldrawImage.tsx +6 -2
- package/src/lib/canvas/TldrawCropHandles.tsx +1 -1
- package/src/lib/canvas/TldrawHandles.tsx +5 -1
- package/src/lib/canvas/TldrawOverlays.tsx +1 -1
- package/src/lib/canvas/TldrawSelectionForeground.tsx +5 -1
- package/src/lib/shapes/shared/PathBuilder.test.tsx +1 -1
- package/src/lib/shapes/shared/PathBuilder.tsx +35 -1
- package/src/lib/shapes/shared/PlainTextLabel.tsx +1 -0
- package/src/lib/shapes/shared/RichTextLabel.tsx +1 -0
- package/src/lib/styles.tsx +3 -1
- package/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx +3 -4
- package/src/lib/ui/components/Spinner.tsx +2 -24
- package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +1 -0
- package/src/lib/ui/components/primitives/Button/TldrawUiButtonIcon.tsx +2 -2
- package/src/lib/ui/components/primitives/TldrawUiDialog.tsx +1 -1
- package/src/lib/ui/components/primitives/TldrawUiIcon.tsx +41 -3
- package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +5 -1
- package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +4 -0
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.tsx +2 -2
- package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +3 -2
- package/src/lib/ui/context/actions.tsx +1 -1
- package/src/lib/ui/hooks/useClipboardEvents.ts +31 -10
- package/src/lib/ui/hooks/useTools.tsx +2 -1
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/ui.css +8 -22
- package/src/test/navigation.test.ts +254 -0
- package/tldraw.css +25 -26
|
@@ -65,7 +65,7 @@ export interface TLUiDialogBodyProps {
|
|
|
65
65
|
/** @public @react */
|
|
66
66
|
export function TldrawUiDialogBody({ className, children, style }: TLUiDialogBodyProps) {
|
|
67
67
|
return (
|
|
68
|
-
<div className={classNames('tlui-dialog__body', className)} style={style}>
|
|
68
|
+
<div className={classNames('tlui-dialog__body', className)} style={style} tabIndex={0}>
|
|
69
69
|
{children}
|
|
70
70
|
</div>
|
|
71
71
|
)
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import classNames from 'classnames'
|
|
2
|
-
import { memo, useLayoutEffect, useRef } from 'react'
|
|
2
|
+
import { cloneElement, memo, ReactElement, useLayoutEffect, useRef } from 'react'
|
|
3
3
|
import { useAssetUrls } from '../../context/asset-urls'
|
|
4
4
|
import { TLUiIconType } from '../../icon-types'
|
|
5
5
|
|
|
6
|
+
/** @public */
|
|
7
|
+
export type TLUiIconJsx = ReactElement<React.HTMLAttributes<HTMLDivElement>>
|
|
8
|
+
|
|
6
9
|
/** @public */
|
|
7
10
|
export interface TLUiIconProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
8
|
-
icon: TLUiIconType | Exclude<string, TLUiIconType>
|
|
11
|
+
icon: TLUiIconType | Exclude<string, TLUiIconType> | TLUiIconJsx
|
|
9
12
|
label: string
|
|
10
13
|
small?: boolean
|
|
11
14
|
color?: string
|
|
@@ -24,6 +27,41 @@ export const TldrawUiIcon = memo(function TldrawUiIcon({
|
|
|
24
27
|
className,
|
|
25
28
|
...props
|
|
26
29
|
}: TLUiIconProps) {
|
|
30
|
+
if (typeof icon === 'string') {
|
|
31
|
+
return (
|
|
32
|
+
<TldrawUIIconInner
|
|
33
|
+
label={label}
|
|
34
|
+
small={small}
|
|
35
|
+
invertIcon={invertIcon}
|
|
36
|
+
icon={icon}
|
|
37
|
+
color={color}
|
|
38
|
+
className={className}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return cloneElement(icon, {
|
|
45
|
+
...props,
|
|
46
|
+
className: classNames({ 'tlui-icon__small': small }, className, icon.props.className),
|
|
47
|
+
'aria-label': label,
|
|
48
|
+
style: {
|
|
49
|
+
color,
|
|
50
|
+
transform: invertIcon ? 'scale(-1, 1)' : undefined,
|
|
51
|
+
...icon.props.style,
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
function TldrawUIIconInner({
|
|
57
|
+
label,
|
|
58
|
+
small,
|
|
59
|
+
invertIcon,
|
|
60
|
+
icon,
|
|
61
|
+
color,
|
|
62
|
+
className,
|
|
63
|
+
...props
|
|
64
|
+
}: TLUiIconProps & { icon: TLUiIconType | Exclude<string, TLUiIconType> }) {
|
|
27
65
|
const assetUrls = useAssetUrls()
|
|
28
66
|
const asset = assetUrls.icons[icon as TLUiIconType] ?? assetUrls.icons['question-mark-circle']
|
|
29
67
|
const ref = useRef<HTMLDivElement>(null)
|
|
@@ -69,4 +107,4 @@ export const TldrawUiIcon = memo(function TldrawUiIcon({
|
|
|
69
107
|
}}
|
|
70
108
|
/>
|
|
71
109
|
)
|
|
72
|
-
}
|
|
110
|
+
}
|
|
@@ -13,6 +13,7 @@ export interface TLUiSliderProps {
|
|
|
13
13
|
onValueChange(value: number): void
|
|
14
14
|
onHistoryMark(id: string): void
|
|
15
15
|
'data-testid'?: string
|
|
16
|
+
ariaValueModifier?: number
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/** @public @react */
|
|
@@ -26,6 +27,7 @@ export const TldrawUiSlider = React.forwardRef<HTMLDivElement, TLUiSliderProps>(
|
|
|
26
27
|
label,
|
|
27
28
|
onValueChange,
|
|
28
29
|
['data-testid']: testId,
|
|
30
|
+
ariaValueModifier = 1,
|
|
29
31
|
}: TLUiSliderProps,
|
|
30
32
|
ref
|
|
31
33
|
) {
|
|
@@ -81,7 +83,9 @@ export const TldrawUiSlider = React.forwardRef<HTMLDivElement, TLUiSliderProps>(
|
|
|
81
83
|
</_Slider.Track>
|
|
82
84
|
{value !== null && (
|
|
83
85
|
<_Slider.Thumb
|
|
84
|
-
aria-
|
|
86
|
+
aria-valuemin={(min ?? 0) * ariaValueModifier}
|
|
87
|
+
aria-valuenow={value * ariaValueModifier}
|
|
88
|
+
aria-valuemax={steps * ariaValueModifier}
|
|
85
89
|
className="tlui-slider__thumb"
|
|
86
90
|
dir="ltr"
|
|
87
91
|
ref={ref}
|
|
@@ -76,6 +76,10 @@ export const TldrawUiToolbarToggleGroup = ({
|
|
|
76
76
|
<_Toolbar.ToggleGroup
|
|
77
77
|
type={type}
|
|
78
78
|
{...props}
|
|
79
|
+
// TODO: this fixes a bug in Radix until they fix it.
|
|
80
|
+
// https://github.com/radix-ui/primitives/issues/3188
|
|
81
|
+
// https://github.com/radix-ui/primitives/pull/3189
|
|
82
|
+
role="radiogroup"
|
|
79
83
|
className={classnames('tlui-toolbar-toggle-group', className)}
|
|
80
84
|
>
|
|
81
85
|
{children}
|
|
@@ -5,7 +5,7 @@ import { TLUiEventSource } from '../../../context/events'
|
|
|
5
5
|
import { useReadonly } from '../../../hooks/useReadonly'
|
|
6
6
|
import { TLUiTranslationKey } from '../../../hooks/useTranslation/TLUiTranslationKey'
|
|
7
7
|
import { useTranslation } from '../../../hooks/useTranslation/useTranslation'
|
|
8
|
-
import { TldrawUiIcon } from '../TldrawUiIcon'
|
|
8
|
+
import { TldrawUiIcon, TLUiIconJsx } from '../TldrawUiIcon'
|
|
9
9
|
import { TldrawUiKbd } from '../TldrawUiKbd'
|
|
10
10
|
import { useTldrawUiMenuContext } from './TldrawUiMenuContext'
|
|
11
11
|
|
|
@@ -14,7 +14,7 @@ export interface TLUiMenuCheckboxItemProps<
|
|
|
14
14
|
TranslationKey extends string = string,
|
|
15
15
|
IconType extends string = string,
|
|
16
16
|
> {
|
|
17
|
-
icon?: IconType
|
|
17
|
+
icon?: IconType | TLUiIconJsx
|
|
18
18
|
id: string
|
|
19
19
|
kbd?: string
|
|
20
20
|
title?: string
|
|
@@ -12,6 +12,7 @@ import { TldrawUiButton } from '../Button/TldrawUiButton'
|
|
|
12
12
|
import { TldrawUiButtonIcon } from '../Button/TldrawUiButtonIcon'
|
|
13
13
|
import { TldrawUiButtonLabel } from '../Button/TldrawUiButtonLabel'
|
|
14
14
|
import { TldrawUiDropdownMenuItem } from '../TldrawUiDropdownMenu'
|
|
15
|
+
import { TLUiIconJsx } from '../TldrawUiIcon'
|
|
15
16
|
import { TldrawUiKbd } from '../TldrawUiKbd'
|
|
16
17
|
import { TldrawUiToolbarButton } from '../TldrawUiToolbar'
|
|
17
18
|
import { useTldrawUiMenuContext } from './TldrawUiMenuContext'
|
|
@@ -25,11 +26,11 @@ export interface TLUiMenuItemProps<
|
|
|
25
26
|
/**
|
|
26
27
|
* The icon to display on the item. Icons are only shown in certain menu types.
|
|
27
28
|
*/
|
|
28
|
-
icon?: IconType
|
|
29
|
+
icon?: IconType | TLUiIconJsx
|
|
29
30
|
/**
|
|
30
31
|
* An icon to display to the left of the menu item.
|
|
31
32
|
*/
|
|
32
|
-
iconLeft?: IconType
|
|
33
|
+
iconLeft?: IconType | TLUiIconJsx
|
|
33
34
|
/**
|
|
34
35
|
* The keyboard shortcut to display on the item.
|
|
35
36
|
*/
|
|
@@ -43,7 +43,7 @@ export interface TLUiActionItem<
|
|
|
43
43
|
TransationKey extends string = string,
|
|
44
44
|
IconType extends string = string,
|
|
45
45
|
> {
|
|
46
|
-
icon?: IconType
|
|
46
|
+
icon?: IconType | React.ReactElement
|
|
47
47
|
id: string
|
|
48
48
|
kbd?: string
|
|
49
49
|
label?: TransationKey | { [key: string]: TransationKey }
|
|
@@ -352,7 +352,7 @@ async function handleClipboardThings(editor: Editor, things: ClipboardThing[], p
|
|
|
352
352
|
|
|
353
353
|
if (tldrawHtmlComment) {
|
|
354
354
|
try {
|
|
355
|
-
// First try parsing as plain JSON (version 2
|
|
355
|
+
// First try parsing as plain JSON (version 2/3 formats)
|
|
356
356
|
let json
|
|
357
357
|
try {
|
|
358
358
|
json = JSON.parse(tldrawHtmlComment)
|
|
@@ -380,19 +380,32 @@ async function handleClipboardThings(editor: Editor, things: ClipboardThing[], p
|
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
// Handle versioned clipboard format
|
|
383
|
-
if (json.version ===
|
|
384
|
-
// Version
|
|
383
|
+
if (json.version === 3) {
|
|
384
|
+
// Version 3: Assets are plain, decompress only other data
|
|
385
385
|
try {
|
|
386
|
-
|
|
386
|
+
const otherData = JSON.parse(
|
|
387
|
+
lz.decompressFromBase64(json.data.otherCompressed) || '{}'
|
|
388
|
+
)
|
|
389
|
+
const reconstructedData = {
|
|
390
|
+
assets: json.data.assets || [],
|
|
391
|
+
...otherData,
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
r({ type: 'tldraw', data: reconstructedData })
|
|
387
395
|
return
|
|
388
396
|
} catch (error) {
|
|
389
397
|
r({
|
|
390
398
|
type: 'error',
|
|
391
399
|
data: json,
|
|
392
|
-
reason: `failed to
|
|
400
|
+
reason: `failed to decompress version 2 clipboard data: ${error}`,
|
|
393
401
|
})
|
|
394
402
|
return
|
|
395
403
|
}
|
|
404
|
+
}
|
|
405
|
+
if (json.version === 2) {
|
|
406
|
+
// Version 2: Everything is plain, this had issues with encoding... :-/
|
|
407
|
+
// TODO: nix this support after some time.
|
|
408
|
+
r({ type: 'tldraw', data: json.data })
|
|
396
409
|
} else {
|
|
397
410
|
// Version 1 or no version: Legacy format
|
|
398
411
|
if (typeof json.data === 'string') {
|
|
@@ -584,13 +597,21 @@ const handleNativeOrMenuCopy = async (editor: Editor) => {
|
|
|
584
597
|
return
|
|
585
598
|
}
|
|
586
599
|
|
|
587
|
-
//
|
|
588
|
-
|
|
600
|
+
// Use versioned clipboard format for better compression
|
|
601
|
+
// Version 3: Don't compress assets, only compress other data
|
|
602
|
+
const { assets, ...otherData } = content
|
|
603
|
+
const clipboardData = {
|
|
589
604
|
type: 'application/tldraw',
|
|
590
605
|
kind: 'content',
|
|
591
|
-
version:
|
|
592
|
-
data:
|
|
593
|
-
|
|
606
|
+
version: 3,
|
|
607
|
+
data: {
|
|
608
|
+
assets: assets || [], // Plain JSON, no compression
|
|
609
|
+
otherCompressed: lz.compressToBase64(JSON.stringify(otherData)), // Only compress non-asset data
|
|
610
|
+
},
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Don't compress the final structure - just use plain JSON
|
|
614
|
+
const stringifiedClipboard = JSON.stringify(clipboardData)
|
|
594
615
|
|
|
595
616
|
if (typeof navigator === 'undefined') {
|
|
596
617
|
return
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Editor, GeoShapeGeoStyle, useMaybeEditor } from '@tldraw/editor'
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import { EmbedDialog } from '../components/EmbedDialog'
|
|
4
|
+
import { TLUiIconJsx } from '../components/primitives/TldrawUiIcon'
|
|
4
5
|
import { useA11y } from '../context/a11y'
|
|
5
6
|
import { TLUiEventSource, useUiEvents } from '../context/events'
|
|
6
7
|
import { TLUiIconType } from '../icon-types'
|
|
@@ -16,7 +17,7 @@ export interface TLUiToolItem<
|
|
|
16
17
|
id: string
|
|
17
18
|
label: TranslationKey
|
|
18
19
|
shortcutsLabel?: TranslationKey
|
|
19
|
-
icon: IconType
|
|
20
|
+
icon: IconType | TLUiIconJsx
|
|
20
21
|
onSelect(source: TLUiEventSource): void
|
|
21
22
|
/**
|
|
22
23
|
* The keyboard shortcut for this tool. This is a string that can be a single key,
|
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.15.0-next.
|
|
4
|
+
export const version = '3.15.0-next.6032a20bb23f'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '2025-07-
|
|
8
|
-
patch: '2025-07-
|
|
7
|
+
minor: '2025-07-25T10:55:37.032Z',
|
|
8
|
+
patch: '2025-07-25T10:55:37.032Z',
|
|
9
9
|
}
|
package/src/lib/ui.css
CHANGED
|
@@ -964,7 +964,7 @@
|
|
|
964
964
|
justify-content: center;
|
|
965
965
|
border-radius: 99px;
|
|
966
966
|
opacity: 0;
|
|
967
|
-
animation: fade-in;
|
|
967
|
+
animation: tl-fade-in;
|
|
968
968
|
animation-duration: 0.12s;
|
|
969
969
|
animation-delay: 2s;
|
|
970
970
|
animation-fill-mode: forwards;
|
|
@@ -1108,20 +1108,6 @@
|
|
|
1108
1108
|
display: none;
|
|
1109
1109
|
}
|
|
1110
1110
|
|
|
1111
|
-
.tlui-navigation-panel__toggle .tlui-icon {
|
|
1112
|
-
opacity: 0.24;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
.tlui-navigation-panel__toggle:active .tlui-icon {
|
|
1116
|
-
opacity: 1;
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
@media (hover: hover) {
|
|
1120
|
-
.tlui-navigation-panel__toggle:hover .tlui-icon {
|
|
1121
|
-
opacity: 1;
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
1111
|
/* Minimap */
|
|
1126
1112
|
|
|
1127
1113
|
.tlui-minimap {
|
|
@@ -1379,11 +1365,11 @@
|
|
|
1379
1365
|
|
|
1380
1366
|
@media (prefers-reduced-motion: no-preference) {
|
|
1381
1367
|
.tlui-toast__container[data-state='open'] {
|
|
1382
|
-
animation: slide-in 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);
|
|
1368
|
+
animation: tlui-slide-in 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);
|
|
1383
1369
|
}
|
|
1384
1370
|
|
|
1385
1371
|
.tlui-toast__container[data-state='closed'] {
|
|
1386
|
-
animation:
|
|
1372
|
+
animation: tlui-fade-out 100ms ease-in;
|
|
1387
1373
|
}
|
|
1388
1374
|
|
|
1389
1375
|
.tlui-toast__container[data-swipe='move'] {
|
|
@@ -1396,7 +1382,7 @@
|
|
|
1396
1382
|
}
|
|
1397
1383
|
|
|
1398
1384
|
.tlui-toast__container[data-swipe='end'] {
|
|
1399
|
-
animation:
|
|
1385
|
+
animation: tlui-slide-out 100ms ease-out;
|
|
1400
1386
|
}
|
|
1401
1387
|
}
|
|
1402
1388
|
|
|
@@ -1411,7 +1397,7 @@
|
|
|
1411
1397
|
z-index: var(--layer-canvas-overlays);
|
|
1412
1398
|
background-color: var(--color-overlay);
|
|
1413
1399
|
pointer-events: all;
|
|
1414
|
-
animation:
|
|
1400
|
+
animation: tl-fade-in 0.12s ease-out;
|
|
1415
1401
|
display: grid;
|
|
1416
1402
|
place-items: center;
|
|
1417
1403
|
overflow-y: auto;
|
|
@@ -1978,7 +1964,7 @@
|
|
|
1978
1964
|
}
|
|
1979
1965
|
|
|
1980
1966
|
/* ------------------- Animations ------------------- */
|
|
1981
|
-
@keyframes
|
|
1967
|
+
@keyframes tlui-fade-out {
|
|
1982
1968
|
0% {
|
|
1983
1969
|
opacity: 1;
|
|
1984
1970
|
}
|
|
@@ -1987,7 +1973,7 @@
|
|
|
1987
1973
|
}
|
|
1988
1974
|
}
|
|
1989
1975
|
|
|
1990
|
-
@keyframes slide-in {
|
|
1976
|
+
@keyframes tlui-slide-in {
|
|
1991
1977
|
from {
|
|
1992
1978
|
transform: translateX(calc(100% + var(--space-3)));
|
|
1993
1979
|
}
|
|
@@ -1996,7 +1982,7 @@
|
|
|
1996
1982
|
}
|
|
1997
1983
|
}
|
|
1998
1984
|
|
|
1999
|
-
@keyframes
|
|
1985
|
+
@keyframes tlui-slide-out {
|
|
2000
1986
|
from {
|
|
2001
1987
|
transform: translateX(var(--radix-toast-swipe-end-x));
|
|
2002
1988
|
}
|
|
@@ -414,6 +414,260 @@ describe('Shape navigation', () => {
|
|
|
414
414
|
expect(editor.getSelectedShapeIds()).toEqual([])
|
|
415
415
|
})
|
|
416
416
|
|
|
417
|
+
it('respects container boundaries when navigating with left/right', () => {
|
|
418
|
+
// Create a frame with shapes inside and shapes outside
|
|
419
|
+
editor.createShapes([
|
|
420
|
+
{
|
|
421
|
+
id: ids.frame1,
|
|
422
|
+
type: 'frame',
|
|
423
|
+
x: 0,
|
|
424
|
+
y: 0,
|
|
425
|
+
props: {
|
|
426
|
+
w: 200,
|
|
427
|
+
h: 200,
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
// Shapes inside frame
|
|
431
|
+
{
|
|
432
|
+
id: ids.box1,
|
|
433
|
+
type: 'geo',
|
|
434
|
+
x: 10,
|
|
435
|
+
y: 100,
|
|
436
|
+
parentId: ids.frame1,
|
|
437
|
+
props: {
|
|
438
|
+
w: 30,
|
|
439
|
+
h: 30,
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
id: ids.box2,
|
|
444
|
+
type: 'geo',
|
|
445
|
+
x: 50,
|
|
446
|
+
y: 100,
|
|
447
|
+
parentId: ids.frame1,
|
|
448
|
+
props: {
|
|
449
|
+
w: 30,
|
|
450
|
+
h: 30,
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
id: ids.box3,
|
|
455
|
+
type: 'geo',
|
|
456
|
+
x: 90,
|
|
457
|
+
y: 100,
|
|
458
|
+
parentId: ids.frame1,
|
|
459
|
+
props: {
|
|
460
|
+
w: 30,
|
|
461
|
+
h: 30,
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
// Shapes outside frame
|
|
465
|
+
{
|
|
466
|
+
id: ids.box4,
|
|
467
|
+
type: 'geo',
|
|
468
|
+
x: 300,
|
|
469
|
+
y: 100,
|
|
470
|
+
props: {
|
|
471
|
+
w: 30,
|
|
472
|
+
h: 30,
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
id: ids.box5,
|
|
477
|
+
type: 'geo',
|
|
478
|
+
x: 350,
|
|
479
|
+
y: 100,
|
|
480
|
+
props: {
|
|
481
|
+
w: 30,
|
|
482
|
+
h: 30,
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
])
|
|
486
|
+
|
|
487
|
+
// Setup shape centers for consistent testing
|
|
488
|
+
jest.spyOn(editor, 'getShapePageBounds').mockImplementation((shape: any) => {
|
|
489
|
+
const positions = {
|
|
490
|
+
[ids.box1]: { x: 25, y: 115 },
|
|
491
|
+
[ids.box2]: { x: 65, y: 115 },
|
|
492
|
+
[ids.box3]: { x: 105, y: 115 },
|
|
493
|
+
[ids.box4]: { x: 315, y: 115 },
|
|
494
|
+
[ids.box5]: { x: 365, y: 115 },
|
|
495
|
+
}
|
|
496
|
+
const pos = positions[shape?.id as keyof typeof positions]
|
|
497
|
+
return pos ? ({ center: pos } as any) : ({ center: { x: 0, y: 0 } } as any)
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
// Select a shape inside the frame
|
|
501
|
+
editor.select(ids.box1)
|
|
502
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box1])
|
|
503
|
+
|
|
504
|
+
// Navigate right - should stay within the frame
|
|
505
|
+
editor.selectAdjacentShape('right')
|
|
506
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box2])
|
|
507
|
+
|
|
508
|
+
// Continue navigating right - should still stay within the frame
|
|
509
|
+
editor.selectAdjacentShape('right')
|
|
510
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box3])
|
|
511
|
+
|
|
512
|
+
// Navigate right again - should not leave the frame to go to box4
|
|
513
|
+
editor.selectAdjacentShape('right')
|
|
514
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box3]) // Should stay at box3
|
|
515
|
+
|
|
516
|
+
// Now navigate left to test the other direction
|
|
517
|
+
editor.selectAdjacentShape('left')
|
|
518
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box2])
|
|
519
|
+
|
|
520
|
+
editor.selectAdjacentShape('left')
|
|
521
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box1])
|
|
522
|
+
|
|
523
|
+
// Navigate left again - should not leave the frame
|
|
524
|
+
editor.selectAdjacentShape('left')
|
|
525
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box1]) // Should stay at box1
|
|
526
|
+
|
|
527
|
+
// Now test navigation outside the frame
|
|
528
|
+
editor.select(ids.box4)
|
|
529
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box4])
|
|
530
|
+
|
|
531
|
+
// Navigate right - should move to box5
|
|
532
|
+
editor.selectAdjacentShape('right')
|
|
533
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box5])
|
|
534
|
+
|
|
535
|
+
// Navigate left - should move back to box4
|
|
536
|
+
editor.selectAdjacentShape('left')
|
|
537
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box4])
|
|
538
|
+
|
|
539
|
+
// Navigate left again - should select the frame (nearest shape to the left)
|
|
540
|
+
editor.selectAdjacentShape('left')
|
|
541
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.frame1]) // Should select frame1
|
|
542
|
+
})
|
|
543
|
+
|
|
544
|
+
it('respects container boundaries when navigating with up/down', () => {
|
|
545
|
+
// Create a frame with shapes inside and shapes outside
|
|
546
|
+
editor.createShapes([
|
|
547
|
+
{
|
|
548
|
+
id: ids.frame1,
|
|
549
|
+
type: 'frame',
|
|
550
|
+
x: 0,
|
|
551
|
+
y: 0,
|
|
552
|
+
props: {
|
|
553
|
+
w: 200,
|
|
554
|
+
h: 200,
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
// Shapes inside frame - vertically arranged
|
|
558
|
+
{
|
|
559
|
+
id: ids.box1,
|
|
560
|
+
type: 'geo',
|
|
561
|
+
x: 100,
|
|
562
|
+
y: 10,
|
|
563
|
+
parentId: ids.frame1,
|
|
564
|
+
props: {
|
|
565
|
+
w: 30,
|
|
566
|
+
h: 30,
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
id: ids.box2,
|
|
571
|
+
type: 'geo',
|
|
572
|
+
x: 100,
|
|
573
|
+
y: 50,
|
|
574
|
+
parentId: ids.frame1,
|
|
575
|
+
props: {
|
|
576
|
+
w: 30,
|
|
577
|
+
h: 30,
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
id: ids.box3,
|
|
582
|
+
type: 'geo',
|
|
583
|
+
x: 100,
|
|
584
|
+
y: 90,
|
|
585
|
+
parentId: ids.frame1,
|
|
586
|
+
props: {
|
|
587
|
+
w: 30,
|
|
588
|
+
h: 30,
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
// Shapes outside frame - vertically arranged
|
|
592
|
+
{
|
|
593
|
+
id: ids.box4,
|
|
594
|
+
type: 'geo',
|
|
595
|
+
x: 300,
|
|
596
|
+
y: 10,
|
|
597
|
+
props: {
|
|
598
|
+
w: 30,
|
|
599
|
+
h: 30,
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
id: ids.box5,
|
|
604
|
+
type: 'geo',
|
|
605
|
+
x: 300,
|
|
606
|
+
y: 50,
|
|
607
|
+
props: {
|
|
608
|
+
w: 30,
|
|
609
|
+
h: 30,
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
])
|
|
613
|
+
|
|
614
|
+
// Setup shape centers for consistent testing
|
|
615
|
+
jest.spyOn(editor, 'getShapePageBounds').mockImplementation((shape: any) => {
|
|
616
|
+
const positions = {
|
|
617
|
+
[ids.box1]: { x: 115, y: 25 },
|
|
618
|
+
[ids.box2]: { x: 115, y: 65 },
|
|
619
|
+
[ids.box3]: { x: 115, y: 105 },
|
|
620
|
+
[ids.box4]: { x: 315, y: 25 },
|
|
621
|
+
[ids.box5]: { x: 315, y: 65 },
|
|
622
|
+
}
|
|
623
|
+
const pos = positions[shape?.id as keyof typeof positions]
|
|
624
|
+
return pos ? ({ center: pos } as any) : ({ center: { x: 0, y: 0 } } as any)
|
|
625
|
+
})
|
|
626
|
+
|
|
627
|
+
// Select a shape inside the frame
|
|
628
|
+
editor.select(ids.box1)
|
|
629
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box1])
|
|
630
|
+
|
|
631
|
+
// Navigate down - should stay within the frame
|
|
632
|
+
editor.selectAdjacentShape('down')
|
|
633
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box2])
|
|
634
|
+
|
|
635
|
+
// Continue navigating down - should still stay within the frame
|
|
636
|
+
editor.selectAdjacentShape('down')
|
|
637
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box3])
|
|
638
|
+
|
|
639
|
+
// Navigate down again - should not leave the frame
|
|
640
|
+
editor.selectAdjacentShape('down')
|
|
641
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box3]) // Should stay at box3
|
|
642
|
+
|
|
643
|
+
// Now navigate up to test the other direction
|
|
644
|
+
editor.selectAdjacentShape('up')
|
|
645
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box2])
|
|
646
|
+
|
|
647
|
+
editor.selectAdjacentShape('up')
|
|
648
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box1])
|
|
649
|
+
|
|
650
|
+
// Navigate up again - should not leave the frame
|
|
651
|
+
editor.selectAdjacentShape('up')
|
|
652
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box1]) // Should stay at box1
|
|
653
|
+
|
|
654
|
+
// Now test navigation outside the frame
|
|
655
|
+
editor.select(ids.box4)
|
|
656
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box4])
|
|
657
|
+
|
|
658
|
+
// Navigate down - should move to box5
|
|
659
|
+
editor.selectAdjacentShape('down')
|
|
660
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box5])
|
|
661
|
+
|
|
662
|
+
// Navigate up - should move back to box4
|
|
663
|
+
editor.selectAdjacentShape('up')
|
|
664
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box4])
|
|
665
|
+
|
|
666
|
+
// Navigate up again - should not enter the frame
|
|
667
|
+
editor.selectAdjacentShape('up')
|
|
668
|
+
expect(editor.getSelectedShapeIds()).toEqual([ids.box4]) // Should stay at box4
|
|
669
|
+
})
|
|
670
|
+
|
|
417
671
|
it('respects container boundaries when navigating with Tab', () => {
|
|
418
672
|
// Create a frame with shapes inside and shapes outside
|
|
419
673
|
editor.createShapes([
|