@flightlesslabs/dodo-ui 0.6.6 → 0.7.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 (118) hide show
  1. package/dist/index.d.ts +28 -7
  2. package/dist/index.js +12 -2
  3. package/dist/stories/Home.mdx +1 -1
  4. package/dist/stories/components/Form/Button/Button.svelte +2 -2
  5. package/dist/stories/components/Form/Button/Button.svelte.d.ts +8 -5
  6. package/dist/stories/components/Form/FormControl/FormControl.svelte +3 -0
  7. package/dist/stories/components/Form/FormControl/FormControl.svelte.d.ts +1 -1
  8. package/dist/stories/components/Form/Label/Label.svelte +5 -2
  9. package/dist/stories/components/Form/Label/Label.svelte.d.ts +1 -1
  10. package/dist/stories/components/Form/Message/Message.svelte +5 -2
  11. package/dist/stories/components/Form/Message/Message.svelte.d.ts +1 -1
  12. package/dist/stories/components/Form/PasswordInput/PasswordInput.svelte +25 -32
  13. package/dist/stories/components/Form/PasswordInput/PasswordInput.svelte.d.ts +5 -5
  14. package/dist/stories/components/Form/Select/Customize/Customize.stories.svelte +120 -0
  15. package/dist/stories/components/Form/Select/Customize/Customize.stories.svelte.d.ts +18 -0
  16. package/dist/stories/components/Form/Select/Events/Events.stories.svelte +138 -0
  17. package/dist/stories/components/Form/Select/Options/OptionFormat.mdx +40 -0
  18. package/dist/stories/components/Form/Select/Roundness/Roundness.stories.svelte +28 -0
  19. package/dist/stories/components/Form/Select/Roundness/Roundness.stories.svelte.d.ts +18 -0
  20. package/dist/stories/components/Form/Select/Select.stories.svelte +114 -0
  21. package/dist/stories/components/Form/Select/Select.stories.svelte.d.ts +22 -0
  22. package/dist/stories/components/Form/Select/Select.svelte +285 -0
  23. package/dist/stories/components/Form/Select/Select.svelte.d.ts +87 -0
  24. package/dist/stories/components/Form/Select/Size/Size.stories.svelte +24 -0
  25. package/dist/stories/components/Form/Select/Size/Size.stories.svelte.d.ts +18 -0
  26. package/dist/stories/components/Form/Select/WithIcon/WithIcon.stories.svelte +39 -0
  27. package/dist/stories/components/Form/Select/WithIcon/WithIcon.stories.svelte.d.ts +18 -0
  28. package/dist/stories/components/Form/TextInput/TextInput.svelte +9 -28
  29. package/dist/stories/components/Form/TextInput/TextInput.svelte.d.ts +8 -5
  30. package/dist/stories/components/Layout/Menu/Menu.stories.svelte +65 -0
  31. package/dist/stories/components/Layout/Menu/Menu.stories.svelte.d.ts +21 -0
  32. package/dist/stories/components/Layout/Menu/Menu.svelte +30 -0
  33. package/dist/stories/components/Layout/Menu/Menu.svelte.d.ts +23 -0
  34. package/dist/stories/components/Layout/Menu/MenuItem/MenuItem.stories.svelte +30 -0
  35. package/dist/stories/components/Layout/Menu/MenuItem/MenuItem.stories.svelte.d.ts +21 -0
  36. package/dist/stories/components/Layout/Menu/MenuItem/MenuItem.svelte +164 -0
  37. package/dist/stories/components/Layout/Menu/MenuItem/MenuItem.svelte.d.ts +49 -0
  38. package/dist/stories/components/Layout/Menu/MenuItem/Size/Size.stories.svelte +16 -0
  39. package/dist/stories/components/Layout/Menu/MenuItem/Type/Type.stories.svelte +21 -0
  40. package/dist/stories/components/{Form/SimpleSelect/WithIcon/WithIcon.stories.svelte.d.ts → Layout/Menu/MenuItem/Type/Type.stories.svelte.d.ts} +3 -3
  41. package/dist/stories/components/Layout/Menu/Size/Size.stories.svelte +37 -0
  42. package/dist/stories/components/{Form/SimpleSelect/Roundness/Roundness.stories.svelte.d.ts → Layout/Menu/Size/Size.stories.svelte.d.ts} +3 -3
  43. package/dist/stories/components/Layout/Paper/Color/Color.stories.svelte +1 -1
  44. package/dist/stories/components/Layout/Paper/Paper.stories.svelte +1 -1
  45. package/dist/stories/components/Layout/Paper/Paper.svelte.d.ts +5 -5
  46. package/dist/stories/components/Layout/Separator/Color/Color.stories.svelte +19 -0
  47. package/dist/stories/components/Layout/Separator/Color/Color.stories.svelte.d.ts +26 -0
  48. package/dist/stories/components/Layout/Separator/Separator.stories.svelte +26 -0
  49. package/dist/stories/components/Layout/Separator/Separator.stories.svelte.d.ts +21 -0
  50. package/dist/stories/components/Layout/Separator/Separator.svelte +66 -0
  51. package/dist/stories/components/Layout/Separator/Separator.svelte.d.ts +22 -0
  52. package/dist/stories/components/Layout/Separator/utils/scss/mixins.scss +24 -0
  53. package/dist/stories/developer tools/components/DynamicInput/DynamicInput.stories.svelte +49 -0
  54. package/dist/stories/{components/Form/SimpleSelect/SimpleSelect.stories.svelte.d.ts → developer tools/components/DynamicInput/DynamicInput.stories.svelte.d.ts } +5 -7
  55. package/dist/stories/developer tools/components/DynamicInput/DynamicInput.svelte +92 -0
  56. package/dist/stories/developer tools/components/DynamicInput/DynamicInput.svelte.d.ts +60 -0
  57. package/dist/stories/{components/Form/SimpleSelect/Size/Size.stories.svelte → developer tools/components/DynamicInput/Size/Size.stories.svelte } +5 -8
  58. package/dist/stories/developer tools/components/DynamicInput/Size/Size.stories.svelte.d.ts +26 -0
  59. package/dist/stories/developer tools/components/InputEnclosure/InputEnclosure.svelte +6 -2
  60. package/dist/stories/developer tools/components/InputEnclosure/InputEnclosure.svelte.d.ts +1 -1
  61. package/dist/stories/developer tools/components/Popper/Popper.stories.svelte +119 -0
  62. package/dist/stories/developer tools/components/Popper/Popper.stories.svelte.d.ts +21 -0
  63. package/dist/stories/developer tools/components/Popper/Popper.svelte +77 -0
  64. package/dist/stories/developer tools/components/Popper/Popper.svelte.d.ts +50 -0
  65. package/dist/stories/developer tools/components/Popper/PopperPopup/PopperPopup.stories.svelte +60 -0
  66. package/dist/stories/developer tools/components/Popper/PopperPopup/PopperPopup.stories.svelte.d.ts +21 -0
  67. package/dist/stories/developer tools/components/Popper/PopperPopup/PopperPopup.svelte +66 -0
  68. package/dist/stories/developer tools/components/Popper/PopperPopup/PopperPopup.svelte.d.ts +34 -0
  69. package/dist/stories/developer tools/components/Popper/PopperPopup/utils/getPopupPosition.d.ts +21 -0
  70. package/dist/stories/developer tools/components/Popper/PopperPopup/utils/getPopupPosition.js +62 -0
  71. package/dist/stories/developer tools/components/Popper/Positions/AutoPosition/AutoPosition.stories.svelte +89 -0
  72. package/dist/stories/developer tools/components/Popper/Positions/AutoPosition/AutoPosition.stories.svelte.d.ts +18 -0
  73. package/dist/stories/developer tools/components/Popper/Positions/Positions.stories.svelte +111 -0
  74. package/dist/stories/developer tools/components/Popper/Positions/Positions.stories.svelte.d.ts +18 -0
  75. package/dist/stories/developer tools/components/UtilityButton/Size/Size.stories.svelte +3 -5
  76. package/dist/stories/developer tools/components/UtilityButton/UtilityButton.stories.svelte +0 -4
  77. package/dist/stories/developer tools/components/UtilityButton/UtilityButton.svelte +5 -2
  78. package/dist/stories/developer tools/components/UtilityButton/UtilityButton.svelte.d.ts +1 -1
  79. package/dist/stories/developer tools/directives/clickOutside/clickOutside.d.ts +3 -0
  80. package/dist/stories/developer tools/directives/clickOutside/clickOutside.js +14 -0
  81. package/dist/stories/developer tools/directives/clickOutside/index.mdx +25 -0
  82. package/dist/styles/_z-index.css +9 -0
  83. package/dist/styles/global.css +1 -0
  84. package/dist/types/position.d.ts +4 -0
  85. package/dist/types/position.js +2 -0
  86. package/package.json +1 -1
  87. package/src/lib/index.ts +50 -9
  88. package/src/lib/stories/components/Form/Button/Button.svelte +14 -10
  89. package/src/lib/stories/components/Form/FormControl/FormControl.svelte +7 -4
  90. package/src/lib/stories/components/Form/Label/Label.svelte +4 -2
  91. package/src/lib/stories/components/Form/Message/Message.svelte +4 -2
  92. package/src/lib/stories/components/Form/PasswordInput/PasswordInput.svelte +48 -49
  93. package/src/lib/stories/components/Form/Select/Select.svelte +501 -0
  94. package/src/lib/stories/components/Form/TextInput/TextInput.svelte +32 -59
  95. package/src/lib/stories/components/Layout/Menu/Menu.svelte +65 -0
  96. package/src/lib/stories/components/Layout/Menu/MenuItem/MenuItem.svelte +268 -0
  97. package/src/lib/stories/components/Layout/Paper/Paper.svelte +8 -9
  98. package/src/lib/stories/components/Layout/Separator/Separator.svelte +96 -0
  99. package/src/lib/stories/components/Layout/Separator/utils/scss/mixins.scss +24 -0
  100. package/src/lib/stories/developer tools/components/DynamicInput/DynamicInput.svelte +195 -0
  101. package/src/lib/stories/developer tools/components/InputEnclosure/InputEnclosure.svelte +5 -2
  102. package/src/lib/stories/developer tools/components/Popper/Popper.svelte +159 -0
  103. package/src/lib/stories/developer tools/components/Popper/PopperPopup/PopperPopup.svelte +120 -0
  104. package/src/lib/stories/developer tools/components/Popper/PopperPopup/utils/getPopupPosition.ts +87 -0
  105. package/src/lib/stories/developer tools/components/UtilityButton/UtilityButton.svelte +4 -2
  106. package/src/lib/stories/developer tools/directives/clickOutside/clickOutside.ts +17 -0
  107. package/src/lib/styles/_z-index.css +9 -0
  108. package/src/lib/styles/global.css +1 -0
  109. package/src/lib/types/position.ts +5 -0
  110. package/dist/stories/components/Form/SimpleSelect/Events/Events.stories.svelte +0 -47
  111. package/dist/stories/components/Form/SimpleSelect/Roundness/Roundness.stories.svelte +0 -24
  112. package/dist/stories/components/Form/SimpleSelect/SimpleSelect.stories.svelte +0 -57
  113. package/dist/stories/components/Form/SimpleSelect/SimpleSelect.svelte +0 -69
  114. package/dist/stories/components/Form/SimpleSelect/SimpleSelect.svelte.d.ts +0 -51
  115. package/dist/stories/components/Form/SimpleSelect/WithIcon/WithIcon.stories.svelte +0 -36
  116. package/src/lib/stories/components/Form/SimpleSelect/SimpleSelect.svelte +0 -160
  117. /package/dist/stories/components/Form/{SimpleSelect → Select}/Events/Events.stories.svelte.d.ts +0 -0
  118. /package/dist/stories/components/{Form/SimpleSelect → Layout/Menu/MenuItem}/Size/Size.stories.svelte.d.ts +0 -0
@@ -0,0 +1,501 @@
1
+ <script lang="ts" module>
2
+ import type { ComponentSize } from '$lib/types/size.js';
3
+ import type { ComponentRoundness } from '$lib/types/roundness.js';
4
+ import type { Snippet } from 'svelte';
5
+ import type {
6
+ ChangeEventHandler,
7
+ ClipboardEventHandler,
8
+ FocusEventHandler,
9
+ FormEventHandler,
10
+ MouseEventHandler,
11
+ } from 'svelte/elements';
12
+
13
+ export type SelectOption = {
14
+ value: string | number | boolean | null | undefined;
15
+ label: string;
16
+ disabled?: boolean;
17
+ };
18
+
19
+ export interface SelectProps {
20
+ /** How large should the button be? */
21
+ size?: ComponentSize;
22
+ /** want a searchable Select? */
23
+ options: SelectOption[];
24
+ /** want a searchable Select? */
25
+ searchable?: boolean;
26
+ /** want a clearable Select? */
27
+ clearable?: boolean;
28
+ /** onselect event handler */
29
+ onselect?: (val: SelectOption) => void;
30
+ /** onclear event handler */
31
+ onclear?: MouseEventHandler<HTMLButtonElement>;
32
+ /** Select ref */
33
+ ref?: HTMLInputElement | HTMLButtonElement;
34
+ /** How round should the border radius be? */
35
+ roundness?: ComponentRoundness;
36
+ /** Add a border around the button. Default True */
37
+ outline?: boolean;
38
+ /** Select value */
39
+ value: SelectOption | undefined;
40
+ /** How round should the border radius be? */
41
+ placeholder?: string;
42
+ /** Placeholder if there are no options found*/
43
+ optionsPlaceholder?: string;
44
+ /** disabled state */
45
+ disabled?: boolean;
46
+ /** Read only ? */
47
+ readonly?: boolean;
48
+ /** is there any associated Error ? */
49
+ error?: boolean;
50
+ /** Name */
51
+ name?: string;
52
+ /** Id */
53
+ id?: string;
54
+ /** Icon before button content */
55
+ before?: Snippet;
56
+ /** Icon after button content */
57
+ after?: Snippet;
58
+ /** Custom css class*/
59
+ class?: string;
60
+ /** onchange event handler */
61
+ onchange?: ChangeEventHandler<HTMLInputElement>;
62
+ /** oninput event handler */
63
+ oninput?: FormEventHandler<HTMLInputElement>;
64
+ /** onblur event handler */
65
+ onblur?: FocusEventHandler<HTMLInputElement | HTMLButtonElement>;
66
+ /** onfocus event handler */
67
+ onfocus?: FocusEventHandler<HTMLInputElement | HTMLButtonElement>;
68
+ /** onpaste event handler */
69
+ onpaste?: ClipboardEventHandler<HTMLInputElement>;
70
+ /** oncopy event handler */
71
+ oncopy?: ClipboardEventHandler<HTMLInputElement>;
72
+ /** oncut event handler */
73
+ oncut?: ClipboardEventHandler<HTMLInputElement>;
74
+ /** custom Content Formatting for variant button */
75
+ customInputContent?: (val: SelectOption) => Snippet;
76
+ /** custom Content Formatting for variant button */
77
+ customMenuItemContent?: (val: SelectOption, selected: boolean) => Snippet;
78
+ /** Custom Popup Content */
79
+ customPopupContent?: (options: SelectOption[], selectedOption: SelectOption) => Snippet;
80
+ /** custom Content Formatting for variant button */
81
+ customPlaceholderMenuItemContent?: () => Snippet;
82
+ /** PopperPopup Max height. Use css compatible value */
83
+ popupMaxHeight?: string;
84
+ /** PaperProps: Paper component props for Popup */
85
+ paperProps?: Partial<PaperProps>;
86
+ /** PopperProps: Popper component props */
87
+ popperProps?: Partial<PopperProps>;
88
+ /** MenuProps: Menu component props */
89
+ menuProps?: Partial<MenuProps>;
90
+ /** MenuItem: Menu component props */
91
+ menuItemProps?: Partial<MenuItemProps>;
92
+ }
93
+ </script>
94
+
95
+ <script lang="ts">
96
+ import InputEnclosure from '$lib/stories/developer tools/components/InputEnclosure/InputEnclosure.svelte';
97
+ import UtilityButton from '$lib/stories/developer tools/components/UtilityButton/UtilityButton.svelte';
98
+ import Icon from '@iconify/svelte';
99
+ import {
100
+ DynamicInput,
101
+ Menu,
102
+ MenuItem,
103
+ Popper,
104
+ type DynamicInputFocusEvent,
105
+ type MenuItemProps,
106
+ type MenuProps,
107
+ type PaperProps,
108
+ type PopperProps,
109
+ } from '$lib/index.js';
110
+ import type { TextInputInputEvent } from '../TextInput/TextInput.svelte';
111
+ import type { ButtonClickEvent } from '../Button/Button.svelte';
112
+
113
+ let {
114
+ size = 'normal',
115
+ roundness = 1,
116
+ outline = true,
117
+ name,
118
+ id,
119
+ class: className = '',
120
+ disabled = false,
121
+ onchange,
122
+ oninput,
123
+ onselect,
124
+ onblur,
125
+ onfocus,
126
+ onpaste,
127
+ oncopy,
128
+ oncut,
129
+ before,
130
+ after,
131
+ error = false,
132
+ value,
133
+ placeholder,
134
+ ref = $bindable<HTMLInputElement | HTMLButtonElement>(),
135
+ readonly = false,
136
+ searchable = false,
137
+ clearable = false,
138
+ onclear,
139
+ options: optionsRaw,
140
+ customInputContent: customInputContentInternal,
141
+ customMenuItemContent: customMenuItemContentInternal,
142
+ customPopupContent: customPopupContentInternal,
143
+ customPlaceholderMenuItemContent: customPlaceholderMenuItemContentInternal,
144
+ popupMaxHeight = '300px',
145
+ paperProps,
146
+ popperProps,
147
+ menuProps,
148
+ menuItemProps,
149
+ optionsPlaceholder = 'No Options',
150
+ }: SelectProps = $props();
151
+
152
+ let open: boolean = $state(false);
153
+ let onInputStart: boolean = $state(false);
154
+ const selectedOption = $derived(value);
155
+ let searchTerm = $state(value?.label.trim() || '');
156
+ let options = $state(optionsRaw);
157
+ let menuRef = $state<HTMLUListElement | undefined>(undefined);
158
+ let menuItemIndex = $state(0);
159
+
160
+ $effect(() => {
161
+ if (!onInputStart) {
162
+ options = optionsRaw;
163
+ return;
164
+ }
165
+
166
+ const searchTermSimplified = searchTerm.trim().toLowerCase();
167
+
168
+ if (!searchTermSimplified) {
169
+ options = optionsRaw;
170
+ return;
171
+ }
172
+
173
+ options = optionsRaw.filter((item) =>
174
+ item.label.trim().toLowerCase().includes(searchTermSimplified),
175
+ );
176
+ });
177
+
178
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
+ let customInputContentTyped = customInputContentInternal as any;
180
+
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ let customMenuItemContentTyped = customMenuItemContentInternal as any;
183
+
184
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
+ let customPopupContentTyped = customPopupContentInternal as any;
186
+
187
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
188
+ let customPlaceholderMenuItemContentTyped = customPlaceholderMenuItemContentInternal as any;
189
+
190
+ function closeMenu() {
191
+ open = false;
192
+ menuItemIndex = 0;
193
+
194
+ ref?.blur();
195
+ }
196
+
197
+ function openMenu() {
198
+ 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
+ }
208
+
209
+ function onfocusMod(e: DynamicInputFocusEvent) {
210
+ openMenu();
211
+
212
+ if (onfocus) {
213
+ onfocus(e);
214
+ }
215
+ }
216
+
217
+ function onblurMod(e: DynamicInputFocusEvent) {
218
+ if (onblur) {
219
+ onblur(e);
220
+ }
221
+ }
222
+
223
+ function onClickOutside() {
224
+ searchTerm = selectedOption?.label || '';
225
+ onInputStart = false;
226
+
227
+ closeMenu();
228
+ }
229
+
230
+ function onselectMod(val: SelectOption) {
231
+ searchTerm = val.label;
232
+ onInputStart = false;
233
+
234
+ closeMenu();
235
+
236
+ if (onselect) {
237
+ onselect(val);
238
+ }
239
+ }
240
+
241
+ function oninputMod(e: TextInputInputEvent) {
242
+ const target = e.target as HTMLInputElement;
243
+ searchTerm = target.value;
244
+ onInputStart = true;
245
+
246
+ if (oninput) {
247
+ oninput(e);
248
+ }
249
+ }
250
+
251
+ function onclearMod(e: ButtonClickEvent) {
252
+ searchTerm = '';
253
+ onInputStart = false;
254
+
255
+ closeMenu();
256
+
257
+ if (onclear) {
258
+ onclear(e);
259
+ }
260
+ }
261
+
262
+ function onKeyBoardEnter(selectedItemIndex: number) {
263
+ const targetOption = options[selectedItemIndex];
264
+
265
+ if (!targetOption) {
266
+ return;
267
+ }
268
+
269
+ if (targetOption.disabled) {
270
+ return;
271
+ }
272
+
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;
341
+ }
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
+ </script>
370
+
371
+ <div class={['dodo-ui-Select', className].join(' ')}>
372
+ <Popper fullWidth {open} {onClickOutside} {...popperProps} {popupMaxHeight} {paperProps}>
373
+ <div
374
+ class:outline
375
+ class:disabled
376
+ class:error
377
+ class={[
378
+ 'Select',
379
+ `size--${size}`,
380
+ `${open ? 'focused' : ''}`,
381
+ `roundness--${roundness}`,
382
+ className,
383
+ ].join(' ')}
384
+ >
385
+ <InputEnclosure
386
+ {outline}
387
+ {disabled}
388
+ {error}
389
+ focused={open}
390
+ {size}
391
+ {roundness}
392
+ {before}
393
+ {after}
394
+ >
395
+ <DynamicInput
396
+ type="text"
397
+ {name}
398
+ {id}
399
+ {disabled}
400
+ bind:ref
401
+ oninput={oninputMod}
402
+ {onchange}
403
+ onfocus={onfocusMod}
404
+ onblur={onblurMod}
405
+ {onpaste}
406
+ {oncopy}
407
+ {oncut}
408
+ {placeholder}
409
+ value={searchable ? searchTerm : selectedOption?.label}
410
+ {readonly}
411
+ variant={searchable ? 'input' : 'button'}
412
+ >
413
+ {#snippet customInputContent()}
414
+ {#if customInputContentTyped}
415
+ {@render customInputContentTyped(selectedOption)}
416
+ {:else}
417
+ {selectedOption?.label || placeholder}
418
+ {/if}
419
+ {/snippet}
420
+ </DynamicInput>
421
+
422
+ {#if selectedOption?.label && clearable && !disabled}
423
+ <div class:after class="SelectClear">
424
+ <UtilityButton {size} title="Clear" onclick={onclearMod}>
425
+ <Icon icon="material-symbols:close-small" width="24" height="24" />
426
+ </UtilityButton>
427
+ </div>
428
+ {/if}
429
+ </InputEnclosure>
430
+ </div>
431
+
432
+ {#snippet popupChildren()}
433
+ {#if customPopupContentTyped}
434
+ {@render customPopupContentTyped(options, selectedOption)}
435
+ {: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>
466
+ {/if}
467
+ {/snippet}
468
+ </Popper>
469
+ </div>
470
+
471
+ <style lang="scss">
472
+ .dodo-ui-Select {
473
+ .Select {
474
+ &.size {
475
+ &--normal {
476
+ .SelectClear {
477
+ &.after {
478
+ margin-right: calc(var(--dodo-ui-space-small) * 2);
479
+ }
480
+ }
481
+ }
482
+
483
+ &--small {
484
+ .SelectClear {
485
+ &.after {
486
+ margin-right: var(--dodo-ui-space);
487
+ }
488
+ }
489
+ }
490
+
491
+ &--large {
492
+ .SelectClear {
493
+ &.after {
494
+ margin-right: calc(var(--dodo-ui-space) * 2);
495
+ }
496
+ }
497
+ }
498
+ }
499
+ }
500
+ }
501
+ </style>
@@ -1,4 +1,15 @@
1
1
  <script lang="ts" module>
2
+ import type { ComponentRoundness } from '$lib/types/roundness.js';
3
+ import type { ComponentSize } from '$lib/types/size.js';
4
+
5
+ import type { Snippet } from 'svelte';
6
+ import type {
7
+ ChangeEventHandler,
8
+ ClipboardEventHandler,
9
+ FocusEventHandler,
10
+ FormEventHandler,
11
+ } from 'svelte/elements';
12
+
2
13
  export type TextInputType = 'text' | 'tel' | 'email' | 'password' | 'url' | 'number';
3
14
 
4
15
  export const textInputTypeArray: TextInputType[] = [
@@ -17,22 +28,12 @@
17
28
  export type TextInputClipboardEvent = ClipboardEvent & {
18
29
  currentTarget: EventTarget & HTMLInputElement;
19
30
  };
20
- </script>
21
-
22
- <script lang="ts">
23
- import InputEnclosure from '$lib/stories/developer tools/components/InputEnclosure/InputEnclosure.svelte';
24
- import type { ComponentRoundness } from '$lib/types/roundness.js';
25
- import type { ComponentSize } from '$lib/types/size.js';
26
31
 
27
- import type { Snippet } from 'svelte';
28
- import type {
29
- ChangeEventHandler,
30
- ClipboardEventHandler,
31
- FocusEventHandler,
32
- FormEventHandler,
33
- } from 'svelte/elements';
32
+ export type TextInputInputEvent = Event & {
33
+ currentTarget: EventTarget & HTMLInputElement;
34
+ };
34
35
 
35
- interface TextInputProps {
36
+ export interface TextInputProps {
36
37
  /** Input type? */
37
38
  type?: TextInputType;
38
39
  /** Input ref */
@@ -78,6 +79,13 @@
78
79
  /** oncut event handler */
79
80
  oncut?: ClipboardEventHandler<HTMLInputElement>;
80
81
  }
82
+ </script>
83
+
84
+ <script lang="ts">
85
+ import InputEnclosure from '$lib/stories/developer tools/components/InputEnclosure/InputEnclosure.svelte';
86
+ import DynamicInput, {
87
+ type DynamicInputFocusEvent,
88
+ } from '$lib/stories/developer tools/components/DynamicInput/DynamicInput.svelte';
81
89
 
82
90
  let {
83
91
  type = 'text',
@@ -106,19 +114,21 @@
106
114
 
107
115
  let focused: boolean = $state(false);
108
116
 
109
- function onfocusMod(e: TextInputFocusEvent) {
117
+ function onfocusMod(e: DynamicInputFocusEvent) {
118
+ const eTyped = e as TextInputFocusEvent;
110
119
  focused = true;
111
120
 
112
121
  if (onfocus) {
113
- onfocus(e);
122
+ onfocus(eTyped);
114
123
  }
115
124
  }
116
125
 
117
- function onblurMod(e: TextInputFocusEvent) {
126
+ function onblurMod(e: DynamicInputFocusEvent) {
127
+ const eTyped = e as TextInputFocusEvent;
118
128
  focused = false;
119
129
 
120
130
  if (onblur) {
121
- onblur(e);
131
+ onblur(eTyped);
122
132
  }
123
133
  }
124
134
  </script>
@@ -131,11 +141,12 @@
131
141
  class={['dodo-ui-TextInput', `size--${size}`, `roundness--${roundness}`, className].join(' ')}
132
142
  >
133
143
  <InputEnclosure {outline} {disabled} {error} {focused} {size} {roundness} {before} {after}>
134
- <input
144
+ <DynamicInput
135
145
  {type}
136
146
  {name}
137
147
  {id}
138
148
  {disabled}
149
+ bind:ref
139
150
  {oninput}
140
151
  {onchange}
141
152
  onfocus={onfocusMod}
@@ -144,47 +155,9 @@
144
155
  {oncopy}
145
156
  {oncut}
146
157
  {placeholder}
147
- {readonly}
148
158
  bind:value
149
- bind:this={ref}
159
+ {readonly}
160
+ variant="input"
150
161
  />
151
162
  </InputEnclosure>
152
163
  </div>
153
-
154
- <style lang="scss">
155
- .dodo-ui-TextInput {
156
- input {
157
- flex: 1;
158
- border: 0;
159
- outline: 0;
160
- height: 100%;
161
- background-color: transparent;
162
- font-family: inherit;
163
- color: inherit;
164
- letter-spacing: 0.3px;
165
- }
166
-
167
- &.size {
168
- &--normal {
169
- input {
170
- font-size: 1rem;
171
- padding: 0 calc(var(--dodo-ui-space-small) * 2);
172
- }
173
- }
174
-
175
- &--small {
176
- input {
177
- padding: 0 var(--dodo-ui-space);
178
- font-size: 0.9rem;
179
- }
180
- }
181
-
182
- &--large {
183
- input {
184
- font-size: 1.1rem;
185
- padding: 0 calc(var(--dodo-ui-space) * 2);
186
- }
187
- }
188
- }
189
- }
190
- </style>
@@ -0,0 +1,65 @@
1
+ <script lang="ts" module>
2
+ import { type Snippet } from 'svelte';
3
+ import type { ComponentSize } from '$lib/types/size.js';
4
+
5
+ export interface MenuProps {
6
+ /** Menu contents goes here */
7
+ children?: Snippet;
8
+ /** Menu ref */
9
+ ref?: HTMLUListElement;
10
+ /** Custom css class */
11
+ class?: string;
12
+ /** Menu Width */
13
+ width?: string;
14
+ /** Menu Height */
15
+ height?: string;
16
+ /** How large should the Menu Items be? */
17
+ size?: ComponentSize;
18
+ /** Menu Separator */
19
+ separator?: boolean;
20
+ /** Id */
21
+ id?: string;
22
+ }
23
+ </script>
24
+
25
+ <script lang="ts">
26
+ import { setContext } from 'svelte';
27
+
28
+ let {
29
+ children,
30
+ id,
31
+ class: className = '',
32
+ width,
33
+ height,
34
+ ref = $bindable<HTMLUListElement>(),
35
+ size,
36
+ separator,
37
+ }: MenuProps = $props();
38
+
39
+ setContext('MenuItemSize', () => size);
40
+ setContext('MenuItemSeparator', () => separator);
41
+ </script>
42
+
43
+ <ul
44
+ class={['dodo-ui-Menu', className].join(' ')}
45
+ {id}
46
+ bind:this={ref}
47
+ style={`${width ? `width:${width};` : ''}
48
+ ${height ? `height:${height};` : ''}
49
+ `}
50
+ >
51
+ {#if children}
52
+ {@render children()}
53
+ {/if}
54
+ </ul>
55
+
56
+ <style lang="scss">
57
+ .dodo-ui-Menu {
58
+ margin: 0;
59
+ padding: 0;
60
+ display: flex;
61
+ flex-direction: column;
62
+ overflow: inherit;
63
+ outline: 0;
64
+ }
65
+ </style>