@neovici/cosmoz-autocomplete 7.10.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/autocomplete/autocomplete.d.ts +2 -1
- package/dist/autocomplete/autocomplete.js +7 -6
- package/dist/autocomplete/chip.css.js +1 -0
- package/dist/autocomplete/chip.d.ts +1 -1
- package/dist/autocomplete/chip.js +8 -7
- package/dist/autocomplete/index.js +6 -5
- package/dist/autocomplete/selection.d.ts +2 -1
- package/dist/autocomplete/selection.js +2 -2
- package/dist/autocomplete/use-autocomplete.d.ts +3 -1
- package/dist/autocomplete/use-autocomplete.js +12 -9
- package/dist/autocomplete/use-keys.js +1 -1
- package/dist/autocomplete/use-overflow.js +1 -1
- package/dist/autocomplete/util.js +1 -1
- package/dist/listbox/index.js +12 -12
- package/dist/listbox/style.css.js +0 -2
- package/dist/listbox/use-items.js +1 -1
- package/dist/listbox/use-keyboard.js +1 -1
- package/dist/listbox/use-listbox.js +1 -1
- package/dist/listbox/use-render-item.js +1 -1
- package/package.json +7 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '@neovici/cosmoz-input';
|
|
2
2
|
import { Props as Base, RProps } from './use-autocomplete';
|
|
3
|
+
import style from './styles.css';
|
|
3
4
|
export interface Props<I> extends Base<I> {
|
|
4
5
|
invalid?: boolean;
|
|
5
6
|
errorMessage?: string;
|
|
@@ -15,4 +16,4 @@ type AProps<I> = Omit<Props<I>, keyof RProps<I>> & RProps<I> & {
|
|
|
15
16
|
onInputRef?: (el?: Element) => void;
|
|
16
17
|
};
|
|
17
18
|
declare const autocomplete: <I>(props: AProps<I>) => import("lit-html").TemplateResult<1>, Autocomplete: <I>(props: Props<I>) => import("lit-html").TemplateResult<1>, observedAttributes: string[];
|
|
18
|
-
export { autocomplete, Autocomplete, observedAttributes };
|
|
19
|
+
export { autocomplete, Autocomplete, observedAttributes, style };
|
|
@@ -2,7 +2,7 @@ import { html, nothing } from 'lit-html'; // eslint-disable-line object-curly-ne
|
|
|
2
2
|
import { live } from 'lit-html/directives/live.js';
|
|
3
3
|
import { until } from 'lit-html/directives/until.js';
|
|
4
4
|
import { when } from 'lit-html/directives/when.js';
|
|
5
|
-
import { useCallback } from '
|
|
5
|
+
import { useCallback } from '@pionjs/pion';
|
|
6
6
|
import { useHost } from '@neovici/cosmoz-utils/hooks/use-host';
|
|
7
7
|
import { useImperativeApi } from '@neovici/cosmoz-utils/hooks/use-imperative-api';
|
|
8
8
|
import '@neovici/cosmoz-input';
|
|
@@ -16,7 +16,7 @@ const inputParts = ['input', 'control', 'label', 'line', 'error']
|
|
|
16
16
|
.map((part) => `${part}: input-${part}`)
|
|
17
17
|
.join();
|
|
18
18
|
const autocomplete = (props) => {
|
|
19
|
-
const { invalid, errorMessage, label, placeholder, disabled, noLabelFloat, alwaysFloatLabel, textual, text, onText, onFocus, onClick, onDeselect, value, limit, showSingle, items$, values$, } = props, host = useHost(), isOne = limit == 1, // eslint-disable-line eqeqeq
|
|
19
|
+
const { invalid, errorMessage, label, placeholder, disabled, noLabelFloat, alwaysFloatLabel, textual, text, onText, onFocus, onClick, onDeselect, value, limit, min, showSingle, items$, values$, } = props, host = useHost(), isOne = limit == 1, // eslint-disable-line eqeqeq
|
|
20
20
|
isSingle = isOne && value?.[0] != null,
|
|
21
21
|
// TODO: Refactor with ref or a state callback
|
|
22
22
|
anchor = useCallback(() => host.shadowRoot.querySelector('#input'), [host, value]), suggestions = until(items$.then((items) => when((!isSingle || showSingle) && items.length, () => listbox({
|
|
@@ -28,9 +28,7 @@ const autocomplete = (props) => {
|
|
|
28
28
|
useImperativeApi({
|
|
29
29
|
focus: () => host.shadowRoot?.querySelector('#input')?.focus(),
|
|
30
30
|
}, []);
|
|
31
|
-
return html `
|
|
32
|
-
${style}
|
|
33
|
-
</style>
|
|
31
|
+
return html `
|
|
34
32
|
<cosmoz-input
|
|
35
33
|
id="input"
|
|
36
34
|
part="input"
|
|
@@ -56,6 +54,7 @@ const autocomplete = (props) => {
|
|
|
56
54
|
<slot name="suffix" slot="suffix"></slot>
|
|
57
55
|
${selection({
|
|
58
56
|
value,
|
|
57
|
+
min,
|
|
59
58
|
isOne,
|
|
60
59
|
onDeselect,
|
|
61
60
|
textual,
|
|
@@ -80,7 +79,9 @@ const autocomplete = (props) => {
|
|
|
80
79
|
'text-property',
|
|
81
80
|
'value-property',
|
|
82
81
|
'limit',
|
|
82
|
+
'min',
|
|
83
83
|
'show-single',
|
|
84
|
+
'preserve-order',
|
|
84
85
|
'keep-opened',
|
|
85
86
|
'keep-query',
|
|
86
87
|
'hide-empty',
|
|
@@ -89,4 +90,4 @@ const autocomplete = (props) => {
|
|
|
89
90
|
'item-limit',
|
|
90
91
|
'wrap',
|
|
91
92
|
];
|
|
92
|
-
export { autocomplete, Autocomplete, observedAttributes };
|
|
93
|
+
export { autocomplete, Autocomplete, observedAttributes, style };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { sheet } from '@neovici/cosmoz-utils';
|
|
2
|
+
import { component, html } from '@pionjs/pion';
|
|
3
3
|
import { ifDefined } from 'lit-html/directives/if-defined.js';
|
|
4
|
+
import { when } from 'lit-html/directives/when.js';
|
|
4
5
|
import styles from './chip.css';
|
|
5
6
|
const clear = html `
|
|
6
7
|
<svg
|
|
@@ -27,15 +28,15 @@ const clear = html `
|
|
|
27
28
|
</svg>
|
|
28
29
|
`;
|
|
29
30
|
export const Chip = ({ onClear, disabled }) => html `
|
|
30
|
-
<style>
|
|
31
|
-
${styles}
|
|
32
|
-
</style>
|
|
33
31
|
<div class="content" part="content chip-text"><slot></slot></div>
|
|
34
|
-
${when(!disabled, () => html ` <span class="clear" part="clear chip-clear" @click=${onClear}>
|
|
32
|
+
${when(onClear && !disabled, () => html ` <span class="clear" part="clear chip-clear" @click=${onClear}>
|
|
35
33
|
${clear}
|
|
36
34
|
</span>`)}
|
|
37
35
|
`;
|
|
38
|
-
customElements.define('cosmoz-autocomplete-chip', component(Chip, {
|
|
36
|
+
customElements.define('cosmoz-autocomplete-chip', component(Chip, {
|
|
37
|
+
observedAttributes: ['disabled'],
|
|
38
|
+
styleSheets: [sheet(styles)],
|
|
39
|
+
}));
|
|
39
40
|
export const chip = ({ content, onClear, disabled, hidden, className = 'chip', slot, }) => html `<cosmoz-autocomplete-chip
|
|
40
41
|
class=${ifDefined(className)}
|
|
41
42
|
slot=${ifDefined(slot)}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { component, useCallback } from '
|
|
2
|
-
import { Autocomplete, observedAttributes as atts, } from './autocomplete';
|
|
1
|
+
import { component, useCallback } from '@pionjs/pion';
|
|
2
|
+
import { Autocomplete, observedAttributes as atts, style, } from './autocomplete';
|
|
3
|
+
import { sheet } from '@neovici/cosmoz-utils';
|
|
3
4
|
const Standalone = (host) => {
|
|
4
5
|
const { onChange, onText, ...props } = host;
|
|
5
6
|
return Autocomplete({
|
|
@@ -14,6 +15,6 @@ const Standalone = (host) => {
|
|
|
14
15
|
}, [onText]),
|
|
15
16
|
});
|
|
16
17
|
};
|
|
17
|
-
const observedAttributes = atts;
|
|
18
|
-
customElements.define('cosmoz-autocomplete-ui', component(Autocomplete, { observedAttributes }));
|
|
19
|
-
customElements.define('cosmoz-autocomplete', component(Standalone, { observedAttributes }));
|
|
18
|
+
const observedAttributes = atts, styleSheets = [sheet(style)];
|
|
19
|
+
customElements.define('cosmoz-autocomplete-ui', component(Autocomplete, { observedAttributes, styleSheets }));
|
|
20
|
+
customElements.define('cosmoz-autocomplete', component(Standalone, { observedAttributes, styleSheets }));
|
|
@@ -2,10 +2,11 @@ import type { RProps } from './use-autocomplete';
|
|
|
2
2
|
type Deselect<I> = RProps<I>['onDeselect'];
|
|
3
3
|
interface Props<I> {
|
|
4
4
|
value: I[];
|
|
5
|
+
min?: number;
|
|
5
6
|
isOne: boolean;
|
|
6
7
|
onDeselect: Deselect<I>;
|
|
7
8
|
textual: (i: I) => string;
|
|
8
9
|
disabled?: boolean;
|
|
9
10
|
}
|
|
10
|
-
export declare const selection: <I>({ value: values, onDeselect, textual, disabled, }: Props<I>) => import("lit-html").TemplateResult<1>[];
|
|
11
|
+
export declare const selection: <I>({ value: values, min, onDeselect, textual, disabled, }: Props<I>) => import("lit-html").TemplateResult<1>[];
|
|
11
12
|
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
2
|
import { chip } from './chip';
|
|
3
|
-
export const selection = ({ value: values, onDeselect, textual, disabled, }) => [
|
|
3
|
+
export const selection = ({ value: values, min = 0, onDeselect, textual, disabled, }) => [
|
|
4
4
|
...values.filter(Boolean).map((value) => chip({
|
|
5
5
|
content: textual(value),
|
|
6
|
-
onClear: () => onDeselect(value),
|
|
6
|
+
onClear: values.length > min && (() => onDeselect(value)),
|
|
7
7
|
disabled,
|
|
8
8
|
slot: 'control',
|
|
9
9
|
})),
|
|
@@ -5,6 +5,7 @@ type Source<I> = (opts: {
|
|
|
5
5
|
interface Base<I> {
|
|
6
6
|
value: I | I[];
|
|
7
7
|
limit?: number;
|
|
8
|
+
min?: number;
|
|
8
9
|
keepOpened?: boolean;
|
|
9
10
|
keepQuery?: boolean;
|
|
10
11
|
onText: (text: string) => void;
|
|
@@ -25,8 +26,9 @@ export interface Props<I> extends Base<I> {
|
|
|
25
26
|
hideEmpty?: boolean;
|
|
26
27
|
disabled?: boolean;
|
|
27
28
|
onFocus?: (focused?: boolean) => void;
|
|
29
|
+
preserveOrder?: boolean;
|
|
28
30
|
}
|
|
29
|
-
export declare const useAutocomplete: <I>({ value: _value, text, onChange: _onChange, onText: _onText, onSelect, limit, source, textProperty, textual: _textual, valueProperty, external, hideEmpty, keepOpened, keepQuery, ...thru }: Props<I>) => {
|
|
31
|
+
export declare const useAutocomplete: <I>({ value: _value, text, onChange: _onChange, onText: _onText, onSelect, limit, min, source, textProperty, textual: _textual, valueProperty, external, hideEmpty, keepOpened, keepQuery, preserveOrder, ...thru }: Props<I>) => {
|
|
30
32
|
active: boolean | undefined;
|
|
31
33
|
query: string;
|
|
32
34
|
textual: (i: I) => string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useMemo, useEffect } from '
|
|
1
|
+
import { useCallback, useMemo, useEffect } from '@pionjs/pion';
|
|
2
2
|
import { without, array } from '@neovici/cosmoz-utils/array';
|
|
3
3
|
import { prop, strProp } from '@neovici/cosmoz-utils/object';
|
|
4
4
|
import { useHost } from '@neovici/cosmoz-utils/hooks/use-host';
|
|
@@ -6,16 +6,16 @@ import { useMeta } from '@neovici/cosmoz-utils/hooks/use-meta';
|
|
|
6
6
|
import { useFocus } from '@neovici/cosmoz-dropdown/use-focus';
|
|
7
7
|
import { useKeys } from './use-keys';
|
|
8
8
|
import { search, normalize, notify, useNotify, EMPTY$ } from './util';
|
|
9
|
-
export const useAutocomplete = ({ value: _value, text, onChange: _onChange, onText: _onText, onSelect, limit, source, textProperty, textual: _textual, valueProperty, external, hideEmpty, keepOpened, keepQuery, ...thru }) => {
|
|
9
|
+
export const useAutocomplete = ({ value: _value, text, onChange: _onChange, onText: _onText, onSelect, limit, min, source, textProperty, textual: _textual, valueProperty, external, hideEmpty, keepOpened, keepQuery, preserveOrder, ...thru }) => {
|
|
10
10
|
const textual = useMemo(() => (_textual ?? strProp)(textProperty), [_textual, textProperty]), { active, focused, onFocus, setClosed } = useFocus(thru), empty = !text, query = useMemo(() => text?.trim(), [text]), host = useHost(), onText = useNotify(host, _onText, 'text'), onChange = useCallback((val) => {
|
|
11
11
|
_onChange?.(val, () => setClosed(true));
|
|
12
12
|
notify(host, 'value', val);
|
|
13
|
-
}, [_onChange]), source$ = useMemo(() => Promise.resolve(typeof source === 'function' ? source({ query, active }) : source), [source, active, query]), value = useMemo(() => array(_value), [_value]), values$ = useMemo(() => source$.then((source) =>
|
|
14
|
-
|
|
13
|
+
}, [_onChange]), source$ = useMemo(() => Promise.resolve(typeof source === 'function' ? source({ query, active }) : source), [source, active, query]), value = useMemo(() => array(_value), [_value]), values$ = useMemo(() => source$.then((source) => preserveOrder
|
|
14
|
+
? normalize(source)
|
|
15
|
+
: [
|
|
15
16
|
...value,
|
|
16
17
|
...without(value, prop(valueProperty))(normalize(source)),
|
|
17
|
-
];
|
|
18
|
-
}), [source$, value, valueProperty]);
|
|
18
|
+
]), [source$, value, valueProperty]);
|
|
19
19
|
useKeys({
|
|
20
20
|
focused,
|
|
21
21
|
empty,
|
|
@@ -34,6 +34,7 @@ export const useAutocomplete = ({ value: _value, text, onChange: _onChange, onTe
|
|
|
34
34
|
onChange,
|
|
35
35
|
value,
|
|
36
36
|
limit,
|
|
37
|
+
min,
|
|
37
38
|
keepQuery,
|
|
38
39
|
keepOpened,
|
|
39
40
|
setClosed,
|
|
@@ -61,13 +62,15 @@ export const useAutocomplete = ({ value: _value, text, onChange: _onChange, onTe
|
|
|
61
62
|
}, [onText, text, setClosed]),
|
|
62
63
|
onSelect: useCallback((newVal) => {
|
|
63
64
|
meta.onSelect?.(newVal, meta);
|
|
64
|
-
const { onChange, onText, limit, value: val, keepQuery, keepOpened, setClosed, } = meta;
|
|
65
|
+
const { onChange, onText, limit, min, value: val, keepQuery, keepOpened, setClosed, } = meta;
|
|
65
66
|
if (!keepQuery)
|
|
66
67
|
onText('');
|
|
67
68
|
if (!keepOpened)
|
|
68
69
|
setClosed(true);
|
|
69
|
-
const value = array(val);
|
|
70
|
-
|
|
70
|
+
const value = array(val), deselect = value.includes(newVal);
|
|
71
|
+
if (deselect && value.length === min)
|
|
72
|
+
return;
|
|
73
|
+
onChange((deselect
|
|
71
74
|
? without(newVal)(value)
|
|
72
75
|
: [...value, newVal]).slice(-limit));
|
|
73
76
|
}, [meta]),
|
package/dist/listbox/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { html, component, useEffect, useRef, useMemo } from 'haunted';
|
|
2
|
-
import { ref } from 'lit-html/directives/ref.js';
|
|
3
1
|
import { virtualize, virtualizerRef, } from '@lit-labs/virtualizer/virtualize.js';
|
|
2
|
+
import { sheet } from '@neovici/cosmoz-utils';
|
|
4
3
|
import { portal } from '@neovici/cosmoz-utils/directives/portal';
|
|
5
4
|
import { spreadProps } from '@neovici/cosmoz-utils/directives/spread-props';
|
|
5
|
+
import { useStyleSheet } from '@neovici/cosmoz-utils/hooks/use-stylesheet';
|
|
6
6
|
import { props } from '@neovici/cosmoz-utils/object';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { component, html, useEffect, useMemo, useRef } from '@pionjs/pion';
|
|
8
|
+
import { ref } from 'lit-html/directives/ref.js';
|
|
9
|
+
import style, { styles } from './style.css';
|
|
10
|
+
import { properties, useListbox } from './use-listbox';
|
|
9
11
|
const Listbox = (props) => {
|
|
10
12
|
const listRef = useRef(undefined);
|
|
11
13
|
const { position, items, renderItem, height, itemHeight } = useListbox(props);
|
|
@@ -17,23 +19,21 @@ const Listbox = (props) => {
|
|
|
17
19
|
return;
|
|
18
20
|
vl.element(position.index)?.scrollIntoView({ block: 'nearest' });
|
|
19
21
|
}, [position]);
|
|
22
|
+
useStyleSheet(styles({ ...position, height, itemHeight }));
|
|
20
23
|
const layout = useMemo(() => ({
|
|
21
24
|
_itemSize: { height: itemHeight - 0.00001 },
|
|
22
25
|
}), [itemHeight]);
|
|
23
|
-
return html
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<div class="items" ${ref((el) => (listRef.current = el))}>
|
|
27
|
-
<div virtualizer-sizer></div>
|
|
28
|
-
${virtualize({
|
|
26
|
+
return html `<div class="items" ${ref((el) => (listRef.current = el))}>
|
|
27
|
+
<div virtualizer-sizer></div>
|
|
28
|
+
${virtualize({
|
|
29
29
|
items,
|
|
30
30
|
renderItem,
|
|
31
31
|
scroller: true,
|
|
32
32
|
layout,
|
|
33
33
|
})}
|
|
34
|
-
|
|
34
|
+
</div>`;
|
|
35
35
|
};
|
|
36
|
-
customElements.define('cosmoz-listbox', component(Listbox));
|
|
36
|
+
customElements.define('cosmoz-listbox', component(Listbox, { styleSheets: [sheet(style)] }));
|
|
37
37
|
export const listbox = ({ multi, ...thru }) => portal(html `<cosmoz-listbox
|
|
38
38
|
part="listbox"
|
|
39
39
|
?multi=${multi}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from '
|
|
1
|
+
import { useCallback, useEffect, useState } from '@pionjs/pion';
|
|
2
2
|
import { useKeyboard } from './use-keyboard';
|
|
3
3
|
export const useItems = ({ items, onSelect, defaultIndex = 0, }) => {
|
|
4
4
|
const [position, setPosition] = useState({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback } from '
|
|
1
|
+
import { useCallback } from '@pionjs/pion';
|
|
2
2
|
import { useMeta } from '@neovici/cosmoz-utils/hooks/use-meta';
|
|
3
3
|
import { itemRenderer as mkItemRenderer, } from './item-renderer';
|
|
4
4
|
export const useRenderItem = ({ itemRenderer = mkItemRenderer(), ...meta }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neovici/cosmoz-autocomplete",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "A autocomplete input web component",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lit-html",
|
|
@@ -68,22 +68,22 @@
|
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@lit-labs/virtualizer": "^2.0.0",
|
|
71
|
-
"@neovici/cosmoz-dropdown": "^
|
|
72
|
-
"@neovici/cosmoz-input": "^
|
|
73
|
-
"@neovici/cosmoz-utils": "^
|
|
74
|
-
"
|
|
71
|
+
"@neovici/cosmoz-dropdown": "^4.0.0",
|
|
72
|
+
"@neovici/cosmoz-input": "^4.0.0",
|
|
73
|
+
"@neovici/cosmoz-utils": "^6.0.0",
|
|
74
|
+
"@pionjs/pion": "^2.0.0",
|
|
75
75
|
"lit-html": "^2.0.0 || ^3.0.0"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@commitlint/cli": "^18.0.0",
|
|
79
79
|
"@commitlint/config-conventional": "^18.0.0",
|
|
80
80
|
"@neovici/cfg": "^1.34.0",
|
|
81
|
-
"@open-wc/testing": "^
|
|
81
|
+
"@open-wc/testing": "^4.0.0",
|
|
82
82
|
"@semantic-release/changelog": "^6.0.0",
|
|
83
83
|
"@semantic-release/git": "^10.0.0",
|
|
84
84
|
"@storybook/storybook-deployer": "^2.8.5",
|
|
85
85
|
"@types/mocha": "^10.0.3",
|
|
86
|
-
"@web/dev-server-storybook": "^
|
|
86
|
+
"@web/dev-server-storybook": "^2.0.0",
|
|
87
87
|
"husky": "^8.0.0",
|
|
88
88
|
"rollup-plugin-terser": "^7.0.2",
|
|
89
89
|
"semantic-release": "^22.0.0",
|