@mosaicajs/components 0.2.0 → 0.4.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/README.md +2 -0
- package/dist/aggregate.d.ts +4 -10
- package/dist/aggregate.js +7 -13
- package/dist/artifact-card.d.ts +21 -7
- package/dist/artifact-card.js +136 -79
- package/dist/base.d.ts +44 -5
- package/dist/base.js +54 -5
- package/dist/cluster-filter.d.ts +29 -25
- package/dist/cluster-filter.js +123 -106
- package/dist/colour-editor.d.ts +15 -4
- package/dist/colour-editor.js +107 -34
- package/dist/colour-picker.d.ts +12 -5
- package/dist/colour-picker.js +119 -32
- package/dist/display.js +77 -9
- package/dist/events.d.ts +21 -7
- package/dist/explorer.d.ts +118 -37
- package/dist/explorer.js +487 -212
- package/dist/field-card.d.ts +304 -48
- package/dist/field-card.js +1364 -244
- package/dist/filter-panel.d.ts +54 -10
- package/dist/filter-panel.js +242 -42
- package/dist/filter.d.ts +22 -4
- package/dist/filter.js +113 -34
- package/dist/hierarchy.d.ts +68 -45
- package/dist/hierarchy.js +320 -243
- package/dist/item-card.d.ts +62 -14
- package/dist/item-card.js +310 -110
- package/dist/key-picker.d.ts +2 -1
- package/dist/key-picker.js +4 -3
- package/dist/layer-picker.d.ts +4 -2
- package/dist/layer-picker.js +16 -7
- package/dist/lineage.d.ts +132 -0
- package/dist/lineage.js +307 -0
- package/dist/map.d.ts +135 -24
- package/dist/map.js +387 -117
- package/dist/mosaica-components.js +1848 -832
- package/dist/mosaica-components.js.sri +1 -1
- package/dist/mosaica-components.json +2 -2
- package/dist/parts.d.ts +4 -4
- package/dist/parts.js +18 -4
- package/dist/paths.d.ts +34 -12
- package/dist/paths.js +84 -46
- package/dist/selection.d.ts +22 -3
- package/dist/selection.js +125 -59
- package/dist/states.d.ts +72 -0
- package/dist/states.js +135 -26
- package/dist/status.d.ts +63 -5
- package/dist/status.js +215 -36
- package/dist/tokens.d.ts +8 -4
- package/dist/tokens.js +144 -14
- package/dist/view-picker.js +5 -0
- package/package.json +4 -3
package/dist/filter.js
CHANGED
|
@@ -40,6 +40,7 @@ import { FloatingList } from './float.js';
|
|
|
40
40
|
import { MosaicaElement, columnCaption, emit } from './base.js';
|
|
41
41
|
import { attachContextRoot, defineOnce } from './define.js';
|
|
42
42
|
import { icon } from './icons.js';
|
|
43
|
+
import { NamedQuery } from './states.js';
|
|
43
44
|
import { chrome, tokens } from './tokens.js';
|
|
44
45
|
/** The operators a keyword control can send. */
|
|
45
46
|
const KEYWORD_OPERATORS = ['contains', 'prefix', 'eq'];
|
|
@@ -57,11 +58,16 @@ const BAR_FLOOR = 2;
|
|
|
57
58
|
* - A text column takes words that an item must all contain. Double quotes match a phrase where the
|
|
58
59
|
* column supports phrases, and `OR` between terms matches either.
|
|
59
60
|
* - A category column suggests values as the viewer types. The arrow keys move through the
|
|
60
|
-
* suggestions,
|
|
61
|
+
* suggestions, as do Home and End once one is reached. Enter chooses or removes the suggestion
|
|
62
|
+
* they reached, and Escape clears what was typed. A line under the box says how many values are
|
|
63
|
+
* listed, or why none is, and that they are loading once that has taken a quarter of a second.
|
|
61
64
|
* - A keyword column has a menu for choosing how to match, by `contains`, `prefix` or `eq`.
|
|
62
65
|
*
|
|
63
66
|
* The box sets the filter 350 ms after the last keystroke, so that the map does not redraw for
|
|
64
|
-
* every letter.
|
|
67
|
+
* every letter. While the query that change started is still loading, and once it has run for a
|
|
68
|
+
* quarter of a second, a spinner takes the search icon's place. The spinner is not read out:
|
|
69
|
+
* `<mosaica-status>` is what announces a running query. If your code sets a text filter the box
|
|
70
|
+
* cannot express, the box shows it read-only.
|
|
65
71
|
*
|
|
66
72
|
* @summary A search box that filters the map by one column.
|
|
67
73
|
* @tagname mosaica-filter
|
|
@@ -69,11 +75,13 @@ const BAR_FLOOR = 2;
|
|
|
69
75
|
* @fires {CustomEvent<MosaicaEventDetails['mosaica-filterchange']>} mosaica-filterchange - The box
|
|
70
76
|
* changed the column's filter.
|
|
71
77
|
* @csspart entry - The box.
|
|
78
|
+
* @csspart spinner - The spinner in the search icon's place while the box's own query runs.
|
|
72
79
|
* @csspart mode - The keyword column's operator button, which opens its menu.
|
|
73
80
|
* @csspart operators - The menu of a keyword column's operators.
|
|
74
81
|
* @csspart operator - One operator in the menu, with `aria-checked`.
|
|
75
82
|
* @csspart values - The typeahead's suggestions.
|
|
76
|
-
* @csspart tick - One suggested value, with `aria-selected
|
|
83
|
+
* @csspart tick - One suggested value, with `aria-selected`, and `data-active` on the one the arrow
|
|
84
|
+
* keys reached.
|
|
77
85
|
* @csspart bar - A bar for a suggested value's share of the items counted.
|
|
78
86
|
* @csspart value-count - A suggested value's count.
|
|
79
87
|
* @csspart more - The note under the box when nothing matches, or when more values match than are
|
|
@@ -144,6 +152,10 @@ let MosaicaFilter = (() => {
|
|
|
144
152
|
.input input {
|
|
145
153
|
font-size: 12px;
|
|
146
154
|
}
|
|
155
|
+
/* In the search icon's place, at its width. */
|
|
156
|
+
.input .spinner {
|
|
157
|
+
margin: 0 1px;
|
|
158
|
+
}
|
|
147
159
|
.ctl-row {
|
|
148
160
|
display: flex;
|
|
149
161
|
align-items: center;
|
|
@@ -233,6 +245,11 @@ let MosaicaFilter = (() => {
|
|
|
233
245
|
[part~='tick'][aria-selected='true'] {
|
|
234
246
|
background: var(--_mosaica-surface-2);
|
|
235
247
|
}
|
|
248
|
+
/* The box keeps focus, so the suggestion the arrow keys reached is ringed as if it had it. */
|
|
249
|
+
[part~='tick'][data-active] {
|
|
250
|
+
outline: 2px solid var(--_mosaica-focus);
|
|
251
|
+
outline-offset: -2px;
|
|
252
|
+
}
|
|
236
253
|
[part~='tick'][aria-selected='true'] {
|
|
237
254
|
font-weight: 600;
|
|
238
255
|
}
|
|
@@ -243,9 +260,10 @@ let MosaicaFilter = (() => {
|
|
|
243
260
|
min-width: 0;
|
|
244
261
|
}
|
|
245
262
|
.opt .name {
|
|
263
|
+
white-space: var(--_mosaica-long-text, nowrap);
|
|
246
264
|
overflow: hidden;
|
|
247
265
|
text-overflow: ellipsis;
|
|
248
|
-
|
|
266
|
+
overflow-wrap: anywhere;
|
|
249
267
|
}
|
|
250
268
|
/* The matched span the server gave. */
|
|
251
269
|
[part~='tick'] mark {
|
|
@@ -264,11 +282,13 @@ let MosaicaFilter = (() => {
|
|
|
264
282
|
height: 3px;
|
|
265
283
|
border-radius: 2px;
|
|
266
284
|
background: var(--_mosaica-surface-3);
|
|
285
|
+
forced-color-adjust: none;
|
|
267
286
|
}
|
|
268
287
|
[part='bar'] {
|
|
269
288
|
height: 3px;
|
|
270
289
|
border-radius: 2px;
|
|
271
290
|
background: color-mix(in srgb, var(--_mosaica-ink-2) 75%, var(--_mosaica-surface));
|
|
291
|
+
forced-color-adjust: none;
|
|
272
292
|
}
|
|
273
293
|
[aria-selected='true'] [part='bar'] {
|
|
274
294
|
background: var(--_mosaica-ink);
|
|
@@ -298,6 +318,21 @@ let MosaicaFilter = (() => {
|
|
|
298
318
|
.skel {
|
|
299
319
|
margin-top: 8px;
|
|
300
320
|
}
|
|
321
|
+
@media (forced-colors: active) {
|
|
322
|
+
[part~='tick'][data-active] {
|
|
323
|
+
outline-color: Highlight;
|
|
324
|
+
}
|
|
325
|
+
.track {
|
|
326
|
+
background: GrayText;
|
|
327
|
+
}
|
|
328
|
+
[part='bar'] {
|
|
329
|
+
background: CanvasText;
|
|
330
|
+
}
|
|
331
|
+
/* A chosen row is painted Highlight there. */
|
|
332
|
+
[aria-selected='true'] [part='bar'] {
|
|
333
|
+
background: HighlightText;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
301
336
|
`
|
|
302
337
|
];
|
|
303
338
|
#column_accessor_storage = __runInitializers(this, _column_initializers, '');
|
|
@@ -324,7 +359,7 @@ let MosaicaFilter = (() => {
|
|
|
324
359
|
get search() { return this.#search_accessor_storage; }
|
|
325
360
|
set search(value) { this.#search_accessor_storage = value; }
|
|
326
361
|
#activeCode_accessor_storage = (__runInitializers(this, _search_extraInitializers), __runInitializers(this, _activeCode_initializers, null));
|
|
327
|
-
/** The suggestion the arrow keys
|
|
362
|
+
/** The suggestion the arrow keys reached, by code; `null` before they reach one. @internal */
|
|
328
363
|
get activeCode() { return this.#activeCode_accessor_storage; }
|
|
329
364
|
set activeCode(value) { this.#activeCode_accessor_storage = value; }
|
|
330
365
|
#focused_accessor_storage = (__runInitializers(this, _activeCode_extraInitializers), __runInitializers(this, _focused_initializers, false));
|
|
@@ -336,6 +371,11 @@ let MosaicaFilter = (() => {
|
|
|
336
371
|
get operatorsOpen() { return this.#operatorsOpen_accessor_storage; }
|
|
337
372
|
set operatorsOpen(value) { this.#operatorsOpen_accessor_storage = value; }
|
|
338
373
|
sent = (__runInitializers(this, _operatorsOpen_extraInitializers), null);
|
|
374
|
+
/** Whether the query the store is running was started by this box. */
|
|
375
|
+
asked = false;
|
|
376
|
+
query = new NamedQuery(this);
|
|
377
|
+
/** Whether the suggestions have been awaited long enough to say so. */
|
|
378
|
+
awaited = new NamedQuery(this);
|
|
339
379
|
floating = new FloatingList(() => {
|
|
340
380
|
const list = this.renderRoot.querySelector('[part="values"]');
|
|
341
381
|
const anchor = this.renderRoot.querySelector('.combo');
|
|
@@ -366,6 +406,7 @@ let MosaicaFilter = (() => {
|
|
|
366
406
|
// The draft being edited was the previous store's; the next is seeded from the one adopted.
|
|
367
407
|
this.draft = null;
|
|
368
408
|
this.sent = null;
|
|
409
|
+
this.asked = false;
|
|
369
410
|
}
|
|
370
411
|
get resolvedOperand() {
|
|
371
412
|
return this.resolvedStore?.get('meta')?.filterOperands.find((o) => o.column === this.column) ?? null;
|
|
@@ -396,14 +437,22 @@ let MosaicaFilter = (() => {
|
|
|
396
437
|
this.ask('');
|
|
397
438
|
this.floating.stop();
|
|
398
439
|
this.operatorMenu.stop();
|
|
440
|
+
this.query.stop();
|
|
441
|
+
this.awaited.stop();
|
|
399
442
|
super.disconnectedCallback();
|
|
400
443
|
}
|
|
401
444
|
onStoreChange() {
|
|
402
|
-
// Re-seed from the store only when its draft changed underneath (a clear all,
|
|
403
|
-
// meta), not while the user's own edit is in flight.
|
|
445
|
+
// Re-seed from the store only when its draft changed underneath (a clear all, a cancelled
|
|
446
|
+
// query, or the first meta), not while the user's own edit is in flight. A draft that no
|
|
447
|
+
// longer holds the control this box sent has dropped it.
|
|
404
448
|
const stored = this.resolvedStore?.get('filters').draft.filter[this.column] ?? null;
|
|
405
|
-
|
|
406
|
-
|
|
449
|
+
const changed = stored ? stored !== this.sent && JSON.stringify(stored) !== JSON.stringify(this.draft) : this.sent !== null;
|
|
450
|
+
if (changed) {
|
|
451
|
+
// What was typed since is not sent over the draft that replaced it.
|
|
452
|
+
if (this.typing)
|
|
453
|
+
clearTimeout(this.typing);
|
|
454
|
+
this.typing = null;
|
|
455
|
+
this.draft = stored ? structuredClone(stored) : null;
|
|
407
456
|
this.sent = stored;
|
|
408
457
|
}
|
|
409
458
|
if (this.resolvedOperand?.family === 'category') {
|
|
@@ -428,6 +477,24 @@ let MosaicaFilter = (() => {
|
|
|
428
477
|
super.updated(changed);
|
|
429
478
|
this.floating.update();
|
|
430
479
|
this.operatorMenu.update();
|
|
480
|
+
if (changed.has('activeCode'))
|
|
481
|
+
this.renderRoot.querySelector('[part~="tick"][data-active]')?.scrollIntoView({ block: 'nearest' });
|
|
482
|
+
if (!this.querying)
|
|
483
|
+
this.asked = false;
|
|
484
|
+
this.query.track(this.asked);
|
|
485
|
+
this.awaited.track(this.awaiting);
|
|
486
|
+
}
|
|
487
|
+
get querying() {
|
|
488
|
+
return this.resolvedStore?.get('status').querying ?? false;
|
|
489
|
+
}
|
|
490
|
+
/** Whether a category box holds text whose suggestions have neither landed nor been refused. */
|
|
491
|
+
get awaiting() {
|
|
492
|
+
const filters = this.resolvedStore?.get('filters');
|
|
493
|
+
return this.resolvedOperand?.family === 'category' && this.search !== '' && this.resolvedSuggestion === null && !filters?.suggestErrors[this.column];
|
|
494
|
+
}
|
|
495
|
+
/** The search icon, or a spinner while the query this box started has run long enough to name. */
|
|
496
|
+
lead() {
|
|
497
|
+
return this.asked && this.querying && this.query.named ? html `<span part="spinner" class="spinner" aria-hidden="true"></span>` : icon('search', 12);
|
|
431
498
|
}
|
|
432
499
|
/** The box's draft: the one being edited, else the store's, else the client's empty one, which also sets a keyword's starting operator. */
|
|
433
500
|
currentDraft(o) {
|
|
@@ -447,6 +514,8 @@ let MosaicaFilter = (() => {
|
|
|
447
514
|
const draft = { ...held, filter: { ...held.filter, [column]: next } };
|
|
448
515
|
this.sent = next;
|
|
449
516
|
s.setFilters(draft);
|
|
517
|
+
this.asked = s.get('status').querying;
|
|
518
|
+
this.requestUpdate();
|
|
450
519
|
emit(this, 'mosaica-filterchange', { column, verb: 'filter', expr: composeFilters(draft, 'filter') });
|
|
451
520
|
};
|
|
452
521
|
if (immediate)
|
|
@@ -472,16 +541,18 @@ let MosaicaFilter = (() => {
|
|
|
472
541
|
return nothing;
|
|
473
542
|
}
|
|
474
543
|
}
|
|
475
|
-
|
|
476
|
-
|
|
544
|
+
/** The box's name: the column, and the placeholder the box shows. */
|
|
545
|
+
label(shown) {
|
|
546
|
+
return shown ? `${columnCaption(this.column)}: ${shown}` : columnCaption(this.column);
|
|
477
547
|
}
|
|
478
548
|
text(draft) {
|
|
479
549
|
// An expression set from outside that no query writes is shown as it is, and cannot be typed over.
|
|
480
550
|
if (draft.expr !== undefined) {
|
|
481
|
-
return html `<div class="input">${icon('search', 12)}<input id="ctl" part="entry" readonly aria-label=${this.label()} .value=${JSON.stringify(draft.expr)} /></div>`;
|
|
551
|
+
return html `<div class="input">${icon('search', 12)}<input id="ctl" part="entry" readonly aria-label=${this.label('')} .value=${JSON.stringify(draft.expr)} /></div>`;
|
|
482
552
|
}
|
|
483
|
-
|
|
484
|
-
|
|
553
|
+
const shown = this.placeholder || 'Search the text';
|
|
554
|
+
return html `<div class="input">${this.lead()}<input id="ctl" part="entry" type="search" .value=${draft.query} placeholder=${shown} autocomplete="off"
|
|
555
|
+
aria-label=${this.label(shown)} @input=${(e) => this.change({ family: 'text', query: e.target.value, phrase: draft.phrase }, false)} /></div>`;
|
|
485
556
|
}
|
|
486
557
|
/** A keyword box, offering the operators the column publishes in a menu. */
|
|
487
558
|
keyword(o, draft) {
|
|
@@ -516,7 +587,7 @@ let MosaicaFilter = (() => {
|
|
|
516
587
|
void this.updateComplete.then(() => this.renderRoot.querySelector('[part~="operator"][aria-checked="true"]')?.focus());
|
|
517
588
|
};
|
|
518
589
|
return html `<div class="ctl-row">
|
|
519
|
-
<div class="input grow">${
|
|
590
|
+
<div class="input grow">${this.lead()}<input id="ctl" part="entry" type="search" .value=${draft.needle} autocomplete="off" placeholder=${this.placeholder || nothing} aria-label=${this.label(this.placeholder)}
|
|
520
591
|
@input=${(e) => this.change({ ...draft, needle: e.target.value }, false)} /></div>
|
|
521
592
|
<div class="op" @focusout=${(e) => {
|
|
522
593
|
const to = e.relatedTarget;
|
|
@@ -574,18 +645,20 @@ let MosaicaFilter = (() => {
|
|
|
574
645
|
this.change({ ...draft, keys: chosen.has(key) ? draft.keys.filter((k) => k !== key) : [...draft.keys, key] }, true);
|
|
575
646
|
};
|
|
576
647
|
const typed = this.search !== '';
|
|
648
|
+
const listed = suggestion?.values ?? [];
|
|
577
649
|
// The suggestions show while the box has focus and holds text; the list closes as focus leaves.
|
|
578
|
-
const rows = typed && this.focused ?
|
|
579
|
-
// The row
|
|
580
|
-
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
650
|
+
const rows = typed && this.focused ? listed : [];
|
|
651
|
+
// The row Enter acts on: the one the arrow keys reached. Until they reach one there is none, so
|
|
652
|
+
// Enter does not take whichever row a page that lands puts first.
|
|
653
|
+
const active = rows.find((v) => v.code === this.activeCode) ?? null;
|
|
654
|
+
const at = active ? rows.indexOf(active) : -1;
|
|
655
|
+
const reach = (i) => {
|
|
656
|
+
if (rows.length > 0)
|
|
657
|
+
this.activeCode = rows[(i + rows.length) % rows.length].code;
|
|
586
658
|
};
|
|
587
|
-
const
|
|
588
|
-
|
|
659
|
+
const shown = this.placeholder || 'Type a value';
|
|
660
|
+
const field = html `<div class="input">${this.lead()}<input id="ctl" part="entry" type="search" autocomplete="off" placeholder=${shown}
|
|
661
|
+
aria-label=${this.label(shown)} role="combobox" aria-autocomplete="list" aria-expanded=${rows.length > 0 ? 'true' : 'false'} aria-controls="values" aria-activedescendant=${active ? `value-${active.code}` : nothing}
|
|
589
662
|
.value=${this.search}
|
|
590
663
|
@focus=${() => (this.focused = true)}
|
|
591
664
|
@blur=${() => (this.focused = false)}
|
|
@@ -598,7 +671,12 @@ let MosaicaFilter = (() => {
|
|
|
598
671
|
@keydown=${(e) => {
|
|
599
672
|
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
600
673
|
e.preventDefault();
|
|
601
|
-
|
|
674
|
+
reach(e.key === 'ArrowDown' ? at + 1 : Math.max(at, 0) - 1);
|
|
675
|
+
}
|
|
676
|
+
else if ((e.key === 'Home' || e.key === 'End') && active) {
|
|
677
|
+
// Before the arrow keys reach a suggestion, Home and End move in the text.
|
|
678
|
+
e.preventDefault();
|
|
679
|
+
reach(e.key === 'Home' ? 0 : -1);
|
|
602
680
|
}
|
|
603
681
|
else if (e.key === 'Enter') {
|
|
604
682
|
if (active)
|
|
@@ -623,20 +701,21 @@ let MosaicaFilter = (() => {
|
|
|
623
701
|
</button>`;
|
|
624
702
|
};
|
|
625
703
|
const list = rows.length > 0
|
|
626
|
-
? html `<div part="values" id="values" popover="manual" role="listbox" aria-label=${`${columnCaption(this.column)} values`} style=${countWidth(rows.map((v) => v.count))}>${repeat(rows, (v) => v.code, option)}</div>`
|
|
704
|
+
? html `<div part="values" id="values" popover="manual" role="listbox" aria-multiselectable=${choose ? nothing : 'true'} aria-label=${`${columnCaption(this.column)} values`} style=${countWidth(rows.map((v) => v.count))}>${repeat(rows, (v) => v.code, option)}</div>`
|
|
627
705
|
: nothing;
|
|
628
|
-
|
|
706
|
+
// How many values are listed, or why none is, for a reader as the list changes under the box.
|
|
707
|
+
const said = !typed
|
|
629
708
|
? nothing
|
|
630
709
|
: refusal
|
|
631
710
|
? html `<span part="refusal" data-code=${refusal.code}><span class="dot refuse"></span>Values unavailable</span>`
|
|
632
711
|
: suggestion === null
|
|
633
|
-
? html `<span class="skel" aria-hidden="true"></span>`
|
|
634
|
-
:
|
|
712
|
+
? html `<span class="skel" aria-hidden="true"></span>${this.awaited.named ? html `<span class="sr-only">Loading values</span>` : nothing}`
|
|
713
|
+
: listed.length === 0
|
|
635
714
|
? html `<span part="more">No value starts with that</span>`
|
|
636
|
-
:
|
|
637
|
-
?
|
|
638
|
-
: nothing
|
|
639
|
-
return html `<div class="combo">${field}${list}</div>${
|
|
715
|
+
: rows.length === 0
|
|
716
|
+
? nothing
|
|
717
|
+
: html `<span class="sr-only">${rows.length} ${rows.length === 1 ? 'value' : 'values'} listed. </span>${suggestion.more ? html `<span part="more">Type more to narrow the list</span>` : nothing}`;
|
|
718
|
+
return html `<div class="combo">${field}${list}</div><div role="status">${said}</div>`;
|
|
640
719
|
}
|
|
641
720
|
};
|
|
642
721
|
})();
|
package/dist/hierarchy.d.ts
CHANGED
|
@@ -10,10 +10,19 @@ import './count.js';
|
|
|
10
10
|
* hierarchy, filter layers among them. When there are several, a select switches between them.
|
|
11
11
|
*
|
|
12
12
|
* Each row shows how many of the artifact's items the viewer may see. While filters are set, it
|
|
13
|
-
* also shows how many of those items match
|
|
14
|
-
*
|
|
13
|
+
* also shows how many of those items match, and a screen reader is given each count with what it
|
|
14
|
+
* counts. An artifact the viewer may not see is left out, and its children appear under its
|
|
15
|
+
* nearest ancestors the viewer may see, or at the top level where it has none. A row under several
|
|
16
|
+
* served parents names the others, and a parent no page has served yet is asked for by name. When
|
|
17
|
+
* the filters change, the rows are fetched again, a few requests at a time: as many pages of the
|
|
18
|
+
* roots and of each open row as More had loaded, since a page's cursor is good only for the
|
|
19
|
+
* filters it was served under. Focus stays on the control it was on.
|
|
15
20
|
*
|
|
16
|
-
* A row's
|
|
21
|
+
* A row's name is one line, cut with an ellipsis where it is too long, unless
|
|
22
|
+
* `--mosaica-long-text` is `normal`. A row's buttons highlight or filter the map by its artifact.
|
|
23
|
+
* They are shown in place of its counts while the pointer or focus is in the row, and Escape puts
|
|
24
|
+
* them away again; where there is no pointer to hover with, they stand beside the counts. Its Fit
|
|
25
|
+
* button only fires
|
|
17
26
|
* `mosaica-artifactfit`, so you handle the event by passing its id to the map's `fitTo`. The
|
|
18
27
|
* explorer does this for a hierarchy you put in one of its slots. `fitTo` finds only the artifacts
|
|
19
28
|
* the map currently draws, so Fit does nothing for a row whose artifact is not drawn at this zoom.
|
|
@@ -28,31 +37,39 @@ import './count.js';
|
|
|
28
37
|
* @fires {CustomEvent<MosaicaEventDetails['mosaica-artifactfit']>} mosaica-artifactfit - A row's Fit
|
|
29
38
|
* button was pressed.
|
|
30
39
|
* @csspart title - The heading.
|
|
31
|
-
* @csspart state - The state line, with `data-state
|
|
40
|
+
* @csspart state - The state line, with `data-state`, inside a `role="status"` region, which also
|
|
41
|
+
* says which rows are loading children and which were refused them.
|
|
32
42
|
* @csspart refusal - The words "View refused" or "Layer unavailable", with `data-code`.
|
|
33
43
|
* @csspart reason - The server's reason for a refusal, under "View refused".
|
|
34
44
|
* @csspart layer - The layer select, shown if there are several layers.
|
|
35
45
|
* @csspart search - The search box.
|
|
36
|
-
* @csspart tree - The tree of rows.
|
|
37
|
-
* @csspart row - One row, with `data-id`,
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
46
|
+
* @csspart tree - The tree of rows, named by the layer.
|
|
47
|
+
* @csspart row - One row, with `data-id`, `data-clause` (`filter`, `highlight` or both, separated
|
|
48
|
+
* by a space) while its artifact is filtered or highlighted, and `data-buttons-away` once Escape has put
|
|
49
|
+
* its buttons away.
|
|
50
|
+
* @csspart expander - A row's expand button, with `aria-expanded` while its children are shown. On
|
|
51
|
+
* a row with no children it has `data-leaf` and is hidden, keeping the names aligned.
|
|
52
|
+
* @csspart name - A row's name. Where the artifact has none it is the artifact's key, with
|
|
53
|
+
* `data-key-name`, and where it has no key either it is the word Unnamed, with `data-unnamed`.
|
|
54
|
+
* Pressing it highlights the artifact, and it has `aria-pressed` while the artifact is
|
|
55
|
+
* highlighted.
|
|
56
|
+
* @csspart counts - A row's counts as they are drawn, hidden from a screen reader, which is given
|
|
57
|
+
* them beside it with what each counts. They are not drawn while the row's buttons are shown
|
|
58
|
+
* over them.
|
|
44
59
|
* @csspart count-matched - The `<mosaica-count>` of a row's items that match the filters, while a
|
|
45
60
|
* filter is set.
|
|
46
61
|
* @csspart count-masked - The `<mosaica-count>` of a row's items the viewer may see.
|
|
47
|
-
* @csspart actions - A row's buttons
|
|
62
|
+
* @csspart actions - A row's buttons, shown over its counts while the pointer or focus is in the
|
|
63
|
+
* row.
|
|
48
64
|
* @csspart highlight - A row's highlight button, with `aria-pressed`.
|
|
49
65
|
* @csspart filter - A row's filter button, with `aria-pressed`.
|
|
50
66
|
* @csspart fit - A row's Fit button, on a layer that draws.
|
|
51
67
|
* @csspart dismiss - A × that removes a row's filter or highlight, one for each, with `data-verb`.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
68
|
+
* The highlight's has a bar under it.
|
|
69
|
+
* @csspart also - The row's other parents, each with `data-key-name` where it is called by its key,
|
|
70
|
+
* or "Children unavailable" with `data-code` if its children were refused.
|
|
54
71
|
* @csspart children - An expanded row's children.
|
|
55
|
-
* @csspart more - The More button, which loads the next page.
|
|
72
|
+
* @csspart more - The More button, which loads the next page and moves focus to its first row.
|
|
56
73
|
*/
|
|
57
74
|
export declare class MosaicaHierarchy extends MosaicaElement {
|
|
58
75
|
static styles: import("lit").CSSResult[];
|
|
@@ -60,34 +77,22 @@ export declare class MosaicaHierarchy extends MosaicaElement {
|
|
|
60
77
|
accessor layer: string;
|
|
61
78
|
/** Rows per page, from 1 up to the server's `maxBrowseRows`. */
|
|
62
79
|
accessor limit: number;
|
|
63
|
-
private accessor roots;
|
|
64
|
-
private accessor rootsNext;
|
|
65
80
|
private accessor query;
|
|
66
81
|
private accessor searching;
|
|
67
|
-
|
|
68
|
-
private accessor
|
|
82
|
+
/** Why the last search was refused. */
|
|
83
|
+
private accessor searchRefusal;
|
|
69
84
|
/** Which paths are open, so a re-render of the tree keeps the walk. */
|
|
70
85
|
private accessor open;
|
|
71
|
-
/**
|
|
72
|
-
private accessor
|
|
73
|
-
/** The
|
|
74
|
-
private
|
|
75
|
-
/**
|
|
76
|
-
* Every name the walk has seen, by identifier. A filter layer's artifacts are never served in a
|
|
77
|
-
* viewport, so this is the client's only source of their names.
|
|
78
|
-
*/
|
|
79
|
-
private names;
|
|
86
|
+
/** The paths of the rows whose buttons Escape has put away, each until the pointer and focus leave it. */
|
|
87
|
+
private accessor buttonsAway;
|
|
88
|
+
/** The browse pages of the current layer: its roots and the children of the rows opened. */
|
|
89
|
+
private readonly lineage;
|
|
80
90
|
private searchTimer;
|
|
81
|
-
/** Moved by {@link resetServerData}, so a
|
|
91
|
+
/** Moved by {@link resetServerData}, so a search asked for before it is dropped. */
|
|
82
92
|
private epoch;
|
|
83
93
|
protected resetServerData(): void;
|
|
84
94
|
private layers;
|
|
85
95
|
private current;
|
|
86
|
-
/**
|
|
87
|
-
* The question the panel is asking, as a string: the layer and the map's filter. A change
|
|
88
|
-
* refetches the roots and drops the walk, whose counts answer the old question.
|
|
89
|
-
*/
|
|
90
|
-
private question;
|
|
91
96
|
/**
|
|
92
97
|
* Whether the panel is shown. A hidden panel sends no request, so a panel in a closed drawer
|
|
93
98
|
* does not load the roots alongside the first viewport.
|
|
@@ -99,30 +104,48 @@ export declare class MosaicaHierarchy extends MosaicaElement {
|
|
|
99
104
|
private observer;
|
|
100
105
|
connectedCallback(): void;
|
|
101
106
|
disconnectedCallback(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Escape puts away the buttons shown over a row's counts, wherever focus is, since the pointer
|
|
109
|
+
* alone shows them. Focus on one of them goes to the row's name.
|
|
110
|
+
*/
|
|
111
|
+
private onEscape;
|
|
112
|
+
/** The buttons Escape put away come back once the pointer and focus have both left the row. */
|
|
113
|
+
private leftRow;
|
|
114
|
+
/** Let a row show its buttons again. */
|
|
115
|
+
private shownAgain;
|
|
102
116
|
/** Whether this element is displayed; see {@link shown}. */
|
|
103
117
|
private displayed;
|
|
104
118
|
/** The roots, where the question they were fetched under is no longer the one being asked. */
|
|
105
119
|
private loadRootsIfStale;
|
|
106
120
|
protected onStoreChange(): void;
|
|
121
|
+
/** The control focus was on as the last render began. */
|
|
122
|
+
private held;
|
|
107
123
|
/** Re-asked before every render, so putting the panel away and taking it out again both count. */
|
|
108
124
|
protected willUpdate(changed: PropertyValues<this>): void;
|
|
109
125
|
protected updated(): void;
|
|
110
|
-
/**
|
|
111
|
-
private
|
|
112
|
-
private loadRoots;
|
|
113
|
-
private node;
|
|
114
|
-
/** What to call an artifact: its name where the walk has met one. */
|
|
115
|
-
private nameOf;
|
|
126
|
+
/** Open a row, fetching its children where it has none yet, or close one that is open or opening. */
|
|
127
|
+
private toggle;
|
|
116
128
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
129
|
+
* Load the next page of the roots, or of `parent`'s children, and move focus from the More
|
|
130
|
+
* button, which the page replaces or removes, to the first row it added. Focus that has gone
|
|
131
|
+
* anywhere else meanwhile stays there.
|
|
119
132
|
*/
|
|
120
|
-
private
|
|
121
|
-
private
|
|
133
|
+
private more;
|
|
134
|
+
private rowElements;
|
|
135
|
+
/** The control in a row that focus is on, read before a render so {@link keepFocus} can put it back. */
|
|
136
|
+
private heldControl;
|
|
137
|
+
/**
|
|
138
|
+
* After a render, put focus back on the control it was on, in the row it was on. Where a ×
|
|
139
|
+
* removed itself, focus goes to the row's other ×, else to the button that makes the clause.
|
|
140
|
+
* Where the row is gone, it goes to the row now in its place, else the last, else the search box.
|
|
141
|
+
*/
|
|
142
|
+
private keepFocus;
|
|
122
143
|
private search;
|
|
123
144
|
/** The positions of the clauses on this row's artifact, which colour the row and set the buttons. */
|
|
124
145
|
private clausesOn;
|
|
125
146
|
private apply;
|
|
147
|
+
/** What the rows' own fetches say, for the status region: the rows loading children, and those refused them. */
|
|
148
|
+
private rowNotes;
|
|
126
149
|
render(): TemplateResult | typeof nothing;
|
|
127
150
|
private renderNode;
|
|
128
151
|
}
|