|
3 | 3 |
|
4 | 4 | import { |
5 | 5 | Button, |
| 6 | + Dialog, |
6 | 7 | Drawer, |
7 | 8 | Form, |
8 | 9 | Icon, |
9 | 10 | MultiSelect, |
10 | 11 | MultiSelectOption, |
| 12 | + TextField, |
| 13 | + Toggle, |
11 | 14 | ToggleButton, |
12 | 15 | ToggleGroup, |
13 | 16 | ToggleOption, |
| 17 | + type MenuOption, |
14 | 18 | } from 'svelte-ux'; |
15 | 19 | import Preview from '$lib/components/Preview.svelte'; |
16 | 20 |
|
17 | | - const options = [ |
| 21 | + let options: MenuOption[] = [ |
18 | 22 | { label: 'One', value: 1 }, |
19 | 23 | { label: 'Two', value: 2 }, |
20 | 24 | { label: 'Three', value: 3 }, |
21 | 25 | { label: 'Four', value: 4 }, |
22 | 26 | ]; |
23 | 27 |
|
| 28 | + const newOption: () => MenuOption = () => { |
| 29 | + return { label: '', value: null }; |
| 30 | + }; |
| 31 | +
|
24 | 32 | const manyOptions = Array.from({ length: 100 }).map((_, i) => ({ |
25 | 33 | label: `${i + 1}`, |
26 | 34 | value: i + 1, |
|
168 | 176 | </div> |
169 | 177 | </Preview> |
170 | 178 |
|
171 | | -<h2>actions slot</h2> |
172 | | - |
173 | | -<Preview> |
174 | | - {value.length} selected |
175 | | - <div class="flex flex-col max-h-[360px] overflow-auto"> |
176 | | - <MultiSelect {options} {value} on:change={(e) => (value = e.detail.value)} search> |
177 | | - <div slot="actions"> |
178 | | - <Button color="primary" icon={mdiPlus}>Add item</Button> |
179 | | - </div> |
180 | | - </MultiSelect> |
181 | | - </div> |
182 | | -</Preview> |
183 | | - |
184 | | -<h2>actions slot with max warning</h2> |
185 | | - |
186 | | -<Preview> |
187 | | - {value.length} selected |
188 | | - <div class="flex flex-col max-h-[360px] overflow-auto"> |
189 | | - <MultiSelect {options} {value} on:change={(e) => (value = e.detail.value)} search max={2}> |
190 | | - <div slot="actions" let:selection class="flex items-center"> |
191 | | - {#if selection.isMaxSelected()} |
192 | | - <div class="text-sm text-danger">Maximum selection reached</div> |
193 | | - {/if} |
194 | | - </div> |
195 | | - </MultiSelect> |
196 | | - </div> |
197 | | -</Preview> |
198 | | - |
199 | 179 | <h2>beforeOptions slot</h2> |
200 | 180 |
|
201 | 181 | <Preview> |
|
254 | 234 | </div> |
255 | 235 | </Preview> |
256 | 236 |
|
| 237 | +<h2>actions slot</h2> |
| 238 | + |
| 239 | +<Preview> |
| 240 | + {value.length} selected |
| 241 | + <div class="flex flex-col max-h-[360px] overflow-auto"> |
| 242 | + <MultiSelect {options} {value} on:change={(e) => (value = e.detail.value)} search> |
| 243 | + <div slot="actions" class="p-2" on:click|stopPropagation role="none"> |
| 244 | + <Toggle let:on={open} let:toggle> |
| 245 | + <Button icon={mdiPlus} color="primary" on:click={toggle}>New item</Button> |
| 246 | + <Form |
| 247 | + initial={newOption()} |
| 248 | + on:change={(e) => { |
| 249 | + // Convert value to number if it's a valid number, otherwise keep as string |
| 250 | + const newOptionData = { ...e.detail }; |
| 251 | + if ( |
| 252 | + newOptionData.value !== null && |
| 253 | + newOptionData.value !== '' && |
| 254 | + !isNaN(Number(newOptionData.value)) |
| 255 | + ) { |
| 256 | + newOptionData.value = Number(newOptionData.value); |
| 257 | + } |
| 258 | + options = [newOptionData, ...options]; |
| 259 | + // Auto-select the newly created option |
| 260 | + value = [...(value || []), newOptionData.value]; |
| 261 | + }} |
| 262 | + let:draft |
| 263 | + let:current |
| 264 | + let:commit |
| 265 | + let:revert |
| 266 | + > |
| 267 | + <Dialog |
| 268 | + {open} |
| 269 | + on:close={() => { |
| 270 | + toggle(); |
| 271 | + }} |
| 272 | + > |
| 273 | + <div slot="title">Create new option</div> |
| 274 | + <div class="px-6 py-3 w-96 grid gap-2"> |
| 275 | + <TextField |
| 276 | + label="Label" |
| 277 | + value={current.label} |
| 278 | + on:change={(e) => { |
| 279 | + draft.label = e.detail.value; |
| 280 | + }} |
| 281 | + autofocus |
| 282 | + /> |
| 283 | + <TextField |
| 284 | + label="Value" |
| 285 | + value={draft.value} |
| 286 | + on:change={(e) => { |
| 287 | + draft.value = e.detail.value; |
| 288 | + }} |
| 289 | + /> |
| 290 | + </div> |
| 291 | + <div slot="actions"> |
| 292 | + <Button |
| 293 | + on:click={() => { |
| 294 | + commit(); |
| 295 | + toggle(); |
| 296 | + }} |
| 297 | + color="primary">Add option</Button |
| 298 | + > |
| 299 | + <Button |
| 300 | + on:click={() => { |
| 301 | + revert(); |
| 302 | + toggle(); |
| 303 | + }}>Cancel</Button |
| 304 | + > |
| 305 | + </div> |
| 306 | + </Dialog> |
| 307 | + </Form> |
| 308 | + </Toggle> |
| 309 | + </div> |
| 310 | + </MultiSelect> |
| 311 | + </div> |
| 312 | +</Preview> |
| 313 | + |
| 314 | +<h2>actions slot with max warning</h2> |
| 315 | + |
| 316 | +<Preview> |
| 317 | + {value.length} selected |
| 318 | + <div class="flex flex-col max-h-[360px] overflow-auto"> |
| 319 | + <MultiSelect {options} {value} on:change={(e) => (value = e.detail.value)} search max={2}> |
| 320 | + <div slot="actions" let:selection class="flex items-center"> |
| 321 | + {#if selection.isMaxSelected()} |
| 322 | + <div class="text-sm text-danger">Maximum selection reached</div> |
| 323 | + {/if} |
| 324 | + </div> |
| 325 | + </MultiSelect> |
| 326 | + </div> |
| 327 | +</Preview> |
| 328 | + |
257 | 329 | <h2>option slot with MultiSelectOption custom actions</h2> |
258 | 330 |
|
259 | 331 | <Preview> |
|
0 commit comments