@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.
- package/dist/index.d.ts +18 -2
- package/dist/index.js +11 -0
- package/dist/stories/components/Form/NumericInput/Events/Events.stories.svelte +126 -0
- package/dist/stories/components/Form/NumericInput/Events/Events.stories.svelte.d.ts +18 -0
- package/dist/stories/components/Form/NumericInput/NumericInput.stories.svelte +79 -0
- package/dist/stories/components/Form/NumericInput/NumericInput.stories.svelte.d.ts +21 -0
- package/dist/stories/components/Form/NumericInput/NumericInput.svelte +161 -0
- package/dist/stories/components/Form/NumericInput/NumericInput.svelte.d.ts +69 -0
- package/dist/stories/components/Form/NumericInput/Validation/Validation.stories.svelte +84 -0
- package/dist/stories/components/Form/NumericInput/Validation/Validation.stories.svelte.d.ts +18 -0
- package/dist/stories/components/Form/PasswordInput/Events/Events.stories.svelte +27 -6
- package/dist/stories/components/Form/PasswordInput/PasswordInput.svelte +5 -3
- package/dist/stories/components/Form/PasswordInput/PasswordInput.svelte.d.ts +7 -1
- package/dist/stories/components/Form/Select/Customize/Customize.stories.svelte +16 -1
- package/dist/stories/components/Form/Select/DropDownArrow/DropDownArrow.stories.svelte +59 -0
- package/dist/stories/components/Form/Select/DropDownArrow/DropDownArrow.stories.svelte.d.ts +18 -0
- package/dist/stories/components/Form/Select/Events/Events.stories.svelte +27 -0
- package/dist/stories/components/Form/Select/Positions/AutoPosition/AutoPosition.stories.svelte +54 -0
- package/dist/stories/components/Form/Select/Positions/AutoPosition/AutoPosition.stories.svelte.d.ts +18 -0
- package/dist/stories/components/Form/Select/Positions/Positions.stories.svelte +83 -0
- package/dist/stories/components/Form/Select/Positions/Positions.stories.svelte.d.ts +18 -0
- package/dist/stories/components/Form/Select/Select.svelte +110 -123
- package/dist/stories/components/Form/Select/Select.svelte.d.ts +22 -3
- package/dist/stories/components/Form/TextInput/Events/Events.stories.svelte +27 -0
- package/dist/stories/components/Form/TextInput/TextInput.svelte +5 -3
- package/dist/stories/components/Form/TextInput/TextInput.svelte.d.ts +10 -1
- package/dist/stories/components/Layout/Menu/DynamicMenu/Customize/Customize.stories.svelte +28 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/Customize/Customize.stories.svelte.d.ts +18 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.stories.svelte +51 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.stories.svelte.d.ts +22 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.svelte +129 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.svelte.d.ts +81 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/Events/Events.stories.svelte +46 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/Events/Events.stories.svelte.d.ts +18 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/KeybaordNavigation/KeybaordNavigation.stories.svelte +27 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/KeybaordNavigation/KeybaordNavigation.stories.svelte.d.ts +18 -0
- package/dist/stories/components/Layout/Menu/DynamicMenu/Options/OptionFormat.mdx +72 -0
- package/dist/stories/components/Layout/Menu/MenuItem/MenuItem.svelte.d.ts +1 -1
- package/dist/stories/developer tools/components/DynamicInput/DynamicInput.svelte +23 -4
- package/dist/stories/developer tools/components/DynamicInput/DynamicInput.svelte.d.ts +13 -2
- package/dist/stories/developer tools/components/DynamicInput/Events/Events.stories.svelte +115 -0
- package/dist/stories/developer tools/components/DynamicInput/Events/Events.stories.svelte.d.ts +18 -0
- package/dist/stories/developer tools/helpers/Numbers/cleanNumericString/cleanNumericString.d.ts +13 -0
- package/dist/stories/developer tools/helpers/Numbers/cleanNumericString/cleanNumericString.js +26 -0
- package/dist/stories/developer tools/helpers/Numbers/cleanNumericString/index.mdx +20 -0
- package/dist/stories/developer tools/helpers/Numbers/isValidNumberValue/index.mdx +71 -0
- package/dist/stories/developer tools/helpers/Numbers/isValidNumberValue/isValidNumberValue.d.ts +51 -0
- package/dist/stories/developer tools/helpers/Numbers/isValidNumberValue/isValidNumberValue.js +96 -0
- package/dist/stories/developer tools/helpers/logger/index.mdx +63 -0
- package/dist/stories/developer tools/helpers/logger/logger.d.ts +24 -0
- package/dist/stories/developer tools/helpers/logger/logger.js +31 -0
- package/package.json +15 -17
- package/src/lib/index.ts +33 -0
- package/src/lib/stories/Home.mdx +59 -0
- package/src/lib/stories/assets/dark-theme-toggle.png +0 -0
- package/src/lib/stories/components/Form/Button/Button.stories.svelte +61 -0
- package/src/lib/stories/components/Form/Button/Color/Color.stories.svelte +43 -0
- package/src/lib/stories/components/Form/Button/Events/Events.stories.svelte +36 -0
- package/src/lib/stories/components/Form/Button/IconButton/IconButton.stories.svelte +43 -0
- package/src/lib/stories/components/Form/Button/Roundness/Roundness.stories.svelte +23 -0
- package/src/lib/stories/components/Form/Button/Size/Size.stories.svelte +16 -0
- package/src/lib/stories/components/Form/Button/Variant/Variant.stories.svelte +18 -0
- package/src/lib/stories/components/Form/FormControl/FormControl.stories.svelte +28 -0
- package/src/lib/stories/components/Form/Label/Label.stories.svelte +13 -0
- package/src/lib/stories/components/Form/Message/Color/Color.stories.svelte +36 -0
- package/src/lib/stories/components/Form/Message/Message.stories.svelte +27 -0
- package/src/lib/stories/components/Form/Message/Size/Size.stories.svelte +22 -0
- package/src/lib/stories/components/Form/NumericInput/Events/Events.stories.svelte +134 -0
- package/src/lib/stories/components/Form/NumericInput/NumericInput.stories.svelte +84 -0
- package/src/lib/stories/components/Form/NumericInput/NumericInput.svelte +286 -0
- package/src/lib/stories/components/Form/NumericInput/Validation/Validation.stories.svelte +87 -0
- package/src/lib/stories/components/Form/PasswordInput/Events/Events.stories.svelte +132 -0
- package/src/lib/stories/components/Form/PasswordInput/PasswordInput.stories.svelte +59 -0
- package/src/lib/stories/components/Form/PasswordInput/PasswordInput.svelte +15 -3
- package/src/lib/stories/components/Form/PasswordInput/Roundness/Roundness.stories.svelte +20 -0
- package/src/lib/stories/components/Form/PasswordInput/Size/Size.stories.svelte +16 -0
- package/src/lib/stories/components/Form/PasswordInput/WithIcon/WithIcon.stories.svelte +31 -0
- package/src/lib/stories/components/Form/Select/Customize/Customize.stories.svelte +139 -0
- package/src/lib/stories/components/Form/Select/DropDownArrow/DropDownArrow.stories.svelte +63 -0
- package/src/lib/stories/components/Form/Select/Events/Events.stories.svelte +174 -0
- package/src/lib/stories/components/Form/Select/Options/OptionFormat.mdx +40 -0
- package/src/lib/stories/components/Form/Select/Positions/AutoPosition/AutoPosition.stories.svelte +58 -0
- package/src/lib/stories/components/Form/Select/Positions/Positions.stories.svelte +87 -0
- package/src/lib/stories/components/Form/Select/Roundness/Roundness.stories.svelte +32 -0
- package/src/lib/stories/components/Form/Select/Select.stories.svelte +121 -0
- package/src/lib/stories/components/Form/Select/Select.svelte +166 -146
- package/src/lib/stories/components/Form/Select/Size/Size.stories.svelte +28 -0
- package/src/lib/stories/components/Form/Select/WithIcon/WithIcon.stories.svelte +43 -0
- package/src/lib/stories/components/Form/TextInput/Events/Events.stories.svelte +125 -0
- package/src/lib/stories/components/Form/TextInput/Roundness/Roundness.stories.svelte +21 -0
- package/src/lib/stories/components/Form/TextInput/Size/Size.stories.svelte +17 -0
- package/src/lib/stories/components/Form/TextInput/TextInput.stories.svelte +43 -0
- package/src/lib/stories/components/Form/TextInput/TextInput.svelte +18 -3
- package/src/lib/stories/components/Form/TextInput/WithIcon/WithIcon.stories.svelte +47 -0
- package/src/lib/stories/components/Layout/Menu/DynamicMenu/Customize/Customize.stories.svelte +30 -0
- package/src/lib/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.stories.svelte +56 -0
- package/src/lib/stories/components/Layout/Menu/DynamicMenu/DynamicMenu.svelte +262 -0
- package/src/lib/stories/components/Layout/Menu/DynamicMenu/Events/Events.stories.svelte +48 -0
- package/src/lib/stories/components/Layout/Menu/DynamicMenu/KeybaordNavigation/KeybaordNavigation.stories.svelte +29 -0
- package/src/lib/stories/components/Layout/Menu/DynamicMenu/Options/OptionFormat.mdx +72 -0
- package/src/lib/stories/components/Layout/Menu/Menu.stories.svelte +69 -0
- package/src/lib/stories/components/Layout/Menu/MenuItem/MenuItem.stories.svelte +34 -0
- package/src/lib/stories/components/Layout/Menu/MenuItem/MenuItem.svelte +1 -1
- package/src/lib/stories/components/Layout/Menu/MenuItem/Size/Size.stories.svelte +16 -0
- package/src/lib/stories/components/Layout/Menu/MenuItem/Type/Type.stories.svelte +21 -0
- package/src/lib/stories/components/Layout/Menu/Size/Size.stories.svelte +37 -0
- package/src/lib/stories/components/Layout/Paper/Color/Color.stories.svelte +63 -0
- package/src/lib/stories/components/Layout/Paper/Paper.stories.svelte +50 -0
- package/src/lib/stories/components/Layout/Paper/Roundness/Roundness.stories.svelte +25 -0
- package/src/lib/stories/components/Layout/Paper/Shadow/Shadow.stories.svelte +24 -0
- package/src/lib/stories/components/Layout/Separator/Color/Color.stories.svelte +19 -0
- package/src/lib/stories/components/Layout/Separator/Separator.stories.svelte +30 -0
- package/src/lib/stories/developer tools/Intro.mdx +9 -0
- package/src/lib/stories/developer tools/components/DynamicInput/DynamicInput.stories.svelte +53 -0
- package/src/lib/stories/developer tools/components/DynamicInput/DynamicInput.svelte +43 -3
- package/src/lib/stories/developer tools/components/DynamicInput/Events/Events.stories.svelte +121 -0
- package/src/lib/stories/developer tools/components/DynamicInput/Size/Size.stories.svelte +17 -0
- package/src/lib/stories/developer tools/components/InputEnclosure/InputEnclosure.stories.svelte +38 -0
- package/src/lib/stories/developer tools/components/InputEnclosure/Roundness/Roundness.stories.svelte +20 -0
- package/src/lib/stories/developer tools/components/InputEnclosure/Size/Size.stories.svelte +16 -0
- package/src/lib/stories/developer tools/components/InputEnclosure/WithIcon/WithIcon.stories.svelte +47 -0
- package/src/lib/stories/developer tools/components/Popper/Popper.stories.svelte +124 -0
- package/src/lib/stories/developer tools/components/Popper/PopperPopup/PopperPopup.stories.svelte +64 -0
- package/src/lib/stories/developer tools/components/Popper/Positions/AutoPosition/AutoPosition.stories.svelte +92 -0
- package/src/lib/stories/developer tools/components/Popper/Positions/Positions.stories.svelte +114 -0
- package/src/lib/stories/developer tools/components/UtilityButton/Size/Size.stories.svelte +25 -0
- package/src/lib/stories/developer tools/components/UtilityButton/UtilityButton.stories.svelte +30 -0
- package/src/lib/stories/developer tools/directives/clickOutside/index.mdx +25 -0
- package/src/lib/stories/developer tools/helpers/Numbers/cleanNumericString/cleanNumericString.ts +27 -0
- package/src/lib/stories/developer tools/helpers/Numbers/cleanNumericString/index.mdx +20 -0
- package/src/lib/stories/developer tools/helpers/Numbers/isValidNumberValue/index.mdx +71 -0
- package/src/lib/stories/developer tools/helpers/Numbers/isValidNumberValue/isValidNumberValue.ts +156 -0
- package/src/lib/stories/developer tools/helpers/logger/index.mdx +63 -0
- package/src/lib/stories/developer tools/helpers/logger/logger.ts +46 -0
- package/src/lib/stories/developer tools/philosophy/Colors/Colors.mdx +43 -0
- package/src/lib/stories/developer tools/philosophy/Colors/Colors.stories.svelte +22 -0
- package/src/lib/stories/developer tools/philosophy/Colors/Opacity.stories.svelte +11 -0
- package/src/lib/stories/developer tools/philosophy/Themes.mdx +23 -0
|
@@ -0,0 +1,17 @@
|
|
|
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="Normal" />
|
|
16
|
+
<Story name="Small" args={{ size: 'small' }} />
|
|
17
|
+
<Story name="Large" args={{ size: 'large' }} />
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import TextInput, { textInputTypeArray } from './TextInput.svelte';
|
|
4
|
+
import type { StoryBookArgTypes } from '$lib/storybook-types.js';
|
|
5
|
+
import { componentRoundnessArray } from '$lib/types/roundness.js';
|
|
6
|
+
import { componentSizeArray } from '$lib/types/size.js';
|
|
7
|
+
|
|
8
|
+
export const storyTextInputArgTypes: StoryBookArgTypes = {
|
|
9
|
+
type: {
|
|
10
|
+
control: { type: 'select' },
|
|
11
|
+
options: textInputTypeArray,
|
|
12
|
+
},
|
|
13
|
+
roundness: {
|
|
14
|
+
control: { type: 'select' },
|
|
15
|
+
options: componentRoundnessArray,
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
control: { type: 'select' },
|
|
19
|
+
options: componentSizeArray,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
|
|
24
|
+
const { Story } = defineMeta({
|
|
25
|
+
component: TextInput,
|
|
26
|
+
tags: ['autodocs'],
|
|
27
|
+
argTypes: storyTextInputArgTypes,
|
|
28
|
+
args: { value: 'Hello world!' },
|
|
29
|
+
});
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<!-- TextInput with default style -->
|
|
33
|
+
<Story name="Default" />
|
|
34
|
+
|
|
35
|
+
<Story name="Placeholder" args={{ value: '', placeholder: 'Type something...' }} />
|
|
36
|
+
|
|
37
|
+
<Story name="No Outline" args={{ outline: false }} />
|
|
38
|
+
|
|
39
|
+
<Story name="Error" args={{ error: true }} />
|
|
40
|
+
|
|
41
|
+
<Story name="Disabled" args={{ disabled: true }} />
|
|
42
|
+
|
|
43
|
+
<Story name="Read only" args={{ readonly: true }} />
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
ClipboardEventHandler,
|
|
9
9
|
FocusEventHandler,
|
|
10
10
|
FormEventHandler,
|
|
11
|
+
KeyboardEventHandler,
|
|
11
12
|
} from 'svelte/elements';
|
|
12
13
|
|
|
13
14
|
export type TextInputType = 'text' | 'tel' | 'email' | 'password' | 'url' | 'number';
|
|
@@ -33,6 +34,10 @@
|
|
|
33
34
|
currentTarget: EventTarget & HTMLInputElement;
|
|
34
35
|
};
|
|
35
36
|
|
|
37
|
+
export type TextInputKeyboardEvent = KeyboardEvent & {
|
|
38
|
+
currentTarget: EventTarget & HTMLInputElement;
|
|
39
|
+
};
|
|
40
|
+
|
|
36
41
|
export interface TextInputProps {
|
|
37
42
|
/** Input type? */
|
|
38
43
|
type?: TextInputType;
|
|
@@ -78,6 +83,12 @@
|
|
|
78
83
|
oncopy?: ClipboardEventHandler<HTMLInputElement>;
|
|
79
84
|
/** oncut event handler */
|
|
80
85
|
oncut?: ClipboardEventHandler<HTMLInputElement>;
|
|
86
|
+
/** onkeydown event handler */
|
|
87
|
+
onkeydown?: KeyboardEventHandler<HTMLInputElement>;
|
|
88
|
+
/** onkeypress event handler */
|
|
89
|
+
onkeypress?: KeyboardEventHandler<HTMLInputElement>;
|
|
90
|
+
/** onkeyup event handler */
|
|
91
|
+
onkeyup?: KeyboardEventHandler<HTMLInputElement>;
|
|
81
92
|
}
|
|
82
93
|
</script>
|
|
83
94
|
|
|
@@ -103,6 +114,9 @@
|
|
|
103
114
|
onpaste,
|
|
104
115
|
oncopy,
|
|
105
116
|
oncut,
|
|
117
|
+
onkeydown,
|
|
118
|
+
onkeypress,
|
|
119
|
+
onkeyup,
|
|
106
120
|
before,
|
|
107
121
|
after,
|
|
108
122
|
error = false,
|
|
@@ -116,8 +130,6 @@
|
|
|
116
130
|
|
|
117
131
|
function onfocusMod(e: DynamicInputFocusEvent) {
|
|
118
132
|
const eTyped = e as TextInputFocusEvent;
|
|
119
|
-
focused = true;
|
|
120
|
-
|
|
121
133
|
if (onfocus) {
|
|
122
134
|
onfocus(eTyped);
|
|
123
135
|
}
|
|
@@ -125,7 +137,6 @@
|
|
|
125
137
|
|
|
126
138
|
function onblurMod(e: DynamicInputFocusEvent) {
|
|
127
139
|
const eTyped = e as TextInputFocusEvent;
|
|
128
|
-
focused = false;
|
|
129
140
|
|
|
130
141
|
if (onblur) {
|
|
131
142
|
onblur(eTyped);
|
|
@@ -147,6 +158,7 @@
|
|
|
147
158
|
{id}
|
|
148
159
|
{disabled}
|
|
149
160
|
bind:ref
|
|
161
|
+
bind:focused
|
|
150
162
|
{oninput}
|
|
151
163
|
{onchange}
|
|
152
164
|
onfocus={onfocusMod}
|
|
@@ -154,6 +166,9 @@
|
|
|
154
166
|
{onpaste}
|
|
155
167
|
{oncopy}
|
|
156
168
|
{oncut}
|
|
169
|
+
onkeydown={onkeydown ? (e) => onkeydown(e as TextInputKeyboardEvent) : undefined}
|
|
170
|
+
onkeypress={onkeypress ? (e) => onkeypress(e as TextInputKeyboardEvent) : undefined}
|
|
171
|
+
onkeyup={onkeyup ? (e) => onkeyup(e as TextInputKeyboardEvent) : undefined}
|
|
157
172
|
{placeholder}
|
|
158
173
|
bind:value
|
|
159
174
|
{readonly}
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
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: TextInput,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
argTypes: storyTextInputArgTypes,
|
|
12
|
+
});
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<!-- TextInput icon in front. -->
|
|
16
|
+
<Story name="Icon Before" asChild>
|
|
17
|
+
<TextInput>
|
|
18
|
+
{#snippet before()}
|
|
19
|
+
<Icon icon="material-symbols:content-copy" />
|
|
20
|
+
{/snippet}
|
|
21
|
+
</TextInput>
|
|
22
|
+
</Story>
|
|
23
|
+
|
|
24
|
+
<!-- TextInput icon in front. -->
|
|
25
|
+
<Story name="Icon After" asChild>
|
|
26
|
+
<TextInput>
|
|
27
|
+
{#snippet after()}
|
|
28
|
+
<Icon icon="material-symbols:download-2" />
|
|
29
|
+
{/snippet}
|
|
30
|
+
</TextInput>
|
|
31
|
+
</Story>
|
|
32
|
+
|
|
33
|
+
<Story name="Small" args={{ size: 'small' }} asChild>
|
|
34
|
+
<TextInput size="small">
|
|
35
|
+
{#snippet before()}
|
|
36
|
+
<Icon icon="material-symbols:content-copy" />
|
|
37
|
+
{/snippet}
|
|
38
|
+
</TextInput>
|
|
39
|
+
</Story>
|
|
40
|
+
|
|
41
|
+
<Story name="Large" args={{ size: 'large' }} asChild>
|
|
42
|
+
<TextInput size="large">
|
|
43
|
+
{#snippet before()}
|
|
44
|
+
<Icon icon="material-symbols:content-copy" />
|
|
45
|
+
{/snippet}
|
|
46
|
+
</TextInput>
|
|
47
|
+
</Story>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import DynamicMenu from '../DynamicMenu.svelte';
|
|
4
|
+
import { dynamicMenuItemOptions, storyDynamicMenuArgTypes } from '../DynamicMenu.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: DynamicMenu,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: storyDynamicMenuArgTypes,
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<!-- Custom Menu Item Content -->
|
|
15
|
+
<Story name="CustomMenuItemContent" asChild>
|
|
16
|
+
<DynamicMenu options={dynamicMenuItemOptions}>
|
|
17
|
+
{#snippet customMenuItemContent(option, selectedOption)}
|
|
18
|
+
{selectedOption?.id === option.id ? '✅' : ''} {option.label} 💯💯💯
|
|
19
|
+
{/snippet}
|
|
20
|
+
</DynamicMenu>
|
|
21
|
+
</Story>
|
|
22
|
+
|
|
23
|
+
<!-- Custom Menu Item Placeholder Content -->
|
|
24
|
+
<Story name="CustomPlaceholderMenuItemContent" asChild>
|
|
25
|
+
<DynamicMenu options={[]} showOptionsPlaceholder>
|
|
26
|
+
{#snippet customPlaceholderMenuItemContent()}
|
|
27
|
+
No dice 💯💯💯
|
|
28
|
+
{/snippet}
|
|
29
|
+
</DynamicMenu>
|
|
30
|
+
</Story>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import DynamicMenu, { type DynamicMenuItemOption } from './DynamicMenu.svelte';
|
|
4
|
+
import type { StoryBookArgTypes } from '$lib/storybook-types.js';
|
|
5
|
+
|
|
6
|
+
export const storyDynamicMenuArgTypes: StoryBookArgTypes = {};
|
|
7
|
+
|
|
8
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
|
|
9
|
+
const { Story } = defineMeta({
|
|
10
|
+
component: DynamicMenu,
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
argTypes: storyDynamicMenuArgTypes,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const dynamicMenuItemOptions: DynamicMenuItemOption[] = [
|
|
16
|
+
{
|
|
17
|
+
id: '1',
|
|
18
|
+
label: 'One',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: '2',
|
|
22
|
+
label: 'Two',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: '3',
|
|
26
|
+
label: 'Three',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: '4',
|
|
30
|
+
label: 'Four',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: '5',
|
|
34
|
+
label: 'Five',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: '6',
|
|
38
|
+
disabled: true,
|
|
39
|
+
label: 'Five',
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<!-- Default DynamicMenu -->
|
|
45
|
+
<Story name="Default" args={{ options: dynamicMenuItemOptions }} />
|
|
46
|
+
|
|
47
|
+
<!-- Selected Option -->
|
|
48
|
+
<Story
|
|
49
|
+
name="SelectedOption"
|
|
50
|
+
args={{ options: dynamicMenuItemOptions, selectedOption: dynamicMenuItemOptions[1] }}
|
|
51
|
+
/>
|
|
52
|
+
|
|
53
|
+
<Story
|
|
54
|
+
name="ShowPlaceholder"
|
|
55
|
+
args={{ options: [], showOptionsPlaceholder: true, optionsPlaceholder: 'No content found.' }}
|
|
56
|
+
/>
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { ComponentSize } from '$lib/types/size.js';
|
|
3
|
+
import type { MenuItemProps, MenuItemType } from '../MenuItem/MenuItem.svelte';
|
|
4
|
+
|
|
5
|
+
export type DynamicMenuItemOption<TMeta = unknown> = {
|
|
6
|
+
id: string;
|
|
7
|
+
/** label */
|
|
8
|
+
label?: string;
|
|
9
|
+
/** Custom Metadata */
|
|
10
|
+
meta?: TMeta;
|
|
11
|
+
/** Menu Item type */
|
|
12
|
+
type?: MenuItemType;
|
|
13
|
+
/** Menu Separator */
|
|
14
|
+
separator?: boolean;
|
|
15
|
+
/** How large should the Menu Items be? */
|
|
16
|
+
size?: ComponentSize;
|
|
17
|
+
/** The onclick event handler */
|
|
18
|
+
onclick?: MouseEventHandler<HTMLButtonElement>;
|
|
19
|
+
/** disabled state */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/** link href */
|
|
22
|
+
href?: string;
|
|
23
|
+
/** Link button: download */
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
download?: any;
|
|
26
|
+
/** Link button: hreflang */
|
|
27
|
+
hreflang?: string | undefined | null;
|
|
28
|
+
/** Link button: media */
|
|
29
|
+
media?: string | undefined | null;
|
|
30
|
+
/** Link button: ping */
|
|
31
|
+
ping?: string | undefined | null;
|
|
32
|
+
/** Link button: rel */
|
|
33
|
+
rel?: string | undefined | null;
|
|
34
|
+
/** Link button: target */
|
|
35
|
+
target?: ButtonLinkTarget;
|
|
36
|
+
/** Link button: Type */
|
|
37
|
+
anchorMediaType?: string | undefined | null;
|
|
38
|
+
/** Link button: referrerpolicy */
|
|
39
|
+
referrerpolicy?: ButtonLinkReferrerpolicy;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export interface DynamicMenuProps {
|
|
43
|
+
/** Menu ref */
|
|
44
|
+
ref?: HTMLUListElement;
|
|
45
|
+
/** Custom css class */
|
|
46
|
+
class?: string;
|
|
47
|
+
/** Menu Width */
|
|
48
|
+
width?: string;
|
|
49
|
+
/** Menu Height */
|
|
50
|
+
height?: string;
|
|
51
|
+
/** How large should the Menu Items be? */
|
|
52
|
+
size?: ComponentSize;
|
|
53
|
+
/** Menu Separator */
|
|
54
|
+
separator?: boolean;
|
|
55
|
+
/** Id */
|
|
56
|
+
id?: string;
|
|
57
|
+
/** Options */
|
|
58
|
+
options?: DynamicMenuItemOption[];
|
|
59
|
+
/** Selected Option */
|
|
60
|
+
selectedOption?: DynamicMenuItemOption;
|
|
61
|
+
/** Custom Menu Item Content */
|
|
62
|
+
customMenuItemContent?: (
|
|
63
|
+
option: DynamicMenuItemOption,
|
|
64
|
+
selectedOption?: DynamicMenuItemOption,
|
|
65
|
+
) => Snippet;
|
|
66
|
+
/** Show Placeholder if no options */
|
|
67
|
+
showOptionsPlaceholder?: boolean;
|
|
68
|
+
/** placeholder Text if no options */
|
|
69
|
+
optionsPlaceholder?: string;
|
|
70
|
+
/** Custom Menu Item Placeholder Content */
|
|
71
|
+
customPlaceholderMenuItemContent?: () => Snippet;
|
|
72
|
+
/** MenuItem: Menu component props */
|
|
73
|
+
menuItemProps?: Partial<MenuItemProps>;
|
|
74
|
+
/** Enable Keyboard Navigation */
|
|
75
|
+
keyboardNavigation?: boolean;
|
|
76
|
+
/** Keyboard Navigation onEnter */
|
|
77
|
+
onEnter?: (e: KeyboardEvent, menuItemIndex: number) => void;
|
|
78
|
+
/** Keyboard Navigation onEsc */
|
|
79
|
+
onEsc?: (e: KeyboardEvent, menuItemIndex: number) => void;
|
|
80
|
+
/** On menu item click */
|
|
81
|
+
onclick?: (e: MouseEvent, option: DynamicMenuItemOption) => void;
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<script lang="ts">
|
|
86
|
+
import Menu from '../Menu.svelte';
|
|
87
|
+
import type { MouseEventHandler } from 'svelte/elements';
|
|
88
|
+
import type {
|
|
89
|
+
ButtonLinkReferrerpolicy,
|
|
90
|
+
ButtonLinkTarget,
|
|
91
|
+
} from '$lib/stories/components/Form/Button/Button.svelte';
|
|
92
|
+
import { MenuItem } from '$lib/index.js';
|
|
93
|
+
import type { Snippet } from 'svelte';
|
|
94
|
+
|
|
95
|
+
let {
|
|
96
|
+
id,
|
|
97
|
+
class: className = '',
|
|
98
|
+
ref = $bindable<HTMLUListElement>(),
|
|
99
|
+
size,
|
|
100
|
+
separator,
|
|
101
|
+
width,
|
|
102
|
+
height,
|
|
103
|
+
options,
|
|
104
|
+
selectedOption,
|
|
105
|
+
customMenuItemContent,
|
|
106
|
+
customPlaceholderMenuItemContent,
|
|
107
|
+
menuItemProps,
|
|
108
|
+
keyboardNavigation = false,
|
|
109
|
+
onEnter,
|
|
110
|
+
onEsc,
|
|
111
|
+
onclick,
|
|
112
|
+
showOptionsPlaceholder = false,
|
|
113
|
+
optionsPlaceholder = 'No options found',
|
|
114
|
+
}: DynamicMenuProps = $props();
|
|
115
|
+
|
|
116
|
+
let menuItemIndex = $state(0);
|
|
117
|
+
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
119
|
+
let customMenuItemContentTyped = customMenuItemContent as any;
|
|
120
|
+
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
+
let customPlaceholderMenuItemContentTyped = customPlaceholderMenuItemContent as any;
|
|
123
|
+
|
|
124
|
+
function onKeyboardNavigation(e: KeyboardEvent) {
|
|
125
|
+
let keyHit: string | undefined = undefined;
|
|
126
|
+
|
|
127
|
+
if (!ref) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
switch (e.key) {
|
|
132
|
+
case 'ArrowDown':
|
|
133
|
+
case 'ArrowUp':
|
|
134
|
+
keyHit = e.key;
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
break;
|
|
137
|
+
case 'Enter':
|
|
138
|
+
case 'Esc':
|
|
139
|
+
keyHit = e.key;
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!keyHit) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (keyHit === 'Enter') {
|
|
150
|
+
if (onEnter) {
|
|
151
|
+
onEnter(e, menuItemIndex);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return;
|
|
155
|
+
} else if (keyHit === 'Escape') {
|
|
156
|
+
if (onEsc) {
|
|
157
|
+
onEsc(e, menuItemIndex);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const listItems = ref.querySelectorAll(':scope > li.dodo-ui-MenuItem');
|
|
164
|
+
|
|
165
|
+
if (!listItems.length) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
for (let index = 0; index < listItems.length; index++) {
|
|
170
|
+
const element = listItems[index];
|
|
171
|
+
|
|
172
|
+
element.classList.remove('hover');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let newMenuItemIndex = menuItemIndex;
|
|
176
|
+
|
|
177
|
+
if (keyHit === 'ArrowDown') {
|
|
178
|
+
if (listItems[newMenuItemIndex + 1]) {
|
|
179
|
+
newMenuItemIndex = newMenuItemIndex + 1;
|
|
180
|
+
} else {
|
|
181
|
+
newMenuItemIndex = 0;
|
|
182
|
+
}
|
|
183
|
+
} else if (keyHit === 'ArrowUp') {
|
|
184
|
+
if (listItems[newMenuItemIndex - 1]) {
|
|
185
|
+
newMenuItemIndex = newMenuItemIndex - 1;
|
|
186
|
+
} else {
|
|
187
|
+
newMenuItemIndex = listItems.length - 1;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const targetItem = listItems[newMenuItemIndex] as HTMLLIElement;
|
|
192
|
+
|
|
193
|
+
targetItem.classList.add('hover');
|
|
194
|
+
|
|
195
|
+
targetItem.focus();
|
|
196
|
+
targetItem.scrollIntoView({ block: 'nearest' });
|
|
197
|
+
|
|
198
|
+
menuItemIndex = newMenuItemIndex;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
$effect(() => {
|
|
202
|
+
if (!keyboardNavigation) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const targetItem = ref.querySelector(':scope > li.dodo-ui-MenuItem.selected') as
|
|
207
|
+
| HTMLLIElement
|
|
208
|
+
| undefined;
|
|
209
|
+
|
|
210
|
+
if (targetItem) {
|
|
211
|
+
const selectedIndex = options?.findIndex((option) => option.id === selectedOption?.id);
|
|
212
|
+
|
|
213
|
+
if (selectedIndex !== undefined && selectedIndex >= 0) {
|
|
214
|
+
menuItemIndex = selectedIndex;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
targetItem.focus();
|
|
218
|
+
targetItem.scrollIntoView({ block: 'nearest' });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
window.addEventListener('keydown', onKeyboardNavigation);
|
|
222
|
+
|
|
223
|
+
return () => {
|
|
224
|
+
window.removeEventListener('keydown', onKeyboardNavigation);
|
|
225
|
+
};
|
|
226
|
+
});
|
|
227
|
+
</script>
|
|
228
|
+
|
|
229
|
+
<Menu
|
|
230
|
+
class={['dodo-ui-DynamicMenu', className].join(' ')}
|
|
231
|
+
{id}
|
|
232
|
+
{size}
|
|
233
|
+
{width}
|
|
234
|
+
{height}
|
|
235
|
+
{separator}
|
|
236
|
+
bind:ref
|
|
237
|
+
>
|
|
238
|
+
{#if options?.length}
|
|
239
|
+
{#each options as option (option.id)}
|
|
240
|
+
<MenuItem
|
|
241
|
+
{...option}
|
|
242
|
+
selected={selectedOption?.id === option.id}
|
|
243
|
+
onclick={(e) => (onclick ? onclick(e, option) : undefined)}
|
|
244
|
+
{...menuItemProps}
|
|
245
|
+
>
|
|
246
|
+
{#if customMenuItemContentTyped}
|
|
247
|
+
{@render customMenuItemContentTyped(option, selectedOption)}
|
|
248
|
+
{:else}
|
|
249
|
+
{option.label || ''}
|
|
250
|
+
{/if}
|
|
251
|
+
</MenuItem>
|
|
252
|
+
{/each}
|
|
253
|
+
{:else if showOptionsPlaceholder}
|
|
254
|
+
<MenuItem type="text" disabled={true} {...menuItemProps}>
|
|
255
|
+
{#if customPlaceholderMenuItemContentTyped}
|
|
256
|
+
{@render customPlaceholderMenuItemContentTyped()}
|
|
257
|
+
{:else}
|
|
258
|
+
{optionsPlaceholder}
|
|
259
|
+
{/if}
|
|
260
|
+
</MenuItem>
|
|
261
|
+
{/if}
|
|
262
|
+
</Menu>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import { dynamicMenuItemOptions, storyDynamicMenuArgTypes } from '../DynamicMenu.stories.svelte';
|
|
4
|
+
import DynamicMenu, { type DynamicMenuItemOption } from '../DynamicMenu.svelte';
|
|
5
|
+
|
|
6
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
|
|
7
|
+
const { Story } = defineMeta({
|
|
8
|
+
component: DynamicMenu,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: storyDynamicMenuArgTypes,
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<!-- Menu Item onclick -->
|
|
15
|
+
<Story name="onclick" asChild>
|
|
16
|
+
<DynamicMenu
|
|
17
|
+
keyboardNavigation
|
|
18
|
+
options={dynamicMenuItemOptions}
|
|
19
|
+
onclick={(e: MouseEvent, option: DynamicMenuItemOption) => {
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
console.log(option);
|
|
22
|
+
}}
|
|
23
|
+
/>
|
|
24
|
+
</Story>
|
|
25
|
+
|
|
26
|
+
<!-- KeyboardNavigation Event: onEnter -->
|
|
27
|
+
<Story name="onEnter" asChild>
|
|
28
|
+
<DynamicMenu
|
|
29
|
+
keyboardNavigation
|
|
30
|
+
options={dynamicMenuItemOptions}
|
|
31
|
+
onEnter={(e: KeyboardEvent, menuItemIndex: number) => {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
console.log(menuItemIndex);
|
|
34
|
+
}}
|
|
35
|
+
/>
|
|
36
|
+
</Story>
|
|
37
|
+
|
|
38
|
+
<!-- KeyboardNavigation Event: onEsc -->
|
|
39
|
+
<Story name="onEsc" asChild>
|
|
40
|
+
<DynamicMenu
|
|
41
|
+
keyboardNavigation
|
|
42
|
+
options={dynamicMenuItemOptions}
|
|
43
|
+
onEsc={(e: KeyboardEvent, menuItemIndex: number) => {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
console.log(menuItemIndex);
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
</Story>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import DynamicMenu from '../DynamicMenu.svelte';
|
|
4
|
+
import { dynamicMenuItemOptions, storyDynamicMenuArgTypes } from '../DynamicMenu.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: DynamicMenu,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: storyDynamicMenuArgTypes,
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<!-- KeyboardNavigationOn -->
|
|
15
|
+
<Story
|
|
16
|
+
name="KeyboardNavigationOn"
|
|
17
|
+
args={{
|
|
18
|
+
options: dynamicMenuItemOptions,
|
|
19
|
+
keyboardNavigation: true,
|
|
20
|
+
onEnter: (e: KeyboardEvent, menuItemIndex: number) => {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
console.log(menuItemIndex);
|
|
23
|
+
},
|
|
24
|
+
onEsc: (e: KeyboardEvent, menuItemIndex: number) => {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
console.log(menuItemIndex);
|
|
27
|
+
},
|
|
28
|
+
}}
|
|
29
|
+
/>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Source } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
# Option Format
|
|
4
|
+
|
|
5
|
+
## DynamicMenuItemOption
|
|
6
|
+
|
|
7
|
+
<Source
|
|
8
|
+
dark
|
|
9
|
+
language="ts"
|
|
10
|
+
code={`
|
|
11
|
+
type DynamicMenuItemOption<TMeta = unknown> = {
|
|
12
|
+
id: string;
|
|
13
|
+
/** label */
|
|
14
|
+
label?: string;
|
|
15
|
+
/** Custom Metadata */
|
|
16
|
+
meta?: TMeta;
|
|
17
|
+
/** Menu Item type */
|
|
18
|
+
type?: MenuItemType;
|
|
19
|
+
/** Menu Separator */
|
|
20
|
+
separator?: boolean;
|
|
21
|
+
/** How large should the Menu Items be? */
|
|
22
|
+
size?: ComponentSize;
|
|
23
|
+
/** The onclick event handler */
|
|
24
|
+
onclick?: MouseEventHandler<HTMLButtonElement>;
|
|
25
|
+
/** disabled state */
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
/** link href */
|
|
28
|
+
href?: string;
|
|
29
|
+
/** Link button: download */
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
download?: any;
|
|
32
|
+
/** Link button: hreflang */
|
|
33
|
+
hreflang?: string | undefined | null;
|
|
34
|
+
/** Link button: media */
|
|
35
|
+
media?: string | undefined | null;
|
|
36
|
+
/** Link button: ping */
|
|
37
|
+
ping?: string | undefined | null;
|
|
38
|
+
/** Link button: rel */
|
|
39
|
+
rel?: string | undefined | null;
|
|
40
|
+
/** Link button: target */
|
|
41
|
+
target?: ButtonLinkTarget;
|
|
42
|
+
/** Link button: Type */
|
|
43
|
+
anchorMediaType?: string | undefined | null;
|
|
44
|
+
/** Link button: referrerpolicy */
|
|
45
|
+
referrerpolicy?: ButtonLinkReferrerpolicy;
|
|
46
|
+
};
|
|
47
|
+
`}
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
## Example values
|
|
51
|
+
|
|
52
|
+
<Source
|
|
53
|
+
dark
|
|
54
|
+
language="ts"
|
|
55
|
+
code={`
|
|
56
|
+
const options: DynamicMenuItemOption[] = [
|
|
57
|
+
{
|
|
58
|
+
id: '1',
|
|
59
|
+
label: 'One',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: '2',
|
|
63
|
+
label: 'Two',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: '3',
|
|
67
|
+
label: 'Three',
|
|
68
|
+
disabled: true,
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
`}
|
|
72
|
+
/>
|