@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
@@ -66,33 +66,45 @@
66
66
  {@render header()}
67
67
  {@render banner?.()}
68
68
 
69
+ <!-- The rail is drawn twice, once docked and once in the sheet. Only one is ever on
70
+ screen, and the sheet mounts its copy on open. -->
69
71
  {#if rail}
70
- <button
71
- type="button"
72
- class={cn(
73
- "t-press flex min-h-11 w-full items-center gap-3 border-b border-border py-2 text-left @3xl:hidden",
74
- gutterClass,
75
- )}
76
- aria-haspopup="dialog"
77
- aria-expanded={railOpen}
78
- onclick={() => (railOpen = true)}
79
- >
80
- <span class="text-sm font-medium">{railTitle ?? t.steps}</span>
81
- {#if railProgress}
82
- <span class="text-xs tabular-nums text-muted-foreground">
83
- {t.stepsCount(railProgress.done, railProgress.total)}
84
- </span>
85
- <Progress
86
- value={percent}
87
- max={100}
88
- class="h-1 flex-1"
89
- aria-label={t.stepsCount(railProgress.done, railProgress.total)}
90
- />
91
- {:else}
92
- <span class="flex-1"></span>
93
- {/if}
94
- <CaretRight class="size-4 shrink-0 text-muted-foreground" aria-hidden="true" />
95
- </button>
72
+ <Sheet.Root bind:open={railOpen}>
73
+ <Sheet.Trigger>
74
+ {#snippet child({ props })}
75
+ <button
76
+ type="button"
77
+ {...props}
78
+ class={cn(
79
+ "t-press flex min-h-11 w-full items-center gap-3 border-b border-border py-2 text-left @3xl:hidden",
80
+ gutterClass,
81
+ )}
82
+ >
83
+ <span class="text-sm font-medium">{railTitle ?? t.steps}</span>
84
+ {#if railProgress}
85
+ <span class="text-xs tabular-nums text-muted-foreground">
86
+ {t.stepsCount(railProgress.done, railProgress.total)}
87
+ </span>
88
+ <Progress
89
+ value={percent}
90
+ max={100}
91
+ class="h-1 flex-1"
92
+ aria-label={t.stepsCount(railProgress.done, railProgress.total)}
93
+ />
94
+ {:else}
95
+ <span class="flex-1"></span>
96
+ {/if}
97
+ <CaretRight class="size-4 shrink-0 text-muted-foreground" aria-hidden="true" />
98
+ </button>
99
+ {/snippet}
100
+ </Sheet.Trigger>
101
+ <Sheet.Content side="bottom" class="gap-0 p-0">
102
+ <Sheet.Header class="sr-only">
103
+ <Sheet.Title>{railTitle ?? t.steps}</Sheet.Title>
104
+ </Sheet.Header>
105
+ <div class="overflow-y-auto p-4 pt-6">{@render rail()}</div>
106
+ </Sheet.Content>
107
+ </Sheet.Root>
96
108
  {/if}
97
109
 
98
110
  <div class="flex min-w-0 flex-col @3xl:flex-row">
@@ -123,16 +135,3 @@
123
135
  {/if}
124
136
  </div>
125
137
  </div>
126
-
127
- <!-- The rail is drawn twice, once docked and once here. Only one is ever on screen,
128
- and the sheet mounts its copy on open. -->
129
- {#if rail}
130
- <Sheet.Root bind:open={railOpen}>
131
- <Sheet.Content side="bottom" class="gap-0 p-0">
132
- <Sheet.Header class="sr-only">
133
- <Sheet.Title>{railTitle ?? t.steps}</Sheet.Title>
134
- </Sheet.Header>
135
- <div class="overflow-y-auto p-4 pt-6">{@render rail()}</div>
136
- </Sheet.Content>
137
- </Sheet.Root>
138
- {/if}
@@ -13,6 +13,7 @@
13
13
  import { TONE_ICON } from "../lib/tone.js";
14
14
  import { DEFAULT_RECORD_FILTERS, DEFAULT_RECORD_KINDS } from "./kinds.js";
15
15
  import { formatDayHeader, formatMoney, formatTime } from "./format.js";
16
+ import { measureClamp } from "./measure-clamp.js";
16
17
  import {
17
18
  dayRelation,
18
19
  eventsForFilter,
@@ -78,6 +79,8 @@
78
79
  // =========================
79
80
  let showAll = $state(false);
80
81
  const expanded = new SvelteSet<string>();
82
+ // Ids whose body is cut by its clamp, as measured.
83
+ const cut = new SvelteSet<string>();
81
84
 
82
85
  // =========================
83
86
  // DERIVED
@@ -110,10 +113,11 @@
110
113
  return kindByKey.get(event.kind) ?? { key: event.kind, label: event.kind };
111
114
  }
112
115
 
113
- // A body this long, or with a line break, gets a More toggle. Measuring each
114
- // row would re-layout the list on every resize.
115
- function isLong(body: string | null | undefined): boolean {
116
- return Boolean(body && (body.length > 180 || body.includes("\n")));
116
+ function measureBody(id: string) {
117
+ return measureClamp(
118
+ () => expanded.has(id),
119
+ (isCut) => (isCut ? cut.add(id) : cut.delete(id)),
120
+ );
117
121
  }
118
122
  </script>
119
123
 
@@ -180,6 +184,7 @@
180
184
  </p>
181
185
  {#if event.body}
182
186
  <p
187
+ {@attach measureBody(event.id)}
183
188
  class={cn(
184
189
  "mt-0.5 text-sm leading-snug whitespace-pre-line text-muted-foreground wrap-anywhere",
185
190
  !expanded.has(event.id) && "line-clamp-3",
@@ -187,7 +192,7 @@
187
192
  >
188
193
  {event.body}
189
194
  </p>
190
- {#if isLong(event.body)}
195
+ {#if cut.has(event.id) || expanded.has(event.id)}
191
196
  <Button
192
197
  variant="ghost"
193
198
  size="sm"
@@ -17,7 +17,8 @@
17
17
  import CountChip from "../components/chip/count-chip.svelte";
18
18
  import { Input } from "../shadcn/input/index.js";
19
19
  import { Skeleton } from "../shadcn/skeleton/index.js";
20
- import * as Popover from "../shadcn/popover/index.js";
20
+ import { DropdownMenu as MenuPrimitive } from "bits-ui";
21
+ import * as DropdownMenu from "../shadcn/dropdown-menu/index.js";
21
22
  import * as ContextMenu from "../shadcn/context-menu/index.js";
22
23
  import { todayIso } from "../work/dates.js";
23
24
  import { formatRange } from "./format.js";
@@ -267,8 +268,8 @@
267
268
  </span>
268
269
  {/if}
269
270
  {#if onToggle && !loading && ordered.length > 0}
270
- <Popover.Root bind:open={menuOpen}>
271
- <Popover.Trigger>
271
+ <DropdownMenu.Root bind:open={menuOpen}>
272
+ <DropdownMenu.Trigger>
272
273
  {#snippet child({ props })}
273
274
  <Button
274
275
  {...props}
@@ -276,7 +277,7 @@
276
277
  size="sm"
277
278
  icon
278
279
  class="relative ml-auto text-muted-foreground"
279
- aria-label={t.editSteps}
280
+ aria-label={hiddenCount > 0 ? `${t.editSteps}: ${hiddenCount}` : t.editSteps}
280
281
  >
281
282
  <DotsThreeVertical aria-hidden="true" class="size-4" weight="bold" />
282
283
  {#if hiddenCount > 0}
@@ -289,44 +290,57 @@
289
290
  {/if}
290
291
  </Button>
291
292
  {/snippet}
292
- </Popover.Trigger>
293
- <Popover.Content align="end" class="w-64 p-1">
293
+ </DropdownMenu.Trigger>
294
+ <DropdownMenu.Content align="end" class="w-64 rounded-2xl">
294
295
  {#each menuBuckets as bucket (bucket.key)}
295
- {#if bucket.label}
296
- <p class="px-2 pt-2 pb-1 text-xs font-medium text-muted-foreground">
297
- {bucket.label}
298
- </p>
299
- {/if}
300
- {#each bucket.steps as step (step.id)}
301
- <Button
302
- variant="ghost"
303
- class="w-full justify-start gap-2.5 px-2 text-left"
304
- aria-pressed={!step.hidden}
305
- onclick={() => onToggle(step.id, Boolean(step.hidden))}
306
- >
307
- <span class="flex size-4 shrink-0 items-center justify-center">
308
- {#if step.hidden}
309
- <EyeSlash aria-hidden="true" class="size-3.5 text-muted-foreground" />
310
- {:else}
311
- <Eye aria-hidden="true" class="size-3.5" />
312
- {/if}
313
- </span>
314
- <span
315
- class={cn(
316
- "min-w-0 flex-1 truncate",
317
- step.hidden && "text-muted-foreground line-through",
318
- )}>{step.label}</span
296
+ <DropdownMenu.Group>
297
+ {#if bucket.label}
298
+ <DropdownMenu.GroupHeading
299
+ class="px-2 pt-2 pb-1 text-xs font-medium text-muted-foreground"
300
+ >
301
+ {bucket.label}
302
+ </DropdownMenu.GroupHeading>
303
+ {/if}
304
+ {#each bucket.steps as step (step.id)}
305
+ <!-- Bits' item around the kit's ghost Button, not the shadcn row: the eye is
306
+ the checkmark here, so the row keeps its own look. -->
307
+ <MenuPrimitive.CheckboxItem
308
+ checked={!step.hidden}
309
+ closeOnSelect={false}
310
+ onCheckedChange={(shown) => onToggle(step.id, shown)}
319
311
  >
320
- </Button>
321
- {/each}
312
+ {#snippet child({ props })}
313
+ <Button
314
+ {...props}
315
+ variant="ghost"
316
+ class="w-full justify-start gap-2.5 px-2 text-left"
317
+ >
318
+ <span class="flex size-4 shrink-0 items-center justify-center">
319
+ {#if step.hidden}
320
+ <EyeSlash aria-hidden="true" class="size-3.5 text-muted-foreground" />
321
+ {:else}
322
+ <Eye aria-hidden="true" class="size-3.5" />
323
+ {/if}
324
+ </span>
325
+ <span
326
+ class={cn(
327
+ "min-w-0 flex-1 truncate",
328
+ step.hidden && "text-muted-foreground line-through",
329
+ )}>{step.label}</span
330
+ >
331
+ </Button>
332
+ {/snippet}
333
+ </MenuPrimitive.CheckboxItem>
334
+ {/each}
335
+ </DropdownMenu.Group>
322
336
  {/each}
323
- <p
337
+ <div
324
338
  class="mt-1 border-t border-border px-2 pt-2 pb-1 text-xs tabular-nums text-muted-foreground"
325
339
  >
326
340
  {t.shownCount(visible.length, ordered.length)}
327
- </p>
328
- </Popover.Content>
329
- </Popover.Root>
341
+ </div>
342
+ </DropdownMenu.Content>
343
+ </DropdownMenu.Root>
330
344
  {/if}
331
345
  </div>
332
346
 
@@ -9,6 +9,7 @@
9
9
  import type { Tone } from "../lib/tone.js";
10
10
  import { gutterClass } from "./gutter.js";
11
11
  import { formatAge } from "./format.js";
12
+ import { measureClamp } from "./measure-clamp.js";
12
13
  import { resolveLabels, type RecordLabels } from "./types.js";
13
14
 
14
15
  // =========================
@@ -35,7 +36,6 @@
35
36
  // =========================
36
37
  let expanded = $state(false);
37
38
  let clamped = $state(false);
38
- let text = $state<HTMLElement | null>(null);
39
39
 
40
40
  // =========================
41
41
  // DERIVED
@@ -43,23 +43,6 @@
43
43
  let t = $derived(resolveLabels(labels));
44
44
  let age = $derived(at ? formatAge(at, now, locale) : null);
45
45
  let hasMeta = $derived(Boolean(health || actor || age));
46
-
47
- // =========================
48
- // EFFECTS
49
- // =========================
50
- // Measured, not guessed from length: a note that fits two lines at 1280px
51
- // overflows at 390px, and "More" shows only when text is cut.
52
- $effect(() => {
53
- if (!text) return;
54
- const el = text;
55
- const measure = () => {
56
- if (!expanded) clamped = el.scrollHeight > el.clientHeight + 1;
57
- };
58
- measure();
59
- const observer = new ResizeObserver(measure);
60
- observer.observe(el);
61
- return () => observer.disconnect();
62
- });
63
46
  </script>
64
47
 
65
48
  <!-- =========================== -->
@@ -83,7 +66,10 @@
83
66
  <!-- No `title` attribute: a native tooltip would repeat the whole note
84
67
  over the steps list. "More" opens it in place. -->
85
68
  <p
86
- bind:this={text}
69
+ {@attach measureClamp(
70
+ () => expanded,
71
+ (cut) => (clamped = cut),
72
+ )}
87
73
  class={cn("text-sm leading-snug text-muted-foreground", !expanded && "line-clamp-2")}
88
74
  >
89
75
  <span class="font-medium text-foreground">{t.whereWeAre}</span>
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import { Separator } from "../separator/index.js";
3
+ import { cn } from "../../lib/utils.js";
4
+ import type { ComponentProps } from "svelte";
5
+
6
+ let {
7
+ ref = $bindable(null),
8
+ class: className,
9
+ orientation = "vertical",
10
+ ...restProps
11
+ }: ComponentProps<typeof Separator> = $props();
12
+ </script>
13
+
14
+ <Separator
15
+ bind:ref
16
+ data-slot="button-group-separator"
17
+ {orientation}
18
+ class={cn(
19
+ "bg-input relative self-stretch data-horizontal:mx-px data-horizontal:w-auto data-vertical:my-px data-vertical:h-auto",
20
+ className
21
+ )}
22
+ {...restProps}
23
+ />
@@ -0,0 +1,13 @@
1
+ declare const $$Component: import("svelte").Component<Omit<{
2
+ orientation?: import("bits-ui").Orientation;
3
+ decorative?: boolean;
4
+ }, "children" | "child"> & {
5
+ child?: import("svelte").Snippet<[{
6
+ props: Record<string, unknown>;
7
+ }]> | undefined;
8
+ children?: import("svelte").Snippet<[]> | undefined;
9
+ style?: import("bits-ui").StyleProperties | string | null | undefined;
10
+ ref?: HTMLElement | null | undefined;
11
+ } & import("bits-ui").Without<import("bits-ui").BitsPrimitiveDivAttributes, import("bits-ui").SeparatorRootPropsWithoutHTML>, {}, "ref">;
12
+ type $$Component = ReturnType<typeof $$Component>;
13
+ export default $$Component;
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import { cn, type WithElementRef } from "../../lib/utils.js";
3
+ import type { Snippet } from "svelte";
4
+ import type { HTMLAttributes } from "svelte/elements";
5
+
6
+ let {
7
+ ref = $bindable(null),
8
+ class: className,
9
+ child,
10
+ ...restProps
11
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
12
+ child?: Snippet<[{ props: Record<string, unknown> }]>;
13
+ } = $props();
14
+
15
+ const mergedProps = $derived({
16
+ ...restProps,
17
+ class: cn("bg-muted gap-2 rounded-md border px-2.5 text-sm font-medium shadow-xs [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none", className),
18
+ "data-slot": "button-group-text",
19
+ });
20
+ </script>
21
+
22
+ {#if child}
23
+ {@render child({ props: mergedProps })}
24
+ {:else}
25
+ <div bind:this={ref} {...mergedProps}>
26
+ {@render mergedProps.children?.()}
27
+ </div>
28
+ {/if}
@@ -0,0 +1,11 @@
1
+ import { type WithElementRef } from "../../lib/utils.js";
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+ type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
5
+ child?: Snippet<[{
6
+ props: Record<string, unknown>;
7
+ }]>;
8
+ };
9
+ declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref">;
10
+ type $$Component = ReturnType<typeof $$Component>;
11
+ export default $$Component;
@@ -0,0 +1,46 @@
1
+ <script lang="ts" module>
2
+ import { tv, type VariantProps } from "tailwind-variants";
3
+
4
+ export const buttonGroupVariants = tv({
5
+ base: "has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md group/button-group flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
6
+ variants: {
7
+ orientation: {
8
+ horizontal:
9
+ "[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-md! [&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
10
+ vertical:
11
+ "[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-md! flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",
12
+ },
13
+ },
14
+ defaultVariants: {
15
+ orientation: "horizontal",
16
+ },
17
+ });
18
+
19
+ export type ButtonGroupOrientation = VariantProps<typeof buttonGroupVariants>["orientation"];
20
+ </script>
21
+
22
+ <script lang="ts">
23
+ import { cn, type WithElementRef } from "../../lib/utils.js";
24
+ import type { HTMLAttributes } from "svelte/elements";
25
+
26
+ let {
27
+ ref = $bindable(null),
28
+ class: className,
29
+ children,
30
+ orientation = "horizontal",
31
+ ...restProps
32
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
33
+ orientation?: ButtonGroupOrientation;
34
+ } = $props();
35
+ </script>
36
+
37
+ <div
38
+ bind:this={ref}
39
+ role="group"
40
+ data-slot="button-group"
41
+ data-orientation={orientation}
42
+ class={cn(buttonGroupVariants({ orientation }), className)}
43
+ {...restProps}
44
+ >
45
+ {@render children?.()}
46
+ </div>
@@ -0,0 +1,16 @@
1
+ import { type VariantProps } from "tailwind-variants";
2
+ export declare const buttonGroupVariants: import("tailwind-variants").TVReturnType<{
3
+ orientation: {
4
+ horizontal: string;
5
+ vertical: string;
6
+ };
7
+ }, undefined, "has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md group/button-group flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", undefined, undefined, undefined>;
8
+ export type ButtonGroupOrientation = VariantProps<typeof buttonGroupVariants>["orientation"];
9
+ import { type WithElementRef } from "../../lib/utils.js";
10
+ import type { HTMLAttributes } from "svelte/elements";
11
+ type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
12
+ orientation?: ButtonGroupOrientation;
13
+ };
14
+ declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref">;
15
+ type $$Component = ReturnType<typeof $$Component>;
16
+ export default $$Component;
@@ -0,0 +1,15 @@
1
+ import Separator from "./button-group-separator.svelte";
2
+ import Text from "./button-group-text.svelte";
3
+ import Root, { buttonGroupVariants, type ButtonGroupOrientation } from "./button-group.svelte";
4
+
5
+ export {
6
+ Root,
7
+ Text,
8
+ Separator,
9
+ buttonGroupVariants,
10
+ type ButtonGroupOrientation,
11
+ //
12
+ Root as ButtonGroup,
13
+ Text as ButtonGroupText,
14
+ Separator as ButtonGroupSeparator,
15
+ };
@@ -0,0 +1,4 @@
1
+ // The checkbox's square, shared with rows that draw it as a picture inside a menu item,
2
+ // where a real checkbox would put a button inside a button.
3
+ export const checkboxBoxClass =
4
+ "border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 not-data-checked:enabled:hover:border-foreground/30 focus-visible:border-ring focus-visible:shadow-(--ring-focus) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-sm border shadow-btn transition-[background-color,border-color,box-shadow] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] group-has-disabled/field:opacity-50 aria-invalid:ring-3 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 any-pointer-coarse:after:-inset-[15px] disabled:cursor-not-allowed disabled:opacity-50";
@@ -2,6 +2,7 @@
2
2
  import { Checkbox as CheckboxPrimitive } from "bits-ui";
3
3
  import { cn, type WithoutChildrenOrChild } from "../../lib/utils.js";
4
4
  import { Check, Minus } from "phosphor-svelte";
5
+ import { checkboxBoxClass } from "./checkbox-box.js";
5
6
 
6
7
  let {
7
8
  ref = $bindable(null),
@@ -15,10 +16,7 @@
15
16
  <CheckboxPrimitive.Root
16
17
  bind:ref
17
18
  data-slot="checkbox"
18
- class={cn(
19
- "border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 not-data-checked:enabled:hover:border-foreground/30 focus-visible:border-ring focus-visible:shadow-(--ring-focus) aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-sm border shadow-btn transition-[background-color,border-color,box-shadow] duration-[var(--duration-quick)] ease-[var(--ease-smooth-out)] group-has-disabled/field:opacity-50 aria-invalid:ring-3 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 any-pointer-coarse:after:-inset-[15px] disabled:cursor-not-allowed disabled:opacity-50",
20
- className,
21
- )}
19
+ class={cn(checkboxBoxClass, className)}
22
20
  bind:checked
23
21
  bind:indeterminate
24
22
  {...restProps}
@@ -0,0 +1,22 @@
1
+ import Addon from "./input-group-addon.svelte";
2
+ import Button from "./input-group-button.svelte";
3
+ import Input from "./input-group-input.svelte";
4
+ import Text from "./input-group-text.svelte";
5
+ import Textarea from "./input-group-textarea.svelte";
6
+ import Root from "./input-group.svelte";
7
+
8
+ export {
9
+ Root,
10
+ Addon,
11
+ Button,
12
+ Input,
13
+ Text,
14
+ Textarea,
15
+ //
16
+ Root as InputGroup,
17
+ Addon as InputGroupAddon,
18
+ Button as InputGroupButton,
19
+ Input as InputGroupInput,
20
+ Text as InputGroupText,
21
+ Textarea as InputGroupTextarea,
22
+ };
@@ -0,0 +1,51 @@
1
+ <script lang="ts" module>
2
+ import { tv, type VariantProps } from "tailwind-variants";
3
+ export const inputGroupAddonVariants = tv({
4
+ base: "text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)_-_5px)] [&>svg:not([class*='size-'])]:size-4 flex cursor-text items-center justify-center select-none",
5
+ variants: {
6
+ align: {
7
+ "inline-start": "pl-2 has-[>button]:-ml-1 has-[>kbd]:ml-[-0.15rem] order-first",
8
+ "inline-end": "pr-2 has-[>button]:-mr-1 has-[>kbd]:mr-[-0.15rem] order-last",
9
+ "block-start": "px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2 order-first w-full justify-start",
10
+ "block-end": "px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2 order-last w-full justify-start",
11
+ },
12
+ },
13
+ defaultVariants: {
14
+ align: "inline-start",
15
+ },
16
+ });
17
+
18
+ export type InputGroupAddonAlign = VariantProps<typeof inputGroupAddonVariants>["align"];
19
+ </script>
20
+
21
+ <script lang="ts">
22
+ import { cn, type WithElementRef } from "../../lib/utils.js";
23
+ import type { HTMLAttributes } from "svelte/elements";
24
+
25
+ let {
26
+ ref = $bindable(null),
27
+ class: className,
28
+ children,
29
+ align = "inline-start",
30
+ ...restProps
31
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
32
+ align?: InputGroupAddonAlign;
33
+ } = $props();
34
+ </script>
35
+
36
+ <div
37
+ bind:this={ref}
38
+ role="group"
39
+ data-slot="input-group-addon"
40
+ data-align={align}
41
+ class={cn(inputGroupAddonVariants({ align }), className)}
42
+ onclick={(e) => {
43
+ if ((e.target as HTMLElement).closest("button")) {
44
+ return;
45
+ }
46
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
47
+ }}
48
+ {...restProps}
49
+ >
50
+ {@render children?.()}
51
+ </div>
@@ -0,0 +1,18 @@
1
+ import { type VariantProps } from "tailwind-variants";
2
+ export declare const inputGroupAddonVariants: import("tailwind-variants").TVReturnType<{
3
+ align: {
4
+ "inline-start": string;
5
+ "inline-end": string;
6
+ "block-start": string;
7
+ "block-end": string;
8
+ };
9
+ }, undefined, "text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)_-_5px)] [&>svg:not([class*='size-'])]:size-4 flex cursor-text items-center justify-center select-none", undefined, undefined, undefined>;
10
+ export type InputGroupAddonAlign = VariantProps<typeof inputGroupAddonVariants>["align"];
11
+ import { type WithElementRef } from "../../lib/utils.js";
12
+ import type { HTMLAttributes } from "svelte/elements";
13
+ type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
14
+ align?: InputGroupAddonAlign;
15
+ };
16
+ declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref">;
17
+ type $$Component = ReturnType<typeof $$Component>;
18
+ export default $$Component;
@@ -0,0 +1,49 @@
1
+ <script lang="ts" module>
2
+ import { tv, type VariantProps } from "tailwind-variants";
3
+
4
+ const inputGroupButtonVariants = tv({
5
+ base: "gap-2 text-sm flex items-center shadow-none",
6
+ variants: {
7
+ size: {
8
+ xs: "h-6 gap-1 rounded-[calc(var(--radius)_-_5px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
9
+ sm: "",
10
+ "icon-xs": "size-6 rounded-[calc(var(--radius)_-_5px)] p-0 has-[>svg]:p-0",
11
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0",
12
+ },
13
+ },
14
+ defaultVariants: {
15
+ size: "xs",
16
+ },
17
+ });
18
+
19
+ export type InputGroupButtonSize = VariantProps<typeof inputGroupButtonVariants>["size"];
20
+ </script>
21
+
22
+ <script lang="ts">
23
+ import { Button } from "../button/index.js";
24
+ import { cn } from "../../lib/utils.js";
25
+ import type { ComponentProps } from "svelte";
26
+
27
+ let {
28
+ ref = $bindable(null),
29
+ class: className,
30
+ children,
31
+ type = "button",
32
+ variant = "ghost",
33
+ size = "xs",
34
+ ...restProps
35
+ }: Omit<ComponentProps<typeof Button>, "href" | "size"> & {
36
+ size?: InputGroupButtonSize;
37
+ } = $props();
38
+ </script>
39
+
40
+ <Button
41
+ bind:ref
42
+ {type}
43
+ data-size={size}
44
+ {variant}
45
+ class={cn(inputGroupButtonVariants({ size }), className)}
46
+ {...restProps}
47
+ >
48
+ {@render children?.()}
49
+ </Button>
@@ -0,0 +1,18 @@
1
+ import { type VariantProps } from "tailwind-variants";
2
+ declare const inputGroupButtonVariants: import("tailwind-variants").TVReturnType<{
3
+ size: {
4
+ xs: string;
5
+ sm: string;
6
+ "icon-xs": string;
7
+ "icon-sm": string;
8
+ };
9
+ }, undefined, "gap-2 text-sm flex items-center shadow-none", undefined, undefined, undefined>;
10
+ export type InputGroupButtonSize = VariantProps<typeof inputGroupButtonVariants>["size"];
11
+ import { Button } from "../button/index.js";
12
+ import type { ComponentProps } from "svelte";
13
+ type $$ComponentProps = Omit<ComponentProps<typeof Button>, "href" | "size"> & {
14
+ size?: InputGroupButtonSize;
15
+ };
16
+ declare const $$Component: import("svelte").Component<$$ComponentProps, {}, "ref">;
17
+ type $$Component = ReturnType<typeof $$Component>;
18
+ export default $$Component;