@flightlesslabs/dodo-ui 0.7.2 → 0.9.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 (138) hide show
  1. package/dist/index.d.ts +18 -2
  2. package/dist/index.js +11 -0
  3. package/dist/stories/components/Form/NumericInput/Events/Events.stories.svelte +126 -0
  4. package/dist/stories/components/Form/NumericInput/Events/Events.stories.svelte.d.ts +18 -0
  5. package/dist/stories/components/Form/NumericInput/NumericInput.stories.svelte +79 -0
  6. package/dist/stories/components/Form/NumericInput/NumericInput.stories.svelte.d.ts +21 -0
  7. package/dist/stories/components/Form/NumericInput/NumericInput.svelte +161 -0
  8. package/dist/stories/components/Form/NumericInput/NumericInput.svelte.d.ts +69 -0
  9. package/dist/stories/components/Form/NumericInput/Validation/Validation.stories.svelte +84 -0
  10. package/dist/stories/components/Form/NumericInput/Validation/Validation.stories.svelte.d.ts +18 -0
  11. package/dist/stories/components/Form/PasswordInput/Events/Events.stories.svelte +27 -6
  12. package/dist/stories/components/Form/PasswordInput/PasswordInput.svelte +5 -3
  13. package/dist/stories/components/Form/PasswordInput/PasswordInput.svelte.d.ts +7 -1
  14. package/dist/stories/components/Form/Select/Customize/Customize.stories.svelte +16 -1
  15. package/dist/stories/components/Form/Select/DropDownArrow/DropDownArrow.stories.svelte +59 -0
  16. package/dist/stories/components/Form/Select/DropDownArrow/DropDownArrow.stories.svelte.d.ts +18 -0
  17. package/dist/stories/components/Form/Select/Events/Events.stories.svelte +27 -0
  18. package/dist/stories/components/Form/Select/Positions/AutoPosition/AutoPosition.stories.svelte +54 -0
  19. package/dist/stories/components/Form/Select/Positions/AutoPosition/AutoPosition.stories.svelte.d.ts +18 -0
  20. package/dist/stories/components/Form/Select/Positions/Positions.stories.svelte +83 -0
  21. package/dist/stories/components/Form/Select/Positions/Positions.stories.svelte.d.ts +18 -0
  22. package/dist/stories/components/Form/Select/Select.svelte +110 -123
  23. package/dist/stories/components/Form/Select/Select.svelte.d.ts +22 -3
  24. package/dist/stories/components/Form/TextInput/Events/Events.stories.svelte +27 -0
  25. package/dist/stories/components/Form/TextInput/TextInput.svelte +5 -3
  26. package/dist/stories/components/Form/TextInput/TextInput.svelte.d.ts +10 -1
  27. package/dist/stories/components/Layout/Menu/DynamicMenu/Customize/Customize.stories.svelte +28 -0
  28. package/dist/stories/components/Layout/Menu/DynamicMenu/Customize/Customize.stories.svelte.d.ts +18 -0
  29. package/dist/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.stories.svelte +51 -0
  30. package/dist/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.stories.svelte.d.ts +22 -0
  31. package/dist/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.svelte +129 -0
  32. package/dist/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.svelte.d.ts +81 -0
  33. package/dist/stories/components/Layout/Menu/DynamicMenu/Events/Events.stories.svelte +46 -0
  34. package/dist/stories/components/Layout/Menu/DynamicMenu/Events/Events.stories.svelte.d.ts +18 -0
  35. package/dist/stories/components/Layout/Menu/DynamicMenu/KeybaordNavigation/KeybaordNavigation.stories.svelte +27 -0
  36. package/dist/stories/components/Layout/Menu/DynamicMenu/KeybaordNavigation/KeybaordNavigation.stories.svelte.d.ts +18 -0
  37. package/dist/stories/components/Layout/Menu/DynamicMenu/Options/OptionFormat.mdx +72 -0
  38. package/dist/stories/components/Layout/Menu/MenuItem/MenuItem.svelte.d.ts +1 -1
  39. package/dist/stories/developer tools/components/DynamicInput/DynamicInput.svelte +23 -4
  40. package/dist/stories/developer tools/components/DynamicInput/DynamicInput.svelte.d.ts +13 -2
  41. package/dist/stories/developer tools/components/DynamicInput/Events/Events.stories.svelte +115 -0
  42. package/dist/stories/developer tools/components/DynamicInput/Events/Events.stories.svelte.d.ts +18 -0
  43. package/dist/stories/developer tools/helpers/Numbers/cleanNumericString/cleanNumericString.d.ts +13 -0
  44. package/dist/stories/developer tools/helpers/Numbers/cleanNumericString/cleanNumericString.js +26 -0
  45. package/dist/stories/developer tools/helpers/Numbers/cleanNumericString/index.mdx +20 -0
  46. package/dist/stories/developer tools/helpers/Numbers/isValidNumberValue/index.mdx +71 -0
  47. package/dist/stories/developer tools/helpers/Numbers/isValidNumberValue/isValidNumberValue.d.ts +51 -0
  48. package/dist/stories/developer tools/helpers/Numbers/isValidNumberValue/isValidNumberValue.js +96 -0
  49. package/dist/stories/developer tools/helpers/logger/index.mdx +63 -0
  50. package/dist/stories/developer tools/helpers/logger/logger.d.ts +24 -0
  51. package/dist/stories/developer tools/helpers/logger/logger.js +31 -0
  52. package/package.json +15 -17
  53. package/src/lib/index.ts +33 -0
  54. package/src/lib/stories/Home.mdx +59 -0
  55. package/src/lib/stories/assets/dark-theme-toggle.png +0 -0
  56. package/src/lib/stories/components/Form/Button/Button.stories.svelte +61 -0
  57. package/src/lib/stories/components/Form/Button/Color/Color.stories.svelte +43 -0
  58. package/src/lib/stories/components/Form/Button/Events/Events.stories.svelte +36 -0
  59. package/src/lib/stories/components/Form/Button/IconButton/IconButton.stories.svelte +43 -0
  60. package/src/lib/stories/components/Form/Button/Roundness/Roundness.stories.svelte +23 -0
  61. package/src/lib/stories/components/Form/Button/Size/Size.stories.svelte +16 -0
  62. package/src/lib/stories/components/Form/Button/Variant/Variant.stories.svelte +18 -0
  63. package/src/lib/stories/components/Form/FormControl/FormControl.stories.svelte +28 -0
  64. package/src/lib/stories/components/Form/Label/Label.stories.svelte +13 -0
  65. package/src/lib/stories/components/Form/Message/Color/Color.stories.svelte +36 -0
  66. package/src/lib/stories/components/Form/Message/Message.stories.svelte +27 -0
  67. package/src/lib/stories/components/Form/Message/Size/Size.stories.svelte +22 -0
  68. package/src/lib/stories/components/Form/NumericInput/Events/Events.stories.svelte +134 -0
  69. package/src/lib/stories/components/Form/NumericInput/NumericInput.stories.svelte +84 -0
  70. package/src/lib/stories/components/Form/NumericInput/NumericInput.svelte +286 -0
  71. package/src/lib/stories/components/Form/NumericInput/Validation/Validation.stories.svelte +87 -0
  72. package/src/lib/stories/components/Form/PasswordInput/Events/Events.stories.svelte +132 -0
  73. package/src/lib/stories/components/Form/PasswordInput/PasswordInput.stories.svelte +59 -0
  74. package/src/lib/stories/components/Form/PasswordInput/PasswordInput.svelte +15 -3
  75. package/src/lib/stories/components/Form/PasswordInput/Roundness/Roundness.stories.svelte +20 -0
  76. package/src/lib/stories/components/Form/PasswordInput/Size/Size.stories.svelte +16 -0
  77. package/src/lib/stories/components/Form/PasswordInput/WithIcon/WithIcon.stories.svelte +31 -0
  78. package/src/lib/stories/components/Form/Select/Customize/Customize.stories.svelte +139 -0
  79. package/src/lib/stories/components/Form/Select/DropDownArrow/DropDownArrow.stories.svelte +63 -0
  80. package/src/lib/stories/components/Form/Select/Events/Events.stories.svelte +174 -0
  81. package/src/lib/stories/components/Form/Select/Options/OptionFormat.mdx +40 -0
  82. package/src/lib/stories/components/Form/Select/Positions/AutoPosition/AutoPosition.stories.svelte +58 -0
  83. package/src/lib/stories/components/Form/Select/Positions/Positions.stories.svelte +87 -0
  84. package/src/lib/stories/components/Form/Select/Roundness/Roundness.stories.svelte +32 -0
  85. package/src/lib/stories/components/Form/Select/Select.stories.svelte +121 -0
  86. package/src/lib/stories/components/Form/Select/Select.svelte +166 -146
  87. package/src/lib/stories/components/Form/Select/Size/Size.stories.svelte +28 -0
  88. package/src/lib/stories/components/Form/Select/WithIcon/WithIcon.stories.svelte +43 -0
  89. package/src/lib/stories/components/Form/TextInput/Events/Events.stories.svelte +125 -0
  90. package/src/lib/stories/components/Form/TextInput/Roundness/Roundness.stories.svelte +21 -0
  91. package/src/lib/stories/components/Form/TextInput/Size/Size.stories.svelte +17 -0
  92. package/src/lib/stories/components/Form/TextInput/TextInput.stories.svelte +43 -0
  93. package/src/lib/stories/components/Form/TextInput/TextInput.svelte +18 -3
  94. package/src/lib/stories/components/Form/TextInput/WithIcon/WithIcon.stories.svelte +47 -0
  95. package/src/lib/stories/components/Layout/Menu/DynamicMenu/Customize/Customize.stories.svelte +30 -0
  96. package/src/lib/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.stories.svelte +56 -0
  97. package/src/lib/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.svelte +262 -0
  98. package/src/lib/stories/components/Layout/Menu/DynamicMenu/Events/Events.stories.svelte +48 -0
  99. package/src/lib/stories/components/Layout/Menu/DynamicMenu/KeybaordNavigation/KeybaordNavigation.stories.svelte +29 -0
  100. package/src/lib/stories/components/Layout/Menu/DynamicMenu/Options/OptionFormat.mdx +72 -0
  101. package/src/lib/stories/components/Layout/Menu/Menu.stories.svelte +69 -0
  102. package/src/lib/stories/components/Layout/Menu/MenuItem/MenuItem.stories.svelte +34 -0
  103. package/src/lib/stories/components/Layout/Menu/MenuItem/MenuItem.svelte +1 -1
  104. package/src/lib/stories/components/Layout/Menu/MenuItem/Size/Size.stories.svelte +16 -0
  105. package/src/lib/stories/components/Layout/Menu/MenuItem/Type/Type.stories.svelte +21 -0
  106. package/src/lib/stories/components/Layout/Menu/Size/Size.stories.svelte +37 -0
  107. package/src/lib/stories/components/Layout/Paper/Color/Color.stories.svelte +63 -0
  108. package/src/lib/stories/components/Layout/Paper/Paper.stories.svelte +50 -0
  109. package/src/lib/stories/components/Layout/Paper/Roundness/Roundness.stories.svelte +25 -0
  110. package/src/lib/stories/components/Layout/Paper/Shadow/Shadow.stories.svelte +24 -0
  111. package/src/lib/stories/components/Layout/Separator/Color/Color.stories.svelte +19 -0
  112. package/src/lib/stories/components/Layout/Separator/Separator.stories.svelte +30 -0
  113. package/src/lib/stories/developer tools/Intro.mdx +9 -0
  114. package/src/lib/stories/developer tools/components/DynamicInput/DynamicInput.stories.svelte +53 -0
  115. package/src/lib/stories/developer tools/components/DynamicInput/DynamicInput.svelte +43 -3
  116. package/src/lib/stories/developer tools/components/DynamicInput/Events/Events.stories.svelte +121 -0
  117. package/src/lib/stories/developer tools/components/DynamicInput/Size/Size.stories.svelte +17 -0
  118. package/src/lib/stories/developer tools/components/InputEnclosure/InputEnclosure.stories.svelte +38 -0
  119. package/src/lib/stories/developer tools/components/InputEnclosure/Roundness/Roundness.stories.svelte +20 -0
  120. package/src/lib/stories/developer tools/components/InputEnclosure/Size/Size.stories.svelte +16 -0
  121. package/src/lib/stories/developer tools/components/InputEnclosure/WithIcon/WithIcon.stories.svelte +47 -0
  122. package/src/lib/stories/developer tools/components/Popper/Popper.stories.svelte +124 -0
  123. package/src/lib/stories/developer tools/components/Popper/PopperPopup/PopperPopup.stories.svelte +64 -0
  124. package/src/lib/stories/developer tools/components/Popper/Positions/AutoPosition/AutoPosition.stories.svelte +92 -0
  125. package/src/lib/stories/developer tools/components/Popper/Positions/Positions.stories.svelte +114 -0
  126. package/src/lib/stories/developer tools/components/UtilityButton/Size/Size.stories.svelte +25 -0
  127. package/src/lib/stories/developer tools/components/UtilityButton/UtilityButton.stories.svelte +30 -0
  128. package/src/lib/stories/developer tools/directives/clickOutside/index.mdx +25 -0
  129. package/src/lib/stories/developer tools/helpers/Numbers/cleanNumericString/cleanNumericString.ts +27 -0
  130. package/src/lib/stories/developer tools/helpers/Numbers/cleanNumericString/index.mdx +20 -0
  131. package/src/lib/stories/developer tools/helpers/Numbers/isValidNumberValue/index.mdx +71 -0
  132. package/src/lib/stories/developer tools/helpers/Numbers/isValidNumberValue/isValidNumberValue.ts +156 -0
  133. package/src/lib/stories/developer tools/helpers/logger/index.mdx +63 -0
  134. package/src/lib/stories/developer tools/helpers/logger/logger.ts +46 -0
  135. package/src/lib/stories/developer tools/philosophy/Colors/Colors.mdx +43 -0
  136. package/src/lib/stories/developer tools/philosophy/Colors/Colors.stories.svelte +22 -0
  137. package/src/lib/stories/developer tools/philosophy/Colors/Opacity.stories.svelte +11 -0
  138. package/src/lib/stories/developer tools/philosophy/Themes.mdx +23 -0
@@ -7,6 +7,7 @@
7
7
  ClipboardEventHandler,
8
8
  FocusEventHandler,
9
9
  FormEventHandler,
10
+ KeyboardEventHandler,
10
11
  MouseEventHandler,
11
12
  } from 'svelte/elements';
12
13
 
@@ -16,6 +17,8 @@
16
17
  disabled?: boolean;
17
18
  };
18
19
 
20
+ export type SelectDropdownArrowPosition = false | 'before' | 'after';
21
+
19
22
  export interface SelectProps {
20
23
  /** How large should the button be? */
21
24
  size?: ComponentSize;
@@ -71,12 +74,22 @@
71
74
  oncopy?: ClipboardEventHandler<HTMLInputElement>;
72
75
  /** oncut event handler */
73
76
  oncut?: ClipboardEventHandler<HTMLInputElement>;
77
+ /** onkeydown event handler */
78
+ onkeydown?: KeyboardEventHandler<HTMLInputElement | HTMLButtonElement>;
79
+ /** onkeypress event handler */
80
+ onkeypress?: KeyboardEventHandler<HTMLInputElement | HTMLButtonElement>;
81
+ /** onkeyup event handler */
82
+ onkeyup?: KeyboardEventHandler<HTMLInputElement | HTMLButtonElement>;
74
83
  /** custom Content Formatting for variant button */
75
84
  customInputContent?: (val: SelectOption) => Snippet;
76
85
  /** custom Content Formatting for variant button */
77
86
  customMenuItemContent?: (val: SelectOption, selected: boolean) => Snippet;
78
87
  /** Custom Popup Content */
79
- customPopupContent?: (options: SelectOption[], selectedOption: SelectOption) => Snippet;
88
+ customPopupContent?: (
89
+ options: SelectOption[],
90
+ selectedOption: SelectOption,
91
+ onselect: (val: SelectOption) => void,
92
+ ) => Snippet;
80
93
  /** custom Content Formatting for variant button */
81
94
  customPlaceholderMenuItemContent?: () => Snippet;
82
95
  /** PopperPopup Max height. Use css compatible value */
@@ -89,6 +102,18 @@
89
102
  menuProps?: Partial<MenuProps>;
90
103
  /** MenuItem: Menu component props */
91
104
  menuItemProps?: Partial<MenuItemProps>;
105
+ /** Dropdown Arrow Icon */
106
+ customDropdownArrowIcon?: (open: boolean) => Snippet;
107
+ /** Select Dropdown Arrow Position */
108
+ dropdownArrowPosition?: SelectDropdownArrowPosition;
109
+ /** Popup stick horizontally */
110
+ popupPositionX?: PositionX;
111
+ /** Popup stick vertically */
112
+ popupPositionY?: PositionY;
113
+ /** Lock positions, disable auto top, bottom position based */
114
+ lockPoistions?: boolean;
115
+ /** Popper Height For Vertical Position, default 300 */
116
+ popperHeightForVerticalPosition?: number;
92
117
  }
93
118
  </script>
94
119
 
@@ -98,14 +123,16 @@
98
123
  import Icon from '@iconify/svelte';
99
124
  import {
100
125
  DynamicInput,
101
- Menu,
102
- MenuItem,
126
+ DynamicMenu,
103
127
  Popper,
104
128
  type DynamicInputFocusEvent,
129
+ type DynamicMenuItemOption,
105
130
  type MenuItemProps,
106
131
  type MenuProps,
107
132
  type PaperProps,
108
133
  type PopperProps,
134
+ type PositionX,
135
+ type PositionY,
109
136
  } from '$lib/index.js';
110
137
  import type { TextInputInputEvent } from '../TextInput/TextInput.svelte';
111
138
  import type { ButtonClickEvent } from '../Button/Button.svelte';
@@ -126,6 +153,9 @@
126
153
  onpaste,
127
154
  oncopy,
128
155
  oncut,
156
+ onkeydown,
157
+ onkeypress,
158
+ onkeyup,
129
159
  before,
130
160
  after,
131
161
  error = false,
@@ -141,37 +171,56 @@
141
171
  customMenuItemContent: customMenuItemContentInternal,
142
172
  customPopupContent: customPopupContentInternal,
143
173
  customPlaceholderMenuItemContent: customPlaceholderMenuItemContentInternal,
174
+ customDropdownArrowIcon: customDropdownArrowIconInternal,
144
175
  popupMaxHeight = '300px',
145
176
  paperProps,
146
177
  popperProps,
147
178
  menuProps,
148
179
  menuItemProps,
149
180
  optionsPlaceholder = 'No Options',
181
+ dropdownArrowPosition = 'after',
182
+ popupPositionX,
183
+ popupPositionY,
184
+ lockPoistions,
185
+ popperHeightForVerticalPosition,
150
186
  }: SelectProps = $props();
151
187
 
188
+ function convertOptionsToDynamicMenuItemOptions(
189
+ options: SelectOption[],
190
+ ): DynamicMenuItemOption<SelectOption>[] {
191
+ const newOptions: DynamicMenuItemOption<SelectOption>[] = options.map((option) => ({
192
+ id: `opt-${option.value}`,
193
+ disabled: option.disabled,
194
+ label: option.label,
195
+ meta: option,
196
+ type: 'button',
197
+ }));
198
+
199
+ return newOptions;
200
+ }
201
+
152
202
  let open: boolean = $state(false);
153
203
  let onInputStart: boolean = $state(false);
154
204
  const selectedOption = $derived(value);
155
205
  let searchTerm = $state(value?.label.trim() || '');
156
- let options = $state(optionsRaw);
206
+ let options = $state(convertOptionsToDynamicMenuItemOptions(optionsRaw));
157
207
  let menuRef = $state<HTMLUListElement | undefined>(undefined);
158
- let menuItemIndex = $state(0);
159
208
 
160
209
  $effect(() => {
161
210
  if (!onInputStart) {
162
- options = optionsRaw;
211
+ options = convertOptionsToDynamicMenuItemOptions(optionsRaw);
163
212
  return;
164
213
  }
165
214
 
166
215
  const searchTermSimplified = searchTerm.trim().toLowerCase();
167
216
 
168
217
  if (!searchTermSimplified) {
169
- options = optionsRaw;
218
+ options = convertOptionsToDynamicMenuItemOptions(optionsRaw);
170
219
  return;
171
220
  }
172
221
 
173
- options = optionsRaw.filter((item) =>
174
- item.label.trim().toLowerCase().includes(searchTermSimplified),
222
+ options = convertOptionsToDynamicMenuItemOptions(optionsRaw).filter((item) =>
223
+ item?.meta?.label.trim().toLowerCase().includes(searchTermSimplified),
175
224
  );
176
225
  });
177
226
 
@@ -187,23 +236,17 @@
187
236
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
188
237
  let customPlaceholderMenuItemContentTyped = customPlaceholderMenuItemContentInternal as any;
189
238
 
239
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
240
+ let customDropdownArrowIconTyped = customDropdownArrowIconInternal as any;
241
+
190
242
  function closeMenu() {
191
243
  open = false;
192
- menuItemIndex = 0;
193
244
 
194
245
  ref?.blur();
195
246
  }
196
247
 
197
248
  function openMenu() {
198
249
  open = true;
199
-
200
- const menuItemIndexNew = options.findIndex((item) => item.value === selectedOption?.value);
201
-
202
- if (menuItemIndexNew < 0) {
203
- menuItemIndex = 0;
204
- } else {
205
- menuItemIndex = menuItemIndexNew;
206
- }
207
250
  }
208
251
 
209
252
  function onfocusMod(e: DynamicInputFocusEvent) {
@@ -259,7 +302,9 @@
259
302
  }
260
303
  }
261
304
 
262
- function onKeyBoardEnter(selectedItemIndex: number) {
305
+ function onKeyBoardEnter(e: KeyboardEvent, selectedItemIndex: number) {
306
+ e.preventDefault();
307
+
263
308
  const targetOption = options[selectedItemIndex];
264
309
 
265
310
  if (!targetOption) {
@@ -270,106 +315,35 @@
270
315
  return;
271
316
  }
272
317
 
273
- onselectMod(targetOption);
274
- }
275
-
276
- function onKeyboardNavigation(e: KeyboardEvent) {
277
- let keyHit: string | undefined = undefined;
278
-
279
- if (!menuRef) {
280
- return;
281
- }
282
-
283
- if (!open) {
284
- return;
285
- }
286
-
287
- switch (e.key) {
288
- case 'ArrowDown':
289
- case 'ArrowUp':
290
- case 'Enter':
291
- keyHit = e.key;
292
- e.preventDefault();
293
- break;
294
- default:
295
- break;
296
- }
297
-
298
- if (!keyHit) {
299
- return;
300
- }
301
-
302
- const listItems = menuRef.querySelectorAll(':scope > li.dodo-ui-MenuItem');
303
-
304
- if (!listItems.length) {
305
- return;
306
- }
307
-
308
- for (let index = 0; index < listItems.length; index++) {
309
- const element = listItems[index];
310
-
311
- element.classList.remove('hover');
312
- }
313
-
314
- let newMenuItemIndex = menuItemIndex;
315
-
316
- if (keyHit === 'ArrowDown') {
317
- if (listItems[newMenuItemIndex + 1]) {
318
- newMenuItemIndex = newMenuItemIndex + 1;
319
- } else {
320
- newMenuItemIndex = 0;
321
- }
322
- } else if (keyHit === 'ArrowUp') {
323
- if (listItems[newMenuItemIndex - 1]) {
324
- newMenuItemIndex = newMenuItemIndex - 1;
325
- } else {
326
- newMenuItemIndex = listItems.length - 1;
327
- }
328
- } else if (keyHit === 'Enter') {
329
- onKeyBoardEnter(newMenuItemIndex);
330
- return;
331
- }
332
-
333
- const targetItem = listItems[newMenuItemIndex] as HTMLLIElement;
334
-
335
- targetItem.classList.add('hover');
336
-
337
- targetItem.focus();
338
- targetItem.scrollIntoView({ block: 'nearest' });
339
-
340
- menuItemIndex = newMenuItemIndex;
318
+ onselectMod(targetOption.meta as SelectOption);
341
319
  }
342
-
343
- $effect(() => {
344
- if (!menuRef) {
345
- return;
346
- }
347
-
348
- if (!open) {
349
- return;
350
- }
351
-
352
- const targetItem = menuRef.querySelector(':scope > li.dodo-ui-MenuItem.selected') as
353
- | HTMLLIElement
354
- | undefined;
355
-
356
- if (targetItem) {
357
- targetItem.classList.add('hover');
358
-
359
- targetItem.focus();
360
- targetItem.scrollIntoView({ block: 'nearest' });
361
- }
362
-
363
- window.addEventListener('keydown', onKeyboardNavigation);
364
-
365
- return () => {
366
- window.removeEventListener('keydown', onKeyboardNavigation);
367
- };
368
- });
369
320
  </script>
370
321
 
322
+ {#snippet selectDropdownArrowIcon()}
323
+ <UtilityButton {size} title="Dropdown" onclick={onfocusMod}>
324
+ {#if customDropdownArrowIconTyped}
325
+ {@render customDropdownArrowIconTyped(open)}
326
+ {:else if open}
327
+ <Icon icon="material-symbols:arrow-drop-up-rounded" width="28" height="28" />
328
+ {:else}
329
+ <Icon icon="material-symbols:arrow-drop-down-rounded" width="28" height="28" />
330
+ {/if}
331
+ </UtilityButton>
332
+ {/snippet}
333
+
371
334
  <div class={['dodo-ui-Select', className].join(' ')}>
372
- <Popper fullWidth {open} {onClickOutside} {...popperProps} {popupMaxHeight} {paperProps}>
335
+ <Popper
336
+ fullWidth
337
+ {open}
338
+ {onClickOutside}
339
+ {...popperProps}
340
+ {popupMaxHeight}
341
+ {popupPositionX}
342
+ {popupPositionY}
343
+ {paperProps}
344
+ {lockPoistions}
345
+ {popperHeightForVerticalPosition}
346
+ >
373
347
  <div
374
348
  class:outline
375
349
  class:disabled
@@ -392,6 +366,12 @@
392
366
  {before}
393
367
  {after}
394
368
  >
369
+ {#if dropdownArrowPosition === 'before'}
370
+ <div class:before class:open class="DropdownArrow">
371
+ {@render selectDropdownArrowIcon()}
372
+ </div>
373
+ {/if}
374
+
395
375
  <DynamicInput
396
376
  type="text"
397
377
  {name}
@@ -405,6 +385,9 @@
405
385
  {onpaste}
406
386
  {oncopy}
407
387
  {oncut}
388
+ {onkeydown}
389
+ {onkeypress}
390
+ {onkeyup}
408
391
  {placeholder}
409
392
  value={searchable ? searchTerm : selectedOption?.label}
410
393
  {readonly}
@@ -426,43 +409,50 @@
426
409
  </UtilityButton>
427
410
  </div>
428
411
  {/if}
412
+
413
+ {#if dropdownArrowPosition === 'after'}
414
+ <div class:after class:open class="DropdownArrow">
415
+ {@render selectDropdownArrowIcon()}
416
+ </div>
417
+ {/if}
429
418
  </InputEnclosure>
430
419
  </div>
431
420
 
432
421
  {#snippet popupChildren()}
433
422
  {#if customPopupContentTyped}
434
- {@render customPopupContentTyped(options, selectedOption)}
423
+ {@render customPopupContentTyped(optionsRaw, selectedOption, onselectMod)}
435
424
  {:else}
436
- <Menu bind:ref={menuRef} {...menuProps}>
437
- {#if options.length}
438
- {#each options as option (option.value)}
439
- <MenuItem
440
- onclick={() => onselectMod(option)}
441
- type="button"
442
- disabled={option.disabled}
443
- selected={selectedOption?.value === option.value}
444
- {...menuItemProps}
445
- >
446
- {#if customMenuItemContentTyped}
447
- {@render customMenuItemContentTyped(
448
- option,
449
- selectedOption?.value === option.value,
450
- )}
451
- {:else}
452
- {option.label}
453
- {/if}
454
- </MenuItem>
455
- {/each}
456
- {:else}
457
- <MenuItem type="text" disabled={true} {...menuItemProps}>
458
- {#if customPlaceholderMenuItemContentTyped}
459
- {@render customPlaceholderMenuItemContentTyped()}
460
- {:else}
461
- {optionsPlaceholder}
462
- {/if}
463
- </MenuItem>
464
- {/if}
465
- </Menu>
425
+ <DynamicMenu
426
+ bind:ref={menuRef}
427
+ {menuItemProps}
428
+ {options}
429
+ keyboardNavigation
430
+ onEnter={onKeyBoardEnter}
431
+ selectedOption={options.find((item) => item.meta?.value === selectedOption?.value)}
432
+ onclick={(_e, option: DynamicMenuItemOption) => onselectMod(option.meta as SelectOption)}
433
+ showOptionsPlaceholder
434
+ {...menuProps}
435
+ >
436
+ {#snippet customMenuItemContent(option, selectedOption)}
437
+ {#if customMenuItemContentTyped}
438
+ {@render customMenuItemContentTyped(
439
+ option?.meta as SelectOption,
440
+ (selectedOption?.meta as SelectOption).value ===
441
+ (option?.meta as SelectOption).value,
442
+ )}
443
+ {:else}
444
+ {(option?.meta as SelectOption).label}
445
+ {/if}
446
+ {/snippet}
447
+
448
+ {#snippet customPlaceholderMenuItemContent()}
449
+ {#if customPlaceholderMenuItemContentTyped}
450
+ {@render customPlaceholderMenuItemContentTyped()}
451
+ {:else}
452
+ {optionsPlaceholder}
453
+ {/if}
454
+ {/snippet}
455
+ </DynamicMenu>
466
456
  {/if}
467
457
  {/snippet}
468
458
  </Popper>
@@ -478,6 +468,16 @@
478
468
  margin-right: calc(var(--dodo-ui-space-small) * 2);
479
469
  }
480
470
  }
471
+
472
+ .DropdownArrow {
473
+ &.after {
474
+ margin-right: calc(var(--dodo-ui-space-small) * 2);
475
+ }
476
+
477
+ &.before {
478
+ margin-right: calc(var(--dodo-ui-space-small) * 2);
479
+ }
480
+ }
481
481
  }
482
482
 
483
483
  &--small {
@@ -486,6 +486,16 @@
486
486
  margin-right: var(--dodo-ui-space);
487
487
  }
488
488
  }
489
+
490
+ .DropdownArrow {
491
+ &.after {
492
+ margin-right: var(--dodo-ui-space);
493
+ }
494
+
495
+ &.before {
496
+ margin-right: var(--dodo-ui-space);
497
+ }
498
+ }
489
499
  }
490
500
 
491
501
  &--large {
@@ -494,6 +504,16 @@
494
504
  margin-right: calc(var(--dodo-ui-space) * 2);
495
505
  }
496
506
  }
507
+
508
+ .DropdownArrow {
509
+ &.after {
510
+ margin-right: calc(var(--dodo-ui-space) * 2);
511
+ }
512
+
513
+ &.before {
514
+ margin-right: calc(var(--dodo-ui-space) * 2);
515
+ }
516
+ }
497
517
  }
498
518
  }
499
519
  }
@@ -0,0 +1,28 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import Select, { type SelectOption } from '../Select.svelte';
4
+ import { selectOptions, storySelectArgTypes } from '../Select.stories.svelte';
5
+
6
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
7
+ const { Story } = defineMeta({
8
+ component: Select,
9
+ tags: ['autodocs'],
10
+ argTypes: storySelectArgTypes,
11
+ parameters: {
12
+ docs: {
13
+ story: {
14
+ height: '400px',
15
+ inline: false,
16
+ },
17
+ },
18
+ },
19
+ });
20
+
21
+ const options = selectOptions;
22
+
23
+ let value = $state<SelectOption>(options[0]);
24
+ </script>
25
+
26
+ <Story name="Normal" args={{ options, value }} />
27
+ <Story name="Small" args={{ options, value, size: 'small' }} />
28
+ <Story name="Large" args={{ options, value, size: 'large' }} />
@@ -0,0 +1,43 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import Select, { type SelectOption } from '../Select.svelte';
4
+ import { selectOptions, storySelectArgTypes } from '../Select.stories.svelte';
5
+ import Icon from '@iconify/svelte';
6
+
7
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
8
+ const { Story } = defineMeta({
9
+ component: Select,
10
+ tags: ['autodocs'],
11
+ argTypes: storySelectArgTypes,
12
+ parameters: {
13
+ docs: {
14
+ story: {
15
+ height: '400px',
16
+ inline: false,
17
+ },
18
+ },
19
+ },
20
+ });
21
+
22
+ const options = selectOptions;
23
+
24
+ let value = $state<SelectOption>(options[0]);
25
+ </script>
26
+
27
+ <!-- Select icon in front. -->
28
+ <Story name="Icon Before" asChild>
29
+ <Select {value} {options}>
30
+ {#snippet before()}
31
+ <Icon icon="material-symbols:content-copy" />
32
+ {/snippet}
33
+ </Select>
34
+ </Story>
35
+
36
+ <!-- Select icon in front. -->
37
+ <Story name="Icon After" asChild>
38
+ <Select {value} {options}>
39
+ {#snippet after()}
40
+ <Icon icon="material-symbols:download-2" />
41
+ {/snippet}
42
+ </Select>
43
+ </Story>
@@ -0,0 +1,125 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import TextInput, {
4
+ type TextInputClipboardEvent,
5
+ type TextInputFocusEvent,
6
+ type TextInputKeyboardEvent,
7
+ } from '../TextInput.svelte';
8
+ import { storyTextInputArgTypes } from '../TextInput.stories.svelte';
9
+
10
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
11
+ const { Story } = defineMeta({
12
+ component: TextInput,
13
+ tags: ['autodocs'],
14
+ argTypes: storyTextInputArgTypes,
15
+ });
16
+ </script>
17
+
18
+ <Story
19
+ name="Input"
20
+ args={{
21
+ oninput: (e: Event) => {
22
+ const target = e.target as HTMLInputElement;
23
+
24
+ console.log('Input Event', target.value);
25
+ },
26
+ }}
27
+ />
28
+
29
+ <Story
30
+ name="Change"
31
+ args={{
32
+ onchange: (e: Event) => {
33
+ const target = e.target as HTMLInputElement;
34
+
35
+ console.log('onChange Event', target.value);
36
+ },
37
+ }}
38
+ />
39
+
40
+ <!-- `e: TextInputFocusEvent` -->
41
+ <Story
42
+ name="Focus"
43
+ args={{
44
+ onfocus: (e: TextInputFocusEvent) => {
45
+ const target = e.target as HTMLInputElement;
46
+
47
+ console.log('onfocus Event', target);
48
+ },
49
+ }}
50
+ />
51
+
52
+ <!-- `e: TextInputFocusEvent` -->
53
+ <Story
54
+ name="Blur"
55
+ args={{
56
+ onblur: (e: TextInputFocusEvent) => {
57
+ const target = e.target as HTMLInputElement;
58
+
59
+ console.log('onblur Event', target);
60
+ },
61
+ }}
62
+ />
63
+
64
+ <!-- `e: TextInputClipboardEvent` -->
65
+ <Story
66
+ name="Copy"
67
+ args={{
68
+ oncopy: (e: TextInputClipboardEvent) => {
69
+ const target = e.target as HTMLInputElement;
70
+
71
+ console.log('oncopy Event', target);
72
+ },
73
+ }}
74
+ />
75
+
76
+ <!-- `e: TextInputClipboardEvent` -->
77
+ <Story
78
+ name="Cut"
79
+ args={{
80
+ oncut: (e: TextInputClipboardEvent) => {
81
+ const target = e.target as HTMLInputElement;
82
+
83
+ console.log('oncut Event', target);
84
+ },
85
+ }}
86
+ />
87
+
88
+ <!-- `e: TextInputClipboardEvent` -->
89
+ <Story
90
+ name="Paste"
91
+ args={{
92
+ onpaste: (e: TextInputClipboardEvent) => {
93
+ const target = e.target as HTMLInputElement;
94
+
95
+ console.log('onpaste Event', target);
96
+ },
97
+ }}
98
+ />
99
+
100
+ <Story
101
+ name="KeyDown"
102
+ args={{
103
+ onkeydown: (e: TextInputKeyboardEvent) => {
104
+ console.log('onkeydown Event', e.key);
105
+ },
106
+ }}
107
+ />
108
+
109
+ <Story
110
+ name="KeyPress"
111
+ args={{
112
+ onkeypress: (e: TextInputKeyboardEvent) => {
113
+ console.log('onkeypress Event', e.key);
114
+ },
115
+ }}
116
+ />
117
+
118
+ <Story
119
+ name="KeyUp"
120
+ args={{
121
+ onkeyup: (e: TextInputKeyboardEvent) => {
122
+ console.log('onkeyup Event', e.key);
123
+ },
124
+ }}
125
+ />
@@ -0,0 +1,21 @@
1
+ <script module>
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import TextInput from '../TextInput.svelte';
4
+ import { storyTextInputArgTypes } from '../TextInput.stories.svelte';
5
+
6
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
7
+ const { Story } = defineMeta({
8
+ component: TextInput,
9
+ tags: ['autodocs'],
10
+ argTypes: storyTextInputArgTypes,
11
+ args: { value: 'Hello world!' },
12
+ });
13
+ </script>
14
+
15
+ <Story name="Roundness 1" />
16
+
17
+ <Story name="Roundness 2" args={{ roundness: 2 }} />
18
+
19
+ <Story name="Roundness 3" args={{ roundness: 3 }} />
20
+
21
+ <Story name="Roundness 0" args={{ roundness: 0 }} />