@primitiv-ui/web 0.1.43 → 0.1.44
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 +49 -6
- package/dist/accordion/AccordionController.d.ts +33 -0
- package/dist/accordion/AccordionController.js +41 -0
- package/dist/accordion/elements.d.ts +79 -0
- package/dist/accordion/elements.js +194 -0
- package/dist/accordion/index.d.ts +2 -0
- package/dist/accordion/index.js +2 -0
- package/dist/cdn/accordion.js +1 -0
- package/dist/cdn/chunks/chunk-FKLOMAT4.js +1 -0
- package/dist/cdn/tabs.js +1 -1
- package/dist/custom-elements.json +308 -48
- package/dist/define/accordion.d.ts +1 -0
- package/dist/define/accordion.js +11 -0
- package/dist/define/index.d.ts +1 -0
- package/dist/define/index.js +1 -0
- package/dist/frameworks/events.d.ts +4 -0
- package/dist/frameworks/react.d.ts +20 -2
- package/dist/frameworks/svelte.d.ts +19 -1
- package/dist/frameworks/vue.d.ts +20 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/internal/RootElement.d.ts +46 -0
- package/dist/internal/RootElement.js +59 -0
- package/dist/internal/TriggerController.d.ts +52 -0
- package/dist/internal/TriggerController.js +76 -0
- package/dist/internal/element.d.ts +38 -0
- package/dist/internal/element.js +60 -0
- package/dist/primitiv-web.js +1 -1
- package/dist/tabs/TabsController.d.ts +6 -31
- package/dist/tabs/TabsController.js +11 -79
- package/dist/tabs/elements.d.ts +13 -26
- package/dist/tabs/elements.js +22 -100
- package/dist/tags.d.ts +6 -0
- package/package.json +10 -2
package/dist/tabs/elements.js
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
import { deriveId, tabsTabStop, } from "@primitiv-ui/core";
|
|
2
|
+
import { defineElements, ElementBase, reportOnce, write } from "../internal/element.js";
|
|
3
|
+
import { PrimitivRootElement } from "../internal/RootElement.js";
|
|
2
4
|
import { TabsController } from "./TabsController.js";
|
|
3
|
-
/**
|
|
4
|
-
* The base class every element extends. It falls back to an empty class where
|
|
5
|
-
* the DOM does not exist, so a server renderer (Next.js, Nuxt, Angular SSR,
|
|
6
|
-
* Astro) can import this module without `HTMLElement` throwing at load time
|
|
7
|
-
* (RFC 0033 D21).
|
|
8
|
-
*/
|
|
9
|
-
const ElementBase = globalThis.HTMLElement ?? class {
|
|
10
|
-
};
|
|
11
5
|
// Stryker disable StringLiteral: static — evaluated once at import, so runtime mutant switching never reaches them; the names are pinned by the defineTabs registry test instead.
|
|
12
6
|
const ROOT = "primitiv-tabs";
|
|
13
7
|
const LIST = "primitiv-tabs-list";
|
|
14
8
|
const TRIGGER = "primitiv-tabs-trigger";
|
|
15
9
|
const CONTENT = "primitiv-tabs-content";
|
|
16
10
|
// Stryker restore StringLiteral
|
|
17
|
-
let generatedIds = 0;
|
|
18
|
-
/** Triggers already reported as misused, so each is reported once, not per reconcile. */
|
|
19
|
-
const reported = new WeakSet();
|
|
20
11
|
/**
|
|
21
12
|
* An interactive part decorates exactly one element child and needs a `value`.
|
|
22
13
|
* Anything else is reported once and left undecorated rather than thrown: a
|
|
@@ -27,37 +18,17 @@ function decoratable(trigger) {
|
|
|
27
18
|
if (trigger.childElementCount === 1 && value !== null) {
|
|
28
19
|
return { tab: trigger.firstElementChild, value };
|
|
29
20
|
}
|
|
30
|
-
|
|
31
|
-
reported.add(trigger);
|
|
32
|
-
console.error(`<${TRIGGER}> needs a value and exactly one element child (a <button>, an <a href> or a router link) to decorate.`, trigger);
|
|
33
|
-
}
|
|
21
|
+
reportOnce(trigger, `<${TRIGGER}> needs a value and exactly one element child (a <button>, an <a href> or a router link) to decorate.`);
|
|
34
22
|
return null;
|
|
35
23
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Write an attribute only when it would change: `null` removes it. The elements
|
|
38
|
-
* write only the attributes in their contract (RFC 0033 §5.3), and comparing
|
|
39
|
-
* first means a value a framework or the server already rendered is never
|
|
40
|
-
* rewritten (D20).
|
|
41
|
-
*/
|
|
42
|
-
function write(element, name, value) {
|
|
43
|
-
if (element.getAttribute(name) === value)
|
|
44
|
-
return;
|
|
45
|
-
if (value === null)
|
|
46
|
-
element.removeAttribute(name);
|
|
47
|
-
else
|
|
48
|
-
element.setAttribute(name, value);
|
|
49
|
-
}
|
|
50
24
|
/**
|
|
51
25
|
* `<primitiv-tabs>` — the state owner of a WAI-ARIA tab set.
|
|
52
26
|
*
|
|
53
|
-
* The root finds its own parts,
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* root's own `value` and `orientation`, and it already delivers a task's changes
|
|
59
|
-
* as one callback, so no further batching is needed. `activation-mode` is read
|
|
60
|
-
* live at each key press, so a change to it needs no reconcile.
|
|
27
|
+
* The root finds its own parts, so a tab set nested in a panel keeps its own,
|
|
28
|
+
* and reconciles their ARIA from its reflected state whenever a part arrives,
|
|
29
|
+
* leaves, or changes an attribute the root reads (the lifecycle is
|
|
30
|
+
* `PrimitivRootElement`'s). `activation-mode` is read live at each key press,
|
|
31
|
+
* so a change to it needs no reconcile.
|
|
61
32
|
*
|
|
62
33
|
* @customElement primitiv-tabs
|
|
63
34
|
* @summary The state owner of a WAI-ARIA tab set.
|
|
@@ -66,37 +37,12 @@ function write(element, name, value) {
|
|
|
66
37
|
* @attr {"automatic" | "manual"} activation-mode - `automatic` activates a tab as focus arrives; `manual` waits for Enter or Space. Defaults to `automatic`.
|
|
67
38
|
* @fires {CustomEvent<{ value: string; index: number }>} primitiv-tabs-change - Bubbles, cancelable, fired before a user-driven change is applied. Call `preventDefault()` to veto it.
|
|
68
39
|
*/
|
|
69
|
-
export class PrimitivTabsElement extends
|
|
70
|
-
#generatedId;
|
|
40
|
+
export class PrimitivTabsElement extends PrimitivRootElement {
|
|
71
41
|
/** The value of the panel last shown, so a change fires primitiv-tabs-show. */
|
|
72
42
|
#shown;
|
|
73
43
|
#rendered = false;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
* Watches the subtree for parts arriving, leaving, or changing the attributes
|
|
77
|
-
* the root reads. The root's own writes (role, aria-*, ids, data-*) are not in
|
|
78
|
-
* the filter, so reconciling never re-triggers itself.
|
|
79
|
-
*/
|
|
80
|
-
#observer = new MutationObserver(() => this.#reconcile());
|
|
81
|
-
connectedCallback() {
|
|
82
|
-
this.#controller.connect();
|
|
83
|
-
this.#observer.observe(this, {
|
|
84
|
-
subtree: true,
|
|
85
|
-
childList: true,
|
|
86
|
-
attributes: true,
|
|
87
|
-
attributeFilter: ["value", "disabled", "label", "orientation"],
|
|
88
|
-
});
|
|
89
|
-
this.#reconcile();
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Leaving the document stops all listening. A move (an `hx-preserve`, a
|
|
93
|
-
* framework reparenting) is a disconnect followed by a connect, so handlers
|
|
94
|
-
* never stack.
|
|
95
|
-
*/
|
|
96
|
-
disconnectedCallback() {
|
|
97
|
-
this.#controller.disconnect();
|
|
98
|
-
this.#observer.disconnect();
|
|
99
|
-
}
|
|
44
|
+
controller = new TabsController(this, this.#host());
|
|
45
|
+
reconcileOn = ["value", "disabled", "label", "orientation"];
|
|
100
46
|
/** The active tab's value, reflected as the `value` attribute. */
|
|
101
47
|
get value() {
|
|
102
48
|
return this.getAttribute("value") ?? undefined;
|
|
@@ -104,27 +50,17 @@ export class PrimitivTabsElement extends ElementBase {
|
|
|
104
50
|
set value(next) {
|
|
105
51
|
this.setAttribute("value", next);
|
|
106
52
|
}
|
|
107
|
-
/**
|
|
108
|
-
* The controller's view of this element. State lives in reflected attributes;
|
|
109
|
-
* an activation is announced first, as a cancelable event, so a listener can
|
|
110
|
-
* veto it — the elements' equivalent of React's controlled mode (D7).
|
|
111
|
-
*/
|
|
53
|
+
/** The controller's view of this element: state lives in reflected attributes. */
|
|
112
54
|
#host() {
|
|
113
55
|
return {
|
|
114
56
|
state: () => ({
|
|
115
57
|
active: this.value,
|
|
116
58
|
orientation: this.#orientation(),
|
|
117
|
-
|
|
118
|
-
dir: this.matches(":dir(rtl)") ? "rtl" : "ltr",
|
|
59
|
+
dir: this.direction(),
|
|
119
60
|
activationMode: this.#activationMode(),
|
|
120
61
|
}),
|
|
121
62
|
activate: (value, index) => {
|
|
122
|
-
|
|
123
|
-
bubbles: true,
|
|
124
|
-
cancelable: true,
|
|
125
|
-
detail: { value, index },
|
|
126
|
-
});
|
|
127
|
-
if (this.dispatchEvent(change))
|
|
63
|
+
if (this.announce("primitiv-tabs-change", { value, index }))
|
|
128
64
|
this.value = value;
|
|
129
65
|
},
|
|
130
66
|
};
|
|
@@ -136,20 +72,12 @@ export class PrimitivTabsElement extends ElementBase {
|
|
|
136
72
|
#orientation() {
|
|
137
73
|
return this.getAttribute("orientation") === "vertical" ? "vertical" : "horizontal";
|
|
138
74
|
}
|
|
139
|
-
|
|
140
|
-
#parts(selector) {
|
|
141
|
-
return [...this.querySelectorAll(selector)].filter((part) => part.closest(ROOT) === this);
|
|
142
|
-
}
|
|
143
|
-
#rootId() {
|
|
144
|
-
// Stryker disable next-line UpdateOperator: equivalent — counting down yields ids just as unique.
|
|
145
|
-
return this.id || (this.#generatedId ??= `${ROOT}-${++generatedIds}`);
|
|
146
|
-
}
|
|
147
|
-
#reconcile() {
|
|
75
|
+
reconcile() {
|
|
148
76
|
const orientation = this.#orientation();
|
|
149
77
|
const active = this.value;
|
|
150
|
-
const rootId = this
|
|
78
|
+
const rootId = this.rootId();
|
|
151
79
|
write(this, "data-orientation", orientation);
|
|
152
|
-
for (const list of this
|
|
80
|
+
for (const list of this.parts(LIST)) {
|
|
153
81
|
write(list, "role", "tablist");
|
|
154
82
|
write(list, "aria-orientation", orientation);
|
|
155
83
|
write(list, "data-orientation", orientation);
|
|
@@ -158,19 +86,19 @@ export class PrimitivTabsElement extends ElementBase {
|
|
|
158
86
|
write(list, "aria-label", label);
|
|
159
87
|
}
|
|
160
88
|
const panels = new Map();
|
|
161
|
-
for (const panel of this
|
|
89
|
+
for (const panel of this.parts(CONTENT)) {
|
|
162
90
|
const value = panel.getAttribute("value");
|
|
163
91
|
if (value !== null)
|
|
164
92
|
panels.set(value, panel);
|
|
165
93
|
}
|
|
166
|
-
const triggers = this
|
|
94
|
+
const triggers = this.parts(TRIGGER).flatMap((trigger) => {
|
|
167
95
|
const part = decoratable(trigger);
|
|
168
96
|
return part ? [{ trigger, ...part }] : [];
|
|
169
97
|
});
|
|
170
98
|
const stop = tabsTabStop({ values: triggers.map((t) => t.value), active });
|
|
171
99
|
// Replace the registration wholesale: a removed part, or a trigger whose child
|
|
172
100
|
// a framework re-rendered, simply is not registered again.
|
|
173
|
-
this
|
|
101
|
+
this.controller.setTriggers(triggers.map(({ trigger, tab, value }) => ({
|
|
174
102
|
value,
|
|
175
103
|
element: tab,
|
|
176
104
|
disabled: trigger.hasAttribute("disabled"),
|
|
@@ -250,16 +178,10 @@ export class PrimitivTabsContentElement extends ElementBase {
|
|
|
250
178
|
* render).
|
|
251
179
|
*/
|
|
252
180
|
export function defineTabs(registry = globalThis.customElements) {
|
|
253
|
-
|
|
254
|
-
return;
|
|
255
|
-
const elements = [
|
|
181
|
+
defineElements(registry, [
|
|
256
182
|
[ROOT, PrimitivTabsElement],
|
|
257
183
|
[LIST, PrimitivTabsListElement],
|
|
258
184
|
[TRIGGER, PrimitivTabsTriggerElement],
|
|
259
185
|
[CONTENT, PrimitivTabsContentElement],
|
|
260
|
-
];
|
|
261
|
-
for (const [name, element] of elements) {
|
|
262
|
-
if (!registry.get(name))
|
|
263
|
-
registry.define(name, element);
|
|
264
|
-
}
|
|
186
|
+
]);
|
|
265
187
|
}
|
package/dist/tags.d.ts
CHANGED
|
@@ -12,9 +12,15 @@
|
|
|
12
12
|
* It is a separate, npm-only entry because it augments a global type, which
|
|
13
13
|
* JSR does not allow a published module to do (RFC 0033 §5.6.4).
|
|
14
14
|
*/
|
|
15
|
+
import type { PrimitivAccordionContentElement, PrimitivAccordionElement, PrimitivAccordionItemElement, PrimitivAccordionTriggerElement, PrimitivAccordionTriggerIconElement } from "./accordion/elements.js";
|
|
15
16
|
import type { PrimitivTabsContentElement, PrimitivTabsElement, PrimitivTabsListElement, PrimitivTabsTriggerElement } from "./tabs/elements.js";
|
|
16
17
|
declare global {
|
|
17
18
|
interface HTMLElementTagNameMap {
|
|
19
|
+
"primitiv-accordion": PrimitivAccordionElement;
|
|
20
|
+
"primitiv-accordion-item": PrimitivAccordionItemElement;
|
|
21
|
+
"primitiv-accordion-trigger": PrimitivAccordionTriggerElement;
|
|
22
|
+
"primitiv-accordion-content": PrimitivAccordionContentElement;
|
|
23
|
+
"primitiv-accordion-trigger-icon": PrimitivAccordionTriggerIconElement;
|
|
18
24
|
"primitiv-tabs": PrimitivTabsElement;
|
|
19
25
|
"primitiv-tabs-list": PrimitivTabsListElement;
|
|
20
26
|
"primitiv-tabs-trigger": PrimitivTabsTriggerElement;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primitiv-ui/web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.44",
|
|
4
4
|
"description": "Primitiv's headless components as standard custom elements: light DOM, no framework, no runtime dependencies. Works in plain HTML, server-rendered stacks, React, Vue, Angular and anything else that renders HTML.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"default": "./dist/index.js"
|
|
12
12
|
},
|
|
13
|
+
"./accordion": {
|
|
14
|
+
"types": "./dist/accordion/index.d.ts",
|
|
15
|
+
"default": "./dist/accordion/index.js"
|
|
16
|
+
},
|
|
13
17
|
"./tabs": {
|
|
14
18
|
"types": "./dist/tabs/index.d.ts",
|
|
15
19
|
"default": "./dist/tabs/index.js"
|
|
@@ -18,6 +22,10 @@
|
|
|
18
22
|
"types": "./dist/define/index.d.ts",
|
|
19
23
|
"default": "./dist/define/index.js"
|
|
20
24
|
},
|
|
25
|
+
"./define/accordion": {
|
|
26
|
+
"types": "./dist/define/accordion.d.ts",
|
|
27
|
+
"default": "./dist/define/accordion.js"
|
|
28
|
+
},
|
|
21
29
|
"./define/tabs": {
|
|
22
30
|
"types": "./dist/define/tabs.d.ts",
|
|
23
31
|
"default": "./dist/define/tabs.js"
|
|
@@ -52,7 +60,7 @@
|
|
|
52
60
|
"directory": "packages/web"
|
|
53
61
|
},
|
|
54
62
|
"dependencies": {
|
|
55
|
-
"@primitiv-ui/core": "0.1.
|
|
63
|
+
"@primitiv-ui/core": "0.1.44"
|
|
56
64
|
},
|
|
57
65
|
"devDependencies": {
|
|
58
66
|
"@custom-elements-manifest/analyzer": "0.11.0",
|