@orochibraru/ui 1.0.0 → 1.0.1

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 (112) hide show
  1. package/README.md +84 -3
  2. package/dist/components/blocks/accent-toggle.svelte +7 -3
  3. package/dist/components/blocks/accent-toggle.svelte.d.ts +1 -1
  4. package/dist/components/blocks/bottom-nav.svelte +1 -1
  5. package/dist/components/blocks/command-palette.svelte +268 -0
  6. package/dist/components/blocks/command-palette.svelte.d.ts +37 -0
  7. package/dist/components/blocks/error-page.svelte +23 -7
  8. package/dist/components/blocks/error-page.svelte.d.ts +4 -0
  9. package/dist/components/blocks/fab.svelte +1 -1
  10. package/dist/components/blocks/install-prompt.svelte +14 -8
  11. package/dist/components/blocks/install-prompt.svelte.d.ts +2 -1
  12. package/dist/components/blocks/music-player/labelled-slider.svelte +3 -12
  13. package/dist/components/blocks/music-player/labelled-slider.svelte.d.ts +1 -2
  14. package/dist/components/blocks/music-player.svelte +13 -12
  15. package/dist/components/blocks/otp-field.svelte +77 -33
  16. package/dist/components/blocks/otp-field.svelte.d.ts +7 -0
  17. package/dist/components/blocks/password-input.svelte +56 -16
  18. package/dist/components/blocks/password-input.svelte.d.ts +3 -0
  19. package/dist/components/blocks/stepper/stepper-phone-header.svelte +93 -0
  20. package/dist/components/blocks/stepper/stepper-phone-header.svelte.d.ts +13 -0
  21. package/dist/components/blocks/stepper.svelte +175 -0
  22. package/dist/components/blocks/stepper.svelte.d.ts +30 -0
  23. package/dist/components/blocks/time-range-picker.svelte +229 -0
  24. package/dist/components/blocks/time-range-picker.svelte.d.ts +33 -0
  25. package/dist/components/blocks/time-series-chart/data-table.svelte +43 -0
  26. package/dist/components/blocks/time-series-chart/data-table.svelte.d.ts +15 -0
  27. package/dist/components/blocks/time-series-chart/legend.svelte +23 -0
  28. package/dist/components/blocks/time-series-chart/legend.svelte.d.ts +10 -0
  29. package/dist/components/blocks/time-series-chart/marks.svelte +118 -0
  30. package/dist/components/blocks/time-series-chart/marks.svelte.d.ts +29 -0
  31. package/dist/components/blocks/time-series-chart/messages.d.ts +13 -0
  32. package/dist/components/blocks/time-series-chart/messages.js +12 -0
  33. package/dist/components/blocks/time-series-chart/readout.svelte +44 -0
  34. package/dist/components/blocks/time-series-chart/readout.svelte.d.ts +15 -0
  35. package/dist/components/blocks/time-series-chart/scale.d.ts +24 -0
  36. package/dist/components/blocks/time-series-chart/scale.js +136 -0
  37. package/dist/components/blocks/time-series-chart/time.d.ts +7 -0
  38. package/dist/components/blocks/time-series-chart/time.js +96 -0
  39. package/dist/components/blocks/time-series-chart.svelte +492 -0
  40. package/dist/components/blocks/time-series-chart.svelte.d.ts +18 -0
  41. package/dist/components/blocks/top-loading-bar.svelte +1 -1
  42. package/dist/components/blocks/video-player/center-controls.svelte +7 -7
  43. package/dist/components/blocks/video-player/control-bar.svelte +41 -35
  44. package/dist/components/blocks/video-player/control-button.svelte +1 -1
  45. package/dist/components/blocks/video-player/option-menu.svelte +1 -1
  46. package/dist/components/blocks/video-player/scrub-bar.svelte +39 -12
  47. package/dist/components/blocks/video-player/scrub-bar.svelte.d.ts +2 -0
  48. package/dist/components/blocks/video-player.svelte +2 -2
  49. package/dist/components/kit/accordion.svelte +70 -0
  50. package/dist/components/kit/accordion.svelte.d.ts +37 -0
  51. package/dist/components/kit/avatar-group.svelte +84 -0
  52. package/dist/components/kit/avatar-group.svelte.d.ts +14 -0
  53. package/dist/components/kit/bar-list.svelte +104 -0
  54. package/dist/components/kit/bar-list.svelte.d.ts +22 -0
  55. package/dist/components/kit/breadcrumbs.svelte +102 -0
  56. package/dist/components/kit/breadcrumbs.svelte.d.ts +14 -0
  57. package/dist/components/kit/checkbox-list.svelte +98 -0
  58. package/dist/components/kit/checkbox-list.svelte.d.ts +15 -0
  59. package/dist/components/kit/checkbox.svelte +84 -0
  60. package/dist/components/kit/checkbox.svelte.d.ts +8 -0
  61. package/dist/components/kit/confirm-dialog.svelte +127 -0
  62. package/dist/components/kit/confirm-dialog.svelte.d.ts +19 -0
  63. package/dist/components/kit/data-table.svelte +280 -0
  64. package/dist/components/kit/data-table.svelte.d.ts +46 -0
  65. package/dist/components/kit/date-field.svelte +210 -0
  66. package/dist/components/kit/date-field.svelte.d.ts +14 -0
  67. package/dist/components/kit/field-frame.svelte +71 -0
  68. package/dist/components/kit/field-frame.svelte.d.ts +26 -0
  69. package/dist/components/kit/fieldset-frame.svelte +69 -0
  70. package/dist/components/kit/fieldset-frame.svelte.d.ts +14 -0
  71. package/dist/components/kit/file-dropzone/files.d.ts +14 -0
  72. package/dist/components/kit/file-dropzone/files.js +69 -0
  73. package/dist/components/kit/file-dropzone.svelte +253 -0
  74. package/dist/components/kit/file-dropzone.svelte.d.ts +25 -0
  75. package/dist/components/kit/form.svelte +335 -0
  76. package/dist/components/kit/form.svelte.d.ts +61 -0
  77. package/dist/components/kit/input.svelte +73 -0
  78. package/dist/components/kit/input.svelte.d.ts +9 -0
  79. package/dist/components/kit/menu.svelte +163 -0
  80. package/dist/components/kit/menu.svelte.d.ts +28 -0
  81. package/dist/components/kit/pagination.svelte +134 -0
  82. package/dist/components/kit/pagination.svelte.d.ts +17 -0
  83. package/dist/components/kit/radio-list.svelte +131 -0
  84. package/dist/components/kit/radio-list.svelte.d.ts +15 -0
  85. package/dist/components/kit/segmented-control/segment.svelte +47 -0
  86. package/dist/components/kit/segmented-control/segment.svelte.d.ts +11 -0
  87. package/dist/components/kit/segmented-control.svelte +70 -0
  88. package/dist/components/kit/segmented-control.svelte.d.ts +16 -0
  89. package/dist/components/kit/select.svelte +244 -0
  90. package/dist/components/kit/select.svelte.d.ts +26 -0
  91. package/dist/components/kit/status-badge.svelte +54 -0
  92. package/dist/components/kit/status-badge.svelte.d.ts +31 -0
  93. package/dist/components/kit/switch.svelte +84 -0
  94. package/dist/components/kit/switch.svelte.d.ts +8 -0
  95. package/dist/components/kit/tabs.svelte +86 -0
  96. package/dist/components/kit/tabs.svelte.d.ts +20 -0
  97. package/dist/components/kit/textarea.svelte +92 -0
  98. package/dist/components/kit/textarea.svelte.d.ts +11 -0
  99. package/dist/components/ui/drawer/drawer-content.svelte +1 -1
  100. package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
  101. package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
  102. package/dist/components/ui/sonner/sonner.svelte +2 -4
  103. package/dist/mcp-content.json +1 -1
  104. package/dist/mcp.js +11 -11
  105. package/dist/server/hooks.d.ts +9 -0
  106. package/dist/server/hooks.js +41 -0
  107. package/dist/server/logger.d.ts +39 -0
  108. package/dist/server/logger.js +144 -0
  109. package/dist/server/telemetry.d.ts +23 -0
  110. package/dist/server/telemetry.js +82 -0
  111. package/dist/theme.css +18 -6
  112. package/package.json +40 -1
@@ -0,0 +1,16 @@
1
+ import type { Component } from "svelte";
2
+ import { type FieldProps } from "./field-frame.svelte";
3
+ interface Option {
4
+ value: string;
5
+ label: string;
6
+ icon?: Component<{
7
+ class?: string;
8
+ }>;
9
+ }
10
+ type Props = FieldProps & {
11
+ options: readonly Option[];
12
+ value?: string;
13
+ };
14
+ declare const SegmentedControl: Component<Props, {}, "value">;
15
+ type SegmentedControl = ReturnType<typeof SegmentedControl>;
16
+ export default SegmentedControl;
@@ -0,0 +1,244 @@
1
+ <script lang="ts" module>
2
+ import type { Component } from "svelte";
3
+
4
+ export interface SelectOption {
5
+ value: string;
6
+ label: string;
7
+ description?: string;
8
+ icon?: Component<{ class?: string }>;
9
+ disabled?: boolean;
10
+ }
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import ChevronsUpDownIcon from "@lucide/svelte/icons/chevrons-up-down";
15
+ import { MediaQuery } from "svelte/reactivity";
16
+
17
+ import * as Command from "../ui/command/index.js";
18
+ import * as Drawer from "../ui/drawer/index.js";
19
+ import * as Popover from "../ui/popover/index.js";
20
+ import * as Select from "../ui/select/index.js";
21
+ import { cn } from "../../utils.js";
22
+
23
+ import FieldFrame, { errorList, type FieldProps } from "./field-frame.svelte";
24
+
25
+ const defaultMessages = {
26
+ placeholder: "Select an option",
27
+ search: "Search…",
28
+ empty: "No results.",
29
+ };
30
+
31
+ type Props = FieldProps & {
32
+ options: SelectOption[];
33
+ value?: string;
34
+ placeholder?: string;
35
+ searchable?: boolean;
36
+ onValueChange?: (value: string) => void;
37
+ messages?: Partial<typeof defaultMessages>;
38
+ };
39
+
40
+ let {
41
+ options,
42
+ value = $bindable(""),
43
+ placeholder,
44
+ searchable = false,
45
+ onValueChange,
46
+ messages = {},
47
+ name,
48
+ label,
49
+ description,
50
+ error,
51
+ required = false,
52
+ disabled = false,
53
+ id,
54
+ class: className,
55
+ }: Props = $props();
56
+
57
+ const desktop = new MediaQuery("(min-width: 768px)");
58
+ const nested = Drawer.isInsideDrawer();
59
+ const generatedId = $props.id();
60
+
61
+ let open = $state(false);
62
+ let query = $state("");
63
+
64
+ const text = $derived({ ...defaultMessages, ...messages });
65
+ const triggerId = $derived(id ?? generatedId);
66
+ const invalid = $derived(errorList(error).length > 0);
67
+ const describedBy = $derived(
68
+ invalid
69
+ ? `${triggerId}-error`
70
+ : description
71
+ ? `${triggerId}-description`
72
+ : undefined,
73
+ );
74
+ const selected = $derived(options.find((option) => option.value === value));
75
+ const triggerClass =
76
+ "border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 flex h-11 w-full items-center gap-2 rounded-md border bg-white px-3 text-left text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-3 aria-invalid:ring-3 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm";
77
+
78
+ function pick(next: string) {
79
+ value = next;
80
+ open = false;
81
+ query = "";
82
+ onValueChange?.(next);
83
+ }
84
+ </script>
85
+
86
+ {#snippet optionContent(option: SelectOption)}
87
+ {#if option.icon}
88
+ <option.icon class="text-muted-foreground size-4 shrink-0" />
89
+ {/if}
90
+ <span class="grid min-w-0 flex-1 text-left">
91
+ <span class="truncate">{option.label}</span>
92
+ {#if option.description}
93
+ <span class="text-muted-foreground truncate text-xs font-normal"
94
+ >{option.description}</span
95
+ >
96
+ {/if}
97
+ </span>
98
+ {/snippet}
99
+
100
+ {#snippet summary(chevron: boolean)}
101
+ {#if selected?.icon}
102
+ <selected.icon class="text-muted-foreground size-4 shrink-0" />
103
+ {/if}
104
+ <span
105
+ class={cn("min-w-0 flex-1 truncate", !selected && "text-muted-foreground")}
106
+ >{selected?.label ?? placeholder ?? text.placeholder}</span
107
+ >
108
+ {#if chevron}
109
+ <ChevronsUpDownIcon class="size-4 shrink-0 opacity-50" />
110
+ {/if}
111
+ {/snippet}
112
+
113
+ {#snippet list(itemClass: string, touch: boolean)}
114
+ {#if searchable}
115
+ <Command.Input
116
+ placeholder={text.search}
117
+ bind:value={query}
118
+ wrapperClass={touch ? "px-0 pt-0 pb-3" : undefined}
119
+ groupClass={touch
120
+ ? "h-12! rounded-xl! *:data-[slot=input-group-addon]:pl-3!"
121
+ : undefined}
122
+ class="text-base md:text-sm"
123
+ />
124
+ {/if}
125
+ <Command.List class="max-h-[min(60dvh,20rem)] max-md:max-h-[55dvh]">
126
+ {#if searchable}
127
+ <Command.Empty>{text.empty}</Command.Empty>
128
+ {/if}
129
+ {#each options as option (option.value)}
130
+ <Command.Item
131
+ value={option.value}
132
+ keywords={[option.label, option.description ?? ""]}
133
+ disabled={option.disabled}
134
+ data-checked={option.value === value}
135
+ onSelect={() => pick(option.value)}
136
+ class={itemClass}
137
+ >
138
+ {@render optionContent(option)}
139
+ </Command.Item>
140
+ {/each}
141
+ </Command.List>
142
+ {/snippet}
143
+
144
+ {#snippet drawerContent()}
145
+ <Drawer.Content id="{triggerId}-drawer">
146
+ <Drawer.Header class="px-5 text-left">
147
+ <Drawer.Title>{label ?? placeholder ?? text.placeholder}</Drawer.Title>
148
+ </Drawer.Header>
149
+ <Command.Root class="bg-transparent px-3">
150
+ {@render list("min-h-12 rounded-xl px-3 text-base", true)}
151
+ </Command.Root>
152
+ </Drawer.Content>
153
+ {/snippet}
154
+
155
+ <FieldFrame
156
+ id={triggerId}
157
+ {label}
158
+ {description}
159
+ {error}
160
+ {required}
161
+ class={className}
162
+ >
163
+ {#if name}
164
+ <input type="hidden" {name} {value} {disabled} />
165
+ {/if}
166
+ {#if !desktop.current}
167
+ <button
168
+ type="button"
169
+ id={triggerId}
170
+ {disabled}
171
+ class={triggerClass}
172
+ role="combobox"
173
+ aria-haspopup="dialog"
174
+ aria-expanded={open}
175
+ aria-controls="{triggerId}-drawer"
176
+ aria-invalid={invalid || undefined}
177
+ aria-describedby={describedBy}
178
+ aria-required={required || undefined}
179
+ onclick={() => (open = true)}
180
+ >
181
+ {@render summary(true)}
182
+ </button>
183
+ {#if nested}
184
+ <Drawer.NestedRoot bind:open>{@render drawerContent()}</Drawer.NestedRoot>
185
+ {:else}
186
+ <Drawer.Root bind:open>{@render drawerContent()}</Drawer.Root>
187
+ {/if}
188
+ {:else if !searchable}
189
+ <Select.Root
190
+ type="single"
191
+ {disabled}
192
+ bind:value
193
+ onValueChange={(next) => onValueChange?.(next)}
194
+ >
195
+ <Select.Trigger
196
+ id={triggerId}
197
+ aria-invalid={invalid || undefined}
198
+ aria-describedby={describedBy}
199
+ aria-required={required || undefined}
200
+ class={cn(triggerClass, "data-[size=default]:h-11")}
201
+ >
202
+ {@render summary(false)}
203
+ </Select.Trigger>
204
+ <Select.Content>
205
+ {#each options as option (option.value)}
206
+ <Select.Item
207
+ value={option.value}
208
+ label={option.label}
209
+ disabled={option.disabled}
210
+ >
211
+ {@render optionContent(option)}
212
+ </Select.Item>
213
+ {/each}
214
+ </Select.Content>
215
+ </Select.Root>
216
+ {:else}
217
+ <Popover.Root
218
+ bind:open
219
+ onOpenChange={(next) => {
220
+ if (!next) query = "";
221
+ }}
222
+ >
223
+ <Popover.Trigger
224
+ id={triggerId}
225
+ {disabled}
226
+ role="combobox"
227
+ aria-invalid={invalid || undefined}
228
+ aria-describedby={describedBy}
229
+ aria-required={required || undefined}
230
+ class={triggerClass}
231
+ >
232
+ {@render summary(true)}
233
+ </Popover.Trigger>
234
+ <Popover.Content
235
+ align="start"
236
+ class="w-(--bits-popover-anchor-width) min-w-64 rounded-xl p-0"
237
+ >
238
+ <Command.Root class="bg-transparent">
239
+ {@render list("", false)}
240
+ </Command.Root>
241
+ </Popover.Content>
242
+ </Popover.Root>
243
+ {/if}
244
+ </FieldFrame>
@@ -0,0 +1,26 @@
1
+ import type { Component } from "svelte";
2
+ export interface SelectOption {
3
+ value: string;
4
+ label: string;
5
+ description?: string;
6
+ icon?: Component<{
7
+ class?: string;
8
+ }>;
9
+ disabled?: boolean;
10
+ }
11
+ import * as Select from "../ui/select/index.js";
12
+ import { type FieldProps } from "./field-frame.svelte";
13
+ declare const Select: Component<FieldProps & {
14
+ options: SelectOption[];
15
+ value?: string;
16
+ placeholder?: string;
17
+ searchable?: boolean;
18
+ onValueChange?: (value: string) => void;
19
+ messages?: Partial<{
20
+ placeholder: string;
21
+ search: string;
22
+ empty: string;
23
+ }>;
24
+ }, {}, "value">;
25
+ type Select = ReturnType<typeof Select>;
26
+ export default Select;
@@ -0,0 +1,54 @@
1
+ <script lang="ts" module>
2
+ export type StatusTone =
3
+ "neutral" | "positive" | "warning" | "negative" | "info";
4
+ </script>
5
+
6
+ <script lang="ts" generics="Status extends string">
7
+ import { cn } from "../../utils.js";
8
+
9
+ interface Props {
10
+ status: Status;
11
+ tones?: Partial<Record<Status, StatusTone>>;
12
+ labels?: Partial<Record<Status, string>>;
13
+ pulse?: boolean;
14
+ class?: string;
15
+ }
16
+
17
+ const {
18
+ status,
19
+ tones = {},
20
+ labels = {},
21
+ pulse = false,
22
+ class: className,
23
+ }: Props = $props();
24
+
25
+ const toneClasses: Record<StatusTone, string> = {
26
+ neutral: "border-border bg-muted text-muted-foreground",
27
+ positive: "border-positive/25 bg-positive/10 text-positive",
28
+ warning:
29
+ "border-amber-500/25 bg-amber-500/10 text-amber-700 dark:text-amber-300",
30
+ negative: "border-negative/25 bg-negative/10 text-negative",
31
+ info: "border-sky-500/25 bg-sky-500/10 text-sky-700 dark:text-sky-300",
32
+ };
33
+
34
+ const tone = $derived(tones[status] ?? "neutral");
35
+ </script>
36
+
37
+ <span
38
+ data-tone={tone}
39
+ class={cn(
40
+ "inline-flex h-6 w-fit shrink-0 items-center gap-1.5 rounded-full border px-2.5 text-xs font-medium whitespace-nowrap",
41
+ toneClasses[tone],
42
+ className,
43
+ )}
44
+ >
45
+ <span class="relative flex size-1.5 shrink-0" aria-hidden="true">
46
+ {#if pulse}
47
+ <span
48
+ class="absolute inset-0 rounded-full bg-current opacity-60 motion-safe:animate-ping"
49
+ ></span>
50
+ {/if}
51
+ <span class="relative size-1.5 rounded-full bg-current"></span>
52
+ </span>
53
+ {labels[status] ?? status}
54
+ </span>
@@ -0,0 +1,31 @@
1
+ export type StatusTone = "neutral" | "positive" | "warning" | "negative" | "info";
2
+ declare function $$render<Status extends string>(): {
3
+ props: {
4
+ status: Status;
5
+ tones?: Partial<Record<Status, StatusTone>>;
6
+ labels?: Partial<Record<Status, string>>;
7
+ pulse?: boolean;
8
+ class?: string;
9
+ };
10
+ exports: {};
11
+ bindings: "";
12
+ slots: {};
13
+ events: {};
14
+ };
15
+ declare class __sveltets_Render<Status extends string> {
16
+ props(): ReturnType<typeof $$render<Status>>['props'];
17
+ events(): ReturnType<typeof $$render<Status>>['events'];
18
+ slots(): ReturnType<typeof $$render<Status>>['slots'];
19
+ bindings(): "";
20
+ exports(): {};
21
+ }
22
+ interface $$IsomorphicComponent {
23
+ new <Status extends string>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Status>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Status>['props']>, ReturnType<__sveltets_Render<Status>['events']>, ReturnType<__sveltets_Render<Status>['slots']>> & {
24
+ $$bindings?: ReturnType<__sveltets_Render<Status>['bindings']>;
25
+ } & ReturnType<__sveltets_Render<Status>['exports']>;
26
+ <Status extends string>(internal: unknown, props: ReturnType<__sveltets_Render<Status>['props']> & {}): ReturnType<__sveltets_Render<Status>['exports']>;
27
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
28
+ }
29
+ declare const StatusBadge: $$IsomorphicComponent;
30
+ type StatusBadge<Status extends string> = InstanceType<typeof StatusBadge<Status>>;
31
+ export default StatusBadge;
@@ -0,0 +1,84 @@
1
+ <script lang="ts">
2
+ import * as Field from "../ui/field/index.js";
3
+ import { Switch } from "../ui/switch/index.js";
4
+ import { cn } from "../../utils.js";
5
+
6
+ import { errorList, type FieldProps } from "./field-frame.svelte";
7
+
8
+ type Props = FieldProps & {
9
+ checked?: boolean;
10
+ };
11
+
12
+ let {
13
+ checked = $bindable(false),
14
+ name,
15
+ label,
16
+ description,
17
+ error,
18
+ required = false,
19
+ disabled = false,
20
+ id,
21
+ class: className,
22
+ }: Props = $props();
23
+
24
+ const generatedId = $props.id();
25
+ const switchId = $derived(id ?? generatedId);
26
+ const errors = $derived(errorList(error));
27
+ const invalid = $derived(errors.length > 0);
28
+ const describedBy = $derived(
29
+ invalid
30
+ ? `${switchId}-error`
31
+ : description
32
+ ? `${switchId}-description`
33
+ : undefined,
34
+ );
35
+ </script>
36
+
37
+ <div
38
+ class={cn("flex w-full flex-col", className)}
39
+ data-invalid={invalid || undefined}
40
+ >
41
+ <div
42
+ class="group/row relative flex min-h-11 items-start justify-between gap-4 py-2.5 has-disabled:opacity-50"
43
+ >
44
+ <span class="min-w-0 flex-1">
45
+ {#if label}
46
+ <label
47
+ for={switchId}
48
+ class={cn(
49
+ "block cursor-pointer font-medium after:absolute after:inset-0 group-has-disabled/row:cursor-not-allowed",
50
+ invalid && "text-destructive",
51
+ )}
52
+ >
53
+ {label}
54
+ {#if required}
55
+ <span class="text-destructive" aria-hidden="true">*</span>
56
+ {/if}
57
+ </label>
58
+ {/if}
59
+ {#if description && !invalid}
60
+ <span
61
+ id="{switchId}-description"
62
+ class="text-muted-foreground mt-0.5 block text-sm">{description}</span
63
+ >
64
+ {/if}
65
+ </span>
66
+ <Switch
67
+ id={switchId}
68
+ {name}
69
+ value="true"
70
+ {required}
71
+ {disabled}
72
+ bind:checked
73
+ aria-invalid={invalid || undefined}
74
+ aria-describedby={describedBy}
75
+ class="z-10 mt-0.5 shrink-0"
76
+ />
77
+ </div>
78
+ {#if invalid}
79
+ <Field.Error
80
+ id="{switchId}-error"
81
+ errors={errors.map((message) => ({ message }))}
82
+ />
83
+ {/if}
84
+ </div>
@@ -0,0 +1,8 @@
1
+ import { Switch } from "../ui/switch/index.js";
2
+ import { type FieldProps } from "./field-frame.svelte";
3
+ type Props = FieldProps & {
4
+ checked?: boolean;
5
+ };
6
+ declare const Switch: import("svelte").Component<Props, {}, "checked">;
7
+ type Switch = ReturnType<typeof Switch>;
8
+ export default Switch;
@@ -0,0 +1,86 @@
1
+ <script lang="ts" module>
2
+ import type { Component, Snippet } from "svelte";
3
+
4
+ export interface TabItem {
5
+ value: string;
6
+ label: string;
7
+ icon?: Component<{ class?: string }>;
8
+ badge?: string | number;
9
+ disabled?: boolean;
10
+ }
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import { Tabs as TabsPrimitive } from "bits-ui";
15
+ import { untrack } from "svelte";
16
+
17
+ import * as Tabs from "../ui/tabs/index.js";
18
+ import { cn } from "../../utils.js";
19
+
20
+ interface Props {
21
+ items: TabItem[];
22
+ value?: string;
23
+ class?: string;
24
+ children: Snippet<[string]>;
25
+ }
26
+
27
+ let {
28
+ items,
29
+ value = $bindable(),
30
+ class: className,
31
+ children,
32
+ }: Props = $props();
33
+
34
+ value ??= untrack(() => items[0]?.value);
35
+
36
+ const active = $derived(value ?? items[0]?.value ?? "");
37
+
38
+ let list = $state<HTMLElement | null>(null);
39
+
40
+ $effect(() => {
41
+ list
42
+ ?.querySelector(`[data-value="${CSS.escape(active)}"]`)
43
+ ?.scrollIntoView({ block: "nearest", inline: "nearest" });
44
+ });
45
+ </script>
46
+
47
+ <Tabs.Root
48
+ bind:value={() => active, (next) => (value = next)}
49
+ class={cn("w-full gap-4", className)}
50
+ >
51
+ <div class="no-scrollbar w-full overflow-x-auto py-1">
52
+ <TabsPrimitive.List
53
+ bind:ref={list}
54
+ class="glass flex w-max min-w-full gap-1 rounded-full p-1 md:min-w-0"
55
+ >
56
+ {#each items as item (item.value)}
57
+ {@const selected = item.value === active}
58
+ <TabsPrimitive.Trigger
59
+ value={item.value}
60
+ disabled={item.disabled}
61
+ class="text-muted-foreground hover:bg-accent/70 hover:text-foreground data-active:bg-primary data-active:text-primary-foreground data-active:glow focus-visible:ring-ring/50 flex h-11 shrink-0 grow items-center justify-center gap-2 rounded-full px-4 text-sm font-medium whitespace-nowrap transition-[color,background-color,box-shadow] outline-none focus-visible:ring-3 disabled:pointer-events-none disabled:opacity-50 md:h-9 md:grow-0"
62
+ >
63
+ {#if item.icon}
64
+ <item.icon class="size-4" />
65
+ {/if}
66
+ {item.label}
67
+ {#if item.badge !== undefined}
68
+ <span
69
+ class={cn(
70
+ "min-w-5 rounded-full px-1.5 text-center text-xs leading-5 tabular-nums",
71
+ selected
72
+ ? "bg-primary-foreground/20 text-primary-foreground"
73
+ : "bg-muted text-muted-foreground",
74
+ )}
75
+ >
76
+ {item.badge}
77
+ </span>
78
+ {/if}
79
+ </TabsPrimitive.Trigger>
80
+ {/each}
81
+ </TabsPrimitive.List>
82
+ </div>
83
+ <Tabs.Content value={active}>
84
+ {@render children(active)}
85
+ </Tabs.Content>
86
+ </Tabs.Root>
@@ -0,0 +1,20 @@
1
+ import type { Component, Snippet } from "svelte";
2
+ export interface TabItem {
3
+ value: string;
4
+ label: string;
5
+ icon?: Component<{
6
+ class?: string;
7
+ }>;
8
+ badge?: string | number;
9
+ disabled?: boolean;
10
+ }
11
+ import * as Tabs from "../ui/tabs/index.js";
12
+ interface Props {
13
+ items: TabItem[];
14
+ value?: string;
15
+ class?: string;
16
+ children: Snippet<[string]>;
17
+ }
18
+ declare const Tabs: Component<Props, {}, "value">;
19
+ type Tabs = ReturnType<typeof Tabs>;
20
+ export default Tabs;
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import type { HTMLTextareaAttributes } from "svelte/elements";
3
+
4
+ import { Textarea } from "../ui/textarea/index.js";
5
+ import { cn } from "../../utils.js";
6
+
7
+ import FieldFrame, { errorList, type FieldProps } from "./field-frame.svelte";
8
+
9
+ const defaultMessages = {
10
+ characters: "characters",
11
+ };
12
+
13
+ type Props = FieldProps &
14
+ Omit<HTMLTextareaAttributes, "value" | "class" | "id" | "name"> & {
15
+ value?: string;
16
+ messages?: Partial<typeof defaultMessages>;
17
+ };
18
+
19
+ let {
20
+ value = $bindable(""),
21
+ name,
22
+ label,
23
+ description,
24
+ error,
25
+ required = false,
26
+ disabled = false,
27
+ id,
28
+ class: className,
29
+ rows,
30
+ maxlength,
31
+ messages = {},
32
+ ...rest
33
+ }: Props = $props();
34
+
35
+ const text = $derived({ ...defaultMessages, ...messages });
36
+ const generatedId = $props.id();
37
+ const textareaId = $derived(id ?? generatedId);
38
+ const invalid = $derived(errorList(error).length > 0);
39
+ const counted = $derived(maxlength !== undefined && maxlength !== null);
40
+ const describedBy = $derived(
41
+ [
42
+ invalid
43
+ ? `${textareaId}-error`
44
+ : description
45
+ ? `${textareaId}-description`
46
+ : undefined,
47
+ counted ? `${textareaId}-counter` : undefined,
48
+ ]
49
+ .filter(Boolean)
50
+ .join(" ") || undefined,
51
+ );
52
+ </script>
53
+
54
+ <FieldFrame
55
+ id={textareaId}
56
+ {label}
57
+ {description}
58
+ {error}
59
+ {required}
60
+ class={className}
61
+ >
62
+ <div class="grid gap-1.5">
63
+ <Textarea
64
+ {...rest}
65
+ id={textareaId}
66
+ {name}
67
+ {rows}
68
+ {maxlength}
69
+ {required}
70
+ {disabled}
71
+ aria-invalid={invalid || undefined}
72
+ aria-describedby={describedBy}
73
+ bind:value
74
+ class={cn(
75
+ "px-3 py-2.5",
76
+ rows === undefined ? "min-h-24" : "field-sizing-fixed min-h-0",
77
+ )}
78
+ />
79
+ {#if counted}
80
+ <p
81
+ id="{textareaId}-counter"
82
+ class={cn(
83
+ "text-muted-foreground text-right text-xs tabular-nums",
84
+ (value ?? "").length >= Number(maxlength) && "text-destructive",
85
+ )}
86
+ >
87
+ {(value ?? "").length}/{maxlength}
88
+ <span class="sr-only">{text.characters}</span>
89
+ </p>
90
+ {/if}
91
+ </div>
92
+ </FieldFrame>
@@ -0,0 +1,11 @@
1
+ import type { HTMLTextareaAttributes } from "svelte/elements";
2
+ import { Textarea } from "../ui/textarea/index.js";
3
+ import { type FieldProps } from "./field-frame.svelte";
4
+ declare const Textarea: import("svelte").Component<FieldProps & Omit<HTMLTextareaAttributes, "id" | "name" | "value" | "class"> & {
5
+ value?: string;
6
+ messages?: Partial<{
7
+ characters: string;
8
+ }>;
9
+ }, {}, "value">;
10
+ type Textarea = ReturnType<typeof Textarea>;
11
+ export default Textarea;
@@ -25,7 +25,7 @@
25
25
  bind:ref
26
26
  data-slot="drawer-content"
27
27
  class={cn(
28
- "bg-popover text-popover-foreground border-border border shadow-[0_-12px_48px_-16px_var(--glass-shadow)] group/drawer-content after:hidden! fixed z-50 flex h-auto flex-col text-sm data-[vaul-drawer-direction=bottom]:right-[max(0.5rem,env(safe-area-inset-right))] data-[vaul-drawer-direction=bottom]:bottom-2 data-[vaul-drawer-direction=bottom]:left-[max(0.5rem,env(safe-area-inset-left))] data-[vaul-drawer-direction=bottom]:max-h-[calc(100dvh-env(safe-area-inset-top)-1.5rem)] data-[vaul-drawer-direction=bottom]:rounded-[2.25rem] data-[vaul-drawer-direction=bottom]:pb-[max(0.5rem,calc(env(safe-area-inset-bottom)-0.5rem))] data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:inset-s-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:inset-e-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b",
28
+ "bg-popover text-popover-foreground border-border border shadow-[0_-12px_48px_-16px_var(--glass-shadow)] group/drawer-content after:hidden! fixed z-50 flex h-auto flex-col text-sm data-[vaul-drawer-direction=bottom]:right-[max(0.5rem,var(--safe-right))] data-[vaul-drawer-direction=bottom]:bottom-2 data-[vaul-drawer-direction=bottom]:left-[max(0.5rem,var(--safe-left))] data-[vaul-drawer-direction=bottom]:max-h-[calc(100dvh-var(--safe-top)-1.5rem)] data-[vaul-drawer-direction=bottom]:rounded-[2.25rem] data-[vaul-drawer-direction=bottom]:pb-[max(0.5rem,calc(var(--safe-bottom)-0.5rem))] data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:inset-s-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:inset-e-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b",
29
29
  className,
30
30
  )}
31
31
  {...restProps}