@genspectrum/dashboard-components 1.9.2 → 1.10.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/custom-elements.json +55 -7
- package/dist/{NumberRangeFilterChangedEvent-BnPI-Asz.js → NumberRangeFilterChangedEvent-Cdtcp9YL.js} +13 -2
- package/dist/NumberRangeFilterChangedEvent-Cdtcp9YL.js.map +1 -0
- package/dist/components.d.ts +45 -25
- package/dist/components.js +301 -62
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +22 -20
- package/dist/util.js +1 -1
- package/package.json +1 -1
- package/src/preact/components/downshift-combobox.tsx +277 -47
- package/src/preact/lineageFilter/LineageFilterChangedEvent.ts +11 -0
- package/src/preact/lineageFilter/fetchLineageAutocompleteList.ts +2 -2
- package/src/preact/lineageFilter/lineage-filter.stories.tsx +181 -2
- package/src/preact/lineageFilter/lineage-filter.tsx +65 -14
- package/src/utils/gsEventNames.ts +1 -0
- package/src/web-components/input/gs-lineage-filter.spec.ts +30 -0
- package/src/web-components/input/gs-lineage-filter.stories.ts +25 -2
- package/src/web-components/input/gs-lineage-filter.tsx +34 -23
- package/standalone-bundle/dashboard-components.js +6750 -6538
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/dist/NumberRangeFilterChangedEvent-BnPI-Asz.js.map +0 -1
package/dist/util.d.ts
CHANGED
|
@@ -135,6 +135,7 @@ export declare const gsEventNames: {
|
|
|
135
135
|
readonly dateRangeOptionChanged: "gs-date-range-option-changed";
|
|
136
136
|
readonly mutationFilterChanged: "gs-mutation-filter-changed";
|
|
137
137
|
readonly lineageFilterChanged: "gs-lineage-filter-changed";
|
|
138
|
+
readonly lineageFilterMultiChanged: "gs-lineage-filter-multi-changed";
|
|
138
139
|
readonly locationChanged: "gs-location-changed";
|
|
139
140
|
readonly textFilterChanged: "gs-text-filter-changed";
|
|
140
141
|
readonly numberRangeFilterChanged: "gs-number-range-filter-changed";
|
|
@@ -973,7 +974,7 @@ declare global {
|
|
|
973
974
|
|
|
974
975
|
declare global {
|
|
975
976
|
interface HTMLElementTagNameMap {
|
|
976
|
-
'gs-
|
|
977
|
+
'gs-prevalence-over-time': PrevalenceOverTimeComponent;
|
|
977
978
|
}
|
|
978
979
|
}
|
|
979
980
|
|
|
@@ -981,7 +982,7 @@ declare global {
|
|
|
981
982
|
declare global {
|
|
982
983
|
namespace JSX {
|
|
983
984
|
interface IntrinsicElements {
|
|
984
|
-
'gs-
|
|
985
|
+
'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
985
986
|
}
|
|
986
987
|
}
|
|
987
988
|
}
|
|
@@ -989,7 +990,7 @@ declare global {
|
|
|
989
990
|
|
|
990
991
|
declare global {
|
|
991
992
|
interface HTMLElementTagNameMap {
|
|
992
|
-
'gs-
|
|
993
|
+
'gs-mutations': MutationsComponent;
|
|
993
994
|
}
|
|
994
995
|
}
|
|
995
996
|
|
|
@@ -997,7 +998,7 @@ declare global {
|
|
|
997
998
|
declare global {
|
|
998
999
|
namespace JSX {
|
|
999
1000
|
interface IntrinsicElements {
|
|
1000
|
-
'gs-
|
|
1001
|
+
'gs-mutations': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1001
1002
|
}
|
|
1002
1003
|
}
|
|
1003
1004
|
}
|
|
@@ -1099,22 +1100,6 @@ declare global {
|
|
|
1099
1100
|
}
|
|
1100
1101
|
|
|
1101
1102
|
|
|
1102
|
-
declare global {
|
|
1103
|
-
interface HTMLElementTagNameMap {
|
|
1104
|
-
'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
declare global {
|
|
1110
|
-
namespace JSX {
|
|
1111
|
-
interface IntrinsicElements {
|
|
1112
|
-
'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
1103
|
declare global {
|
|
1119
1104
|
interface HTMLElementTagNameMap {
|
|
1120
1105
|
'gs-date-range-filter': DateRangeFilterComponent;
|
|
@@ -1198,6 +1183,7 @@ declare global {
|
|
|
1198
1183
|
}
|
|
1199
1184
|
interface HTMLElementEventMap {
|
|
1200
1185
|
[gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
|
|
1186
|
+
[gsEventNames.lineageFilterMultiChanged]: LineageMultiFilterChangedEvent;
|
|
1201
1187
|
}
|
|
1202
1188
|
}
|
|
1203
1189
|
|
|
@@ -1231,6 +1217,22 @@ declare global {
|
|
|
1231
1217
|
}
|
|
1232
1218
|
|
|
1233
1219
|
|
|
1220
|
+
declare global {
|
|
1221
|
+
interface HTMLElementTagNameMap {
|
|
1222
|
+
'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
declare global {
|
|
1228
|
+
namespace JSX {
|
|
1229
|
+
interface IntrinsicElements {
|
|
1230
|
+
'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
|
|
1234
1236
|
declare module 'chart.js' {
|
|
1235
1237
|
interface CartesianScaleTypeRegistry {
|
|
1236
1238
|
logit: {
|
package/dist/util.js
CHANGED
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCombobox } from 'downshift/preact';
|
|
1
|
+
import { useCombobox, useMultipleSelection } from 'downshift/preact';
|
|
2
2
|
import { type ComponentChild } from 'preact';
|
|
3
3
|
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
|
4
4
|
|
|
@@ -43,17 +43,7 @@ export function DownshiftCombobox<Item>({
|
|
|
43
43
|
divRef.current?.dispatchEvent(createEvent(item));
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
const environment =
|
|
49
|
-
shadowRoot !== undefined
|
|
50
|
-
? {
|
|
51
|
-
addEventListener: window.addEventListener.bind(window),
|
|
52
|
-
removeEventListener: window.removeEventListener.bind(window),
|
|
53
|
-
document: shadowRoot.ownerDocument,
|
|
54
|
-
Node: window.Node,
|
|
55
|
-
}
|
|
56
|
-
: undefined;
|
|
46
|
+
const environment = useShadowEnvironment(divRef);
|
|
57
47
|
|
|
58
48
|
const {
|
|
59
49
|
isOpen,
|
|
@@ -120,44 +110,284 @@ export function DownshiftCombobox<Item>({
|
|
|
120
110
|
{...getInputProps()}
|
|
121
111
|
onBlur={onInputBlur}
|
|
122
112
|
/>
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
className={`px-2 ${inputValue === '' && 'hidden'}`}
|
|
126
|
-
type='button'
|
|
127
|
-
onClick={clearInput}
|
|
128
|
-
tabIndex={-1}
|
|
129
|
-
>
|
|
130
|
-
<DeleteIcon />
|
|
131
|
-
</button>
|
|
132
|
-
<button
|
|
133
|
-
aria-label='toggle menu'
|
|
134
|
-
className='px-2'
|
|
135
|
-
type='button'
|
|
136
|
-
{...getToggleButtonProps()}
|
|
137
|
-
ref={buttonRef}
|
|
138
|
-
>
|
|
139
|
-
{isOpen ? <>↑</> : <>↓</>}
|
|
140
|
-
</button>
|
|
113
|
+
<ClearButton onClick={clearInput} isHidden={inputValue === ''} />
|
|
114
|
+
<ToggleButton isOpen={isOpen} buttonRef={buttonRef} getToggleButtonProps={getToggleButtonProps} />
|
|
141
115
|
</div>
|
|
142
116
|
</div>
|
|
143
|
-
<
|
|
144
|
-
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
{
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
117
|
+
<DropdownMenu
|
|
118
|
+
isOpen={isOpen}
|
|
119
|
+
getMenuProps={getMenuProps}
|
|
120
|
+
items={items}
|
|
121
|
+
highlightedIndex={highlightedIndex}
|
|
122
|
+
getItemProps={getItemProps}
|
|
123
|
+
formatItemInList={formatItemInList}
|
|
124
|
+
itemToString={itemToString}
|
|
125
|
+
selectedItem={selectedItem}
|
|
126
|
+
emptyMessage='No elements to select.'
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function DownshiftMultiCombobox<Item>({
|
|
133
|
+
allItems,
|
|
134
|
+
value,
|
|
135
|
+
filterItemsByInputValue,
|
|
136
|
+
createEvent,
|
|
137
|
+
itemToString,
|
|
138
|
+
placeholderText,
|
|
139
|
+
formatItemInList,
|
|
140
|
+
formatSelectedItem,
|
|
141
|
+
inputClassName = '',
|
|
142
|
+
}: {
|
|
143
|
+
allItems: Item[];
|
|
144
|
+
value: Item[];
|
|
145
|
+
filterItemsByInputValue: (item: Item, value: string) => boolean;
|
|
146
|
+
createEvent: (items: Item[]) => CustomEvent;
|
|
147
|
+
itemToString: (item: Item | undefined | null) => string;
|
|
148
|
+
placeholderText?: string;
|
|
149
|
+
formatItemInList: (item: Item) => ComponentChild;
|
|
150
|
+
formatSelectedItem?: (item: Item) => ComponentChild;
|
|
151
|
+
inputClassName?: string;
|
|
152
|
+
}) {
|
|
153
|
+
const [selectedItems, setSelectedItems] = useState<Item[]>(() => value);
|
|
154
|
+
const [itemsFilter, setItemsFilter] = useState('');
|
|
155
|
+
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
setSelectedItems(value);
|
|
158
|
+
}, [value]);
|
|
159
|
+
|
|
160
|
+
const availableItems = useMemo(() => {
|
|
161
|
+
return allItems.filter((item) => {
|
|
162
|
+
const notAlreadySelected = !selectedItems.find(
|
|
163
|
+
(selectedItem) => itemToString(selectedItem) === itemToString(item),
|
|
164
|
+
);
|
|
165
|
+
const matchesFilter = filterItemsByInputValue(item, itemsFilter);
|
|
166
|
+
return notAlreadySelected && matchesFilter;
|
|
167
|
+
});
|
|
168
|
+
}, [allItems, selectedItems, filterItemsByInputValue, itemsFilter, itemToString]);
|
|
169
|
+
|
|
170
|
+
const divRef = useRef<HTMLDivElement>(null);
|
|
171
|
+
|
|
172
|
+
const dispatchEvent = (items: Item[]) => {
|
|
173
|
+
setSelectedItems(items);
|
|
174
|
+
divRef.current?.dispatchEvent(createEvent(items));
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const environment = useShadowEnvironment(divRef);
|
|
178
|
+
|
|
179
|
+
const { getDropdownProps, removeSelectedItem } = useMultipleSelection({
|
|
180
|
+
selectedItems,
|
|
181
|
+
onStateChange({ selectedItems: newSelectedItems, type }) {
|
|
182
|
+
switch (type) {
|
|
183
|
+
case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem:
|
|
184
|
+
dispatchEvent(newSelectedItems ?? []);
|
|
185
|
+
break;
|
|
186
|
+
default:
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
environment,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const { isOpen, getToggleButtonProps, getMenuProps, getInputProps, highlightedIndex, getItemProps, closeMenu } =
|
|
194
|
+
useCombobox({
|
|
195
|
+
items: availableItems,
|
|
196
|
+
itemToString(item) {
|
|
197
|
+
return itemToString(item);
|
|
198
|
+
},
|
|
199
|
+
inputValue: itemsFilter,
|
|
200
|
+
onStateChange({ inputValue: newInputValue, type, selectedItem: newSelectedItem }) {
|
|
201
|
+
switch (type) {
|
|
202
|
+
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
203
|
+
case useCombobox.stateChangeTypes.ItemClick:
|
|
204
|
+
if (newSelectedItem) {
|
|
205
|
+
dispatchEvent([...selectedItems, newSelectedItem]);
|
|
206
|
+
setItemsFilter('');
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
case useCombobox.stateChangeTypes.InputChange:
|
|
210
|
+
setItemsFilter(newInputValue?.trim() ?? '');
|
|
211
|
+
break;
|
|
212
|
+
default:
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
stateReducer(state, actionAndChanges) {
|
|
217
|
+
const { changes, type } = actionAndChanges;
|
|
218
|
+
switch (type) {
|
|
219
|
+
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
220
|
+
case useCombobox.stateChangeTypes.ItemClick:
|
|
221
|
+
return {
|
|
222
|
+
...changes,
|
|
223
|
+
isOpen: true,
|
|
224
|
+
highlightedIndex: state.highlightedIndex,
|
|
225
|
+
inputValue: '',
|
|
226
|
+
};
|
|
227
|
+
default:
|
|
228
|
+
return changes;
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
environment,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const clearAll = () => {
|
|
235
|
+
dispatchEvent([]);
|
|
236
|
+
setItemsFilter('');
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const buttonRef = useRef(null);
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<div ref={divRef} className={'relative w-full'}>
|
|
243
|
+
<div className='w-full flex flex-col gap-1'>
|
|
244
|
+
<div
|
|
245
|
+
className={`flex gap-1 flex-wrap p-1.5 input min-w-24 h-fit w-full ${inputClassName}`}
|
|
246
|
+
onBlur={(event) => {
|
|
247
|
+
if (event.relatedTarget != buttonRef.current) {
|
|
248
|
+
closeMenu();
|
|
249
|
+
}
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
{selectedItems.map((selectedItem, index) => (
|
|
253
|
+
<span
|
|
254
|
+
key={`${itemToString(selectedItem)}-${index}`}
|
|
255
|
+
className='inline-flex items-center gap-1 px-2 py-0.5 bg-blue-100 text-black rounded'
|
|
153
256
|
>
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
257
|
+
{formatSelectedItem ? formatSelectedItem(selectedItem) : itemToString(selectedItem)}
|
|
258
|
+
<button
|
|
259
|
+
aria-label={`remove ${itemToString(selectedItem)}`}
|
|
260
|
+
className='cursor-pointer hover:text-red-600'
|
|
261
|
+
type='button'
|
|
262
|
+
onClick={() => removeSelectedItem(selectedItem)}
|
|
263
|
+
tabIndex={-1}
|
|
264
|
+
>
|
|
265
|
+
×
|
|
266
|
+
</button>
|
|
267
|
+
</span>
|
|
268
|
+
))}
|
|
269
|
+
<div className='flex gap-0.5 grow min-w-32'>
|
|
270
|
+
<input
|
|
271
|
+
placeholder={placeholderText}
|
|
272
|
+
className='w-full px-1 py-0.5 focus:outline-none min-w-24'
|
|
273
|
+
{...getInputProps(getDropdownProps({ preventKeyAction: isOpen }))}
|
|
274
|
+
/>
|
|
275
|
+
<ClearButton onClick={clearAll} isHidden={selectedItems.length === 0} />
|
|
276
|
+
<ToggleButton
|
|
277
|
+
isOpen={isOpen}
|
|
278
|
+
buttonRef={buttonRef}
|
|
279
|
+
getToggleButtonProps={getToggleButtonProps}
|
|
280
|
+
/>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
<DropdownMenu
|
|
285
|
+
isOpen={isOpen}
|
|
286
|
+
getMenuProps={getMenuProps}
|
|
287
|
+
items={availableItems}
|
|
288
|
+
highlightedIndex={highlightedIndex}
|
|
289
|
+
getItemProps={getItemProps}
|
|
290
|
+
formatItemInList={formatItemInList}
|
|
291
|
+
itemToString={itemToString}
|
|
292
|
+
selectedItem={selectedItems}
|
|
293
|
+
emptyMessage={selectedItems.length > 0 ? 'No more elements to select.' : 'No elements to select.'}
|
|
294
|
+
/>
|
|
161
295
|
</div>
|
|
162
296
|
);
|
|
163
297
|
}
|
|
298
|
+
|
|
299
|
+
function useShadowEnvironment(divRef: React.RefObject<HTMLDivElement>) {
|
|
300
|
+
const shadowRoot = divRef.current?.shadowRoot ?? undefined;
|
|
301
|
+
|
|
302
|
+
return shadowRoot !== undefined
|
|
303
|
+
? {
|
|
304
|
+
addEventListener: window.addEventListener.bind(window),
|
|
305
|
+
removeEventListener: window.removeEventListener.bind(window),
|
|
306
|
+
document: shadowRoot.ownerDocument,
|
|
307
|
+
Node: window.Node,
|
|
308
|
+
}
|
|
309
|
+
: undefined;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function ToggleButton({
|
|
313
|
+
isOpen,
|
|
314
|
+
buttonRef,
|
|
315
|
+
getToggleButtonProps,
|
|
316
|
+
onClick,
|
|
317
|
+
}: {
|
|
318
|
+
isOpen: boolean;
|
|
319
|
+
buttonRef?: React.Ref<HTMLButtonElement>;
|
|
320
|
+
getToggleButtonProps?: () => Record<string, unknown>;
|
|
321
|
+
onClick?: () => void;
|
|
322
|
+
}) {
|
|
323
|
+
const props = getToggleButtonProps ? getToggleButtonProps() : { onClick };
|
|
324
|
+
return (
|
|
325
|
+
<button aria-label='toggle menu' className='px-2' type='button' {...props} ref={buttonRef}>
|
|
326
|
+
{isOpen ? <>↑</> : <>↓</>}
|
|
327
|
+
</button>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function ClearButton({ onClick, isHidden }: { onClick: () => void; isHidden: boolean }) {
|
|
332
|
+
return (
|
|
333
|
+
<button
|
|
334
|
+
aria-label='clear selection'
|
|
335
|
+
className={`px-2 ${isHidden ? 'hidden' : ''}`}
|
|
336
|
+
type='button'
|
|
337
|
+
onClick={onClick}
|
|
338
|
+
tabIndex={-1}
|
|
339
|
+
>
|
|
340
|
+
<DeleteIcon />
|
|
341
|
+
</button>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function DropdownMenu<Item>({
|
|
346
|
+
isOpen,
|
|
347
|
+
getMenuProps,
|
|
348
|
+
items,
|
|
349
|
+
highlightedIndex,
|
|
350
|
+
getItemProps,
|
|
351
|
+
formatItemInList,
|
|
352
|
+
itemToString,
|
|
353
|
+
selectedItem,
|
|
354
|
+
emptyMessage,
|
|
355
|
+
}: {
|
|
356
|
+
isOpen: boolean;
|
|
357
|
+
getMenuProps: () => Record<string, unknown>;
|
|
358
|
+
items: Item[];
|
|
359
|
+
highlightedIndex: number;
|
|
360
|
+
getItemProps: (options: { item: Item; index: number }) => Record<string, unknown>;
|
|
361
|
+
formatItemInList: (item: Item) => ComponentChild;
|
|
362
|
+
itemToString: (item: Item) => string;
|
|
363
|
+
selectedItem?: Item | Item[] | null;
|
|
364
|
+
emptyMessage: string;
|
|
365
|
+
}) {
|
|
366
|
+
const isItemSelected = (item: Item) => {
|
|
367
|
+
if (Array.isArray(selectedItem)) {
|
|
368
|
+
return selectedItem.some((selected) => itemToString(selected) === itemToString(item));
|
|
369
|
+
}
|
|
370
|
+
return selectedItem !== null && selectedItem !== undefined && itemToString(selectedItem) === itemToString(item);
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
return (
|
|
374
|
+
<ul
|
|
375
|
+
className={`absolute bg-white mt-1 shadow-md max-h-80 overflow-scroll z-10 w-full min-w-32 ${isOpen ? '' : 'hidden'}`}
|
|
376
|
+
{...getMenuProps()}
|
|
377
|
+
>
|
|
378
|
+
{items.length > 0 ? (
|
|
379
|
+
items.map((item, index) => (
|
|
380
|
+
<li
|
|
381
|
+
className={`${highlightedIndex === index ? 'bg-blue-300' : ''} ${isItemSelected(item) ? 'font-bold' : ''} py-2 px-3 shadow-xs cursor-pointer`}
|
|
382
|
+
key={itemToString(item)}
|
|
383
|
+
{...getItemProps({ item, index })}
|
|
384
|
+
>
|
|
385
|
+
{formatItemInList(item)}
|
|
386
|
+
</li>
|
|
387
|
+
))
|
|
388
|
+
) : (
|
|
389
|
+
<li className='py-2 px-3 shadow-xs'>{emptyMessage}</li>
|
|
390
|
+
)}
|
|
391
|
+
</ul>
|
|
392
|
+
);
|
|
393
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { gsEventNames } from '../../utils/gsEventNames';
|
|
2
2
|
|
|
3
3
|
type LapisLineageFilter = Record<string, string | undefined>;
|
|
4
|
+
type LapisLineageMultiFilter = Record<string, string[] | undefined>;
|
|
4
5
|
|
|
5
6
|
export class LineageFilterChangedEvent extends CustomEvent<LapisLineageFilter> {
|
|
6
7
|
constructor(detail: LapisLineageFilter) {
|
|
@@ -11,3 +12,13 @@ export class LineageFilterChangedEvent extends CustomEvent<LapisLineageFilter> {
|
|
|
11
12
|
});
|
|
12
13
|
}
|
|
13
14
|
}
|
|
15
|
+
|
|
16
|
+
export class LineageMultiFilterChangedEvent extends CustomEvent<LapisLineageMultiFilter> {
|
|
17
|
+
constructor(detail: LapisLineageMultiFilter) {
|
|
18
|
+
super(gsEventNames.lineageFilterMultiChanged, {
|
|
19
|
+
detail,
|
|
20
|
+
bubbles: true,
|
|
21
|
+
composed: true,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -2,6 +2,8 @@ import { fetchLineageDefinition } from '../../lapisApi/lapisApi';
|
|
|
2
2
|
import { FetchAggregatedOperator } from '../../operator/FetchAggregatedOperator';
|
|
3
3
|
import type { LapisFilter } from '../../types';
|
|
4
4
|
|
|
5
|
+
export type LineageItem = { lineage: string; count: number };
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Generates the autocomplete list for lineage search. It includes lineages with wild cards
|
|
7
9
|
* (i.e. "BA.3.2.1" and "BA.3.2.1*") as well as all prefixes of lineages with an asterisk ("BA.3.2*").
|
|
@@ -56,8 +58,6 @@ export async function fetchLineageAutocompleteList({
|
|
|
56
58
|
});
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
export type LineageItem = { lineage: string; count: number };
|
|
60
|
-
|
|
61
61
|
async function getCountsByLineage({
|
|
62
62
|
lapisUrl,
|
|
63
63
|
lapisField,
|