@fusion.dev/helix-headless 0.230.0 → 0.231.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/api.json +1 -1
- package/custom-elements.json +1 -1
- package/dist/data-table/events/before-column-toggle.d.ts +14 -0
- package/dist/data-table/events/before-column-toggle.js +11 -0
- package/dist/data-table/events/column-toggle.d.ts +11 -0
- package/dist/data-table/events/column-toggle.js +11 -0
- package/dist/data-table/events/details.d.ts +7 -0
- package/dist/data-table/events/index.d.ts +6 -2
- package/dist/data-table/events/index.js +5 -1
- package/dist/data-table/root/data-table-root.d.ts +102 -0
- package/dist/data-table/root/data-table-root.js +183 -3
- package/dist/element-events.generated.js +2 -0
- package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +9 -9
- package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.d.ts +1 -0
- package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.js +3 -2
- package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.d.ts +1 -0
- package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.js +3 -2
- package/dist/spatial-scatter-chart/view.d.ts +30 -1
- package/dist/spatial-scatter-chart/view.js +56 -9
- package/dist/spatial-surface-chart/root/spatial-surface-chart-root.d.ts +1 -0
- package/dist/spatial-surface-chart/root/spatial-surface-chart-root.js +3 -2
- package/dist/spatial-treemap/root/spatial-treemap-root.d.ts +1 -0
- package/dist/spatial-treemap/root/spatial-treemap-root.js +3 -2
- package/dist/table/column/table-column.d.ts +23 -0
- package/dist/table/column/table-column.js +45 -6
- package/examples.json +1 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +139 -1
- package/types/vue.d.ts +68 -1
- package/types/words.d.ts +43 -0
|
@@ -4,7 +4,7 @@ import { html, nothing, svg } from 'lit';
|
|
|
4
4
|
import { property, state } from 'lit/decorators.js';
|
|
5
5
|
import '../../tooltip/index.js';
|
|
6
6
|
import { fieldOf } from '../field.js';
|
|
7
|
-
import { boundsOf, boxView, DragTurn, drawBox, inBox, nearestTo, SPATIAL_VIEW, turned, } from '../view.js';
|
|
7
|
+
import { boundsOf, boxView, DragTurn, drawBox, inBox, nearestTo, PlotRoomWatch, SPATIAL_VIEW, turned, wordsFitted, } from '../view.js';
|
|
8
8
|
const SERIES = 24;
|
|
9
9
|
const TURNS = {
|
|
10
10
|
ArrowDown: { across: 0, up: -1 },
|
|
@@ -22,6 +22,7 @@ let HelixHeadlessSpatialScatterChart = class HelixHeadlessSpatialScatterChart ex
|
|
|
22
22
|
this.summaryLabel = '{count, plural, one {# point} other {# points}}: {x} across, {y} up, and {z} in depth.';
|
|
23
23
|
this.rendered = false;
|
|
24
24
|
this.camera = SPATIAL_VIEW;
|
|
25
|
+
this.plotRoom = new PlotRoomWatch(this);
|
|
25
26
|
this.drag = new DragTurn((next) => {
|
|
26
27
|
this.camera = next(this.camera);
|
|
27
28
|
}, () => this.requestUpdate());
|
|
@@ -155,7 +156,7 @@ let HelixHeadlessSpatialScatterChart = class HelixHeadlessSpatialScatterChart ex
|
|
|
155
156
|
${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderSonify()}${this.renderTable()}`;
|
|
156
157
|
}
|
|
157
158
|
renderFallback(field, at) {
|
|
158
|
-
const view = boxView(field.axes, this.camera, [], this.noBox);
|
|
159
|
+
const view = wordsFitted(this.plotRoom.room, (words) => boxView(field.axes, this.camera, [], this.noBox, words));
|
|
159
160
|
const places = field.points.map((point, index) => {
|
|
160
161
|
const box = [
|
|
161
162
|
inBox(field.axes.x, point.x),
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* toward the viewer, which is the way back from the floor's back.
|
|
14
14
|
*/
|
|
15
15
|
import { type Camera, type PagePoint, type Projection } from '@fusion.dev/helix-core';
|
|
16
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
16
17
|
import { type SVGTemplateResult } from 'lit';
|
|
17
18
|
import type { PlotAxis } from './field.js';
|
|
18
19
|
/**
|
|
@@ -52,6 +53,8 @@ export interface BoxView {
|
|
|
52
53
|
/** The box's edges and its grid, as one path. */
|
|
53
54
|
grid: string;
|
|
54
55
|
labels: ViewLabel[];
|
|
56
|
+
/** How large the words are, as a share of `TICK_SIZE` and `NAME_SIZE`. */
|
|
57
|
+
words: number;
|
|
55
58
|
/** A place in the box, on the page. */
|
|
56
59
|
on(point: BoxPoint): PagePoint;
|
|
57
60
|
/** How near the camera a place in the box stands: the larger, the nearer. */
|
|
@@ -74,7 +77,33 @@ export declare const boxView: (axes: {
|
|
|
74
77
|
x: PlotAxis;
|
|
75
78
|
y: PlotAxis;
|
|
76
79
|
z: PlotAxis;
|
|
77
|
-
}, camera: Camera, extra?: readonly PagePoint[], bare?: boolean) => BoxView;
|
|
80
|
+
}, camera: Camera, extra?: readonly PagePoint[], bare?: boolean, words?: number) => BoxView;
|
|
81
|
+
/** The drawing's box on the page, and the font size its words are measured against. */
|
|
82
|
+
export interface PlotRoom {
|
|
83
|
+
font: number;
|
|
84
|
+
height: number;
|
|
85
|
+
width: number;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The view whose ticks stand at the box's font size or larger on the page.
|
|
89
|
+
*
|
|
90
|
+
* The drawing scales through its `viewBox`, so a tick written in the box's units
|
|
91
|
+
* shrinks with the box: measured October 2026 at 390 wide, 7.4 pixels on Scatter
|
|
92
|
+
* and Surface, under the 12 of the box's own font. The words widen the frame they
|
|
93
|
+
* are measured on, so the measure runs again on what they widened. Where nothing
|
|
94
|
+
* has measured the box, as on a server, the words stay at their written size.
|
|
95
|
+
*/
|
|
96
|
+
export declare const wordsFitted: (room: PlotRoom | undefined, draw: (words: number) => BoxView) => BoxView;
|
|
97
|
+
/** Keeps a chart told the size and font size of the box its own drawing fills. */
|
|
98
|
+
export declare class PlotRoomWatch implements ReactiveController {
|
|
99
|
+
private readonly host;
|
|
100
|
+
room: PlotRoom | undefined;
|
|
101
|
+
private box;
|
|
102
|
+
private sizes;
|
|
103
|
+
constructor(host: ReactiveControllerHost & HTMLElement);
|
|
104
|
+
hostUpdated(): void;
|
|
105
|
+
hostDisconnected(): void;
|
|
106
|
+
}
|
|
78
107
|
/**
|
|
79
108
|
* The part of the page that holds `points`, `pad` beyond them on every side:
|
|
80
109
|
* the frame of a drawing with no box, which fits its readings alone.
|
|
@@ -11,7 +11,7 @@ const WIDE = 0.58;
|
|
|
11
11
|
export const TICK_SIZE = 0.13;
|
|
12
12
|
export const NAME_SIZE = 0.15;
|
|
13
13
|
const LABEL_GAP = 0.06;
|
|
14
|
-
export const boxView = (axes, camera, extra = [], bare = false) => {
|
|
14
|
+
export const boxView = (axes, camera, extra = [], bare = false, words = 1) => {
|
|
15
15
|
const projection = projectionOf(camera);
|
|
16
16
|
const [ex, ey, ez] = axes.extent ?? [1, 1, 1];
|
|
17
17
|
const on = ([x, y, z]) => projection.at(x * ex, -z * ez, y * ey);
|
|
@@ -45,14 +45,18 @@ export const boxView = (axes, camera, extra = [], bare = false) => {
|
|
|
45
45
|
}
|
|
46
46
|
const text = (axis) => ({
|
|
47
47
|
name: axis.name,
|
|
48
|
-
ticks: axis.ticks.map((tick, index) =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
ticks: axis.ticks.map((tick, index) => {
|
|
49
|
+
const label = axis.labels?.[index];
|
|
50
|
+
return {
|
|
51
|
+
at: inBox(axis, tick),
|
|
52
|
+
category: label !== undefined && !/\d/.test(label),
|
|
53
|
+
text: label ?? tickText(tick),
|
|
54
|
+
};
|
|
55
|
+
}),
|
|
52
56
|
});
|
|
53
57
|
const labels = bare
|
|
54
58
|
? []
|
|
55
|
-
: boxLabels((point) => ({ ...on(point), depth: depth(point) }), { x: text(axes.x), y: text(axes.y), z: text(axes.z) }, { gap: LABEL_GAP, name: NAME_SIZE, tick: TICK_SIZE, wide: WIDE });
|
|
59
|
+
: boxLabels((point) => ({ ...on(point), depth: depth(point) }), { x: text(axes.x), y: text(axes.y), z: text(axes.z) }, { gap: LABEL_GAP * words, name: NAME_SIZE * words, tick: TICK_SIZE * words, wide: WIDE });
|
|
56
60
|
const corners = [];
|
|
57
61
|
for (const x of [-1, 1]) {
|
|
58
62
|
for (const y of [-1, 1]) {
|
|
@@ -62,7 +66,7 @@ export const boxView = (axes, camera, extra = [], bare = false) => {
|
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
for (const one of labels) {
|
|
65
|
-
const size = one.name ? NAME_SIZE : TICK_SIZE;
|
|
69
|
+
const size = (one.name ? NAME_SIZE : TICK_SIZE) * words;
|
|
66
70
|
const half = (one.text.length * size * WIDE) / 2;
|
|
67
71
|
corners.push({ x: one.x - half, y: one.y - size }, { x: one.x + half, y: one.y + size });
|
|
68
72
|
}
|
|
@@ -77,8 +81,51 @@ export const boxView = (axes, camera, extra = [], bare = false) => {
|
|
|
77
81
|
labels,
|
|
78
82
|
on,
|
|
79
83
|
projection,
|
|
84
|
+
words,
|
|
80
85
|
};
|
|
81
86
|
};
|
|
87
|
+
const WORD_PASSES = 4;
|
|
88
|
+
const MOST_WORDS = 3;
|
|
89
|
+
export const wordsFitted = (room, draw) => {
|
|
90
|
+
let view = draw(1);
|
|
91
|
+
if (!room) {
|
|
92
|
+
return view;
|
|
93
|
+
}
|
|
94
|
+
for (let pass = 0; pass < WORD_PASSES; pass += 1) {
|
|
95
|
+
const pixel = 1 / Math.min(room.width / view.bounds.width, room.height / view.bounds.height);
|
|
96
|
+
view = draw(Math.min(MOST_WORDS, Math.max(1, (room.font * pixel) / TICK_SIZE)));
|
|
97
|
+
}
|
|
98
|
+
return view;
|
|
99
|
+
};
|
|
100
|
+
export class PlotRoomWatch {
|
|
101
|
+
constructor(host) {
|
|
102
|
+
this.host = host;
|
|
103
|
+
host.addController(this);
|
|
104
|
+
}
|
|
105
|
+
hostUpdated() {
|
|
106
|
+
const box = this.host.shadowRoot?.querySelector('[part="fallback"]');
|
|
107
|
+
if (!box || box === this.box || typeof ResizeObserver !== 'function') {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.box = box;
|
|
111
|
+
this.sizes?.disconnect();
|
|
112
|
+
this.sizes = new ResizeObserver(([entry]) => {
|
|
113
|
+
const { height, width } = entry?.contentRect ?? { height: 0, width: 0 };
|
|
114
|
+
const font = Number.parseFloat(getComputedStyle(box).fontSize) || 16;
|
|
115
|
+
const was = this.room;
|
|
116
|
+
if (was?.width !== width || was.height !== height || was.font !== font) {
|
|
117
|
+
this.room = height > 0 && width > 0 ? { font, height, width } : undefined;
|
|
118
|
+
this.host.requestUpdate();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
this.sizes.observe(box);
|
|
122
|
+
}
|
|
123
|
+
hostDisconnected() {
|
|
124
|
+
this.sizes?.disconnect();
|
|
125
|
+
this.sizes = undefined;
|
|
126
|
+
this.box = undefined;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
82
129
|
export const boundsOf = (points, pad) => {
|
|
83
130
|
if (points.length === 0) {
|
|
84
131
|
return { height: 2 * pad, width: 2 * pad, x: -pad, y: -pad };
|
|
@@ -103,8 +150,8 @@ export const turned = (camera, across, up) => ({
|
|
|
103
150
|
turn: camera.turn + across * VIEW_STEP,
|
|
104
151
|
});
|
|
105
152
|
export const drawBox = (view) => svg `<path d=${view.grid} part="grid"></path>${view.labels.map((label) => label.name
|
|
106
|
-
? svg `<text dominant-baseline="central" font-size=${NAME_SIZE} part="axis-name" text-anchor="middle" x=${shortNumber(label.x)} y=${shortNumber(label.y)}>${label.text}</text>`
|
|
107
|
-
: svg `<text dominant-baseline="central" font-size=${TICK_SIZE} part="tick" text-anchor="middle" x=${shortNumber(label.x)} y=${shortNumber(label.y)}>${label.text}</text>`)}`;
|
|
153
|
+
? svg `<text dominant-baseline="central" font-size=${NAME_SIZE * view.words} part="axis-name" text-anchor="middle" x=${shortNumber(label.x)} y=${shortNumber(label.y)}>${label.text}</text>`
|
|
154
|
+
: svg `<text dominant-baseline="central" font-size=${TICK_SIZE * view.words} part="tick" text-anchor="middle" x=${shortNumber(label.x)} y=${shortNumber(label.y)}>${label.text}</text>`)}`;
|
|
108
155
|
export const nearestTo = (drawing, event, places, reach = 14) => {
|
|
109
156
|
const matrix = drawing?.getScreenCTM?.();
|
|
110
157
|
if (!matrix) {
|
|
@@ -151,6 +151,7 @@ export declare class HelixHeadlessSpatialSurfaceChart extends HelixHeadlessSpati
|
|
|
151
151
|
/** Where the chart's own drawing is seen from, which a drag or Shift with an arrow turns. */
|
|
152
152
|
private camera;
|
|
153
153
|
/** A drag across the chart's own drawing, which turns its view. */
|
|
154
|
+
private readonly plotRoom;
|
|
154
155
|
private readonly drag;
|
|
155
156
|
/** Each reading where the chart's own drawing put it, which the pointer finds. */
|
|
156
157
|
private placed;
|
|
@@ -3,7 +3,7 @@ import { AriaForwardController, CHART_PLOT, customElement, drawTooltip, formatWo
|
|
|
3
3
|
import { html, nothing, svg, } from 'lit';
|
|
4
4
|
import { property, state } from 'lit/decorators.js';
|
|
5
5
|
import '../../tooltip/index.js';
|
|
6
|
-
import { boundsOf, boxView, DragTurn, drawBox, inBox, nearestTo, SPATIAL_VIEW, shape, turned, } from '../../spatial-scatter-chart/view.js';
|
|
6
|
+
import { boundsOf, boxView, DragTurn, drawBox, inBox, nearestTo, PlotRoomWatch, SPATIAL_VIEW, shape, turned, wordsFitted, } from '../../spatial-scatter-chart/view.js';
|
|
7
7
|
import { levelOf, surfaceOf } from '../grid.js';
|
|
8
8
|
const LIGHT = (() => {
|
|
9
9
|
const [x, y, z] = [-0.45, 1, 0.35];
|
|
@@ -27,6 +27,7 @@ let HelixHeadlessSpatialSurfaceChart = class HelixHeadlessSpatialSurfaceChart ex
|
|
|
27
27
|
this.summaryLabel = '{count, plural, one {# reading} other {# readings}} of {y} over {x} and {z}, from {low} to {high}.';
|
|
28
28
|
this.rendered = false;
|
|
29
29
|
this.camera = SPATIAL_VIEW;
|
|
30
|
+
this.plotRoom = new PlotRoomWatch(this);
|
|
30
31
|
this.drag = new DragTurn((next) => {
|
|
31
32
|
this.camera = next(this.camera);
|
|
32
33
|
}, () => this.requestUpdate());
|
|
@@ -162,7 +163,7 @@ let HelixHeadlessSpatialSurfaceChart = class HelixHeadlessSpatialSurfaceChart ex
|
|
|
162
163
|
${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderSonify()}${this.renderTable()}`;
|
|
163
164
|
}
|
|
164
165
|
renderFallback(field, at) {
|
|
165
|
-
const view = boxView(field.axes, this.camera, [], this.noBox);
|
|
166
|
+
const view = wordsFitted(this.plotRoom.room, (words) => boxView(field.axes, this.camera, [], this.noBox, words));
|
|
166
167
|
const depths = field.depths.length;
|
|
167
168
|
const boxed = field.cells.map((cell) => cell.value === undefined
|
|
168
169
|
? undefined
|
|
@@ -143,6 +143,7 @@ export declare class HelixHeadlessSpatialTreemap extends HelixHeadlessSpatialTre
|
|
|
143
143
|
/** Where the chart's own drawing is seen from, which a drag or Shift with an arrow turns. */
|
|
144
144
|
private camera;
|
|
145
145
|
/** A drag across the chart's own drawing, which turns its view. */
|
|
146
|
+
private readonly plotRoom;
|
|
146
147
|
private readonly drag;
|
|
147
148
|
/** Where the renderer draws the reading, which the tooltip points at. */
|
|
148
149
|
private place;
|
|
@@ -3,7 +3,7 @@ import { AriaForwardController, CHART_PLOT, customElement, drawTooltip, formatWo
|
|
|
3
3
|
import { html, nothing, svg, } from 'lit';
|
|
4
4
|
import { property, state } from 'lit/decorators.js';
|
|
5
5
|
import '../../tooltip/index.js';
|
|
6
|
-
import { boundsOf, boxView, DragTurn, drawBox, SPATIAL_VIEW, turned, } from '../../spatial-scatter-chart/view.js';
|
|
6
|
+
import { boundsOf, boxView, DragTurn, drawBox, PlotRoomWatch, SPATIAL_VIEW, turned, wordsFitted, } from '../../spatial-scatter-chart/view.js';
|
|
7
7
|
import { blocksDrawn, paintOrder, treemapOf, } from '../blocks.js';
|
|
8
8
|
const TURNS = {
|
|
9
9
|
ArrowDown: { across: 0, up: -1 },
|
|
@@ -21,6 +21,7 @@ let HelixHeadlessSpatialTreemap = class HelixHeadlessSpatialTreemap extends Mixi
|
|
|
21
21
|
this.summaryLabel = '{count, plural, one {# item} other {# items}} in {groups, plural, one {# group} other {# groups}}, sized from {low} to {high}.';
|
|
22
22
|
this.rendered = false;
|
|
23
23
|
this.camera = SPATIAL_VIEW;
|
|
24
|
+
this.plotRoom = new PlotRoomWatch(this);
|
|
24
25
|
this.drag = new DragTurn((next) => {
|
|
25
26
|
this.camera = next(this.camera);
|
|
26
27
|
}, () => this.requestUpdate());
|
|
@@ -155,7 +156,7 @@ let HelixHeadlessSpatialTreemap = class HelixHeadlessSpatialTreemap extends Mixi
|
|
|
155
156
|
${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderSonify()}${this.renderTable()}`;
|
|
156
157
|
}
|
|
157
158
|
renderFallback(field, at) {
|
|
158
|
-
const view = boxView(field.axes, this.camera, [], this.noBox);
|
|
159
|
+
const view = wordsFitted(this.plotRoom.room, (words) => boxView(field.axes, this.camera, [], this.noBox, words));
|
|
159
160
|
const blocks = blocksDrawn(field, view.projection);
|
|
160
161
|
const active = blocks[at];
|
|
161
162
|
const bounds = this.drag.frame(this.noBox
|
|
@@ -61,6 +61,10 @@ export { edgeOf } from '../edge.js';
|
|
|
61
61
|
* current direction takes the heading's ink, so you can see which columns sort
|
|
62
62
|
* and which way the table is sorted.
|
|
63
63
|
*
|
|
64
|
+
* `menu` adds a small button after the chevrons, in a `DataTable`. The button
|
|
65
|
+
* opens a menu of what you can do with the column, and a press anywhere else on
|
|
66
|
+
* the heading still sorts. `DataTable` describes the rows.
|
|
67
|
+
*
|
|
64
68
|
* @since 0.1.0
|
|
65
69
|
* @status stable
|
|
66
70
|
* @aliases Column header, Th
|
|
@@ -74,12 +78,15 @@ export { edgeOf } from '../edge.js';
|
|
|
74
78
|
* @csspart column - The heading cell.
|
|
75
79
|
* @csspart grip - The handle for moving the column, when the table allows it.
|
|
76
80
|
* @csspart grip-mark - The icon on the grip.
|
|
81
|
+
* @csspart menu-button - The button that opens the column's menu, with `menu` in a `DataTable`.
|
|
82
|
+
* @csspart menu-mark - The icon on the menu button.
|
|
77
83
|
* @csspart resizer - The edge that is dragged to change the width, when the column is resizable.
|
|
78
84
|
* @csspart sort - The button that a sortable heading renders as.
|
|
79
85
|
* @csspart sort-down - The lower chevron of `sort-mark`, which shows a descending sort.
|
|
80
86
|
* @csspart sort-mark - The two chevrons on a sortable heading. To remove them, set `display: none` on this part.
|
|
81
87
|
* @csspart sort-up - The upper chevron of `sort-mark`, which shows an ascending sort.
|
|
82
88
|
*
|
|
89
|
+
* @cssstate has-menu - While the heading draws its menu button.
|
|
83
90
|
* @cssstate sorted-ascending - While the table is sorted by this column, smallest first.
|
|
84
91
|
* @cssstate sorted-descending - While the table is sorted by this column, largest first.
|
|
85
92
|
*/
|
|
@@ -126,6 +133,20 @@ export declare class HelixHeadlessTableColumn extends HelixElement {
|
|
|
126
133
|
* such a column is never sortable however it is marked.
|
|
127
134
|
*/
|
|
128
135
|
field: string;
|
|
136
|
+
/**
|
|
137
|
+
* Draws a button in the heading that opens a menu of what you can do with the
|
|
138
|
+
* column, in a `DataTable`.
|
|
139
|
+
*
|
|
140
|
+
* The menu offers sorting where the column is `sortable`, moving where the
|
|
141
|
+
* table is `reorderable`, and hiding. It needs a `field`, and a `Table` ignores
|
|
142
|
+
* it.
|
|
143
|
+
*/
|
|
144
|
+
menu: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* The accessible name of the menu button. `{name}` is the heading text, and
|
|
147
|
+
* `{named}` is `no` for a column without one.
|
|
148
|
+
*/
|
|
149
|
+
menuLabel: string;
|
|
129
150
|
/**
|
|
130
151
|
* The accessible name of the grip that moves the column. `{name}` is the
|
|
131
152
|
* heading text, and `{named}` is `no` for a column without one, so the
|
|
@@ -281,6 +302,8 @@ export declare class HelixHeadlessTableColumn extends HelixElement {
|
|
|
281
302
|
*/
|
|
282
303
|
order(row: Readonly<Record<string, unknown>>): unknown;
|
|
283
304
|
protected willUpdate(changed: PropertyValues): void;
|
|
305
|
+
/** Whether the heading holds a menu button: `menu`, in a table that draws the menu. */
|
|
306
|
+
get drawsMenu(): boolean;
|
|
284
307
|
/** The words of the heading, for naming the controls on it. */
|
|
285
308
|
private get name();
|
|
286
309
|
connectedCallback(): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var HelixHeadlessTableColumn_1;
|
|
2
2
|
import { __decorate } from "tslib";
|
|
3
3
|
import { customElement, formatWords, HelixBeforeResizeEvent, HelixElement, HelixResizedEvent, hostAria, inlineStep, internalsOf, isRtl, requestChange, valueAt, words, } from '@fusion.dev/helix-core';
|
|
4
|
-
import { dotsSixVertical } from '@fusion.dev/helix-icons';
|
|
4
|
+
import { dotsSixVertical, dotsThree } from '@fusion.dev/helix-icons';
|
|
5
5
|
import { html, nothing } from 'lit';
|
|
6
6
|
import { property, state } from 'lit/decorators.js';
|
|
7
7
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
@@ -12,6 +12,8 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
|
|
|
12
12
|
this.cell = undefined;
|
|
13
13
|
this.compare = undefined;
|
|
14
14
|
this.field = '';
|
|
15
|
+
this.menu = false;
|
|
16
|
+
this.menuLabel = '{named, select, no {Column menu} other {{name} column menu}}';
|
|
15
17
|
this.moveLabel = '{named, select, no {Move column} other {Move {name}}}';
|
|
16
18
|
this.resizable = false;
|
|
17
19
|
this.resizeLabel = '{named, select, no {Resize column} other {Resize {name}}}';
|
|
@@ -25,6 +27,8 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
|
|
|
25
27
|
this.gridCell = false;
|
|
26
28
|
this.tabStop = false;
|
|
27
29
|
this.sortWords = '';
|
|
30
|
+
this.menuDrawer = undefined;
|
|
31
|
+
this.menuFacts = '';
|
|
28
32
|
this.drawn = 0;
|
|
29
33
|
this.handleResizeStart = (event) => {
|
|
30
34
|
if (event.button !== 0) {
|
|
@@ -84,13 +88,14 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
|
|
|
84
88
|
}
|
|
85
89
|
willUpdate(changed) {
|
|
86
90
|
super.willUpdate(changed);
|
|
87
|
-
const named = this.movable || this.resizable;
|
|
91
|
+
const named = this.movable || this.resizable || this.drawsMenu;
|
|
88
92
|
const host = !this.gridCell;
|
|
89
93
|
hostAria(internalsOf(this), {
|
|
90
94
|
label: host && named ? this.name : '',
|
|
91
95
|
role: host ? 'columnheader' : '',
|
|
92
96
|
});
|
|
93
97
|
internalsOf(this).ariaSort = host && this.canSort ? (this.sorted ?? 'none') : null;
|
|
98
|
+
this.toggleState('has-menu', this.drawsMenu);
|
|
94
99
|
this.toggleState('sorted-ascending', this.sorted === 'ascending');
|
|
95
100
|
this.toggleState('sorted-descending', this.sorted === 'descending');
|
|
96
101
|
}
|
|
@@ -98,11 +103,19 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
|
|
|
98
103
|
return this.partNamed('column');
|
|
99
104
|
}
|
|
100
105
|
get controls() {
|
|
101
|
-
return ['grip', 'sort', 'resizer'].flatMap((name) => {
|
|
106
|
+
return ['grip', 'sort', 'menu-button', 'resizer'].flatMap((name) => {
|
|
102
107
|
const control = this.partNamed(name);
|
|
103
108
|
return control ? [control] : [];
|
|
104
109
|
});
|
|
105
110
|
}
|
|
111
|
+
openMenu() {
|
|
112
|
+
const button = this.partNamed('menu-button');
|
|
113
|
+
button?.focus();
|
|
114
|
+
button?.click();
|
|
115
|
+
}
|
|
116
|
+
get drawsMenu() {
|
|
117
|
+
return this.menu && this.menuDrawer !== undefined;
|
|
118
|
+
}
|
|
106
119
|
get name() {
|
|
107
120
|
return this.textContent?.trim() ?? '';
|
|
108
121
|
}
|
|
@@ -137,7 +150,7 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
|
|
|
137
150
|
}
|
|
138
151
|
updated(changed) {
|
|
139
152
|
super.updated(changed);
|
|
140
|
-
if (changed.has('textAlign') || changed.has('width')) {
|
|
153
|
+
if (changed.has('menu') || changed.has('textAlign') || changed.has('width')) {
|
|
141
154
|
this.tellHolder();
|
|
142
155
|
}
|
|
143
156
|
}
|
|
@@ -169,12 +182,13 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
|
|
|
169
182
|
const grid = this.gridCell;
|
|
170
183
|
const control = grid ? '-1' : nothing;
|
|
171
184
|
const width = Math.min(4096, Math.max(HelixHeadlessTableColumn_1.NARROWEST, this.drawn));
|
|
185
|
+
const hasMenu = this.drawsMenu;
|
|
172
186
|
return html `<span
|
|
173
187
|
part="column"
|
|
174
188
|
role=${grid ? 'columnheader' : nothing}
|
|
175
189
|
tabindex=${grid ? (this.tabStop ? '0' : '-1') : nothing}
|
|
176
190
|
aria-sort=${grid && this.canSort ? (this.sorted ?? 'none') : nothing}
|
|
177
|
-
aria-label=${grid && (this.movable || this.resizable) && name ? name : nothing}
|
|
191
|
+
aria-label=${grid && (this.movable || this.resizable || hasMenu) && name ? name : nothing}
|
|
178
192
|
>${this.movable
|
|
179
193
|
? html `<button part="grip" type="button" tabindex=${control} aria-label=${formatWords(this, 'moveLabel', named)}>
|
|
180
194
|
<svg aria-hidden="true" fill="currentColor" part="grip-mark" viewBox=${dotsSixVertical.viewBox}>
|
|
@@ -186,7 +200,19 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
|
|
|
186
200
|
<path d="M5.5 7.2 9 3.7l3.5 3.5" part="sort-up" />
|
|
187
201
|
<path d="M5.5 10.8 9 14.3l3.5-3.5" part="sort-down" />
|
|
188
202
|
</svg></button>`
|
|
189
|
-
: html `<slot></slot>`}${
|
|
203
|
+
: html `<slot></slot>`}${hasMenu
|
|
204
|
+
? this.menuDrawer?.(this, html `<button
|
|
205
|
+
part="menu-button"
|
|
206
|
+
slot="trigger"
|
|
207
|
+
type="button"
|
|
208
|
+
tabindex=${control}
|
|
209
|
+
aria-label=${formatWords(this, 'menuLabel', named)}
|
|
210
|
+
>
|
|
211
|
+
<svg aria-hidden="true" fill="currentColor" part="menu-mark" viewBox=${dotsThree.viewBox}>
|
|
212
|
+
${unsafeSVG(dotsThree.content)}
|
|
213
|
+
</svg>
|
|
214
|
+
</button>`)
|
|
215
|
+
: nothing}${this.resizable
|
|
190
216
|
? html `<span
|
|
191
217
|
part="resizer"
|
|
192
218
|
role="separator"
|
|
@@ -215,6 +241,13 @@ __decorate([
|
|
|
215
241
|
__decorate([
|
|
216
242
|
property()
|
|
217
243
|
], HelixHeadlessTableColumn.prototype, "field", void 0);
|
|
244
|
+
__decorate([
|
|
245
|
+
property({ type: Boolean })
|
|
246
|
+
], HelixHeadlessTableColumn.prototype, "menu", void 0);
|
|
247
|
+
__decorate([
|
|
248
|
+
words,
|
|
249
|
+
property({ attribute: 'menu-label' })
|
|
250
|
+
], HelixHeadlessTableColumn.prototype, "menuLabel", void 0);
|
|
218
251
|
__decorate([
|
|
219
252
|
words,
|
|
220
253
|
property({ attribute: 'move-label' })
|
|
@@ -257,6 +290,12 @@ __decorate([
|
|
|
257
290
|
__decorate([
|
|
258
291
|
state()
|
|
259
292
|
], HelixHeadlessTableColumn.prototype, "sortWords", void 0);
|
|
293
|
+
__decorate([
|
|
294
|
+
state()
|
|
295
|
+
], HelixHeadlessTableColumn.prototype, "menuDrawer", void 0);
|
|
296
|
+
__decorate([
|
|
297
|
+
state()
|
|
298
|
+
], HelixHeadlessTableColumn.prototype, "menuFacts", void 0);
|
|
260
299
|
__decorate([
|
|
261
300
|
state()
|
|
262
301
|
], HelixHeadlessTableColumn.prototype, "drawn", void 0);
|