twintrinsic 0.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.
- package/LICENSE +674 -0
- package/README.md +150 -0
- package/dist/App/App.svelte +54 -0
- package/dist/App/App.svelte.d.ts +65 -0
- package/dist/Section.svelte +25 -0
- package/dist/Section.svelte.d.ts +34 -0
- package/dist/actions/clickOutside.d.ts +9 -0
- package/dist/actions/clickOutside.js +19 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/components/Accordion/Accordion.svelte +75 -0
- package/dist/components/Accordion/Accordion.svelte.d.ts +39 -0
- package/dist/components/Accordion/AccordionItem.svelte +150 -0
- package/dist/components/Accordion/AccordionItem.svelte.d.ts +30 -0
- package/dist/components/App/App.story.md +8 -0
- package/dist/components/App/App.story.svelte +170 -0
- package/dist/components/App/App.story.svelte.d.ts +22 -0
- package/dist/components/App/App.svelte +77 -0
- package/dist/components/App/App.svelte.d.ts +66 -0
- package/dist/components/App/Split.svelte +346 -0
- package/dist/components/App/Split.svelte.d.ts +54 -0
- package/dist/components/App/index.d.ts +2 -0
- package/dist/components/App/index.js +3 -0
- package/dist/components/AppHeader/AppHeader.svelte +439 -0
- package/dist/components/AppHeader/AppHeader.svelte.d.ts +24 -0
- package/dist/components/Avatar/Avatar.svelte +300 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +48 -0
- package/dist/components/Avatar/AvatarGroup.svelte +185 -0
- package/dist/components/Avatar/AvatarGroup.svelte.d.ts +46 -0
- package/dist/components/Badge/Badge.svelte +186 -0
- package/dist/components/Badge/Badge.svelte.d.ts +51 -0
- package/dist/components/BottomBar/BottomBar.svelte +146 -0
- package/dist/components/BottomBar/BottomBar.svelte.d.ts +38 -0
- package/dist/components/Breadcrumb/Breadcrumb.svelte +77 -0
- package/dist/components/Breadcrumb/Breadcrumb.svelte.d.ts +42 -0
- package/dist/components/Breadcrumb/BreadcrumbItem.svelte +171 -0
- package/dist/components/Breadcrumb/BreadcrumbItem.svelte.d.ts +38 -0
- package/dist/components/Button/Button.svelte +252 -0
- package/dist/components/Button/Button.svelte.d.ts +80 -0
- package/dist/components/Button/ButtonGroup.svelte +127 -0
- package/dist/components/Button/ButtonGroup.svelte.d.ts +44 -0
- package/dist/components/Card/Card.svelte +152 -0
- package/dist/components/Card/Card.svelte.d.ts +55 -0
- package/dist/components/Carousel/Carousel.svelte +461 -0
- package/dist/components/Carousel/Carousel.svelte.d.ts +79 -0
- package/dist/components/Carousel/CarouselItem.svelte +149 -0
- package/dist/components/Carousel/CarouselItem.svelte.d.ts +35 -0
- package/dist/components/Chip/Chip.svelte +288 -0
- package/dist/components/Chip/Chip.svelte.d.ts +71 -0
- package/dist/components/Chip/ChipGroup.svelte +190 -0
- package/dist/components/Chip/ChipGroup.svelte.d.ts +71 -0
- package/dist/components/CodeBlock/CodeBlock.svelte +356 -0
- package/dist/components/CodeBlock/CodeBlock.svelte.d.ts +44 -0
- package/dist/components/CodeBlock/index.d.ts +1 -0
- package/dist/components/CodeBlock/index.js +1 -0
- package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte +145 -0
- package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte.d.ts +44 -0
- package/dist/components/CodeEditor/CodeEditor.svelte +229 -0
- package/dist/components/CodeEditor/CodeEditor.svelte.d.ts +23 -0
- package/dist/components/Combobox/Combobox.svelte +279 -0
- package/dist/components/Combobox/Combobox.svelte.d.ts +34 -0
- package/dist/components/Container/Container.svelte +45 -0
- package/dist/components/Container/Container.svelte.d.ts +36 -0
- package/dist/components/DataTable/DataTable.svelte +879 -0
- package/dist/components/DataTable/DataTable.svelte.d.ts +102 -0
- package/dist/components/Form/AutoComplete.svelte +357 -0
- package/dist/components/Form/AutoComplete.svelte.d.ts +73 -0
- package/dist/components/Form/Calendar.svelte +429 -0
- package/dist/components/Form/Calendar.svelte.d.ts +53 -0
- package/dist/components/Form/Checkbox.svelte +196 -0
- package/dist/components/Form/Checkbox.svelte.d.ts +50 -0
- package/dist/components/Form/ColorPicker.svelte +396 -0
- package/dist/components/Form/ColorPicker.svelte.d.ts +43 -0
- package/dist/components/Form/Combobox.svelte +645 -0
- package/dist/components/Form/Combobox.svelte.d.ts +93 -0
- package/dist/components/Form/Dropdown.svelte +773 -0
- package/dist/components/Form/Dropdown.svelte.d.ts +81 -0
- package/dist/components/Form/FileUpload.svelte +796 -0
- package/dist/components/Form/FileUpload.svelte.d.ts +78 -0
- package/dist/components/Form/FloatLabel.svelte +245 -0
- package/dist/components/Form/FloatLabel.svelte.d.ts +44 -0
- package/dist/components/Form/Form.svelte +281 -0
- package/dist/components/Form/Form.svelte.d.ts +54 -0
- package/dist/components/Form/FormField.svelte +218 -0
- package/dist/components/Form/FormField.svelte.d.ts +47 -0
- package/dist/components/Form/Input.svelte +340 -0
- package/dist/components/Form/Input.svelte.d.ts +79 -0
- package/dist/components/Form/InputSwitch.svelte +189 -0
- package/dist/components/Form/InputSwitch.svelte.d.ts +46 -0
- package/dist/components/Form/InvalidState.svelte +97 -0
- package/dist/components/Form/InvalidState.svelte.d.ts +37 -0
- package/dist/components/Form/Knob.svelte +537 -0
- package/dist/components/Form/Knob.svelte.d.ts +78 -0
- package/dist/components/Form/ListInput.svelte +469 -0
- package/dist/components/Form/ListInput.svelte.d.ts +70 -0
- package/dist/components/Form/Listbox.svelte +513 -0
- package/dist/components/Form/Listbox.svelte.d.ts +74 -0
- package/dist/components/Form/NumberInput.svelte +452 -0
- package/dist/components/Form/NumberInput.svelte.d.ts +82 -0
- package/dist/components/Form/Radio.svelte +192 -0
- package/dist/components/Form/Radio.svelte.d.ts +53 -0
- package/dist/components/Form/RadioGroup.svelte +155 -0
- package/dist/components/Form/RadioGroup.svelte.d.ts +48 -0
- package/dist/components/Form/Rating.svelte +380 -0
- package/dist/components/Form/Rating.svelte.d.ts +64 -0
- package/dist/components/Form/Select.svelte +436 -0
- package/dist/components/Form/Select.svelte.d.ts +49 -0
- package/dist/components/Form/SelectGroup.svelte +34 -0
- package/dist/components/Form/SelectGroup.svelte.d.ts +33 -0
- package/dist/components/Form/Slider.svelte +622 -0
- package/dist/components/Form/Slider.svelte.d.ts +73 -0
- package/dist/components/Form/Switch.svelte +192 -0
- package/dist/components/Form/Switch.svelte.d.ts +46 -0
- package/dist/components/Form/TextInput.svelte +274 -0
- package/dist/components/Form/TextInput.svelte.d.ts +74 -0
- package/dist/components/Form/Textarea.svelte +207 -0
- package/dist/components/Form/Textarea.svelte.d.ts +62 -0
- package/dist/components/Icon/Icon.svelte +140 -0
- package/dist/components/Icon/Icon.svelte.d.ts +25 -0
- package/dist/components/Icon/index.d.ts +1 -0
- package/dist/components/Icon/index.js +1 -0
- package/dist/components/Lazy/Lazy.svelte +158 -0
- package/dist/components/Lazy/Lazy.svelte.d.ts +42 -0
- package/dist/components/Masonry/Masonry.svelte +299 -0
- package/dist/components/Masonry/Masonry.svelte.d.ts +55 -0
- package/dist/components/Menu/Menu/Menu.svelte +65 -0
- package/dist/components/Menu/Menu/Menu.svelte.d.ts +17 -0
- package/dist/components/Menu/Menu/MenuItem.svelte +90 -0
- package/dist/components/Menu/Menu/MenuItem.svelte.d.ts +27 -0
- package/dist/components/Modal/Modal.svelte +334 -0
- package/dist/components/Modal/Modal.svelte.d.ts +55 -0
- package/dist/components/Panel/Card.svelte +141 -0
- package/dist/components/Panel/Card.svelte.d.ts +52 -0
- package/dist/components/Panel/Hero/Hero.story.md +9 -0
- package/dist/components/Panel/Hero/Hero.story.svelte +49 -0
- package/dist/components/Panel/Hero/Hero.story.svelte.d.ts +21 -0
- package/dist/components/Panel/Hero/Hero.svelte +24 -0
- package/dist/components/Panel/Hero/Hero.svelte.d.ts +32 -0
- package/dist/components/Panel/LazyPanel.svelte +110 -0
- package/dist/components/Panel/LazyPanel.svelte.d.ts +46 -0
- package/dist/components/Panel/Panel.svelte +205 -0
- package/dist/components/Panel/Panel.svelte.d.ts +23 -0
- package/dist/components/Progress/Progress.svelte +220 -0
- package/dist/components/Progress/Progress.svelte.d.ts +61 -0
- package/dist/components/Separator/Separator.svelte +109 -0
- package/dist/components/Separator/Separator.svelte.d.ts +35 -0
- package/dist/components/Sidebar/Sidebar.svelte +213 -0
- package/dist/components/Sidebar/Sidebar.svelte.d.ts +60 -0
- package/dist/components/Skeleton/Skeleton.svelte +170 -0
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +48 -0
- package/dist/components/Stepper/Stepper.svelte +111 -0
- package/dist/components/Stepper/Stepper.svelte.d.ts +54 -0
- package/dist/components/Stepper/StepperStep.svelte +369 -0
- package/dist/components/Stepper/StepperStep.svelte.d.ts +63 -0
- package/dist/components/Table/Table.svelte +167 -0
- package/dist/components/Table/Table.svelte.d.ts +56 -0
- package/dist/components/Table/TableBody.svelte +41 -0
- package/dist/components/Table/TableBody.svelte.d.ts +33 -0
- package/dist/components/Table/TableCell.svelte +76 -0
- package/dist/components/Table/TableCell.svelte.d.ts +36 -0
- package/dist/components/Table/TableHead.svelte +41 -0
- package/dist/components/Table/TableHead.svelte.d.ts +32 -0
- package/dist/components/Table/TableHeader.svelte +148 -0
- package/dist/components/Table/TableHeader.svelte.d.ts +42 -0
- package/dist/components/Table/TableRow.svelte +99 -0
- package/dist/components/Table/TableRow.svelte.d.ts +40 -0
- package/dist/components/Tabs/Tab.svelte +145 -0
- package/dist/components/Tabs/Tab.svelte.d.ts +36 -0
- package/dist/components/Tabs/TabList.svelte +60 -0
- package/dist/components/Tabs/TabList.svelte.d.ts +32 -0
- package/dist/components/Tabs/TabPanel.svelte +118 -0
- package/dist/components/Tabs/TabPanel.svelte.d.ts +38 -0
- package/dist/components/Tabs/Tabs.svelte +287 -0
- package/dist/components/Tabs/Tabs.svelte.d.ts +50 -0
- package/dist/components/Tag/Tag.svelte +260 -0
- package/dist/components/Tag/Tag.svelte.d.ts +54 -0
- package/dist/components/Tag/TagGroup.svelte +147 -0
- package/dist/components/Tag/TagGroup.svelte.d.ts +62 -0
- package/dist/components/ThemeToggle/ThemeToggle.svelte +93 -0
- package/dist/components/ThemeToggle/ThemeToggle.svelte.d.ts +12 -0
- package/dist/components/Timeline/Timeline.svelte +144 -0
- package/dist/components/Timeline/Timeline.svelte.d.ts +48 -0
- package/dist/components/Timeline/TimelineItem.svelte +391 -0
- package/dist/components/Timeline/TimelineItem.svelte.d.ts +63 -0
- package/dist/components/Toast/Toast.svelte +313 -0
- package/dist/components/Toast/Toast.svelte.d.ts +44 -0
- package/dist/components/Toast/toastStore.d.ts +40 -0
- package/dist/components/Toast/toastStore.js +293 -0
- package/dist/components/Tooltip/Tooltip.svelte +282 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +55 -0
- package/dist/components/Tree/Tree.svelte +129 -0
- package/dist/components/Tree/Tree.svelte.d.ts +61 -0
- package/dist/components/Tree/TreeNode.svelte +332 -0
- package/dist/components/Tree/TreeNode.svelte.d.ts +55 -0
- package/dist/components/icons/TwintrinsicLogo.svelte +73 -0
- package/dist/components/icons/TwintrinsicLogo.svelte.d.ts +17 -0
- package/dist/components/icons/twintrinsic-source.svg +73 -0
- package/dist/components/icons/twintrinsic.svg +38 -0
- package/dist/docs/EventsTable.svelte +86 -0
- package/dist/docs/EventsTable.svelte.d.ts +27 -0
- package/dist/docs/PropsTable.svelte +103 -0
- package/dist/docs/PropsTable.svelte.d.ts +28 -0
- package/dist/docs/index.d.ts +2 -0
- package/dist/docs/index.js +2 -0
- package/dist/helpers/detectLanguage.d.ts +6 -0
- package/dist/helpers/detectLanguage.js +60 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +94 -0
- package/dist/twintrinsic.css +347 -0
- package/package.json +98 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Select - A form component for selecting options from a dropdown list.
|
|
4
|
+
Supports single and multiple selection, option groups, and custom option templates.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<Select
|
|
9
|
+
label="Country"
|
|
10
|
+
options={countries}
|
|
11
|
+
onchange={handleChange}
|
|
12
|
+
/>
|
|
13
|
+
|
|
14
|
+
<Select
|
|
15
|
+
label="Skills"
|
|
16
|
+
options={skills}
|
|
17
|
+
multiple={true}
|
|
18
|
+
placeholder="Select skills..."
|
|
19
|
+
/>
|
|
20
|
+
```
|
|
21
|
+
-->
|
|
22
|
+
<script>
|
|
23
|
+
import { slide } from "svelte/transition"
|
|
24
|
+
import { clickOutside } from "../../actions/index.js"
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
/** @type {string} - Input label */
|
|
28
|
+
label = "",
|
|
29
|
+
/** @type {Array<{ value: string, label: string, group?: string }>} - Options to display */
|
|
30
|
+
options = [],
|
|
31
|
+
/** @type {string | string[]} - Selected value(s) */
|
|
32
|
+
value = "",
|
|
33
|
+
/** @type {boolean} - Whether multiple selection is allowed */
|
|
34
|
+
multiple = false,
|
|
35
|
+
/** @type {string} - Placeholder text */
|
|
36
|
+
placeholder = "Select...",
|
|
37
|
+
/** @type {boolean} - Whether the select is disabled */
|
|
38
|
+
disabled = false,
|
|
39
|
+
/** @type {string} - Error message */
|
|
40
|
+
error = "",
|
|
41
|
+
/** @type {boolean} - Whether the field is required */
|
|
42
|
+
required = false,
|
|
43
|
+
/** @type {string} - Additional CSS classes */
|
|
44
|
+
class: className = "",
|
|
45
|
+
/** @type {(event: CustomEvent) => void} - Change event handler */
|
|
46
|
+
onchange,
|
|
47
|
+
} = $props()
|
|
48
|
+
|
|
49
|
+
let showDropdown = $state(false)
|
|
50
|
+
let searchValue = $state("")
|
|
51
|
+
let selectedValues = $state([])
|
|
52
|
+
let focusedIndex = $state(-1)
|
|
53
|
+
let dropdownRef = $state()
|
|
54
|
+
|
|
55
|
+
// Initialize selected values
|
|
56
|
+
$effect(() => {
|
|
57
|
+
selectedValues = Array.isArray(value) ? value : value ? [value] : []
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Filter options based on search
|
|
61
|
+
const filteredOptions = $derived(() => {
|
|
62
|
+
const search = searchValue.toLowerCase()
|
|
63
|
+
return options.filter((option) => option.label.toLowerCase().includes(search))
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// Group options by their group property
|
|
67
|
+
const groupedOptions = $derived(() => {
|
|
68
|
+
const groups = new Map()
|
|
69
|
+
|
|
70
|
+
for (const option of filteredOptions) {
|
|
71
|
+
const group = option.group || ""
|
|
72
|
+
if (!groups.has(group)) {
|
|
73
|
+
groups.set(group, [])
|
|
74
|
+
}
|
|
75
|
+
groups.get(group).push(option)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return groups
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Handle option selection
|
|
82
|
+
function selectOption(option) {
|
|
83
|
+
if (disabled) return
|
|
84
|
+
|
|
85
|
+
const value = option.value
|
|
86
|
+
let newValues
|
|
87
|
+
|
|
88
|
+
if (multiple) {
|
|
89
|
+
newValues = selectedValues.includes(value)
|
|
90
|
+
? selectedValues.filter((v) => v !== value)
|
|
91
|
+
: [...selectedValues, value]
|
|
92
|
+
} else {
|
|
93
|
+
newValues = [value]
|
|
94
|
+
showDropdown = false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
selectedValues = newValues
|
|
98
|
+
onchange?.(new CustomEvent("change", {
|
|
99
|
+
detail: {
|
|
100
|
+
value: multiple ? newValues : newValues[0],
|
|
101
|
+
}
|
|
102
|
+
}))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Handle keyboard navigation
|
|
106
|
+
function handleKeydown(event) {
|
|
107
|
+
if (disabled) return
|
|
108
|
+
|
|
109
|
+
switch (event.key) {
|
|
110
|
+
case "Enter":
|
|
111
|
+
case " ":
|
|
112
|
+
if (!showDropdown) {
|
|
113
|
+
event.preventDefault()
|
|
114
|
+
showDropdown = true
|
|
115
|
+
} else if (focusedIndex >= 0) {
|
|
116
|
+
event.preventDefault()
|
|
117
|
+
selectOption(filteredOptions[focusedIndex])
|
|
118
|
+
}
|
|
119
|
+
break
|
|
120
|
+
|
|
121
|
+
case "ArrowDown":
|
|
122
|
+
event.preventDefault()
|
|
123
|
+
if (!showDropdown) {
|
|
124
|
+
showDropdown = true
|
|
125
|
+
} else {
|
|
126
|
+
focusedIndex = Math.min(focusedIndex + 1, filteredOptions.length - 1)
|
|
127
|
+
scrollToOption(focusedIndex)
|
|
128
|
+
}
|
|
129
|
+
break
|
|
130
|
+
|
|
131
|
+
case "ArrowUp":
|
|
132
|
+
event.preventDefault()
|
|
133
|
+
if (showDropdown) {
|
|
134
|
+
focusedIndex = Math.max(focusedIndex - 1, 0)
|
|
135
|
+
scrollToOption(focusedIndex)
|
|
136
|
+
}
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
case "Escape":
|
|
140
|
+
showDropdown = false
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
case "Tab":
|
|
144
|
+
showDropdown = false
|
|
145
|
+
break
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Scroll focused option into view
|
|
150
|
+
function scrollToOption(index) {
|
|
151
|
+
if (!dropdownRef) return
|
|
152
|
+
|
|
153
|
+
const options = dropdownRef.querySelectorAll(".select-option")
|
|
154
|
+
if (options[index]) {
|
|
155
|
+
options[index].scrollIntoView({
|
|
156
|
+
block: "nearest",
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Clear selection
|
|
162
|
+
function clearSelection(event) {
|
|
163
|
+
event.stopPropagation()
|
|
164
|
+
if (disabled) return
|
|
165
|
+
selectedValues = []
|
|
166
|
+
dispatch("change", { value: multiple ? [] : "" })
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Get display value
|
|
170
|
+
const displayValue = $derived(() => {
|
|
171
|
+
if (!selectedValues.length) return ""
|
|
172
|
+
|
|
173
|
+
const selected = options.filter((option) => selectedValues.includes(option.value))
|
|
174
|
+
|
|
175
|
+
if (multiple) {
|
|
176
|
+
return selected.length === 1 ? selected[0].label : `${selected.length} items selected`
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return selected[0]?.label || ""
|
|
180
|
+
})
|
|
181
|
+
</script>
|
|
182
|
+
|
|
183
|
+
<div
|
|
184
|
+
class="select {className}"
|
|
185
|
+
class:select-error={!!error}
|
|
186
|
+
class:select-disabled={disabled}
|
|
187
|
+
use:clickOutside
|
|
188
|
+
onclickOutside={() => showDropdown = false}
|
|
189
|
+
>
|
|
190
|
+
<label class="select-label">
|
|
191
|
+
{#if label}
|
|
192
|
+
<span class="select-label-text">
|
|
193
|
+
{label}
|
|
194
|
+
{#if required}
|
|
195
|
+
<span class="select-required" aria-hidden="true">*</span>
|
|
196
|
+
{/if}
|
|
197
|
+
</span>
|
|
198
|
+
{/if}
|
|
199
|
+
|
|
200
|
+
<div
|
|
201
|
+
class="select-control"
|
|
202
|
+
role="combobox"
|
|
203
|
+
aria-expanded={showDropdown}
|
|
204
|
+
aria-haspopup="listbox"
|
|
205
|
+
aria-controls="select-options"
|
|
206
|
+
aria-label={label}
|
|
207
|
+
aria-required={required}
|
|
208
|
+
aria-invalid={!!error}
|
|
209
|
+
aria-describedby={error ? 'select-error' : undefined}
|
|
210
|
+
tabindex={disabled ? -1 : 0}
|
|
211
|
+
onclick={() => !disabled && (showDropdown = !showDropdown)}
|
|
212
|
+
onkeydown={handleKeydown}
|
|
213
|
+
>
|
|
214
|
+
<div class="select-value">
|
|
215
|
+
{#if selectedValues.length}
|
|
216
|
+
<span class="select-text">{displayValue}</span>
|
|
217
|
+
{#if !disabled}
|
|
218
|
+
<button
|
|
219
|
+
type="button"
|
|
220
|
+
class="select-clear"
|
|
221
|
+
aria-label="Clear selection"
|
|
222
|
+
onclick={clearSelection}
|
|
223
|
+
>
|
|
224
|
+
×
|
|
225
|
+
</button>
|
|
226
|
+
{/if}
|
|
227
|
+
{:else}
|
|
228
|
+
<span class="select-placeholder">{placeholder}</span>
|
|
229
|
+
{/if}
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<div class="select-indicator">
|
|
233
|
+
<svg
|
|
234
|
+
class="select-arrow"
|
|
235
|
+
class:select-arrow-open={showDropdown}
|
|
236
|
+
viewBox="0 0 24 24"
|
|
237
|
+
width="16"
|
|
238
|
+
height="16"
|
|
239
|
+
>
|
|
240
|
+
<path
|
|
241
|
+
fill="currentColor"
|
|
242
|
+
d="M7 10l5 5 5-5z"
|
|
243
|
+
/>
|
|
244
|
+
</svg>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
</label>
|
|
248
|
+
|
|
249
|
+
{#if error}
|
|
250
|
+
<div
|
|
251
|
+
id="select-error"
|
|
252
|
+
class="select-error-text"
|
|
253
|
+
aria-live="polite"
|
|
254
|
+
>
|
|
255
|
+
{error}
|
|
256
|
+
</div>
|
|
257
|
+
{/if}
|
|
258
|
+
|
|
259
|
+
{#if showDropdown}
|
|
260
|
+
<div
|
|
261
|
+
class="select-dropdown"
|
|
262
|
+
id="select-options"
|
|
263
|
+
role="listbox"
|
|
264
|
+
aria-multiselectable={multiple}
|
|
265
|
+
bind:this={dropdownRef}
|
|
266
|
+
transition:slide={{ duration: 150 }}
|
|
267
|
+
>
|
|
268
|
+
{#if options.length > 10}
|
|
269
|
+
<div class="select-search">
|
|
270
|
+
<input
|
|
271
|
+
type="text"
|
|
272
|
+
placeholder="Search..."
|
|
273
|
+
bind:value={searchValue}
|
|
274
|
+
onclick={event => event.stopPropagation()}
|
|
275
|
+
/>
|
|
276
|
+
</div>
|
|
277
|
+
{/if}
|
|
278
|
+
|
|
279
|
+
<div class="select-options">
|
|
280
|
+
{#each [...groupedOptions.entries()] as [group, options]}
|
|
281
|
+
{#if group}
|
|
282
|
+
<div class="select-group">
|
|
283
|
+
<div class="select-group-label">{group}</div>
|
|
284
|
+
{#each options as option, index}
|
|
285
|
+
<div
|
|
286
|
+
class="select-option"
|
|
287
|
+
class:select-option-selected={selectedValues.includes(option.value)}
|
|
288
|
+
class:select-option-focused={focusedIndex === index}
|
|
289
|
+
role="option"
|
|
290
|
+
aria-selected={selectedValues.includes(option.value)}
|
|
291
|
+
onclick={() => selectOption(option)}
|
|
292
|
+
>
|
|
293
|
+
{option.label}
|
|
294
|
+
</div>
|
|
295
|
+
{/each}
|
|
296
|
+
</div>
|
|
297
|
+
{:else}
|
|
298
|
+
{#each options as option, index}
|
|
299
|
+
<div
|
|
300
|
+
class="select-option"
|
|
301
|
+
class:select-option-selected={selectedValues.includes(option.value)}
|
|
302
|
+
class:select-option-focused={focusedIndex === index}
|
|
303
|
+
role="option"
|
|
304
|
+
aria-selected={selectedValues.includes(option.value)}
|
|
305
|
+
onclick={() => selectOption(option)}
|
|
306
|
+
>
|
|
307
|
+
{option.label}
|
|
308
|
+
</div>
|
|
309
|
+
{/each}
|
|
310
|
+
{/if}
|
|
311
|
+
{/each}
|
|
312
|
+
|
|
313
|
+
{#if filteredOptions.length === 0}
|
|
314
|
+
<div class="select-no-results">
|
|
315
|
+
No options found
|
|
316
|
+
</div>
|
|
317
|
+
{/if}
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
{/if}
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<style>
|
|
324
|
+
@reference '../../twintrinsic.css';
|
|
325
|
+
|
|
326
|
+
.select {
|
|
327
|
+
@apply relative inline-block w-full;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.select-label {
|
|
331
|
+
@apply block;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.select-label-text {
|
|
335
|
+
@apply block mb-1 text-sm font-medium;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.select-required {
|
|
339
|
+
@apply ml-1 text-error;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.select-control {
|
|
343
|
+
@apply relative flex items-center w-full px-3 py-2;
|
|
344
|
+
@apply bg-surface border border-border rounded-md;
|
|
345
|
+
@apply text-sm cursor-pointer;
|
|
346
|
+
@apply transition-colors duration-150;
|
|
347
|
+
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.select-disabled .select-control {
|
|
351
|
+
@apply bg-surface/50 cursor-not-allowed;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.select-error .select-control {
|
|
355
|
+
@apply border-error;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.select-value {
|
|
359
|
+
@apply flex-1 flex items-center min-w-0;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.select-text {
|
|
363
|
+
@apply truncate;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.select-placeholder {
|
|
367
|
+
@apply text-muted;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.select-clear {
|
|
371
|
+
@apply ml-2 text-lg text-muted hover:text-primary-text;
|
|
372
|
+
@apply focus:outline-none focus:text-primary-text;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.select-indicator {
|
|
376
|
+
@apply ml-2 flex-none;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.select-arrow {
|
|
380
|
+
@apply transition-transform duration-150;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.select-arrow-open {
|
|
384
|
+
@apply rotate-180;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.select-dropdown {
|
|
388
|
+
@apply absolute z-50 w-full mt-1;
|
|
389
|
+
@apply bg-surface border border-border rounded-md shadow-lg;
|
|
390
|
+
@apply max-h-60 overflow-auto;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.select-search {
|
|
394
|
+
@apply sticky top-0 p-2 border-b border-border bg-surface;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.select-search input {
|
|
398
|
+
@apply w-full px-2 py-1 text-sm;
|
|
399
|
+
@apply bg-surface border border-border rounded;
|
|
400
|
+
@apply focus:outline-none focus:ring-2 focus:ring-primary/50;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.select-options {
|
|
404
|
+
@apply py-1;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.select-group {
|
|
408
|
+
@apply py-1;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.select-group-label {
|
|
412
|
+
@apply px-3 py-1 text-xs font-medium text-muted;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.select-option {
|
|
416
|
+
@apply px-3 py-2 text-sm cursor-pointer;
|
|
417
|
+
@apply hover:bg-hover focus:bg-hover;
|
|
418
|
+
@apply transition-colors duration-150;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.select-option-selected {
|
|
422
|
+
@apply bg-primary/10 text-primary;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.select-option-focused {
|
|
426
|
+
@apply bg-hover;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.select-no-results {
|
|
430
|
+
@apply px-3 py-2 text-sm text-muted;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.select-error-text {
|
|
434
|
+
@apply mt-1 text-sm text-error;
|
|
435
|
+
}
|
|
436
|
+
</style>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export default Select;
|
|
2
|
+
type Select = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Select - A form component for selecting options from a dropdown list.
|
|
8
|
+
* Supports single and multiple selection, option groups, and custom option templates.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <Select
|
|
13
|
+
* label="Country"
|
|
14
|
+
* options={countries}
|
|
15
|
+
* onchange={handleChange}
|
|
16
|
+
* />
|
|
17
|
+
*
|
|
18
|
+
* <Select
|
|
19
|
+
* label="Skills"
|
|
20
|
+
* options={skills}
|
|
21
|
+
* multiple={true}
|
|
22
|
+
* placeholder="Select skills..."
|
|
23
|
+
* />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare const Select: import("svelte").Component<{
|
|
27
|
+
label?: string;
|
|
28
|
+
options?: any[];
|
|
29
|
+
value?: string;
|
|
30
|
+
multiple?: boolean;
|
|
31
|
+
placeholder?: string;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
error?: string;
|
|
34
|
+
required?: boolean;
|
|
35
|
+
class?: string;
|
|
36
|
+
onchange: any;
|
|
37
|
+
}, {}, "">;
|
|
38
|
+
type $$ComponentProps = {
|
|
39
|
+
label?: string;
|
|
40
|
+
options?: any[];
|
|
41
|
+
value?: string;
|
|
42
|
+
multiple?: boolean;
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
error?: string;
|
|
46
|
+
required?: boolean;
|
|
47
|
+
class?: string;
|
|
48
|
+
onchange: any;
|
|
49
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
SelectGroup - A component for grouping related options in a Select component.
|
|
4
|
+
Provides semantic grouping with optional labels.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<Select name="country">
|
|
9
|
+
<SelectGroup label="Europe">
|
|
10
|
+
<option value="fr">France</option>
|
|
11
|
+
<option value="de">Germany</option>
|
|
12
|
+
</SelectGroup>
|
|
13
|
+
<SelectGroup label="North America">
|
|
14
|
+
<option value="us">United States</option>
|
|
15
|
+
<option value="ca">Canada</option>
|
|
16
|
+
</SelectGroup>
|
|
17
|
+
</Select>
|
|
18
|
+
```
|
|
19
|
+
-->
|
|
20
|
+
<script>
|
|
21
|
+
const {
|
|
22
|
+
/** @type {string} - Group label */
|
|
23
|
+
label,
|
|
24
|
+
|
|
25
|
+
/** @type {boolean} - Whether the group is disabled */
|
|
26
|
+
disabled = false,
|
|
27
|
+
|
|
28
|
+
children,
|
|
29
|
+
} = $props()
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<optgroup {label} {disabled}>
|
|
33
|
+
{@render children?.()}
|
|
34
|
+
</optgroup>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default SelectGroup;
|
|
2
|
+
type SelectGroup = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* SelectGroup - A component for grouping related options in a Select component.
|
|
8
|
+
* Provides semantic grouping with optional labels.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <Select name="country">
|
|
13
|
+
* <SelectGroup label="Europe">
|
|
14
|
+
* <option value="fr">France</option>
|
|
15
|
+
* <option value="de">Germany</option>
|
|
16
|
+
* </SelectGroup>
|
|
17
|
+
* <SelectGroup label="North America">
|
|
18
|
+
* <option value="us">United States</option>
|
|
19
|
+
* <option value="ca">Canada</option>
|
|
20
|
+
* </SelectGroup>
|
|
21
|
+
* </Select>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare const SelectGroup: import("svelte").Component<{
|
|
25
|
+
label: any;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
children: any;
|
|
28
|
+
}, {}, "">;
|
|
29
|
+
type $$ComponentProps = {
|
|
30
|
+
label: any;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
children: any;
|
|
33
|
+
};
|