@incursa/ui-kit 1.0.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI-AGENT-INSTRUCTIONS.md +49 -28
- package/LLMS.txt +58 -42
- package/README.md +181 -68
- package/dist/inc-design-language.css +655 -251
- package/dist/inc-design-language.css.map +1 -1
- package/dist/inc-design-language.js +520 -0
- package/dist/inc-design-language.min.css +1 -1
- package/dist/inc-design-language.min.css.map +1 -1
- package/dist/web-components/README.md +92 -0
- package/dist/web-components/RUNTIME-NOTES.md +40 -0
- package/dist/web-components/base-element.js +193 -0
- package/dist/web-components/components/feedback.js +1074 -0
- package/dist/web-components/components/forms.js +979 -0
- package/dist/web-components/components/layout.js +408 -0
- package/dist/web-components/components/navigation.js +854 -0
- package/dist/web-components/components/overlays.js +634 -0
- package/dist/web-components/controllers/focus.js +101 -0
- package/dist/web-components/controllers/overlay.js +128 -0
- package/dist/web-components/controllers/selection.js +145 -0
- package/dist/web-components/controllers/theme.js +173 -0
- package/dist/web-components/index.js +887 -0
- package/dist/web-components/package.json +3 -0
- package/dist/web-components/registry.js +74 -0
- package/dist/web-components/shared.js +186 -0
- package/dist/web-components/style.css +6 -0
- package/package.json +11 -2
- package/src/inc-design-language.js +520 -0
- package/src/inc-design-language.scss +692 -258
- package/src/web-components/README.md +92 -0
- package/src/web-components/RUNTIME-NOTES.md +40 -0
- package/src/web-components/base-element.js +193 -0
- package/src/web-components/components/feedback.js +1074 -0
- package/src/web-components/components/forms.js +979 -0
- package/src/web-components/components/layout.js +408 -0
- package/src/web-components/components/navigation.js +854 -0
- package/src/web-components/components/overlays.js +634 -0
- package/src/web-components/controllers/focus.js +101 -0
- package/src/web-components/controllers/overlay.js +128 -0
- package/src/web-components/controllers/selection.js +145 -0
- package/src/web-components/controllers/theme.js +173 -0
- package/src/web-components/index.js +887 -0
- package/src/web-components/package.json +3 -0
- package/src/web-components/registry.js +74 -0
- package/src/web-components/shared.js +186 -0
- package/src/web-components/style.css +6 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Web Components Runtime Notes
|
|
2
|
+
|
|
3
|
+
This folder contains the additive browser-native layer for `@incursa/ui-kit`.
|
|
4
|
+
|
|
5
|
+
The CSS-first [`inc-*`](../../reference.html) class surface remains the canonical API. The Web Component layer exists so the same design language can also be consumed from plain HTML, JavaScript, and browser-native primitives without creating a second design system.
|
|
6
|
+
|
|
7
|
+
## Public Entry Point
|
|
8
|
+
|
|
9
|
+
- Package entrypoint: `@incursa/ui-kit/web-components`
|
|
10
|
+
- Style entrypoint: `@incursa/ui-kit/web-components/style.css`
|
|
11
|
+
- Built output: `dist/web-components/`
|
|
12
|
+
- Package export: `./web-components` resolves to `dist/web-components/index.js`
|
|
13
|
+
- Package export: `./web-components/style.css` resolves to `dist/web-components/style.css`
|
|
14
|
+
- Module boundary: `src/web-components/package.json` sets this subtree to `type: module`
|
|
15
|
+
|
|
16
|
+
Load these entrypoints only when the consuming app wants the custom elements and their default look. CSS-only consumers should not pay for the runtime.
|
|
17
|
+
|
|
18
|
+
Recommended browser usage:
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<link rel="stylesheet" href="/node_modules/@incursa/ui-kit/web-components/style.css">
|
|
22
|
+
<script type="module">
|
|
23
|
+
import "@incursa/ui-kit/web-components";
|
|
24
|
+
</script>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The runtime auto-defines the shipped elements on load. If a consumer needs explicit registry control, `registerIncWebComponents()` remains available.
|
|
28
|
+
|
|
29
|
+
## Runtime Rules
|
|
30
|
+
|
|
31
|
+
- Keep the layer additive.
|
|
32
|
+
- Reuse the current design tokens, CSS vocabulary, and helper behavior where they already exist.
|
|
33
|
+
- Prefer light DOM and slotted native content by default.
|
|
34
|
+
- Use open Shadow DOM only when a component needs a stable internal scaffold that materially improves correctness or maintainability.
|
|
35
|
+
- Keep public tag names in the [`inc-`](../../reference.html) namespace and align them with the CSS family names whenever possible.
|
|
36
|
+
- Expose public state through attributes and mirrored properties only when that state belongs in the contract.
|
|
37
|
+
- Dispatch DOM events instead of framework callbacks.
|
|
38
|
+
- Clean up observers, timers, and listeners on disconnect.
|
|
39
|
+
|
|
40
|
+
## Current Source Layout
|
|
41
|
+
|
|
42
|
+
- [`shared.js`](shared.js)
|
|
43
|
+
Shared parsing, reflection, event, and namespace helpers.
|
|
44
|
+
- [`base-element.js`](base-element.js)
|
|
45
|
+
The `IncElement` base class, including reflected attribute/property wiring and slot helpers.
|
|
46
|
+
- [`registry.js`](registry.js)
|
|
47
|
+
Idempotent registration helpers and the `IncWebComponents.registry` namespace.
|
|
48
|
+
- [`controllers/focus.js`](controllers/focus.js)
|
|
49
|
+
Shared focus utilities for focus trapping, focus restoration, and focusable-element discovery.
|
|
50
|
+
- [`controllers/selection.js`](controllers/selection.js)
|
|
51
|
+
Roving tabindex and keyboard navigation helpers for selection-based widgets.
|
|
52
|
+
- [`controllers/overlay.js`](controllers/overlay.js)
|
|
53
|
+
Shared open/close, escape, backdrop, and focus-restoration behavior for overlays.
|
|
54
|
+
- [`controllers/theme.js`](controllers/theme.js)
|
|
55
|
+
Shared root-theme helpers and the legacy bridge used by theme controls.
|
|
56
|
+
- [`components/layout.js`](components/layout.js)
|
|
57
|
+
Layout and shell custom elements.
|
|
58
|
+
- [`components/navigation.js`](components/navigation.js)
|
|
59
|
+
Navbar, tabs, and user-menu custom elements.
|
|
60
|
+
- [`components/forms.js`](components/forms.js)
|
|
61
|
+
Field wrappers, input groups, choice groups, read-only fields, and validation summary custom elements.
|
|
62
|
+
- [`components/feedback.js`](components/feedback.js)
|
|
63
|
+
State panel, live-region, auto-refresh, and theme-switcher custom elements.
|
|
64
|
+
- [`components/overlays.js`](components/overlays.js)
|
|
65
|
+
Disclosure, dialog, and drawer custom elements.
|
|
66
|
+
- [`index.js`](index.js)
|
|
67
|
+
Additive package bootstrap that registers the shipped families and exposes the public namespace.
|
|
68
|
+
|
|
69
|
+
As more families land, keep them in this same shape: small shared controllers plus family modules that register their public elements through the registry.
|
|
70
|
+
|
|
71
|
+
## Design Intent
|
|
72
|
+
|
|
73
|
+
The Web Component layer should mirror the current CSS kit, not reinterpret it.
|
|
74
|
+
|
|
75
|
+
- Layout primitives should stay composable and slot-driven.
|
|
76
|
+
- Form wrappers should keep native controls native.
|
|
77
|
+
- Navigation components should reflect keyboard and focus state in the DOM.
|
|
78
|
+
- Feedback and status shells should announce state accessibly.
|
|
79
|
+
- Overlays should prefer native `<details>` and `<dialog>` behavior when that satisfies the contract.
|
|
80
|
+
- Tables, data presentation, utility atoms, and other presentation-only surfaces should remain CSS-first until the component contract is explicit and worth the runtime cost.
|
|
81
|
+
|
|
82
|
+
## Maintenance Notes
|
|
83
|
+
|
|
84
|
+
When you add or change a component:
|
|
85
|
+
|
|
86
|
+
1. Reuse `IncElement` or the shared controllers before inventing new behavior.
|
|
87
|
+
2. Keep the public contract declarative and observable in the DOM.
|
|
88
|
+
3. Update the package docs and the browser examples together.
|
|
89
|
+
4. Add or update Playwright coverage for keyboard, focus, dismissal, theming, and accessibility behavior.
|
|
90
|
+
5. Do not introduce a second token family or styling vocabulary.
|
|
91
|
+
|
|
92
|
+
The CSS class surface and the Web Component layer are meant to evolve together. If a change cannot be explained in terms of the existing [`inc-*`](../../reference.html) vocabulary, it is probably the wrong change.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Web Components Runtime Notes
|
|
2
|
+
|
|
3
|
+
This folder ships the optional `./web-components` entrypoint for the UI kit.
|
|
4
|
+
|
|
5
|
+
## v1 Scope
|
|
6
|
+
|
|
7
|
+
The runtime defines the approved v1 host family set:
|
|
8
|
+
|
|
9
|
+
- layouts and shells: `inc-app-shell`, `inc-page`, `inc-page-header`, `inc-section`, `inc-card`, `inc-summary-overview`, `inc-summary-block`, `inc-footer-bar`
|
|
10
|
+
- navigation: `inc-navbar`, `inc-tabs`, `inc-user-menu`
|
|
11
|
+
- forms and inputs: `inc-field`, `inc-input-group`, `inc-choice-group`, `inc-readonly-field`, `inc-validation-summary`
|
|
12
|
+
- feedback and status: `inc-state-panel`, `inc-live-region`, `inc-auto-refresh`, `inc-theme-switcher`
|
|
13
|
+
- overlays: `inc-disclosure`, `inc-dialog`, `inc-drawer`
|
|
14
|
+
|
|
15
|
+
## Contract shape
|
|
16
|
+
|
|
17
|
+
- CSS-first is still canonical. Components reuse existing `inc-*` class contracts.
|
|
18
|
+
- Package consumers should pair `@incursa/ui-kit/web-components` with `@incursa/ui-kit/web-components/style.css` when they want the default look out of the box.
|
|
19
|
+
- v1 stays light DOM first so current style selectors keep working.
|
|
20
|
+
- Native primitives are used for disclosure/menu/dialog behavior where practical.
|
|
21
|
+
- `index.js` is a thin bootstrap that registers family modules:
|
|
22
|
+
- `components/layout.js`
|
|
23
|
+
- `components/navigation.js`
|
|
24
|
+
- `components/forms.js`
|
|
25
|
+
- `components/feedback.js`
|
|
26
|
+
- `components/overlays.js`
|
|
27
|
+
- Public registration API is idempotent:
|
|
28
|
+
- `window.IncWebComponents.defineAll()`
|
|
29
|
+
- `window.IncWebComponents.registerIncWebComponents()`
|
|
30
|
+
- The dedicated entrypoint auto-defines components on load.
|
|
31
|
+
|
|
32
|
+
## Explicitly deferred surfaces in v1
|
|
33
|
+
|
|
34
|
+
- tooltip and popover components
|
|
35
|
+
- permission-banner and toast runtime orchestration
|
|
36
|
+
- table/data wrappers and grid-like behavior
|
|
37
|
+
- filter/file/bulk workflow wrappers
|
|
38
|
+
- legacy helper-managed modal/offcanvas compatibility wrappers
|
|
39
|
+
|
|
40
|
+
Those surfaces remain CSS-first until a follow-up requirement pass promotes them.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import {
|
|
2
|
+
dispatchComponentEvent,
|
|
3
|
+
getAssignedSlotElements,
|
|
4
|
+
normalizeAttributeConfig,
|
|
5
|
+
parseValueFromAttribute,
|
|
6
|
+
readReflectedAttribute,
|
|
7
|
+
reflectAttributeValue,
|
|
8
|
+
serializeValueForAttribute,
|
|
9
|
+
} from "./shared.js";
|
|
10
|
+
|
|
11
|
+
const HostElement = typeof HTMLElement === "undefined" ? class {} : HTMLElement;
|
|
12
|
+
const metadataByConstructor = new WeakMap();
|
|
13
|
+
|
|
14
|
+
function buildMetadata(constructor) {
|
|
15
|
+
if (metadataByConstructor.has(constructor)) {
|
|
16
|
+
return metadataByConstructor.get(constructor);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const reflectedConfig = constructor.reflectedAttributes || {};
|
|
20
|
+
const propertyToConfig = new Map();
|
|
21
|
+
const attributeToConfig = new Map();
|
|
22
|
+
|
|
23
|
+
Object.keys(reflectedConfig).forEach((property) => {
|
|
24
|
+
const normalized = normalizeAttributeConfig(property, reflectedConfig[property]);
|
|
25
|
+
propertyToConfig.set(property, normalized);
|
|
26
|
+
attributeToConfig.set(normalized.attribute, normalized);
|
|
27
|
+
|
|
28
|
+
if (Object.prototype.hasOwnProperty.call(constructor.prototype, property)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Object.defineProperty(constructor.prototype, property, {
|
|
33
|
+
configurable: true,
|
|
34
|
+
enumerable: true,
|
|
35
|
+
get() {
|
|
36
|
+
return this._propertyValues.get(property);
|
|
37
|
+
},
|
|
38
|
+
set(value) {
|
|
39
|
+
this._setReflectedPropertyValue(property, value, { reflect: true });
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const metadata = {
|
|
45
|
+
propertyToConfig,
|
|
46
|
+
attributeToConfig,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
metadataByConstructor.set(constructor, metadata);
|
|
50
|
+
return metadata;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class IncElement extends HostElement {
|
|
54
|
+
static reflectedAttributes = {};
|
|
55
|
+
|
|
56
|
+
static get observedAttributes() {
|
|
57
|
+
const metadata = buildMetadata(this);
|
|
58
|
+
return [...metadata.attributeToConfig.keys()];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
constructor() {
|
|
62
|
+
super();
|
|
63
|
+
this._propertyValues = new Map();
|
|
64
|
+
this._slotListeners = new Map();
|
|
65
|
+
this._isReflectingAttribute = false;
|
|
66
|
+
this._isConnected = false;
|
|
67
|
+
|
|
68
|
+
const metadata = buildMetadata(this.constructor);
|
|
69
|
+
|
|
70
|
+
metadata.propertyToConfig.forEach((config, property) => {
|
|
71
|
+
if (this.hasAttribute(config.attribute)) {
|
|
72
|
+
this._propertyValues.set(property, readReflectedAttribute(this, config));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this._propertyValues.set(property, config.defaultValue);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
connectedCallback() {
|
|
81
|
+
this._isConnected = true;
|
|
82
|
+
if (typeof this.onConnected === "function") {
|
|
83
|
+
this.onConnected();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
disconnectedCallback() {
|
|
88
|
+
this._isConnected = false;
|
|
89
|
+
this._slotListeners.forEach((listener, slot) => {
|
|
90
|
+
slot.removeEventListener("slotchange", listener);
|
|
91
|
+
});
|
|
92
|
+
this._slotListeners.clear();
|
|
93
|
+
|
|
94
|
+
if (typeof this.onDisconnected === "function") {
|
|
95
|
+
this.onDisconnected();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
100
|
+
if (oldValue === newValue) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const metadata = buildMetadata(this.constructor);
|
|
105
|
+
const config = metadata.attributeToConfig.get(name);
|
|
106
|
+
|
|
107
|
+
if (config) {
|
|
108
|
+
const parsed = parseValueFromAttribute(newValue, config);
|
|
109
|
+
this._setReflectedPropertyValue(config.property, parsed, { reflect: false });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (typeof this.onAttributeValueChanged === "function") {
|
|
113
|
+
this.onAttributeValueChanged(name, oldValue, newValue);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
emit(type, detail = {}, options = {}) {
|
|
118
|
+
return dispatchComponentEvent(this, type, detail, options);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getSlotElements(slotName = "") {
|
|
122
|
+
return getAssignedSlotElements(this, slotName);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
observeSlot(slotName = "", callback = null) {
|
|
126
|
+
const selector = slotName ? `slot[name="${slotName}"]` : "slot:not([name])";
|
|
127
|
+
const slot = this.querySelector(selector);
|
|
128
|
+
|
|
129
|
+
if (typeof HTMLSlotElement === "undefined" || !(slot instanceof HTMLSlotElement)) {
|
|
130
|
+
return () => {};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const listener = () => {
|
|
134
|
+
if (typeof callback === "function") {
|
|
135
|
+
callback(this.getSlotElements(slotName));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
slot.addEventListener("slotchange", listener);
|
|
140
|
+
this._slotListeners.set(slot, listener);
|
|
141
|
+
listener();
|
|
142
|
+
|
|
143
|
+
return () => {
|
|
144
|
+
slot.removeEventListener("slotchange", listener);
|
|
145
|
+
this._slotListeners.delete(slot);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
reflectAllProperties() {
|
|
150
|
+
const metadata = buildMetadata(this.constructor);
|
|
151
|
+
metadata.propertyToConfig.forEach((config, property) => {
|
|
152
|
+
const value = this._propertyValues.get(property);
|
|
153
|
+
if (!config.reflect) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const serialized = serializeValueForAttribute(value, config);
|
|
158
|
+
reflectAttributeValue(this, config.attribute, serialized);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_setReflectedPropertyValue(property, value, options = {}) {
|
|
163
|
+
const metadata = buildMetadata(this.constructor);
|
|
164
|
+
const config = metadata.propertyToConfig.get(property);
|
|
165
|
+
|
|
166
|
+
if (!config) {
|
|
167
|
+
this._propertyValues.set(property, value);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const previousValue = this._propertyValues.get(property);
|
|
172
|
+
if (Object.is(previousValue, value)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this._propertyValues.set(property, value);
|
|
177
|
+
|
|
178
|
+
if (options.reflect !== false && config.reflect && !this._isReflectingAttribute) {
|
|
179
|
+
const serialized = serializeValueForAttribute(value, config);
|
|
180
|
+
this._isReflectingAttribute = true;
|
|
181
|
+
reflectAttributeValue(this, config.attribute, serialized);
|
|
182
|
+
this._isReflectingAttribute = false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (typeof this.onPropertyValueChanged === "function") {
|
|
186
|
+
this.onPropertyValueChanged(property, previousValue, value);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export {
|
|
192
|
+
IncElement,
|
|
193
|
+
};
|