@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.96 → 2.0.0-next.98
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/bundle/openbridge-webcomponents.bundle.js +39 -124
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +343 -142
- package/dist/ar/poi/poi.d.ts +4 -2
- package/dist/ar/poi/poi.d.ts.map +1 -1
- package/dist/ar/poi/poi.js.map +1 -1
- package/dist/ar/poi-layer/poi-layer.d.ts +1 -0
- package/dist/ar/poi-layer/poi-layer.d.ts.map +1 -1
- package/dist/ar/poi-layer/poi-layer.js.map +1 -1
- package/dist/ar/poi-object/poi-object-aton.d.ts +1 -0
- package/dist/ar/poi-object/poi-object-aton.d.ts.map +1 -1
- package/dist/ar/poi-object/poi-object-aton.js.map +1 -1
- package/dist/automation/automation-badge/automation-badge.d.ts +2 -0
- package/dist/automation/automation-badge/automation-badge.d.ts.map +1 -1
- package/dist/automation/automation-badge/automation-badge.js.map +1 -1
- package/dist/automation/automation-button/automation-button.d.ts +9 -0
- package/dist/automation/automation-button/automation-button.d.ts.map +1 -1
- package/dist/automation/automation-button/automation-button.js.map +1 -1
- package/dist/automation/automation-input-modal/automation-input-modal.d.ts +4 -0
- package/dist/automation/automation-input-modal/automation-input-modal.d.ts.map +1 -1
- package/dist/automation/automation-input-modal/automation-input-modal.js.map +1 -1
- package/dist/automation/automation-tank/automation-tank.css.js +8 -87
- package/dist/automation/automation-tank/automation-tank.css.js.map +1 -1
- package/dist/automation/automation-tank/automation-tank.d.ts +13 -33
- package/dist/automation/automation-tank/automation-tank.d.ts.map +1 -1
- package/dist/automation/automation-tank/automation-tank.js +12 -28
- package/dist/automation/automation-tank/automation-tank.js.map +1 -1
- package/dist/building-blocks/alert-list/alert-list.d.ts +4 -0
- package/dist/building-blocks/alert-list/alert-list.d.ts.map +1 -1
- package/dist/building-blocks/alert-list/alert-list.js.map +1 -1
- package/dist/building-blocks/bar-horizontal/bar-horizontal.d.ts +2 -0
- package/dist/building-blocks/bar-horizontal/bar-horizontal.d.ts.map +1 -1
- package/dist/building-blocks/bar-horizontal/bar-horizontal.js.map +1 -1
- package/dist/building-blocks/bar-vertical/bar-vertical.d.ts +2 -0
- package/dist/building-blocks/bar-vertical/bar-vertical.d.ts.map +1 -1
- package/dist/building-blocks/bar-vertical/bar-vertical.js.map +1 -1
- package/dist/components/advice-floating-item/advice-floating-item.d.ts +2 -4
- package/dist/components/advice-floating-item/advice-floating-item.d.ts.map +1 -1
- package/dist/components/advice-floating-item/advice-floating-item.js.map +1 -1
- package/dist/components/advice-message-item/advice-message-item.d.ts +6 -19
- package/dist/components/advice-message-item/advice-message-item.d.ts.map +1 -1
- package/dist/components/advice-message-item/advice-message-item.js.map +1 -1
- package/dist/components/alert-floating-item/alert-floating-item.d.ts +12 -0
- package/dist/components/alert-floating-item/alert-floating-item.d.ts.map +1 -1
- package/dist/components/alert-floating-item/alert-floating-item.js.map +1 -1
- package/dist/components/alert-menu/alert-menu.d.ts +6 -20
- package/dist/components/alert-menu/alert-menu.d.ts.map +1 -1
- package/dist/components/alert-menu/alert-menu.js.map +1 -1
- package/dist/components/alert-menu-item/alert-menu-item.d.ts +4 -0
- package/dist/components/alert-menu-item/alert-menu-item.d.ts.map +1 -1
- package/dist/components/alert-menu-item/alert-menu-item.js.map +1 -1
- package/dist/components/chat-message/chat-message.d.ts +3 -0
- package/dist/components/chat-message/chat-message.d.ts.map +1 -1
- package/dist/components/chat-message/chat-message.js.map +1 -1
- package/dist/components/elevated-card-radio/elevated-card-radio.d.ts +2 -6
- package/dist/components/elevated-card-radio/elevated-card-radio.d.ts.map +1 -1
- package/dist/components/elevated-card-radio/elevated-card-radio.js.map +1 -1
- package/dist/components/keyboard-numeric/keyboard-numeric.d.ts +6 -0
- package/dist/components/keyboard-numeric/keyboard-numeric.d.ts.map +1 -1
- package/dist/components/keyboard-numeric/keyboard-numeric.js.map +1 -1
- package/dist/components/notification-floating-item/notification-floating-item.d.ts +2 -4
- package/dist/components/notification-floating-item/notification-floating-item.d.ts.map +1 -1
- package/dist/components/notification-floating-item/notification-floating-item.js.map +1 -1
- package/dist/components/sequence-card/sequence-card.d.ts +9 -0
- package/dist/components/sequence-card/sequence-card.d.ts.map +1 -1
- package/dist/components/sequence-card/sequence-card.js.map +1 -1
- package/dist/components/sequence-item/sequence-item.d.ts +5 -0
- package/dist/components/sequence-item/sequence-item.d.ts.map +1 -1
- package/dist/components/sequence-item/sequence-item.js.map +1 -1
- package/dist/components/sequence-modal/sequence-modal.d.ts +4 -0
- package/dist/components/sequence-modal/sequence-modal.d.ts.map +1 -1
- package/dist/components/sequence-modal/sequence-modal.js.map +1 -1
- package/dist/components/sequence-step/sequence-step.d.ts.map +1 -1
- package/dist/components/sequence-step/sequence-step.js.map +1 -1
- package/dist/components/slider-double/slider-double.d.ts +4 -8
- package/dist/components/slider-double/slider-double.d.ts.map +1 -1
- package/dist/components/slider-double/slider-double.js.map +1 -1
- package/dist/components/status-indicator/status-indicator.d.ts +1 -0
- package/dist/components/status-indicator/status-indicator.d.ts.map +1 -1
- package/dist/components/status-indicator/status-indicator.js.map +1 -1
- package/dist/components/system-menu/system-menu.d.ts +2 -0
- package/dist/components/system-menu/system-menu.d.ts.map +1 -1
- package/dist/components/system-menu/system-menu.js.map +1 -1
- package/dist/components/table-header-item/table-header-item.d.ts +2 -0
- package/dist/components/table-header-item/table-header-item.d.ts.map +1 -1
- package/dist/components/table-header-item/table-header-item.js.map +1 -1
- package/dist/components/tag/tag.d.ts +1 -0
- package/dist/components/tag/tag.d.ts.map +1 -1
- package/dist/components/tag/tag.js.map +1 -1
- package/dist/components/toggletip/toggletip.d.ts +4 -4
- package/dist/components/toggletip/toggletip.js.map +1 -1
- package/dist/components/user-menu/user-menu.d.ts +1 -0
- package/dist/components/user-menu/user-menu.d.ts.map +1 -1
- package/dist/components/user-menu/user-menu.js.map +1 -1
- package/dist/integration-systems/integration-app-bar/integration-app-bar.d.ts +1 -0
- package/dist/integration-systems/integration-app-bar/integration-app-bar.d.ts.map +1 -1
- package/dist/integration-systems/integration-app-bar/integration-app-bar.js.map +1 -1
- package/dist/integration-systems/integration-bar/integration-bar.d.ts +0 -1
- package/dist/integration-systems/integration-bar/integration-bar.d.ts.map +1 -1
- package/dist/integration-systems/integration-bar/integration-bar.js.map +1 -1
- package/dist/integration-systems/integration-bar-dropdown/integration-bar-dropdown.d.ts +9 -9
- package/dist/integration-systems/integration-bar-dropdown/integration-bar-dropdown.js.map +1 -1
- package/dist/integration-systems/integration-fleet-button/integration-fleet-button.d.ts +1 -0
- package/dist/integration-systems/integration-fleet-button/integration-fleet-button.d.ts.map +1 -1
- package/dist/integration-systems/integration-fleet-button/integration-fleet-button.js.map +1 -1
- package/dist/integration-systems/integration-tabs/integration-tabs.d.ts +1 -0
- package/dist/integration-systems/integration-tabs/integration-tabs.d.ts.map +1 -1
- package/dist/integration-systems/integration-tabs/integration-tabs.js.map +1 -1
- package/dist/navigation-instruments/compass-sector/compass-sector.d.ts +0 -1
- package/dist/navigation-instruments/compass-sector/compass-sector.d.ts.map +1 -1
- package/dist/navigation-instruments/compass-sector/compass-sector.js.map +1 -1
- package/dist/navigation-instruments/watch/tickmark.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/tickmark.js +17 -7
- package/dist/navigation-instruments/watch/tickmark.js.map +1 -1
- package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
- package/dist/navigation-instruments/watch/watch.js +2 -2
- package/dist/navigation-instruments/watch/watch.js.map +1 -1
- package/dist/navigation-instruments/wind-propulsion/wind-propulsion.d.ts +0 -1
- package/dist/navigation-instruments/wind-propulsion/wind-propulsion.d.ts.map +1 -1
- package/dist/navigation-instruments/wind-propulsion/wind-propulsion.js.map +1 -1
- package/dist/pages/alert-detail-page/alert-detail-page.d.ts.map +1 -1
- package/dist/pages/alert-detail-page/alert-detail-page.js.map +1 -1
- package/package.json +3 -2
- package/script/check-slot-event-docs.ts +353 -0
- package/src/ar/poi/poi.ts +4 -2
- package/src/ar/poi-layer/poi-layer.ts +1 -0
- package/src/ar/poi-object/poi-object-aton.ts +1 -0
- package/src/automation/automation-badge/automation-badge.ts +2 -0
- package/src/automation/automation-button/automation-button.ts +9 -0
- package/src/automation/automation-input-modal/automation-input-modal.ts +4 -0
- package/src/automation/automation-tank/automation-tank.css +8 -64
- package/src/automation/automation-tank/automation-tank.stories.ts +41 -23
- package/src/automation/automation-tank/automation-tank.ts +25 -65
- package/src/building-blocks/alert-list/alert-list.ts +4 -0
- package/src/building-blocks/bar-horizontal/bar-horizontal.ts +2 -0
- package/src/building-blocks/bar-vertical/bar-vertical.ts +2 -0
- package/src/components/advice-floating-item/advice-floating-item.ts +2 -4
- package/src/components/advice-message-item/advice-message-item.ts +6 -19
- package/src/components/alert-floating-item/alert-floating-item.ts +12 -0
- package/src/components/alert-menu/alert-menu.ts +6 -20
- package/src/components/alert-menu-item/alert-menu-item.ts +4 -0
- package/src/components/chat-message/chat-message.ts +3 -0
- package/src/components/elevated-card-radio/elevated-card-radio.ts +2 -6
- package/src/components/keyboard-numeric/keyboard-numeric.ts +6 -0
- package/src/components/notification-floating-item/notification-floating-item.ts +2 -4
- package/src/components/sequence-card/sequence-card.ts +9 -0
- package/src/components/sequence-item/sequence-item.ts +5 -0
- package/src/components/sequence-modal/sequence-modal.ts +4 -0
- package/src/components/sequence-step/sequence-step.ts +3 -0
- package/src/components/slider-double/slider-double.ts +4 -8
- package/src/components/status-indicator/status-indicator.ts +1 -0
- package/src/components/system-menu/system-menu.ts +2 -0
- package/src/components/table-header-item/table-header-item.ts +2 -0
- package/src/components/tag/tag.ts +1 -0
- package/src/components/toggletip/toggletip.ts +4 -4
- package/src/components/user-menu/user-menu.ts +1 -0
- package/src/integration-systems/integration-app-bar/integration-app-bar.ts +1 -0
- package/src/integration-systems/integration-bar/integration-bar.ts +0 -1
- package/src/integration-systems/integration-bar-dropdown/integration-bar-dropdown.ts +9 -9
- package/src/integration-systems/integration-fleet-button/integration-fleet-button.ts +1 -0
- package/src/integration-systems/integration-tabs/integration-tabs.ts +1 -0
- package/src/navigation-instruments/compass-sector/compass-sector.ts +0 -1
- package/src/navigation-instruments/watch/tickmark.spec.ts +99 -0
- package/src/navigation-instruments/watch/tickmark.ts +21 -8
- package/src/navigation-instruments/watch/watch.ts +7 -2
- package/src/navigation-instruments/wind-propulsion/wind-propulsion.ts +0 -1
- package/src/pages/alert-detail-page/alert-detail-page.ts +7 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module SlotEventDocAudit
|
|
3
|
+
* @description
|
|
4
|
+
* Audits every registered web component (`@customElement`) so that its rendered
|
|
5
|
+
* `<slot>` elements and dispatched custom events are declared with matching
|
|
6
|
+
* `@slot` / `@fires` JSDoc tags on the class.
|
|
7
|
+
*
|
|
8
|
+
* Why this matters: `custom-elements.json` is generated by `cem analyze`, which
|
|
9
|
+
* discovers slots and events **only** from `@slot` / `@fires` JSDoc tags — never
|
|
10
|
+
* from the template or from `dispatchEvent(...)` calls. If a component renders a
|
|
11
|
+
* `<slot name="leading-icon">` but forgets the `@slot leading-icon` tag, the slot
|
|
12
|
+
* is invisible to the manifest and therefore to every downstream consumer (the
|
|
13
|
+
* framework wrappers, IDE autocomplete, Storybook autodocs, code playgrounds).
|
|
14
|
+
* A `@slot`/`@fires` tag that has no counterpart in the template/code produces the
|
|
15
|
+
* opposite problem: a phantom control that does nothing. See issue #1033.
|
|
16
|
+
*
|
|
17
|
+
* The checks are intentionally conservative to stay false-positive-free in CI:
|
|
18
|
+
*
|
|
19
|
+
* - **Missing slot (error):** a *static* `<slot name="literal">` or a *bare*
|
|
20
|
+
* default `<slot>` with no matching `@slot` tag. Slots rendered by an inherited
|
|
21
|
+
* base class live in the base file, so subclasses are never falsely flagged.
|
|
22
|
+
* - **Missing event (error):** a literal `new CustomEvent('name')` /
|
|
23
|
+
* `new Event('name')` with no matching `@fires`/`@event name` tag.
|
|
24
|
+
* - **Phantom slot (error):** a `@slot name` tag with no matching static
|
|
25
|
+
* `<slot name="name">` element — only evaluated for files that `extends
|
|
26
|
+
* LitElement` directly and contain no dynamic `<slot name="${…}">`, so
|
|
27
|
+
* inheritance- and dynamic-name-based false positives are skipped. A
|
|
28
|
+
* `slot="name"` *projection attribute* in the template does NOT suppress this
|
|
29
|
+
* (it exposes nothing); only a genuine imperative read of the consumer's
|
|
30
|
+
* children (e.g. `getAttribute('slot') === 'name'`) counts as a real slot.
|
|
31
|
+
* - **Empty description (warning):** a `@slot`/`@fires` tag with only a name and
|
|
32
|
+
* no descriptive text. These become blank cells in the manifest / Storybook
|
|
33
|
+
* controls; warnings do not fail CI.
|
|
34
|
+
*
|
|
35
|
+
* There is deliberately no phantom-`@fires` *error*: unlike slots (which must be
|
|
36
|
+
* a `<slot>` element in the component's own shadow DOM), a documented event may
|
|
37
|
+
* legitimately originate elsewhere — a native event bubbling from an inner
|
|
38
|
+
* element (`click`, `blur`), or an event re-fired by a child — so "documented
|
|
39
|
+
* but not locally dispatched" is not, on its own, a defect.
|
|
40
|
+
*
|
|
41
|
+
* Dynamic slot names (`<slot name="${expr}">` or `<slot name="pre-${id}-post">`)
|
|
42
|
+
* are skipped for the missing-slot check because their concrete names cannot be
|
|
43
|
+
* known statically; document them with a placeholder tag such as
|
|
44
|
+
* `@slot tab-<id>-icon` (see .cursor/rules/comments.mdc § Structured-tag rules).
|
|
45
|
+
*
|
|
46
|
+
* Usage:
|
|
47
|
+
* ```bash
|
|
48
|
+
* npm run lint:slots
|
|
49
|
+
* tsx script/check-slot-event-docs.ts
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* Exits with code 1 when any error-level finding is present, suitable for CI.
|
|
53
|
+
*
|
|
54
|
+
* Note: standalone script; exports nothing. Regex-based like the sibling
|
|
55
|
+
* `check-css-*` audits — it does not build a full AST.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
import fs from 'fs';
|
|
59
|
+
import path from 'path';
|
|
60
|
+
import {globby} from 'globby';
|
|
61
|
+
|
|
62
|
+
interface Finding {
|
|
63
|
+
file: string;
|
|
64
|
+
message: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Remove block and line comments so template scans ignore commented-out code. */
|
|
68
|
+
function stripComments(source: string): string {
|
|
69
|
+
return source
|
|
70
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
71
|
+
.replace(/(^|[^:])\/\/[^\n]*/g, '$1');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Static exposed slot names (`<slot name="literal">`, no interpolation). */
|
|
75
|
+
function findStaticSlots(code: string): Set<string> {
|
|
76
|
+
const slots = new Set<string>();
|
|
77
|
+
const re = /<slot\b([^>]*)>/g;
|
|
78
|
+
let m: RegExpExecArray | null;
|
|
79
|
+
while ((m = re.exec(code)) !== null) {
|
|
80
|
+
const attrs = m[1];
|
|
81
|
+
const nameMatch = /\bname\s*=\s*["']([^"']*)["']/.exec(attrs);
|
|
82
|
+
if (!nameMatch) continue; // bare/default slot handled separately
|
|
83
|
+
const name = nameMatch[1];
|
|
84
|
+
if (name.includes('${')) continue; // dynamic — cannot resolve statically
|
|
85
|
+
slots.add(name);
|
|
86
|
+
}
|
|
87
|
+
return slots;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** True when the template renders a bare default `<slot>` (no `name` attribute). */
|
|
91
|
+
function hasDefaultSlot(code: string): boolean {
|
|
92
|
+
const re = /<slot\b([^>]*)>/g;
|
|
93
|
+
let m: RegExpExecArray | null;
|
|
94
|
+
while ((m = re.exec(code)) !== null) {
|
|
95
|
+
if (!/\bname\s*=/.test(m[1])) return true;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** True when any `<slot name="${…}">` uses an interpolated (dynamic) name. */
|
|
101
|
+
function hasDynamicSlot(code: string): boolean {
|
|
102
|
+
const re = /<slot\b([^>]*)>/g;
|
|
103
|
+
let m: RegExpExecArray | null;
|
|
104
|
+
while ((m = re.exec(code)) !== null) {
|
|
105
|
+
const nameMatch = /\bname\s*=\s*(?:["'][^"']*["']|`[^`]*`|[^\s>]+)/.exec(
|
|
106
|
+
m[1]
|
|
107
|
+
);
|
|
108
|
+
if (nameMatch && nameMatch[0].includes('${')) return true;
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Literal event names the host itself dispatches:
|
|
115
|
+
* `this.dispatchEvent(new CustomEvent('name'…))`. Restricted to `this.` so
|
|
116
|
+
* events re-dispatched on inner child elements (e.g. a native `<input>`) are
|
|
117
|
+
* not mistaken for the component's own public API.
|
|
118
|
+
*/
|
|
119
|
+
function findDispatchedEvents(code: string): Set<string> {
|
|
120
|
+
const events = new Set<string>();
|
|
121
|
+
const re =
|
|
122
|
+
/this\.dispatchEvent\(\s*new\s+(?:Custom)?Event\s*\(\s*['"]([^'"]+)['"]/g;
|
|
123
|
+
let m: RegExpExecArray | null;
|
|
124
|
+
while ((m = re.exec(code)) !== null) events.add(m[1]);
|
|
125
|
+
return events;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Documented slot names from `@slot name` tags (`-` denotes the default slot). */
|
|
129
|
+
function findDocumentedSlots(source: string): {
|
|
130
|
+
names: Set<string>;
|
|
131
|
+
hasDefault: boolean;
|
|
132
|
+
} {
|
|
133
|
+
const names = new Set<string>();
|
|
134
|
+
let hasDefault = false;
|
|
135
|
+
const re = /@slot\s+(\S+)/g;
|
|
136
|
+
let m: RegExpExecArray | null;
|
|
137
|
+
while ((m = re.exec(source)) !== null) {
|
|
138
|
+
if (m[1] === '-') hasDefault = true;
|
|
139
|
+
else names.add(m[1]);
|
|
140
|
+
}
|
|
141
|
+
return {names, hasDefault};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Documented event names from `@fires name` / `@event name` tags. */
|
|
145
|
+
function findDocumentedEvents(source: string): Set<string> {
|
|
146
|
+
const names = new Set<string>();
|
|
147
|
+
// Accept both tag orders: `@fires name {Type}` and `@fires {Type} name`.
|
|
148
|
+
// The optional leading `{Type}` may contain one level of nested braces,
|
|
149
|
+
// e.g. `{CustomEvent<{date: Date}>}`.
|
|
150
|
+
const re = /@(?:fires|event)\s+(?:\{(?:[^{}]|\{[^{}]*\})*\}\s+)?(\S+)/g;
|
|
151
|
+
let m: RegExpExecArray | null;
|
|
152
|
+
while ((m = re.exec(source)) !== null) names.add(m[1]);
|
|
153
|
+
return names;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface TagDoc {
|
|
157
|
+
name: string;
|
|
158
|
+
hasDescription: boolean;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** A plausible slot/event name (identifier or kebab-case), or `-` for default. */
|
|
162
|
+
function isValidTagName(name: string): boolean {
|
|
163
|
+
return name === '-' || /^[A-Za-z][\w-]*$/.test(name);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Parse `@slot` tags into name + whether descriptive text follows. */
|
|
167
|
+
function parseSlotTags(source: string): TagDoc[] {
|
|
168
|
+
const out: TagDoc[] = [];
|
|
169
|
+
const re = /@slot[ \t]+(\S+)([^\n]*)/g;
|
|
170
|
+
let m: RegExpExecArray | null;
|
|
171
|
+
while ((m = re.exec(source)) !== null) {
|
|
172
|
+
if (!isValidTagName(m[1])) continue;
|
|
173
|
+
const rest = m[2].replace(/^\s*-\s*/, '').trim();
|
|
174
|
+
out.push({name: m[1], hasDescription: rest.length > 0});
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Parse `@fires` / `@event` tags into name + whether descriptive text follows,
|
|
181
|
+
* tolerating a leading or trailing `{Type}` (with one level of nested braces).
|
|
182
|
+
*/
|
|
183
|
+
function parseEventTags(source: string): TagDoc[] {
|
|
184
|
+
const out: TagDoc[] = [];
|
|
185
|
+
const type = '\\{(?:[^{}]|\\{[^{}]*\\})*\\}';
|
|
186
|
+
const re = new RegExp(
|
|
187
|
+
`@(?:fires|event)[ \\t]+(?:${type}[ \\t]+)?(\\S+)([^\\n]*)`,
|
|
188
|
+
'g'
|
|
189
|
+
);
|
|
190
|
+
let m: RegExpExecArray | null;
|
|
191
|
+
while ((m = re.exec(source)) !== null) {
|
|
192
|
+
if (!isValidTagName(m[1])) continue;
|
|
193
|
+
const rest = m[2]
|
|
194
|
+
.replace(new RegExp(`^\\s*${type}\\s*`), '')
|
|
195
|
+
.replace(/^\s*-\s*/, '')
|
|
196
|
+
.trim();
|
|
197
|
+
out.push({name: m[1], hasDescription: rest.length > 0});
|
|
198
|
+
}
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Whether the class extends `LitElement` directly (not a base class or mixin). */
|
|
203
|
+
function extendsLitElementDirectly(source: string): boolean {
|
|
204
|
+
return /class\s+\w+\s+extends\s+LitElement\b/.test(source);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Whether the (comment-stripped) code references `name` as a string literal. */
|
|
208
|
+
function referencesNameAsString(code: string, name: string): boolean {
|
|
209
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
210
|
+
return new RegExp(`['"\`]${escaped}['"\`]`).test(code);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function run(): Promise<void> {
|
|
214
|
+
const cwd = process.cwd();
|
|
215
|
+
const files = await globby(['src/**/*.ts'], {
|
|
216
|
+
cwd,
|
|
217
|
+
ignore: ['src/**/*.stories.ts', 'src/icons/**'],
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const errors: Finding[] = [];
|
|
221
|
+
const warnings: Finding[] = [];
|
|
222
|
+
let componentCount = 0;
|
|
223
|
+
|
|
224
|
+
for (const rel of files.sort()) {
|
|
225
|
+
const source = fs.readFileSync(path.join(cwd, rel), 'utf8');
|
|
226
|
+
if (!/@customElement\(/.test(source)) continue;
|
|
227
|
+
componentCount++;
|
|
228
|
+
|
|
229
|
+
const code = stripComments(source);
|
|
230
|
+
const staticSlots = findStaticSlots(code);
|
|
231
|
+
const defaultSlot = hasDefaultSlot(code);
|
|
232
|
+
const dynamicSlot = hasDynamicSlot(code);
|
|
233
|
+
const events = findDispatchedEvents(code);
|
|
234
|
+
|
|
235
|
+
const docSlots = findDocumentedSlots(source);
|
|
236
|
+
const docEvents = findDocumentedEvents(source);
|
|
237
|
+
|
|
238
|
+
// Missing @slot for static exposed slots.
|
|
239
|
+
for (const name of staticSlots) {
|
|
240
|
+
if (!docSlots.names.has(name)) {
|
|
241
|
+
errors.push({
|
|
242
|
+
file: rel,
|
|
243
|
+
message: `renders <slot name="${name}"> but has no matching @slot ${name} tag`,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Missing @slot - for a bare default slot.
|
|
249
|
+
if (defaultSlot && !docSlots.hasDefault) {
|
|
250
|
+
errors.push({
|
|
251
|
+
file: rel,
|
|
252
|
+
message: `renders a default <slot> but has no matching "@slot -" tag`,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Missing @fires for dispatched custom events.
|
|
257
|
+
for (const name of events) {
|
|
258
|
+
if (!docEvents.has(name)) {
|
|
259
|
+
errors.push({
|
|
260
|
+
file: rel,
|
|
261
|
+
message: `dispatches CustomEvent('${name}') but has no matching @fires ${name} tag`,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Phantom @slot — only for direct LitElement subclasses without dynamic
|
|
267
|
+
// slot names, so inherited/dynamic slots never produce false positives.
|
|
268
|
+
if (extendsLitElementDirectly(source) && !dynamicSlot) {
|
|
269
|
+
// A `slot="name"` attribute in the template is a *projection into a child*
|
|
270
|
+
// — it exposes nothing and must NOT suppress the phantom warning. Strip
|
|
271
|
+
// those before the string-literal check so only genuine imperative reads
|
|
272
|
+
// (e.g. `getAttribute('slot') === 'header'`, which relocate the consumer's
|
|
273
|
+
// `slot="name"` children) count as a real, if non-declarative, slot.
|
|
274
|
+
const codeNoProjections = code.replace(
|
|
275
|
+
/\bslot\s*=\s*["'][^"']*["']/g,
|
|
276
|
+
' '
|
|
277
|
+
);
|
|
278
|
+
for (const name of docSlots.names) {
|
|
279
|
+
if (name.includes('<') || name.includes('${')) continue; // placeholder
|
|
280
|
+
if (staticSlots.has(name)) continue; // rendered as a real <slot>
|
|
281
|
+
if (referencesNameAsString(codeNoProjections, name)) continue;
|
|
282
|
+
errors.push({
|
|
283
|
+
file: rel,
|
|
284
|
+
message: `documents @slot ${name} but renders no <slot name="${name}"> (phantom — likely a slot="${name}" projection into a child)`,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Empty description (warning) — a @slot/@fires tag with only a name produces
|
|
290
|
+
// a blank cell in the manifest / Storybook controls.
|
|
291
|
+
for (const tag of parseSlotTags(source)) {
|
|
292
|
+
if (!tag.hasDescription) {
|
|
293
|
+
const label = tag.name === '-' ? 'default slot' : `@slot ${tag.name}`;
|
|
294
|
+
warnings.push({file: rel, message: `${label} has no description`});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
for (const tag of parseEventTags(source)) {
|
|
298
|
+
if (!tag.hasDescription) {
|
|
299
|
+
warnings.push({
|
|
300
|
+
file: rel,
|
|
301
|
+
message: `@fires ${tag.name} has no description`,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
console.log('Slot & event documentation audit');
|
|
308
|
+
console.log(`Scanned ${componentCount} registered components under src/`);
|
|
309
|
+
|
|
310
|
+
const printByFile = (
|
|
311
|
+
findings: Finding[],
|
|
312
|
+
log: (msg: string) => void,
|
|
313
|
+
indent = ' '
|
|
314
|
+
) => {
|
|
315
|
+
const byFile = new Map<string, string[]>();
|
|
316
|
+
for (const f of findings) {
|
|
317
|
+
if (!byFile.has(f.file)) byFile.set(f.file, []);
|
|
318
|
+
byFile.get(f.file)!.push(f.message);
|
|
319
|
+
}
|
|
320
|
+
for (const [file, messages] of [...byFile.entries()].sort()) {
|
|
321
|
+
log(`\n ${file}`);
|
|
322
|
+
for (const msg of messages) log(`${indent}- ${msg}`);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
if (warnings.length > 0) {
|
|
327
|
+
console.warn(
|
|
328
|
+
`\n⚠️ ${warnings.length} empty-description warning(s) (non-blocking):`
|
|
329
|
+
);
|
|
330
|
+
printByFile(warnings, (m) => console.warn(m));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (errors.length > 0) {
|
|
334
|
+
console.error(`\nFound ${errors.length} issue(s):`);
|
|
335
|
+
printByFile(errors, (m) => console.error(m));
|
|
336
|
+
console.error(
|
|
337
|
+
`\n❌ Slot & event documentation audit failed. Add the missing @slot/@fires ` +
|
|
338
|
+
`tags (or remove the phantom ones). See .cursor/rules/comments.mdc.`
|
|
339
|
+
);
|
|
340
|
+
process.exitCode = 1;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const suffix =
|
|
345
|
+
warnings.length > 0 ? ` (${warnings.length} non-blocking warning(s))` : '';
|
|
346
|
+
console.log(`\n✅ Slot & event documentation audit passed.${suffix}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
run().catch((error: unknown) => {
|
|
350
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
351
|
+
console.error(`❌ Slot & event documentation audit failed: ${message}`);
|
|
352
|
+
process.exitCode = 1;
|
|
353
|
+
});
|
package/src/ar/poi/poi.ts
CHANGED
|
@@ -164,7 +164,8 @@ const POINT_POINTER_OFFSET_PX = 12;
|
|
|
164
164
|
* ## Slots/Content
|
|
165
165
|
*
|
|
166
166
|
* - Default slot: Main icon/content rendered inside `obc-poi-button`.
|
|
167
|
-
* - `
|
|
167
|
+
* - `button`: Optional custom button element replacing the default `obc-poi-button`.
|
|
168
|
+
* - `header`: Optional header content. Relocated into the inner `obc-poi-button` at runtime by a `MutationObserver` (there is no `<slot name="header">` element).
|
|
168
169
|
*
|
|
169
170
|
* ## Events
|
|
170
171
|
*
|
|
@@ -185,7 +186,8 @@ const POINT_POINTER_OFFSET_PX = 12;
|
|
|
185
186
|
* ```
|
|
186
187
|
*
|
|
187
188
|
* @slot - Default POI button content.
|
|
188
|
-
* @slot
|
|
189
|
+
* @slot button - Optional custom button element replacing the default `obc-poi-button`.
|
|
190
|
+
* @slot header - Optional header content, relocated into the inner `obc-poi-button` at runtime.
|
|
189
191
|
* @experimental
|
|
190
192
|
*/
|
|
191
193
|
@customElement('obc-poi')
|
|
@@ -90,6 +90,7 @@ export enum OverlapMode {
|
|
|
90
90
|
*
|
|
91
91
|
* @slot - Default slot for POI targets and optional POI groups.
|
|
92
92
|
* @fires layer-resize {CustomEvent<{height:number,label:string}>} Fired when the layer height changes.
|
|
93
|
+
* @fires layer-selection-changed {Event} Fired when the layer's `isSelected` state changes. Bubbles.
|
|
93
94
|
* @experimental
|
|
94
95
|
*/
|
|
95
96
|
@customElement('obc-poi-layer')
|
|
@@ -51,6 +51,8 @@ const ALERT_BADGE_TYPES: ObcAutomationBadgeType[] = [
|
|
|
51
51
|
];
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
+
* @slot icon-silhouette - Custom silhouette/outline icon, rendered behind the default slot content when no built-in badge type matches.
|
|
55
|
+
* @slot - Default slot for custom badge content, rendered when no built-in badge type matches.
|
|
54
56
|
* @stable
|
|
55
57
|
*/
|
|
56
58
|
@customElement('obc-automation-badge')
|
|
@@ -97,6 +97,15 @@ export enum AutomationButtonPositioning {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
+
* @slot badge-top-right - Content projected into the top-right badge position of the button.
|
|
101
|
+
* @slot badge-top-left - Content projected into the top-left badge position of the button.
|
|
102
|
+
* @slot badge-bottom-left - Content projected into the bottom-left badge position of the button.
|
|
103
|
+
* @slot badge-bottom-right - Content projected into the bottom-right badge position of the button.
|
|
104
|
+
* @slot icon - Primary icon shown inside the button (regular, double, flat and square variants).
|
|
105
|
+
* @slot icon-silhouette - Silhouette/outline icon shown behind the primary icon (flat variant only).
|
|
106
|
+
* @slot alert-icon - Custom icon for the alert frame, shown when alert==true and positioning=="point".
|
|
107
|
+
* @slot alert-label - Label for the alert frame, shown when alert==true and positioning=="point".
|
|
108
|
+
* @slot alert-timer - Timer for the alert frame, shown when alert==true and positioning=="point".
|
|
100
109
|
* @stable
|
|
101
110
|
*/
|
|
102
111
|
@customElement('obc-automation-button')
|
|
@@ -3,6 +3,10 @@ import {LitElement, html, unsafeCSS} from 'lit';
|
|
|
3
3
|
import compentStyle from './automation-input-modal.css?inline';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
+
* @slot header - Header content shown at the top of the modal.
|
|
7
|
+
* @slot preview - Preview content shown in the body of the modal.
|
|
8
|
+
* @slot action-primary - Primary action control shown in the actions row.
|
|
9
|
+
* @slot action-secondary - Secondary action control shown in the actions row.
|
|
6
10
|
* @experimental
|
|
7
11
|
*/
|
|
8
12
|
@customElement('obc-automation-input-modal')
|
|
@@ -488,70 +488,14 @@
|
|
|
488
488
|
margin-top: 8px;
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
padding-top: 8px;
|
|
500
|
-
white-space: nowrap;
|
|
501
|
-
@mixin font-instrument-unit;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
.rich-row {
|
|
505
|
-
display: grid;
|
|
506
|
-
grid-template-columns: subgrid;
|
|
507
|
-
grid-column: 1 / -1;
|
|
508
|
-
align-items: baseline;
|
|
509
|
-
padding-left: var(
|
|
510
|
-
--instrument-components-readout-new-list-item-regular-container-padding-horizontal
|
|
511
|
-
);
|
|
512
|
-
padding-right: var(
|
|
513
|
-
--instrument-components-readout-new-list-item-regular-container-padding-horizontal
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
.rich-label {
|
|
518
|
-
@mixin font-instrument-label;
|
|
519
|
-
color: var(--element-inactive-color);
|
|
520
|
-
padding-right: 8px;
|
|
521
|
-
text-align: left;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
.rich-value {
|
|
525
|
-
@mixin font-instrument-value-regular-neutral;
|
|
526
|
-
color: var(--element-neutral-color);
|
|
527
|
-
font-variant-numeric: tabular-nums;
|
|
528
|
-
text-align: right;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
.rich-suffix {
|
|
532
|
-
display: inline-flex;
|
|
533
|
-
align-items: baseline;
|
|
534
|
-
/* Default 2px gap between value and unit when there is no degree/percent
|
|
535
|
-
* glyph. When a glyph is present it sits flush against the value and the
|
|
536
|
-
* unit follows flush after the glyph (see `.rich-glyph + .rich-unit`). */
|
|
537
|
-
padding-left: 2px;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
.rich-suffix:has(.rich-glyph) {
|
|
541
|
-
padding-left: 0;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
.rich-glyph {
|
|
545
|
-
@mixin font-instrument-value-regular-neutral;
|
|
546
|
-
color: var(--element-neutral-color);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
.rich-unit {
|
|
550
|
-
color: var(--element-inactive-color);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
.rich-glyph + .rich-unit {
|
|
554
|
-
padding-left: 0;
|
|
491
|
+
/* Rich detail rows are consumer-slotted into `slot="rich"` — canonical usage is
|
|
492
|
+
* an `<obc-readout-list>` of `<obc-readout-list-item>` rows, which owns its own
|
|
493
|
+
* cap-height (textbox) typography and cross-row column alignment. The tank only
|
|
494
|
+
* spaces the slotted content below the divider; the divider itself is shown only
|
|
495
|
+
* when the slot is filled (`?hidden` bound to `_hasRichSlot`). */
|
|
496
|
+
::slotted(obc-readout-list) {
|
|
497
|
+
display: block;
|
|
498
|
+
margin-top: 8px;
|
|
555
499
|
}
|
|
556
500
|
|
|
557
501
|
/* The chart cell is a plain sizing/clipping box. Both child renderers —
|
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
TankPositioning,
|
|
7
7
|
TankTrend,
|
|
8
8
|
TankType,
|
|
9
|
-
type TankReadoutItem,
|
|
10
9
|
} from './automation-tank.js';
|
|
11
10
|
import './automation-tank.js';
|
|
12
|
-
import
|
|
11
|
+
import '../../navigation-instruments/readout-list/readout-list.js';
|
|
12
|
+
import '../../navigation-instruments/readout-list-item/readout-list-item.js';
|
|
13
|
+
import {html, nothing} from 'lit';
|
|
13
14
|
import {crossDecorator} from '../../storybook-util.js';
|
|
14
15
|
import {AdviceType} from '../../navigation-instruments/watch/advice.js';
|
|
15
16
|
import type {LinearAdvice} from '../../building-blocks/instrument-linear/advice.js';
|
|
@@ -57,13 +58,37 @@ const SAMPLE_ADVICE: LinearAdvice[] = [
|
|
|
57
58
|
{min: 7500, max: 9000, type: AdviceType.advice, hinted: false},
|
|
58
59
|
];
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
// Canonical rich-detail content for the tank: an `<obc-readout-list>` of
|
|
62
|
+
// `<obc-readout-list-item>` rows slotted into `slot="rich"`. The list owns the
|
|
63
|
+
// cross-row column alignment and cap-height typography, replacing the tank's
|
|
64
|
+
// former hand-rolled `.rich` grid.
|
|
65
|
+
const renderRichReadout = () => html`
|
|
66
|
+
<obc-readout-list slot="rich">
|
|
67
|
+
<obc-readout-list-item
|
|
68
|
+
.label=${'Temperature'}
|
|
69
|
+
.value=${45}
|
|
70
|
+
.hasDegree=${true}
|
|
71
|
+
.unit=${'C'}
|
|
72
|
+
></obc-readout-list-item>
|
|
73
|
+
<obc-readout-list-item
|
|
74
|
+
.label=${'Pressure'}
|
|
75
|
+
.value=${45}
|
|
76
|
+
.unit=${'Pa'}
|
|
77
|
+
></obc-readout-list-item>
|
|
78
|
+
<obc-readout-list-item
|
|
79
|
+
.label=${'Flow speed'}
|
|
80
|
+
.value=${45}
|
|
81
|
+
.unit=${'m/s'}
|
|
82
|
+
></obc-readout-list-item>
|
|
83
|
+
</obc-readout-list>
|
|
84
|
+
`;
|
|
65
85
|
|
|
66
|
-
|
|
86
|
+
// `richReadout` is slotted into `slot="rich"`; defaults to `nothing` so most
|
|
87
|
+
// stories render no detail rows. Kept as a separate helper (not the meta
|
|
88
|
+
// `render`) because Storybook calls `render(args, context)` with a second
|
|
89
|
+
// argument — `renderTank` below wraps it so that extra arg can't leak in as
|
|
90
|
+
// slotted content.
|
|
91
|
+
const renderTankEl = (args: StoryArgs, richReadout: unknown = nothing) => html`
|
|
67
92
|
<obc-automation-tank
|
|
68
93
|
.value=${args.value}
|
|
69
94
|
.max=${args.max}
|
|
@@ -82,7 +107,6 @@ const renderTank = (args: StoryArgs) => html`
|
|
|
82
107
|
.hasGraphIcon=${args.hasGraphIcon}
|
|
83
108
|
.showTrendSymbol=${args.showTrendSymbol}
|
|
84
109
|
.percentFractionDigits=${args.percentFractionDigits}
|
|
85
|
-
.readout=${args.readout}
|
|
86
110
|
?alert=${args.alert}
|
|
87
111
|
.alertFrameType=${args.alertFrameType}
|
|
88
112
|
.alertFrameThickness=${args.alertFrameThickness}
|
|
@@ -95,9 +119,12 @@ const renderTank = (args: StoryArgs) => html`
|
|
|
95
119
|
.badgeCommandLocked=${args.badgeCommandLocked}
|
|
96
120
|
.priority=${args.priority}
|
|
97
121
|
>
|
|
122
|
+
${richReadout}
|
|
98
123
|
</obc-automation-tank>
|
|
99
124
|
`;
|
|
100
125
|
|
|
126
|
+
const renderTank = (args: StoryArgs) => renderTankEl(args);
|
|
127
|
+
|
|
101
128
|
const meta: Meta<StoryArgs> = {
|
|
102
129
|
title: 'Automation/Tanks/Tank',
|
|
103
130
|
tags: ['autodocs', '6.0'],
|
|
@@ -120,7 +147,6 @@ const meta: Meta<StoryArgs> = {
|
|
|
120
147
|
hasGraphIcon: false,
|
|
121
148
|
showTrendSymbol: true,
|
|
122
149
|
percentFractionDigits: 0,
|
|
123
|
-
readout: [],
|
|
124
150
|
alert: false,
|
|
125
151
|
alertFrameType: ObcAlertFrameType.SmallSideFlip,
|
|
126
152
|
alertFrameThickness: ObcAlertFrameThickness.Small,
|
|
@@ -193,11 +219,6 @@ const meta: Meta<StoryArgs> = {
|
|
|
193
219
|
description:
|
|
194
220
|
'Advice overlay bands. `min`/`max` are in the same units as `max`. Toggle visibility with `hasAdvice`. Works in all three `chartMode` variants — `bar` overlays advice pills on the static bar, `graph` and `graph-and-bar` forward them to the embedded `obc-gauge-trend`.',
|
|
195
221
|
},
|
|
196
|
-
readout: {
|
|
197
|
-
control: {type: 'object'},
|
|
198
|
-
description:
|
|
199
|
-
'Rich detail rows shown below the main percent/value block in the regular (non-compact, non-static) layout, separated by a divider. Each row: `{label, value, hasDegree?, hasPercentage?, unit}`. Empty array — nothing is rendered. Values use `percentFractionDigits` for formatting. Override the whole block via `slot="rich"`.',
|
|
200
|
-
},
|
|
201
222
|
badgeControl: {
|
|
202
223
|
options: Object.values(AutomationButtonBadgeControl),
|
|
203
224
|
control: {type: 'select'},
|
|
@@ -405,8 +426,8 @@ export const Rich: Story = {
|
|
|
405
426
|
args: {
|
|
406
427
|
type: TankType.atmospheric,
|
|
407
428
|
chartMode: TankChartMode.graphAndBar,
|
|
408
|
-
readout: SAMPLE_READOUT,
|
|
409
429
|
},
|
|
430
|
+
render: (args) => renderTankEl(args, renderRichReadout()),
|
|
410
431
|
};
|
|
411
432
|
|
|
412
433
|
export const HorizontalRich: Story = {
|
|
@@ -414,8 +435,8 @@ export const HorizontalRich: Story = {
|
|
|
414
435
|
type: TankType.atmospheric,
|
|
415
436
|
orientation: TankOrientation.horizontal,
|
|
416
437
|
chartMode: TankChartMode.graphAndBar,
|
|
417
|
-
readout: SAMPLE_READOUT,
|
|
418
438
|
},
|
|
439
|
+
render: (args) => renderTankEl(args, renderRichReadout()),
|
|
419
440
|
};
|
|
420
441
|
|
|
421
442
|
/**
|
|
@@ -431,9 +452,9 @@ export const HorizontalRich: Story = {
|
|
|
431
452
|
* and always render it as an integer to keep their fixed-width footprint
|
|
432
453
|
* stable, so fraction-digit control only applies to the non-compact layout.
|
|
433
454
|
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
455
|
+
* The rich detail rows are now consumer-slotted (`slot="rich"`, canonically an
|
|
456
|
+
* `<obc-readout-list>`); each `<obc-readout-list-item>` owns its own
|
|
457
|
+
* `fractionDigits`, so `percentFractionDigits` no longer affects them.
|
|
437
458
|
*/
|
|
438
459
|
export const WithFractionDigits: Story = {
|
|
439
460
|
args: {
|
|
@@ -463,7 +484,6 @@ export const WithFractionDigits: Story = {
|
|
|
463
484
|
.showTrendSymbol=${args.showTrendSymbol}
|
|
464
485
|
.priority=${args.priority}
|
|
465
486
|
.percentFractionDigits=${args.percentFractionDigits}
|
|
466
|
-
.readout=${args.readout}
|
|
467
487
|
>
|
|
468
488
|
<span slot="current-value">${args.value.toFixed(2)}</span>
|
|
469
489
|
<span slot="max-value">${args.max.toFixed(2)}</span>
|
|
@@ -502,7 +522,6 @@ export const WithAlertAlarm: Story = {
|
|
|
502
522
|
.hasGraphIcon=${args.hasGraphIcon}
|
|
503
523
|
.showTrendSymbol=${args.showTrendSymbol}
|
|
504
524
|
.percentFractionDigits=${args.percentFractionDigits}
|
|
505
|
-
.readout=${args.readout}
|
|
506
525
|
?alert=${args.alert}
|
|
507
526
|
.alertFrameType=${args.alertFrameType}
|
|
508
527
|
.alertFrameThickness=${args.alertFrameThickness}
|
|
@@ -625,7 +644,6 @@ export const Responsive: Story = {
|
|
|
625
644
|
.hasGraphIcon=${args.hasGraphIcon}
|
|
626
645
|
.showTrendSymbol=${args.showTrendSymbol}
|
|
627
646
|
.percentFractionDigits=${args.percentFractionDigits}
|
|
628
|
-
.readout=${args.readout}
|
|
629
647
|
?alert=${args.alert}
|
|
630
648
|
.alertFrameType=${args.alertFrameType}
|
|
631
649
|
.alertFrameThickness=${args.alertFrameThickness}
|