@lanrenbang/basecoat-ultra-svelte 0.1.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 (88) hide show
  1. package/dist/components/Accordion.svelte +64 -0
  2. package/dist/components/Accordion.svelte.d.ts +14 -0
  3. package/dist/components/Alert.svelte +51 -0
  4. package/dist/components/Alert.svelte.d.ts +12 -0
  5. package/dist/components/Avatar.svelte +57 -0
  6. package/dist/components/Avatar.svelte.d.ts +13 -0
  7. package/dist/components/Badge.svelte +30 -0
  8. package/dist/components/Badge.svelte.d.ts +10 -0
  9. package/dist/components/Breadcrumb.svelte +54 -0
  10. package/dist/components/Breadcrumb.svelte.d.ts +14 -0
  11. package/dist/components/Button.svelte +38 -0
  12. package/dist/components/Button.svelte.d.ts +12 -0
  13. package/dist/components/ButtonGroup.svelte +26 -0
  14. package/dist/components/ButtonGroup.svelte.d.ts +10 -0
  15. package/dist/components/Card.svelte +67 -0
  16. package/dist/components/Card.svelte.d.ts +13 -0
  17. package/dist/components/Carousel.svelte +142 -0
  18. package/dist/components/Carousel.svelte.d.ts +11 -0
  19. package/dist/components/CatppuccinThemeSwitcher.svelte +132 -0
  20. package/dist/components/CatppuccinThemeSwitcher.svelte.d.ts +3 -0
  21. package/dist/components/Checkbox.svelte +20 -0
  22. package/dist/components/Checkbox.svelte.d.ts +8 -0
  23. package/dist/components/Collapsible.svelte +39 -0
  24. package/dist/components/Collapsible.svelte.d.ts +13 -0
  25. package/dist/components/Command.svelte +78 -0
  26. package/dist/components/Command.svelte.d.ts +12 -0
  27. package/dist/components/DatePicker.svelte +172 -0
  28. package/dist/components/DatePicker.svelte.d.ts +13 -0
  29. package/dist/components/Dialog.svelte +91 -0
  30. package/dist/components/Dialog.svelte.d.ts +14 -0
  31. package/dist/components/Drawer.svelte +127 -0
  32. package/dist/components/Drawer.svelte.d.ts +12 -0
  33. package/dist/components/DropdownMenu.svelte +62 -0
  34. package/dist/components/DropdownMenu.svelte.d.ts +14 -0
  35. package/dist/components/Empty.svelte +58 -0
  36. package/dist/components/Empty.svelte.d.ts +12 -0
  37. package/dist/components/Input.svelte +22 -0
  38. package/dist/components/Input.svelte.d.ts +9 -0
  39. package/dist/components/InputOTP.svelte +189 -0
  40. package/dist/components/InputOTP.svelte.d.ts +12 -0
  41. package/dist/components/Item.svelte +64 -0
  42. package/dist/components/Item.svelte.d.ts +14 -0
  43. package/dist/components/Kbd.svelte +28 -0
  44. package/dist/components/Kbd.svelte.d.ts +9 -0
  45. package/dist/components/Label.svelte +30 -0
  46. package/dist/components/Label.svelte.d.ts +10 -0
  47. package/dist/components/Pagination.svelte +120 -0
  48. package/dist/components/Pagination.svelte.d.ts +35 -0
  49. package/dist/components/Popover.svelte +68 -0
  50. package/dist/components/Popover.svelte.d.ts +16 -0
  51. package/dist/components/Progress.svelte +26 -0
  52. package/dist/components/Progress.svelte.d.ts +9 -0
  53. package/dist/components/Radio.svelte +22 -0
  54. package/dist/components/Radio.svelte.d.ts +9 -0
  55. package/dist/components/Resizable.svelte +66 -0
  56. package/dist/components/Resizable.svelte.d.ts +13 -0
  57. package/dist/components/Select.svelte +183 -0
  58. package/dist/components/Select.svelte.d.ts +16 -0
  59. package/dist/components/Separator.svelte +19 -0
  60. package/dist/components/Separator.svelte.d.ts +8 -0
  61. package/dist/components/Sheet.svelte +182 -0
  62. package/dist/components/Sheet.svelte.d.ts +13 -0
  63. package/dist/components/Skeleton.svelte +27 -0
  64. package/dist/components/Skeleton.svelte.d.ts +8 -0
  65. package/dist/components/Slider.svelte +38 -0
  66. package/dist/components/Slider.svelte.d.ts +11 -0
  67. package/dist/components/Spinner.svelte +28 -0
  68. package/dist/components/Spinner.svelte.d.ts +8 -0
  69. package/dist/components/Switch.svelte +20 -0
  70. package/dist/components/Switch.svelte.d.ts +8 -0
  71. package/dist/components/Table.svelte +61 -0
  72. package/dist/components/Table.svelte.d.ts +13 -0
  73. package/dist/components/Tabs.svelte +97 -0
  74. package/dist/components/Tabs.svelte.d.ts +15 -0
  75. package/dist/components/Textarea.svelte +22 -0
  76. package/dist/components/Textarea.svelte.d.ts +9 -0
  77. package/dist/components/Toast.svelte +73 -0
  78. package/dist/components/Toast.svelte.d.ts +3 -0
  79. package/dist/components/Toggle.svelte +69 -0
  80. package/dist/components/Toggle.svelte.d.ts +13 -0
  81. package/dist/components/ToggleGroup.svelte +69 -0
  82. package/dist/components/ToggleGroup.svelte.d.ts +12 -0
  83. package/dist/components/Tooltip.svelte +32 -0
  84. package/dist/components/Tooltip.svelte.d.ts +11 -0
  85. package/dist/index.d.ts +42 -0
  86. package/dist/index.js +47 -0
  87. package/dist/reference.css +2 -0
  88. package/package.json +70 -0
@@ -0,0 +1,120 @@
1
+ <script lang="ts">
2
+ import { onMount, createEventDispatcher } from 'svelte';
3
+ import type { Snippet } from 'svelte';
4
+
5
+ let {
6
+ total = 1,
7
+ current = $bindable(1),
8
+ onPageChange,
9
+ class: className = '',
10
+ prevIcon,
11
+ nextIcon,
12
+ ...rest
13
+ }: {
14
+ total: number;
15
+ current: number;
16
+ onPageChange?: (page: number) => void;
17
+ class?: string;
18
+ prevIcon?: Snippet;
19
+ nextIcon?: Snippet;
20
+ [key: string]: any;
21
+ } = $props();
22
+
23
+ const dispatch = createEventDispatcher<{
24
+ change: { page: number; previousPage: number; totalPages: number };
25
+ 'basecoat:initialized': void;
26
+ }>();
27
+
28
+ let containerRef: HTMLElement;
29
+
30
+ const handlePageClick = (page: number) => {
31
+ if (page < 1 || page > total || page === current) return;
32
+ const previousPage = current;
33
+ current = page;
34
+ onPageChange?.(page);
35
+ dispatch('change', { page: current, previousPage, totalPages: total });
36
+ };
37
+
38
+ // Keyboard navigation
39
+ const handleKeydown = (e: KeyboardEvent) => {
40
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
41
+ e.preventDefault();
42
+ handlePageClick(current - 1);
43
+ } else if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
44
+ e.preventDefault();
45
+ handlePageClick(current + 1);
46
+ } else if (e.key === 'Home') {
47
+ e.preventDefault();
48
+ handlePageClick(1);
49
+ } else if (e.key === 'End') {
50
+ e.preventDefault();
51
+ handlePageClick(total);
52
+ }
53
+ };
54
+
55
+ // Expose API methods via element
56
+ onMount(() => {
57
+ if (containerRef) {
58
+ (containerRef as any).goToPage = (page: number) => handlePageClick(page);
59
+ (containerRef as any).getCurrentPage = () => current;
60
+ (containerRef as any).getTotalPages = () => total;
61
+ }
62
+ dispatch('basecoat:initialized');
63
+ });
64
+
65
+ const finalClass = $derived(['pagination flex items-center gap-1', className].filter(Boolean).join(' '));
66
+ </script>
67
+
68
+ <nav
69
+ bind:this={containerRef}
70
+ class={finalClass}
71
+ onkeydown={handleKeydown}
72
+ {...rest}
73
+ >
74
+ <!-- Previous -->
75
+ <button
76
+ type="button"
77
+ class="btn btn-ghost btn-icon"
78
+ disabled={current <= 1}
79
+ aria-disabled={current <= 1 ? 'true' : undefined}
80
+ onclick={() => handlePageClick(current - 1)}
81
+ aria-label="Previous page"
82
+ data-pagination-prev
83
+ >
84
+ {#if prevIcon}
85
+ {@render prevIcon()}
86
+ {:else}
87
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
88
+ {/if}
89
+ </button>
90
+
91
+ <!-- Pages -->
92
+ {#each Array.from({ length: total }, (_, i) => i + 1) as page}
93
+ <button
94
+ type="button"
95
+ class={['btn w-9', current === page ? 'btn-outline' : 'btn-ghost'].join(' ')}
96
+ onclick={() => handlePageClick(page)}
97
+ aria-current={current === page ? 'page' : undefined}
98
+ data-pagination-page={page}
99
+ >
100
+ {page}
101
+ </button>
102
+ {/each}
103
+
104
+ <!-- Next -->
105
+ <button
106
+ type="button"
107
+ class="btn btn-ghost btn-icon"
108
+ disabled={current >= total}
109
+ aria-disabled={current >= total ? 'true' : undefined}
110
+ onclick={() => handlePageClick(current + 1)}
111
+ aria-label="Next page"
112
+ data-pagination-next
113
+ >
114
+ {#if nextIcon}
115
+ {@render nextIcon()}
116
+ {:else}
117
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
118
+ {/if}
119
+ </button>
120
+ </nav>
@@ -0,0 +1,35 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ total: number;
4
+ current: number;
5
+ onPageChange?: (page: number) => void;
6
+ class?: string;
7
+ prevIcon?: Snippet;
8
+ nextIcon?: Snippet;
9
+ [key: string]: any;
10
+ };
11
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
12
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
13
+ $$bindings?: Bindings;
14
+ } & Exports;
15
+ (internal: unknown, props: Props & {
16
+ $$events?: Events;
17
+ $$slots?: Slots;
18
+ }): Exports & {
19
+ $set?: any;
20
+ $on?: any;
21
+ };
22
+ z_$$bindings?: Bindings;
23
+ }
24
+ declare const Pagination: $$__sveltets_2_IsomorphicComponent<$$ComponentProps, {
25
+ change: CustomEvent<{
26
+ page: number;
27
+ previousPage: number;
28
+ totalPages: number;
29
+ }>;
30
+ 'basecoat:initialized': CustomEvent<void>;
31
+ } & {
32
+ [evt: string]: CustomEvent<any>;
33
+ }, {}, {}, "current">;
34
+ type Pagination = InstanceType<typeof Pagination>;
35
+ export default Pagination;
@@ -0,0 +1,68 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { onMount } from 'svelte';
4
+
5
+ let {
6
+ open = $bindable(false),
7
+ trigger,
8
+ children,
9
+ side = 'bottom',
10
+ align = 'start',
11
+ class: className = '',
12
+ contentClass = '',
13
+ ...rest
14
+ }: {
15
+ open?: boolean;
16
+ trigger: Snippet<{ open: boolean }>;
17
+ children: Snippet;
18
+ side?: 'top' | 'bottom' | 'left' | 'right';
19
+ align?: 'start' | 'center' | 'end';
20
+ class?: string;
21
+ contentClass?: string;
22
+ [key: string]: any;
23
+ } = $props();
24
+
25
+ let container: HTMLDivElement;
26
+
27
+ // 点击外部关闭
28
+ function handleClickOutside(event: MouseEvent) {
29
+ if (open && container && !container.contains(event.target as Node)) {
30
+ open = false;
31
+ }
32
+ }
33
+
34
+ onMount(() => {
35
+ document.addEventListener('click', handleClickOutside);
36
+ return () => document.removeEventListener('click', handleClickOutside);
37
+ });
38
+ </script>
39
+
40
+ <div bind:this={container} class={['popover relative', className].filter(Boolean).join(' ')} {...rest}>
41
+ <div
42
+ role="button"
43
+ tabindex="0"
44
+ aria-expanded={open}
45
+ onclick={(e) => { e.stopPropagation(); open = !open; }}
46
+ onkeydown={(e) => e.key === 'Enter' && (open = !open)}
47
+ class="inline-block"
48
+ >
49
+ {@render trigger({ open })}
50
+ </div>
51
+
52
+ {#if open}
53
+ <section
54
+ data-popover
55
+ aria-hidden={!open}
56
+ data-side={side}
57
+ data-align={align}
58
+ class={['absolute z-50 p-4 bg-popover text-popover-foreground rounded-md border shadow-md outline-none animate-in fade-in-0 zoom-in-95', contentClass].filter(Boolean).join(' ')}
59
+ >
60
+ {@render children()}
61
+ </section>
62
+ {/if}
63
+ </div>
64
+
65
+ <style>
66
+ @import "../../../../ultra/src/css/parts/components/popover.css";
67
+ @reference "../reference.css";
68
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ open?: boolean;
4
+ trigger: Snippet<{
5
+ open: boolean;
6
+ }>;
7
+ children: Snippet;
8
+ side?: 'top' | 'bottom' | 'left' | 'right';
9
+ align?: 'start' | 'center' | 'end';
10
+ class?: string;
11
+ contentClass?: string;
12
+ [key: string]: any;
13
+ };
14
+ declare const Popover: import("svelte").Component<$$ComponentProps, {}, "open">;
15
+ type Popover = ReturnType<typeof Popover>;
16
+ export default Popover;
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ let {
3
+ value = 0,
4
+ max = 100,
5
+ class: className = '',
6
+ ...rest
7
+ }: {
8
+ value?: number;
9
+ max?: number;
10
+ class?: string;
11
+ [key: string]: any;
12
+ } = $props();
13
+
14
+ const percentage = $derived(Math.min(Math.max((value / max) * 100, 0), 100));
15
+ </script>
16
+
17
+ <div class={['progress h-2 bg-muted rounded-full overflow-hidden w-full', className].filter(Boolean).join(' ')} {...rest}>
18
+ <div
19
+ class="bg-primary h-full transition-all duration-300 ease-in-out"
20
+ role="progressbar"
21
+ aria-valuenow={value}
22
+ aria-valuemin="0"
23
+ aria-valuemax={max}
24
+ style="width: {percentage}%"
25
+ ></div>
26
+ </div>
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ value?: number;
3
+ max?: number;
4
+ class?: string;
5
+ [key: string]: any;
6
+ };
7
+ declare const Progress: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type Progress = ReturnType<typeof Progress>;
9
+ export default Progress;
@@ -0,0 +1,22 @@
1
+ <script lang="ts">
2
+ let {
3
+ group = $bindable(),
4
+ value,
5
+ class: className = '',
6
+ ...rest
7
+ }: {
8
+ group?: any;
9
+ value?: any;
10
+ class?: string;
11
+ [key: string]: any;
12
+ } = $props();
13
+
14
+ const finalClass = $derived(['input', className].filter(Boolean).join(' '));
15
+ </script>
16
+
17
+ <input type="radio" class={finalClass} bind:group {value} {...rest} />
18
+
19
+ <style>
20
+ @import "../../../../ultra/src/css/parts/components/radio.css";
21
+ @reference "../reference.css";
22
+ </style>
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ group?: any;
3
+ value?: any;
4
+ class?: string;
5
+ [key: string]: any;
6
+ };
7
+ declare const Radio: import("svelte").Component<$$ComponentProps, {}, "group">;
8
+ type Radio = ReturnType<typeof Radio>;
9
+ export default Radio;
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from 'svelte';
3
+ import Split from 'split.js';
4
+ import type { Instance } from 'split.js';
5
+ import type { Snippet } from 'svelte';
6
+
7
+ let {
8
+ children,
9
+ direction = 'horizontal',
10
+ sizes,
11
+ minSize = 100,
12
+ gutterSize = 10,
13
+ class: className = '',
14
+ ...rest
15
+ }: {
16
+ children?: Snippet;
17
+ direction?: 'horizontal' | 'vertical';
18
+ sizes?: number[];
19
+ minSize?: number | number[];
20
+ gutterSize?: number;
21
+ class?: string;
22
+ [key: string]: any;
23
+ } = $props();
24
+
25
+ let container: HTMLDivElement;
26
+ let splitInstance: Instance;
27
+
28
+ const finalClass = $derived([
29
+ 'resizable-group h-full flex',
30
+ direction === 'vertical' ? 'flex-col' : 'flex-row',
31
+ className
32
+ ].filter(Boolean).join(' '));
33
+
34
+ onMount(() => {
35
+ const elements = Array.from(container.children).filter(el => !el.classList.contains('gutter')) as HTMLElement[];
36
+ if (elements.length < 2) return;
37
+
38
+ splitInstance = Split(elements, {
39
+ direction,
40
+ sizes,
41
+ minSize,
42
+ gutterSize,
43
+ gutter: (index, direction) => {
44
+ const gutter = document.createElement('div');
45
+ gutter.className = `gutter gutter-${direction}`;
46
+ return gutter;
47
+ }
48
+ });
49
+ });
50
+
51
+ onDestroy(() => {
52
+ splitInstance?.destroy();
53
+ });
54
+ </script>
55
+
56
+ <div
57
+ bind:this={container}
58
+ class={finalClass}
59
+ {...rest}
60
+ >
61
+ {@render children?.()}
62
+ </div>
63
+
64
+ <style>
65
+ @reference "../reference.css";
66
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children?: Snippet;
4
+ direction?: 'horizontal' | 'vertical';
5
+ sizes?: number[];
6
+ minSize?: number | number[];
7
+ gutterSize?: number;
8
+ class?: string;
9
+ [key: string]: any;
10
+ };
11
+ declare const Resizable: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Resizable = ReturnType<typeof Resizable>;
13
+ export default Resizable;
@@ -0,0 +1,183 @@
1
+ <script lang="ts">
2
+ import { onMount, tick } from 'svelte';
3
+
4
+ let {
5
+ value = $bindable(),
6
+ options = [],
7
+ placeholder = 'Select an option',
8
+ searchable = false,
9
+ searchPlaceholder = 'Search...',
10
+ class: className = '',
11
+ ...rest
12
+ }: {
13
+ value?: any;
14
+ options: Array<{ value: any; label: string; disabled?: boolean }>;
15
+ placeholder?: string;
16
+ searchable?: boolean;
17
+ searchPlaceholder?: string;
18
+ class?: string;
19
+ [key: string]: any;
20
+ } = $props();
21
+
22
+ let open = $state(false);
23
+ let searchTerm = $state('');
24
+ let activeIndex = $state(-1);
25
+ let container = $state<HTMLDivElement>();
26
+ let filterInput = $state<HTMLInputElement>();
27
+
28
+ const filteredOptions = $derived(
29
+ options.filter(opt =>
30
+ !searchTerm || opt.label.toLowerCase().includes(searchTerm.toLowerCase())
31
+ )
32
+ );
33
+
34
+ const selectedLabel = $derived(
35
+ options.find(opt => opt.value === value)?.label || placeholder
36
+ );
37
+
38
+ async function toggleOpen() {
39
+ open = !open;
40
+ if (open) {
41
+ activeIndex = filteredOptions.findIndex(opt => opt.value === value);
42
+ await tick();
43
+ if (searchable) filterInput?.focus();
44
+
45
+ // 自动滚动到选中项
46
+ const selectedEl = container.querySelector('[aria-selected="true"]');
47
+ selectedEl?.scrollIntoView({ block: 'nearest' });
48
+ } else {
49
+ searchTerm = '';
50
+ }
51
+ }
52
+
53
+ function handleSelect(option: typeof options[0]) {
54
+ if (option.disabled) return;
55
+ value = option.value;
56
+ open = false;
57
+ searchTerm = '';
58
+ }
59
+
60
+ function handleKeyDown(event: KeyboardEvent) {
61
+ if (!open) {
62
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'Enter') {
63
+ event.preventDefault();
64
+ toggleOpen();
65
+ }
66
+ return;
67
+ }
68
+
69
+ switch (event.key) {
70
+ case 'Escape':
71
+ open = false;
72
+ break;
73
+ case 'Enter':
74
+ event.preventDefault();
75
+ if (activeIndex >= 0 && activeIndex < filteredOptions.length) {
76
+ handleSelect(filteredOptions[activeIndex]);
77
+ }
78
+ break;
79
+ case 'ArrowDown':
80
+ event.preventDefault();
81
+ activeIndex = (activeIndex + 1) % filteredOptions.length;
82
+ scrollToActive();
83
+ break;
84
+ case 'ArrowUp':
85
+ event.preventDefault();
86
+ activeIndex = (activeIndex - 1 + filteredOptions.length) % filteredOptions.length;
87
+ scrollToActive();
88
+ break;
89
+ }
90
+ }
91
+
92
+ function scrollToActive() {
93
+ tick().then(() => {
94
+ const activeEl = container.querySelector('.active-option');
95
+ activeEl?.scrollIntoView({ block: 'nearest' });
96
+ });
97
+ }
98
+
99
+ function handleClickOutside(event: MouseEvent) {
100
+ if (open && container && !container.contains(event.target as Node)) {
101
+ open = false;
102
+ searchTerm = '';
103
+ }
104
+ }
105
+
106
+ onMount(() => {
107
+ document.addEventListener('click', handleClickOutside);
108
+ return () => document.removeEventListener('click', handleClickOutside);
109
+ });
110
+ </script>
111
+
112
+ <div bind:this={container} class={['select relative', className].filter(Boolean).join(' ')} {...rest}>
113
+ <button
114
+ type="button"
115
+ class="btn btn-outline w-full justify-between"
116
+ aria-expanded={open}
117
+ aria-haspopup="listbox"
118
+ onclick={(e) => { e.stopPropagation(); toggleOpen(); }}
119
+ onkeydown={handleKeyDown}
120
+ >
121
+ <span class="truncate">{selectedLabel}</span>
122
+ <svg class="opacity-50 shrink-0" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
123
+ </button>
124
+
125
+ {#if open}
126
+ <section
127
+ data-popover
128
+ class="absolute z-50 w-full mt-1 bg-popover border rounded-md shadow-md animate-in fade-in-0 zoom-in-95 overflow-hidden"
129
+ >
130
+ {#if searchable}
131
+ <header class="flex items-center border-b px-3">
132
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="opacity-50 mr-2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
133
+ <input
134
+ bind:this={filterInput}
135
+ bind:value={searchTerm}
136
+ type="text"
137
+ placeholder={searchPlaceholder}
138
+ class="flex h-9 w-full bg-transparent py-2 text-sm outline-none placeholder:text-muted-foreground border-none focus:ring-0"
139
+ onkeydown={handleKeyDown}
140
+ />
141
+ </header>
142
+ {/if}
143
+
144
+ <div role="listbox" class="p-1 max-h-48 overflow-y-auto scrollbar">
145
+ {#if filteredOptions.length === 0}
146
+ <div class="px-2 py-4 text-center text-sm text-muted-foreground">
147
+ No results found.
148
+ </div>
149
+ {:else}
150
+ {#each filteredOptions as option, i}
151
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
152
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
153
+ <div
154
+ role="option"
155
+ tabindex="-1"
156
+ data-value={option.value}
157
+ aria-selected={value === option.value}
158
+ aria-disabled={option.disabled}
159
+ class={[
160
+ 'relative flex w-full cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground data-[disabled=true]:opacity-50 data-[disabled=true]:pointer-events-none',
161
+ activeIndex === i ? 'bg-accent text-accent-foreground active-option' : '',
162
+ value === option.value ? 'bg-accent/50' : ''
163
+ ].join(' ')}
164
+ data-disabled={option.disabled}
165
+ onclick={() => handleSelect(option)}
166
+ onmouseenter={() => activeIndex = i}
167
+ >
168
+ {option.label}
169
+ {#if value === option.value}
170
+ <svg class="ml-auto size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
171
+ {/if}
172
+ </div>
173
+ {/each}
174
+ {/if}
175
+ </div>
176
+ </section>
177
+ {/if}
178
+ </div>
179
+
180
+ <style>
181
+ @import "../../../../ultra/src/css/parts/components/select.css";
182
+ @reference "../reference.css";
183
+ </style>
@@ -0,0 +1,16 @@
1
+ type $$ComponentProps = {
2
+ value?: any;
3
+ options: Array<{
4
+ value: any;
5
+ label: string;
6
+ disabled?: boolean;
7
+ }>;
8
+ placeholder?: string;
9
+ searchable?: boolean;
10
+ searchPlaceholder?: string;
11
+ class?: string;
12
+ [key: string]: any;
13
+ };
14
+ declare const Select: import("svelte").Component<$$ComponentProps, {}, "value">;
15
+ type Select = ReturnType<typeof Select>;
16
+ export default Select;
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ let {
3
+ orientation = 'horizontal',
4
+ class: className = '',
5
+ ...rest
6
+ }: {
7
+ orientation?: 'horizontal' | 'vertical';
8
+ class?: string;
9
+ [key: string]: any;
10
+ } = $props();
11
+
12
+ const finalClass = $derived([
13
+ 'shrink-0 bg-border',
14
+ orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
15
+ className
16
+ ].filter(Boolean).join(' '));
17
+ </script>
18
+
19
+ <hr class={finalClass} aria-orientation={orientation} {...rest} />
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ orientation?: 'horizontal' | 'vertical';
3
+ class?: string;
4
+ [key: string]: any;
5
+ };
6
+ declare const Separator: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Separator = ReturnType<typeof Separator>;
8
+ export default Separator;