@neovici/cosmoz-autocomplete 5.3.2 → 6.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.
Files changed (43) hide show
  1. package/dist/autocomplete/autocomplete.d.ts +16 -0
  2. package/dist/autocomplete/autocomplete.js +79 -0
  3. package/dist/autocomplete/index.d.ts +1 -0
  4. package/dist/autocomplete/index.js +19 -0
  5. package/dist/autocomplete/selection.d.ts +11 -0
  6. package/dist/autocomplete/selection.js +58 -0
  7. package/dist/autocomplete/styles.css.d.ts +2 -0
  8. package/{lib/autocomplete.css.js → dist/autocomplete/styles.css.js} +1 -2
  9. package/dist/autocomplete/use-autocomplete.d.ts +41 -0
  10. package/dist/autocomplete/use-autocomplete.js +74 -0
  11. package/dist/autocomplete/use-keys.d.ts +10 -0
  12. package/dist/autocomplete/use-keys.js +29 -0
  13. package/dist/autocomplete/util.d.ts +4 -0
  14. package/dist/autocomplete/util.js +25 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/listbox/index.d.ts +20 -0
  18. package/dist/listbox/index.js +41 -0
  19. package/dist/listbox/item-renderer.d.ts +25 -0
  20. package/dist/listbox/item-renderer.js +21 -0
  21. package/dist/listbox/style.css.d.ts +7 -0
  22. package/dist/listbox/style.css.js +104 -0
  23. package/dist/listbox/use-items.d.ts +14 -0
  24. package/dist/listbox/use-items.js +26 -0
  25. package/dist/listbox/use-keyboard.d.ts +8 -0
  26. package/dist/listbox/use-keyboard.js +32 -0
  27. package/dist/listbox/use-listbox.d.ts +28 -0
  28. package/dist/listbox/use-listbox.js +54 -0
  29. package/dist/listbox/use-render-item.d.ts +5 -0
  30. package/dist/listbox/use-render-item.js +7 -0
  31. package/dist/listbox/util.d.ts +2 -0
  32. package/dist/listbox/util.js +24 -0
  33. package/package.json +16 -12
  34. package/cosmoz-autocomplete.js +0 -21
  35. package/cosmoz-listbox.js +0 -145
  36. package/lib/autocomplete.js +0 -190
  37. package/lib/item-renderer.js +0 -26
  38. package/lib/on-scrolled.js +0 -37
  39. package/lib/use-autocomplete.js +0 -102
  40. package/lib/use-keyboard.js +0 -35
  41. package/lib/use-keys.js +0 -39
  42. package/lib/use-listbox.js +0 -123
  43. package/lib/utils.js +0 -62
@@ -0,0 +1,8 @@
1
+ type Handler = () => void;
2
+ export interface Handlers {
3
+ onUp: Handler;
4
+ onDown: Handler;
5
+ onEnter: Handler;
6
+ }
7
+ export declare const useKeyboard: (handlers: Handlers) => void;
8
+ export {};
@@ -0,0 +1,32 @@
1
+ import { useEffect } from 'haunted';
2
+ import { useMeta } from '@neovici/cosmoz-utils/hooks/use-meta';
3
+ export const useKeyboard = (handlers) => {
4
+ const listeners = useMeta(handlers);
5
+ useEffect(() => {
6
+ const handler = (e) => {
7
+ if ((e.ctrlKey && e.altKey) || e.defaultPrevented) {
8
+ return;
9
+ }
10
+ switch (e.key) {
11
+ case 'Up':
12
+ case 'ArrowUp':
13
+ e.preventDefault();
14
+ listeners.onUp();
15
+ break;
16
+ case 'Down':
17
+ case 'ArrowDown':
18
+ e.preventDefault();
19
+ listeners.onDown();
20
+ break;
21
+ case 'Enter':
22
+ e.preventDefault();
23
+ listeners.onEnter();
24
+ break;
25
+ default:
26
+ break;
27
+ }
28
+ };
29
+ document.addEventListener('keydown', handler, true);
30
+ return () => document.removeEventListener('keydown', handler, true);
31
+ }, [listeners]);
32
+ };
@@ -0,0 +1,28 @@
1
+ import { Placement } from '@neovici/cosmoz-dropdown/use-position';
2
+ import { useRenderItem, ItemRenderer } from './use-render-item';
3
+ export declare const properties: string[];
4
+ export interface Props<I> {
5
+ value: I | I[];
6
+ valueProperty?: string;
7
+ items: I[];
8
+ onSelect: (i: I) => void;
9
+ defaultIndex?: number;
10
+ query: string;
11
+ textual: (i: I) => string;
12
+ itemRenderer?: ItemRenderer<I>;
13
+ itemHeight?: number;
14
+ itemLimit?: number;
15
+ anchor?: () => HTMLElement | null;
16
+ confinement?: HTMLElement;
17
+ placement?: Placement;
18
+ }
19
+ export declare const useListbox: <I>({ value, valueProperty, items: _items, onSelect, defaultIndex, query, textual, itemRenderer, itemHeight, itemLimit, ...thru }: Props<I>) => {
20
+ position: import("./use-items").Position;
21
+ items: I[];
22
+ height: number;
23
+ highlight: (index: number) => void;
24
+ select: (item: I) => void;
25
+ itemHeight: number;
26
+ renderItem: (item: I, i: number) => import("lit-html").TemplateResult;
27
+ };
28
+ export { useRenderItem };
@@ -0,0 +1,54 @@
1
+ import { useMemo } from 'haunted';
2
+ import { useHost } from '@neovici/cosmoz-utils/hooks/use-host';
3
+ import { usePosition } from '@neovici/cosmoz-dropdown/use-position';
4
+ import { byValue } from './util';
5
+ import { useItems } from './use-items';
6
+ import { useRenderItem } from './use-render-item';
7
+ export const properties = [
8
+ 'query',
9
+ 'items',
10
+ 'onSelect',
11
+ 'textual',
12
+ 'anchor',
13
+ 'confinement',
14
+ 'placement',
15
+ 'itemHeight',
16
+ 'itemLimit',
17
+ 'itemRenderer',
18
+ 'defaultIndex',
19
+ 'value',
20
+ 'valueProperty',
21
+ ];
22
+ export const useListbox = ({ value, valueProperty, items: _items, onSelect, defaultIndex, query, textual, itemRenderer, itemHeight = 40, itemLimit = 5, ...thru }) => {
23
+ const isSelected = useMemo(() => byValue(value, valueProperty), [value, valueProperty]),
24
+ // TODO: investigate if we can drop this
25
+ items = useMemo(() => _items.slice(), [_items, isSelected]), { position, highlight, select } = useItems({
26
+ items,
27
+ onSelect,
28
+ defaultIndex: isNaN(defaultIndex)
29
+ ? undefined
30
+ : Number(defaultIndex),
31
+ });
32
+ usePosition({
33
+ host: useHost(),
34
+ ...thru,
35
+ limit: true,
36
+ });
37
+ return {
38
+ position,
39
+ items,
40
+ height: Math.min(itemLimit, items.length) * itemHeight,
41
+ highlight,
42
+ select,
43
+ itemHeight,
44
+ renderItem: useRenderItem({
45
+ itemRenderer,
46
+ highlight,
47
+ select,
48
+ textual,
49
+ query,
50
+ isSelected,
51
+ }),
52
+ };
53
+ };
54
+ export { useRenderItem };
@@ -0,0 +1,5 @@
1
+ import { ItemRenderer, Opts } from './item-renderer';
2
+ export declare const useRenderItem: <I>({ itemRenderer, ...meta }: Opts<I> & {
3
+ itemRenderer?: ItemRenderer<I> | undefined;
4
+ }) => (item: I, i: number) => import("lit-html").TemplateResult;
5
+ export type { ItemRenderer };
@@ -0,0 +1,7 @@
1
+ import { useCallback } from 'haunted';
2
+ import { useMeta } from '@neovici/cosmoz-utils/hooks/use-meta';
3
+ import { itemRenderer as mkItemRenderer, } from './item-renderer';
4
+ export const useRenderItem = ({ itemRenderer = mkItemRenderer(), ...meta }) => {
5
+ const info = useMeta(meta);
6
+ return useCallback((item, i) => itemRenderer(item, i, info), [info, itemRenderer]);
7
+ };
@@ -0,0 +1,2 @@
1
+ export declare const byValue: <K extends PropertyKey | undefined, T>(value: T | T[], valueProperty: K) => ((item: T) => boolean) | ((item: T) => T | undefined);
2
+ export declare const mark: (text: string, query: string) => string | (string | import("lit-html").TemplateResult<1>)[];
@@ -0,0 +1,24 @@
1
+ import { html } from 'lit-html';
2
+ import { array } from '@neovici/cosmoz-utils/array';
3
+ export const byValue = (value, valueProperty) => {
4
+ if (!valueProperty) {
5
+ return (item) => array(value).includes(item);
6
+ }
7
+ return (item) => array(value).find((value) => value[valueProperty] ===
8
+ item[valueProperty]);
9
+ };
10
+ export const mark = (text, query) => {
11
+ if (!query || !text) {
12
+ return text;
13
+ }
14
+ const i = text.toLowerCase().indexOf(query.toLowerCase());
15
+ if (i < 0) {
16
+ return text;
17
+ }
18
+ const end = i + query.length;
19
+ return [
20
+ text.slice(0, i),
21
+ html `<mark>${text.slice(i, end)}</mark>`,
22
+ text.slice(end),
23
+ ];
24
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neovici/cosmoz-autocomplete",
3
- "version": "5.3.2",
3
+ "version": "6.1.0",
4
4
  "description": "A autocomplete input web component",
5
5
  "keywords": [
6
6
  "lit-html",
@@ -21,14 +21,14 @@
21
21
  "test": "test"
22
22
  },
23
23
  "scripts": {
24
- "lint": "eslint --cache --ext .js .",
25
- "lint-tsc": "tsc",
24
+ "lint": "tsc && eslint --cache .",
25
+ "build": "tsc -p tsconfig.build.json",
26
26
  "start": "wds",
27
27
  "test": "wtr --coverage",
28
28
  "test:watch": "wtr --watch",
29
+ "prepare": "husky install",
29
30
  "storybook:build": "build-storybook",
30
- "storybook:deploy": "storybook-to-ghpages",
31
- "prepare": "husky install"
31
+ "storybook:deploy": "storybook-to-ghpages"
32
32
  },
33
33
  "release": {
34
34
  "plugins": [
@@ -46,8 +46,7 @@
46
46
  "access": "public"
47
47
  },
48
48
  "files": [
49
- "*.js",
50
- "lib/**/*.js"
49
+ "dist/"
51
50
  ],
52
51
  "commitlint": {
53
52
  "extends": [
@@ -61,14 +60,19 @@
61
60
  ]
62
61
  }
63
62
  },
63
+ "exports": {
64
+ ".": "./dist/index.js",
65
+ "./autocomplete": "./dist/autocomplete/index.js",
66
+ "./listbox": "./dist/listbox/index.js",
67
+ "./item-renderer": "./dist/listbox/item-renderer.js"
68
+ },
64
69
  "dependencies": {
65
- "@lit-labs/virtualizer": "^1.0.0",
70
+ "@lit-labs/virtualizer": "^2.0.0",
66
71
  "@neovici/cosmoz-dropdown": "^3.0.0",
67
72
  "@neovici/cosmoz-input": "^3.0.0",
68
73
  "@neovici/cosmoz-utils": "^5.0.0",
69
74
  "haunted": "^5.0.0",
70
- "lit-html": "^2.0.0",
71
- "position.js": "^0.3.0"
75
+ "lit-html": "^2.0.0"
72
76
  },
73
77
  "devDependencies": {
74
78
  "@commitlint/cli": "^17.0.0",
@@ -81,8 +85,8 @@
81
85
  "@web/dev-server-storybook": "^0.6.0",
82
86
  "eslint-import-resolver-exports": "^1.0.0-beta.5",
83
87
  "husky": "^8.0.0",
84
- "semantic-release": "^20.0.0",
88
+ "semantic-release": "^21.0.0",
85
89
  "sinon": "^15.0.0",
86
- "typescript": "^4.6.0"
90
+ "typescript": "^5.0.0"
87
91
  }
88
92
  }
@@ -1,21 +0,0 @@
1
- import { component, useCallback } from 'haunted';
2
- import { Autocomplete, observedAttributes } from './lib/autocomplete';
3
-
4
- const Standalone = host => {
5
- const { onChange, onText, ...props } = host;
6
-
7
- return Autocomplete.call(host, {
8
- ...props,
9
- onChange: useCallback((value, ...args) => {
10
- host.value = value;
11
- onChange?.(value, ...args);
12
- }, [onChange]),
13
- onText: useCallback(text => {
14
- host.text = text;
15
- onText?.(text);
16
- }, [onText])
17
- });
18
- };
19
-
20
- customElements.define('cosmoz-autocomplete-ui', component(Autocomplete, { observedAttributes }));
21
- customElements.define('cosmoz-autocomplete', component(Standalone, { observedAttributes }));
package/cosmoz-listbox.js DELETED
@@ -1,145 +0,0 @@
1
- import { html, component, useEffect, useRef, useMemo } from 'haunted';
2
- import { ref } from 'lit-html/directives/ref.js';
3
- import {
4
- virtualize,
5
- virtualizerRef,
6
- } from '@lit-labs/virtualizer/virtualize.js';
7
- import { tagged as css } from '@neovici/cosmoz-utils';
8
- import { portal } from '@neovici/cosmoz-utils/directives/portal';
9
- import { spreadProps } from '@neovici/cosmoz-utils/directives/spread-props';
10
- import { props } from '@neovici/cosmoz-utils/object';
11
- import { useListbox, properties } from './lib/use-listbox';
12
-
13
- const svg =
14
- /* eslint-disable quotes */
15
- "data:image/svg+xml,%3Csvg width='11' height='8' viewBox='0 0 11 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath " +
16
- "d='M9.5 1L5.20039 7.04766L1.66348 3.46152' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E",
17
- /* eslint-enable quotes */
18
- styles = ({ index, height, itemHeight }) => css`
19
- :host {
20
- position: fixed;
21
- min-height: ${itemHeight}px;
22
- z-index: 1000;
23
- height: ${height}px;
24
- font-family: var(--paper-font-subhead_-_font-family, initial);
25
- background: #fff;
26
- min-width: 72px;
27
- box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
28
- 0 1px 8px 0 rgba(0, 0, 0, 0.12), 0 3px 3px -2px rgba(0, 0, 0, 0.4);
29
- }
30
- .items {
31
- position: relative;
32
- overflow-y: auto;
33
- contain: layout paint !important;
34
- height: 100%;
35
- }
36
- .item {
37
- font-size: 14px;
38
- line-height: ${itemHeight}px;
39
- height: ${itemHeight}px;
40
- padding: 0 20px;
41
- box-sizing: border-box;
42
- width: 100%;
43
- cursor: pointer;
44
- text-overflow: ellipsis;
45
- white-space: nowrap;
46
- transition: background 0.2s;
47
- color: var(--cosmoz-listbox-color, #101010);
48
- }
49
-
50
- .item[data-index='${index || '0'}'] {
51
- background: var(
52
- --cosmoz-listbox-active-color,
53
- var(--cosmoz-selection-color, rgba(58, 145, 226, 0.1))
54
- );
55
- }
56
- .sizer {
57
- position: relative;
58
- visibility: hidden;
59
- opacity: 0;
60
- pointer-events: none;
61
- z-index: -1;
62
- height: 0;
63
- width: auto;
64
- padding: 0 20px;
65
- overflow: hidden;
66
- max-width: inherit;
67
- font-size: 14px;
68
- }
69
-
70
- :host(:not([multi])) .item[aria-selected] {
71
- background: var(--cosmoz-listbox-single-selection-color, #dadada);
72
- }
73
-
74
- :host([multi]) .item {
75
- padding-left: 0;
76
- }
77
- :host([multi]) .item::before {
78
- content: '';
79
- font-size: 0;
80
- padding: 7.5px;
81
- margin: 0 8px;
82
- background: #fff;
83
- border: 1px solid #d6d6d5;
84
- border-radius: 4px;
85
- vertical-align: top;
86
- }
87
- :host([multi]) .item[aria-selected]::before {
88
- border-color: #5881f6;
89
- /* prettier-ignore */
90
- background: url("${svg}") #5881f6 no-repeat 50%;
91
- }
92
- :host([multi]) .sizer {
93
- padding-left: 33px;
94
- }
95
- .swatch {
96
- width: 18px;
97
- height: 18px;
98
- display: inline-block;
99
- box-sizing: border-box;
100
- vertical-align: middle;
101
- border-radius: 50%;
102
- border: 2px solid #eee;
103
- }
104
- `,
105
- Listbox = (host) => {
106
- const listRef = useRef(undefined);
107
- const { position, items, renderItem, height, itemHeight } =
108
- useListbox(host);
109
-
110
- useEffect(() => {
111
- if (!position.scroll) return;
112
- const vl = listRef.current[virtualizerRef];
113
- if (!vl._layout) return;
114
- vl.element(position.index)?.scrollIntoView({ block: 'nearest' });
115
- }, [position]);
116
-
117
- const layout = useMemo(
118
- () => ({ _itemSize: { height: itemHeight - 0.00001 } }),
119
- [itemHeight]
120
- );
121
-
122
- return html` <style>
123
- ${styles({ ...position, height, itemHeight })}
124
- </style>
125
- <div class="items" ${ref((el) => (listRef.current = el))}>
126
- <div virtualizer-sizer></div>
127
- ${virtualize({
128
- items,
129
- renderItem,
130
- scroller: true,
131
- layout,
132
- })}
133
- </div>`;
134
- };
135
-
136
- customElements.define('cosmoz-listbox', component(Listbox));
137
-
138
- export const listbox = ({ multi, ...thru }) =>
139
- portal(
140
- html`<cosmoz-listbox
141
- part="listbox"
142
- ?multi=${multi}
143
- ...=${spreadProps(props(properties)(thru))}
144
- ></cosmoz-listbox>`
145
- );
@@ -1,190 +0,0 @@
1
- import { html, nothing } from 'lit-html'; // eslint-disable-line object-curly-newline
2
- import { live } from 'lit-html/directives/live.js';
3
- import { until } from 'lit-html/directives/until.js';
4
- import { when } from 'lit-html/directives/when.js';
5
- import { useCallback } from 'haunted';
6
- import { useHost } from '@neovici/cosmoz-utils/hooks/use-host';
7
- import { array } from '@neovici/cosmoz-utils/array';
8
- import '@neovici/cosmoz-input';
9
- import { useAutocomplete } from './use-autocomplete';
10
- import { listbox } from '../cosmoz-listbox';
11
- import style from './autocomplete.css';
12
-
13
- const blank = () => nothing,
14
- clear = html`
15
- <svg
16
- width="9"
17
- height="8"
18
- viewBox="0 0 9 8"
19
- fill="none"
20
- xmlns="http://www.w3.org/2000/svg"
21
- >
22
- <line
23
- x1="7.53033"
24
- y1="0.994808"
25
- x2="1.16637"
26
- y2="7.35877"
27
- stroke-width="1.5"
28
- />
29
- <line
30
- x1="7.46967"
31
- y1="7.35882"
32
- x2="1.10571"
33
- y2="0.99486"
34
- stroke-width="1.5"
35
- />
36
- </svg>
37
- `,
38
- renderChip = (text, onClear, disabled, isOne) => html`
39
- <div
40
- class="chip"
41
- part="chip"
42
- slot="suffix"
43
- title=${text}
44
- ?data-one=${isOne}
45
- >
46
- <span class="chip-text" part="chip-text">${text}</span>
47
- ${when(
48
- !disabled,
49
- () =>
50
- html` <span class="chip-clear" part="chip-clear" @click=${onClear}>
51
- ${clear}
52
- </span>`
53
- )}
54
- </div>
55
- `,
56
- renderBadge = ({ value, onDeselect }) => {
57
- const len = value?.length;
58
- return when(
59
- len > 0,
60
- () => html`<div class="badge" slot="suffix" part="badge">
61
- ${len}
62
- <span
63
- class="badge-clear"
64
- part="chip-clear"
65
- @click=${() => onDeselect(value)}
66
- >${clear}</span
67
- >
68
- </div>`
69
- );
70
- },
71
- renderSelection = (value, isOne, onDeselect, textual, disabled) => {
72
- if (isOne || value.length === 1) {
73
- const val = value[0];
74
- return when(val, () =>
75
- renderChip(textual(val), () => onDeselect(val), disabled, isOne)
76
- );
77
- }
78
- return renderBadge({ value, onDeselect });
79
- },
80
- inputParts = ['input', 'label', 'line', 'error']
81
- .map((part) => `${part}: input-${part}`)
82
- .join(),
83
- autocomplete = (props) => {
84
- const {
85
- invalid,
86
- errorMessage,
87
- label,
88
- placeholder,
89
- disabled,
90
- noLabelFloat,
91
- alwaysFloatLabel,
92
- textual,
93
- text,
94
- onText,
95
- onFocus,
96
- onClick,
97
- onDeselect,
98
- value,
99
- limit,
100
- showSingle,
101
- items$,
102
- values$,
103
- } = props,
104
- host = useHost(),
105
- isOne = limit == 1, // eslint-disable-line eqeqeq
106
- isSingle = isOne && array(value)?.[0] != null,
107
- anchor = useCallback(
108
- () => host.shadowRoot.querySelector('#input'),
109
- [host, value]
110
- ),
111
- suggestions = until(
112
- items$.then((items) =>
113
- when((!isSingle || showSingle) && items.length, () =>
114
- listbox({
115
- ...props,
116
- anchor,
117
- items,
118
- multi: !isOne,
119
- })
120
- )
121
- )
122
- );
123
-
124
- return html` <style>
125
- ${style}
126
- </style>
127
- <cosmoz-input
128
- id="input"
129
- part="input"
130
- .label=${label}
131
- .placeholder=${isSingle ? undefined : placeholder}
132
- ?no-label-float=${noLabelFloat}
133
- ?always-float-label=${isSingle || alwaysFloatLabel}
134
- ?readonly=${isSingle}
135
- ?disabled=${disabled}
136
- ?invalid=${until(
137
- values$.then(
138
- () => invalid,
139
- () => true
140
- ),
141
- invalid
142
- )}
143
- .errorMessage=${until(
144
- values$.then(
145
- () => errorMessage,
146
- (e) => e.message
147
- ),
148
- errorMessage
149
- )}
150
- .value=${live(text)}
151
- @value-changed=${onText}
152
- @focusin=${onFocus}
153
- @focusout=${onFocus}
154
- @click=${onClick}
155
- autocomplete="off"
156
- exportparts=${inputParts}
157
- >
158
- <slot name="prefix" slot="prefix"></slot>
159
- <slot name="suffix" slot="suffix"></slot>
160
- ${renderSelection(array(value), isOne, onDeselect, textual, disabled)}
161
- ${until(
162
- values$.then(blank, blank),
163
- html`<div slot="suffix" class="spinner"></div>`
164
- )}
165
- </cosmoz-input>
166
-
167
- ${suggestions}`;
168
- },
169
- Autocomplete = (props) =>
170
- autocomplete({
171
- ...props,
172
- ...useAutocomplete(props),
173
- }),
174
- observedAttributes = [
175
- 'disabled',
176
- 'invalid',
177
- 'no-label-float',
178
- 'always-float-label',
179
- 'text-property',
180
- 'limit',
181
- 'show-single',
182
- 'keep-query',
183
- 'keep-opened',
184
- 'hide-empty',
185
- 'default-index',
186
- 'item-height',
187
- 'item-limit',
188
- ];
189
-
190
- export { autocomplete, Autocomplete, observedAttributes };
@@ -1,26 +0,0 @@
1
- import { html } from 'haunted';
2
- import { mark, identity } from './utils';
3
- export const itemRenderer =
4
- (render = identity) =>
5
- (item, i, { highlight, select, textual = identity, query, isSelected }) => {
6
- const text = textual(item),
7
- content = mark(text, query),
8
- rendered = render(content, item, i);
9
-
10
- return html` <div
11
- class="item"
12
- role="option"
13
- part="option"
14
- ?aria-selected=${isSelected(item)}
15
- data-index=${i}
16
- @mouseenter=${() => highlight(i)}
17
- @click=${() => select(item)}
18
- @mousedown=${(e) => e.preventDefault()}
19
- title=${text}
20
- >
21
- ${rendered}
22
- </div>
23
- <div class="sizer" virtualizer-sizer>${rendered}</div>`;
24
- };
25
-
26
- export default itemRenderer();
@@ -1,37 +0,0 @@
1
- /**
2
- * Returns all ancestor ShadowRoot elements
3
- * @param {HTMLElement} el The element
4
- * @param {HTMLElement} limit The limit to stop searching
5
- * @returns {ShadowRoot[]} Array of ancestor roots
6
- */
7
- const ancestorRoots = (el, limit = document.body) => {
8
- const roots = [];
9
- let ancestor = el;
10
- while (ancestor && ancestor !== limit) {
11
- if (ancestor.assignedSlot) {
12
- ancestor = ancestor.assignedSlot;
13
- continue;
14
- }
15
- if (ancestor instanceof ShadowRoot) {
16
- roots.push(ancestor);
17
- ancestor = ancestor.host;
18
- continue;
19
- }
20
- ancestor = ancestor.parentNode;
21
- }
22
- return roots;
23
- },
24
- /**
25
- * Listen to a scroll event in a ancestor root
26
- * @param {HTMLElement} el The element
27
- * @param {function} handler The event handler
28
- * @returns {function} Function to cleanup the event listeners
29
- */
30
- onScrolled = (el, handler) => {
31
- const roots = ancestorRoots(el);
32
- roots.forEach(r => r.addEventListener('scroll', handler, true));
33
- return () => {
34
- roots.forEach(r => r.removeEventListener('scroll', handler, true));
35
- };
36
- };
37
- export { onScrolled };