@fusion.dev/helix-headless 0.178.0 → 0.180.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 +543 -145
- package/dist/button/root/button-root.d.ts +12 -0
- package/dist/button/root/button-root.js +9 -0
- package/dist/chat/composer/chat-composer.d.ts +2 -1
- package/dist/chat/composer/chat-composer.js +1 -1
- package/dist/combobox/root/combobox-root.d.ts +12 -0
- package/dist/combobox/root/combobox-root.js +3 -2
- package/dist/command/root/command-root.d.ts +12 -0
- package/dist/command/root/command-root.js +3 -2
- package/dist/data-table/root/data-table-root.d.ts +13 -1
- package/dist/data-table/root/data-table-root.js +18 -15
- package/dist/date-input/root/date-input-root.d.ts +7 -0
- package/dist/date-input/root/date-input-root.js +12 -0
- package/dist/date-picker/root/date-picker-root.d.ts +7 -1
- package/dist/date-picker/root/date-picker-root.js +12 -0
- package/dist/drilldown/item/drilldown-item.d.ts +6 -0
- package/dist/drilldown/item/drilldown-item.js +9 -0
- package/dist/drilldown/root/drilldown-root.js +9 -0
- package/dist/element-events.generated.js +7 -1
- package/dist/filter-input/root/filter-input-root.d.ts +13 -0
- package/dist/filter-input/root/filter-input-root.js +2 -1
- package/dist/heatmap/root/heatmap-root.d.ts +54 -42
- package/dist/heatmap/root/heatmap-root.js +72 -54
- package/dist/listbox/root/listbox-root.d.ts +16 -2
- package/dist/listbox/root/listbox-root.js +8 -4
- package/dist/menu/item/menu-item.d.ts +6 -0
- package/dist/menu/item/menu-item.js +9 -0
- package/dist/meter/root/meter-root.js +1 -2
- package/dist/notifications/notification/notification.d.ts +37 -9
- package/dist/notifications/notification/notification.js +35 -5
- package/dist/option/among.d.ts +23 -0
- package/dist/option/among.js +2 -0
- package/dist/option/root/option-root.d.ts +1 -17
- package/dist/option/root/option-root.js +1 -4
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +5 -4
- package/dist/scatter-chart/root/scatter-chart-root.js +1 -0
- package/dist/select/root/select-root.d.ts +13 -2
- package/dist/select/root/select-root.js +3 -3
- package/dist/speed-dial/item/speed-dial-item.d.ts +6 -0
- package/dist/speed-dial/item/speed-dial-item.js +9 -0
- package/dist/table/column/table-column.d.ts +1 -10
- package/dist/table/column/table-column.js +1 -2
- package/dist/table/edge.d.ts +10 -0
- package/dist/table/edge.js +2 -0
- package/dist/tabs/root/tabs-root.d.ts +1 -0
- package/dist/tabs/root/tabs-root.js +13 -0
- package/dist/tabs/tab/tab.d.ts +6 -0
- package/dist/tabs/tab/tab.js +9 -0
- package/dist/tooltip/root/tooltip-root.js +1 -0
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +472 -130
- package/types/vue.d.ts +71 -50
- package/types/words.d.ts +27 -6
|
@@ -4,16 +4,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { AriaForwardController, byRow, CHART_PLOT, commaList, customElement, drawTooltip, formatWords, HelixElement, HelixResizeEvent, MixinChartSource, markRegion, pressedValue, rangeAdd, requestValue, StatusController, words, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, byRow, CHART_PLOT, commaList, customElement, drawTooltip, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, HelixResizeEvent, MixinChartSource, MixinChartStatus, markRegion, pressedValue, rangeAdd, requestValue, StatusController, spokenReading, words, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
import '../../tooltip/index.js';
|
|
11
11
|
import { fits, monthPage, overviewOf, toneOf } from './buckets.js';
|
|
12
|
-
let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(HelixElement) {
|
|
12
|
+
let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartStatus(MixinChartSource(HelixElement)) {
|
|
13
13
|
constructor() {
|
|
14
14
|
super(...arguments);
|
|
15
15
|
this.busiestLabel = 'Busiest';
|
|
16
|
-
this.emptyText = 'No readings';
|
|
17
16
|
this.gap = 2;
|
|
18
17
|
this.minCell = 8;
|
|
19
18
|
this.overviewLabel = '{busiestLabel}: {busiest}; {totalLabel} {total}.';
|
|
@@ -69,6 +68,16 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
69
68
|
const index = Number(cell?.getAttribute('data-index') ?? -1);
|
|
70
69
|
this.pointer.set(Number.isInteger(index) ? index : -1);
|
|
71
70
|
};
|
|
71
|
+
this.onPointerUp = (event) => {
|
|
72
|
+
if (event.pointerType !== 'touch') {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const cell = event.target?.closest?.('[part~="cell"]');
|
|
76
|
+
const index = Number(cell?.getAttribute('data-index') ?? -1);
|
|
77
|
+
if (cell && Number.isInteger(index) && index >= 0) {
|
|
78
|
+
this.pointer.tapTo(index);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
72
81
|
this.onPointerLeave = () => {
|
|
73
82
|
if (!this.pointer.keyed) {
|
|
74
83
|
this.pointer.set(-1);
|
|
@@ -112,9 +121,6 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
112
121
|
}
|
|
113
122
|
return byRow(series, this.sourceRows.length).map((point) => point ?? { value: undefined });
|
|
114
123
|
}
|
|
115
|
-
get sourceKnown() {
|
|
116
|
-
return this.readingsKnown;
|
|
117
|
-
}
|
|
118
124
|
get rowCount() {
|
|
119
125
|
return Math.max(1, Math.floor(this.rows) || 1);
|
|
120
126
|
}
|
|
@@ -160,6 +166,12 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
160
166
|
}
|
|
161
167
|
connectedCallback() {
|
|
162
168
|
super.connectedCallback();
|
|
169
|
+
this.pointer.axis = 'none';
|
|
170
|
+
this.pointer.onReading = (heard, detail) => {
|
|
171
|
+
const row = detail.index < 0 ? -1 : this.points.length - this.drawn.length + detail.index;
|
|
172
|
+
const told = { ...detail, index: row, label: this.sourceRows[row]?.label ?? '' };
|
|
173
|
+
this.dispatchEvent(heard === 'press' ? new HelixReadingPressEvent(told) : new HelixReadingChangeEvent(told));
|
|
174
|
+
};
|
|
163
175
|
this.addEventListener('keydown', this.onKeydown);
|
|
164
176
|
if (typeof ResizeObserver !== 'undefined') {
|
|
165
177
|
this.observer = new ResizeObserver((entries) => {
|
|
@@ -184,8 +196,6 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
184
196
|
super.updated(changed);
|
|
185
197
|
this.markEmpty();
|
|
186
198
|
this.measure();
|
|
187
|
-
this.toggleState('active', this.pointer.at >= 0);
|
|
188
|
-
this.toggleState('empty', this.drawn.length === 0 && this.sourceKnown);
|
|
189
199
|
const showing = this.visible;
|
|
190
200
|
const written = showing > 0 ? String(showing) : null;
|
|
191
201
|
if (this.getAttribute('visible') !== written) {
|
|
@@ -206,41 +216,48 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
206
216
|
const palette = writtenList(this.tones);
|
|
207
217
|
const cells = this.drawn;
|
|
208
218
|
const readable = cells.length > 0;
|
|
219
|
+
const grid = readable && this.selectionMode !== undefined;
|
|
209
220
|
const keys = this.selectionMode ? this.keysOf(cells) : [];
|
|
210
221
|
const chosen = new Set(this.selectionMode ? commaList(this.value) : []);
|
|
222
|
+
const drawn = cells.map((reading, index) => {
|
|
223
|
+
const tone = toneOf(reading, bounds, palette);
|
|
224
|
+
const active = this.pointer.at === index;
|
|
225
|
+
const picked = chosen.has(keys[index] ?? '');
|
|
226
|
+
if (grid) {
|
|
227
|
+
return this.renderGridCell(index, active, picked, tone);
|
|
228
|
+
}
|
|
229
|
+
return tone === undefined
|
|
230
|
+
? html `<span
|
|
231
|
+
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
232
|
+
data-index=${index}
|
|
233
|
+
></span>`
|
|
234
|
+
: html `<span
|
|
235
|
+
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
236
|
+
data-index=${index}
|
|
237
|
+
data-tone=${tone}
|
|
238
|
+
style="--heatmap-tone: var(--heatmap-tone-${tone})"
|
|
239
|
+
></span>`;
|
|
240
|
+
});
|
|
241
|
+
const at = this.pointer.at;
|
|
211
242
|
return html `<div
|
|
243
|
+
aria-activedescendant=${grid && at >= 0 ? `cell-${at}` : nothing}
|
|
212
244
|
aria-describedby=${readable ? 'helix-chart-summary' : nothing}
|
|
213
245
|
aria-label=${this.named || this.stripLabel}
|
|
214
|
-
|
|
246
|
+
aria-multiselectable=${grid && this.selectionMode === 'multiple' ? 'true' : nothing}
|
|
247
|
+
part="strip plot"
|
|
215
248
|
id=${CHART_PLOT}
|
|
216
|
-
role=${readable ? 'application' : 'img'}
|
|
249
|
+
role=${grid ? 'grid' : readable ? 'application' : 'img'}
|
|
217
250
|
style="--heatmap-rows: ${this.rowCount}"
|
|
218
251
|
tabindex=${readable ? '0' : nothing}
|
|
219
252
|
@click=${this.onClick}
|
|
220
253
|
@pointerover=${this.onPointerOver}
|
|
221
254
|
@pointerleave=${this.onPointerLeave}
|
|
255
|
+
@pointerup=${this.onPointerUp}
|
|
222
256
|
>
|
|
223
|
-
${
|
|
224
|
-
|
|
225
|
-
: cells.map((reading, index) => {
|
|
226
|
-
const tone = toneOf(reading, bounds, palette);
|
|
227
|
-
const active = this.pointer.at === index;
|
|
228
|
-
const picked = chosen.has(keys[index] ?? '');
|
|
229
|
-
return tone === undefined
|
|
230
|
-
? html `<span
|
|
231
|
-
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
232
|
-
data-index=${index}
|
|
233
|
-
></span>`
|
|
234
|
-
: html `<span
|
|
235
|
-
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
236
|
-
data-index=${index}
|
|
237
|
-
data-tone=${tone}
|
|
238
|
-
style="--heatmap-tone: var(--heatmap-tone-${tone})"
|
|
239
|
-
></span>`;
|
|
240
|
-
})}
|
|
241
|
-
${this.renderTooltip()}
|
|
257
|
+
${grid ? this.renderRows(drawn) : drawn}
|
|
258
|
+
${this.renderTooltip(grid)}
|
|
242
259
|
</div>
|
|
243
|
-
${cells.length === 0
|
|
260
|
+
${cells.length === 0 ? this.renderStatus() : nothing}
|
|
244
261
|
<div part="footer">
|
|
245
262
|
<span part="start"><slot name="start"></slot></span>
|
|
246
263
|
<span part="summary"><slot name="summary"></slot></span>
|
|
@@ -249,6 +266,28 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
249
266
|
? html `<span part="selection" role="status">${this.status.said}</span>`
|
|
250
267
|
: nothing}${this.renderTable()}`;
|
|
251
268
|
}
|
|
269
|
+
renderRows(cells) {
|
|
270
|
+
const size = this.rowCount === 1 ? cells.length : this.rowCount;
|
|
271
|
+
const rows = [];
|
|
272
|
+
for (let start = 0; start < cells.length; start += size) {
|
|
273
|
+
rows.push(html `<div part="row" role="row" style="display: contents">${cells.slice(start, start + size)}</div>`);
|
|
274
|
+
}
|
|
275
|
+
return rows;
|
|
276
|
+
}
|
|
277
|
+
renderGridCell(index, active, picked, tone) {
|
|
278
|
+
const row = this.sourceRows[this.points.length - this.drawn.length + index];
|
|
279
|
+
const name = row ? spokenReading(row) : '';
|
|
280
|
+
return html `<span
|
|
281
|
+
aria-label=${name || nothing}
|
|
282
|
+
aria-selected=${picked ? 'true' : 'false'}
|
|
283
|
+
data-index=${index}
|
|
284
|
+
data-tone=${tone ?? nothing}
|
|
285
|
+
id="cell-${index}"
|
|
286
|
+
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
287
|
+
role="gridcell"
|
|
288
|
+
style=${tone === undefined ? nothing : `--heatmap-tone: var(--heatmap-tone-${tone})`}
|
|
289
|
+
></span>`;
|
|
290
|
+
}
|
|
252
291
|
renderOverview(cells) {
|
|
253
292
|
const rows = this.sourceRows;
|
|
254
293
|
const offset = this.points.length - cells.length;
|
|
@@ -264,10 +303,10 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
264
303
|
});
|
|
265
304
|
return html `<p id="helix-chart-summary" part="overview">${said}</p>`;
|
|
266
305
|
}
|
|
267
|
-
renderTooltip() {
|
|
306
|
+
renderTooltip(grid = false) {
|
|
268
307
|
const at = this.pointer.at;
|
|
269
308
|
if (at < 0) {
|
|
270
|
-
return drawTooltip(this.sourceRows, -1, this.tag('tooltip'), undefined, undefined, this.pointer);
|
|
309
|
+
return drawTooltip(this.sourceRows, -1, this.tag('tooltip'), undefined, undefined, grid ? undefined : this.pointer);
|
|
271
310
|
}
|
|
272
311
|
const offset = this.points.length - this.drawn.length;
|
|
273
312
|
const cell = this.cellPx;
|
|
@@ -278,34 +317,13 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
278
317
|
placement: 'top',
|
|
279
318
|
x: `${column * (cell + gap) + cell / 2}px`,
|
|
280
319
|
y: `${row * (cell + gap)}px`,
|
|
281
|
-
}, undefined, this.
|
|
282
|
-
}
|
|
283
|
-
reading(at) {
|
|
284
|
-
const pointer = this.pointer;
|
|
285
|
-
const key = this.keysOf(this.drawn)[at] ?? '';
|
|
286
|
-
if (!this.selectionMode || key === '' || this.selectedLabel === '') {
|
|
287
|
-
return pointer;
|
|
288
|
-
}
|
|
289
|
-
if (!commaList(this.value).includes(key)) {
|
|
290
|
-
return pointer;
|
|
291
|
-
}
|
|
292
|
-
return {
|
|
293
|
-
column: pointer.column,
|
|
294
|
-
keyed: pointer.keyed,
|
|
295
|
-
position: pointer.position,
|
|
296
|
-
shows: (cell) => pointer.shows(cell),
|
|
297
|
-
state: this.selectedLabel,
|
|
298
|
-
};
|
|
320
|
+
}, undefined, grid ? undefined : this.pointer);
|
|
299
321
|
}
|
|
300
322
|
};
|
|
301
323
|
__decorate([
|
|
302
324
|
words,
|
|
303
325
|
property({ attribute: 'busiest-label' })
|
|
304
326
|
], HelixHeadlessHeatmap.prototype, "busiestLabel", void 0);
|
|
305
|
-
__decorate([
|
|
306
|
-
words,
|
|
307
|
-
property({ attribute: 'empty-text' })
|
|
308
|
-
], HelixHeadlessHeatmap.prototype, "emptyText", void 0);
|
|
309
327
|
__decorate([
|
|
310
328
|
property({ type: Number })
|
|
311
329
|
], HelixHeadlessHeatmap.prototype, "gap", void 0);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AriaForwardController, type DataOptionItem, DisabledCascadeController, HelixFormElement, UpgradeController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
-
import { HelixHeadlessOption } from '../../option/root/option-root.js';
|
|
3
|
+
import type { HelixHeadlessOption } from '../../option/root/option-root.js';
|
|
4
4
|
declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusion.dev/helix-core").CollectionElement) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
|
|
5
5
|
/**
|
|
6
6
|
* @summary A list of options, from which you choose one or several.
|
|
@@ -76,6 +76,19 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
|
|
|
76
76
|
* filter narrows the list, under `layout="grid"`, or while the list is
|
|
77
77
|
* disabled.
|
|
78
78
|
*
|
|
79
|
+
* **The options are an import of their own.** An option you write is the Option
|
|
80
|
+
* family's, which the list does not register, so a page that writes one imports
|
|
81
|
+
* it beside the list:
|
|
82
|
+
*
|
|
83
|
+
* ```js
|
|
84
|
+
* import '@fusion.dev/helix/listbox'
|
|
85
|
+
* import '@fusion.dev/helix/option'
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* In development, a list holding an option nobody registered says so once the
|
|
89
|
+
* page has loaded, with the line to add. The options `items` draws are this
|
|
90
|
+
* Option too, and need the same import.
|
|
91
|
+
*
|
|
79
92
|
* @since 0.1.0
|
|
80
93
|
* @status stable
|
|
81
94
|
* @category forms
|
|
@@ -218,7 +231,8 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
|
|
|
218
231
|
*
|
|
219
232
|
* **The list needs a height to scroll in**: the styled tier's
|
|
220
233
|
* `--listbox-max-block-size`, or a `max-block-size` on `::part(items)` in the
|
|
221
|
-
* headless tier.
|
|
234
|
+
* headless tier. The options drawn are this tier's Option, which the page
|
|
235
|
+
* imports, as it imports the options it writes.
|
|
222
236
|
* A server sends the list empty, and the options are drawn once it hydrates.
|
|
223
237
|
*/
|
|
224
238
|
items: readonly DataOptionItem[] | undefined;
|
|
@@ -4,12 +4,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { AriaForwardController, centerInView, chosenValues, columnStep, countLabel, customElement, DelayController, DisabledCascadeController, drawAutofill, endInSight, filterKeeps, formatWords, HelixFilterEvent, HelixFormElement, HelixLoadMoreEvent, hostAria, inlineStep, MixinCollection, MixinControllerValue, MixinField, markMatches, OptionWindowController, onApple, pageRows, rangeAdd, reportValues, requestChange, scrollItemIntoView, selectsAll, Typeahead, toggleEvery, typesCharacter, UpgradeController, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, auditChildren, centerInView, chosenValues, columnStep, countLabel, customElement, DelayController, DisabledCascadeController, drawAutofill, endInSight, filterKeeps, formatWords, HelixFilterEvent, HelixFormElement, HelixLoadMoreEvent, hostAria, inlineStep, MixinCollection, MixinControllerValue, MixinField, markMatches, OptionWindowController, onApple, pageRows, rangeAdd, reportValues, requestChange, scrollItemIntoView, selectsAll, Typeahead, toggleEvery, typesCharacter, UpgradeController, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { magnifyingGlass } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
12
|
-
import {
|
|
12
|
+
import { isOption } from '../../option/among.js';
|
|
13
13
|
const FIELD_MOVES = new Set(['ArrowDown', 'ArrowUp', 'PageDown', 'PageUp']);
|
|
14
14
|
const MOVES = new Set([...FIELD_MOVES, 'End', 'Home']);
|
|
15
15
|
const PAGE = 10;
|
|
@@ -306,7 +306,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
|
|
|
306
306
|
return this.multiple ? 'select-multiple' : 'select-one';
|
|
307
307
|
}
|
|
308
308
|
get options() {
|
|
309
|
-
return this.among(
|
|
309
|
+
return this.among(isOption, this.children);
|
|
310
310
|
}
|
|
311
311
|
among(is, nodes) {
|
|
312
312
|
const found = [];
|
|
@@ -421,7 +421,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
|
|
|
421
421
|
option.selected = chosen.has(option.value);
|
|
422
422
|
}
|
|
423
423
|
for (const group of this.groups) {
|
|
424
|
-
const inside = this.among(
|
|
424
|
+
const inside = this.among(isOption, group.children);
|
|
425
425
|
group.hidden = inside.length > 0 && inside.every((option) => option.hidden);
|
|
426
426
|
}
|
|
427
427
|
for (const rule of this.among((node) => node.localName.endsWith('-option-separator'), this.children)) {
|
|
@@ -479,6 +479,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
|
|
|
479
479
|
this.addEventListener('helix-select', this.handleSelection);
|
|
480
480
|
this.addEventListener('keydown', this.handleKeydown);
|
|
481
481
|
this.addEventListener('click', this.handleClick, true);
|
|
482
|
+
auditChildren(this, 'option', 'option', 'the list offers no choices');
|
|
482
483
|
}
|
|
483
484
|
disconnectedCallback() {
|
|
484
485
|
super.disconnectedCallback();
|
|
@@ -500,6 +501,9 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
|
|
|
500
501
|
if (this.childrenKnown) {
|
|
501
502
|
this.seedDeclared();
|
|
502
503
|
}
|
|
504
|
+
if (changed.has('items') && this.items) {
|
|
505
|
+
auditChildren(this, 'option', 'option', 'the items draw no choices', true);
|
|
506
|
+
}
|
|
503
507
|
if (this.childrenKnown) {
|
|
504
508
|
this.place();
|
|
505
509
|
this.tell(changed);
|
|
@@ -436,6 +436,12 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
436
436
|
protected renderMark(submenu: boolean): TemplateResult | typeof nothing;
|
|
437
437
|
/** Whether the row says what it is from its host, for a field's `aria-activedescendant`. */
|
|
438
438
|
private get onHost();
|
|
439
|
+
/**
|
|
440
|
+
* Presses the row, as `click()` presses a native button: passed to the
|
|
441
|
+
* control inside, which is what answers a click, so a script's `click()`
|
|
442
|
+
* chooses the row, or follows its link, once.
|
|
443
|
+
*/
|
|
444
|
+
click(): void;
|
|
439
445
|
protected render(): TemplateResult | typeof nothing;
|
|
440
446
|
}
|
|
441
447
|
declare global {
|
|
@@ -240,6 +240,15 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
|
240
240
|
get onHost() {
|
|
241
241
|
return this.virtual && this.onPage && !this.showingSubmenu;
|
|
242
242
|
}
|
|
243
|
+
click() {
|
|
244
|
+
const control = this.partNamed('item');
|
|
245
|
+
if (control) {
|
|
246
|
+
control.click();
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
super.click();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
243
252
|
render() {
|
|
244
253
|
const submenu = this.hasSubmenu;
|
|
245
254
|
const ticks = this.choice;
|
|
@@ -83,8 +83,7 @@ let HelixHeadlessMeter = class HelixHeadlessMeter extends HelixElement {
|
|
|
83
83
|
this.internals.ariaValueMin = String(min);
|
|
84
84
|
this.internals.ariaValueMax = String(max);
|
|
85
85
|
this.internals.ariaValueNow = String(this.reading);
|
|
86
|
-
this.internals.ariaValueText =
|
|
87
|
-
this.valueText || this.formatOptions !== undefined || this.beyond ? this.text : null;
|
|
86
|
+
this.internals.ariaValueText = this.text;
|
|
88
87
|
const band = this.computedBand;
|
|
89
88
|
const beyond = this.beyond;
|
|
90
89
|
this.toggleState('fair', band === 'fair');
|
|
@@ -89,6 +89,15 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
|
|
|
89
89
|
* Whether the notification starts as read, used until `read` is set.
|
|
90
90
|
*/
|
|
91
91
|
defaultRead: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* The dismiss control's name, read by screen readers, with `{label}` for the
|
|
94
|
+
* row's own words.
|
|
95
|
+
*
|
|
96
|
+
* One sentence with the words in it, so a translation puts them where its
|
|
97
|
+
* language does: `registerWords('de', { 'helix-notification': { dismissLabel:
|
|
98
|
+
* '{label} schließen' } })`.
|
|
99
|
+
*/
|
|
100
|
+
dismissLabel: string;
|
|
92
101
|
/**
|
|
93
102
|
* Locale for the timestamp.
|
|
94
103
|
*
|
|
@@ -129,13 +138,6 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
|
|
|
129
138
|
* the first render on the client is the server's.
|
|
130
139
|
*/
|
|
131
140
|
stop: boolean;
|
|
132
|
-
/**
|
|
133
|
-
* What a screen reader hears before an unread notification's text.
|
|
134
|
-
*
|
|
135
|
-
* The unread mark is drawn, so a reader who cannot see it hears this word in
|
|
136
|
-
* its place. Translate it with the rest of your interface.
|
|
137
|
-
*/
|
|
138
|
-
unreadLabel: string;
|
|
139
141
|
/**
|
|
140
142
|
* @internal Read by the list to identify its children across the light DOM,
|
|
141
143
|
* which is cheaper than `instanceof` and is why this is public rather than
|
|
@@ -231,11 +233,37 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
|
|
|
231
233
|
*/
|
|
232
234
|
private renderTime;
|
|
233
235
|
protected render(): TemplateResult;
|
|
236
|
+
/**
|
|
237
|
+
* What a screen reader hears before an unread notification's text.
|
|
238
|
+
*
|
|
239
|
+
* The unread mark is drawn, so a reader who cannot see it hears this word in
|
|
240
|
+
* its place. Translate it with the rest of your interface.
|
|
241
|
+
*/
|
|
242
|
+
unreadLabel: string;
|
|
243
|
+
/**
|
|
244
|
+
* The dismiss control's name: `dismissLabel` with the row's words.
|
|
245
|
+
*
|
|
246
|
+
* **Named for its row**, because a feed of rows each with a control named
|
|
247
|
+
* Dismiss is a list of buttons that says nothing about which notice each one
|
|
248
|
+
* ends. The words are the default slot's, the ones `helix-dismiss` reports,
|
|
249
|
+
* so a row's actions are not read into it. A page's own `dismiss-label` with
|
|
250
|
+
* no `{label}` is followed by them, unless it holds them already, as `Tag`
|
|
251
|
+
* names its remove button. Read from the children, so the verb alone on a
|
|
252
|
+
* server, until the update after.
|
|
253
|
+
*/
|
|
254
|
+
private get dismissName();
|
|
255
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
234
256
|
private readonly handleActions;
|
|
235
257
|
private readonly handleDismiss;
|
|
236
258
|
/**
|
|
237
|
-
* Where focus goes when this row leaves: the next row
|
|
238
|
-
*
|
|
259
|
+
* Where focus goes when this row leaves: the next row, the one before, then
|
|
260
|
+
* the first control left in the feed.
|
|
261
|
+
*
|
|
262
|
+
* **The row itself, and not a control in it**, because the row is what a
|
|
263
|
+
* reader moves through the feed by, and every row takes focus. Sent to the
|
|
264
|
+
* next dismiss control, focus passed over each row with no controls between,
|
|
265
|
+
* and to Load more where no row after had one. Focused, the row becomes the
|
|
266
|
+
* feed's stop, as one reached with the arrows does.
|
|
239
267
|
*/
|
|
240
268
|
private neighbor;
|
|
241
269
|
}
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { customElement, deepActiveElement, deepContains, focusableElements, HelixElement, HelixReadEvent, MixinDismissible, requestChange, slotFilled, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, deepActiveElement, deepContains, focusableElements, formatWords, HelixElement, HelixReadEvent, MixinDismissible, requestChange, slotFilled, slottedWords, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { x } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
@@ -16,13 +16,14 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
16
16
|
super(...arguments);
|
|
17
17
|
this.at = '';
|
|
18
18
|
this.defaultRead = false;
|
|
19
|
+
this.dismissLabel = 'Dismiss {label}';
|
|
19
20
|
this.locale = '';
|
|
20
21
|
this.position = 0;
|
|
21
22
|
this.setSize = 0;
|
|
22
23
|
this.stop = true;
|
|
23
|
-
this.unreadLabel = 'Unread';
|
|
24
24
|
this.isNotification = true;
|
|
25
25
|
this.readState = undefined;
|
|
26
|
+
this.unreadLabel = 'Unread';
|
|
26
27
|
this.handleActions = () => {
|
|
27
28
|
this.markActions();
|
|
28
29
|
};
|
|
@@ -30,8 +31,14 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
30
31
|
event.stopPropagation();
|
|
31
32
|
if (this.dismiss()) {
|
|
32
33
|
const next = deepContains(this, deepActiveElement()) ? this.neighbor() : null;
|
|
34
|
+
const holder = this.parentElement;
|
|
33
35
|
this.remove();
|
|
34
36
|
next?.focus();
|
|
37
|
+
if (next && next.getRootNode().activeElement !== next) {
|
|
38
|
+
void Promise.resolve()
|
|
39
|
+
.then(() => holder?.updateComplete)
|
|
40
|
+
.then(() => next.focus());
|
|
41
|
+
}
|
|
35
42
|
}
|
|
36
43
|
};
|
|
37
44
|
}
|
|
@@ -82,7 +89,7 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
82
89
|
return html `<button
|
|
83
90
|
part="dismiss"
|
|
84
91
|
type="button"
|
|
85
|
-
aria-label=${this.
|
|
92
|
+
aria-label=${this.dismissName}
|
|
86
93
|
@click=${this.handleDismiss}
|
|
87
94
|
>
|
|
88
95
|
<slot name="dismiss">
|
|
@@ -132,6 +139,18 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
132
139
|
</div>
|
|
133
140
|
</div>`;
|
|
134
141
|
}
|
|
142
|
+
get dismissName() {
|
|
143
|
+
const { dismissLabel } = this;
|
|
144
|
+
const label = this.childrenKnown ? slottedWords(this, ['']) : '';
|
|
145
|
+
if (dismissLabel.includes('{label}')) {
|
|
146
|
+
return formatWords(this, 'dismissLabel', { label }).trim();
|
|
147
|
+
}
|
|
148
|
+
return label === '' || dismissLabel.includes(label) ? dismissLabel : `${dismissLabel} ${label}`;
|
|
149
|
+
}
|
|
150
|
+
firstUpdated(changed) {
|
|
151
|
+
super.firstUpdated(changed);
|
|
152
|
+
this.renderRoot.addEventListener('slotchange', () => this.requestUpdate());
|
|
153
|
+
}
|
|
135
154
|
neighbor() {
|
|
136
155
|
let feed = this.parentElement;
|
|
137
156
|
while (feed && !('markEveryoneRead' in feed)) {
|
|
@@ -141,9 +160,16 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
141
160
|
? feed.items
|
|
142
161
|
: [...(this.parentElement?.children ?? [])].filter((one) => 'isNotification' in one);
|
|
143
162
|
const at = rows.indexOf(this);
|
|
144
|
-
const
|
|
163
|
+
const group = this.parentElement;
|
|
164
|
+
const promoted = group && 'isNotificationGroup' in group && this.assignedSlot !== null
|
|
165
|
+
? [...group.children].find((one) => one !== this && 'isNotification' in one)
|
|
166
|
+
: undefined;
|
|
167
|
+
const drawn = (row) => !('isNotificationGroup' in (row.parentElement ?? {})) ||
|
|
168
|
+
row.assignedSlot !== null ||
|
|
169
|
+
row === promoted;
|
|
170
|
+
const order = [...rows.slice(at + 1), ...rows.slice(0, Math.max(at, 0)).reverse()].filter(drawn);
|
|
145
171
|
for (const row of order) {
|
|
146
|
-
const target = row.shadowRoot?.querySelector('[part="
|
|
172
|
+
const target = row.shadowRoot?.querySelector('[part="item"]');
|
|
147
173
|
if (target) {
|
|
148
174
|
return target;
|
|
149
175
|
}
|
|
@@ -162,6 +188,10 @@ __decorate([
|
|
|
162
188
|
__decorate([
|
|
163
189
|
property({ attribute: 'default-read', type: Boolean })
|
|
164
190
|
], HelixHeadlessNotification.prototype, "defaultRead", void 0);
|
|
191
|
+
__decorate([
|
|
192
|
+
words,
|
|
193
|
+
property({ attribute: 'dismiss-label' })
|
|
194
|
+
], HelixHeadlessNotification.prototype, "dismissLabel", void 0);
|
|
165
195
|
__decorate([
|
|
166
196
|
property()
|
|
167
197
|
], HelixHeadlessNotification.prototype, "locale", void 0);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HelixHeadlessOption } from './root/option-root.js';
|
|
2
|
+
/**
|
|
3
|
+
* Whether an element is an option, by its shape.
|
|
4
|
+
*
|
|
5
|
+
* **Not by `instanceof`.** A tag is defined once, by whichever copy of this
|
|
6
|
+
* class registered it first, and a second copy in the same document tests
|
|
7
|
+
* against a class no element on the page has: every option failed it, and a
|
|
8
|
+
* select said "Choose" over its value. Two copies are a consumer with a
|
|
9
|
+
* duplicated dependency, or the browser specs, where each family's bundle
|
|
10
|
+
* carries its own and they share one page. An option nobody registered has no
|
|
11
|
+
* such shape, and is no option until its entry is imported.
|
|
12
|
+
*/
|
|
13
|
+
export declare const isOption: (element: Element) => element is HelixHeadlessOption;
|
|
14
|
+
/**
|
|
15
|
+
* The options among some elements, reaching one level into a group.
|
|
16
|
+
*
|
|
17
|
+
* What a control holding options reads its words from, off its own children
|
|
18
|
+
* rather than off the listbox they are slotted into: the slot exists only once a
|
|
19
|
+
* render has made it, so the listbox has none on a first render, and on a page a
|
|
20
|
+
* server rendered the `slotchange` that would correct it fires before anything
|
|
21
|
+
* listens.
|
|
22
|
+
*/
|
|
23
|
+
export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadlessOption[];
|
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import { HelixElement, type LinkModifiers } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
-
|
|
4
|
-
* The options among some elements, reaching one level into a group.
|
|
5
|
-
*
|
|
6
|
-
* What a control holding options reads its words from, off its own children
|
|
7
|
-
* rather than off the listbox they are slotted into: the slot exists only once a
|
|
8
|
-
* render has made it, so the listbox has none on a first render, and on a page a
|
|
9
|
-
* server rendered the `slotchange` that would correct it fires before anything
|
|
10
|
-
* listens.
|
|
11
|
-
*
|
|
12
|
-
* **An option by its shape, not by `instanceof`.** A tag is defined once, by
|
|
13
|
-
* whichever copy of this class registered it first, and a second copy in the
|
|
14
|
-
* same document tests against a class no element on the page has: every option
|
|
15
|
-
* failed it, and a select said "Choose" over its value. Two copies are a
|
|
16
|
-
* consumer with a duplicated dependency, or the browser specs, where each
|
|
17
|
-
* family's bundle carries its own and they share one page.
|
|
18
|
-
*/
|
|
19
|
-
export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadlessOption[];
|
|
3
|
+
export { isOption, optionsAmong } from '../among.js';
|
|
20
4
|
/**
|
|
21
5
|
* @summary One choice in a listbox.
|
|
22
6
|
*
|
|
@@ -7,10 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { customElement, followLink, HelixElement, HelixSelectEvent, hostAria, slotFilled, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
-
export
|
|
11
|
-
const isOption = (element) => 'text' in element && 'value' in element && 'selected' in element;
|
|
12
|
-
return elements.flatMap((element) => isOption(element) ? [element] : [...element.children].filter(isOption));
|
|
13
|
-
};
|
|
10
|
+
export { isOption, optionsAmong } from '../among.js';
|
|
14
11
|
let HelixHeadlessOption = class HelixHeadlessOption extends HelixElement {
|
|
15
12
|
constructor() {
|
|
16
13
|
super(...arguments);
|
|
@@ -62,9 +62,10 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
|
|
|
62
62
|
*
|
|
63
63
|
* The plot runs left to right in every text direction, as a number line does.
|
|
64
64
|
* The plot, its axes, and the axis names sit in a `plane` part with
|
|
65
|
-
* `dir="ltr"
|
|
66
|
-
*
|
|
67
|
-
*
|
|
65
|
+
* `dir="ltr"`, and the arrow keys follow the drawing: Right moves to the next
|
|
66
|
+
* point on a right-to-left page too. The axis names carry `dir="auto"`, so a
|
|
67
|
+
* name in Arabic or Hebrew reads in its own direction. The summary and the
|
|
68
|
+
* tooltip bubble follow the page's direction.
|
|
68
69
|
*
|
|
69
70
|
* **A tap shows the reading under it**, and keeps it until a tap lands
|
|
70
71
|
* elsewhere. `helix-reading-change` says each move of the reading, by pointer,
|
|
@@ -86,7 +87,7 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
|
|
|
86
87
|
* | Key | What it does |
|
|
87
88
|
* |---|---|
|
|
88
89
|
* | Tab | Moves focus to the plot and shows the first reading. |
|
|
89
|
-
* | Right, Left | Moves to the next or the previous point, left to right across the plot
|
|
90
|
+
* | Right, Left | Moves to the next or the previous point, left to right across the plot, on a right-to-left page too. |
|
|
90
91
|
* | Down, Up | Moves among the series of the point. With one series, moves to the next or the previous point. |
|
|
91
92
|
* | Home, End | Moves to the first or the last point. |
|
|
92
93
|
* | Page Down, Page Up | Moves a tenth of the points on or back. |
|
|
@@ -76,6 +76,7 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
|
|
|
76
76
|
connectedCallback() {
|
|
77
77
|
super.connectedCallback();
|
|
78
78
|
this.pointer.axis = 'none';
|
|
79
|
+
this.pointer.drawnLtr = true;
|
|
79
80
|
this.pointer.onReading = (heard, detail) => this.dispatchEvent(heard === 'press'
|
|
80
81
|
? new HelixReadingPressEvent(detail)
|
|
81
82
|
: new HelixReadingChangeEvent(detail));
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { AriaForwardController, type DataOptionItem, HelixFormElement, PopoverController, UpgradeController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
-
import {
|
|
3
|
+
import type { HelixHeadlessOption } from '../../option/root/option-root.js';
|
|
4
4
|
import '../../listbox/index.js';
|
|
5
|
-
import '../../option/index.js';
|
|
6
5
|
declare const HelixHeadlessSelect_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
|
|
7
6
|
/**
|
|
8
7
|
* @summary A control that opens a list of options to choose from.
|
|
@@ -60,6 +59,18 @@ declare const HelixHeadlessSelect_base: (new (...args: any[]) => import("@fusion
|
|
|
60
59
|
* Set `autocomplete` to let a browser's autofill choose, as a native `<select>`
|
|
61
60
|
* with the same attribute would.
|
|
62
61
|
*
|
|
62
|
+
* **The options are an import of their own.** An option is the Option family's,
|
|
63
|
+
* which the select does not register, so a page that writes one imports it
|
|
64
|
+
* beside the select:
|
|
65
|
+
*
|
|
66
|
+
* ```js
|
|
67
|
+
* import '@fusion.dev/helix/select'
|
|
68
|
+
* import '@fusion.dev/helix/option'
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* In development, a select holding an option nobody registered says so once the
|
|
72
|
+
* page has loaded, with the line to add.
|
|
73
|
+
*
|
|
63
74
|
* @since 0.1.0
|
|
64
75
|
* @status stable
|
|
65
76
|
* @category forms
|