@insymetri/styleguide 0.1.13 → 0.1.14

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 (39) hide show
  1. package/dist/IICalendar/IICalendar.svelte +101 -0
  2. package/dist/IICalendar/IICalendar.svelte.d.ts +13 -0
  3. package/dist/IICalendar/IICalendarStories.svelte +60 -0
  4. package/dist/IICalendar/IICalendarStories.svelte.d.ts +3 -0
  5. package/dist/IICalendar/index.d.ts +1 -0
  6. package/dist/IICalendar/index.js +1 -0
  7. package/dist/IICombobox/IICombobox.svelte +75 -29
  8. package/dist/IICombobox/IICombobox.svelte.d.ts +6 -0
  9. package/dist/IICombobox/IIComboboxStories.svelte +97 -0
  10. package/dist/IICombobox/IIComboboxStories.svelte.d.ts +3 -0
  11. package/dist/IIDropdownInput/IIDropdownInput.svelte +30 -6
  12. package/dist/IIDropdownInput/IIDropdownInput.svelte.d.ts +5 -0
  13. package/dist/IIDropdownInput/IIDropdownInputStories.svelte +88 -0
  14. package/dist/IIDropdownInput/IIDropdownInputStories.svelte.d.ts +3 -0
  15. package/dist/IIDropdownMenu/IIDropdownMenu.svelte +90 -23
  16. package/dist/IIDropdownMenu/IIDropdownMenu.svelte.d.ts +14 -2
  17. package/dist/IIDropdownMenu/IIDropdownMenuStories.svelte +101 -0
  18. package/dist/IIDropdownMenu/IIDropdownMenuStories.svelte.d.ts +18 -0
  19. package/dist/IIMultiSelect/IIMultiSelect.svelte +141 -0
  20. package/dist/IIMultiSelect/IIMultiSelect.svelte.d.ts +20 -0
  21. package/dist/IIMultiSelect/IIMultiSelectStories.svelte +78 -0
  22. package/dist/IIMultiSelect/IIMultiSelectStories.svelte.d.ts +3 -0
  23. package/dist/IIMultiSelect/index.d.ts +1 -0
  24. package/dist/IIMultiSelect/index.js +1 -0
  25. package/dist/IIPopover/IIPopover.svelte +48 -0
  26. package/dist/IIPopover/IIPopover.svelte.d.ts +15 -0
  27. package/dist/IIPopover/IIPopoverStories.svelte +108 -0
  28. package/dist/IIPopover/IIPopoverStories.svelte.d.ts +3 -0
  29. package/dist/IIPopover/index.d.ts +1 -0
  30. package/dist/IIPopover/index.js +1 -0
  31. package/dist/IIToggle/IIToggle.svelte +52 -0
  32. package/dist/IIToggle/IIToggle.svelte.d.ts +15 -0
  33. package/dist/IIToggle/IIToggleStories.svelte +89 -0
  34. package/dist/IIToggle/IIToggleStories.svelte.d.ts +3 -0
  35. package/dist/IIToggle/index.d.ts +1 -0
  36. package/dist/IIToggle/index.js +1 -0
  37. package/dist/index.d.ts +4 -0
  38. package/dist/index.js +4 -0
  39. package/package.json +1 -1
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ import IIDropdownInput from './IIDropdownInput.svelte'
3
+ import {IIIcon} from '../IIIcon'
4
+ import type {IconName} from '../icons'
5
+
6
+ let basicValue = $state<string | undefined>(undefined)
7
+ let disabledItemValue = $state<string | undefined>(undefined)
8
+
9
+ const statusItems = [
10
+ {label: 'Active', value: 'active'},
11
+ {label: 'Pending', value: 'pending'},
12
+ {label: 'Closed', value: 'closed'},
13
+ {label: 'Delinquent', value: 'delinquent'},
14
+ ]
15
+
16
+ const disabledItems = [
17
+ {label: 'Active', value: 'active'},
18
+ {label: 'Pending', value: 'pending', disabled: true},
19
+ {label: 'Closed', value: 'closed'},
20
+ {label: 'Archived', value: 'archived', disabled: true},
21
+ ]
22
+
23
+ const iconMap: Record<string, IconName> = {
24
+ active: 'check-circle',
25
+ pending: 'clock-countdown',
26
+ closed: 'warning-circle',
27
+ }
28
+
29
+ const colorMap: Record<string, string> = {
30
+ active: 'text-success',
31
+ pending: 'text-warning',
32
+ closed: 'text-error',
33
+ }
34
+ </script>
35
+
36
+ <div class="flex flex-col gap-32">
37
+ <!-- With Custom Render Item (icons) -->
38
+ <section>
39
+ <h2 class="text-default-emphasis text-primary mb-8">Custom Item Rendering with Icons</h2>
40
+ <p class="text-small text-secondary mb-12">Use renderItem to add icons and custom layout to dropdown items.</p>
41
+ <div class="w-200">
42
+ <IIDropdownInput
43
+ items={[
44
+ {label: 'Active', value: 'active'},
45
+ {label: 'Pending', value: 'pending'},
46
+ {label: 'Closed', value: 'closed'},
47
+ ]}
48
+ bind:value={basicValue}
49
+ placeholder="Select status..."
50
+ >
51
+ {#snippet renderItem(item, selected)}
52
+ <span class="flex items-center gap-8 flex-1">
53
+ <IIIcon iconName={iconMap[item.value]} class={colorMap[item.value]} />
54
+ <span class="flex-1">{item.label}</span>
55
+ {#if selected}
56
+ <IIIcon iconName="check-circle" class="w-14 h-14 text-accent shrink-0" />
57
+ {/if}
58
+ </span>
59
+ {/snippet}
60
+ </IIDropdownInput>
61
+ </div>
62
+ </section>
63
+
64
+ <!-- Disabled Items -->
65
+ <section>
66
+ <h2 class="text-default-emphasis text-primary mb-8">Per-Item Disabled</h2>
67
+ <p class="text-small text-secondary mb-12">Individual items can be disabled.</p>
68
+ <div class="w-200">
69
+ <IIDropdownInput items={disabledItems} bind:value={disabledItemValue} placeholder="Select status..." />
70
+ </div>
71
+ </section>
72
+
73
+ <!-- Custom Render Selected -->
74
+ <section>
75
+ <h2 class="text-default-emphasis text-primary mb-8">Custom Selected Display</h2>
76
+ <p class="text-small text-secondary mb-12">Use renderSelected to customize how the selected item appears in the trigger.</p>
77
+ <div class="w-200">
78
+ <IIDropdownInput items={statusItems} bind:value={basicValue} placeholder="Select status...">
79
+ {#snippet renderSelected(item)}
80
+ <span class="flex items-center gap-6">
81
+ <span class="w-8 h-8 rounded-full bg-success"></span>
82
+ <span class="text-small-emphasis">{item.label}</span>
83
+ </span>
84
+ {/snippet}
85
+ </IIDropdownInput>
86
+ </div>
87
+ </section>
88
+ </div>
@@ -0,0 +1,3 @@
1
+ declare const IIDropdownInputStories: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type IIDropdownInputStories = ReturnType<typeof IIDropdownInputStories>;
3
+ export default IIDropdownInputStories;
@@ -12,28 +12,96 @@
12
12
  variant?: 'default' | 'destructive'
13
13
  }
14
14
 
15
- type Props = {
15
+ type SeparatorEntry = {
16
+ type: 'separator'
17
+ }
18
+
19
+ type GroupEntry = {
20
+ type: 'group'
21
+ heading?: string
16
22
  items: Item[]
23
+ }
24
+
25
+ type MenuEntry = Item | SeparatorEntry | GroupEntry
26
+
27
+ type Props = {
28
+ items: MenuEntry[]
17
29
  onSelect: (value: string) => void
30
+ open?: boolean
18
31
  children?: Snippet
32
+ renderItem?: Snippet<[Item]>
19
33
  side?: 'top' | 'right' | 'bottom' | 'left'
20
34
  align?: 'start' | 'center' | 'end'
35
+ triggerClass?: string
21
36
  class?: string
22
37
  }
23
38
 
24
- let {items, onSelect, children, side = 'bottom', align = 'end', class: className}: Props = $props()
25
-
26
- let open = $state(false)
39
+ let {
40
+ items,
41
+ onSelect,
42
+ open = $bindable(false),
43
+ children,
44
+ renderItem,
45
+ side = 'bottom',
46
+ align = 'end',
47
+ triggerClass,
48
+ class: className,
49
+ }: Props = $props()
27
50
 
28
51
  function handleSelect(value: string) {
29
52
  onSelect(value)
30
53
  open = false
31
54
  }
55
+
56
+ function isSeparator(entry: MenuEntry): entry is SeparatorEntry {
57
+ return 'type' in entry && entry.type === 'separator'
58
+ }
59
+
60
+ function isGroup(entry: MenuEntry): entry is GroupEntry {
61
+ return 'type' in entry && entry.type === 'group'
62
+ }
63
+
64
+ function isItem(entry: MenuEntry): entry is Item {
65
+ return !('type' in entry)
66
+ }
32
67
  </script>
33
68
 
69
+ {#snippet itemContent(item: Item)}
70
+ {#if renderItem}
71
+ {@render renderItem(item)}
72
+ {:else}
73
+ {#if item.icon}
74
+ <div class="w-16 h-16 flex items-center justify-center shrink-0 [&_svg]:w-16 [&_svg]:h-16">
75
+ {@render item.icon()}
76
+ </div>
77
+ {/if}
78
+ <span class="flex-1">{item.label}</span>
79
+ {/if}
80
+ {/snippet}
81
+
82
+ {#snippet menuItem(item: Item)}
83
+ <DropdownMenu.Item
84
+ disabled={item.disabled}
85
+ class={cn(
86
+ 'flex items-center gap-8 px-12 py-8 rounded-4 text-small cursor-default select-none outline-none data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none motion-reduce:transition-none',
87
+ item.variant === 'destructive'
88
+ ? 'text-error hover:bg-error-bg data-[highlighted]:bg-error-bg data-[highlighted]:outline-none'
89
+ : 'text-dropdown-item hover:bg-dropdown-item-hover data-[highlighted]:bg-dropdown-item-hover data-[highlighted]:outline-none'
90
+ )}
91
+ onSelect={() => handleSelect(item.value)}
92
+ >
93
+ {@render itemContent(item)}
94
+ </DropdownMenu.Item>
95
+ {/snippet}
96
+
34
97
  <DropdownMenu.Root bind:open>
35
98
  <DropdownMenu.Trigger
36
- class="[all:unset] cursor-default inline-flex items-center justify-center p-4 rounded-4 text-secondary transition-all duration-fast hover:bg-background hover:text-body data-[state=open]:bg-background data-[state=open]:text-body motion-reduce:transition-none"
99
+ class={cn(
100
+ children && triggerClass
101
+ ? triggerClass
102
+ : '[all:unset] cursor-default inline-flex items-center justify-center p-4 rounded-4 text-secondary transition-all duration-fast hover:bg-background hover:text-body data-[state=open]:bg-background data-[state=open]:text-body motion-reduce:transition-none',
103
+ !children && !triggerClass && ''
104
+ )}
37
105
  >
38
106
  {#if children}
39
107
  {@render children()}
@@ -50,24 +118,23 @@
50
118
  className
51
119
  )}
52
120
  >
53
- {#each items as item (item.value)}
54
- <DropdownMenu.Item
55
- disabled={item.disabled}
56
- class={cn(
57
- 'flex items-center gap-8 px-12 py-8 rounded-4 text-small cursor-default select-none outline-none data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none motion-reduce:transition-none',
58
- item.variant === 'destructive'
59
- ? 'text-error hover:bg-error-bg data-[highlighted]:bg-error-bg data-[highlighted]:outline-none'
60
- : 'text-dropdown-item hover:bg-dropdown-item-hover data-[highlighted]:bg-dropdown-item-hover data-[highlighted]:outline-none'
61
- )}
62
- onSelect={() => handleSelect(item.value)}
63
- >
64
- {#if item.icon}
65
- <div class="w-16 h-16 flex items-center justify-center shrink-0 [&_svg]:w-16 [&_svg]:h-16">
66
- {@render item.icon()}
67
- </div>
68
- {/if}
69
- <span class="flex-1">{item.label}</span>
70
- </DropdownMenu.Item>
121
+ {#each items as entry, i (i)}
122
+ {#if isSeparator(entry)}
123
+ <DropdownMenu.Separator class="h-1 bg-muted mx-4 my-4" />
124
+ {:else if isGroup(entry)}
125
+ <DropdownMenu.Group>
126
+ {#if entry.heading}
127
+ <DropdownMenu.GroupHeading class="text-tiny-emphasis text-secondary px-12 py-4 uppercase select-none">
128
+ {entry.heading}
129
+ </DropdownMenu.GroupHeading>
130
+ {/if}
131
+ {#each entry.items as item (item.value)}
132
+ {@render menuItem(item)}
133
+ {/each}
134
+ </DropdownMenu.Group>
135
+ {:else if isItem(entry)}
136
+ {@render menuItem(entry)}
137
+ {/if}
71
138
  {/each}
72
139
  </DropdownMenu.Content>
73
140
  </DropdownMenu.Portal>
@@ -6,14 +6,26 @@ type Item = {
6
6
  disabled?: boolean;
7
7
  variant?: 'default' | 'destructive';
8
8
  };
9
- type Props = {
9
+ type SeparatorEntry = {
10
+ type: 'separator';
11
+ };
12
+ type GroupEntry = {
13
+ type: 'group';
14
+ heading?: string;
10
15
  items: Item[];
16
+ };
17
+ type MenuEntry = Item | SeparatorEntry | GroupEntry;
18
+ type Props = {
19
+ items: MenuEntry[];
11
20
  onSelect: (value: string) => void;
21
+ open?: boolean;
12
22
  children?: Snippet;
23
+ renderItem?: Snippet<[Item]>;
13
24
  side?: 'top' | 'right' | 'bottom' | 'left';
14
25
  align?: 'start' | 'center' | 'end';
26
+ triggerClass?: string;
15
27
  class?: string;
16
28
  };
17
- declare const IIDropdownMenu: import("svelte").Component<Props, {}, "">;
29
+ declare const IIDropdownMenu: import("svelte").Component<Props, {}, "open">;
18
30
  type IIDropdownMenu = ReturnType<typeof IIDropdownMenu>;
19
31
  export default IIDropdownMenu;
@@ -0,0 +1,101 @@
1
+ <script lang="ts">
2
+ import IIDropdownMenu from './IIDropdownMenu.svelte'
3
+ import {IIButton} from '../IIButton'
4
+ import {IIIcon} from '../IIIcon'
5
+
6
+ function handleSelect(value: string) {
7
+ console.log('Selected:', value)
8
+ }
9
+ </script>
10
+
11
+ <div class="flex flex-col gap-32">
12
+ <!-- Separators -->
13
+ <section>
14
+ <h2 class="text-default-emphasis text-primary mb-8">With Separators</h2>
15
+ <p class="text-small text-secondary mb-12">Menu entries can include separator dividers.</p>
16
+ <IIDropdownMenu
17
+ items={[
18
+ {label: 'Edit', value: 'edit'},
19
+ {label: 'Duplicate', value: 'duplicate'},
20
+ {type: 'separator'},
21
+ {label: 'Archive', value: 'archive'},
22
+ {label: 'Delete', value: 'delete', variant: 'destructive'},
23
+ ]}
24
+ onSelect={handleSelect}
25
+ />
26
+ </section>
27
+
28
+ <!-- Groups -->
29
+ <section>
30
+ <h2 class="text-default-emphasis text-primary mb-8">With Groups</h2>
31
+ <p class="text-small text-secondary mb-12">Items organized into labeled groups with headings.</p>
32
+ <IIDropdownMenu
33
+ items={[
34
+ {
35
+ type: 'group',
36
+ heading: 'Actions',
37
+ items: [
38
+ {label: 'Edit', value: 'edit'},
39
+ {label: 'Duplicate', value: 'duplicate'},
40
+ ],
41
+ },
42
+ {type: 'separator'},
43
+ {
44
+ type: 'group',
45
+ heading: 'Danger Zone',
46
+ items: [
47
+ {label: 'Archive', value: 'archive'},
48
+ {label: 'Delete', value: 'delete', variant: 'destructive'},
49
+ ],
50
+ },
51
+ ]}
52
+ onSelect={handleSelect}
53
+ />
54
+ </section>
55
+
56
+ <!-- Custom Trigger with triggerClass -->
57
+ <section>
58
+ <h2 class="text-default-emphasis text-primary mb-8">Custom Trigger</h2>
59
+ <p class="text-small text-secondary mb-12">Using the children snippet with triggerClass for custom trigger styling.</p>
60
+ <IIDropdownMenu
61
+ items={[
62
+ {label: 'Profile', value: 'profile'},
63
+ {label: 'Settings', value: 'settings'},
64
+ {type: 'separator'},
65
+ {label: 'Sign Out', value: 'signout'},
66
+ ]}
67
+ onSelect={handleSelect}
68
+ triggerClass="inline-flex items-center gap-4 px-12 py-5 rounded-10 border border-button-secondary text-small text-button-secondary hover:border-button-secondary-hover cursor-default"
69
+ >
70
+ {#snippet children()}
71
+ <IIIcon iconName="user" class="w-14 h-14" />
72
+ <span>Account</span>
73
+ <IIIcon iconName="caret-down" class="w-12 h-12" />
74
+ {/snippet}
75
+ </IIDropdownMenu>
76
+ </section>
77
+
78
+ <!-- Mixed Entries -->
79
+ <section>
80
+ <h2 class="text-default-emphasis text-primary mb-8">Mixed: Items + Separators + Groups</h2>
81
+ <IIDropdownMenu
82
+ items={[
83
+ {label: 'Quick Action', value: 'quick'},
84
+ {type: 'separator'},
85
+ {
86
+ type: 'group',
87
+ heading: 'File',
88
+ items: [
89
+ {label: 'New', value: 'new'},
90
+ {label: 'Open', value: 'open'},
91
+ {label: 'Save', value: 'save'},
92
+ ],
93
+ },
94
+ {type: 'separator'},
95
+ {label: 'Exit', value: 'exit', variant: 'destructive'},
96
+ ]}
97
+ onSelect={handleSelect}
98
+ align="start"
99
+ />
100
+ </section>
101
+ </div>
@@ -0,0 +1,18 @@
1
+ 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> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const IIDropdownMenuStories: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type IIDropdownMenuStories = InstanceType<typeof IIDropdownMenuStories>;
18
+ export default IIDropdownMenuStories;
@@ -0,0 +1,141 @@
1
+ <script lang="ts">
2
+ import type {Snippet} from 'svelte'
3
+ import {DropdownMenu} from 'bits-ui'
4
+ import {IIIcon} from '../IIIcon'
5
+ import {cn} from '../utils/cn'
6
+
7
+ type Item = {
8
+ label: string
9
+ value: string
10
+ disabled?: boolean
11
+ }
12
+
13
+ type Props = {
14
+ items: Item[]
15
+ values: string[]
16
+ placeholder?: string
17
+ disabled?: boolean
18
+ onValuesChange?: (values: string[]) => void
19
+ matchTriggerWidth?: boolean
20
+ maxDisplay?: number
21
+ renderItem?: Snippet<[item: Item, selected: boolean]>
22
+ class?: string
23
+ }
24
+
25
+ let {
26
+ items,
27
+ values = $bindable([]),
28
+ placeholder = 'Select...',
29
+ disabled = false,
30
+ onValuesChange,
31
+ matchTriggerWidth = false,
32
+ maxDisplay,
33
+ renderItem,
34
+ class: className,
35
+ }: Props = $props()
36
+
37
+ let open = $state(false)
38
+ let triggerEl = $state<HTMLElement | null>(null)
39
+ let triggerWidth = $state<number | undefined>(undefined)
40
+
41
+ const selectedItems = $derived(items.filter(i => values.includes(i.value)))
42
+
43
+ const displayedChips = $derived(
44
+ maxDisplay && selectedItems.length > maxDisplay ? selectedItems.slice(0, maxDisplay) : selectedItems
45
+ )
46
+
47
+ const overflowCount = $derived(
48
+ maxDisplay && selectedItems.length > maxDisplay ? selectedItems.length - maxDisplay : 0
49
+ )
50
+
51
+ function toggleValue(itemValue: string) {
52
+ if (values.includes(itemValue)) {
53
+ values = values.filter(v => v !== itemValue)
54
+ } else {
55
+ values = [...values, itemValue]
56
+ }
57
+ onValuesChange?.(values)
58
+ }
59
+
60
+ function removeValue(itemValue: string, e: MouseEvent) {
61
+ e.stopPropagation()
62
+ values = values.filter(v => v !== itemValue)
63
+ onValuesChange?.(values)
64
+ }
65
+
66
+ $effect(() => {
67
+ if (matchTriggerWidth && open && triggerEl) {
68
+ triggerWidth = triggerEl.offsetWidth
69
+ }
70
+ })
71
+ </script>
72
+
73
+ <DropdownMenu.Root bind:open>
74
+ <DropdownMenu.Trigger
75
+ bind:ref={triggerEl}
76
+ {disabled}
77
+ class={cn(
78
+ 'flex items-center gap-4 py-5 pl-12 pr-8 border rounded-10 bg-button-secondary cursor-default text-small text-button-secondary box-border appearance-none font-inherit outline-none transition-colors duration-base ease-in-out hover:text-button-secondary-hover hover:border-button-secondary-hover focus:border-accent focus:ring-3 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed',
79
+ open ? 'border-button-secondary-hover' : 'border-button-secondary',
80
+ className
81
+ )}
82
+ >
83
+ <span class="flex-1 flex items-center gap-4 flex-wrap min-w-0">
84
+ {#if selectedItems.length === 0}
85
+ <span class="text-small text-input-placeholder">{placeholder}</span>
86
+ {:else}
87
+ {#each displayedChips as chip (chip.value)}
88
+ <span class="inline-flex items-center gap-2 px-6 py-1 rounded-6 bg-badge-grey text-badge-grey text-tiny">
89
+ {chip.label}
90
+ <button
91
+ type="button"
92
+ class="inline-flex items-center justify-center w-12 h-12 rounded-full hover:bg-gray-300 transition-colors duration-fast [&_svg]:w-8 [&_svg]:h-8"
93
+ onclick={(e) => removeValue(chip.value, e)}
94
+ >
95
+ <IIIcon iconName="x" />
96
+ </button>
97
+ </span>
98
+ {/each}
99
+ {#if overflowCount > 0}
100
+ <span class="text-tiny text-secondary">+{overflowCount} more</span>
101
+ {/if}
102
+ {/if}
103
+ </span>
104
+ <IIIcon iconName="caret-down" class="w-14 h-14 shrink-0" />
105
+ </DropdownMenu.Trigger>
106
+ <DropdownMenu.Portal>
107
+ <DropdownMenu.Content
108
+ class="min-w-100 bg-dropdown-bg border border-dropdown-border rounded-10 shadow-dropdown p-4 z-16 outline-none max-h-300 overflow-y-auto"
109
+ sideOffset={2}
110
+ side="bottom"
111
+ align="start"
112
+ >
113
+ {#snippet child({props, wrapperProps})}
114
+ <div {...wrapperProps} style:min-width={matchTriggerWidth && triggerWidth ? `${triggerWidth}px` : undefined}>
115
+ <div {...props}>
116
+ {#each items as item (item.value)}
117
+ <DropdownMenu.CheckboxItem
118
+ checked={values.includes(item.value)}
119
+ onCheckedChange={() => toggleValue(item.value)}
120
+ disabled={item.disabled}
121
+ class={cn(
122
+ 'flex items-center gap-8 px-12 py-6 rounded-6 text-small text-dropdown-item cursor-default outline-none data-[highlighted]:bg-dropdown-item-hover data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed',
123
+ values.includes(item.value) && 'text-dropdown-item-selected'
124
+ )}
125
+ >
126
+ {#if renderItem}
127
+ {@render renderItem(item, values.includes(item.value))}
128
+ {:else}
129
+ <span class="flex-1">{item.label}</span>
130
+ {#if values.includes(item.value)}
131
+ <IIIcon iconName="check" class="w-14 h-14 text-accent shrink-0" />
132
+ {/if}
133
+ {/if}
134
+ </DropdownMenu.CheckboxItem>
135
+ {/each}
136
+ </div>
137
+ </div>
138
+ {/snippet}
139
+ </DropdownMenu.Content>
140
+ </DropdownMenu.Portal>
141
+ </DropdownMenu.Root>
@@ -0,0 +1,20 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Item = {
3
+ label: string;
4
+ value: string;
5
+ disabled?: boolean;
6
+ };
7
+ type Props = {
8
+ items: Item[];
9
+ values: string[];
10
+ placeholder?: string;
11
+ disabled?: boolean;
12
+ onValuesChange?: (values: string[]) => void;
13
+ matchTriggerWidth?: boolean;
14
+ maxDisplay?: number;
15
+ renderItem?: Snippet<[item: Item, selected: boolean]>;
16
+ class?: string;
17
+ };
18
+ declare const IIMultiSelect: import("svelte").Component<Props, {}, "values">;
19
+ type IIMultiSelect = ReturnType<typeof IIMultiSelect>;
20
+ export default IIMultiSelect;
@@ -0,0 +1,78 @@
1
+ <script lang="ts">
2
+ import IIMultiSelect from './IIMultiSelect.svelte'
3
+
4
+ let basicValues = $state<string[]>([])
5
+ let presetValues = $state(['react', 'svelte'])
6
+ let maxValues = $state(['js', 'ts', 'py', 'go', 'rs'])
7
+
8
+ const tagItems = [
9
+ {label: 'React', value: 'react'},
10
+ {label: 'Svelte', value: 'svelte'},
11
+ {label: 'Vue', value: 'vue'},
12
+ {label: 'Angular', value: 'angular'},
13
+ {label: 'Solid', value: 'solid'},
14
+ ]
15
+
16
+ const languageItems = [
17
+ {label: 'JavaScript', value: 'js'},
18
+ {label: 'TypeScript', value: 'ts'},
19
+ {label: 'Python', value: 'py'},
20
+ {label: 'Go', value: 'go'},
21
+ {label: 'Rust', value: 'rs'},
22
+ {label: 'Java', value: 'java'},
23
+ {label: 'C#', value: 'csharp'},
24
+ {label: 'Ruby', value: 'ruby', disabled: true},
25
+ ]
26
+
27
+ const roleItems = [
28
+ {label: 'Admin', value: 'admin'},
29
+ {label: 'Editor', value: 'editor'},
30
+ {label: 'Viewer', value: 'viewer'},
31
+ {label: 'Guest', value: 'guest', disabled: true},
32
+ ]
33
+ </script>
34
+
35
+ <div class="flex flex-col gap-32">
36
+ <!-- Basic -->
37
+ <section>
38
+ <h2 class="text-default-emphasis text-primary mb-8">Basic Multi-Select</h2>
39
+ <p class="text-small text-secondary mb-12">Select multiple items. Chips appear in the trigger with X to remove.</p>
40
+ <div class="w-280">
41
+ <IIMultiSelect items={tagItems} bind:values={basicValues} placeholder="Select frameworks..." />
42
+ </div>
43
+ <p class="text-small text-secondary mt-8">Selected: {basicValues.length ? basicValues.join(', ') : 'None'}</p>
44
+ </section>
45
+
46
+ <!-- Preselected -->
47
+ <section>
48
+ <h2 class="text-default-emphasis text-primary mb-8">With Preset Values</h2>
49
+ <div class="w-280">
50
+ <IIMultiSelect items={tagItems} bind:values={presetValues} placeholder="Select frameworks..." />
51
+ </div>
52
+ </section>
53
+
54
+ <!-- Max Display -->
55
+ <section>
56
+ <h2 class="text-default-emphasis text-primary mb-8">Max Display (Overflow)</h2>
57
+ <p class="text-small text-secondary mb-12">When maxDisplay is set, excess chips collapse to "+N more".</p>
58
+ <div class="w-280">
59
+ <IIMultiSelect items={languageItems} bind:values={maxValues} maxDisplay={3} placeholder="Select languages..." />
60
+ </div>
61
+ </section>
62
+
63
+ <!-- Match Trigger Width -->
64
+ <section>
65
+ <h2 class="text-default-emphasis text-primary mb-8">Match Trigger Width</h2>
66
+ <div class="w-320">
67
+ <IIMultiSelect items={roleItems} values={[]} matchTriggerWidth placeholder="Assign roles..." />
68
+ </div>
69
+ </section>
70
+
71
+ <!-- Disabled -->
72
+ <section>
73
+ <h2 class="text-default-emphasis text-primary mb-8">Disabled</h2>
74
+ <div class="w-280">
75
+ <IIMultiSelect items={tagItems} values={['react', 'svelte']} disabled placeholder="Select frameworks..." />
76
+ </div>
77
+ </section>
78
+ </div>
@@ -0,0 +1,3 @@
1
+ declare const IIMultiSelectStories: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type IIMultiSelectStories = ReturnType<typeof IIMultiSelectStories>;
3
+ export default IIMultiSelectStories;
@@ -0,0 +1 @@
1
+ export { default as IIMultiSelect } from './IIMultiSelect.svelte';
@@ -0,0 +1 @@
1
+ export { default as IIMultiSelect } from './IIMultiSelect.svelte';