tldraw 4.4.0-canary.afdcafe834b3 → 4.4.0-canary.bb78e813fee7

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.
@@ -0,0 +1,114 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useContainer } from "@tldraw/editor";
3
+ import classNames from "classnames";
4
+ import { Select as _Select } from "radix-ui";
5
+ import * as React from "react";
6
+ import { useMenuIsOpen } from "../../hooks/useMenuIsOpen.mjs";
7
+ import { TldrawUiIcon } from "./TldrawUiIcon.mjs";
8
+ function TldrawUiSelect({
9
+ id,
10
+ value,
11
+ onValueChange,
12
+ onOpenChange,
13
+ disabled,
14
+ className,
15
+ children,
16
+ "data-testid": dataTestId,
17
+ "aria-label": ariaLabel
18
+ }) {
19
+ const [open, handleOpenChange] = useMenuIsOpen(id, onOpenChange);
20
+ return /* @__PURE__ */ jsx(
21
+ _Select.Root,
22
+ {
23
+ value,
24
+ onValueChange,
25
+ onOpenChange: handleOpenChange,
26
+ open,
27
+ disabled,
28
+ dir: "ltr",
29
+ children: /* @__PURE__ */ jsx(
30
+ "div",
31
+ {
32
+ id,
33
+ className: classNames("tlui-select", className),
34
+ "data-testid": dataTestId,
35
+ "aria-label": ariaLabel,
36
+ children
37
+ }
38
+ )
39
+ }
40
+ );
41
+ }
42
+ const TldrawUiSelectTrigger = React.forwardRef(
43
+ function TldrawUiSelectTrigger2({ children, className }, ref) {
44
+ return /* @__PURE__ */ jsxs(
45
+ _Select.Trigger,
46
+ {
47
+ ref,
48
+ className: classNames("tlui-button tlui-select__trigger", className),
49
+ children: [
50
+ children,
51
+ /* @__PURE__ */ jsx(_Select.Icon, { className: "tlui-select__chevron", children: /* @__PURE__ */ jsx(TldrawUiIcon, { icon: "chevron-down", label: "", small: true }) })
52
+ ]
53
+ }
54
+ );
55
+ }
56
+ );
57
+ function TldrawUiSelectValue({ placeholder, icon, children }) {
58
+ return /* @__PURE__ */ jsx(_Select.Value, { placeholder, children: /* @__PURE__ */ jsxs("span", { className: "tlui-select__value", children: [
59
+ icon && /* @__PURE__ */ jsx(TldrawUiIcon, { icon, label: "", small: true }),
60
+ /* @__PURE__ */ jsx("span", { className: "tlui-button__label", children })
61
+ ] }) });
62
+ }
63
+ function TldrawUiSelectContent({
64
+ children,
65
+ side = "bottom",
66
+ align = "start",
67
+ className
68
+ }) {
69
+ const container = useContainer();
70
+ return /* @__PURE__ */ jsx(_Select.Portal, { container, children: /* @__PURE__ */ jsx(
71
+ _Select.Content,
72
+ {
73
+ className: classNames("tlui-menu tlui-select__content", className),
74
+ position: "popper",
75
+ side,
76
+ align,
77
+ sideOffset: 4,
78
+ collisionPadding: 4,
79
+ children: /* @__PURE__ */ jsx(_Select.Viewport, { className: "tlui-select__viewport", children })
80
+ }
81
+ ) });
82
+ }
83
+ function TldrawUiSelectItem({
84
+ value,
85
+ label,
86
+ icon,
87
+ disabled,
88
+ className
89
+ }) {
90
+ return /* @__PURE__ */ jsxs(
91
+ _Select.Item,
92
+ {
93
+ value,
94
+ disabled,
95
+ className: classNames(
96
+ "tlui-button tlui-button__menu tlui-button__checkbox tlui-select__item",
97
+ className
98
+ ),
99
+ children: [
100
+ /* @__PURE__ */ jsx(TldrawUiIcon, { small: true, icon: "check", label: "", className: "tlui-select__item-indicator" }),
101
+ icon && /* @__PURE__ */ jsx(TldrawUiIcon, { icon, label: "", small: true }),
102
+ /* @__PURE__ */ jsx(_Select.ItemText, { className: "tlui-button__label", children: label })
103
+ ]
104
+ }
105
+ );
106
+ }
107
+ export {
108
+ TldrawUiSelect,
109
+ TldrawUiSelectContent,
110
+ TldrawUiSelectItem,
111
+ TldrawUiSelectTrigger,
112
+ TldrawUiSelectValue
113
+ };
114
+ //# sourceMappingURL=TldrawUiSelect.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiSelect.tsx"],
4
+ "sourcesContent": ["import { useContainer } from '@tldraw/editor'\nimport classNames from 'classnames'\nimport { Select as _Select } from 'radix-ui'\nimport * as React from 'react'\nimport { useMenuIsOpen } from '../../hooks/useMenuIsOpen'\nimport { TLUiIconType } from '../../icon-types'\nimport { TldrawUiIcon } from './TldrawUiIcon'\n\n/* --------------------- Root --------------------- */\n\n/** @public */\nexport interface TLUiSelectProps {\n\tid: string\n\tvalue: string\n\tonValueChange(value: string): void\n\tonOpenChange?(isOpen: boolean): void\n\tdisabled?: boolean\n\tclassName?: string\n\tchildren: React.ReactNode\n\t'data-testid'?: string\n\t'aria-label'?: string\n}\n\n/**\n * A select dropdown component.\n *\n * @example\n * ```tsx\n * <TldrawUiSelect id=\"my-select\" value={value} onValueChange={setValue}>\n * <TldrawUiSelectTrigger>\n * <TldrawUiSelectValue placeholder=\"Select...\" />\n * </TldrawUiSelectTrigger>\n * <TldrawUiSelectContent>\n * <TldrawUiSelectItem value=\"one\" label=\"One\" />\n * <TldrawUiSelectItem value=\"two\" label=\"Two\" />\n * </TldrawUiSelectContent>\n * </TldrawUiSelect>\n * ```\n *\n * @public\n * @react\n */\nexport function TldrawUiSelect({\n\tid,\n\tvalue,\n\tonValueChange,\n\tonOpenChange,\n\tdisabled,\n\tclassName,\n\tchildren,\n\t'data-testid': dataTestId,\n\t'aria-label': ariaLabel,\n}: TLUiSelectProps) {\n\tconst [open, handleOpenChange] = useMenuIsOpen(id, onOpenChange)\n\n\treturn (\n\t\t<_Select.Root\n\t\t\tvalue={value}\n\t\t\tonValueChange={onValueChange}\n\t\t\tonOpenChange={handleOpenChange}\n\t\t\topen={open}\n\t\t\tdisabled={disabled}\n\t\t\tdir=\"ltr\"\n\t\t>\n\t\t\t<div\n\t\t\t\tid={id}\n\t\t\t\tclassName={classNames('tlui-select', className)}\n\t\t\t\tdata-testid={dataTestId}\n\t\t\t\taria-label={ariaLabel}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</_Select.Root>\n\t)\n}\n\n/* --------------------- Trigger --------------------- */\n\n/** @public */\nexport interface TLUiSelectTriggerProps {\n\tchildren: React.ReactNode\n\tclassName?: string\n}\n\n/**\n * The trigger button for the select dropdown.\n *\n * @public\n * @react\n */\nexport const TldrawUiSelectTrigger = React.forwardRef<HTMLButtonElement, TLUiSelectTriggerProps>(\n\tfunction TldrawUiSelectTrigger({ children, className }, ref) {\n\t\treturn (\n\t\t\t<_Select.Trigger\n\t\t\t\tref={ref}\n\t\t\t\tclassName={classNames('tlui-button tlui-select__trigger', className)}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t\t<_Select.Icon className=\"tlui-select__chevron\">\n\t\t\t\t\t<TldrawUiIcon icon=\"chevron-down\" label=\"\" small />\n\t\t\t\t</_Select.Icon>\n\t\t\t</_Select.Trigger>\n\t\t)\n\t}\n)\n\n/* --------------------- Value --------------------- */\n\n/** @public */\nexport interface TLUiSelectValueProps {\n\tplaceholder?: string\n\ticon?: TLUiIconType | Exclude<string, TLUiIconType>\n\tchildren?: React.ReactNode\n}\n\n/**\n * Displays the currently selected value in the trigger.\n *\n * @public\n * @react\n */\nexport function TldrawUiSelectValue({ placeholder, icon, children }: TLUiSelectValueProps) {\n\treturn (\n\t\t<_Select.Value placeholder={placeholder}>\n\t\t\t<span className=\"tlui-select__value\">\n\t\t\t\t{icon && <TldrawUiIcon icon={icon} label=\"\" small />}\n\t\t\t\t<span className=\"tlui-button__label\">{children}</span>\n\t\t\t</span>\n\t\t</_Select.Value>\n\t)\n}\n\n/* --------------------- Content --------------------- */\n\n/** @public */\nexport interface TLUiSelectContentProps {\n\tchildren: React.ReactNode\n\tside?: 'top' | 'bottom'\n\talign?: 'start' | 'center' | 'end'\n\tclassName?: string\n}\n\n/**\n * The dropdown content container for select items.\n *\n * @public\n * @react\n */\nexport function TldrawUiSelectContent({\n\tchildren,\n\tside = 'bottom',\n\talign = 'start',\n\tclassName,\n}: TLUiSelectContentProps) {\n\tconst container = useContainer()\n\n\treturn (\n\t\t<_Select.Portal container={container}>\n\t\t\t<_Select.Content\n\t\t\t\tclassName={classNames('tlui-menu tlui-select__content', className)}\n\t\t\t\tposition=\"popper\"\n\t\t\t\tside={side}\n\t\t\t\talign={align}\n\t\t\t\tsideOffset={4}\n\t\t\t\tcollisionPadding={4}\n\t\t\t>\n\t\t\t\t<_Select.Viewport className=\"tlui-select__viewport\">{children}</_Select.Viewport>\n\t\t\t</_Select.Content>\n\t\t</_Select.Portal>\n\t)\n}\n\n/* --------------------- Item --------------------- */\n\n/** @public */\nexport interface TLUiSelectItemProps {\n\tvalue: string\n\tlabel: string\n\ticon?: TLUiIconType | Exclude<string, TLUiIconType>\n\tdisabled?: boolean\n\tclassName?: string\n}\n\n/**\n * An item in the select dropdown. Styled to match TldrawUiMenuCheckboxItem.\n *\n * @public\n * @react\n */\nexport function TldrawUiSelectItem({\n\tvalue,\n\tlabel,\n\ticon,\n\tdisabled,\n\tclassName,\n}: TLUiSelectItemProps) {\n\treturn (\n\t\t<_Select.Item\n\t\t\tvalue={value}\n\t\t\tdisabled={disabled}\n\t\t\tclassName={classNames(\n\t\t\t\t'tlui-button tlui-button__menu tlui-button__checkbox tlui-select__item',\n\t\t\t\tclassName\n\t\t\t)}\n\t\t>\n\t\t\t<TldrawUiIcon small icon=\"check\" label=\"\" className=\"tlui-select__item-indicator\" />\n\t\t\t{icon && <TldrawUiIcon icon={icon} label=\"\" small />}\n\t\t\t<_Select.ItemText className=\"tlui-button__label\">{label}</_Select.ItemText>\n\t\t</_Select.Item>\n\t)\n}\n"],
5
+ "mappings": "AAgEG,cA6BA,YA7BA;AAhEH,SAAS,oBAAoB;AAC7B,OAAO,gBAAgB;AACvB,SAAS,UAAU,eAAe;AAClC,YAAY,WAAW;AACvB,SAAS,qBAAqB;AAE9B,SAAS,oBAAoB;AAoCtB,SAAS,eAAe;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,cAAc;AACf,GAAoB;AACnB,QAAM,CAAC,MAAM,gBAAgB,IAAI,cAAc,IAAI,YAAY;AAE/D,SACC;AAAA,IAAC,QAAQ;AAAA,IAAR;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA,KAAI;AAAA,MAEJ;AAAA,QAAC;AAAA;AAAA,UACA;AAAA,UACA,WAAW,WAAW,eAAe,SAAS;AAAA,UAC9C,eAAa;AAAA,UACb,cAAY;AAAA,UAEX;AAAA;AAAA,MACF;AAAA;AAAA,EACD;AAEF;AAgBO,MAAM,wBAAwB,MAAM;AAAA,EAC1C,SAASA,uBAAsB,EAAE,UAAU,UAAU,GAAG,KAAK;AAC5D,WACC;AAAA,MAAC,QAAQ;AAAA,MAAR;AAAA,QACA;AAAA,QACA,WAAW,WAAW,oCAAoC,SAAS;AAAA,QAElE;AAAA;AAAA,UACD,oBAAC,QAAQ,MAAR,EAAa,WAAU,wBACvB,8BAAC,gBAAa,MAAK,gBAAe,OAAM,IAAG,OAAK,MAAC,GAClD;AAAA;AAAA;AAAA,IACD;AAAA,EAEF;AACD;AAiBO,SAAS,oBAAoB,EAAE,aAAa,MAAM,SAAS,GAAyB;AAC1F,SACC,oBAAC,QAAQ,OAAR,EAAc,aACd,+BAAC,UAAK,WAAU,sBACd;AAAA,YAAQ,oBAAC,gBAAa,MAAY,OAAM,IAAG,OAAK,MAAC;AAAA,IAClD,oBAAC,UAAK,WAAU,sBAAsB,UAAS;AAAA,KAChD,GACD;AAEF;AAkBO,SAAS,sBAAsB;AAAA,EACrC;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AACD,GAA2B;AAC1B,QAAM,YAAY,aAAa;AAE/B,SACC,oBAAC,QAAQ,QAAR,EAAe,WACf;AAAA,IAAC,QAAQ;AAAA,IAAR;AAAA,MACA,WAAW,WAAW,kCAAkC,SAAS;AAAA,MACjE,UAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,kBAAkB;AAAA,MAElB,8BAAC,QAAQ,UAAR,EAAiB,WAAU,yBAAyB,UAAS;AAAA;AAAA,EAC/D,GACD;AAEF;AAmBO,SAAS,mBAAmB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAwB;AACvB,SACC;AAAA,IAAC,QAAQ;AAAA,IAAR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,QACV;AAAA,QACA;AAAA,MACD;AAAA,MAEA;AAAA,4BAAC,gBAAa,OAAK,MAAC,MAAK,SAAQ,OAAM,IAAG,WAAU,+BAA8B;AAAA,QACjF,QAAQ,oBAAC,gBAAa,MAAY,OAAM,IAAG,OAAK,MAAC;AAAA,QAClD,oBAAC,QAAQ,UAAR,EAAiB,WAAU,sBAAsB,iBAAM;AAAA;AAAA;AAAA,EACzD;AAEF;",
6
+ "names": ["TldrawUiSelectTrigger"]
7
+ }
@@ -1,8 +1,8 @@
1
- const version = "4.4.0-canary.afdcafe834b3";
1
+ const version = "4.4.0-canary.bb78e813fee7";
2
2
  const publishDates = {
3
3
  major: "2025-09-18T14:39:22.803Z",
4
- minor: "2026-02-04T08:49:09.694Z",
5
- patch: "2026-02-04T08:49:09.694Z"
4
+ minor: "2026-02-04T09:45:29.031Z",
5
+ patch: "2026-02-04T09:45:29.031Z"
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 = '4.4.0-canary.afdcafe834b3'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2026-02-04T08:49:09.694Z',\n\tpatch: '2026-02-04T08:49:09.694Z',\n}\n"],
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 = '4.4.0-canary.bb78e813fee7'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2026-02-04T09:45:29.031Z',\n\tpatch: '2026-02-04T09:45:29.031Z',\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": "4.4.0-canary.afdcafe834b3",
4
+ "version": "4.4.0-canary.bb78e813fee7",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -62,8 +62,8 @@
62
62
  "@tiptap/pm": "^3.12.1",
63
63
  "@tiptap/react": "^3.12.1",
64
64
  "@tiptap/starter-kit": "^3.12.1",
65
- "@tldraw/editor": "4.4.0-canary.afdcafe834b3",
66
- "@tldraw/store": "4.4.0-canary.afdcafe834b3",
65
+ "@tldraw/editor": "4.4.0-canary.bb78e813fee7",
66
+ "@tldraw/store": "4.4.0-canary.bb78e813fee7",
67
67
  "classnames": "^2.5.1",
68
68
  "hotkeys-js": "^3.13.9",
69
69
  "idb": "^7.1.1",
package/src/index.ts CHANGED
@@ -400,6 +400,7 @@ export {
400
400
  TldrawUiDropdownMenuItem,
401
401
  TldrawUiDropdownMenuRoot,
402
402
  TldrawUiDropdownMenuSub,
403
+ TldrawUiDropdownMenuSubContent,
403
404
  TldrawUiDropdownMenuSubTrigger,
404
405
  TldrawUiDropdownMenuTrigger,
405
406
  type TLUiDropdownMenuCheckboxItemProps,
@@ -407,6 +408,7 @@ export {
407
408
  type TLUiDropdownMenuGroupProps,
408
409
  type TLUiDropdownMenuItemProps,
409
410
  type TLUiDropdownMenuRootProps,
411
+ type TLUiDropdownMenuSubContentProps,
410
412
  type TLUiDropdownMenuSubProps,
411
413
  type TLUiDropdownMenuSubTriggerProps,
412
414
  type TLUiDropdownMenuTriggerProps,
@@ -426,6 +428,18 @@ export {
426
428
  type TLUiPopoverProps,
427
429
  type TLUiPopoverTriggerProps,
428
430
  } from './lib/ui/components/primitives/TldrawUiPopover'
431
+ export {
432
+ TldrawUiSelect,
433
+ TldrawUiSelectContent,
434
+ TldrawUiSelectItem,
435
+ TldrawUiSelectTrigger,
436
+ TldrawUiSelectValue,
437
+ type TLUiSelectContentProps,
438
+ type TLUiSelectItemProps,
439
+ type TLUiSelectProps,
440
+ type TLUiSelectTriggerProps,
441
+ type TLUiSelectValueProps,
442
+ } from './lib/ui/components/primitives/TldrawUiSelect'
429
443
  export { TldrawUiSlider, type TLUiSliderProps } from './lib/ui/components/primitives/TldrawUiSlider'
430
444
  export {
431
445
  TldrawUiToolbar,
@@ -671,7 +685,7 @@ export {
671
685
  type TLUiTranslationContextType,
672
686
  type TLUiTranslationProviderProps,
673
687
  } from './lib/ui/hooks/useTranslation/useTranslation'
674
- export { type TLUiIconType } from './lib/ui/icon-types'
688
+ export { iconTypes, type TLUiIconType } from './lib/ui/icon-types'
675
689
  export { useDefaultHelpers, type TLUiOverrideHelpers, type TLUiOverrides } from './lib/ui/overrides'
676
690
  export { TldrawUi, TldrawUiInFrontOfTheCanvas, type TldrawUiProps } from './lib/ui/TldrawUi'
677
691
  export { containBoxSize, downsizeImage, type BoxWidthHeight } from './lib/utils/assets/assets'
@@ -58,8 +58,20 @@ export class ArrowBindingUtil extends BindingUtil<TLArrowBinding> {
58
58
 
59
59
  // when the arrow itself changes
60
60
  override onAfterChangeFromShape({
61
+ shapeBefore,
61
62
  shapeAfter,
63
+ reason,
62
64
  }: BindingOnShapeChangeOptions<TLArrowBinding>): void {
65
+ // When translating arrows together with their bound shapes, only x/y changes.
66
+ // In this case, bindings remain valid and no reparenting is needed.
67
+ // This is a significant performance optimization when moving many bound shapes.
68
+ if (
69
+ reason !== 'ancestry' &&
70
+ shapeBefore.parentId === shapeAfter.parentId &&
71
+ shapeBefore.index === shapeAfter.index
72
+ ) {
73
+ return
74
+ }
63
75
  arrowDidUpdate(this.editor, shapeAfter as TLArrowShape)
64
76
  }
65
77
 
@@ -0,0 +1,211 @@
1
+ import { useContainer } from '@tldraw/editor'
2
+ import classNames from 'classnames'
3
+ import { Select as _Select } from 'radix-ui'
4
+ import * as React from 'react'
5
+ import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
6
+ import { TLUiIconType } from '../../icon-types'
7
+ import { TldrawUiIcon } from './TldrawUiIcon'
8
+
9
+ /* --------------------- Root --------------------- */
10
+
11
+ /** @public */
12
+ export interface TLUiSelectProps {
13
+ id: string
14
+ value: string
15
+ onValueChange(value: string): void
16
+ onOpenChange?(isOpen: boolean): void
17
+ disabled?: boolean
18
+ className?: string
19
+ children: React.ReactNode
20
+ 'data-testid'?: string
21
+ 'aria-label'?: string
22
+ }
23
+
24
+ /**
25
+ * A select dropdown component.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <TldrawUiSelect id="my-select" value={value} onValueChange={setValue}>
30
+ * <TldrawUiSelectTrigger>
31
+ * <TldrawUiSelectValue placeholder="Select..." />
32
+ * </TldrawUiSelectTrigger>
33
+ * <TldrawUiSelectContent>
34
+ * <TldrawUiSelectItem value="one" label="One" />
35
+ * <TldrawUiSelectItem value="two" label="Two" />
36
+ * </TldrawUiSelectContent>
37
+ * </TldrawUiSelect>
38
+ * ```
39
+ *
40
+ * @public
41
+ * @react
42
+ */
43
+ export function TldrawUiSelect({
44
+ id,
45
+ value,
46
+ onValueChange,
47
+ onOpenChange,
48
+ disabled,
49
+ className,
50
+ children,
51
+ 'data-testid': dataTestId,
52
+ 'aria-label': ariaLabel,
53
+ }: TLUiSelectProps) {
54
+ const [open, handleOpenChange] = useMenuIsOpen(id, onOpenChange)
55
+
56
+ return (
57
+ <_Select.Root
58
+ value={value}
59
+ onValueChange={onValueChange}
60
+ onOpenChange={handleOpenChange}
61
+ open={open}
62
+ disabled={disabled}
63
+ dir="ltr"
64
+ >
65
+ <div
66
+ id={id}
67
+ className={classNames('tlui-select', className)}
68
+ data-testid={dataTestId}
69
+ aria-label={ariaLabel}
70
+ >
71
+ {children}
72
+ </div>
73
+ </_Select.Root>
74
+ )
75
+ }
76
+
77
+ /* --------------------- Trigger --------------------- */
78
+
79
+ /** @public */
80
+ export interface TLUiSelectTriggerProps {
81
+ children: React.ReactNode
82
+ className?: string
83
+ }
84
+
85
+ /**
86
+ * The trigger button for the select dropdown.
87
+ *
88
+ * @public
89
+ * @react
90
+ */
91
+ export const TldrawUiSelectTrigger = React.forwardRef<HTMLButtonElement, TLUiSelectTriggerProps>(
92
+ function TldrawUiSelectTrigger({ children, className }, ref) {
93
+ return (
94
+ <_Select.Trigger
95
+ ref={ref}
96
+ className={classNames('tlui-button tlui-select__trigger', className)}
97
+ >
98
+ {children}
99
+ <_Select.Icon className="tlui-select__chevron">
100
+ <TldrawUiIcon icon="chevron-down" label="" small />
101
+ </_Select.Icon>
102
+ </_Select.Trigger>
103
+ )
104
+ }
105
+ )
106
+
107
+ /* --------------------- Value --------------------- */
108
+
109
+ /** @public */
110
+ export interface TLUiSelectValueProps {
111
+ placeholder?: string
112
+ icon?: TLUiIconType | Exclude<string, TLUiIconType>
113
+ children?: React.ReactNode
114
+ }
115
+
116
+ /**
117
+ * Displays the currently selected value in the trigger.
118
+ *
119
+ * @public
120
+ * @react
121
+ */
122
+ export function TldrawUiSelectValue({ placeholder, icon, children }: TLUiSelectValueProps) {
123
+ return (
124
+ <_Select.Value placeholder={placeholder}>
125
+ <span className="tlui-select__value">
126
+ {icon && <TldrawUiIcon icon={icon} label="" small />}
127
+ <span className="tlui-button__label">{children}</span>
128
+ </span>
129
+ </_Select.Value>
130
+ )
131
+ }
132
+
133
+ /* --------------------- Content --------------------- */
134
+
135
+ /** @public */
136
+ export interface TLUiSelectContentProps {
137
+ children: React.ReactNode
138
+ side?: 'top' | 'bottom'
139
+ align?: 'start' | 'center' | 'end'
140
+ className?: string
141
+ }
142
+
143
+ /**
144
+ * The dropdown content container for select items.
145
+ *
146
+ * @public
147
+ * @react
148
+ */
149
+ export function TldrawUiSelectContent({
150
+ children,
151
+ side = 'bottom',
152
+ align = 'start',
153
+ className,
154
+ }: TLUiSelectContentProps) {
155
+ const container = useContainer()
156
+
157
+ return (
158
+ <_Select.Portal container={container}>
159
+ <_Select.Content
160
+ className={classNames('tlui-menu tlui-select__content', className)}
161
+ position="popper"
162
+ side={side}
163
+ align={align}
164
+ sideOffset={4}
165
+ collisionPadding={4}
166
+ >
167
+ <_Select.Viewport className="tlui-select__viewport">{children}</_Select.Viewport>
168
+ </_Select.Content>
169
+ </_Select.Portal>
170
+ )
171
+ }
172
+
173
+ /* --------------------- Item --------------------- */
174
+
175
+ /** @public */
176
+ export interface TLUiSelectItemProps {
177
+ value: string
178
+ label: string
179
+ icon?: TLUiIconType | Exclude<string, TLUiIconType>
180
+ disabled?: boolean
181
+ className?: string
182
+ }
183
+
184
+ /**
185
+ * An item in the select dropdown. Styled to match TldrawUiMenuCheckboxItem.
186
+ *
187
+ * @public
188
+ * @react
189
+ */
190
+ export function TldrawUiSelectItem({
191
+ value,
192
+ label,
193
+ icon,
194
+ disabled,
195
+ className,
196
+ }: TLUiSelectItemProps) {
197
+ return (
198
+ <_Select.Item
199
+ value={value}
200
+ disabled={disabled}
201
+ className={classNames(
202
+ 'tlui-button tlui-button__menu tlui-button__checkbox tlui-select__item',
203
+ className
204
+ )}
205
+ >
206
+ <TldrawUiIcon small icon="check" label="" className="tlui-select__item-indicator" />
207
+ {icon && <TldrawUiIcon icon={icon} label="" small />}
208
+ <_Select.ItemText className="tlui-button__label">{label}</_Select.ItemText>
209
+ </_Select.Item>
210
+ )
211
+ }
@@ -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 = '4.4.0-canary.afdcafe834b3'
4
+ export const version = '4.4.0-canary.bb78e813fee7'
5
5
  export const publishDates = {
6
6
  major: '2025-09-18T14:39:22.803Z',
7
- minor: '2026-02-04T08:49:09.694Z',
8
- patch: '2026-02-04T08:49:09.694Z',
7
+ minor: '2026-02-04T09:45:29.031Z',
8
+ patch: '2026-02-04T09:45:29.031Z',
9
9
  }
package/src/lib/ui.css CHANGED
@@ -523,6 +523,52 @@
523
523
  }
524
524
  }
525
525
 
526
+ /* --------------------- Select --------------------- */
527
+
528
+ /* Trigger extends .tlui-button with space-between layout */
529
+ .tlui-select__trigger {
530
+ justify-content: space-between;
531
+ gap: var(--tl-space-3);
532
+ }
533
+
534
+ .tlui-select__trigger[data-state='open']::after {
535
+ opacity: 1;
536
+ }
537
+
538
+ .tlui-select__value {
539
+ display: flex;
540
+ align-items: center;
541
+ gap: var(--tl-space-2);
542
+ }
543
+
544
+ .tlui-select__chevron {
545
+ flex-shrink: 0;
546
+ color: var(--tl-color-text-2);
547
+ }
548
+
549
+ /* Content extends .tlui-menu with Radix-aware sizing */
550
+ .tlui-select__content {
551
+ min-width: var(--radix-select-trigger-width);
552
+ max-height: var(--radix-select-content-available-height);
553
+ }
554
+
555
+ .tlui-select__viewport {
556
+ padding: 4px 0;
557
+ }
558
+
559
+ /* Item extends .tlui-button__checkbox with left-aligned text */
560
+ .tlui-select__item {
561
+ justify-content: flex-start;
562
+ }
563
+
564
+ .tlui-select__item-indicator {
565
+ visibility: hidden;
566
+ }
567
+
568
+ .tlui-select__item[data-state='checked'] .tlui-select__item-indicator {
569
+ visibility: visible;
570
+ }
571
+
526
572
  /* ----------------------- Kbd ---------------------- */
527
573
 
528
574
  .tlui-kbd {
package/tldraw.css CHANGED
@@ -2323,6 +2323,52 @@ it from receiving any pointer events or affecting the cursor. */
2323
2323
  }
2324
2324
  }
2325
2325
 
2326
+ /* --------------------- Select --------------------- */
2327
+
2328
+ /* Trigger extends .tlui-button with space-between layout */
2329
+ .tlui-select__trigger {
2330
+ justify-content: space-between;
2331
+ gap: var(--tl-space-3);
2332
+ }
2333
+
2334
+ .tlui-select__trigger[data-state='open']::after {
2335
+ opacity: 1;
2336
+ }
2337
+
2338
+ .tlui-select__value {
2339
+ display: flex;
2340
+ align-items: center;
2341
+ gap: var(--tl-space-2);
2342
+ }
2343
+
2344
+ .tlui-select__chevron {
2345
+ flex-shrink: 0;
2346
+ color: var(--tl-color-text-2);
2347
+ }
2348
+
2349
+ /* Content extends .tlui-menu with Radix-aware sizing */
2350
+ .tlui-select__content {
2351
+ min-width: var(--radix-select-trigger-width);
2352
+ max-height: var(--radix-select-content-available-height);
2353
+ }
2354
+
2355
+ .tlui-select__viewport {
2356
+ padding: 4px 0;
2357
+ }
2358
+
2359
+ /* Item extends .tlui-button__checkbox with left-aligned text */
2360
+ .tlui-select__item {
2361
+ justify-content: flex-start;
2362
+ }
2363
+
2364
+ .tlui-select__item-indicator {
2365
+ visibility: hidden;
2366
+ }
2367
+
2368
+ .tlui-select__item[data-state='checked'] .tlui-select__item-indicator {
2369
+ visibility: visible;
2370
+ }
2371
+
2326
2372
  /* ----------------------- Kbd ---------------------- */
2327
2373
 
2328
2374
  .tlui-kbd {