@flightlesslabs/dodo-ui 0.7.1 → 0.8.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 +5 -0
- package/dist/index.js +3 -0
- 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/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 +107 -123
- package/dist/stories/components/Form/Select/Select.svelte.d.ts +15 -2
- 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 +1 -0
- package/package.json +17 -18
- package/src/lib/index.ts +13 -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/PasswordInput/Events/Events.stories.svelte +110 -0
- package/src/lib/stories/components/Form/PasswordInput/PasswordInput.stories.svelte +59 -0
- 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 +144 -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 +153 -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 +97 -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/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 +1 -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/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,97 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import TextInput, {
|
|
4
|
+
type TextInputClipboardEvent,
|
|
5
|
+
type TextInputFocusEvent,
|
|
6
|
+
} from '../TextInput.svelte';
|
|
7
|
+
import { storyTextInputArgTypes } from '../TextInput.stories.svelte';
|
|
8
|
+
|
|
9
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
|
|
10
|
+
const { Story } = defineMeta({
|
|
11
|
+
component: TextInput,
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
argTypes: storyTextInputArgTypes,
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<Story
|
|
18
|
+
name="Input"
|
|
19
|
+
args={{
|
|
20
|
+
oninput: (e: Event) => {
|
|
21
|
+
const target = e.target as HTMLInputElement;
|
|
22
|
+
|
|
23
|
+
console.log('Input Event', target.value);
|
|
24
|
+
},
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<Story
|
|
29
|
+
name="Change"
|
|
30
|
+
args={{
|
|
31
|
+
onchange: (e: Event) => {
|
|
32
|
+
const target = e.target as HTMLInputElement;
|
|
33
|
+
|
|
34
|
+
console.log('onChange Event', target.value);
|
|
35
|
+
},
|
|
36
|
+
}}
|
|
37
|
+
/>
|
|
38
|
+
|
|
39
|
+
<!-- `e: TextInputFocusEvent` -->
|
|
40
|
+
<Story
|
|
41
|
+
name="Focus"
|
|
42
|
+
args={{
|
|
43
|
+
onfocus: (e: TextInputFocusEvent) => {
|
|
44
|
+
const target = e.target as HTMLInputElement;
|
|
45
|
+
|
|
46
|
+
console.log('onfocus Event', target);
|
|
47
|
+
},
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
<!-- `e: TextInputFocusEvent` -->
|
|
52
|
+
<Story
|
|
53
|
+
name="Blur"
|
|
54
|
+
args={{
|
|
55
|
+
onblur: (e: TextInputFocusEvent) => {
|
|
56
|
+
const target = e.target as HTMLInputElement;
|
|
57
|
+
|
|
58
|
+
console.log('onblur Event', target);
|
|
59
|
+
},
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
<!-- `e: TextInputClipboardEvent` -->
|
|
64
|
+
<Story
|
|
65
|
+
name="Copy"
|
|
66
|
+
args={{
|
|
67
|
+
oncopy: (e: TextInputClipboardEvent) => {
|
|
68
|
+
const target = e.target as HTMLInputElement;
|
|
69
|
+
|
|
70
|
+
console.log('oncopy Event', target);
|
|
71
|
+
},
|
|
72
|
+
}}
|
|
73
|
+
/>
|
|
74
|
+
|
|
75
|
+
<!-- `e: TextInputClipboardEvent` -->
|
|
76
|
+
<Story
|
|
77
|
+
name="Cut"
|
|
78
|
+
args={{
|
|
79
|
+
oncut: (e: TextInputClipboardEvent) => {
|
|
80
|
+
const target = e.target as HTMLInputElement;
|
|
81
|
+
|
|
82
|
+
console.log('oncut Event', target);
|
|
83
|
+
},
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
<!-- `e: TextInputClipboardEvent` -->
|
|
88
|
+
<Story
|
|
89
|
+
name="Paste"
|
|
90
|
+
args={{
|
|
91
|
+
onpaste: (e: TextInputClipboardEvent) => {
|
|
92
|
+
const target = e.target as HTMLInputElement;
|
|
93
|
+
|
|
94
|
+
console.log('onpaste Event', target);
|
|
95
|
+
},
|
|
96
|
+
}}
|
|
97
|
+
/>
|
|
@@ -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 }} />
|
|
@@ -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 }} />
|
|
@@ -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 === 'Esc') {
|
|
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
|
+
/>
|