tldraw 3.15.0-next.d30ed5ad740e → 3.15.0-next.e136ad205948

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.
Files changed (77) hide show
  1. package/dist-cjs/index.d.ts +5 -0
  2. package/dist-cjs/index.js +2 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/canvas/TldrawCropHandles.js +1 -1
  5. package/dist-cjs/lib/canvas/TldrawCropHandles.js.map +2 -2
  6. package/dist-cjs/lib/canvas/TldrawHandles.js +1 -1
  7. package/dist-cjs/lib/canvas/TldrawHandles.js.map +2 -2
  8. package/dist-cjs/lib/canvas/TldrawOverlays.js +1 -1
  9. package/dist-cjs/lib/canvas/TldrawOverlays.js.map +2 -2
  10. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +279 -271
  11. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
  12. package/dist-cjs/lib/shapes/shared/PathBuilder.js +21 -3
  13. package/dist-cjs/lib/shapes/shared/PathBuilder.js.map +2 -2
  14. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -0
  15. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  16. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +1 -0
  17. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
  18. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js +3 -4
  19. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js.map +2 -2
  20. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +2 -1
  21. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  22. package/dist-cjs/lib/ui/components/primitives/TldrawUiDialog.js +1 -1
  23. package/dist-cjs/lib/ui/components/primitives/TldrawUiDialog.js.map +2 -2
  24. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +5 -2
  25. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
  26. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +1 -0
  27. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  28. package/dist-cjs/lib/ui/version.js +3 -3
  29. package/dist-cjs/lib/ui/version.js.map +1 -1
  30. package/dist-esm/index.d.mts +5 -0
  31. package/dist-esm/index.mjs +3 -1
  32. package/dist-esm/index.mjs.map +2 -2
  33. package/dist-esm/lib/canvas/TldrawCropHandles.mjs +1 -1
  34. package/dist-esm/lib/canvas/TldrawCropHandles.mjs.map +2 -2
  35. package/dist-esm/lib/canvas/TldrawHandles.mjs +1 -1
  36. package/dist-esm/lib/canvas/TldrawHandles.mjs.map +2 -2
  37. package/dist-esm/lib/canvas/TldrawOverlays.mjs +1 -1
  38. package/dist-esm/lib/canvas/TldrawOverlays.mjs.map +2 -2
  39. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +279 -271
  40. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
  41. package/dist-esm/lib/shapes/shared/PathBuilder.mjs +22 -3
  42. package/dist-esm/lib/shapes/shared/PathBuilder.mjs.map +2 -2
  43. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -0
  44. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  45. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +1 -0
  46. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  47. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs +3 -4
  48. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs.map +2 -2
  49. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +2 -1
  50. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  51. package/dist-esm/lib/ui/components/primitives/TldrawUiDialog.mjs +1 -1
  52. package/dist-esm/lib/ui/components/primitives/TldrawUiDialog.mjs.map +2 -2
  53. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +5 -2
  54. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
  55. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +1 -0
  56. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  57. package/dist-esm/lib/ui/version.mjs +3 -3
  58. package/dist-esm/lib/ui/version.mjs.map +1 -1
  59. package/package.json +4 -3
  60. package/src/index.ts +1 -0
  61. package/src/lib/canvas/TldrawCropHandles.tsx +1 -1
  62. package/src/lib/canvas/TldrawHandles.tsx +5 -1
  63. package/src/lib/canvas/TldrawOverlays.tsx +1 -1
  64. package/src/lib/canvas/TldrawSelectionForeground.tsx +5 -1
  65. package/src/lib/shapes/shared/PathBuilder.test.tsx +1 -1
  66. package/src/lib/shapes/shared/PathBuilder.tsx +35 -1
  67. package/src/lib/shapes/shared/PlainTextLabel.tsx +1 -0
  68. package/src/lib/shapes/shared/RichTextLabel.tsx +1 -0
  69. package/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx +3 -4
  70. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +1 -0
  71. package/src/lib/ui/components/primitives/TldrawUiDialog.tsx +1 -1
  72. package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +5 -1
  73. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +4 -0
  74. package/src/lib/ui/version.ts +3 -3
  75. package/src/lib/ui.css +0 -14
  76. package/src/test/navigation.test.ts +254 -0
  77. package/tldraw.css +2 -16
@@ -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-label={msg('style-panel.opacity')}
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}
@@ -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.d30ed5ad740e'
4
+ export const version = '3.15.0-next.e136ad205948'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-07-10T10:00:49.217Z',
8
- patch: '2025-07-10T10:00:49.217Z',
7
+ minor: '2025-07-16T10:25:56.932Z',
8
+ patch: '2025-07-16T10:25:56.932Z',
9
9
  }
package/src/lib/ui.css CHANGED
@@ -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 {
@@ -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([
package/tldraw.css CHANGED
@@ -171,7 +171,7 @@
171
171
  --color-text: hsl(0, 0%, 0%);
172
172
  --color-text-0: hsl(0, 0%, 11%);
173
173
  --color-text-1: hsl(0, 0%, 18%);
174
- --color-text-3: hsl(220, 2%, 65%);
174
+ --color-text-3: hsl(204, 4%, 45%);
175
175
  --color-text-shadow: hsl(0, 0%, 100%);
176
176
  --color-text-highlight: hsl(52, 100%, 50%);
177
177
  --color-text-highlight-p3: color(display-p3 0.972 0.8205 0.05);
@@ -226,7 +226,7 @@
226
226
  --color-text: hsl(210, 17%, 98%);
227
227
  --color-text-0: hsl(0, 9%, 94%);
228
228
  --color-text-1: hsl(0, 0%, 85%);
229
- --color-text-3: hsl(210, 6%, 45%);
229
+ --color-text-3: hsl(204, 4%, 75%);
230
230
  --color-text-shadow: hsl(210, 13%, 18%);
231
231
  --color-text-highlight: hsl(52, 100%, 41%);
232
232
  --color-text-highlight-p3: color(display-p3 0.8078 0.6225 0.0312);
@@ -2892,20 +2892,6 @@ it from receiving any pointer events or affecting the cursor. */
2892
2892
  display: none;
2893
2893
  }
2894
2894
 
2895
- .tlui-navigation-panel__toggle .tlui-icon {
2896
- opacity: 0.24;
2897
- }
2898
-
2899
- .tlui-navigation-panel__toggle:active .tlui-icon {
2900
- opacity: 1;
2901
- }
2902
-
2903
- @media (hover: hover) {
2904
- .tlui-navigation-panel__toggle:hover .tlui-icon {
2905
- opacity: 1;
2906
- }
2907
- }
2908
-
2909
2895
  /* Minimap */
2910
2896
 
2911
2897
  .tlui-minimap {