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