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,773 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Dropdown - A component for selecting one or multiple options from a dropdown menu.
|
|
4
|
+
Supports icons, groups, cascading menus, and keyboard navigation.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<Dropdown
|
|
9
|
+
name="country"
|
|
10
|
+
options={countries}
|
|
11
|
+
placeholder="Select a country"
|
|
12
|
+
/>
|
|
13
|
+
|
|
14
|
+
<Dropdown
|
|
15
|
+
name="skills"
|
|
16
|
+
options={skills}
|
|
17
|
+
multiple
|
|
18
|
+
placeholder="Select skills"
|
|
19
|
+
/>
|
|
20
|
+
|
|
21
|
+
<Dropdown
|
|
22
|
+
name="category"
|
|
23
|
+
options={categories}
|
|
24
|
+
optionLabel="name"
|
|
25
|
+
optionValue="id"
|
|
26
|
+
optionIcon="icon"
|
|
27
|
+
/>
|
|
28
|
+
```
|
|
29
|
+
-->
|
|
30
|
+
<script>
|
|
31
|
+
import { getContext } from "svelte"
|
|
32
|
+
import { slide } from "svelte/transition"
|
|
33
|
+
import { clickOutside } from "../../actions/clickOutside.js"
|
|
34
|
+
|
|
35
|
+
const {
|
|
36
|
+
/** @type {string} - Additional CSS classes */
|
|
37
|
+
class: className = "",
|
|
38
|
+
|
|
39
|
+
/** @type {string} - HTML id for accessibility */
|
|
40
|
+
id = crypto.randomUUID(),
|
|
41
|
+
|
|
42
|
+
/** @type {string} - Input name */
|
|
43
|
+
name,
|
|
44
|
+
|
|
45
|
+
/** @type {Array} - Options to display */
|
|
46
|
+
options = [],
|
|
47
|
+
|
|
48
|
+
/** @type {any} - Selected value(s) */
|
|
49
|
+
value,
|
|
50
|
+
|
|
51
|
+
/** @type {string} - Placeholder text */
|
|
52
|
+
placeholder = "Select an option",
|
|
53
|
+
|
|
54
|
+
/** @type {boolean} - Whether multiple selection is allowed */
|
|
55
|
+
multiple = false,
|
|
56
|
+
|
|
57
|
+
/** @type {string} - Property name for option label */
|
|
58
|
+
optionLabel = "label",
|
|
59
|
+
|
|
60
|
+
/** @type {string} - Property name for option value */
|
|
61
|
+
optionValue = "value",
|
|
62
|
+
|
|
63
|
+
/** @type {string} - Property name for option icon */
|
|
64
|
+
optionIcon,
|
|
65
|
+
|
|
66
|
+
/** @type {string} - Property name for option children (for cascading) */
|
|
67
|
+
optionChildren = "items",
|
|
68
|
+
|
|
69
|
+
/** @type {boolean} - Whether the dropdown is disabled */
|
|
70
|
+
disabled = false,
|
|
71
|
+
|
|
72
|
+
/** @type {boolean} - Whether the dropdown is required */
|
|
73
|
+
required = false,
|
|
74
|
+
|
|
75
|
+
/** @type {boolean} - Whether to filter options by typing */
|
|
76
|
+
filter = false,
|
|
77
|
+
|
|
78
|
+
/** @type {string} - Size of the dropdown (sm, md, lg) */
|
|
79
|
+
size = "md",
|
|
80
|
+
|
|
81
|
+
/** @type {boolean} - Whether to show a clear button */
|
|
82
|
+
clearable = false,
|
|
83
|
+
|
|
84
|
+
/** @type {string} - ARIA label for accessibility */
|
|
85
|
+
ariaLabel,
|
|
86
|
+
|
|
87
|
+
/** @type {(event: CustomEvent) => void} - Change event handler */
|
|
88
|
+
onchange,
|
|
89
|
+
/** @type {(event: CustomEvent) => void} - Clear event handler */
|
|
90
|
+
onclear,
|
|
91
|
+
/** @type {(event: CustomEvent) => void} - Open event handler */
|
|
92
|
+
onopen,
|
|
93
|
+
/** @type {(event: CustomEvent) => void} - Close event handler */
|
|
94
|
+
onclose,
|
|
95
|
+
/** @type {(event: CustomEvent) => void} - Filter event handler */
|
|
96
|
+
onfilter,
|
|
97
|
+
} = $props()
|
|
98
|
+
|
|
99
|
+
// Get form context if available
|
|
100
|
+
const formContext = getContext("form")
|
|
101
|
+
|
|
102
|
+
// Component state
|
|
103
|
+
let isOpen = $state(false)
|
|
104
|
+
let selectedValues = $state(multiple ? [] : null)
|
|
105
|
+
let filterValue = $state("")
|
|
106
|
+
let highlightedIndex = $state(0)
|
|
107
|
+
let dropdownElement = $state()
|
|
108
|
+
let inputElement = $state()
|
|
109
|
+
let menuElement = $state()
|
|
110
|
+
let activeSubmenu = $state(null)
|
|
111
|
+
|
|
112
|
+
// Register with form if available
|
|
113
|
+
let fieldApi = $state()
|
|
114
|
+
|
|
115
|
+
$effect(() => {
|
|
116
|
+
if (formContext && name) {
|
|
117
|
+
fieldApi = formContext.registerField(name, value)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Update value when form field changes
|
|
122
|
+
$effect(() => {
|
|
123
|
+
if (fieldApi) {
|
|
124
|
+
const formValue = fieldApi.getValue()
|
|
125
|
+
if (formValue !== undefined && JSON.stringify(formValue) !== JSON.stringify(selectedValues)) {
|
|
126
|
+
selectedValues = formValue
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// Initialize selected values from prop
|
|
132
|
+
$effect(() => {
|
|
133
|
+
if (value !== undefined) {
|
|
134
|
+
selectedValues = value
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Gets the display label for an option
|
|
140
|
+
* @param {Object|string} option - Option to get label for
|
|
141
|
+
* @returns {string} - Display label
|
|
142
|
+
*/
|
|
143
|
+
function getOptionLabel(option) {
|
|
144
|
+
if (!option) return ""
|
|
145
|
+
|
|
146
|
+
if (typeof option === "object") {
|
|
147
|
+
return option[optionLabel] || ""
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return option.toString()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Gets the value for an option
|
|
155
|
+
* @param {Object|string} option - Option to get value for
|
|
156
|
+
* @returns {any} - Option value
|
|
157
|
+
*/
|
|
158
|
+
function getOptionValue(option) {
|
|
159
|
+
if (!option) return null
|
|
160
|
+
|
|
161
|
+
if (typeof option === "object") {
|
|
162
|
+
return option[optionValue]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return option
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Gets the icon for an option
|
|
170
|
+
* @param {Object} option - Option to get icon for
|
|
171
|
+
* @returns {string} - Icon HTML
|
|
172
|
+
*/
|
|
173
|
+
function getOptionIcon(option) {
|
|
174
|
+
if (!option || !optionIcon || typeof option !== "object") return null
|
|
175
|
+
|
|
176
|
+
return option[optionIcon]
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Gets the children for an option (for cascading)
|
|
181
|
+
* @param {Object} option - Option to get children for
|
|
182
|
+
* @returns {Array} - Child options
|
|
183
|
+
*/
|
|
184
|
+
function getOptionChildren(option) {
|
|
185
|
+
if (!option || typeof option !== "object") return null
|
|
186
|
+
|
|
187
|
+
return option[optionChildren] || null
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Checks if an option is selected
|
|
192
|
+
* @param {Object|string} option - Option to check
|
|
193
|
+
* @returns {boolean} - Whether the option is selected
|
|
194
|
+
*/
|
|
195
|
+
function isOptionSelected(option) {
|
|
196
|
+
const value = getOptionValue(option)
|
|
197
|
+
|
|
198
|
+
if (multiple) {
|
|
199
|
+
return (
|
|
200
|
+
Array.isArray(selectedValues) &&
|
|
201
|
+
selectedValues.some((v) => (typeof v === "object" ? v[optionValue] === value : v === value))
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
selectedValues === value ||
|
|
207
|
+
(typeof selectedValues === "object" && selectedValues && selectedValues[optionValue] === value)
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Filters options based on input value
|
|
213
|
+
* @param {Array} opts - Options to filter
|
|
214
|
+
* @returns {Array} - Filtered options
|
|
215
|
+
*/
|
|
216
|
+
function filterOptions(opts) {
|
|
217
|
+
if (!filter || !filterValue) return opts
|
|
218
|
+
|
|
219
|
+
return opts.filter((option) => {
|
|
220
|
+
const label = getOptionLabel(option).toLowerCase()
|
|
221
|
+
return label.includes(filterValue.toLowerCase())
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Gets the display value for the input
|
|
227
|
+
* @returns {string} - Display value
|
|
228
|
+
*/
|
|
229
|
+
function getDisplayValue() {
|
|
230
|
+
if (!selectedValues || (Array.isArray(selectedValues) && selectedValues.length === 0)) {
|
|
231
|
+
return ""
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (multiple) {
|
|
235
|
+
if (Array.isArray(selectedValues)) {
|
|
236
|
+
return selectedValues
|
|
237
|
+
.map((v) =>
|
|
238
|
+
typeof v === "object"
|
|
239
|
+
? getOptionLabel(v)
|
|
240
|
+
: getOptionLabel(options.find((o) => getOptionValue(o) === v))
|
|
241
|
+
)
|
|
242
|
+
.join(", ")
|
|
243
|
+
}
|
|
244
|
+
return ""
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (typeof selectedValues === "object") {
|
|
248
|
+
return getOptionLabel(selectedValues)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const selectedOption = options.find((o) => getOptionValue(o) === selectedValues)
|
|
252
|
+
return selectedOption ? getOptionLabel(selectedOption) : ""
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Selects an option
|
|
257
|
+
* @param {Object|string} option - Option to select
|
|
258
|
+
*/
|
|
259
|
+
function selectOption(option, evt) {
|
|
260
|
+
evt.stopPropagation()
|
|
261
|
+
const value = getOptionValue(option)
|
|
262
|
+
|
|
263
|
+
if (multiple) {
|
|
264
|
+
if (isOptionSelected(option)) {
|
|
265
|
+
// Remove from selection
|
|
266
|
+
selectedValues = Array.isArray(selectedValues)
|
|
267
|
+
? selectedValues.filter((v) =>
|
|
268
|
+
typeof v === "object" ? v[optionValue] !== value : v !== value
|
|
269
|
+
)
|
|
270
|
+
: []
|
|
271
|
+
} else {
|
|
272
|
+
// Add to selection
|
|
273
|
+
selectedValues = Array.isArray(selectedValues) ? [...selectedValues, value] : [value]
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
// Single selection
|
|
277
|
+
selectedValues = value
|
|
278
|
+
closeDropdown()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Update form field if available
|
|
282
|
+
if (fieldApi) {
|
|
283
|
+
fieldApi.setValue(selectedValues)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
onchange?.(new CustomEvent("change", { detail: { value: selectedValues } }))
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Clears the selection
|
|
291
|
+
*/
|
|
292
|
+
function clearSelection(evt) {
|
|
293
|
+
evt.stopPropagation()
|
|
294
|
+
selectedValues = multiple ? [] : null
|
|
295
|
+
|
|
296
|
+
// Update form field if available
|
|
297
|
+
if (fieldApi) {
|
|
298
|
+
fieldApi.setValue(selectedValues)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
onchange?.(new CustomEvent("change", { detail: { value: selectedValues } }))
|
|
302
|
+
onclear?.(new CustomEvent("clear"))
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Opens the dropdown
|
|
307
|
+
*/
|
|
308
|
+
function openDropdown() {
|
|
309
|
+
if (disabled) return
|
|
310
|
+
|
|
311
|
+
isOpen = true
|
|
312
|
+
highlightedIndex = 0
|
|
313
|
+
activeSubmenu = null
|
|
314
|
+
|
|
315
|
+
// Focus the filter input if filtering is enabled
|
|
316
|
+
if (filter) {
|
|
317
|
+
setTimeout(() => {
|
|
318
|
+
inputElement?.focus()
|
|
319
|
+
}, 0)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
onopen?.(new CustomEvent("open"))
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Closes the dropdown
|
|
327
|
+
*/
|
|
328
|
+
function closeDropdown() {
|
|
329
|
+
isOpen = false
|
|
330
|
+
filterValue = ""
|
|
331
|
+
activeSubmenu = null
|
|
332
|
+
|
|
333
|
+
onclose?.(new CustomEvent("close"))
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Toggles the dropdown
|
|
338
|
+
*/
|
|
339
|
+
function toggleDropdown() {
|
|
340
|
+
if (isOpen) {
|
|
341
|
+
closeDropdown()
|
|
342
|
+
} else {
|
|
343
|
+
openDropdown()
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Opens a submenu
|
|
349
|
+
* @param {Object} option - Parent option
|
|
350
|
+
* @param {Event} event - Mouse event
|
|
351
|
+
*/
|
|
352
|
+
function openSubmenu(option, event) {
|
|
353
|
+
if (event) {
|
|
354
|
+
event.stopPropagation()
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
activeSubmenu = option
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Handles keydown events
|
|
362
|
+
* @param {KeyboardEvent} event - Keydown event
|
|
363
|
+
*/
|
|
364
|
+
function handleKeydown(event) {
|
|
365
|
+
if (disabled) return
|
|
366
|
+
|
|
367
|
+
const filteredOptions = filterOptions(options)
|
|
368
|
+
|
|
369
|
+
switch (event.key) {
|
|
370
|
+
case "ArrowDown":
|
|
371
|
+
event.preventDefault()
|
|
372
|
+
if (!isOpen) {
|
|
373
|
+
openDropdown()
|
|
374
|
+
} else {
|
|
375
|
+
highlightedIndex = (highlightedIndex + 1) % filteredOptions.length
|
|
376
|
+
scrollOptionIntoView()
|
|
377
|
+
}
|
|
378
|
+
break
|
|
379
|
+
|
|
380
|
+
case "ArrowUp":
|
|
381
|
+
event.preventDefault()
|
|
382
|
+
if (!isOpen) {
|
|
383
|
+
openDropdown()
|
|
384
|
+
} else {
|
|
385
|
+
highlightedIndex = (highlightedIndex - 1 + filteredOptions.length) % filteredOptions.length
|
|
386
|
+
scrollOptionIntoView()
|
|
387
|
+
}
|
|
388
|
+
break
|
|
389
|
+
|
|
390
|
+
case "Enter":
|
|
391
|
+
event.preventDefault()
|
|
392
|
+
if (isOpen) {
|
|
393
|
+
if (filteredOptions[highlightedIndex]) {
|
|
394
|
+
selectOption(filteredOptions[highlightedIndex])
|
|
395
|
+
}
|
|
396
|
+
} else {
|
|
397
|
+
openDropdown()
|
|
398
|
+
}
|
|
399
|
+
break
|
|
400
|
+
|
|
401
|
+
case "Escape":
|
|
402
|
+
event.preventDefault()
|
|
403
|
+
closeDropdown()
|
|
404
|
+
break
|
|
405
|
+
|
|
406
|
+
case "Tab":
|
|
407
|
+
if (isOpen) {
|
|
408
|
+
closeDropdown()
|
|
409
|
+
}
|
|
410
|
+
break
|
|
411
|
+
|
|
412
|
+
case " ":
|
|
413
|
+
if (!filter) {
|
|
414
|
+
event.preventDefault()
|
|
415
|
+
if (!isOpen) {
|
|
416
|
+
openDropdown()
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
break
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Scrolls the highlighted option into view
|
|
425
|
+
*/
|
|
426
|
+
function scrollOptionIntoView() {
|
|
427
|
+
if (!menuElement) return
|
|
428
|
+
|
|
429
|
+
const highlightedOption = menuElement.querySelector(`[data-index="${highlightedIndex}"]`)
|
|
430
|
+
if (highlightedOption) {
|
|
431
|
+
highlightedOption.scrollIntoView({ block: "nearest" })
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Handles filter input
|
|
437
|
+
* @param {Event} event - Input event
|
|
438
|
+
*/
|
|
439
|
+
function handleFilterInput(event) {
|
|
440
|
+
filterValue = event.target.value
|
|
441
|
+
highlightedIndex = 0
|
|
442
|
+
|
|
443
|
+
onfilter?.(new CustomEvent("filter", { detail: { filter: filterValue } }))
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Determine size classes
|
|
447
|
+
const sizeClasses = $derived(
|
|
448
|
+
{
|
|
449
|
+
sm: "h-8 text-sm",
|
|
450
|
+
md: "h-10 text-base",
|
|
451
|
+
lg: "h-12 text-lg",
|
|
452
|
+
}[size] || "h-10 text-base"
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
// Computed display value
|
|
456
|
+
const displayValue = $derived(getDisplayValue())
|
|
457
|
+
</script>
|
|
458
|
+
|
|
459
|
+
<div
|
|
460
|
+
class="
|
|
461
|
+
dropdown
|
|
462
|
+
{isOpen ? 'dropdown-open' : ''}
|
|
463
|
+
{disabled ? 'dropdown-disabled' : ''}
|
|
464
|
+
{className}
|
|
465
|
+
"
|
|
466
|
+
bind:this={dropdownElement}
|
|
467
|
+
use:clickOutside={() => isOpen && closeDropdown()}
|
|
468
|
+
>
|
|
469
|
+
<div
|
|
470
|
+
class="dropdown-control {sizeClasses}"
|
|
471
|
+
onclick={toggleDropdown}
|
|
472
|
+
onkeydown={handleKeydown}
|
|
473
|
+
tabindex={disabled ? undefined : 0}
|
|
474
|
+
role="combobox"
|
|
475
|
+
aria-expanded={isOpen}
|
|
476
|
+
aria-haspopup="listbox"
|
|
477
|
+
aria-controls={`${id}-menu`}
|
|
478
|
+
aria-label={ariaLabel || name}
|
|
479
|
+
aria-disabled={disabled ? 'true' : undefined}
|
|
480
|
+
>
|
|
481
|
+
{#if filter && isOpen}
|
|
482
|
+
<input
|
|
483
|
+
type="text"
|
|
484
|
+
class="dropdown-filter"
|
|
485
|
+
placeholder={placeholder}
|
|
486
|
+
value={filterValue}
|
|
487
|
+
oninput={handleFilterInput}
|
|
488
|
+
onkeydown={handleKeydown}
|
|
489
|
+
bind:this={inputElement}
|
|
490
|
+
{disabled}
|
|
491
|
+
/>
|
|
492
|
+
{:else}
|
|
493
|
+
<div class="dropdown-value">
|
|
494
|
+
{#if displayValue}
|
|
495
|
+
{displayValue}
|
|
496
|
+
{:else}
|
|
497
|
+
<span class="dropdown-placeholder">{placeholder}</span>
|
|
498
|
+
{/if}
|
|
499
|
+
</div>
|
|
500
|
+
{/if}
|
|
501
|
+
|
|
502
|
+
<div class="dropdown-indicators">
|
|
503
|
+
{#if clearable && (selectedValues !== null && (!Array.isArray(selectedValues) || selectedValues.length > 0))}
|
|
504
|
+
<button
|
|
505
|
+
type="button"
|
|
506
|
+
class="dropdown-clear-button"
|
|
507
|
+
aria-label="Clear selection"
|
|
508
|
+
onclick={clearSelection}
|
|
509
|
+
tabindex="-1"
|
|
510
|
+
>
|
|
511
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
512
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
513
|
+
</svg>
|
|
514
|
+
</button>
|
|
515
|
+
{/if}
|
|
516
|
+
|
|
517
|
+
<div class="dropdown-arrow">
|
|
518
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
519
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
|
520
|
+
</svg>
|
|
521
|
+
</div>
|
|
522
|
+
</div>
|
|
523
|
+
</div>
|
|
524
|
+
|
|
525
|
+
{#if isOpen}
|
|
526
|
+
<div
|
|
527
|
+
id="{id}-menu"
|
|
528
|
+
class="dropdown-menu"
|
|
529
|
+
role="listbox"
|
|
530
|
+
aria-multiselectable={multiple ? 'true' : undefined}
|
|
531
|
+
bind:this={menuElement}
|
|
532
|
+
transition:slide={{ duration: 200 }}
|
|
533
|
+
>
|
|
534
|
+
<ul class="dropdown-options">
|
|
535
|
+
{#each filterOptions(options) as option, index}
|
|
536
|
+
{@const hasChildren = getOptionChildren(option) && getOptionChildren(option).length > 0}
|
|
537
|
+
{@const isHighlighted = index === highlightedIndex}
|
|
538
|
+
{@const isSelected = isOptionSelected(option)}
|
|
539
|
+
|
|
540
|
+
<li
|
|
541
|
+
class="
|
|
542
|
+
dropdown-option
|
|
543
|
+
{isHighlighted ? 'dropdown-option-highlighted' : ''}
|
|
544
|
+
{isSelected ? 'dropdown-option-selected' : ''}
|
|
545
|
+
{hasChildren ? 'dropdown-option-parent' : ''}
|
|
546
|
+
"
|
|
547
|
+
role="option"
|
|
548
|
+
aria-selected={isSelected ? 'true' : 'false'}
|
|
549
|
+
data-index={index}
|
|
550
|
+
onclick={() => selectOption(option)}
|
|
551
|
+
onmouseenter={() => {
|
|
552
|
+
highlightedIndex = index;
|
|
553
|
+
if (hasChildren) {
|
|
554
|
+
openSubmenu(option);
|
|
555
|
+
} else {
|
|
556
|
+
activeSubmenu = null;
|
|
557
|
+
}
|
|
558
|
+
}}
|
|
559
|
+
>
|
|
560
|
+
<div class="dropdown-option-content">
|
|
561
|
+
{#if multiple}
|
|
562
|
+
<div class="dropdown-checkbox">
|
|
563
|
+
{#if isSelected}
|
|
564
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
565
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
566
|
+
</svg>
|
|
567
|
+
{/if}
|
|
568
|
+
</div>
|
|
569
|
+
{/if}
|
|
570
|
+
|
|
571
|
+
{#if optionIcon && getOptionIcon(option)}
|
|
572
|
+
<div class="dropdown-option-icon">
|
|
573
|
+
{@html getOptionIcon(option)}
|
|
574
|
+
</div>
|
|
575
|
+
{/if}
|
|
576
|
+
|
|
577
|
+
<div class="dropdown-option-label">
|
|
578
|
+
{getOptionLabel(option)}
|
|
579
|
+
</div>
|
|
580
|
+
|
|
581
|
+
{#if hasChildren}
|
|
582
|
+
<div class="dropdown-submenu-arrow">
|
|
583
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
584
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
585
|
+
</svg>
|
|
586
|
+
</div>
|
|
587
|
+
{/if}
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
{#if hasChildren && activeSubmenu === option}
|
|
591
|
+
<div class="dropdown-submenu">
|
|
592
|
+
<ul class="dropdown-options">
|
|
593
|
+
{#each getOptionChildren(option) as childOption, childIndex}
|
|
594
|
+
{@const isChildSelected = isOptionSelected(childOption)}
|
|
595
|
+
|
|
596
|
+
<li
|
|
597
|
+
class="
|
|
598
|
+
dropdown-option
|
|
599
|
+
{isChildSelected ? 'dropdown-option-selected' : ''}
|
|
600
|
+
"
|
|
601
|
+
role="option"
|
|
602
|
+
aria-selected={isChildSelected ? 'true' : 'false'}
|
|
603
|
+
onclick={() => selectOption(childOption)}
|
|
604
|
+
>
|
|
605
|
+
<div class="dropdown-option-content">
|
|
606
|
+
{#if multiple}
|
|
607
|
+
<div class="dropdown-checkbox">
|
|
608
|
+
{#if isChildSelected}
|
|
609
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
610
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
611
|
+
</svg>
|
|
612
|
+
{/if}
|
|
613
|
+
</div>
|
|
614
|
+
{/if}
|
|
615
|
+
|
|
616
|
+
{#if optionIcon && getOptionIcon(childOption)}
|
|
617
|
+
<div class="dropdown-option-icon">
|
|
618
|
+
{@html getOptionIcon(childOption)}
|
|
619
|
+
</div>
|
|
620
|
+
{/if}
|
|
621
|
+
|
|
622
|
+
<div class="dropdown-option-label">
|
|
623
|
+
{getOptionLabel(childOption)}
|
|
624
|
+
</div>
|
|
625
|
+
</div>
|
|
626
|
+
</li>
|
|
627
|
+
{/each}
|
|
628
|
+
</ul>
|
|
629
|
+
</div>
|
|
630
|
+
{/if}
|
|
631
|
+
</li>
|
|
632
|
+
{:else}
|
|
633
|
+
<li class="dropdown-empty">No options available</li>
|
|
634
|
+
{/each}
|
|
635
|
+
</ul>
|
|
636
|
+
</div>
|
|
637
|
+
{/if}
|
|
638
|
+
|
|
639
|
+
<!-- Hidden input for form submission -->
|
|
640
|
+
<input
|
|
641
|
+
type="hidden"
|
|
642
|
+
{id}
|
|
643
|
+
{name}
|
|
644
|
+
value={JSON.stringify(selectedValues)}
|
|
645
|
+
{required}
|
|
646
|
+
{disabled}
|
|
647
|
+
/>
|
|
648
|
+
</div>
|
|
649
|
+
|
|
650
|
+
<style>
|
|
651
|
+
@reference "../../twintrinsic.css";
|
|
652
|
+
|
|
653
|
+
.dropdown {
|
|
654
|
+
@apply relative w-full;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.dropdown-control {
|
|
658
|
+
@apply flex items-center justify-between;
|
|
659
|
+
@apply bg-background dark:bg-background;
|
|
660
|
+
@apply border border-border dark:border-border rounded-md;
|
|
661
|
+
@apply focus:outline-none focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400 focus:border-primary-500 dark:focus:border-primary-400;
|
|
662
|
+
@apply transition-colors duration-200;
|
|
663
|
+
@apply px-3 cursor-pointer;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.dropdown-disabled .dropdown-control {
|
|
667
|
+
@apply opacity-50 cursor-not-allowed;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.dropdown-value {
|
|
671
|
+
@apply flex-grow truncate text-text dark:text-text;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.dropdown-placeholder {
|
|
675
|
+
@apply text-muted dark:text-muted;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.dropdown-indicators {
|
|
679
|
+
@apply flex items-center ml-2;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
.dropdown-arrow {
|
|
683
|
+
@apply flex-shrink-0 text-muted dark:text-muted transition-transform duration-200;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.dropdown-open .dropdown-arrow {
|
|
687
|
+
@apply rotate-180;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
.dropdown-clear-button {
|
|
691
|
+
@apply p-1 mr-1 rounded-full text-muted dark:text-muted;
|
|
692
|
+
@apply hover:bg-hover dark:hover:bg-hover hover:text-text dark:hover:text-text;
|
|
693
|
+
@apply focus:outline-none focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.dropdown-filter {
|
|
697
|
+
@apply w-full bg-transparent border-none outline-none;
|
|
698
|
+
@apply text-text dark:text-text placeholder:text-muted dark:placeholder:text-muted;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
.dropdown-menu {
|
|
702
|
+
@apply absolute z-50 w-full mt-1;
|
|
703
|
+
@apply bg-background dark:bg-background;
|
|
704
|
+
@apply border border-border dark:border-border rounded-md;
|
|
705
|
+
@apply shadow-lg;
|
|
706
|
+
@apply max-h-60 overflow-y-auto;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.dropdown-options {
|
|
710
|
+
@apply py-1;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.dropdown-option {
|
|
714
|
+
@apply relative px-3 py-2 cursor-pointer;
|
|
715
|
+
@apply text-text dark:text-text;
|
|
716
|
+
@apply hover:bg-hover dark:hover:bg-hover;
|
|
717
|
+
@apply transition-colors duration-150;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
.dropdown-option-content {
|
|
721
|
+
@apply flex items-center gap-2;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
.dropdown-option-highlighted {
|
|
725
|
+
@apply bg-hover dark:bg-hover;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
.dropdown-option-selected {
|
|
729
|
+
@apply bg-primary-50 dark:bg-primary-900/20;
|
|
730
|
+
@apply text-primary-700 dark:text-primary-300;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.dropdown-option-parent {
|
|
734
|
+
@apply pr-8;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.dropdown-checkbox {
|
|
738
|
+
@apply w-4 h-4 flex-shrink-0;
|
|
739
|
+
@apply border border-border dark:border-border rounded;
|
|
740
|
+
@apply flex items-center justify-center;
|
|
741
|
+
@apply bg-background dark:bg-background;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
.dropdown-option-selected .dropdown-checkbox {
|
|
745
|
+
@apply bg-primary-500 dark:bg-primary-400 border-primary-500 dark:border-primary-400;
|
|
746
|
+
@apply text-white dark:text-white;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.dropdown-option-icon {
|
|
750
|
+
@apply flex-shrink-0 w-5 h-5;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
.dropdown-option-label {
|
|
754
|
+
@apply flex-grow truncate;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.dropdown-submenu-arrow {
|
|
758
|
+
@apply absolute right-3 top-1/2 -translate-y-1/2;
|
|
759
|
+
@apply text-muted dark:text-muted;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
.dropdown-submenu {
|
|
763
|
+
@apply absolute left-full top-0 ml-1;
|
|
764
|
+
@apply bg-background dark:bg-background;
|
|
765
|
+
@apply border border-border dark:border-border rounded-md;
|
|
766
|
+
@apply shadow-lg;
|
|
767
|
+
@apply min-w-[200px] max-h-60 overflow-y-auto;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.dropdown-empty {
|
|
771
|
+
@apply px-3 py-2 text-muted dark:text-muted text-center;
|
|
772
|
+
}
|
|
773
|
+
</style>
|