@unofficialbox/box-open-elements 0.5.0 → 0.6.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 +3 -3
- package/dist/components/collections/table.d.ts +57 -3
- package/dist/components/collections/table.js +339 -20
- package/dist/components/feedback/due-badge.d.ts +38 -0
- package/dist/components/feedback/due-badge.js +168 -0
- package/dist/components/feedback/due-types.d.ts +32 -0
- package/dist/components/feedback/due-types.js +91 -0
- package/dist/components/feedback/progress-steps.d.ts +51 -5
- package/dist/components/feedback/progress-steps.js +200 -22
- package/dist/components/feedback/stage-path.d.ts +44 -0
- package/dist/components/feedback/stage-path.js +235 -0
- package/dist/components/forms/select.d.ts +21 -0
- package/dist/components/forms/select.js +100 -3
- package/dist/components/forms/text-field.d.ts +21 -0
- package/dist/components/forms/text-field.js +100 -1
- package/dist/components/layout/nav-sidebar.d.ts +14 -0
- package/dist/components/layout/nav-sidebar.js +49 -0
- package/dist/components/layout/split-view.d.ts +14 -0
- package/dist/components/layout/split-view.js +80 -1
- package/dist/components/overlays/command-palette.d.ts +62 -0
- package/dist/components/overlays/command-palette.js +537 -0
- package/dist/components/overlays/command-types.d.ts +90 -0
- package/dist/components/overlays/command-types.js +259 -0
- package/dist/components/overlays/drawer.d.ts +18 -0
- package/dist/components/overlays/drawer.js +135 -9
- package/dist/components/overlays/shortcuts-overlay.d.ts +48 -0
- package/dist/components/overlays/shortcuts-overlay.js +385 -0
- package/dist/components/overlays/tooltip.js +1 -1
- package/dist/element-maps.d.ts +292 -0
- package/dist/element-maps.js +1 -0
- package/dist/entries/activity-density.d.ts +1 -0
- package/dist/entries/activity-density.js +1 -0
- package/dist/entries/agent-chat.d.ts +1 -0
- package/dist/entries/agent-chat.js +1 -0
- package/dist/entries/audit-log.d.ts +1 -0
- package/dist/entries/audit-log.js +1 -0
- package/dist/entries/command-palette.d.ts +1 -0
- package/dist/entries/command-palette.js +1 -0
- package/dist/entries/compare-view.d.ts +1 -0
- package/dist/entries/compare-view.js +1 -0
- package/dist/entries/diff-viewer.d.ts +1 -0
- package/dist/entries/diff-viewer.js +1 -0
- package/dist/entries/due-badge.d.ts +1 -0
- package/dist/entries/due-badge.js +1 -0
- package/dist/entries/form-wizard.d.ts +1 -0
- package/dist/entries/form-wizard.js +1 -0
- package/dist/entries/lineage-graph.d.ts +1 -0
- package/dist/entries/lineage-graph.js +1 -0
- package/dist/entries/notification-bell.d.ts +1 -0
- package/dist/entries/notification-bell.js +1 -0
- package/dist/entries/notification-inbox.d.ts +1 -0
- package/dist/entries/notification-inbox.js +1 -0
- package/dist/entries/provenance-strip.d.ts +1 -0
- package/dist/entries/provenance-strip.js +1 -0
- package/dist/entries/run-trace.d.ts +1 -0
- package/dist/entries/run-trace.js +1 -0
- package/dist/entries/shortcuts-overlay.d.ts +1 -0
- package/dist/entries/shortcuts-overlay.js +1 -0
- package/dist/entries/signature-ceremony.d.ts +1 -0
- package/dist/entries/signature-ceremony.js +1 -0
- package/dist/entries/stage-path.d.ts +1 -0
- package/dist/entries/stage-path.js +1 -0
- package/dist/entries/timeline.d.ts +1 -0
- package/dist/entries/timeline.js +1 -0
- package/dist/entries/version-graph.d.ts +1 -0
- package/dist/entries/version-graph.js +1 -0
- package/dist/entries/version-list.d.ts +1 -0
- package/dist/entries/version-list.js +1 -0
- package/dist/entries/wizard-summary.d.ts +1 -0
- package/dist/entries/wizard-summary.js +1 -0
- package/dist/entries/work-queue.d.ts +1 -0
- package/dist/entries/work-queue.js +1 -0
- package/dist/entries/workload-board.d.ts +1 -0
- package/dist/entries/workload-board.js +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/patterns/agent-chat/agent-chat.d.ts +75 -0
- package/dist/patterns/agent-chat/agent-chat.js +820 -0
- package/dist/patterns/agent-chat/controller.d.ts +27 -0
- package/dist/patterns/agent-chat/controller.js +182 -0
- package/dist/patterns/agent-chat/index.d.ts +3 -0
- package/dist/patterns/agent-chat/index.js +6 -0
- package/dist/patterns/agent-chat/types.d.ts +109 -0
- package/dist/patterns/agent-chat/types.js +1 -0
- package/dist/patterns/audit/activity-density.d.ts +68 -0
- package/dist/patterns/audit/activity-density.js +428 -0
- package/dist/patterns/audit/audit-log.d.ts +81 -0
- package/dist/patterns/audit/audit-log.js +794 -0
- package/dist/patterns/audit/index.d.ts +3 -0
- package/dist/patterns/audit/index.js +7 -0
- package/dist/patterns/audit/shared.d.ts +10 -0
- package/dist/patterns/audit/shared.js +50 -0
- package/dist/patterns/audit/types.d.ts +143 -0
- package/dist/patterns/audit/types.js +327 -0
- package/dist/patterns/content-explorer/actions/controller.js +12 -2
- package/dist/patterns/content-explorer/box-transport.js +117 -25
- package/dist/patterns/content-explorer/content-explorer.d.ts +13 -1
- package/dist/patterns/content-explorer/content-explorer.js +36 -3
- package/dist/patterns/content-explorer/contracts.d.ts +24 -1
- package/dist/patterns/content-explorer/contracts.js +68 -11
- package/dist/patterns/content-explorer/controller.d.ts +16 -1
- package/dist/patterns/content-explorer/controller.js +130 -4
- package/dist/patterns/content-explorer/navigation/controller.js +11 -9
- package/dist/patterns/content-explorer/types.d.ts +58 -1
- package/dist/patterns/content-picker/content-picker.d.ts +71 -0
- package/dist/patterns/content-picker/content-picker.js +747 -0
- package/dist/patterns/content-picker/controller.d.ts +38 -0
- package/dist/patterns/content-picker/controller.js +141 -0
- package/dist/patterns/content-picker/index.d.ts +3 -0
- package/dist/patterns/content-picker/index.js +6 -0
- package/dist/patterns/content-picker/types.d.ts +52 -0
- package/dist/patterns/content-picker/types.js +26 -0
- package/dist/patterns/content-sidebar/content-sidebar.d.ts +43 -0
- package/dist/patterns/content-sidebar/content-sidebar.js +293 -0
- package/dist/patterns/content-sidebar/index.d.ts +2 -0
- package/dist/patterns/content-sidebar/index.js +4 -0
- package/dist/patterns/content-sidebar/types.d.ts +18 -0
- package/dist/patterns/content-sidebar/types.js +20 -0
- package/dist/patterns/content-uploader/box-transport.d.ts +14 -0
- package/dist/patterns/content-uploader/box-transport.js +54 -0
- package/dist/patterns/content-uploader/content-uploader.d.ts +62 -0
- package/dist/patterns/content-uploader/content-uploader.js +559 -0
- package/dist/patterns/content-uploader/controller.d.ts +39 -0
- package/dist/patterns/content-uploader/controller.js +227 -0
- package/dist/patterns/content-uploader/index.d.ts +4 -0
- package/dist/patterns/content-uploader/index.js +8 -0
- package/dist/patterns/content-uploader/types.d.ts +120 -0
- package/dist/patterns/content-uploader/types.js +38 -0
- package/dist/patterns/diff/compare-view.d.ts +47 -0
- package/dist/patterns/diff/compare-view.js +276 -0
- package/dist/patterns/diff/diff-viewer.d.ts +42 -0
- package/dist/patterns/diff/diff-viewer.js +434 -0
- package/dist/patterns/diff/engine.d.ts +22 -0
- package/dist/patterns/diff/engine.js +276 -0
- package/dist/patterns/diff/index.d.ts +4 -0
- package/dist/patterns/diff/index.js +8 -0
- package/dist/patterns/diff/types.d.ts +69 -0
- package/dist/patterns/diff/types.js +24 -0
- package/dist/patterns/form-wizard/controller.d.ts +39 -0
- package/dist/patterns/form-wizard/controller.js +157 -0
- package/dist/patterns/form-wizard/form-wizard.d.ts +63 -0
- package/dist/patterns/form-wizard/form-wizard.js +468 -0
- package/dist/patterns/form-wizard/index.d.ts +4 -0
- package/dist/patterns/form-wizard/index.js +8 -0
- package/dist/patterns/form-wizard/types.d.ts +109 -0
- package/dist/patterns/form-wizard/types.js +83 -0
- package/dist/patterns/form-wizard/wizard-summary.d.ts +62 -0
- package/dist/patterns/form-wizard/wizard-summary.js +324 -0
- package/dist/patterns/internal/safe-href.d.ts +17 -0
- package/dist/patterns/internal/safe-href.js +17 -0
- package/dist/patterns/lineage/index.d.ts +3 -0
- package/dist/patterns/lineage/index.js +6 -0
- package/dist/patterns/lineage/lineage-graph.d.ts +30 -0
- package/dist/patterns/lineage/lineage-graph.js +434 -0
- package/dist/patterns/lineage/provenance-strip.d.ts +22 -0
- package/dist/patterns/lineage/provenance-strip.js +194 -0
- package/dist/patterns/lineage/types.d.ts +53 -0
- package/dist/patterns/lineage/types.js +49 -0
- package/dist/patterns/notifications/index.d.ts +3 -0
- package/dist/patterns/notifications/index.js +6 -0
- package/dist/patterns/notifications/notification-bell.d.ts +38 -0
- package/dist/patterns/notifications/notification-bell.js +198 -0
- package/dist/patterns/notifications/notification-inbox.d.ts +46 -0
- package/dist/patterns/notifications/notification-inbox.js +537 -0
- package/dist/patterns/notifications/types.d.ts +61 -0
- package/dist/patterns/notifications/types.js +108 -0
- package/dist/patterns/preview/annotation-inspector.d.ts +25 -6
- package/dist/patterns/preview/annotation-inspector.js +239 -94
- package/dist/patterns/preview/annotation-thread.d.ts +25 -5
- package/dist/patterns/preview/annotation-thread.js +233 -82
- package/dist/patterns/preview/annotation-toolbar.d.ts +20 -6
- package/dist/patterns/preview/annotation-toolbar.js +162 -96
- package/dist/patterns/preview/content-preview-adapter.d.ts +10 -1
- package/dist/patterns/preview/content-preview-adapter.js +26 -1
- package/dist/patterns/preview/preview-element.d.ts +15 -2
- package/dist/patterns/preview/preview-element.js +223 -52
- package/dist/patterns/preview/provider-adapter.d.ts +47 -0
- package/dist/patterns/preview/provider-adapter.js +17 -0
- package/dist/patterns/run/index.d.ts +2 -0
- package/dist/patterns/run/index.js +4 -0
- package/dist/patterns/run/run-trace.d.ts +40 -0
- package/dist/patterns/run/run-trace.js +441 -0
- package/dist/patterns/run/types.d.ts +66 -0
- package/dist/patterns/run/types.js +152 -0
- package/dist/patterns/signature/index.d.ts +2 -0
- package/dist/patterns/signature/index.js +4 -0
- package/dist/patterns/signature/signature-ceremony.d.ts +33 -0
- package/dist/patterns/signature/signature-ceremony.js +317 -0
- package/dist/patterns/signature/types.d.ts +68 -0
- package/dist/patterns/signature/types.js +89 -0
- package/dist/patterns/timeline/index.d.ts +2 -0
- package/dist/patterns/timeline/index.js +4 -0
- package/dist/patterns/timeline/timeline.d.ts +27 -0
- package/dist/patterns/timeline/timeline.js +430 -0
- package/dist/patterns/timeline/types.d.ts +36 -0
- package/dist/patterns/timeline/types.js +27 -0
- package/dist/patterns/versions/graph-layout.d.ts +43 -0
- package/dist/patterns/versions/graph-layout.js +159 -0
- package/dist/patterns/versions/index.d.ts +4 -0
- package/dist/patterns/versions/index.js +8 -0
- package/dist/patterns/versions/shared.d.ts +4 -0
- package/dist/patterns/versions/shared.js +13 -0
- package/dist/patterns/versions/types.d.ts +37 -0
- package/dist/patterns/versions/types.js +39 -0
- package/dist/patterns/versions/version-graph.d.ts +36 -0
- package/dist/patterns/versions/version-graph.js +438 -0
- package/dist/patterns/versions/version-list.d.ts +39 -0
- package/dist/patterns/versions/version-list.js +407 -0
- package/dist/patterns/work-queue/controller.d.ts +26 -0
- package/dist/patterns/work-queue/controller.js +150 -0
- package/dist/patterns/work-queue/index.d.ts +4 -0
- package/dist/patterns/work-queue/index.js +7 -0
- package/dist/patterns/work-queue/types.d.ts +128 -0
- package/dist/patterns/work-queue/types.js +45 -0
- package/dist/patterns/work-queue/work-queue.d.ts +48 -0
- package/dist/patterns/work-queue/work-queue.js +534 -0
- package/dist/patterns/work-queue/workload-board.d.ts +53 -0
- package/dist/patterns/work-queue/workload-board.js +608 -0
- package/package.json +89 -5
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
> **Community project — not affiliated with, authorized, or endorsed by Box, Inc.** “Box” is a trademark of Box, Inc. This library tracks Box’s public design language for interoperability; it ships no Box code.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
The library is organized around three layers:
|
|
14
14
|
|
|
15
15
|
- **Foundations** — design decisions as data: tokens, color, typography, iconography, accessibility, theming
|
|
16
16
|
- **Components** — accessible Web Components for single controls, organized by category
|
|
@@ -18,7 +18,7 @@ It is a ground-up rebuild of [`box-open-web-components`](https://github.com/unof
|
|
|
18
18
|
|
|
19
19
|
See [docs/taxonomy.md](./docs/taxonomy.md) for the canonical model.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Core implementation principles:
|
|
22
22
|
|
|
23
23
|
- plain TypeScript modules, no React requirement in the core package
|
|
24
24
|
- state and business logic separate from rendering: controllers and stores, not framework components
|
|
@@ -73,7 +73,7 @@ Full catalog parity with the reference repo, plus every scoped gap the research
|
|
|
73
73
|
- `src/patterns/content-explorer` — the full headless explorer stack (collection, navigation, selection, actions, facade controller, data-source contracts, Box transport, wire schemas) plus the `box-explorer-*` presentation adapters and the composed `box-content-explorer` surface
|
|
74
74
|
- `src/patterns/{search,item,metadata,share,preview,file-request,task,governance,insights}` — all nine composition/workflow areas, including contracts and wire schemas for metadata and share, the provider-neutral preview adapter stack, the pluggable `box-preview-element`, and the share workflows (`box-presence`, `box-invite-collaborators-modal`, `box-unified-share-modal`, `box-access-stats`, `box-collaborator-avatars`)
|
|
75
75
|
- `packages/box-server` — a dependency-free server-side adapter: CCG auth + REST client, Box-backed explorer/share/metadata data sources, DTO mappers, and framework-neutral route handlers (see [docs/integration/box-server.md](./docs/integration/box-server.md))
|
|
76
|
-
- `packages/react` —
|
|
76
|
+
- `packages/{react,angular,vue,svelte}` — lockstep `0.1.0` framework adapters for `Button`, `TextField`, `Select`, `Dialog`, and explorer selection composition. Progress is tracked in [docs/integration/framework-adapters.md](./docs/integration/framework-adapters.md).
|
|
77
77
|
- `storybook/` — a Bun-native workshop: typed stories → identity-guarded extracted JSON → a self-contained, separately-deployable static site, with no Storybook/Vite runtime or consumer dependency (see [storybook/README.md](./storybook/README.md))
|
|
78
78
|
- Docs site + CI — live GitHub Pages deploy, Storybook-backed variant dropdown, and a strict pixel-diff visual-regression gate in CI
|
|
79
79
|
|
|
@@ -7,12 +7,47 @@ export interface TableColumn {
|
|
|
7
7
|
/** Mark the column sortable — clicking its header emits `sort`. */
|
|
8
8
|
sortable?: boolean;
|
|
9
9
|
}
|
|
10
|
+
export type TableCellTone = "neutral" | "brand" | "success" | "warning" | "error";
|
|
11
|
+
/**
|
|
12
|
+
* Declarative cell content beyond plain text. Deliberately a descriptor the
|
|
13
|
+
* table renders itself, never an HTML string or a render callback: cell data
|
|
14
|
+
* is server-supplied and stays escaped — a string renderer would reopen the
|
|
15
|
+
* injection hole the escaping exists to close.
|
|
16
|
+
*/
|
|
17
|
+
export interface TableCellDescriptor {
|
|
18
|
+
kind: "text" | "badge" | "link";
|
|
19
|
+
text: string;
|
|
20
|
+
/** Badge colour; the text still carries the meaning. */
|
|
21
|
+
tone?: TableCellTone;
|
|
22
|
+
/** Link target; non-http(s) schemes render as plain text. */
|
|
23
|
+
href?: string;
|
|
24
|
+
}
|
|
25
|
+
export type TableCellValue = string | TableCellDescriptor;
|
|
10
26
|
export interface TableRow {
|
|
11
27
|
id: string;
|
|
12
|
-
/** Cell
|
|
13
|
-
cells: Record<string,
|
|
28
|
+
/** Cell values keyed by column key, or positional, matching `columns`. */
|
|
29
|
+
cells: Record<string, TableCellValue> | TableCellValue[];
|
|
30
|
+
/**
|
|
31
|
+
* Expandable detail. Renders a per-row toggle and a full-width detail row
|
|
32
|
+
* holding this text plus a `detail-<id>` slot for rich host content.
|
|
33
|
+
*/
|
|
34
|
+
detail?: string;
|
|
14
35
|
}
|
|
15
36
|
export type TableSelectionMode = "none" | "single" | "multiple";
|
|
37
|
+
/** Detail of the `sort` event, for typed listeners. */
|
|
38
|
+
export interface TableSortDetail {
|
|
39
|
+
key: string;
|
|
40
|
+
direction: "ascending" | "descending";
|
|
41
|
+
}
|
|
42
|
+
/** Detail of the `selection-changed` event, for typed listeners. */
|
|
43
|
+
export interface TableSelectionChangedDetail {
|
|
44
|
+
selectedIds: string[];
|
|
45
|
+
}
|
|
46
|
+
/** Detail of the `row-toggled` event, for typed listeners. */
|
|
47
|
+
export interface TableRowToggledDetail {
|
|
48
|
+
rowId: string;
|
|
49
|
+
expanded: boolean;
|
|
50
|
+
}
|
|
16
51
|
/**
|
|
17
52
|
* A semantic, selectable data table — box-ui-elements `Table` + `makeSelectable`.
|
|
18
53
|
* Rows and columns come from JSON. Selection supports single / Ctrl-click toggle
|
|
@@ -27,6 +62,18 @@ export declare class Table extends BaseElement {
|
|
|
27
62
|
private headEl;
|
|
28
63
|
private selected;
|
|
29
64
|
private anchorIndex;
|
|
65
|
+
private readonly expandedIds;
|
|
66
|
+
/** True while rows are being fetched: the body states "Loading rows…". */
|
|
67
|
+
get loading(): boolean;
|
|
68
|
+
set loading(value: boolean);
|
|
69
|
+
/** What the body says when there are no rows and nothing is loading. */
|
|
70
|
+
get emptyText(): string;
|
|
71
|
+
set emptyText(value: string);
|
|
72
|
+
/** Non-empty puts the table in an error state stating this text. */
|
|
73
|
+
get errorText(): string;
|
|
74
|
+
set errorText(value: string);
|
|
75
|
+
/** Ids of the rows whose detail is expanded (host-readable). */
|
|
76
|
+
get expandedRows(): string[];
|
|
30
77
|
get columns(): TableColumn[];
|
|
31
78
|
set columns(value: TableColumn[]);
|
|
32
79
|
get rows(): TableRow[];
|
|
@@ -38,10 +85,17 @@ export declare class Table extends BaseElement {
|
|
|
38
85
|
get label(): string;
|
|
39
86
|
set label(value: string);
|
|
40
87
|
private parseJson;
|
|
41
|
-
private
|
|
88
|
+
private cellValue;
|
|
89
|
+
/**
|
|
90
|
+
* Descriptor rendering. Everything is escaped; a link with a non-http(s)
|
|
91
|
+
* href renders as plain text; a badge's tone colours a word that already
|
|
92
|
+
* carries the meaning.
|
|
93
|
+
*/
|
|
94
|
+
private cellMarkup;
|
|
42
95
|
protected renderTemplate(): void;
|
|
43
96
|
protected setupListeners(): void;
|
|
44
97
|
private rowElements;
|
|
98
|
+
private toggleRowDetail;
|
|
45
99
|
private onBodyKeydown;
|
|
46
100
|
private toggleSort;
|
|
47
101
|
private activateRow;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { boePanel, boeRadius, boeSpace } from "../../foundations/geometry/index.js";
|
|
3
|
+
import { isSafeHref } from "../../patterns/internal/safe-href.js";
|
|
3
4
|
const DEFAULT_TAG_NAME = "box-table";
|
|
5
|
+
const CELL_TONES = new Set(["neutral", "brand", "success", "warning", "error"]);
|
|
6
|
+
const CELL_ALIGNS = new Set(["start", "end", "center"]);
|
|
7
|
+
/** `align` is author input from a JSON attribute like everything else — an
|
|
8
|
+
* unvalidated value would close the attribute and inject markup. */
|
|
9
|
+
const alignAttr = (align) => align && CELL_ALIGNS.has(align) ? ` data-align="${align}"` : "";
|
|
10
|
+
const isCellDescriptor = (value) => typeof value === "object" && value !== null && typeof value.text === "string";
|
|
11
|
+
/** Escape a value for use inside a double-quoted attribute selector. */
|
|
12
|
+
const cssAttrValue = (value) => value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
4
13
|
const escapeHtml = (value) => value
|
|
5
14
|
.replaceAll("&", "&")
|
|
6
15
|
.replaceAll("<", "<")
|
|
@@ -47,8 +56,35 @@ const tableStyles = `
|
|
|
47
56
|
th[part="sortable"] {
|
|
48
57
|
cursor: pointer;
|
|
49
58
|
user-select: none;
|
|
59
|
+
/* The button carries the padding so its hit area fills the cell. */
|
|
60
|
+
padding: 0;
|
|
50
61
|
}
|
|
51
62
|
th[part="sortable"]:hover { color: var(--boe-token-text-text, #222222); }
|
|
63
|
+
|
|
64
|
+
/* A real button, so sorting is reachable by keyboard: focus, Enter and
|
|
65
|
+
Space come from the platform instead of a keydown reimplementation. */
|
|
66
|
+
[part="sort-button"] {
|
|
67
|
+
appearance: none;
|
|
68
|
+
display: block;
|
|
69
|
+
width: 100%;
|
|
70
|
+
padding: ${boeSpace[2]} ${boeSpace[3]};
|
|
71
|
+
border: 0;
|
|
72
|
+
background: transparent;
|
|
73
|
+
font: inherit;
|
|
74
|
+
letter-spacing: inherit;
|
|
75
|
+
text-transform: inherit;
|
|
76
|
+
text-align: inherit;
|
|
77
|
+
color: inherit;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
th[data-align="end"] [part="sort-button"] { text-align: end; }
|
|
82
|
+
th[data-align="center"] [part="sort-button"] { text-align: center; }
|
|
83
|
+
|
|
84
|
+
[part="sort-button"]:focus-visible {
|
|
85
|
+
outline: none;
|
|
86
|
+
box-shadow: inset 0 0 0 2px var(--boe-token-surface-surface-brand, #0061d5);
|
|
87
|
+
}
|
|
52
88
|
th[aria-sort] .boe-sort-arrow::after { content: " ↕"; opacity: 0.5; }
|
|
53
89
|
th[aria-sort="ascending"] .boe-sort-arrow::after { content: " ↑"; opacity: 1; }
|
|
54
90
|
th[aria-sort="descending"] .boe-sort-arrow::after { content: " ↓"; opacity: 1; }
|
|
@@ -76,7 +112,143 @@ const tableStyles = `
|
|
|
76
112
|
tbody tr[part="row"]:focus-visible td {
|
|
77
113
|
box-shadow: inset 0 0 0 2px var(--boe-token-surface-surface-brand, #0061d5);
|
|
78
114
|
}
|
|
115
|
+
|
|
116
|
+
[part="cell-badge"] {
|
|
117
|
+
display: inline-block;
|
|
118
|
+
padding: 0.08rem 0.5rem;
|
|
119
|
+
border-radius: 999px;
|
|
120
|
+
font-size: 0.74rem;
|
|
121
|
+
font-weight: 700;
|
|
122
|
+
color: color-mix(in srgb, var(--cell-tone, var(--boe-token-text-text-secondary, #6f6f6f)) 76%, black 24%);
|
|
123
|
+
background: color-mix(in srgb, var(--cell-tone, var(--boe-token-text-text-secondary, #6f6f6f)) 14%, transparent);
|
|
124
|
+
border: 1px solid color-mix(in srgb, var(--cell-tone, var(--boe-token-text-text-secondary, #6f6f6f)) 30%, transparent);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
[part="cell-link"] {
|
|
128
|
+
color: var(--boe-token-surface-surface-brand, #0061d5);
|
|
129
|
+
text-decoration: none;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
[part="cell-link"]:hover {
|
|
133
|
+
text-decoration: underline;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
[part="expander"] {
|
|
137
|
+
appearance: none;
|
|
138
|
+
display: inline-grid;
|
|
139
|
+
place-items: center;
|
|
140
|
+
inline-size: 1.4rem;
|
|
141
|
+
block-size: 1.4rem;
|
|
142
|
+
border: 0;
|
|
143
|
+
border-radius: 4px;
|
|
144
|
+
background: transparent;
|
|
145
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
146
|
+
font: inherit;
|
|
147
|
+
font-size: 0.72rem;
|
|
148
|
+
cursor: pointer;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
[part="expander"]:hover {
|
|
152
|
+
background: var(--boe-token-surface-surface-hover, #f4f4f4);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
[part="expander"]:focus-visible {
|
|
156
|
+
outline: 2px solid color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 34%, transparent);
|
|
157
|
+
outline-offset: 1px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
[part="expander"]::before {
|
|
161
|
+
content: "▸";
|
|
162
|
+
transition: transform 120ms ease;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
[part="expander"][aria-expanded="true"]::before {
|
|
166
|
+
transform: rotate(90deg);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
tr[part="detail-row"] td {
|
|
170
|
+
background: color-mix(in srgb, var(--boe-token-surface-surface-secondary, #fbfbfb) 80%, transparent);
|
|
171
|
+
font-size: 0.84rem;
|
|
172
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
tr[part="state-row"] td {
|
|
176
|
+
padding: 1.1rem ${boeSpace[3]};
|
|
177
|
+
text-align: center;
|
|
178
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
tr[part="state-row"][data-state="error"] td {
|
|
182
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 76%, black 24%);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
[part="state-spinner"] {
|
|
186
|
+
display: inline-block;
|
|
187
|
+
vertical-align: -0.15rem;
|
|
188
|
+
margin-inline-end: 0.45rem;
|
|
189
|
+
inline-size: 0.95rem;
|
|
190
|
+
block-size: 0.95rem;
|
|
191
|
+
border-radius: 999px;
|
|
192
|
+
border: 2px solid color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 20%, transparent);
|
|
193
|
+
border-top-color: var(--boe-token-surface-surface-brand, #0061d5);
|
|
194
|
+
animation: boe-table-spin 0.9s linear infinite;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@keyframes boe-table-spin {
|
|
198
|
+
to { transform: rotate(360deg); }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@media (prefers-reduced-motion: reduce) {
|
|
202
|
+
[part="state-spinner"] { animation-duration: 1.6s; }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Stacked rows: each row becomes a card, each cell states its column via
|
|
206
|
+
data-label. Roles are explicit on rows and cells, so the grid semantics
|
|
207
|
+
survive the display change. stacked="always" forces it; stacked="auto"
|
|
208
|
+
stacks when the host container narrows. */
|
|
209
|
+
:host {
|
|
210
|
+
container-type: inline-size;
|
|
211
|
+
}
|
|
212
|
+
` +
|
|
213
|
+
(() => {
|
|
214
|
+
const rules = (scope) => `
|
|
215
|
+
${scope} thead { display: none; }
|
|
216
|
+
${scope} tr[part="row"] {
|
|
217
|
+
display: block;
|
|
218
|
+
padding: ${boeSpace[2]} 0;
|
|
219
|
+
}
|
|
220
|
+
${scope} tbody td {
|
|
221
|
+
display: grid;
|
|
222
|
+
grid-template-columns: minmax(7.5rem, 9rem) 1fr;
|
|
223
|
+
gap: 0.5rem;
|
|
224
|
+
border-bottom: none;
|
|
225
|
+
padding-block: 0.18rem;
|
|
226
|
+
text-align: start;
|
|
227
|
+
}
|
|
228
|
+
${scope} tbody td[data-label]::before {
|
|
229
|
+
content: attr(data-label);
|
|
230
|
+
font-size: 0.74rem;
|
|
231
|
+
font-weight: 700;
|
|
232
|
+
letter-spacing: 0.05em;
|
|
233
|
+
text-transform: uppercase;
|
|
234
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
235
|
+
align-self: center;
|
|
236
|
+
}
|
|
237
|
+
${scope} tr[part="row"]:not(:last-child) {
|
|
238
|
+
border-bottom: 1px solid var(--boe-token-stroke-stroke, #e8e8e8);
|
|
239
|
+
}
|
|
240
|
+
${scope} tr[part="detail-row"] td,
|
|
241
|
+
${scope} tr[part="state-row"] td {
|
|
242
|
+
display: block;
|
|
243
|
+
}
|
|
79
244
|
`;
|
|
245
|
+
return `
|
|
246
|
+
${rules(':host([stacked]:not([stacked="auto"]))')}
|
|
247
|
+
@container (max-width: 560px) {
|
|
248
|
+
${rules(':host([stacked="auto"])')}
|
|
249
|
+
}
|
|
250
|
+
`;
|
|
251
|
+
})();
|
|
80
252
|
/**
|
|
81
253
|
* A semantic, selectable data table — box-ui-elements `Table` + `makeSelectable`.
|
|
82
254
|
* Rows and columns come from JSON. Selection supports single / Ctrl-click toggle
|
|
@@ -87,12 +259,54 @@ const tableStyles = `
|
|
|
87
259
|
export class Table extends BaseElement {
|
|
88
260
|
static tagName = DEFAULT_TAG_NAME;
|
|
89
261
|
static get observedAttributes() {
|
|
90
|
-
return [
|
|
262
|
+
return [
|
|
263
|
+
"columns",
|
|
264
|
+
"empty-text",
|
|
265
|
+
"error-text",
|
|
266
|
+
"label",
|
|
267
|
+
"loading",
|
|
268
|
+
"rows",
|
|
269
|
+
"selection-mode",
|
|
270
|
+
"sort-direction",
|
|
271
|
+
"sort-key",
|
|
272
|
+
"stacked",
|
|
273
|
+
];
|
|
91
274
|
}
|
|
92
275
|
bodyEl;
|
|
93
276
|
headEl;
|
|
94
277
|
selected = new Set();
|
|
95
278
|
anchorIndex = -1;
|
|
279
|
+
expandedIds = new Set();
|
|
280
|
+
/** True while rows are being fetched: the body states "Loading rows…". */
|
|
281
|
+
get loading() {
|
|
282
|
+
return this.hasAttribute("loading");
|
|
283
|
+
}
|
|
284
|
+
set loading(value) {
|
|
285
|
+
this.toggleAttribute("loading", Boolean(value));
|
|
286
|
+
}
|
|
287
|
+
/** What the body says when there are no rows and nothing is loading. */
|
|
288
|
+
get emptyText() {
|
|
289
|
+
return this.getAttribute("empty-text") ?? "No rows";
|
|
290
|
+
}
|
|
291
|
+
set emptyText(value) {
|
|
292
|
+
this.setAttribute("empty-text", value);
|
|
293
|
+
}
|
|
294
|
+
/** Non-empty puts the table in an error state stating this text. */
|
|
295
|
+
get errorText() {
|
|
296
|
+
return this.getAttribute("error-text") ?? "";
|
|
297
|
+
}
|
|
298
|
+
set errorText(value) {
|
|
299
|
+
if (value) {
|
|
300
|
+
this.setAttribute("error-text", value);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
this.removeAttribute("error-text");
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/** Ids of the rows whose detail is expanded (host-readable). */
|
|
307
|
+
get expandedRows() {
|
|
308
|
+
return [...this.expandedIds];
|
|
309
|
+
}
|
|
96
310
|
get columns() {
|
|
97
311
|
return this.parseJson("columns", []);
|
|
98
312
|
}
|
|
@@ -139,12 +353,35 @@ export class Table extends BaseElement {
|
|
|
139
353
|
return fallback;
|
|
140
354
|
}
|
|
141
355
|
}
|
|
142
|
-
|
|
356
|
+
cellValue(row, column, index) {
|
|
143
357
|
if (Array.isArray(row.cells)) {
|
|
144
358
|
return row.cells[index] ?? "";
|
|
145
359
|
}
|
|
146
360
|
return row.cells[column.key] ?? "";
|
|
147
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Descriptor rendering. Everything is escaped; a link with a non-http(s)
|
|
364
|
+
* href renders as plain text; a badge's tone colours a word that already
|
|
365
|
+
* carries the meaning.
|
|
366
|
+
*/
|
|
367
|
+
cellMarkup(value) {
|
|
368
|
+
if (!isCellDescriptor(value)) {
|
|
369
|
+
return escapeHtml(typeof value === "string" ? value : "");
|
|
370
|
+
}
|
|
371
|
+
if (value.kind === "badge") {
|
|
372
|
+
const tone = value.tone && CELL_TONES.has(value.tone) ? value.tone : "neutral";
|
|
373
|
+
const toneVar = tone === "neutral"
|
|
374
|
+
? "var(--boe-token-text-text-secondary, #6f6f6f)"
|
|
375
|
+
: tone === "brand"
|
|
376
|
+
? "var(--boe-token-surface-surface-brand, #0061d5)"
|
|
377
|
+
: `var(--boe-token-surface-status-surface-${tone}, #6f6f6f)`;
|
|
378
|
+
return `<span part="cell-badge" style="--cell-tone:${toneVar};">${escapeHtml(value.text)}</span>`;
|
|
379
|
+
}
|
|
380
|
+
if (value.kind === "link" && value.href && isSafeHref(value.href)) {
|
|
381
|
+
return `<a part="cell-link" href="${escapeHtml(value.href)}">${escapeHtml(value.text)}</a>`;
|
|
382
|
+
}
|
|
383
|
+
return escapeHtml(value.text);
|
|
384
|
+
}
|
|
148
385
|
renderTemplate() {
|
|
149
386
|
if (!this.shadowRoot)
|
|
150
387
|
return;
|
|
@@ -168,6 +405,11 @@ export class Table extends BaseElement {
|
|
|
168
405
|
this.toggleSort(th.dataset.key ?? "");
|
|
169
406
|
});
|
|
170
407
|
this.bodyEl.addEventListener("click", event => {
|
|
408
|
+
const expander = event.target.closest('[part="expander"]');
|
|
409
|
+
if (expander) {
|
|
410
|
+
this.toggleRowDetail(expander.dataset.rowId ?? "");
|
|
411
|
+
return; // expanding is not selecting
|
|
412
|
+
}
|
|
171
413
|
const row = event.target.closest('[part="row"]');
|
|
172
414
|
if (!row)
|
|
173
415
|
return;
|
|
@@ -179,7 +421,33 @@ export class Table extends BaseElement {
|
|
|
179
421
|
rowElements() {
|
|
180
422
|
return Array.from(this.bodyEl.querySelectorAll('[part="row"]'));
|
|
181
423
|
}
|
|
424
|
+
toggleRowDetail(rowId) {
|
|
425
|
+
if (!rowId)
|
|
426
|
+
return;
|
|
427
|
+
const expanded = !this.expandedIds.has(rowId);
|
|
428
|
+
if (expanded) {
|
|
429
|
+
this.expandedIds.add(rowId);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
this.expandedIds.delete(rowId);
|
|
433
|
+
}
|
|
434
|
+
this.dispatchEvent(new CustomEvent("row-toggled", {
|
|
435
|
+
bubbles: true,
|
|
436
|
+
composed: true,
|
|
437
|
+
detail: { rowId, expanded },
|
|
438
|
+
}));
|
|
439
|
+
this.update();
|
|
440
|
+
// update() rewrote the body, destroying the button that was activated;
|
|
441
|
+
// put focus on its replacement so a keyboard user is not dropped.
|
|
442
|
+
this.bodyEl
|
|
443
|
+
.querySelector(`[part="expander"][data-row-id="${cssAttrValue(rowId)}"]`)
|
|
444
|
+
?.focus();
|
|
445
|
+
}
|
|
182
446
|
onBodyKeydown(event) {
|
|
447
|
+
// Keys aimed at the expander button (Enter/Space activate it natively)
|
|
448
|
+
// must not double as row selection.
|
|
449
|
+
if (event.target.closest('[part="expander"]'))
|
|
450
|
+
return;
|
|
183
451
|
const rows = this.rowElements();
|
|
184
452
|
if (rows.length === 0)
|
|
185
453
|
return;
|
|
@@ -226,7 +494,11 @@ export class Table extends BaseElement {
|
|
|
226
494
|
const currentKey = this.getAttribute("sort-key");
|
|
227
495
|
const currentDir = this.getAttribute("sort-direction");
|
|
228
496
|
const direction = currentKey === key && currentDir === "ascending" ? "descending" : "ascending";
|
|
229
|
-
this.dispatchEvent(new CustomEvent("sort", {
|
|
497
|
+
this.dispatchEvent(new CustomEvent("sort", {
|
|
498
|
+
bubbles: true,
|
|
499
|
+
composed: true,
|
|
500
|
+
detail: { key, direction },
|
|
501
|
+
}));
|
|
230
502
|
}
|
|
231
503
|
activateRow(index, options = {}) {
|
|
232
504
|
const rows = this.rows;
|
|
@@ -284,32 +556,79 @@ export class Table extends BaseElement {
|
|
|
284
556
|
const selectable = this.selectionMode !== "none";
|
|
285
557
|
const sortKey = this.getAttribute("sort-key");
|
|
286
558
|
const sortDir = this.getAttribute("sort-direction");
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
559
|
+
// A non-string detail is malformed input, not a crash: it gets no expander.
|
|
560
|
+
const expandable = rows.some(row => typeof row.detail === "string");
|
|
561
|
+
const columnCount = columns.length + (expandable ? 1 : 0);
|
|
562
|
+
const tableEl = this.shadowRoot?.querySelector('[part="table"]');
|
|
563
|
+
tableEl?.setAttribute("aria-label", this.label);
|
|
564
|
+
tableEl?.setAttribute("role", selectable ? "grid" : "table");
|
|
565
|
+
if (this.loading) {
|
|
566
|
+
tableEl?.setAttribute("aria-busy", "true");
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
tableEl?.removeAttribute("aria-busy");
|
|
570
|
+
}
|
|
571
|
+
this.headEl.innerHTML =
|
|
572
|
+
(expandable ? `<th scope="col" aria-label="Row details"></th>` : "") +
|
|
573
|
+
columns
|
|
574
|
+
.map(column => {
|
|
575
|
+
const align = alignAttr(column.align);
|
|
576
|
+
if (column.sortable) {
|
|
577
|
+
const sorted = sortKey === column.key;
|
|
578
|
+
const sort = sorted
|
|
579
|
+
? ` aria-sort="${sortDir === "descending" ? "descending" : "ascending"}"`
|
|
580
|
+
: ' aria-sort="none"';
|
|
581
|
+
// The state, in words as well as the arrow glyph.
|
|
582
|
+
const stateLabel = sorted
|
|
583
|
+
? `, sorted ${sortDir === "descending" ? "descending" : "ascending"}`
|
|
584
|
+
: ", not sorted";
|
|
585
|
+
return `<th part="sortable" scope="col" data-key="${escapeHtml(column.key)}"${align}${sort}><button type="button" part="sort-button" aria-label="Sort by ${escapeHtml(column.label)}${stateLabel}">${escapeHtml(column.label)}<span class="boe-sort-arrow" aria-hidden="true"></span></button></th>`;
|
|
586
|
+
}
|
|
587
|
+
return `<th scope="col"${align}>${escapeHtml(column.label)}</th>`;
|
|
588
|
+
})
|
|
589
|
+
.join("");
|
|
590
|
+
// Loading, error, and empty are stated in words in the body — a blank
|
|
591
|
+
// grid reads as broken. Loading wins; a stale error must not outlive a
|
|
592
|
+
// retry that is visibly in flight.
|
|
593
|
+
if (this.loading) {
|
|
594
|
+
this.bodyEl.innerHTML = `<tr part="state-row" data-state="loading" role="row"><td role="${selectable ? "gridcell" : "cell"}" colspan="${String(columnCount)}"><span part="state-spinner" aria-hidden="true"></span>Loading rows…</td></tr>`;
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
if (this.errorText) {
|
|
598
|
+
this.bodyEl.innerHTML = `<tr part="state-row" data-state="error" role="row"><td role="${selectable ? "gridcell" : "cell"}" colspan="${String(columnCount)}">${escapeHtml(this.errorText)}</td></tr>`;
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
if (rows.length === 0) {
|
|
602
|
+
this.bodyEl.innerHTML = `<tr part="state-row" data-state="empty" role="row"><td role="${selectable ? "gridcell" : "cell"}" colspan="${String(columnCount)}">${escapeHtml(this.emptyText)}</td></tr>`;
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
299
605
|
this.bodyEl.innerHTML = rows
|
|
300
606
|
.map((row, index) => {
|
|
301
607
|
const selected = this.selected.has(row.id);
|
|
608
|
+
const cellRole = selectable ? ' role="gridcell"' : ' role="cell"';
|
|
302
609
|
const rowAttrs = selectable
|
|
303
610
|
? ` part="row" role="row" tabindex="${index === 0 ? "0" : "-1"}" aria-selected="${selected}"`
|
|
304
611
|
: ' part="row" role="row"';
|
|
612
|
+
const expanded = this.expandedIds.has(row.id);
|
|
613
|
+
const expanderCell = expandable
|
|
614
|
+
? `<td${cellRole} part="expander-cell">${typeof row.detail === "string"
|
|
615
|
+
? `<button type="button" part="expander" data-row-id="${escapeHtml(row.id)}" aria-expanded="${String(expanded)}" aria-label="${expanded ? "Hide" : "Show"} details for row ${escapeHtml(row.id)}"></button>`
|
|
616
|
+
: ""}</td>`
|
|
617
|
+
: "";
|
|
305
618
|
const cells = columns
|
|
306
|
-
.map(column => {
|
|
307
|
-
const align = column.align
|
|
308
|
-
const
|
|
309
|
-
return `<td${align}${
|
|
619
|
+
.map((column, columnIndex) => {
|
|
620
|
+
const align = alignAttr(column.align);
|
|
621
|
+
const value = this.cellValue(row, column, columnIndex);
|
|
622
|
+
return `<td${align}${cellRole} data-label="${escapeHtml(column.label)}">${this.cellMarkup(value)}</td>`;
|
|
310
623
|
})
|
|
311
624
|
.join("");
|
|
312
|
-
|
|
625
|
+
const rowMarkup = `<tr${rowAttrs} data-index="${index}" data-id="${escapeHtml(row.id)}">${expanderCell}${cells}</tr>`;
|
|
626
|
+
// The detail row renders only while expanded, keeping the roving
|
|
627
|
+
// tabindex and shift-range math on visible [part="row"] rows only.
|
|
628
|
+
const detailRow = typeof row.detail === "string" && expanded
|
|
629
|
+
? `<tr part="detail-row" role="row" data-for="${escapeHtml(row.id)}"><td${cellRole} colspan="${String(columnCount)}">${escapeHtml(row.detail)}<slot name="detail-${escapeHtml(row.id)}"></slot></td></tr>`
|
|
630
|
+
: "";
|
|
631
|
+
return rowMarkup + detailRow;
|
|
313
632
|
})
|
|
314
633
|
.join("");
|
|
315
634
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DueBucket } from "./due-types.js";
|
|
2
|
+
import { BaseElement } from "../../core/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* SLA / aging urgency for a due date — the badge the work-queue rows and
|
|
5
|
+
* record headers were specified to share.
|
|
6
|
+
*
|
|
7
|
+
* The label states the urgency in words ("Overdue by 3 days"), so colour is
|
|
8
|
+
* never the only signal, and the day count answers the question a due badge
|
|
9
|
+
* exists for: *how late is this?* A bare date makes the reader do that
|
|
10
|
+
* arithmetic themselves.
|
|
11
|
+
*
|
|
12
|
+
* `reference-time` pins "now" so rendering is deterministic — the same
|
|
13
|
+
* inputs always produce the same badge, in tests and in screenshots.
|
|
14
|
+
*/
|
|
15
|
+
export declare class DueBadge extends BaseElement {
|
|
16
|
+
static readonly tagName: string;
|
|
17
|
+
static get observedAttributes(): string[];
|
|
18
|
+
private badgeEl;
|
|
19
|
+
private textEl;
|
|
20
|
+
/** ISO timestamp the badge describes. Absent renders the no-due-date state. */
|
|
21
|
+
get dueAt(): string;
|
|
22
|
+
set dueAt(value: string);
|
|
23
|
+
/** Overrides the derived wording; the bucket still drives the tone. */
|
|
24
|
+
get label(): string;
|
|
25
|
+
set label(value: string);
|
|
26
|
+
get compact(): boolean;
|
|
27
|
+
set compact(value: boolean);
|
|
28
|
+
get referenceTime(): string;
|
|
29
|
+
set referenceTime(value: string);
|
|
30
|
+
private now;
|
|
31
|
+
/** The urgency bucket currently rendered. */
|
|
32
|
+
get bucket(): DueBucket;
|
|
33
|
+
/** The rendered wording, derived unless `label` overrides it. */
|
|
34
|
+
get resolvedLabel(): string;
|
|
35
|
+
protected renderTemplate(): void;
|
|
36
|
+
protected setupListeners(): void;
|
|
37
|
+
protected update(): void;
|
|
38
|
+
}
|