@juspay/svelte-ui-components 2.9.0 → 2.11.0

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 (189) hide show
  1. package/README.md +337 -77
  2. package/dist/Accordion/Accordion.svelte +4 -2
  3. package/dist/Accordion/Accordion.svelte.d.ts +2 -4
  4. package/dist/Accordion/properties.d.ts +6 -0
  5. package/dist/Accordion/properties.js +1 -0
  6. package/dist/Animations/ModalAnimation.svelte +11 -9
  7. package/dist/Avatar/Avatar.svelte +122 -0
  8. package/dist/Avatar/Avatar.svelte.d.ts +4 -0
  9. package/dist/Avatar/properties.d.ts +15 -0
  10. package/dist/Avatar/properties.js +1 -0
  11. package/dist/Badge/Badge.svelte +2 -2
  12. package/dist/Badge/properties.d.ts +1 -0
  13. package/dist/Banner/Banner.svelte +132 -48
  14. package/dist/Banner/Banner.svelte.d.ts +1 -1
  15. package/dist/Banner/properties.d.ts +8 -3
  16. package/dist/Book/Book.svelte +281 -0
  17. package/dist/Book/Book.svelte.d.ts +4 -0
  18. package/dist/Book/properties.d.ts +24 -0
  19. package/dist/Book/properties.js +1 -0
  20. package/dist/BrandLoader/BrandLoader.svelte +3 -3
  21. package/dist/BrandLoader/properties.d.ts +1 -0
  22. package/dist/Browser/Browser.svelte +193 -0
  23. package/dist/Browser/Browser.svelte.d.ts +3 -0
  24. package/dist/Browser/properties.d.ts +16 -0
  25. package/dist/Browser/properties.js +1 -0
  26. package/dist/Button/Button.svelte +20 -7
  27. package/dist/Button/properties.d.ts +7 -4
  28. package/dist/Calendar/Calendar.svelte +476 -0
  29. package/dist/Calendar/Calendar.svelte.d.ts +4 -0
  30. package/dist/Calendar/properties.d.ts +30 -0
  31. package/dist/Calendar/properties.js +1 -0
  32. package/dist/Carousel/Carousel.svelte +19 -16
  33. package/dist/Carousel/properties.d.ts +1 -0
  34. package/dist/CheckListItem/CheckListItem.svelte +31 -26
  35. package/dist/CheckListItem/properties.d.ts +4 -1
  36. package/dist/Checkbox/Checkbox.svelte +157 -0
  37. package/dist/Checkbox/Checkbox.svelte.d.ts +4 -0
  38. package/dist/Checkbox/properties.d.ts +17 -0
  39. package/dist/Checkbox/properties.js +1 -0
  40. package/dist/Choicebox/Choicebox.svelte +85 -0
  41. package/dist/Choicebox/Choicebox.svelte.d.ts +4 -0
  42. package/dist/Choicebox/properties.d.ts +14 -0
  43. package/dist/Choicebox/properties.js +1 -0
  44. package/dist/CommandMenu/CommandMenu.svelte +452 -0
  45. package/dist/CommandMenu/CommandMenu.svelte.d.ts +4 -0
  46. package/dist/CommandMenu/properties.d.ts +26 -0
  47. package/dist/CommandMenu/properties.js +1 -0
  48. package/dist/ContextMenu/ContextMenu.svelte +308 -0
  49. package/dist/ContextMenu/ContextMenu.svelte.d.ts +4 -0
  50. package/dist/ContextMenu/properties.d.ts +26 -0
  51. package/dist/ContextMenu/properties.js +1 -0
  52. package/dist/Gauge/Gauge.svelte +70 -0
  53. package/dist/Gauge/Gauge.svelte.d.ts +4 -0
  54. package/dist/Gauge/properties.d.ts +9 -0
  55. package/dist/Gauge/properties.js +1 -0
  56. package/dist/GridItem/GridItem.svelte +5 -4
  57. package/dist/GridItem/properties.d.ts +1 -0
  58. package/dist/Icon/Icon.svelte +3 -3
  59. package/dist/Icon/properties.d.ts +1 -0
  60. package/dist/IconStack/IconStack.svelte +3 -3
  61. package/dist/IconStack/properties.d.ts +1 -0
  62. package/dist/Img/Img.svelte +5 -3
  63. package/dist/Img/properties.d.ts +5 -1
  64. package/dist/Input/Input.svelte +12 -6
  65. package/dist/Input/properties.d.ts +1 -0
  66. package/dist/InputButton/InputButton.svelte +4 -3
  67. package/dist/InputButton/properties.d.ts +5 -6
  68. package/dist/KeyboardInput/KeyboardInput.svelte +93 -0
  69. package/dist/KeyboardInput/KeyboardInput.svelte.d.ts +4 -0
  70. package/dist/KeyboardInput/properties.d.ts +12 -0
  71. package/dist/KeyboardInput/properties.js +1 -0
  72. package/dist/ListItem/ListItem.svelte +31 -28
  73. package/dist/ListItem/properties.d.ts +1 -0
  74. package/dist/Loader/Loader.svelte +10 -6
  75. package/dist/Loader/Loader.svelte.d.ts +3 -25
  76. package/dist/Loader/properties.d.ts +3 -0
  77. package/dist/Loader/properties.js +1 -0
  78. package/dist/LoadingDots/LoadingDots.svelte +64 -0
  79. package/dist/LoadingDots/LoadingDots.svelte.d.ts +3 -0
  80. package/dist/LoadingDots/properties.d.ts +8 -0
  81. package/dist/LoadingDots/properties.js +1 -0
  82. package/dist/Menu/Menu.svelte +349 -0
  83. package/dist/Menu/Menu.svelte.d.ts +4 -0
  84. package/dist/Menu/properties.d.ts +24 -0
  85. package/dist/Menu/properties.js +1 -0
  86. package/dist/Modal/Modal.svelte +10 -9
  87. package/dist/Modal/properties.d.ts +1 -0
  88. package/dist/Pagination/Pagination.svelte +152 -0
  89. package/dist/Pagination/Pagination.svelte.d.ts +4 -0
  90. package/dist/Pagination/properties.d.ts +14 -0
  91. package/dist/Pagination/properties.js +1 -0
  92. package/dist/Phone/Phone.svelte +234 -0
  93. package/dist/Phone/Phone.svelte.d.ts +3 -0
  94. package/dist/Phone/properties.d.ts +11 -0
  95. package/dist/Phone/properties.js +1 -0
  96. package/dist/Pill/Pill.svelte +130 -0
  97. package/dist/Pill/Pill.svelte.d.ts +4 -0
  98. package/dist/Pill/properties.d.ts +16 -0
  99. package/dist/Pill/properties.js +1 -0
  100. package/dist/Progress/Progress.svelte +68 -0
  101. package/dist/Progress/Progress.svelte.d.ts +4 -0
  102. package/dist/Progress/properties.d.ts +10 -0
  103. package/dist/Progress/properties.js +1 -0
  104. package/dist/Radio/Radio.svelte +128 -0
  105. package/dist/Radio/Radio.svelte.d.ts +4 -0
  106. package/dist/Radio/properties.d.ts +15 -0
  107. package/dist/Radio/properties.js +1 -0
  108. package/dist/RelativeTime/RelativeTime.svelte +117 -0
  109. package/dist/RelativeTime/RelativeTime.svelte.d.ts +4 -0
  110. package/dist/RelativeTime/properties.d.ts +13 -0
  111. package/dist/RelativeTime/properties.js +1 -0
  112. package/dist/Scroller/Scroller.svelte +389 -0
  113. package/dist/Scroller/Scroller.svelte.d.ts +4 -0
  114. package/dist/Scroller/properties.d.ts +30 -0
  115. package/dist/Scroller/properties.js +1 -0
  116. package/dist/Select/Select.svelte +382 -344
  117. package/dist/Select/Select.svelte.d.ts +1 -1
  118. package/dist/Select/properties.d.ts +16 -26
  119. package/dist/Sheet/Sheet.svelte +264 -0
  120. package/dist/Sheet/Sheet.svelte.d.ts +4 -0
  121. package/dist/Sheet/properties.d.ts +19 -0
  122. package/dist/Sheet/properties.js +1 -0
  123. package/dist/Shimmer/Shimmer.svelte +44 -0
  124. package/dist/Shimmer/Shimmer.svelte.d.ts +4 -0
  125. package/dist/Shimmer/properties.d.ts +4 -0
  126. package/dist/Shimmer/properties.js +1 -0
  127. package/dist/Slider/Slider.svelte +144 -0
  128. package/dist/Slider/Slider.svelte.d.ts +4 -0
  129. package/dist/Slider/properties.d.ts +17 -0
  130. package/dist/Slider/properties.js +1 -0
  131. package/dist/Snippet/Snippet.svelte +123 -0
  132. package/dist/Snippet/Snippet.svelte.d.ts +4 -0
  133. package/dist/Snippet/properties.d.ts +15 -0
  134. package/dist/Snippet/properties.js +1 -0
  135. package/dist/SplitButton/SplitButton.svelte +135 -0
  136. package/dist/SplitButton/SplitButton.svelte.d.ts +4 -0
  137. package/dist/SplitButton/properties.d.ts +17 -0
  138. package/dist/SplitButton/properties.js +1 -0
  139. package/dist/Status/Status.svelte +5 -3
  140. package/dist/Status/properties.d.ts +1 -0
  141. package/dist/Stepper/Step.svelte +3 -3
  142. package/dist/Stepper/Stepper.svelte +3 -3
  143. package/dist/Stepper/properties.d.ts +2 -0
  144. package/dist/Table/Table.svelte +15 -8
  145. package/dist/Table/properties.d.ts +1 -0
  146. package/dist/Tabs/Tabs.svelte +240 -0
  147. package/dist/Tabs/Tabs.svelte.d.ts +4 -0
  148. package/dist/Tabs/properties.d.ts +16 -0
  149. package/dist/Tabs/properties.js +1 -0
  150. package/dist/ThemeSwitcher/ThemeSwitcher.svelte +249 -0
  151. package/dist/ThemeSwitcher/ThemeSwitcher.svelte.d.ts +4 -0
  152. package/dist/ThemeSwitcher/properties.d.ts +19 -0
  153. package/dist/ThemeSwitcher/properties.js +1 -0
  154. package/dist/Toast/Toast.svelte +25 -30
  155. package/dist/Toast/properties.d.ts +1 -0
  156. package/dist/Toggle/Toggle.svelte +2 -2
  157. package/dist/Toggle/properties.d.ts +1 -0
  158. package/dist/Toolbar/Toolbar.svelte +8 -7
  159. package/dist/Toolbar/properties.d.ts +1 -0
  160. package/dist/Tooltip/Tooltip.svelte +153 -0
  161. package/dist/Tooltip/Tooltip.svelte.d.ts +4 -0
  162. package/dist/Tooltip/properties.d.ts +13 -0
  163. package/dist/Tooltip/properties.js +1 -0
  164. package/dist/assets/battery.svg +5 -0
  165. package/dist/assets/checkmark.svg +3 -0
  166. package/dist/assets/chevron-down-sm.svg +3 -0
  167. package/dist/assets/chevron-down.svg +3 -0
  168. package/dist/assets/chevron-left-lg.svg +3 -0
  169. package/dist/assets/chevron-left.svg +3 -0
  170. package/dist/assets/chevron-right-lg.svg +3 -0
  171. package/dist/assets/chevron-right.svg +3 -0
  172. package/dist/assets/chevron-up.svg +3 -0
  173. package/dist/assets/close.svg +4 -0
  174. package/dist/assets/copy.svg +4 -0
  175. package/dist/assets/error-circle.svg +5 -0
  176. package/dist/assets/lock.svg +3 -0
  177. package/dist/assets/minus.svg +3 -0
  178. package/dist/assets/monitor.svg +5 -0
  179. package/dist/assets/moon.svg +3 -0
  180. package/dist/assets/palette.svg +7 -0
  181. package/dist/assets/search.svg +4 -0
  182. package/dist/assets/signal.svg +6 -0
  183. package/dist/assets/sun.svg +11 -0
  184. package/dist/assets/wifi.svg +3 -0
  185. package/dist/index.d.ts +55 -0
  186. package/dist/index.js +27 -0
  187. package/dist/utils.d.ts +2 -0
  188. package/dist/utils.js +18 -4
  189. package/package.json +26 -1
@@ -0,0 +1,349 @@
1
+ <script lang="ts">
2
+ import { tick, onMount } from 'svelte';
3
+ import { SvelteMap } from 'svelte/reactivity';
4
+ import Img from '../Img/Img.svelte';
5
+ import type { MenuProperties, MenuItem } from './properties';
6
+
7
+ let {
8
+ items,
9
+ open = $bindable(false),
10
+ testId,
11
+ trigger,
12
+ onselect,
13
+ onopen,
14
+ onclose,
15
+ classes
16
+ }: MenuProperties = $props();
17
+
18
+ let menuContainerEl: HTMLDivElement | null = $state(null);
19
+ let menuListEl: HTMLDivElement | null = $state(null);
20
+ let triggerEl: HTMLDivElement | null = $state(null);
21
+ let focusedIndex: number = $state(-1);
22
+ let typeaheadQuery: string = $state('');
23
+ let typeaheadTimer: ReturnType<typeof setTimeout> | null = $state(null);
24
+
25
+ let selectableItems: MenuItem[] = $derived(
26
+ items.filter((item) => item.separator !== true && item.disabled !== true)
27
+ );
28
+
29
+ let selectableIndexMap: SvelteMap<MenuItem, number> = $derived(
30
+ new SvelteMap(selectableItems.map((item, i) => [item, i]))
31
+ );
32
+
33
+ function toggle() {
34
+ if (open) {
35
+ close();
36
+ } else {
37
+ openMenu();
38
+ }
39
+ }
40
+
41
+ function openMenu(startIndex: number = 0) {
42
+ open = true;
43
+ focusedIndex = startIndex;
44
+ onopen?.();
45
+ tick().then(() => {
46
+ focusItem(startIndex);
47
+ });
48
+ }
49
+
50
+ function close() {
51
+ open = false;
52
+ focusedIndex = -1;
53
+ typeaheadQuery = '';
54
+ onclose?.();
55
+ if (triggerEl !== null) {
56
+ triggerEl.focus({ preventScroll: true });
57
+ }
58
+ }
59
+
60
+ function selectItem(item: MenuItem) {
61
+ if (item.disabled === true) {
62
+ return;
63
+ }
64
+ onselect?.(item);
65
+ close();
66
+ }
67
+
68
+ function focusItem(index: number) {
69
+ if (menuListEl === null) {
70
+ return;
71
+ }
72
+ const focusableItems = menuListEl.querySelectorAll(
73
+ '[role="menuitem"]:not([aria-disabled="true"])'
74
+ );
75
+ const item = focusableItems.item(index);
76
+ if (index >= 0 && index < focusableItems.length && item instanceof HTMLElement) {
77
+ item.focus();
78
+ }
79
+ }
80
+
81
+ function getSelectableIndex(item: MenuItem): number {
82
+ return selectableIndexMap.get(item) ?? -1;
83
+ }
84
+
85
+ function handleTriggerKeydown(event: KeyboardEvent) {
86
+ if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {
87
+ event.preventDefault();
88
+ openMenu();
89
+ } else if (event.key === 'ArrowUp') {
90
+ event.preventDefault();
91
+ openMenu(selectableItems.length - 1);
92
+ }
93
+ }
94
+
95
+ function handleMenuKeydown(event: KeyboardEvent) {
96
+ switch (event.key) {
97
+ case 'ArrowDown': {
98
+ event.preventDefault();
99
+ const next = focusedIndex + 1;
100
+ focusedIndex = next >= selectableItems.length ? 0 : next;
101
+ focusItem(focusedIndex);
102
+ break;
103
+ }
104
+ case 'ArrowUp': {
105
+ event.preventDefault();
106
+ const prev = focusedIndex - 1;
107
+ focusedIndex = prev < 0 ? selectableItems.length - 1 : prev;
108
+ focusItem(focusedIndex);
109
+ break;
110
+ }
111
+ case 'Home': {
112
+ event.preventDefault();
113
+ focusedIndex = 0;
114
+ focusItem(focusedIndex);
115
+ break;
116
+ }
117
+ case 'End': {
118
+ event.preventDefault();
119
+ focusedIndex = selectableItems.length - 1;
120
+ focusItem(focusedIndex);
121
+ break;
122
+ }
123
+ case 'Enter':
124
+ case ' ': {
125
+ event.preventDefault();
126
+ const selected = selectableItems.at(focusedIndex);
127
+ if (focusedIndex >= 0 && typeof selected !== 'undefined') {
128
+ selectItem(selected);
129
+ }
130
+ break;
131
+ }
132
+ case 'Escape': {
133
+ event.preventDefault();
134
+ close();
135
+ break;
136
+ }
137
+ case 'Tab': {
138
+ close();
139
+ break;
140
+ }
141
+ default: {
142
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
143
+ event.preventDefault();
144
+ handleTypeahead(event.key);
145
+ }
146
+ break;
147
+ }
148
+ }
149
+ }
150
+
151
+ function handleTypeahead(char: string) {
152
+ if (typeaheadTimer !== null) {
153
+ clearTimeout(typeaheadTimer);
154
+ }
155
+ typeaheadQuery += char.toLowerCase();
156
+ typeaheadTimer = setTimeout(() => {
157
+ typeaheadQuery = '';
158
+ }, 500);
159
+
160
+ const matchIndex = selectableItems.findIndex((item) =>
161
+ item.label.toLowerCase().startsWith(typeaheadQuery)
162
+ );
163
+ if (matchIndex >= 0) {
164
+ focusedIndex = matchIndex;
165
+ focusItem(focusedIndex);
166
+ }
167
+ }
168
+
169
+ function handleClickOutside(event: Event) {
170
+ if (
171
+ open &&
172
+ event.target instanceof Node &&
173
+ menuContainerEl !== null &&
174
+ !menuContainerEl.contains(event.target)
175
+ ) {
176
+ close();
177
+ }
178
+ }
179
+
180
+ onMount(() => {
181
+ document.addEventListener('click', handleClickOutside);
182
+ return () => {
183
+ document.removeEventListener('click', handleClickOutside);
184
+ if (typeaheadTimer !== null) {
185
+ clearTimeout(typeaheadTimer);
186
+ }
187
+ };
188
+ });
189
+ </script>
190
+
191
+ <div
192
+ class="menu-container {classes ?? ''}"
193
+ bind:this={menuContainerEl}
194
+ data-pw={typeof testId === 'string' ? testId : null}
195
+ >
196
+ <div
197
+ class="menu-trigger"
198
+ bind:this={triggerEl}
199
+ onclick={toggle}
200
+ onkeydown={handleTriggerKeydown}
201
+ role="button"
202
+ tabindex="0"
203
+ aria-haspopup="menu"
204
+ aria-expanded={open}
205
+ >
206
+ {#if typeof trigger === 'function'}
207
+ {@render trigger()}
208
+ {/if}
209
+ </div>
210
+
211
+ {#if open}
212
+ <div
213
+ class="menu-dropdown"
214
+ bind:this={menuListEl}
215
+ role="menu"
216
+ tabindex="-1"
217
+ onkeydown={handleMenuKeydown}
218
+ >
219
+ {#each items as item (item.value)}
220
+ {#if item.separator === true}
221
+ <div class="menu-separator" role="separator"></div>
222
+ {:else}
223
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
224
+ <div
225
+ class="menu-item"
226
+ class:menu-item-danger={item.danger === true}
227
+ class:menu-item-disabled={item.disabled === true}
228
+ role="menuitem"
229
+ tabindex={item.disabled === true ? -1 : 0}
230
+ aria-disabled={item.disabled === true ? 'true' : null}
231
+ onclick={() => selectItem(item)}
232
+ onfocus={() => {
233
+ if (item.disabled !== true) {
234
+ focusedIndex = getSelectableIndex(item);
235
+ }
236
+ }}
237
+ data-pw={typeof testId === 'string' ? `${testId}-item-${item.value}` : null}
238
+ >
239
+ {#if typeof item.icon === 'string'}
240
+ <span class="menu-item-icon">
241
+ <Img src={item.icon} alt="" />
242
+ </span>
243
+ {/if}
244
+ <span class="menu-item-label">{item.label}</span>
245
+ </div>
246
+ {/if}
247
+ {/each}
248
+ </div>
249
+ {/if}
250
+ </div>
251
+
252
+ <style>
253
+ .menu-container {
254
+ position: var(--menu-container-position, relative);
255
+ display: var(--menu-container-display, inline-block);
256
+ font-family: var(--menu-font-family, inherit);
257
+ font-size: var(--menu-font-size, 14px);
258
+ }
259
+
260
+ .menu-trigger {
261
+ cursor: pointer;
262
+ -webkit-tap-highlight-color: transparent;
263
+ }
264
+
265
+ .menu-trigger:focus {
266
+ outline: var(--menu-trigger-focus-outline, none);
267
+ }
268
+
269
+ .menu-dropdown {
270
+ position: absolute;
271
+ z-index: var(--menu-z-index, 10);
272
+ top: var(--menu-dropdown-top, 100%);
273
+ left: var(--menu-dropdown-left, 0);
274
+ background-color: var(--menu-background-color, #ffffff);
275
+ border: var(--menu-border, 1px solid #e0e0e0);
276
+ border-radius: var(--menu-border-radius, 6px);
277
+ box-shadow: var(--menu-box-shadow, 0px 4px 16px rgba(0, 0, 0, 0.12));
278
+ min-width: var(--menu-min-width, 160px);
279
+ max-height: var(--menu-max-height, 240px);
280
+ overflow-y: auto;
281
+ padding: var(--menu-padding, 4px 0);
282
+ margin: var(--menu-margin, 4px 0);
283
+ }
284
+
285
+ .menu-item {
286
+ display: flex;
287
+ align-items: center;
288
+ padding: var(--menu-item-padding, 8px 12px);
289
+ cursor: pointer;
290
+ color: var(--menu-item-color, #333333);
291
+ background-color: var(--menu-item-background-color, transparent);
292
+ gap: var(--menu-item-gap, 8px);
293
+ white-space: var(--menu-item-white-space, nowrap);
294
+ -webkit-tap-highlight-color: transparent;
295
+ }
296
+
297
+ .menu-item:hover {
298
+ background-color: var(--menu-item-hover-background-color, #f5f5f5);
299
+ color: var(--menu-item-hover-color, var(--menu-item-color, #333333));
300
+ }
301
+
302
+ .menu-item:focus {
303
+ background-color: var(--menu-item-focus-background-color, #f0f0f0);
304
+ outline: var(--menu-item-focus-outline, none);
305
+ }
306
+
307
+ .menu-item-danger {
308
+ color: var(--menu-item-danger-color, #dc3545);
309
+ }
310
+
311
+ .menu-item-danger:hover {
312
+ background-color: var(--menu-item-danger-hover-background-color, #fff0f0);
313
+ color: var(--menu-item-danger-hover-color, var(--menu-item-danger-color, #dc3545));
314
+ }
315
+
316
+ .menu-item-danger:focus {
317
+ background-color: var(--menu-item-danger-focus-background-color, #fff0f0);
318
+ }
319
+
320
+ .menu-item-disabled {
321
+ opacity: var(--menu-item-disabled-opacity, 0.4);
322
+ cursor: var(--menu-item-disabled-cursor, not-allowed);
323
+ pointer-events: none;
324
+ }
325
+
326
+ .menu-separator {
327
+ height: var(--menu-separator-height, 1px);
328
+ background-color: var(--menu-separator-color, #e0e0e0);
329
+ margin: var(--menu-separator-margin, 4px 0);
330
+ }
331
+
332
+ .menu-item-icon {
333
+ display: inline-flex;
334
+ height: var(--menu-item-icon-height, 16px);
335
+ width: var(--menu-item-icon-width, 16px);
336
+ flex-shrink: 0;
337
+ }
338
+
339
+ .menu-item-icon :global(img) {
340
+ width: 100%;
341
+ height: 100%;
342
+ }
343
+
344
+ .menu-item-label {
345
+ flex: 1;
346
+ font-weight: var(--menu-item-font-weight, 400);
347
+ line-height: var(--menu-item-line-height, 1.4);
348
+ }
349
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { MenuProperties } from './properties';
2
+ declare const Menu: import("svelte").Component<MenuProperties, {}, "open">;
3
+ type Menu = ReturnType<typeof Menu>;
4
+ export default Menu;
@@ -0,0 +1,24 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type MenuItem = {
3
+ label: string;
4
+ value: string;
5
+ icon?: string;
6
+ disabled?: boolean;
7
+ danger?: boolean;
8
+ separator?: boolean;
9
+ };
10
+ export type MenuProperties = MandatoryMenuProperties & OptionalMenuProperties & MenuEventProperties;
11
+ export type MandatoryMenuProperties = {
12
+ items: MenuItem[];
13
+ };
14
+ export type OptionalMenuProperties = {
15
+ open?: boolean;
16
+ testId?: string;
17
+ trigger?: Snippet;
18
+ classes?: string;
19
+ };
20
+ export type MenuEventProperties = {
21
+ onselect?: (item: MenuItem) => void;
22
+ onopen?: () => void;
23
+ onclose?: () => void;
24
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -6,7 +6,7 @@
6
6
  import { createDebouncer } from '../utils';
7
7
  import Button from '../Button/Button.svelte';
8
8
 
9
- let overlayDiv: HTMLDivElement | undefined = $state();
9
+ let overlayDiv: HTMLDivElement | null = $state(null);
10
10
  let backPressed = false;
11
11
 
12
12
  let {
@@ -16,7 +16,7 @@
16
16
  supportHardwareBackPress = false,
17
17
  enableTransition = true,
18
18
  transitionType = 'ALL',
19
- header = { leftImage: undefined, rightImage: undefined, text: undefined },
19
+ header = {},
20
20
  footer,
21
21
  debounceTime = 700,
22
22
  leftImageTestId,
@@ -29,7 +29,8 @@
29
29
  onprimaryButtonClick,
30
30
  onsecondaryButtonClick,
31
31
  onoverlayClick,
32
- onkeydown
32
+ onkeydown,
33
+ classes
33
34
  }: ModalProperties = $props();
34
35
 
35
36
  const debounce = createDebouncer(debounceTime);
@@ -94,11 +95,11 @@
94
95
 
95
96
  <svelte:window onkeydown={handleKeyDown} />
96
97
 
97
- {#if content}
98
+ {#if typeof content === 'function'}
98
99
  <OverlayAnimation>
99
100
  <div
100
101
  bind:this={overlayDiv}
101
- class="modal {align} {showOverlay ? 'overlay-active' : 'overlay-inactive'}"
102
+ class="modal {align} {showOverlay ? 'overlay-active' : 'overlay-inactive'} {classes ?? ''}"
102
103
  onclick={handleOverlayClick}
103
104
  {onkeydown}
104
105
  role="button"
@@ -107,7 +108,7 @@
107
108
  >
108
109
  <ModalAnimation enable={enableTransition} {align} {transitionType}>
109
110
  <div class="modal-content {size}">
110
- {#if header?.leftImage || header?.text || header?.rightImage}
111
+ {#if (typeof header?.leftImage === 'string' && header.leftImage.length > 0) || (typeof header?.text === 'string' && header.text.length > 0) || (typeof header?.rightImage === 'string' && header.rightImage.length > 0)}
111
112
  <div class="header">
112
113
  {#if header.leftImage}
113
114
  <div
@@ -141,11 +142,11 @@
141
142
  <div class="slot-content">
142
143
  {@render content?.()}
143
144
  </div>
144
- {#if footerSnippet}
145
+ {#if typeof footerSnippet === 'function'}
145
146
  <div class="footer-content">
146
147
  {@render footerSnippet?.()}
147
148
  </div>
148
- {:else if footer?.primaryButton || footer?.secondaryButton}
149
+ {:else if typeof footer?.primaryButton === 'object' || typeof footer?.secondaryButton === 'object'}
149
150
  <div class="footer-content">
150
151
  <div class="footer-action-buttons">
151
152
  {#if footer.secondaryButton}
@@ -268,7 +269,7 @@
268
269
  .footer-action-buttons {
269
270
  display: flex;
270
271
  gap: var(--modal-footer-gap, 0px);
271
- width: var(--modal-footer-action-buttons-width, auto);
272
+ width: var(--modal-footer-action-buttons-width, fit-content);
272
273
  }
273
274
 
274
275
  .footer-secondary-button {
@@ -26,6 +26,7 @@ export type ModalProperties = ModalEventProperties & {
26
26
  testId?: string;
27
27
  content?: Snippet;
28
28
  footerSnippet?: Snippet;
29
+ classes?: string;
29
30
  };
30
31
  export type ModalEventProperties = {
31
32
  onclose?: () => void;
@@ -0,0 +1,152 @@
1
+ <script lang="ts">
2
+ import type { PaginationProperties } from './properties';
3
+
4
+ let {
5
+ totalPages,
6
+ currentPage = $bindable(1),
7
+ siblingCount = 1,
8
+ disabled = false,
9
+ testId,
10
+ onchange,
11
+ classes
12
+ }: PaginationProperties = $props();
13
+
14
+ function generatePages(total: number, current: number, siblings: number): (number | '...')[] {
15
+ const pages: (number | '...')[] = [];
16
+ pages.push(1);
17
+
18
+ const leftSibling = Math.max(2, current - siblings);
19
+ const rightSibling = Math.min(total - 1, current + siblings);
20
+
21
+ if (leftSibling > 2) {
22
+ pages.push('...');
23
+ }
24
+
25
+ for (let i = leftSibling; i <= rightSibling; i++) {
26
+ pages.push(i);
27
+ }
28
+
29
+ if (rightSibling < total - 1) {
30
+ pages.push('...');
31
+ }
32
+
33
+ if (total > 1) {
34
+ pages.push(total);
35
+ }
36
+
37
+ return pages;
38
+ }
39
+
40
+ let pages = $derived(generatePages(totalPages, currentPage, siblingCount));
41
+
42
+ function goToPage(page: number): void {
43
+ if (disabled || page < 1 || page > totalPages || page === currentPage) {
44
+ return;
45
+ }
46
+ currentPage = page;
47
+ onchange?.(page);
48
+ }
49
+ </script>
50
+
51
+ <nav
52
+ class="pagination {classes ?? ''}"
53
+ class:disabled
54
+ data-pw={typeof testId === 'string' ? testId : null}
55
+ >
56
+ <button
57
+ class="page-button prev-button"
58
+ disabled={disabled || currentPage <= 1}
59
+ onclick={() => goToPage(currentPage - 1)}
60
+ aria-label="Previous page"
61
+ >
62
+ &#8249;
63
+ </button>
64
+
65
+ {#each pages as page, i (i)}
66
+ {#if page === '...'}
67
+ <span class="ellipsis">&#8230;</span>
68
+ {:else}
69
+ <button
70
+ class="page-button"
71
+ class:active={page === currentPage}
72
+ {disabled}
73
+ onclick={() => goToPage(page)}
74
+ aria-label="Page {page}"
75
+ aria-current={page === currentPage ? 'page' : null}
76
+ >
77
+ {page}
78
+ </button>
79
+ {/if}
80
+ {/each}
81
+
82
+ <button
83
+ class="page-button next-button"
84
+ disabled={disabled || currentPage >= totalPages}
85
+ onclick={() => goToPage(currentPage + 1)}
86
+ aria-label="Next page"
87
+ >
88
+ &#8250;
89
+ </button>
90
+ </nav>
91
+
92
+ <style>
93
+ .pagination {
94
+ display: var(--pagination-display, flex);
95
+ gap: var(--pagination-gap, 4px);
96
+ align-items: var(--pagination-align-items, center);
97
+ }
98
+
99
+ .pagination.disabled {
100
+ opacity: var(--pagination-disabled-opacity, 0.5);
101
+ cursor: var(--pagination-disabled-cursor, not-allowed);
102
+ }
103
+
104
+ .page-button {
105
+ padding: var(--pagination-button-padding, 6px 10px);
106
+ font-size: var(--pagination-button-font-size, 14px);
107
+ font-weight: var(--pagination-button-font-weight, 400);
108
+ font-family: var(--pagination-button-font-family, inherit);
109
+ color: var(--pagination-button-color, #3a4550);
110
+ background: var(--pagination-button-background, transparent);
111
+ border: var(--pagination-button-border, 1px solid #d1d5db);
112
+ border-radius: var(--pagination-button-border-radius, 4px);
113
+ cursor: var(--pagination-button-cursor, pointer);
114
+ min-width: var(--pagination-button-min-width, 36px);
115
+ height: var(--pagination-button-height, 36px);
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ transition: var(--pagination-transition, background 0.15s ease, color 0.15s ease);
120
+ }
121
+
122
+ .page-button:hover:not(:disabled):not(.active) {
123
+ color: var(--pagination-button-hover-color, #111827);
124
+ background: var(--pagination-button-hover-background, #f3f4f6);
125
+ }
126
+
127
+ .page-button.active {
128
+ color: var(--pagination-active-color, #ffffff);
129
+ background: var(--pagination-active-background, #3a4550);
130
+ border: var(--pagination-active-border, 1px solid #3a4550);
131
+ font-weight: var(--pagination-active-font-weight, 600);
132
+ }
133
+
134
+ .page-button:disabled {
135
+ opacity: var(--pagination-disabled-opacity, 0.5);
136
+ cursor: var(--pagination-disabled-cursor, not-allowed);
137
+ }
138
+
139
+ .pagination.disabled .page-button {
140
+ opacity: 1;
141
+ }
142
+
143
+ .ellipsis {
144
+ color: var(--pagination-ellipsis-color, #6b7280);
145
+ font-size: var(--pagination-ellipsis-font-size, 14px);
146
+ min-width: var(--pagination-button-min-width, 36px);
147
+ height: var(--pagination-button-height, 36px);
148
+ display: flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ }
152
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { PaginationProperties } from './properties';
2
+ declare const Pagination: import("svelte").Component<PaginationProperties, {}, "currentPage">;
3
+ type Pagination = ReturnType<typeof Pagination>;
4
+ export default Pagination;
@@ -0,0 +1,14 @@
1
+ export type PaginationProperties = MandatoryPaginationProperties & OptionalPaginationProperties & PaginationEventProperties;
2
+ export type MandatoryPaginationProperties = {
3
+ totalPages: number;
4
+ };
5
+ export type OptionalPaginationProperties = {
6
+ currentPage?: number;
7
+ siblingCount?: number;
8
+ disabled?: boolean;
9
+ testId?: string;
10
+ classes?: string;
11
+ };
12
+ export type PaginationEventProperties = {
13
+ onchange?: (page: number) => void;
14
+ };
@@ -0,0 +1 @@
1
+ export {};