feat(Dropdown): selectedIndex -> selectedId (#1004)

* feat(breaking): selectedIndex -> selectedId in Dropdown

* feat: omit `selectedIndex` from the `select` event
This commit is contained in:
Koichi Kiyokawa 2022-01-18 23:37:32 +09:00 committed by GitHub
commit e11a893bee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 52 deletions

View file

@ -1166,7 +1166,7 @@ export interface DropdownItem {
| ref | <code>let</code> | Yes | <code>null &#124; HTMLButtonElement</code> | <code>null</code> | Obtain a reference to the button HTML element | | ref | <code>let</code> | Yes | <code>null &#124; HTMLButtonElement</code> | <code>null</code> | Obtain a reference to the button HTML element |
| inline | <code>let</code> | Yes | <code>boolean</code> | <code>false</code> | Set to `true` to use the inline variant | | inline | <code>let</code> | Yes | <code>boolean</code> | <code>false</code> | Set to `true` to use the inline variant |
| open | <code>let</code> | Yes | <code>boolean</code> | <code>false</code> | Set to `true` to open the dropdown | | open | <code>let</code> | Yes | <code>boolean</code> | <code>false</code> | Set to `true` to open the dropdown |
| selectedIndex | <code>let</code> | Yes | <code>number</code> | <code>-1</code> | Specify the selected item index | | selectedId | <code>let</code> | Yes | <code>DropdownItemId</code> | -- | Specify the selected item id |
| items | <code>let</code> | No | <code>DropdownItem[]</code> | <code>[]</code> | Set the dropdown items | | items | <code>let</code> | No | <code>DropdownItem[]</code> | <code>[]</code> | Set the dropdown items |
| itemToString | <code>let</code> | No | <code>(item: DropdownItem) => string</code> | <code>(item) => item.text &#124;&#124; item.id</code> | Override the display of a dropdown item | | itemToString | <code>let</code> | No | <code>(item: DropdownItem) => string</code> | <code>(item) => item.text &#124;&#124; item.id</code> | Override the display of a dropdown item |
| type | <code>let</code> | No | <code>"default" &#124; "inline"</code> | <code>"default"</code> | Specify the type of dropdown | | type | <code>let</code> | No | <code>"default" &#124; "inline"</code> | <code>"default"</code> | Specify the type of dropdown |
@ -1193,8 +1193,8 @@ None.
### Events ### Events
| Event name | Type | Detail | | Event name | Type | Detail |
| :--------- | :--------- | :--------------------------------------------------------------------------------------------- | | :--------- | :--------- | :---------------------------------------------------------------------- |
| select | dispatched | <code>{ selectedId: DropdownItemId, selectedIndex: number, selectedItem: DropdownItem }</code> | | select | dispatched | <code>{ selectedId: DropdownItemId, selectedItem: DropdownItem }</code> |
## `DropdownSkeleton` ## `DropdownSkeleton`

View file

@ -3008,11 +3008,10 @@
"reactive": false "reactive": false
}, },
{ {
"name": "selectedIndex", "name": "selectedId",
"kind": "let", "kind": "let",
"description": "Specify the selected item index", "description": "Specify the selected item id",
"type": "number", "type": "DropdownItemId",
"value": "-1",
"isFunction": false, "isFunction": false,
"isFunctionDeclaration": false, "isFunctionDeclaration": false,
"constant": false, "constant": false,
@ -3229,7 +3228,7 @@
{ {
"type": "dispatched", "type": "dispatched",
"name": "select", "name": "select",
"detail": "{ selectedId: DropdownItemId, selectedIndex: number, selectedItem: DropdownItem }" "detail": "{ selectedId: DropdownItemId, selectedItem: DropdownItem }"
} }
], ],
"typedefs": [ "typedefs": [

View file

@ -9,13 +9,13 @@ components: ["Dropdown", "DropdownSkeleton"]
### Default ### Default
<Dropdown titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
### Hidden label ### Hidden label
<Dropdown hideLabel titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown hideLabel titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
@ -25,7 +25,7 @@ Use the `itemToString` prop to format the display of individual items.
<Dropdown itemToString={item => { <Dropdown itemToString={item => {
return item.text + ' (' + item.id +')' return item.text + ' (' + item.id +')'
}} titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, }} titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
@ -37,49 +37,49 @@ Use the `itemToString` prop to format the display of individual items.
Set `direction` to `"top"` for the dropdown menu to appear above the input. Set `direction` to `"top"` for the dropdown menu to appear above the input.
<Dropdown direction="top" titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown direction="top" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
### Light variant ### Light variant
<Dropdown light titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown light titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
### Inline variant ### Inline variant
<Dropdown type="inline" titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown type="inline" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
### Extra-large size ### Extra-large size
<Dropdown size="xl" titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown size="xl" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
### Small size ### Small size
<Dropdown size="sm" titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown size="sm" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
### Invalid state ### Invalid state
<Dropdown invalid invalidText="Secondary contact method must be different from the primary contact" titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown invalid invalidText="Secondary contact method must be different from the primary contact" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
### Warning state ### Warning state
<Dropdown warn warnText="This contact method is not associated with your account" titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown warn warnText="This contact method is not associated with your account" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />
### Disabled state ### Disabled state
<Dropdown disabled titleText="Contact" selectedIndex={0} items="{[{id: "0", text: "Slack"}, <Dropdown disabled titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"}, {id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" /> {id: "2", text: "Fax"}]}" />

View file

@ -7,28 +7,29 @@
{ id: "2", text: "Fax" }, { id: "2", text: "Fax" },
]; ];
let dropdown1_selectedIndex = 0; let dropdown1_selectedId = "0";
let dropdown2_selectedIndex = 1; let dropdown2_selectedId = "1";
const formatSelected = (i) => (items[i] ? items[i].text : "N/A"); const formatSelected = (id) =>
items.find((item) => item.id === id)?.text ?? "N/A";
$: primary = formatSelected(dropdown1_selectedIndex); $: primary = formatSelected(dropdown1_selectedId);
$: secondary = formatSelected(dropdown2_selectedIndex); $: secondary = formatSelected(dropdown2_selectedId);
</script> </script>
<Dropdown <Dropdown
titleText="Primary contact" titleText="Primary contact"
bind:selectedIndex="{dropdown1_selectedIndex}" bind:selectedId="{dropdown1_selectedId}"
items="{items}" items="{items}"
/> />
<div>Primary: {primary}</div> <div>Primary: {primary}</div>
<Dropdown <Dropdown
invalid="{dropdown1_selectedIndex === dropdown2_selectedIndex}" invalid="{dropdown1_selectedId === dropdown2_selectedId}"
invalidText="Secondary contact method must be different from the primary contact" invalidText="Secondary contact method must be different from the primary contact"
titleText="Secondary contact" titleText="Secondary contact"
bind:selectedIndex="{dropdown2_selectedIndex}" bind:selectedId="{dropdown2_selectedId}"
items="{items}" items="{items}"
/> />

View file

@ -3,7 +3,7 @@
* @typedef {string} DropdownItemId * @typedef {string} DropdownItemId
* @typedef {string} DropdownItemText * @typedef {string} DropdownItemText
* @typedef {{ id: DropdownItemId; text: DropdownItemText; }} DropdownItem * @typedef {{ id: DropdownItemId; text: DropdownItemText; }} DropdownItem
* @event {{ selectedId: DropdownItemId, selectedIndex: number, selectedItem: DropdownItem }} select * @event {{ selectedId: DropdownItemId, selectedItem: DropdownItem }} select
*/ */
/** /**
@ -18,8 +18,11 @@
*/ */
export let itemToString = (item) => item.text || item.id; export let itemToString = (item) => item.text || item.id;
/** Specify the selected item index */ /**
export let selectedIndex = -1; * Specify the selected item id
* @type {DropdownItemId}
*/
export let selectedId = undefined;
/** /**
* Specify the type of dropdown * Specify the type of dropdown
@ -108,7 +111,6 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let selectedId = undefined;
let highlightedIndex = -1; let highlightedIndex = -1;
function change(dir) { function change(dir) {
@ -123,12 +125,11 @@
highlightedIndex = index; highlightedIndex = index;
} }
$: if (selectedIndex > -1) { $: if (selectedId !== undefined) {
dispatch("select", { selectedId, selectedIndex, selectedItem }); dispatch("select", { selectedId, selectedItem });
} }
$: inline = type === "inline"; $: inline = type === "inline";
$: selectedItem = items[selectedIndex]; $: selectedItem = items.find((item) => item.id === selectedId);
$: selectedId = items[selectedIndex] ? items[selectedIndex].id : undefined;
$: if (!open) { $: if (!open) {
highlightedIndex = -1; highlightedIndex = -1;
} }
@ -206,8 +207,11 @@
} }
if (key === 'Enter') { if (key === 'Enter') {
open = !open; open = !open;
if (highlightedIndex > -1 && highlightedIndex !== selectedIndex) { if (
selectedIndex = highlightedIndex; highlightedIndex > -1 &&
items[highlightedIndex].id !== selectedId
) {
selectedId = items[highlightedIndex].id;
open = false; open = false;
} }
} else if (key === 'Tab') { } else if (key === 'Tab') {
@ -233,11 +237,10 @@
{#each items as item, i (item.id)} {#each items as item, i (item.id)}
<ListBoxMenuItem <ListBoxMenuItem
id="{item.id}" id="{item.id}"
active="{selectedIndex === i || selectedId === item.id}" active="{selectedId === item.id}"
highlighted="{highlightedIndex === i || selectedIndex === i}" highlighted="{highlightedIndex === i || selectedId === item.id}"
on:click="{() => { on:click="{() => {
selectedId = item.id; selectedId = item.id;
selectedIndex = i;
ref.focus(); ref.focus();
}}" }}"
on:mouseenter="{() => { on:mouseenter="{() => {

View file

@ -5,7 +5,7 @@
<Dropdown <Dropdown
direction="top" direction="top"
titleText="Contact" titleText="Contact"
selectedIndex="{0}" selectedId="0"
items="{[ items="{[
{ id: '0', text: 'Slack' }, { id: '0', text: 'Slack' },
{ id: '1', text: 'Email' }, { id: '1', text: 'Email' },
@ -18,7 +18,7 @@
return item.text + ' (' + item.id + ')'; return item.text + ' (' + item.id + ')';
}}" }}"
titleText="Contact" titleText="Contact"
selectedIndex="{0}" selectedId="0"
items="{[ items="{[
{ id: '0', text: 'Slack' }, { id: '0', text: 'Slack' },
{ id: '1', text: 'Email' }, { id: '1', text: 'Email' },
@ -29,7 +29,7 @@
<Dropdown <Dropdown
light light
titleText="Contact" titleText="Contact"
selectedIndex="{0}" selectedId="0"
items="{[ items="{[
{ id: '0', text: 'Slack' }, { id: '0', text: 'Slack' },
{ id: '1', text: 'Email' }, { id: '1', text: 'Email' },
@ -40,7 +40,7 @@
<Dropdown <Dropdown
type="inline" type="inline"
titleText="Contact" titleText="Contact"
selectedIndex="{0}" selectedId="0"
items="{[ items="{[
{ id: '0', text: 'Slack' }, { id: '0', text: 'Slack' },
{ id: '1', text: 'Email' }, { id: '1', text: 'Email' },
@ -51,7 +51,7 @@
<Dropdown <Dropdown
size="xl" size="xl"
titleText="Contact" titleText="Contact"
selectedIndex="{0}" selectedId="0"
items="{[ items="{[
{ id: '0', text: 'Slack' }, { id: '0', text: 'Slack' },
{ id: '1', text: 'Email' }, { id: '1', text: 'Email' },
@ -62,7 +62,7 @@
<Dropdown <Dropdown
size="sm" size="sm"
titleText="Contact" titleText="Contact"
selectedIndex="{0}" selectedId="0"
items="{[ items="{[
{ id: '0', text: 'Slack' }, { id: '0', text: 'Slack' },
{ id: '1', text: 'Email' }, { id: '1', text: 'Email' },
@ -73,7 +73,7 @@
<Dropdown <Dropdown
disabled disabled
titleText="Contact" titleText="Contact"
selectedIndex="{0}" selectedId="0"
items="{[ items="{[
{ id: '0', text: 'Slack' }, { id: '0', text: 'Slack' },
{ id: '1', text: 'Email' }, { id: '1', text: 'Email' },

View file

@ -25,10 +25,9 @@ export interface DropdownProps
itemToString?: (item: DropdownItem) => string; itemToString?: (item: DropdownItem) => string;
/** /**
* Specify the selected item index * Specify the selected item id
* @default -1
*/ */
selectedIndex?: number; selectedId?: DropdownItemId;
/** /**
* Specify the type of dropdown * Specify the type of dropdown
@ -146,7 +145,6 @@ export default class Dropdown extends SvelteComponentTyped<
{ {
select: CustomEvent<{ select: CustomEvent<{
selectedId: DropdownItemId; selectedId: DropdownItemId;
selectedIndex: number;
selectedItem: DropdownItem; selectedItem: DropdownItem;
}>; }>;
}, },