@invopop/popui 0.1.2 → 0.1.3-6.beta.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.
Files changed (178) hide show
  1. package/dist/AlertDialog.svelte +10 -11
  2. package/dist/BaseButton.svelte +25 -104
  3. package/dist/BaseCard.svelte +35 -30
  4. package/dist/BaseCounter.svelte +11 -8
  5. package/dist/BaseDropdown.svelte +5 -5
  6. package/dist/BaseFlag.svelte +5 -3
  7. package/dist/BaseFlag.svelte.d.ts +1 -0
  8. package/dist/BaseTable.svelte +16 -16
  9. package/dist/BaseTable.svelte.d.ts +1 -1
  10. package/dist/BaseTableActions.svelte +4 -6
  11. package/dist/BaseTableCellContent.svelte +9 -21
  12. package/dist/BaseTableCheckbox.svelte +9 -11
  13. package/dist/BaseTableHeaderContent.svelte +4 -4
  14. package/dist/BaseTableHeaderOrderBy.svelte +23 -12
  15. package/dist/BaseTableRow.svelte +12 -10
  16. package/dist/Breadcrumb.svelte +40 -0
  17. package/dist/Breadcrumb.svelte.d.ts +4 -0
  18. package/dist/Breadcrumbs.svelte +5 -30
  19. package/dist/ButtonFile.svelte +40 -30
  20. package/dist/ButtonUuidCopy.svelte +6 -3
  21. package/dist/CardCheckbox.svelte +45 -32
  22. package/dist/CardCheckbox.svelte.d.ts +1 -1
  23. package/dist/CardRelation.svelte +12 -13
  24. package/dist/CompanySelector.svelte +35 -7
  25. package/dist/CounterWidget.svelte +52 -0
  26. package/dist/CounterWidget.svelte.d.ts +4 -0
  27. package/dist/DataListItem.svelte +14 -10
  28. package/dist/DatePicker.svelte +20 -17
  29. package/dist/DrawerContext.svelte +207 -15
  30. package/dist/DrawerContextItem.svelte +50 -50
  31. package/dist/DrawerContextSeparator.svelte +1 -1
  32. package/dist/DropdownSelect.svelte +46 -18
  33. package/dist/EmptyState.svelte +42 -0
  34. package/dist/EmptyState.svelte.d.ts +4 -0
  35. package/dist/FeedEvents.svelte +9 -5
  36. package/dist/FeedIconEvent.svelte +2 -2
  37. package/dist/FeedIconStatus.svelte +4 -4
  38. package/dist/FeedItem.svelte +10 -11
  39. package/dist/FeedItemDetail.svelte +32 -6
  40. package/dist/FeedViewer.svelte +1 -1
  41. package/dist/GlobalSearch.svelte +13 -12
  42. package/dist/InputCheckbox.svelte +2 -5
  43. package/dist/InputError.svelte +4 -9
  44. package/dist/InputLabel.svelte +3 -1
  45. package/dist/InputRadio.svelte +29 -13
  46. package/dist/InputRadio.svelte.d.ts +1 -1
  47. package/dist/InputSearch.svelte +8 -8
  48. package/dist/InputSelect.svelte +32 -31
  49. package/dist/InputText.svelte +32 -24
  50. package/dist/InputTextarea.svelte +25 -19
  51. package/dist/InputToggle.svelte +24 -18
  52. package/dist/MenuItem.svelte +16 -11
  53. package/dist/MenuItemCollapsible.svelte +7 -7
  54. package/dist/Notification.svelte +60 -25
  55. package/dist/ProfileAvatar.svelte +43 -14
  56. package/dist/ProgressBar.svelte +42 -0
  57. package/dist/ProgressBar.svelte.d.ts +4 -0
  58. package/dist/ProgressBarCircle.svelte +46 -0
  59. package/dist/ProgressBarCircle.svelte.d.ts +4 -0
  60. package/dist/SeparatorHorizontal.svelte +2 -2
  61. package/dist/ShortcutWrapper.svelte +14 -5
  62. package/dist/StatusLabel.svelte +4 -5
  63. package/dist/StepIconList.svelte +11 -9
  64. package/dist/TagBeta.svelte +26 -14
  65. package/dist/TagProgress.svelte +28 -0
  66. package/dist/TagProgress.svelte.d.ts +4 -0
  67. package/dist/TagSearch.svelte +4 -4
  68. package/dist/TagStatus.svelte +32 -34
  69. package/dist/TitleMain.svelte +1 -1
  70. package/dist/TitleSection.svelte +1 -1
  71. package/dist/UuidCopy.svelte +4 -4
  72. package/dist/alert-dialog/alert-dialog-action.svelte +8 -4
  73. package/dist/alert-dialog/alert-dialog-cancel.svelte +7 -3
  74. package/dist/alert-dialog/alert-dialog-content.svelte +1 -1
  75. package/dist/alert-dialog/alert-dialog-description.svelte +1 -1
  76. package/dist/alert-dialog/alert-dialog-footer.svelte +1 -1
  77. package/dist/alert-dialog/alert-dialog-header.svelte +1 -1
  78. package/dist/alert-dialog/alert-dialog-overlay.svelte +1 -1
  79. package/dist/alert-dialog/alert-dialog-title.svelte +1 -1
  80. package/dist/alert-dialog/alert-dialog-trigger.svelte +4 -2
  81. package/dist/button/button.svelte +200 -24
  82. package/dist/button/button.svelte.d.ts +51 -26
  83. package/dist/clickOutside.d.ts +5 -2
  84. package/dist/clickOutside.js +9 -3
  85. package/dist/data-table/cells/boolean-cell.svelte +16 -0
  86. package/dist/data-table/cells/boolean-cell.svelte.d.ts +8 -0
  87. package/dist/data-table/cells/currency-cell.svelte +10 -0
  88. package/dist/data-table/cells/currency-cell.svelte.d.ts +8 -0
  89. package/dist/data-table/cells/date-cell.svelte +10 -0
  90. package/dist/data-table/cells/date-cell.svelte.d.ts +8 -0
  91. package/dist/data-table/cells/tag-cell.svelte +12 -0
  92. package/dist/data-table/cells/tag-cell.svelte.d.ts +8 -0
  93. package/dist/data-table/cells/text-cell.svelte +10 -0
  94. package/dist/data-table/cells/text-cell.svelte.d.ts +8 -0
  95. package/dist/data-table/column-definitions.d.ts +12 -0
  96. package/dist/data-table/column-definitions.js +40 -0
  97. package/dist/data-table/column-sizing-helpers.d.ts +6 -0
  98. package/dist/data-table/column-sizing-helpers.js +24 -0
  99. package/dist/data-table/create-columns.d.ts +3 -0
  100. package/dist/data-table/create-columns.js +50 -0
  101. package/dist/data-table/data-table-pagination.svelte +144 -0
  102. package/dist/data-table/data-table-pagination.svelte.d.ts +4 -0
  103. package/dist/data-table/data-table-svelte.svelte.d.ts +40 -0
  104. package/dist/data-table/data-table-svelte.svelte.js +111 -0
  105. package/dist/data-table/data-table-toolbar.svelte +16 -0
  106. package/dist/data-table/data-table-toolbar.svelte.d.ts +29 -0
  107. package/dist/data-table/data-table-types.d.ts +66 -0
  108. package/dist/data-table/data-table-types.js +1 -0
  109. package/dist/data-table/data-table-view-options.svelte +88 -0
  110. package/dist/data-table/data-table-view-options.svelte.d.ts +27 -0
  111. package/dist/data-table/data-table.svelte +303 -0
  112. package/dist/data-table/data-table.svelte.d.ts +25 -0
  113. package/dist/data-table/flex-render.svelte +40 -0
  114. package/dist/data-table/flex-render.svelte.d.ts +33 -0
  115. package/dist/data-table/index.d.ts +13 -0
  116. package/dist/data-table/index.js +13 -0
  117. package/dist/data-table/render-helpers.d.ts +90 -0
  118. package/dist/data-table/render-helpers.js +99 -0
  119. package/dist/data-table/table-setup.d.ts +36 -0
  120. package/dist/data-table/table-setup.js +130 -0
  121. package/dist/data-table/table-styles.d.ts +17 -0
  122. package/dist/data-table/table-styles.js +48 -0
  123. package/dist/helpers.d.ts +1 -0
  124. package/dist/helpers.js +3 -0
  125. package/dist/index.d.ts +15 -7
  126. package/dist/index.js +31 -14
  127. package/dist/range-calendar/range-calendar-cell.svelte +1 -1
  128. package/dist/range-calendar/range-calendar-day.svelte +11 -12
  129. package/dist/range-calendar/range-calendar-head-cell.svelte +1 -1
  130. package/dist/range-calendar/range-calendar-header.svelte +1 -1
  131. package/dist/range-calendar/range-calendar-month-select.svelte +1 -1
  132. package/dist/range-calendar/range-calendar-next-button.svelte +3 -3
  133. package/dist/range-calendar/range-calendar-prev-button.svelte +3 -3
  134. package/dist/range-calendar/range-calendar.svelte +1 -1
  135. package/dist/sonner/index.d.ts +1 -0
  136. package/dist/sonner/index.js +1 -0
  137. package/dist/sonner/sonner.svelte +44 -0
  138. package/dist/sonner/sonner.svelte.d.ts +4 -0
  139. package/dist/svg/CheckBadge.svelte +18 -0
  140. package/dist/svg/CheckBadge.svelte.d.ts +26 -0
  141. package/dist/svg/IconDelivery.svelte +86 -0
  142. package/dist/svg/IconDelivery.svelte.d.ts +20 -0
  143. package/dist/svg/IconEmpty.svelte +113 -121
  144. package/dist/svg/IconOrder.svelte +81 -0
  145. package/dist/svg/IconOrder.svelte.d.ts +20 -0
  146. package/dist/svg/IconPayment.svelte +86 -0
  147. package/dist/svg/IconPayment.svelte.d.ts +20 -0
  148. package/dist/table/table-body.svelte +5 -1
  149. package/dist/table/table-cell.svelte +4 -2
  150. package/dist/table/table-footer.svelte +1 -1
  151. package/dist/table/table-head.svelte +4 -2
  152. package/dist/table/table-header.svelte +1 -1
  153. package/dist/table/table-row.svelte +4 -2
  154. package/dist/table/table.svelte +2 -2
  155. package/dist/tabs/tabs-list.svelte +8 -2
  156. package/dist/tabs/tabs-list.svelte.d.ts +4 -1
  157. package/dist/tabs/tabs-trigger.svelte +5 -3
  158. package/dist/tabs/tabs-trigger.svelte.d.ts +4 -1
  159. package/dist/tailwind.theme.css +998 -0
  160. package/dist/tooltip/tooltip-content.svelte +2 -2
  161. package/dist/types.d.ts +71 -50
  162. package/package.json +20 -10
  163. package/dist/CounterWorkflow.svelte +0 -19
  164. package/dist/CounterWorkflow.svelte.d.ts +0 -4
  165. package/dist/DrawerContextWorkspace.svelte +0 -126
  166. package/dist/DrawerContextWorkspace.svelte.d.ts +0 -4
  167. package/dist/EmptyStateIcon.svelte +0 -52
  168. package/dist/EmptyStateIcon.svelte.d.ts +0 -4
  169. package/dist/EmptyStateIllustration.svelte +0 -66
  170. package/dist/EmptyStateIllustration.svelte.d.ts +0 -4
  171. package/dist/FormLayoutModal.svelte +0 -14
  172. package/dist/FormLayoutModal.svelte.d.ts +0 -4
  173. package/dist/ProfileSelector.svelte +0 -41
  174. package/dist/ProfileSelector.svelte.d.ts +0 -4
  175. package/dist/SectionLayout.svelte +0 -13
  176. package/dist/SectionLayout.svelte.d.ts +0 -4
  177. package/dist/tw.theme.d.ts +0 -142
  178. package/dist/tw.theme.js +0 -158
@@ -115,7 +115,7 @@
115
115
  onSelect
116
116
  }: DatePickerProps = $props()
117
117
 
118
- let selectedPeriod = $state('this-week')
118
+ let selectedPeriod = $state('custom')
119
119
  let value = $state<DateRange>({
120
120
  start: undefined,
121
121
  end: undefined
@@ -123,8 +123,8 @@
123
123
  let isOpen = $state(false)
124
124
  let styles = $derived(
125
125
  clsx({
126
- 'border-workspace-accent focus:border-workspace-accent shadow-active': isOpen,
127
- 'border-neutral-200 hover:border-neutral-300': !isOpen
126
+ 'border-border-selected-bold shadow-active': isOpen,
127
+ 'border-border-secondary hover:border-border-default-secondary-hover': !isOpen
128
128
  })
129
129
  )
130
130
  let selectedLabel = $state(label)
@@ -143,14 +143,15 @@
143
143
  }
144
144
  return
145
145
  }
146
- value = {
147
- start: toCalendarDate(startOfThisWeek),
148
- end: toCalendarDate(endOfThisWeek)
149
- }
150
- selectedPeriod = 'this-week'
151
146
  })
152
147
 
153
148
  function cancel() {
149
+ value = {
150
+ start: undefined,
151
+ end: undefined
152
+ }
153
+ selectedPeriod = 'custom'
154
+ selectedLabel = label
154
155
  isOpen = false
155
156
  onSelect?.({ from: '', to: '' })
156
157
  }
@@ -187,11 +188,11 @@
187
188
  onclick={() => {
188
189
  isOpen = !isOpen
189
190
  }}
190
- class="{styles} datepicker-trigger w-full py-1.25 pl-7 pr-8 border rounded-md text-neutral-800 placeholder-neutral-800 text-base cursor-pointer"
191
+ class="{styles} datepicker-trigger w-full py-1.25 pl-7 pr-2 text-left border border-border-default-secondary rounded-lg text-foreground placeholder-foreground text-base cursor-pointer"
191
192
  >
192
193
  {selectedLabel}
193
194
  </button>
194
- <Icon src={Calendar} class="h-4 w-4 absolute top-2 left-2 text-neutral-500" />
195
+ <Icon src={Calendar} class="h-4 w-4 absolute top-2 left-2 text-foreground-default-secondary" />
195
196
  </div>
196
197
 
197
198
  <div class="relative">
@@ -208,21 +209,21 @@
208
209
  <div
209
210
  class:left-0={position === 'left'}
210
211
  class:right-0={position === 'right'}
211
- class="bg-white inline-flex flex-col shadow rounded-lg absolute right-0 top-2 z-40"
212
+ class="bg-background inline-flex flex-col shadow-lg rounded-xl absolute right-0 top-2 z-40 border border-border"
212
213
  use:clickOutside
213
214
  onclick_outside={() => {
214
215
  if (!isOpen) return
215
216
  cancel()
216
217
  }}
217
218
  >
218
- <div class="flex border-b border-neutral-100 min-h-[300px] rounded-lg shadow-calendar">
219
- <div class="flex flex-col space-y-2 items-start p-3 border-r border-neutral-100">
219
+ <div class="flex border-b border-border min-h-[300px] shadow-calendar">
220
+ <div class="flex flex-col space-y-2 items-start p-3 border-r border-border">
220
221
  {#each periods as period}
221
222
  <button
222
223
  onclick={period.action}
223
224
  class="{selectedPeriod === period.slug
224
- ? 'selected-period text-workspace-accent-600 bg-workspace-accent-100'
225
- : 'text-neutral-500'} whitespace-nowrap text-base px-2 py-1 tracking-normal rounded cursor-pointer"
225
+ ? 'selected-period text-foreground-selected bg-background-selected font-medium'
226
+ : 'text-foreground-default-secondary'} whitespace-nowrap text-base px-2 py-1 tracking-normal rounded-md cursor-pointer"
226
227
  >
227
228
  {period.label}
228
229
  </button>
@@ -231,8 +232,10 @@
231
232
  <RangeCalendar bind:value numberOfMonths={2} />
232
233
  </div>
233
234
  <div class="p-3 flex justify-end items-center space-x-3">
234
- <BaseButton variant="secondary" onclick={cancel}>Cancel</BaseButton>
235
- <BaseButton variant="primary" onclick={confirm} disabled={!value.end}>Confirm</BaseButton>
235
+ <BaseButton variant="secondary" size="lg" onclick={cancel}>Cancel</BaseButton>
236
+ <BaseButton variant="primary" size="lg" onclick={confirm} disabled={!value.end}
237
+ >Confirm</BaseButton
238
+ >
236
239
  </div>
237
240
  </div>
238
241
  </Transition>
@@ -1,46 +1,238 @@
1
1
  <script lang="ts">
2
2
  import type { DrawerContextProps, DrawerOption } from './types.ts'
3
3
  import DrawerContextItem from './DrawerContextItem.svelte'
4
- import InputSearch from './InputSearch.svelte'
5
4
  import DrawerContextSeparator from './DrawerContextSeparator.svelte'
5
+ import EmptyState from './EmptyState.svelte'
6
+ import BaseCounter from './BaseCounter.svelte'
7
+ import { Icon } from '@steeze-ui/svelte-icon'
8
+ import { ChevronRight } from '@steeze-ui/heroicons'
9
+ import { slide } from 'svelte/transition'
10
+ import Sortable from 'sortablejs'
11
+ import { onMount } from 'svelte'
6
12
 
7
13
  let {
8
14
  items = $bindable([]),
9
15
  multiple = false,
10
- searchable = false,
16
+ draggable = false,
11
17
  widthClass = 'w-60',
12
18
  onclick,
13
- onselect
19
+ onselect,
20
+ onreorder,
21
+ children,
22
+ groups
14
23
  }: DrawerContextProps = $props()
15
24
 
16
25
  let selectedItems = $derived(items.filter((i) => i.selected))
26
+ let hasGroups = $derived(groups && groups.length > 0)
27
+ let { groupedItems, ungroupedItems } = $derived.by(() => {
28
+ if (!hasGroups) return { groupedItems: new Map(), ungroupedItems: items }
29
+
30
+ const grouped = new Map<string, DrawerOption[]>()
31
+ const ungrouped: DrawerOption[] = []
32
+
33
+ groups!.forEach((group) => {
34
+ grouped.set(group.slug, [])
35
+ })
36
+
37
+ items.forEach((item) => {
38
+ if (item.groupBy && grouped.has(item.groupBy)) {
39
+ grouped.get(item.groupBy)!.push(item)
40
+ } else {
41
+ ungrouped.push(item)
42
+ }
43
+ })
44
+
45
+ return { groupedItems: grouped, ungroupedItems: ungrouped }
46
+ })
47
+
48
+ let openGroups = $state<Record<string, boolean>>({})
49
+ let ungroupedContainer: HTMLElement | null = $state(null)
50
+ let groupContainers: Record<string, HTMLElement | null> = {}
51
+
52
+ $effect(() => {
53
+ if (hasGroups) {
54
+ const selectedItem = items.find((i) => i.selected)
55
+ if (selectedItem?.groupBy && Object.keys(openGroups).length === 0) {
56
+ openGroups = { [selectedItem.groupBy]: true }
57
+ }
58
+ }
59
+ })
60
+
17
61
  $effect(() => {
18
62
  onselect?.(selectedItems)
19
63
  })
20
64
 
65
+ function initializeSortable() {
66
+ if (!draggable) return
67
+
68
+ // Initialize sortable for ungrouped items
69
+ if (ungroupedContainer && ungroupedItems.length > 0) {
70
+ Sortable.create(ungroupedContainer, {
71
+ animation: 150,
72
+ handle: '.draggable-item',
73
+ ghostClass: 'opacity-10',
74
+ dragClass: 'cursor-grabbing',
75
+ forceFallback: true,
76
+ onEnd: (event) => {
77
+ if (event.oldIndex !== undefined && event.newIndex !== undefined) {
78
+ const newItems = [...items]
79
+ const ungroupedIndices = items
80
+ .map((item, index) => (!item.groupBy ? index : -1))
81
+ .filter((i) => i !== -1)
82
+
83
+ const fromIndex = ungroupedIndices[event.oldIndex]
84
+ const toIndex = ungroupedIndices[event.newIndex]
85
+
86
+ const [removed] = newItems.splice(fromIndex, 1)
87
+ newItems.splice(toIndex, 0, removed)
88
+
89
+ items = newItems
90
+ onreorder?.(newItems)
91
+ }
92
+ }
93
+ })
94
+ }
95
+
96
+ // Initialize sortable for grouped items
97
+ if (hasGroups && groups) {
98
+ groups.forEach((group) => {
99
+ const container = groupContainers[group.slug]
100
+ const groupItems = groupedItems.get(group.slug) || []
101
+
102
+ if (container && groupItems.length > 0) {
103
+ Sortable.create(container, {
104
+ animation: 150,
105
+ handle: '.draggable-item',
106
+ ghostClass: 'opacity-10',
107
+ dragClass: 'cursor-grabbing',
108
+ forceFallback: true,
109
+ onEnd: (event) => {
110
+ if (event.oldIndex !== undefined && event.newIndex !== undefined) {
111
+ const newItems = [...items]
112
+ const groupedIndices = items
113
+ .map((item, index) => (item.groupBy === group.slug ? index : -1))
114
+ .filter((i) => i !== -1)
115
+
116
+ const fromIndex = groupedIndices[event.oldIndex]
117
+ const toIndex = groupedIndices[event.newIndex]
118
+
119
+ const [removed] = newItems.splice(fromIndex, 1)
120
+ newItems.splice(toIndex, 0, removed)
121
+
122
+ items = newItems
123
+ onreorder?.(newItems)
124
+ }
125
+ }
126
+ })
127
+ }
128
+ })
129
+ }
130
+ }
131
+
132
+ onMount(() => {
133
+ if (draggable) {
134
+ // Small delay to ensure DOM is ready
135
+ setTimeout(initializeSortable, 100)
136
+ }
137
+ })
138
+
21
139
  function updateItem(item: DrawerOption) {
22
140
  items = items.map((i) => {
23
141
  if (i.value === item.value) return item
24
142
  return i
25
143
  })
26
144
  }
145
+
146
+ function toggleGroup(groupSlug: string) {
147
+ openGroups = openGroups[groupSlug] ? {} : { [groupSlug]: true }
148
+
149
+ // Reinitialize sortable when a group is toggled
150
+ if (draggable) {
151
+ setTimeout(initializeSortable, 100)
152
+ }
153
+ }
27
154
  </script>
28
155
 
29
- <div
30
- class="{widthClass} border border-neutral-200 py-1 rounded-md shadow-lg space-y-0.5 bg-white max-h-80 overflow-y-auto"
31
- >
32
- {#if searchable}
33
- <div class="px-2 mt-2 mb-1">
34
- <InputSearch placeholder="Search" />
156
+ {#snippet drawerItem(item: DrawerOption)}
157
+ {#if item.separator}
158
+ <DrawerContextSeparator />
159
+ {:else}
160
+ <div class:px-1={!item.groupBy} class:draggable-item={draggable} class:cursor-grab={draggable}>
161
+ <DrawerContextItem {item} {multiple} {onclick} onchange={updateItem} />
35
162
  </div>
36
163
  {/if}
37
- <ul class="space-y-0.5 max-h-80 overflow-y-auto">
38
- {#each items as item}
39
- {#if item.separator}
164
+ {/snippet}
165
+
166
+ <div
167
+ class="{widthClass} border border-border rounded-2xl shadow-lg bg-background flex flex-col py-1 max-h-[568px] list-none"
168
+ >
169
+ {@render children?.()}
170
+
171
+ {#if hasGroups}
172
+ {#each groups as group, index}
173
+ {@const groupItems = groupedItems.get(group.slug) || []}
174
+ {@const isLastGroup = index === groups!.length - 1}
175
+ {@const isOpen = openGroups[group.slug]}
176
+ {@const hasOpenGroup = Object.values(openGroups).some((v) => v)}
177
+ <div
178
+ class="px-1"
179
+ class:flex-1={isOpen}
180
+ class:flex={isOpen}
181
+ class:flex-col={isOpen}
182
+ class:min-h-0={isOpen}
183
+ class:flex-shrink-0={!isOpen && hasOpenGroup}
184
+ >
185
+ <button
186
+ class="cursor-pointer flex items-center justify-between h-8 pl-2.5 pr-2.5 py-2.5 text-base font-medium text-foreground-default-secondary w-full hover:bg-background-default-secondary rounded-lg overflow-clip flex-shrink-0"
187
+ onclick={() => toggleGroup(group.slug)}
188
+ >
189
+ <div class="flex items-center gap-1.5">
190
+ <span>{group.label}</span>
191
+ <Icon
192
+ src={ChevronRight}
193
+ class="size-3 text-icon-default-secondary transition-all transform {isOpen
194
+ ? 'rotate-90'
195
+ : ''}"
196
+ />
197
+ </div>
198
+ {#if groupItems.length}
199
+ <BaseCounter value={groupItems.length} />
200
+ {/if}
201
+ </button>
202
+
203
+ {#if isOpen}
204
+ <div
205
+ class="w-full overflow-y-auto flex-1 min-h-0"
206
+ transition:slide={{ duration: 200 }}
207
+ bind:this={groupContainers[group.slug]}
208
+ >
209
+ {#if !groupItems.length}
210
+ <div class="px-1 pt-1 pb-5">
211
+ <EmptyState
212
+ iconSource={group.emptyIcon}
213
+ title={group.emptyTitle || 'No items here'}
214
+ description={group.emptyDescription || 'Add items to get started'}
215
+ />
216
+ </div>
217
+ {:else}
218
+ {#each groupItems as item}
219
+ {@render drawerItem(item)}
220
+ {/each}
221
+ {/if}
222
+ </div>
223
+ {/if}
224
+ </div>
225
+ {#if !isLastGroup}
40
226
  <DrawerContextSeparator />
41
- {:else}
42
- <DrawerContextItem {item} {multiple} {onclick} onchange={updateItem} />
43
227
  {/if}
44
228
  {/each}
45
- </ul>
229
+ {/if}
230
+
231
+ {#if ungroupedItems.length}
232
+ <div class="flex-shrink-0 overflow-y-auto max-h-[564px]" bind:this={ungroupedContainer}>
233
+ {#each ungroupedItems as item (item.value)}
234
+ {@render drawerItem(item)}
235
+ {/each}
236
+ </div>
237
+ {/if}
46
238
  </div>
@@ -2,57 +2,54 @@
2
2
  import type { DrawerContextItemProps } from './types.ts'
3
3
  import InputCheckbox from './InputCheckbox.svelte'
4
4
  import { Icon } from '@steeze-ui/svelte-icon'
5
- import { onMount } from 'svelte'
6
- import { Success, Tick } from '@invopop/ui-icons'
7
- import ProfileAvatar from './ProfileAvatar.svelte'
5
+ import { onMount, onDestroy } from 'svelte'
6
+ import { Success } from '@invopop/ui-icons'
8
7
  import clsx from 'clsx'
9
8
  import BaseFlag from './BaseFlag.svelte'
10
9
  import { getCountryName } from './helpers.js'
11
10
  import TagStatus from './TagStatus.svelte'
11
+ import ProfileAvatar from './ProfileAvatar.svelte'
12
12
 
13
13
  let {
14
14
  multiple = false,
15
15
  item = $bindable(),
16
16
  scrollIfSelected = false,
17
- workspace = false,
18
17
  onchange,
19
18
  onclick
20
19
  }: DrawerContextItemProps = $props()
21
20
 
22
21
  let el: HTMLElement | undefined = $state()
23
22
 
24
- let hasIcon = $derived(item.icon || workspace)
25
-
26
23
  let styles = $derived(
27
24
  clsx(
28
- { 'py-1 space-x-3': workspace },
29
- { 'py-1.5 space-x-1.5': !workspace },
30
- { 'pl-1.5': !hasIcon },
31
- { 'pl-2': hasIcon },
32
- { 'bg-workspace-accent-100': item.selected && !multiple },
33
- { 'group-hover:bg-neutral-100': (!item.selected && !item.disabled) || multiple }
25
+ 'px-2 py-1.5 space-x-1.5',
26
+ { 'bg-background-selected': item?.selected && !multiple },
27
+ {
28
+ 'group-hover:bg-background-default-secondary':
29
+ (!item?.selected && !item?.disabled) || multiple
30
+ }
34
31
  )
35
32
  )
33
+
36
34
  let labelStyles = $derived(
37
35
  clsx(
38
- { 'text-danger-500': item.destructive },
39
- { 'text-neutral-800': !item.destructive },
40
- { 'tracking-tight max-w-[200px]': workspace },
41
- { 'tracking-normal': !workspace }
36
+ { 'text-foreground-critical': item?.destructive },
37
+ { 'text-foreground': !item?.destructive },
38
+ { 'opacity-30': item?.locked }
42
39
  )
43
40
  )
44
- let title = $derived(item.label.length > 25 ? item.label : undefined)
41
+ let title = $derived(item?.label && item.label.length > 25 ? item.label : undefined)
45
42
 
46
43
  onMount(() => {
47
- if (!scrollIfSelected) return
48
-
49
- if (item.selected) {
44
+ if (scrollIfSelected && item?.selected) {
50
45
  el?.scrollIntoView()
51
46
  }
52
47
  })
53
48
 
54
49
  function handleClick(event: MouseEvent) {
55
50
  event.stopPropagation()
51
+ if (!item) return
52
+
56
53
  if (multiple) {
57
54
  item.selected = !item.selected
58
55
  onchange?.(item)
@@ -64,49 +61,52 @@
64
61
 
65
62
  <button
66
63
  bind:this={el}
67
- class="w-full px-1 py-0.5 disabled:opacity-30 group"
68
- disabled={item.disabled}
64
+ class="cursor-pointer w-full disabled:opacity-30 group"
65
+ disabled={item?.disabled}
69
66
  onclick={handleClick}
70
67
  >
71
- <div class="{styles} rounded pr-2 flex items-center justify-start w-full">
72
- {#if workspace}
73
- <ProfileAvatar name={item.label} picture={item.picture} large />
74
- {:else if item.icon}
68
+ <div class="{styles} rounded-md pr-2 flex items-center justify-start w-full">
69
+ {#if item?.useAvatar}
70
+ <ProfileAvatar name={item?.label || ''} picture={item?.picture || ''} variant="sm" />
71
+ {:else if item?.picture}
72
+ <ProfileAvatar name={item?.label || ''} picture={item?.picture} variant="sm" />
73
+ {:else if item?.icon}
75
74
  <Icon
76
75
  src={item.icon}
77
- class="w-4 h-4 {item.destructive
78
- ? 'text-danger-500'
79
- : item.iconClass || 'text-neutral-500'}"
76
+ class="w-4 h-4 {item?.destructive
77
+ ? 'text-icon-critical'
78
+ : item?.iconClass || 'text-icon'} {item?.locked ? 'opacity-30' : ''}"
80
79
  />
81
80
  {/if}
82
- <div class="whitespace-nowrap flex-1 text-left flex flex-col truncate" {title}>
83
- <span class="flex items-center space-x-1.5">
84
- {#if item.color}
85
- <TagStatus status={item.color} dot />
86
- {/if}
87
- <span class="{labelStyles} text-base font-medium truncate">{item.label}</span>
88
- </span>
81
+ <div class="whitespace-nowrap flex-1 text-left flex items-center space-x-1.5 truncate" {title}>
82
+ {#if item?.color}
83
+ <TagStatus status={item.color} dot />
84
+ {/if}
85
+ <span class="{labelStyles} text-base font-medium truncate">{item?.label || ''}</span>
89
86
 
90
- {#if item.country}
91
- <span class="flex space-x-1 items-center">
92
- <BaseFlag country={item.country} width={10} />
93
- <span class="text-sm text-neutral-500 tracking-normal"
94
- >{getCountryName(item.country)}</span
95
- >
87
+ {#if item?.country}
88
+ <BaseFlag country={item.country} />
89
+ <span class="text-xs font-medium text-foreground-default-secondary uppercase">
90
+ {item.country}
96
91
  </span>
97
92
  {/if}
98
93
  </div>
99
- {#if multiple}
94
+ {#if item?.action}
95
+ {@render item.action(item)}
96
+ {:else if multiple}
100
97
  <InputCheckbox
101
- bind:checked={item.selected}
102
- onchange={() => {
103
- onchange?.(item)
98
+ checked={item?.selected ?? false}
99
+ onchange={(value) => {
100
+ if (item) {
101
+ item.selected = value
102
+ onchange?.(item)
103
+ }
104
104
  }}
105
105
  />
106
- {:else if item.selected}
107
- <Icon src={Success} class="w-4 h-4 text-workspace-accent" />
108
- {:else if item.rightIcon}
109
- <Icon src={item.rightIcon} class="w-4 h-4 text-neutral-400" />
106
+ {:else if item?.selected}
107
+ <Icon src={Success} class="size-4 text-icon-selected" />
108
+ {:else if item?.rightIcon}
109
+ <Icon src={item.rightIcon} class="size-4 text-icon-default-secondary" />
110
110
  {/if}
111
111
  </div>
112
112
  </button>
@@ -1 +1 @@
1
- <li class="bg-neutral-100 h-px w-full"></li>
1
+ <li class="bg-border h-px min-h-px w-full my-1"></li>
@@ -41,17 +41,17 @@
41
41
  let selectedColor = $derived(!multiple && items.find((i) => i.selected)?.color)
42
42
  let selectedIcon = $derived(!multiple && items.find((i) => i.selected)?.icon)
43
43
  let selectedIconColor = $derived(
44
- (!multiple && items.find((i) => i.selected)?.iconClass) || 'text-neutral-500'
44
+ (!multiple && items.find((i) => i.selected)?.iconClass) || 'text-foreground-default-secondary'
45
45
  )
46
46
  let selectedLabel = $derived(
47
- `${selectedItems[0]?.label || ''}${selectedItems.length > 1 ? ' and more...' : ''}` ||
47
+ `${selectedItems[0]?.label || ''}${selectedItems.length > 1 && multiple ? ' and more' : ''}` ||
48
48
  placeholder
49
49
  )
50
50
 
51
51
  let styles = $derived(
52
- clsx({
53
- 'shadow-active border-workspace-accent hover:border-workspace-accent': isOpen,
54
- 'border-neutral-100': !isOpen
52
+ clsx('border backdrop-blur-sm backdrop-filter', {
53
+ 'border-border-selected-bold shadow-active': isOpen,
54
+ 'border-border-default-secondary hover:border-border-default-secondary-hover': !isOpen
55
55
  })
56
56
  )
57
57
 
@@ -75,30 +75,58 @@
75
75
  }
76
76
  </script>
77
77
 
78
- <BaseDropdown bind:isOpen placement="bottom-start" {fullWidth} bind:this={selectDropdown}>
78
+ {#snippet label()}
79
+ <span
80
+ class="flex-1 text-base truncate {selectedItems.length
81
+ ? 'text-foreground'
82
+ : 'text-foreground-default-secondary'}"
83
+ >
84
+ {selectedLabel}
85
+ </span>
86
+ {/snippet}
87
+
88
+ <BaseDropdown
89
+ bind:isOpen
90
+ placement="bottom-start"
91
+ {fullWidth}
92
+ bind:this={selectDropdown}
93
+ class={fullWidth ? '' : widthClass}
94
+ >
79
95
  {#snippet trigger()}
80
96
  <div
81
- class="{styles} dropdown-select max-w-[420px] flex items-center border hover:border-neutral-300 rounded-md py-1.25 pl-2 gap-1 bg-white whitespace-nowrap"
97
+ class="{styles} dropdown-select flex items-center rounded-lg py-1.5 pl-2 pr-[28px] bg-background overflow-hidden w-full h-8"
82
98
  >
83
99
  {#if selectedColor}
84
- <TagStatus dot status={selectedColor} />
85
- {:else if selectedIcon}
86
- <Icon src={selectedIcon} {iconTheme} class="{selectedIconColor} h-4 w-4 flex-shrink-0" />
87
- {:else if resolvedIcon}
88
- <Icon src={resolvedIcon} {iconTheme} class="h-4 w-4 text-neutral-500 flex-shrink-0" />
100
+ <div class="flex items-center gap-1 flex-1 min-w-0">
101
+ <TagStatus dot status={selectedColor} />
102
+ {@render label()}
103
+ </div>
104
+ {:else if selectedIcon || resolvedIcon}
105
+ <div class="flex items-center gap-1 flex-1 min-w-0">
106
+ {#if selectedIcon}
107
+ <Icon src={selectedIcon} {iconTheme} class="{selectedIconColor} size-4 flex-shrink-0" />
108
+ {:else if resolvedIcon}
109
+ <Icon src={resolvedIcon} {iconTheme} class="text-icon size-4 flex-shrink-0" />
110
+ {/if}
111
+ {@render label()}
112
+ </div>
113
+ {:else}
114
+ {@render label()}
89
115
  {/if}
90
-
91
- <span class="w-full pr-8 text-neutral-800 placeholder-neutral-800 text-base truncate">
92
- {selectedLabel}
93
- </span>
94
116
  </div>
95
117
  {/snippet}
96
- <DrawerContext {widthClass} {multiple} {items} onclick={handleClick} onselect={handleSelected} />
118
+ <DrawerContext
119
+ widthClass="min-w-[256px]"
120
+ {multiple}
121
+ {items}
122
+ onclick={handleClick}
123
+ onselect={handleSelected}
124
+ />
97
125
  </BaseDropdown>
98
126
 
99
127
  <style>
100
128
  .dropdown-select {
101
- background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3QgeD0iMiIgeT0iMiIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiByeD0iNCIgZmlsbD0iI0YzRjRGNiIvPgo8cGF0aCBkPSJNNi41IDguMjUwMDRMMTAgMTEuNzVMMTMuNSA4LjI1IiBzdHJva2U9IiMwMzA3MTIiIHN0cm9rZS13aWR0aD0iMS4xIi8+Cjwvc3ZnPg==');
129
+ background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiByeD0iNCIgZmlsbD0icmdiYSg1LCA1LCAzNiwgMC4wNikiLz4KPHBhdGggZD0iTTQuNSA2LjVMOCAxMEwxMS41IDYuNSIgc3Ryb2tlPSIjMGIwYjEwIiBzdHJva2Utd2lkdGg9IjEuMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPg==');
102
130
  background-repeat: no-repeat;
103
131
  background-position: center right 8px;
104
132
  }
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ import IconEmpty from './svg/IconEmpty.svelte'
3
+ import CheckBadge from './svg/CheckBadge.svelte'
4
+ import { Icon } from '@steeze-ui/svelte-icon'
5
+ import type { EmptyStateProps } from './types'
6
+
7
+ let {
8
+ icon,
9
+ iconSource,
10
+ title = '',
11
+ description = '',
12
+ check = false,
13
+ children
14
+ }: EmptyStateProps = $props()
15
+ </script>
16
+
17
+ <div class="flex flex-col items-center justify-center">
18
+ {#if icon}
19
+ <div class="relative h-[120px] w-[352px] max-w-sm flex items-center justify-center">
20
+ {@render icon()}
21
+ </div>
22
+ {:else if iconSource}
23
+ <div class="relative h-[120px] w-[352px] max-w-sm">
24
+ <IconEmpty />
25
+ <div class="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2">
26
+ <Icon src={iconSource} class="size-10 text-icon-accent" />
27
+ {#if check}
28
+ <CheckBadge />
29
+ {/if}
30
+ </div>
31
+ </div>
32
+ {/if}
33
+ <div class="flex flex-col items-center gap-0.5 text-center">
34
+ <h4 class="font-medium text-foreground text-base">{title}</h4>
35
+ <p class="text-foreground-default-secondary text-base">{description}</p>
36
+ </div>
37
+ {#if children}
38
+ <div class="mt-4">
39
+ {@render children()}
40
+ </div>
41
+ {/if}
42
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { EmptyStateProps } from './types';
2
+ declare const EmptyState: import("svelte").Component<EmptyStateProps, {}, "">;
3
+ type EmptyState = ReturnType<typeof EmptyState>;
4
+ export default EmptyState;