diff --git a/src/components/Dropdown/Dropdown.Skeleton.svelte b/src/components/Dropdown/Dropdown.Skeleton.svelte new file mode 100644 index 00000000..d6381922 --- /dev/null +++ b/src/components/Dropdown/Dropdown.Skeleton.svelte @@ -0,0 +1,20 @@ + + +
+
+ +
+
diff --git a/src/components/Dropdown/Dropdown.Story.svelte b/src/components/Dropdown/Dropdown.Story.svelte new file mode 100644 index 00000000..7ff22997 --- /dev/null +++ b/src/components/Dropdown/Dropdown.Story.svelte @@ -0,0 +1,49 @@ + + + + + {#if story === 'skeleton'} +
+ +   + +
+ {:else} +

Currently, this component does not support items as slots.

+

+ items + must be an array of objects; mandatory fields are `id` and `text`. +

+
+      {'items = Array<{ id: string; text: string; }>'}
+    
+
+ +
+
+ +
+ {/if} +
diff --git a/src/components/Dropdown/Dropdown.stories.js b/src/components/Dropdown/Dropdown.stories.js new file mode 100644 index 00000000..0fe46fd6 --- /dev/null +++ b/src/components/Dropdown/Dropdown.stories.js @@ -0,0 +1,34 @@ +import { withKnobs, select, text, boolean } from '@storybook/addon-knobs'; +import Component from './Dropdown.Story.svelte'; + +export default { title: 'Dropdown', decorators: [withKnobs] }; + +const types = { + 'Default (default)': 'default', + 'Inline (inline)': 'inline' +}; + +const sizes = { + 'Extra large size (xl)': 'xl', + 'Regular size (lg)': '', + 'Small size (sm)': 'sm' +}; + +export const Default = () => ({ + Component, + props: { + id: text('Dropdown ID (id)', 'carbon-dropdown-example'), + type: select('Dropdown type (type)', types, 'default'), + size: select('Field size (size)', sizes, '') || undefined, + label: text('Label (label)', 'Dropdown menu options'), + 'aria-label': text('Aria Label (aria-label)', 'Dropdown'), + disabled: boolean('Disabled (disabled)', false), + light: boolean('Light variant (light)', false), + titleText: text('Title (titleText)', 'This is not a dropdown title.'), + helperText: text('Helper text (helperText)', 'This is not some helper text.'), + invalid: boolean('Show form validation UI (invalid)', false), + invalidText: text('Form validation UI content (invalidText)', 'A valid value is required') + } +}); + +export const Skeleton = () => ({ Component, props: { story: 'skeleton' } }); diff --git a/src/components/Dropdown/Dropdown.svelte b/src/components/Dropdown/Dropdown.svelte new file mode 100644 index 00000000..53ecb4fa --- /dev/null +++ b/src/components/Dropdown/Dropdown.svelte @@ -0,0 +1,144 @@ + + + { + if (open && fieldRef && !fieldRef.contains(target)) { + open = false; + } + }} /> + +
+ {#if titleText} + + {/if} + {#if !inline && helperText} +
+ {helperText} +
+ {/if} + { + open = fieldRef.contains(target) ? !open : false; + }} + {disabled} + {open} + {invalid} + {invalidText} + {light}> + {#if invalid} + + {/if} + { + if (key === 'Enter') { + open = !open; + if (highlightedIndex > -1 && highlightedIndex !== selectedIndex) { + selectedIndex = highlightedIndex; + open = false; + } + } else if (key === 'Tab') { + open = false; + fieldRef.blur(); + } else if (key === 'ArrowDown') { + change(1); + } else if (key === 'ArrowUp') { + change(-1); + } + }} + on:blur={({ relatedTarget }) => { + if (relatedTarget) { + fieldRef.focus(); + } + }} + {disabled} + {translateWithId} + {id}> + + {#if selectedItem}{itemToString(selectedItem)}{:else}{label}{/if} + + + + {#if open} + + {#each items as item, i (item.id || i)} + { + selectedId = item.id; + selectedIndex = i; + }} + on:mouseenter={() => { + highlightedIndex = i; + }}> + {itemToString(item)} + + {/each} + + {/if} + +
diff --git a/src/components/Dropdown/index.js b/src/components/Dropdown/index.js new file mode 100644 index 00000000..b6df05fe --- /dev/null +++ b/src/components/Dropdown/index.js @@ -0,0 +1,4 @@ +import Dropdown from './Dropdown.svelte'; + +export default Dropdown; +export { default as DropdownSkeleton } from './Dropdown.Skeleton.svelte';