From 298025c30fdca153a7b1354428b768a33e45655f Mon Sep 17 00:00:00 2001 From: Adan Ulloa Date: Tue, 21 Jan 2020 10:08:47 -0600 Subject: [PATCH] fetch upstream --- src/components/ComboBox/ComboBox.Story.svelte | 53 ++++ src/components/ComboBox/ComboBox.stories.js | 24 ++ src/components/ComboBox/ComboBox.svelte | 188 +++++++++++ src/components/ComboBox/index.js | 3 + .../MultiSelect/MultiSelect.Story.svelte | 41 +++ .../MultiSelect/MultiSelect.stories.js | 38 +++ src/components/MultiSelect/MultiSelect.svelte | 295 ++++++++++++++++++ src/components/MultiSelect/index.js | 3 + 8 files changed, 645 insertions(+) create mode 100644 src/components/ComboBox/ComboBox.Story.svelte create mode 100644 src/components/ComboBox/ComboBox.stories.js create mode 100644 src/components/ComboBox/ComboBox.svelte create mode 100644 src/components/ComboBox/index.js create mode 100644 src/components/MultiSelect/MultiSelect.Story.svelte create mode 100644 src/components/MultiSelect/MultiSelect.stories.js create mode 100644 src/components/MultiSelect/MultiSelect.svelte create mode 100644 src/components/MultiSelect/index.js diff --git a/src/components/ComboBox/ComboBox.Story.svelte b/src/components/ComboBox/ComboBox.Story.svelte new file mode 100644 index 00000000..652f51ec --- /dev/null +++ b/src/components/ComboBox/ComboBox.Story.svelte @@ -0,0 +1,53 @@ + + + +

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; }>'}
+  
+
+ + +
+
+ +
+
diff --git a/src/components/ComboBox/ComboBox.stories.js b/src/components/ComboBox/ComboBox.stories.js new file mode 100644 index 00000000..acfc0a76 --- /dev/null +++ b/src/components/ComboBox/ComboBox.stories.js @@ -0,0 +1,24 @@ +import { withKnobs, select, boolean, text } from '@storybook/addon-knobs'; +import Component from './ComboBox.Story.svelte'; + +export default { title: 'ComboBox', decorators: [withKnobs] }; + +const sizes = { + 'Extra large size (xl)': 'xl', + 'Regular size (lg)': '', + 'Small size (sm)': 'sm' +}; + +export const Default = () => ({ + Component, + props: { + size: select('Field size (size)', sizes, ''), + placeholder: text('Placeholder text (placeholder)', 'Filter...'), + titleText: text('Title (titleText)', 'Combobox title'), + helperText: text('Helper text (helperText)', 'Optional helper text here'), + light: boolean('Light (light)', false), + disabled: boolean('Disabled (disabled)', false), + invalid: boolean('Invalid (invalid)', false), + invalidText: text('Invalid text (invalidText)', 'A valid value is required') + } +}); diff --git a/src/components/ComboBox/ComboBox.svelte b/src/components/ComboBox/ComboBox.svelte new file mode 100644 index 00000000..f487980b --- /dev/null +++ b/src/components/ComboBox/ComboBox.svelte @@ -0,0 +1,188 @@ + + + { + if (open && inputRef && !inputRef.contains(target)) { + open = false; + } + }} /> + +
+ {#if titleText} + + {/if} + {#if helperText} +
+ {helperText} +
+ {/if} + + { + open = true; + }} + {id} + {disabled} + {translateWithId}> + { + inputValue = target.value; + }} + on:keydown + on:keydown|stopPropagation={({ key }) => { + if (key === 'Enter') { + open = !open; + if (highlightedIndex > -1 && highlightedIndex !== selectedIndex) { + selectedIndex = highlightedIndex; + open = false; + } + } else if (key === 'Tab') { + open = false; + } else if (key === 'ArrowDown') { + change(1); + } else if (key === 'ArrowUp') { + change(-1); + } + }} + on:focus + on:blur + on:blur={({ relatedTarget }) => { + if (relatedTarget && relatedTarget.getAttribute('role') !== 'button') { + inputRef.focus(); + } + }} + {disabled} + {placeholder} + {id} + value={inputValue} /> + {#if invalid} + + {/if} + {#if inputValue} + { + selectedIndex = -1; + open = false; + }} + {translateWithId} + {disabled} + {open} /> + {/if} + { + open = !open; + }} + {translateWithId} + {open} /> + + {#if open} + + {#each filteredItems as item, i (item.id || i)} + { + selectedId = item.id; + selectedIndex = items.map(({ id }) => id).indexOf(filteredItems[i].id); + open = false; + }} + on:mouseenter={() => { + highlightedIndex = i; + }}> + {itemToString(item)} + + {/each} + + {/if} + +
diff --git a/src/components/ComboBox/index.js b/src/components/ComboBox/index.js new file mode 100644 index 00000000..f6e61b53 --- /dev/null +++ b/src/components/ComboBox/index.js @@ -0,0 +1,3 @@ +import ComboBox from './ComboBox.svelte'; + +export default ComboBox; diff --git a/src/components/MultiSelect/MultiSelect.Story.svelte b/src/components/MultiSelect/MultiSelect.Story.svelte new file mode 100644 index 00000000..d8e42196 --- /dev/null +++ b/src/components/MultiSelect/MultiSelect.Story.svelte @@ -0,0 +1,41 @@ + + + +
+ +
+
+ +
+
diff --git a/src/components/MultiSelect/MultiSelect.stories.js b/src/components/MultiSelect/MultiSelect.stories.js new file mode 100644 index 00000000..52982d72 --- /dev/null +++ b/src/components/MultiSelect/MultiSelect.stories.js @@ -0,0 +1,38 @@ +import { withKnobs, select, boolean, text } from '@storybook/addon-knobs'; +import Component from './MultiSelect.Story.svelte'; + +export default { title: 'MultiSelect', 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: 'multiselect', + titleText: text('Title (titleText)', 'Multiselect Title'), + helperText: text('Helper text (helperText)', 'This is not helper text'), + filterable: boolean('Filterable (filterable)', false), + selectionFeedback: select( + 'Selection feedback (selectionFeedback)', + ['top', 'fixed', 'top-after-reopen'], + 'top-after-reopen' + ), + disabled: boolean('Disabled (disabled)', false), + light: boolean('Light variant (light)', false), + useTitleInItem: boolean('Show tooltip on hover', false), + type: select('UI type (Only for ``) (type)', types, 'default'), + size: select('Field size (size)', sizes, '') || undefined, + label: text('Label (label)', 'MultiSelect Label'), + invalid: boolean('Show form validation UI (invalid)', false), + invalidText: text('Form validation UI content (invalidText)', 'Invalid Selection') + } +}); diff --git a/src/components/MultiSelect/MultiSelect.svelte b/src/components/MultiSelect/MultiSelect.svelte new file mode 100644 index 00000000..6b825d8b --- /dev/null +++ b/src/components/MultiSelect/MultiSelect.svelte @@ -0,0 +1,295 @@ + + + { + if (open && multiSelectRef && !multiSelectRef.contains(target)) { + open = false; + } + }} /> + +
+ {#if titleText} + + {/if} + {#if !inline && helperText} +
+ {helperText} +
+ {/if} + 0 && '--multi-select--selected')} + {id} + {disabled} + {invalid} + {invalidText} + {open} + {light} + {size}> + {#if invalid} + + {/if} + { + if (filterable) { + open = true; + inputRef.focus(); + } else { + open = !open; + } + }} + on:keydown={({ key }) => { + if (filterable) { + return; + } + if (key === ' ') { + open = !open; + } else if (key === 'Tab') { + if (selectionRef && checked.length > 0) { + selectionRef.focus(); + } else { + open = false; + fieldRef.blur(); + } + } else if (key === 'ArrowDown') { + change(1); + } else if (key === 'ArrowUp') { + change(-1); + } else if (key === 'Enter') { + if (highlightedIndex > -1) { + sortedItems[highlightedIndex].checked = !sortedItems[highlightedIndex].checked; + } + } + }} + on:blur={({ relatedTarget }) => { + if (relatedTarget && relatedTarget.getAttribute('role') !== 'button') { + fieldRef.focus(); + } + }} + {id} + {disabled} + {translateWithId}> + {#if checked.length > 0} + { + sortedItems = sortedItems.map(item => ({ ...item, checked: false })); + fieldRef.blur(); + }} + {translateWithId} + {disabled} /> + {/if} + {#if filterable} + { + inputValue = target.value; + }} + on:keydown + on:keydown|stopPropagation={({ key }) => { + if (key === 'Enter') { + if (highlightedIndex > -1) { + sortedItems[highlightedIndex].checked = !sortedItems[highlightedIndex].checked; + } + } else if (key === 'Tab') { + open = false; + } else if (key === 'ArrowDown') { + change(1); + } else if (key === 'ArrowUp') { + change(-1); + } + }} + on:focus + on:blur + on:blur={({ relatedTarget }) => { + if (relatedTarget && relatedTarget.getAttribute('role') !== 'button') { + inputRef.focus(); + } + }} + {disabled} + {placeholder} + {id} + value={inputValue} /> + {#if invalid} + + {/if} + {#if inputValue} + { + inputValue = ''; + open = false; + }} + {translateWithId} + {disabled} + {open} /> + {/if} + { + open = !open; + }} + {translateWithId} + {open} /> + {/if} + {#if !filterable} + {label} + + {/if} + + {#if open} + + {#each filterable ? filteredItems : sortedItems as item, i (item.id || i)} + { + sortedItems = sortedItems.map(_ => + _.id === item.id ? { ..._, checked: !_.checked } : _ + ); + fieldRef.focus(); + }} + on:mouseenter={() => { + highlightedIndex = i; + }}> + + + {/each} + + {/if} + +
diff --git a/src/components/MultiSelect/index.js b/src/components/MultiSelect/index.js new file mode 100644 index 00000000..5164a2b4 --- /dev/null +++ b/src/components/MultiSelect/index.js @@ -0,0 +1,3 @@ +import MultiSelect from './MultiSelect.svelte'; + +export default MultiSelect;