@heyramzi/ui 3.1.0 → 3.2.0

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 (144) hide show
  1. package/dist/lib/sliding-pill.js +2 -2
  2. package/dist/record/measure-clamp.js +23 -0
  3. package/dist/shadcn/button-group/index.js +6 -0
  4. package/dist/shadcn/checkbox/checkbox-box.js +3 -0
  5. package/dist/shadcn/input-group/index.js +9 -0
  6. package/dist/shadcn/item/index.js +13 -0
  7. package/dist/shadcn/native-select/index.js +6 -0
  8. package/dist/shadcn/pagination/index.js +12 -0
  9. package/package.json +1 -1
  10. package/scripts/sync-shadcn.mjs +0 -5
  11. package/src/ai/generation-progress/GenerationProgress.svelte +14 -12
  12. package/src/canvas/pen/PenOverlay.svelte +1 -30
  13. package/src/canvas/screen/PartView.svelte +13 -8
  14. package/src/canvas/screen/ScreenBody.svelte +18 -11
  15. package/src/canvas/ui/BarTooltip.svelte +46 -0
  16. package/src/canvas/ui/BarTooltip.svelte.d.ts +17 -0
  17. package/src/canvas/ui/CanvasToolbar.svelte +170 -156
  18. package/src/canvas/ui/StylePanel.svelte +179 -102
  19. package/src/canvas/ui/canvas-ui.css +12 -76
  20. package/src/charts/dashboard/data-table.svelte +80 -80
  21. package/src/charts/dashboard/hint.svelte +34 -0
  22. package/src/charts/dashboard/hint.svelte.d.ts +10 -0
  23. package/src/charts/dashboard/rating-chip.svelte +4 -3
  24. package/src/charts/legend.css +0 -8
  25. package/src/charts/legend.svelte +26 -30
  26. package/src/components/copy/copy-button.svelte +10 -2
  27. package/src/components/copy/copy-button.test.ts +7 -0
  28. package/src/components/data-table/table-skeleton.svelte +15 -7
  29. package/src/components/data-table/table-skeleton.svelte.d.ts +4 -0
  30. package/src/components/docs/on-this-page.svelte +2 -2
  31. package/src/components/hold-to-confirm/hold-to-confirm.svelte +2 -1
  32. package/src/components/list-page/filter-item.svelte +20 -7
  33. package/src/components/list-page/filter-popover.svelte +41 -16
  34. package/src/components/list-page/filter-popover.svelte.d.ts +1 -1
  35. package/src/components/list-page/grouped-list.svelte +41 -23
  36. package/src/components/picker/picker.svelte +8 -3
  37. package/src/components/picker/picker.svelte.d.ts +2 -1
  38. package/src/components/segmented/segmented-filter.svelte +19 -10
  39. package/src/components/segmented/segmented-track.svelte +11 -3
  40. package/src/components/table-view/table-view.svelte +22 -6
  41. package/src/components/tags-input/tags-input.svelte +94 -77
  42. package/src/components/toolbar/toolbar.svelte +2 -1
  43. package/src/components/toolbar/toolbar.test.ts +5 -4
  44. package/src/components/work-parts/group-head.svelte +28 -9
  45. package/src/components/work-parts/group-head.svelte.d.ts +5 -2
  46. package/src/editor/editor-toolbar.svelte +223 -193
  47. package/src/editor/editor.svelte +56 -38
  48. package/src/editor/image-toolbar.svelte +99 -37
  49. package/src/editor/image-toolbar.svelte.d.ts +4 -5
  50. package/src/editor/url-dialog.svelte +96 -0
  51. package/src/editor/url-dialog.svelte.d.ts +15 -0
  52. package/src/effects/theme-toggle/ThemeToggle.css +8 -8
  53. package/src/effects/theme-toggle/ThemeToggle.svelte +20 -6
  54. package/src/inbox/thread-inbox.svelte +9 -1
  55. package/src/lib/live-region.svelte +12 -4
  56. package/src/lib/live-region.svelte.d.ts +5 -1
  57. package/src/lib/sliding-pill.ts +3 -2
  58. package/src/record/info-list.svelte +33 -59
  59. package/src/record/measure-clamp.ts +31 -0
  60. package/src/record/record-shell.svelte +38 -39
  61. package/src/record/record-timeline.svelte +10 -5
  62. package/src/record/steps-rail.svelte +50 -36
  63. package/src/record/where-we-are.svelte +5 -19
  64. package/src/shadcn/button-group/button-group-separator.svelte +23 -0
  65. package/src/shadcn/button-group/button-group-separator.svelte.d.ts +13 -0
  66. package/src/shadcn/button-group/button-group-text.svelte +28 -0
  67. package/src/shadcn/button-group/button-group-text.svelte.d.ts +11 -0
  68. package/src/shadcn/button-group/button-group.svelte +46 -0
  69. package/src/shadcn/button-group/button-group.svelte.d.ts +16 -0
  70. package/src/shadcn/button-group/index.ts +15 -0
  71. package/src/shadcn/checkbox/checkbox-box.ts +4 -0
  72. package/src/shadcn/checkbox/checkbox.svelte +2 -4
  73. package/src/shadcn/input-group/index.ts +22 -0
  74. package/src/shadcn/input-group/input-group-addon.svelte +51 -0
  75. package/src/shadcn/input-group/input-group-addon.svelte.d.ts +18 -0
  76. package/src/shadcn/input-group/input-group-button.svelte +49 -0
  77. package/src/shadcn/input-group/input-group-button.svelte.d.ts +18 -0
  78. package/src/shadcn/input-group/input-group-input.svelte +20 -0
  79. package/src/shadcn/input-group/input-group-input.svelte.d.ts +11 -0
  80. package/src/shadcn/input-group/input-group-text.svelte +19 -0
  81. package/src/shadcn/input-group/input-group-text.svelte.d.ts +5 -0
  82. package/src/shadcn/input-group/input-group-textarea.svelte +20 -0
  83. package/src/shadcn/input-group/input-group-textarea.svelte.d.ts +3 -0
  84. package/src/shadcn/input-group/input-group.svelte +24 -0
  85. package/src/shadcn/input-group/input-group.svelte.d.ts +5 -0
  86. package/src/shadcn/item/index.ts +34 -0
  87. package/src/shadcn/item/item-actions.svelte +20 -0
  88. package/src/shadcn/item/item-actions.svelte.d.ts +5 -0
  89. package/src/shadcn/item/item-content.svelte +23 -0
  90. package/src/shadcn/item/item-content.svelte.d.ts +5 -0
  91. package/src/shadcn/item/item-description.svelte +23 -0
  92. package/src/shadcn/item/item-description.svelte.d.ts +5 -0
  93. package/src/shadcn/item/item-footer.svelte +20 -0
  94. package/src/shadcn/item/item-footer.svelte.d.ts +5 -0
  95. package/src/shadcn/item/item-group.svelte +21 -0
  96. package/src/shadcn/item/item-group.svelte.d.ts +5 -0
  97. package/src/shadcn/item/item-header.svelte +20 -0
  98. package/src/shadcn/item/item-header.svelte.d.ts +5 -0
  99. package/src/shadcn/item/item-media.svelte +42 -0
  100. package/src/shadcn/item/item-media.svelte.d.ts +17 -0
  101. package/src/shadcn/item/item-separator.svelte +19 -0
  102. package/src/shadcn/item/item-separator.svelte.d.ts +13 -0
  103. package/src/shadcn/item/item-title.svelte +20 -0
  104. package/src/shadcn/item/item-title.svelte.d.ts +5 -0
  105. package/src/shadcn/item/item.svelte +61 -0
  106. package/src/shadcn/item/item.svelte.d.ts +28 -0
  107. package/src/shadcn/native-select/index.ts +13 -0
  108. package/src/shadcn/native-select/native-select-opt-group.svelte +20 -0
  109. package/src/shadcn/native-select/native-select-opt-group.svelte.d.ts +5 -0
  110. package/src/shadcn/native-select/native-select-option.svelte +20 -0
  111. package/src/shadcn/native-select/native-select-option.svelte.d.ts +5 -0
  112. package/src/shadcn/native-select/native-select.svelte +45 -0
  113. package/src/shadcn/native-select/native-select.svelte.d.ts +8 -0
  114. package/src/shadcn/pagination/index.ts +31 -0
  115. package/src/shadcn/pagination/pagination-content.svelte +20 -0
  116. package/src/shadcn/pagination/pagination-content.svelte.d.ts +5 -0
  117. package/src/shadcn/pagination/pagination-ellipsis.svelte +22 -0
  118. package/src/shadcn/pagination/pagination-ellipsis.svelte.d.ts +5 -0
  119. package/src/shadcn/pagination/pagination-item.svelte +14 -0
  120. package/src/shadcn/pagination/pagination-item.svelte.d.ts +5 -0
  121. package/src/shadcn/pagination/pagination-link.svelte +41 -0
  122. package/src/shadcn/pagination/pagination-link.svelte.d.ts +9 -0
  123. package/src/shadcn/pagination/pagination-next-button.svelte +34 -0
  124. package/src/shadcn/pagination/pagination-next-button.svelte.d.ts +4 -0
  125. package/src/shadcn/pagination/pagination-next.svelte +26 -0
  126. package/src/shadcn/pagination/pagination-next.svelte.d.ts +4 -0
  127. package/src/shadcn/pagination/pagination-prev-button.svelte +34 -0
  128. package/src/shadcn/pagination/pagination-prev-button.svelte.d.ts +4 -0
  129. package/src/shadcn/pagination/pagination-previous.svelte +30 -0
  130. package/src/shadcn/pagination/pagination-previous.svelte.d.ts +4 -0
  131. package/src/shadcn/pagination/pagination.svelte +27 -0
  132. package/src/shadcn/pagination/pagination.svelte.d.ts +4 -0
  133. package/src/slides/Controls.svelte +39 -36
  134. package/src/slides/Deck.svelte +11 -8
  135. package/src/thread/thread.svelte +15 -8
  136. package/src/work/detail/task-detail.svelte +1 -1
  137. package/src/work/gantt/gantt-task-row.svelte +3 -6
  138. package/src/work/inbox/inbox-card.svelte +7 -5
  139. package/src/work/inbox/inbox-trace.svelte +14 -10
  140. package/src/work/kanban-board.svelte +40 -25
  141. package/src/work/kanban-hidden-rail.svelte +67 -65
  142. package/src/work/task-list.svelte +145 -124
  143. package/src/work/task-list.svelte.test.ts +5 -2
  144. package/src/work/time/time-tracker.svelte +35 -10
@@ -17,11 +17,14 @@
17
17
  // IMPORTS
18
18
  // =========================
19
19
  import { Panel } from "@xyflow/svelte";
20
+ import { mergeProps, Toolbar } from "bits-ui";
20
21
  import type { Snippet } from "svelte";
21
22
  import "../pen/pen.css";
22
23
  import { PEN_COLORS } from "../pen/types.js";
23
24
  import "./canvas-ui.css";
24
25
  import ToolIcon from "../components/icons/ToolIcon.svelte";
26
+ import BarTooltip from "./BarTooltip.svelte";
27
+ import * as Tooltip from "../../shadcn/tooltip/index.js";
25
28
  import {
26
29
  hasBrush,
27
30
  hasColor,
@@ -63,6 +66,9 @@
63
66
  // =========================
64
67
  let lastShape = $state<"rectangle" | "ellipse" | "diamond">("rectangle");
65
68
  let lastBrush = $state<BrushTool>("pen");
69
+ let styleBar = $state<HTMLElement | null>(null);
70
+ /** The value of the style bar item that had focus when its layout changed, or null when focus was elsewhere. */
71
+ let refocus: string | null = null;
66
72
 
67
73
  // =========================
68
74
  // DERIVED
@@ -70,10 +76,17 @@
70
76
  const BRUSHES = ["pen", "highlighter", "eraser"] as const;
71
77
  const SHAPES = ["rectangle", "ellipse", "diamond"] as const;
72
78
  const BRUSH_DOT = [4, 7, 11] as const;
79
+ const SINGLE_TOOLS = ["select", "hand", "connect"] as const;
73
80
 
74
81
  let shapeActive = $derived(isShapeTool(tool));
75
82
  let brushActive = $derived(isBrushTool(tool));
76
83
  let showStyle = $derived(brushActive || shapeActive || hasColor(tool));
84
+ /** The brush and shape buttons stand for a family of tools, so the rail's pick is the family. */
85
+ let railValue = $derived(brushActive ? "brush" : shapeActive ? "shapes" : tool);
86
+ // Roving focus remembers its tab stop by element, so a bar whose items change is rebuilt fresh.
87
+ let styleLayout = $derived(
88
+ `${brushActive ? "brush" : shapeActive ? "shape" : "none"}-${hasColor(tool)}-${hasBrush(tool)}`,
89
+ );
77
90
 
78
91
  // =========================
79
92
  // EFFECTS
@@ -83,175 +96,176 @@
83
96
  if (isBrushTool(tool)) lastBrush = tool;
84
97
  });
85
98
 
99
+ $effect.pre(() => {
100
+ void styleLayout;
101
+ const active = document.activeElement;
102
+ refocus =
103
+ active instanceof HTMLElement && styleBar?.contains(active)
104
+ ? (active.dataset.value ?? "")
105
+ : null;
106
+ });
107
+
108
+ $effect(() => {
109
+ void styleLayout;
110
+ if (refocus === null || !styleBar) return;
111
+ const items = [...styleBar.querySelectorAll<HTMLElement>("[data-toolbar-item]")];
112
+ (items.find((item) => item.dataset.value === refocus) ?? items[0])?.focus();
113
+ refocus = null;
114
+ });
115
+
86
116
  // =========================
87
117
  // FUNCTIONS
88
118
  // =========================
89
- function tip(id: CanvasTool, label = labels.tools[id]): string {
90
- const key = keyForTool(id);
91
- return key ? `${label} ${key}` : label;
119
+ // A single-select group sends "" when its pressed item is pressed again. A tool stays picked.
120
+ function pickRail(value: string) {
121
+ if (value === "brush") tool = lastBrush;
122
+ else if (value === "shapes") tool = lastShape;
123
+ else if (value) tool = value as CanvasTool;
124
+ }
125
+
126
+ // Svelte Flow blurs a node a frame after Esc deselects it, which stops the tool letters.
127
+ // A later frame refocuses it. On the window, so it runs after the node's delegated handler.
128
+ function keepNodeFocus(panel: HTMLElement) {
129
+ const flow = panel.closest<HTMLElement>(".svelte-flow");
130
+ if (!flow) return;
131
+ const onkeydown = (event: KeyboardEvent) => {
132
+ const node = event.target;
133
+ if (event.key !== "Escape" || !(node instanceof HTMLElement)) return;
134
+ if (!node.matches(".svelte-flow__node") || !flow.contains(node)) return;
135
+ requestAnimationFrame(() => node.isConnected && node.focus());
136
+ };
137
+ window.addEventListener("keydown", onkeydown);
138
+ return () => window.removeEventListener("keydown", onkeydown);
139
+ }
140
+
141
+ function pickTool(value: string) {
142
+ if (value) tool = value as CanvasTool;
143
+ }
144
+
145
+ function pickColor(value: string) {
146
+ if (value) color = value as PenColor;
147
+ }
148
+
149
+ function pickLevel(value: string) {
150
+ if (value) level = Number(value) as BrushLevel;
92
151
  }
93
152
  </script>
94
153
 
95
154
  <!-- =========================== -->
96
155
  <!-- MARKUP -->
97
156
  <!-- =========================== -->
98
- <Panel position="center-left" class="cv-dock">
99
- <div class="cv-row">
100
- <div
101
- class="cv-bar cv-rail"
102
- role="toolbar"
103
- aria-orientation="vertical"
104
- aria-label={labels.toolbar}
105
- >
106
- <button
107
- type="button"
108
- class="cv-btn"
109
- aria-pressed={tool === "select"}
110
- aria-label={labels.tools.select}
111
- data-tip={tip("select")}
112
- onclick={() => (tool = "select")}
113
- >
114
- <ToolIcon name="select" />
115
- </button>
116
- <button
117
- type="button"
118
- class="cv-btn"
119
- aria-pressed={tool === "hand"}
120
- aria-label={labels.tools.hand}
121
- data-tip={tip("hand")}
122
- onclick={() => (tool = "hand")}
123
- >
124
- <ToolIcon name="hand" />
125
- </button>
126
- <button
127
- type="button"
128
- class="cv-btn"
129
- aria-pressed={tool === "connect"}
130
- aria-label={labels.tools.connect}
131
- data-tip={tip("connect")}
132
- onclick={() => (tool = "connect")}
133
- >
134
- <ToolIcon name="connect" />
135
- </button>
136
- <button
137
- type="button"
138
- class="cv-btn"
139
- aria-pressed={brushActive}
140
- aria-label={labels.tools[lastBrush]}
141
- data-tip={tip(lastBrush)}
142
- onclick={() => (tool = lastBrush)}
143
- >
144
- <ToolIcon name={brushActive ? (tool as BrushTool) : lastBrush} />
145
- </button>
146
- <button
147
- type="button"
148
- class="cv-btn"
149
- aria-pressed={tool === "text"}
150
- aria-label={labels.tools.text}
151
- data-tip={tip("text")}
152
- onclick={() => (tool = "text")}
153
- >
154
- <ToolIcon name="text" />
155
- </button>
156
- <button
157
- type="button"
158
- class="cv-btn"
159
- aria-pressed={shapeActive}
160
- aria-label={labels.tools.shapes}
161
- data-tip={tip("rectangle", labels.tools.shapes)}
162
- onclick={() => (tool = lastShape)}
157
+ <Panel position="center-left" class="cv-dock" {@attach keepNodeFocus}>
158
+ <Tooltip.Provider>
159
+ <div class="cv-row">
160
+ <Toolbar.Root
161
+ class="cv-bar cv-rail"
162
+ orientation="vertical"
163
+ aria-orientation="vertical"
164
+ aria-label={labels.toolbar}
163
165
  >
164
- <ToolIcon name={shapeActive ? (tool as typeof lastShape) : lastShape} />
165
- </button>
166
- <button
167
- type="button"
168
- class="cv-btn"
169
- aria-pressed={tool === "frame"}
170
- aria-label={labels.tools.frame}
171
- data-tip={tip("frame")}
172
- onclick={() => (tool = "frame")}
173
- >
174
- <ToolIcon name="frame" />
175
- </button>
176
- {#if onNote}
177
- <button
178
- type="button"
179
- class="cv-btn"
180
- aria-label={labels.tools.note}
181
- data-tip="{labels.tools.note} N"
182
- onclick={onNote}
183
- >
184
- <ToolIcon name="note" />
185
- </button>
186
- {/if}
187
-
188
- {#if extra}
189
- <span class="cv-gap" aria-hidden="true"></span>
190
- {@render extra()}
191
- {/if}
192
- </div>
193
-
194
- {#if showStyle}
195
- <div class="cv-bar t-enter" role="toolbar" aria-label={labels.style}>
196
- {#if brushActive}
197
- {#each BRUSHES as brush (brush)}
198
- <button
199
- type="button"
200
- class="cv-btn"
201
- aria-pressed={tool === brush}
202
- aria-label={labels.tools[brush]}
203
- data-tip={tip(brush)}
204
- onclick={() => (tool = brush)}
205
- >
206
- <ToolIcon name={brush} />
207
- </button>
208
- {/each}
209
- {:else if shapeActive}
210
- {#each SHAPES as shape (shape)}
211
- <button
212
- type="button"
213
- class="cv-btn"
214
- aria-pressed={tool === shape}
215
- aria-label={labels.tools[shape]}
216
- data-tip={tip(shape)}
217
- onclick={() => (tool = shape)}
218
- >
219
- <ToolIcon name={shape} />
220
- </button>
166
+ <Toolbar.Group type="single" class="cv-group" bind:value={() => railValue, pickRail}>
167
+ {#each SINGLE_TOOLS as id (id)}
168
+ {@render railItem(id, labels.tools[id], keyForTool(id), id)}
221
169
  {/each}
170
+ {@render railItem(
171
+ "brush",
172
+ labels.tools[lastBrush],
173
+ keyForTool(lastBrush),
174
+ brushActive ? (tool as BrushTool) : lastBrush,
175
+ )}
176
+ {@render railItem("text", labels.tools.text, keyForTool("text"), "text")}
177
+ {@render railItem(
178
+ "shapes",
179
+ labels.tools.shapes,
180
+ keyForTool("rectangle"),
181
+ shapeActive ? (tool as typeof lastShape) : lastShape,
182
+ )}
183
+ {@render railItem("frame", labels.tools.frame, keyForTool("frame"), "frame")}
184
+ </Toolbar.Group>
185
+ {#if onNote}
186
+ <BarTooltip label={labels.tools.note} shortcut="N" side="right">
187
+ {#snippet child({ props })}
188
+ <Toolbar.Button {...mergeProps(props, { onclick: onNote })} class="cv-btn">
189
+ <ToolIcon name="note" />
190
+ </Toolbar.Button>
191
+ {/snippet}
192
+ </BarTooltip>
222
193
  {/if}
223
194
 
224
- {#if hasColor(tool)}
225
- {#if brushActive || shapeActive}<span class="cv-gap" aria-hidden="true"></span>{/if}
226
- {#each PEN_COLORS as swatch (swatch)}
227
- <button
228
- type="button"
229
- class="cv-swatch"
230
- aria-pressed={color === swatch}
231
- aria-label={labels.colors[swatch]}
232
- data-tip={labels.colors[swatch]}
233
- style:--swatch="var(--pen-{swatch})"
234
- onclick={() => (color = swatch)}
235
- ></button>
236
- {/each}
237
- {/if}
238
-
239
- {#if hasBrush(tool)}
195
+ {#if extra}
240
196
  <span class="cv-gap" aria-hidden="true"></span>
241
- {#each BRUSH_DOT as dot, index (index)}
242
- <button
243
- type="button"
244
- class="cv-btn"
245
- aria-pressed={level === index}
246
- aria-label={labels.sizes[index]}
247
- data-tip={labels.sizes[index]}
248
- onclick={() => (level = index as BrushLevel)}
249
- >
250
- <span class="cv-dot" style:--dot="{dot}px"></span>
251
- </button>
252
- {/each}
197
+ {@render extra()}
253
198
  {/if}
254
- </div>
255
- {/if}
256
- </div>
199
+ </Toolbar.Root>
200
+
201
+ {#if showStyle}
202
+ <div class="cv-bar t-enter" bind:this={styleBar}>
203
+ {#key styleLayout}
204
+ <Toolbar.Root class="cv-group" aria-label={labels.style}>
205
+ {#if brushActive || shapeActive}
206
+ <Toolbar.Group type="single" class="cv-group" bind:value={() => tool, pickTool}>
207
+ {#each brushActive ? BRUSHES : SHAPES as kind (kind)}
208
+ <BarTooltip label={labels.tools[kind]} shortcut={keyForTool(kind)}>
209
+ {#snippet child({ props })}
210
+ <Toolbar.GroupItem {...props} value={kind} class="cv-btn">
211
+ <ToolIcon name={kind} />
212
+ </Toolbar.GroupItem>
213
+ {/snippet}
214
+ </BarTooltip>
215
+ {/each}
216
+ </Toolbar.Group>
217
+ {/if}
218
+
219
+ {#if hasColor(tool)}
220
+ {#if brushActive || shapeActive}<span class="cv-gap" aria-hidden="true"></span>{/if}
221
+ <Toolbar.Group type="single" class="cv-group" bind:value={() => color, pickColor}>
222
+ {#each PEN_COLORS as swatch (swatch)}
223
+ <BarTooltip label={labels.colors[swatch]}>
224
+ {#snippet child({ props })}
225
+ <Toolbar.GroupItem
226
+ {...props}
227
+ value={swatch}
228
+ class="cv-swatch"
229
+ style="--swatch: var(--pen-{swatch})"
230
+ />
231
+ {/snippet}
232
+ </BarTooltip>
233
+ {/each}
234
+ </Toolbar.Group>
235
+ {/if}
236
+
237
+ {#if hasBrush(tool)}
238
+ <span class="cv-gap" aria-hidden="true"></span>
239
+ <Toolbar.Group
240
+ type="single"
241
+ class="cv-group"
242
+ bind:value={() => String(level), pickLevel}
243
+ >
244
+ {#each BRUSH_DOT as dot, index (index)}
245
+ <BarTooltip label={labels.sizes[index]}>
246
+ {#snippet child({ props })}
247
+ <Toolbar.GroupItem {...props} value={String(index)} class="cv-btn">
248
+ <span class="cv-dot" style:--dot="{dot}px"></span>
249
+ </Toolbar.GroupItem>
250
+ {/snippet}
251
+ </BarTooltip>
252
+ {/each}
253
+ </Toolbar.Group>
254
+ {/if}
255
+ </Toolbar.Root>
256
+ {/key}
257
+ </div>
258
+ {/if}
259
+ </div>
260
+ </Tooltip.Provider>
257
261
  </Panel>
262
+
263
+ {#snippet railItem(value: string, label: string, shortcut: string, icon: CanvasTool)}
264
+ <BarTooltip {label} {shortcut} side="right">
265
+ {#snippet child({ props })}
266
+ <Toolbar.GroupItem {...props} {value} class="cv-btn">
267
+ <ToolIcon name={icon} />
268
+ </Toolbar.GroupItem>
269
+ {/snippet}
270
+ </BarTooltip>
271
+ {/snippet}