feat(ComboBox): selectedIndex -> selectedId (#1016)

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

* docs: update ComboBox
This commit is contained in:
Koichi Kiyokawa 2022-01-18 23:37:55 +09:00 committed by GitHub
commit cde8a79fa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 91 additions and 102 deletions

View file

@ -656,31 +656,31 @@ export interface ComboBoxItem {
### Props
| Prop name | Kind | Reactive | Type | Default value | Description |
| :--------------- | :-------------------- | :------- | :---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| listRef | <code>let</code> | Yes | <code>null &#124; HTMLDivElement</code> | <code>null</code> | Obtain a reference to the list HTML element |
| ref | <code>let</code> | Yes | <code>null &#124; HTMLInputElement</code> | <code>null</code> | Obtain a reference to the input HTML element |
| open | <code>let</code> | Yes | <code>boolean</code> | <code>false</code> | Set to `true` to open the combobox menu dropdown |
| value | <code>let</code> | Yes | <code>string</code> | <code>""</code> | Specify the selected combobox value |
| selectedIndex | <code>let</code> | Yes | <code>number</code> | <code>-1</code> | Set the selected item by value index |
| items | <code>let</code> | No | <code>ComboBoxItem[]</code> | <code>[]</code> | Set the combobox items |
| itemToString | <code>let</code> | No | <code>(item: ComboBoxItem) => string</code> | <code>(item) => item.text &#124;&#124; item.id</code> | Override the display of a combobox item |
| direction | <code>let</code> | No | <code>"bottom" &#124; "top"</code> | <code>"bottom"</code> | Specify the direction of the combobox dropdown menu |
| size | <code>let</code> | No | <code>"sm" &#124; "xl"</code> | -- | Set the size of the combobox |
| disabled | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the combobox |
| titleText | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the title text of the combobox |
| placeholder | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the placeholder text |
| helperText | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the helper text |
| invalidText | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the invalid state text |
| invalid | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to indicate an invalid state |
| warn | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to indicate an warning state |
| warnText | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the warning state text |
| light | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to enable the light variant |
| shouldFilterItem | <code>let</code> | No | <code>(item: ComboBoxItem, value: string) => boolean</code> | <code>() => true</code> | Determine if an item should be filtered given the current combobox value |
| translateWithId | <code>let</code> | No | <code>(id: any) => string</code> | -- | Override the default translation ids |
| id | <code>let</code> | No | <code>string</code> | <code>"ccs-" + Math.random().toString(36)</code> | Set an id for the list box component |
| name | <code>let</code> | No | <code>string</code> | -- | Specify a name attribute for the input |
| clear | <code>function</code> | No | <code>(options?: { focus?: boolean; }) => void</code> | <code>() => { prevSelectedIndex = undefined; selectedIndex = -1; highlightedIndex = -1; highlightedId = undefined; selectedId = undefined; selectedItem = undefined; open = false; inputValue = ""; if (options?.focus !== false) ref?.focus(); }</code> | Clear the combo box programmatically |
| Prop name | Kind | Reactive | Type | Default value | Description |
| :--------------- | :-------------------- | :------- | :---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| listRef | <code>let</code> | Yes | <code>null &#124; HTMLDivElement</code> | <code>null</code> | Obtain a reference to the list HTML element |
| ref | <code>let</code> | Yes | <code>null &#124; HTMLInputElement</code> | <code>null</code> | Obtain a reference to the input HTML element |
| open | <code>let</code> | Yes | <code>boolean</code> | <code>false</code> | Set to `true` to open the combobox menu dropdown |
| value | <code>let</code> | Yes | <code>string</code> | <code>""</code> | Specify the selected combobox value |
| selectedId | <code>let</code> | Yes | <code>string</code> | -- | Set the selected item by value id |
| items | <code>let</code> | No | <code>ComboBoxItem[]</code> | <code>[]</code> | Set the combobox items |
| itemToString | <code>let</code> | No | <code>(item: ComboBoxItem) => string</code> | <code>(item) => item.text &#124;&#124; item.id</code> | Override the display of a combobox item |
| direction | <code>let</code> | No | <code>"bottom" &#124; "top"</code> | <code>"bottom"</code> | Specify the direction of the combobox dropdown menu |
| size | <code>let</code> | No | <code>"sm" &#124; "xl"</code> | -- | Set the size of the combobox |
| disabled | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the combobox |
| titleText | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the title text of the combobox |
| placeholder | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the placeholder text |
| helperText | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the helper text |
| invalidText | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the invalid state text |
| invalid | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to indicate an invalid state |
| warn | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to indicate an warning state |
| warnText | <code>let</code> | No | <code>string</code> | <code>""</code> | Specify the warning state text |
| light | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to enable the light variant |
| shouldFilterItem | <code>let</code> | No | <code>(item: ComboBoxItem, value: string) => boolean</code> | <code>() => true</code> | Determine if an item should be filtered given the current combobox value |
| translateWithId | <code>let</code> | No | <code>(id: any) => string</code> | -- | Override the default translation ids |
| id | <code>let</code> | No | <code>string</code> | <code>"ccs-" + Math.random().toString(36)</code> | Set an id for the list box component |
| name | <code>let</code> | No | <code>string</code> | -- | Specify a name attribute for the input |
| clear | <code>function</code> | No | <code>(options?: { focus?: boolean; }) => void</code> | <code>() => { prevSelectedId = null; highlightedIndex = -1; highlightedId = undefined; selectedId = undefined; selectedItem = undefined; open = false; inputValue = ""; if (options?.focus !== false) ref?.focus(); }</code> | Clear the combo box programmatically |
### Slots
@ -688,15 +688,15 @@ None.
### Events
| Event name | Type | Detail |
| :--------- | :--------- | :------------------------------------------------------------------------------------- |
| select | dispatched | <code>{ selectedId: string; selectedIndex: number; selectedItem: ComboBoxItem }</code> |
| keydown | forwarded | -- |
| keyup | forwarded | -- |
| focus | forwarded | -- |
| blur | forwarded | -- |
| clear | forwarded | -- |
| scroll | forwarded | -- |
| Event name | Type | Detail |
| :--------- | :--------- | :-------------------------------------------------------------- |
| select | dispatched | <code>{ selectedId: string; selectedItem: ComboBoxItem }</code> |
| keydown | forwarded | -- |
| keyup | forwarded | -- |
| focus | forwarded | -- |
| blur | forwarded | -- |
| clear | forwarded | -- |
| scroll | forwarded | -- |
## `ComposedModal`

View file

@ -1395,11 +1395,10 @@
"reactive": false
},
{
"name": "selectedIndex",
"name": "selectedId",
"kind": "let",
"description": "Set the selected item by value index",
"type": "number",
"value": "-1",
"description": "Set the selected item by value id",
"type": "string",
"isFunction": false,
"isFunctionDeclaration": false,
"constant": false,
@ -1616,7 +1615,7 @@
"kind": "function",
"description": "Clear the combo box programmatically",
"type": "(options?: { focus?: boolean; }) => void",
"value": "() => { prevSelectedIndex = undefined; selectedIndex = -1; highlightedIndex = -1; highlightedId = undefined; selectedId = undefined; selectedItem = undefined; open = false; inputValue = \"\"; if (options?.focus !== false) ref?.focus(); }",
"value": "() => { prevSelectedId = null; highlightedIndex = -1; highlightedId = undefined; selectedId = undefined; selectedItem = undefined; open = false; inputValue = \"\"; if (options?.focus !== false) ref?.focus(); }",
"isFunction": true,
"isFunctionDeclaration": true,
"constant": false,
@ -1628,7 +1627,7 @@
{
"type": "dispatched",
"name": "select",
"detail": "{ selectedId: string; selectedIndex: number; selectedItem: ComboBoxItem }"
"detail": "{ selectedId: string; selectedItem: ComboBoxItem }"
},
{ "type": "forwarded", "name": "keydown", "element": "input" },
{ "type": "forwarded", "name": "keyup", "element": "input" },

View file

@ -12,10 +12,10 @@ items={[
{id: "2", text: "Fax"}
]} />
### Selected index
### Selected id
<ComboBox titleText="Contact" placeholder="Select contact method"
selectedIndex={1}
selectedId="1"
items={[
{id: "0", text: "Slack"},
{id: "1", text: "Email"},

View file

@ -7,7 +7,7 @@
<ComboBox
titleText="Contact"
placeholder="Select contact method"
selectedIndex="{1}"
selectedId="1"
bind:this="{ref}"
items="{[
{ id: '0', text: 'Slack' },

View file

@ -7,17 +7,18 @@
{ id: "2", text: "Fax" },
];
let comboBox1_selectedIndex = -1;
let comboBox2_selectedIndex = -1;
let comboBox1_selectedId = undefined;
let comboBox2_selectedId = undefined;
const formatSelected = (i) => (items[i] ? items[i].text : "N/A");
const formatSelected = (id) =>
items.find((item) => item.id === id)?.text ?? "N/A";
$: primary = formatSelected(comboBox1_selectedIndex);
$: secondary = formatSelected(comboBox2_selectedIndex);
$: primary = formatSelected(comboBox1_selectedId);
$: secondary = formatSelected(comboBox2_selectedId);
</script>
<ComboBox
bind:selectedIndex="{comboBox1_selectedIndex}"
bind:selectedId="{comboBox1_selectedId}"
titleText="Primary contact"
placeholder="Select primary contact method"
items="{items}"
@ -26,7 +27,7 @@
<div>Primary: {primary}</div>
<ComboBox
bind:selectedIndex="{comboBox2_selectedIndex}"
bind:selectedId="{comboBox2_selectedId}"
titleText="Secondary contact"
placeholder="Select secondary contact method"
items="{items}"

View file

@ -1,12 +1,12 @@
<script>
import { ComboBox, Button } from "carbon-components-svelte";
let selectedIndex = 1;
let selectedId = "1";
</script>
<ComboBox
titleText="Contact"
placeholder="Select contact method"
bind:selectedIndex
bind:selectedId
items="{[
{ id: '0', text: 'Slack' },
{ id: '1', text: 'Email' },
@ -14,5 +14,7 @@
]}"
/>
<br />
<Button on:click="{() => (selectedIndex = -1)}">Set to -1 (unselected)</Button>
<Button on:click="{() => (selectedIndex = 2)}">Set to 2 (Fax)</Button>
<Button on:click="{() => (selectedId = undefined)}"
>Set to undefined (unselected)</Button
>
<Button on:click="{() => (selectedId = '2')}">Set to 2 (Fax)</Button>

View file

@ -1,7 +1,7 @@
<script>
/**
* @typedef {{ id: string; text: string; }} ComboBoxItem
* @event {{ selectedId: string; selectedIndex: number; selectedItem: ComboBoxItem }} select
* @event {{ selectedId: string; selectedItem: ComboBoxItem }} select
*/
/**
@ -16,8 +16,11 @@
*/
export let itemToString = (item) => item.text || item.id;
/** Set the selected item by value index */
export let selectedIndex = -1;
/**
* Set the selected item by value id
* @type {string}
*/
export let selectedId = undefined;
/** Specify the selected combobox value */
export let value = "";
@ -107,10 +110,9 @@
const dispatch = createEventDispatcher();
let selectedId = undefined;
let selectedItem = undefined;
let inputValue = value;
let prevSelectedIndex = undefined;
let prevSelectedId = null;
let highlightedIndex = -1;
function change(dir) {
@ -129,8 +131,7 @@
* @type {(options?: { focus?: boolean; }) => void}
*/
export function clear(options = {}) {
prevSelectedIndex = undefined;
selectedIndex = -1;
prevSelectedId = null;
highlightedIndex = -1;
highlightedId = undefined;
selectedId = undefined;
@ -149,34 +150,31 @@
filteredItems = [];
if (!selectedItem) {
selectedId = undefined;
selectedIndex = -1;
inputValue = "";
highlightedIndex = -1;
highlightedId = undefined;
prevSelectedIndex = undefined;
} else {
// programmatically set selectedIndex
// programmatically set inputValue
inputValue = selectedItem.text;
}
}
});
$: if (selectedIndex > -1) {
if (prevSelectedIndex !== selectedIndex) {
prevSelectedIndex = selectedIndex;
$: if (selectedId !== undefined) {
if (prevSelectedId !== selectedId) {
prevSelectedId = selectedId;
if (filteredItems?.length === 1 && open) {
selectedId = filteredItems[0].id;
selectedItem = filteredItems[0];
highlightedIndex = -1;
highlightedId = undefined;
} else {
selectedId = items[selectedIndex].id;
selectedItem = items[selectedIndex];
selectedItem = items.find((item) => item.id === selectedId);
}
dispatch("select", { selectedId, selectedIndex, selectedItem });
dispatch("select", { selectedId, selectedItem });
}
} else {
prevSelectedIndex = selectedIndex;
prevSelectedId = selectedId;
selectedItem = undefined;
}
@ -269,29 +267,25 @@
if (key === 'Enter') {
open = !open;
if (highlightedIndex > -1 && highlightedIndex !== selectedIndex) {
selectedIndex = highlightedIndex;
if (
highlightedIndex > -1 &&
filteredItems[highlightedIndex]?.id !== selectedId
) {
open = false;
highlightedIndex = -1;
if (filteredItems[selectedIndex]) {
inputValue = filteredItems[selectedIndex].text;
selectedItem = filteredItems[selectedIndex];
selectedId = filteredItems[selectedIndex].id;
if (filteredItems[highlightedIndex]) {
inputValue = filteredItems[highlightedIndex].text;
selectedItem = filteredItems[highlightedIndex];
selectedId = filteredItems[highlightedIndex].id;
}
selectedIndex = items.findIndex((item) => item.id === selectedId);
} else {
selectedIndex = 0;
open = false;
highlightedIndex = -1;
if (filteredItems[selectedIndex]) {
inputValue = filteredItems[selectedIndex].text;
selectedItem = filteredItems[selectedIndex];
selectedId = filteredItems[selectedIndex].id;
selectedIndex = items.findIndex(
(item) => item.id === selectedId
);
if (filteredItems[0]) {
inputValue = filteredItems[0].text;
selectedItem = filteredItems[0];
selectedId = filteredItems[0].id;
}
}
highlightedIndex = -1;
} else if (key === 'Tab') {
open = false;
} else if (key === 'ArrowDown') {
@ -354,12 +348,10 @@
{#each filteredItems as item, i (item.id)}
<ListBoxMenuItem
id="{item.id}"
active="{selectedIndex === i || selectedId === item.id}"
highlighted="{highlightedIndex === i || selectedIndex === i}"
active="{selectedId === item.id}"
highlighted="{highlightedIndex === i}"
on:click="{() => {
selectedIndex = items
.map(({ id }) => id)
.indexOf(filteredItems[i].id);
selectedId = item.id;
open = false;
if (filteredItems[i]) {

View file

@ -25,7 +25,7 @@
<ComboBox
titleText="Contact"
placeholder="Select contact method"
selectedIndex="{1}"
selectedId="1"
items="{items}"
/>

View file

@ -21,10 +21,9 @@ export interface ComboBoxProps
itemToString?: (item: ComboBoxItem) => string;
/**
* Set the selected item by value index
* @default -1
* Set the selected item by value id
*/
selectedIndex?: number;
selectedId?: string;
/**
* Specify the selected combobox value
@ -141,11 +140,7 @@ export interface ComboBoxProps
export default class ComboBox extends SvelteComponentTyped<
ComboBoxProps,
{
select: CustomEvent<{
selectedId: string;
selectedIndex: number;
selectedItem: ComboBoxItem;
}>;
select: CustomEvent<{ selectedId: string; selectedItem: ComboBoxItem }>;
keydown: WindowEventMap["keydown"];
keyup: WindowEventMap["keyup"];
focus: WindowEventMap["focus"];