Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multiselect #179

Merged
merged 2 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Changelog
=========

## v9.4.0 *(2024-01-04)*
- Add `multiselect` option to the `Combobox`.


## v9.3.4, v9.3.3 *(2023-12-25)*
- Fix `InputDate` when picker was not in sync with the input value.
- Better dropdown alignment for `Popover` and `Menu` onScroll (should work if scrolling other elements beside the `<body>`).
Expand Down
34 changes: 34 additions & 0 deletions docs-src/code-example/JsonBox.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<pre><code class="language-json">{@html code}</code></pre>

<script>
import { afterUpdate } from 'svelte';
export let value ='';
let code ='';


afterUpdate(() => {
requestAnimationFrame(update);
});


function update () {
if (typeof value !== 'string') value = stringify(value);
code = window.Prism.highlight(value, window.Prism.languages.json, 'json');
}



function stringify (json) {
if (!json) return '';
let s = JSON.stringify(json);
s = s.replace(/([:,])/g, '$1 ');
if (s.match(/^{/)) s = s.replace(/{/g, '{ ');
else {
if (s.match(/}/)) s = s.replace(/\]/g, '\n]');
s = s.replace(/{/g, '\n { ');
}
s = s.replace(/}/g, ' }');
return s;
}

</script>
1 change: 1 addition & 0 deletions docs-src/code-example/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as CodeExample } from './CodeExample.svelte';
export { default as CodeBox } from './CodeBox.svelte';
export { default as JsonBox } from './JsonBox.svelte';
84 changes: 63 additions & 21 deletions docs-src/components/input/combobox/Combobox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
bind:value="{itemValue}" />

<h4>Selected value: </h4>
<code>{JSON.stringify(itemValue || {})}</code>
<JsonBox value="{itemValue}" />


<h3>Disabled</h3>
Expand All @@ -17,19 +17,28 @@
<Combobox
{items}
placeholder="Type to filter"
allowNew="true"
allowNew
bind:value="{itemValue}" />


<h3>Show on focus</h3>
<Combobox showOnFocus="true" {items} bind:value="{itemValue}" />

<h3>Simpler data (no ID, just 'name')</h3>
<Combobox items="{dataSimple}" placeholder="Type to filter"
<Combobox items="{dataSimpler}" placeholder="Type to filter"
bind:value="{valueSimpler}" />

<h4>Selected value: </h4>
<JsonBox value="{valueSimpler}" />


<h3>Simple data (just an array of strings)</h3>
<Combobox items="{dataSimple}" placeholder="Type to filter" allowNew
bind:value="{valueSimple}" />

<h3>Simplest data (just an array of strings)</h3>
<Combobox items="{dataSimplest}" placeholder="Type to filter" allowNew="true"
bind:value="{valueSimplest}" />
<h4>Selected value: </h4>
<JsonBox value="{valueSimple}" />


<h3>Label</h3>
<Combobox {items} label="Combobox label" />
Expand All @@ -41,7 +50,31 @@
<Combobox {items} label="Combobox label" error="You picked the wrong side!" />

<h3>Label on the left</h3>
<Combobox {items} label="Label is on the left" labelOnTheLeft="true"/>
<Combobox {items} label="Label is on the left" labelOnTheLeft/>



<h2>Multiselect</h2>
<p>This adds checkboxes to the list items, but it disables the auto-lookup functionality,<br>as the input value string becomes a comma-separated list of selected items' names.</p>

<h3>Simple data</h3>
<Combobox
items="{dataSimple}"
multiselect
placeholder="Type to filter"
bind:value="{multiselectSimpleValue}" />
<h4>Selected value: </h4>
<JsonBox value="{multiselectSimpleValue}" />


<h3>Complex data</h3>
<Combobox
{items}
multiselect
placeholder="Type to filter"
bind:value="{multiselectValue}" />
<h4>Selected value: </h4>
<JsonBox value="{multiselectValue}" />



Expand All @@ -54,28 +87,36 @@
<script>
import { Combobox } from '../../../../src';
import { API } from '../../../api-table';
import { CodeExample } from '../../../code-example';
import { CodeExample, JsonBox } from '../../../code-example';


const apiProps = [
{ name: 'allowNew', type: ['true', 'false'], default: 'false', description: 'Whether to allow arbitrary values (that don\'t exist in the list).' },
{ name: 'allowNew', description: 'Whether to allow arbitrary values (that don\'t exist in the list).' },
{ name: 'class', type: 'string', description: 'Additional css class name to be added to the component.' },
{ name: 'clearOnEsc', type: ['true', 'false'], default: 'false', description: 'If <i>true</i> - the combobox will be cleared when Escape is pressed.' },
{ name: 'clearOnEsc', description: 'If present - the combobox will be cleared when Escape is pressed.' },
{ name: 'disabled', description: 'Make the combobox disabled.' },
{ name: 'error', type: 'string', description: 'Error message to show above the combobox.' },
{ name: 'hideOnResize', type: ['true', 'false'], default: 'false', description: 'If <i>true</i> - resizing the window will close the popup.' },
{ name: 'hideOnResize', description: 'If present - resizing the window will close the popup.' },
{ name: 'id', type: 'string', description: 'Assign ID to the underlying input.' },
{ name: 'info', type: 'string', description: 'Show info message above the combobox.' },
{ name: 'items', type: 'array', required: true, description: 'An array of strings or objects in the following format: <code>&lbrace; name: string, id?: string | number, group?: string &rbrace;</code>(<i>name</i> should be unique, or - if <i>id</i> is present - <i>id</i> should be unique).' },
{ name: 'label', type: 'string', description: 'Label for the combobox.' },
{ name: 'labelOnTheLeft', type: ['true', 'false'], default: 'false', description: 'Put label to the left of the input (instead of at the top). Usually in longer forms, to align labels and inputs, hence input also gets <em>width: 100%</em>, as it will be constraint by the form container.' },
{ name: 'labelOnTheLeft', description: 'Put label to the left of the input (instead of at the top). Usually in longer forms, to align labels and inputs, hence input also gets <em>width: 100%</em>, as it will be constraint by the form container.' },
{ name: 'name', type: 'string', description: 'Assign title to the underlying input.' },
{ name: 'multiselect', description: 'This changes the control to a multiselect. The following changes will apply:<ul>' +
'<li>dropdown items will receive checkboxes,' +
'<li>the input textbox will become read-only,' +
'<li>text input will loose the auto-lookup functionality,' +
'<li>the control will only allow to change the value by clicking on items (or check them using the `Space` key),' +
'<li>the value will become an array,' +
'<li>arguments `allowNew`, `clearOnEsc` and `placeholder` will have no effect.' +
'</ul>'
},
{ name: 'placeholder', type: 'string', description: 'Shows placeholder text.' },
{ name: 'required', description: 'Mark the combobox as <i>aria-required</i>.' },
{ name: 'showAllInitially', type: ['true', 'false'], default: 'true', description: 'When the combobox has a value - the list in the poput is filtered by the combobox value.<br>If this option is set to true (default) - when user navigates to the combobox (with a value)<br> or clicks such an combobox - the poput initially will show all items unfiltered, and only once<br> user starts typing - the list will be filtered again.<br> If this value is set to <i>"false"</i> (or boolean <i>false</i>) - the list will always be filtered. ' },
{ name: 'showOnFocus', type: ['true', 'false'], default: 'false', description: 'If <i>true</i> - the popup will be automatically open when the combobox gets focus (as opposed to, when the user starts typing).' },
{ name: 'showOnFocus', description: 'If present - the popup will be automatically open when the combobox gets focus (as opposed to, when the user starts typing).' },
{ name: 'title', type: 'string', description: 'Assign title to the underlying input.' },
{ name: 'value', type: ['string', 'number'], description: 'Initial value of the combobox.' },
{ name: 'value', type: ['string', 'number', 'object', 'array'], description: 'Value of the combobox.<br>If combobox is <em>multiselect</em>, the value will be an array. ' },
{ name: 'bind:element', type: 'element', description: 'Exposes the HTML element of the component.' },
{ name: 'bind:inputElement', type: 'element', description: 'Exposes the HTML element of the underlying input.' },
{ name: 'on:change', type: 'function', description: 'Triggered when the value changes.' },
Expand Down Expand Up @@ -128,9 +169,9 @@ const items = [
{ id: 17, name: 'Lambda', group: 'Group 3' },
];
let itemValue = items[1];
let multiselectValue = [items[0], items[1]];


const dataSimple = [
const dataSimpler = [
{ name: 'Alpha', group: 'Group 1' },
{ name: 'Beta', group: 'Group 1' },
{ name: 'Gamma', group: 'Group 1' },
Expand All @@ -150,9 +191,9 @@ const dataSimple = [
{ name: 'Delta' },
{ name: 'Epsilon' },
];
let valueSimple = dataSimple[3];
let valueSimpler = dataSimpler[3];

const dataSimplest = [
const dataSimple = [
'Alpha',
'Beta',
'Gamma',
Expand All @@ -162,15 +203,16 @@ const dataSimplest = [
'Eta',
'Theta',
'Iota',
'Iota',
'Kappa',
'Lambda is the last item in this list',
];
let valueSimplest = 'Gamma';
let valueSimple = 'Gamma';
let multiselectSimpleValue = [dataSimple[0], dataSimple[1]];


function onChange (e) {
const { value, oldValue } = e.detail;
console.log({ value, oldValue });
}

</script>
4 changes: 2 additions & 2 deletions docs-src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<meta charset="UTF-8">
<title>PerfectThings UI</title>
<meta name="viewport" content="initial-scale=1.0">
<link rel="stylesheet" href="ui.css?v=15">
<link rel="stylesheet" href="docs.css?v=15">
<link rel="stylesheet" href="ui.css?v=16">
<link rel="stylesheet" href="docs.css?v=16">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
</head>
<body tabindex="0">
Expand Down
4 changes: 4 additions & 0 deletions docs-src/pages/changelog.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<h1>Changelog</h1>
<h2>v9.4.0 <em>(2024-01-04)</em></h2>
<ul>
<li>Add <code>multiselect</code> option to the <code>Combobox</code>.</li>
</ul>
<h2>v9.3.4, v9.3.3 <em>(2023-12-25)</em></h2>
<ul>
<li>Fix <code>InputDate</code> when picker was not in sync with the input value.</li>
Expand Down
244 changes: 123 additions & 121 deletions docs/docs.js

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions docs/docs.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<meta charset="UTF-8">
<title>PerfectThings UI</title>
<meta name="viewport" content="initial-scale=1.0">
<link rel="stylesheet" href="ui.css?v=15">
<link rel="stylesheet" href="docs.css?v=15">
<link rel="stylesheet" href="ui.css?v=16">
<link rel="stylesheet" href="docs.css?v=16">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
</head>
<body tabindex="0">
Expand Down
2 changes: 1 addition & 1 deletion docs/ui.css

Large diffs are not rendered by default.

25 changes: 16 additions & 9 deletions src/input/combobox/Combobox.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.combobox .input-inner { position: relative; }

.combobox input { padding-right: 36px; }
.multiselect input { text-overflow: ellipsis; }

.combobox-button:focus { box-shadow: none; }

Expand All @@ -23,8 +24,8 @@
box-shadow: var(--ui-shadow-fancy);
}

.combobox-list:empty { padding: 0; box-shadow: none; border: none; }
.combobox-list:not(:empty) { min-height: 2rem; }
.combobox-list.empty { padding: 0; box-shadow: none; border: none; }
.combobox-list:not(.empty) { min-height: 2rem; }
.combobox-list.hidden { display: none; }

.combobox-list-header,
Expand Down Expand Up @@ -63,22 +64,28 @@
touch-action: manipulation;
}

.combobox-list-item.in-group {
padding-left: 2rem;
.combobox-list-item svg { margin-right: 0.5rem; }

.combobox-list:not(.multiselect) .combobox-list-item.in-group { padding-left: 2rem; }


.mobile .combobox-list-item { transition: background-color 0.3s ease-out; }
.mobile .combobox-list-item.blinking {
transition: background-color 0.1s;
background-color: var(--ui-color-highlight-1);
}

.combobox-list-item.selected {

.desktop .combobox-list-item.selected {
background-color: var(--ui-color-highlight-1);
outline: 1px solid transparent;
}
.combobox-list-item:hover {
.desktop .combobox-list-item:hover {
background-color: var(--ui-color-highlight);
outline: 1px solid transparent;
}

.combobox-list-item b {
color: var(--ui-color-accent);
}
.combobox-list-item b { color: var(--ui-color-accent); }



Expand Down
Loading