@rcarls/rc-disclosure 0.1.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 ADDED
@@ -0,0 +1,3 @@
1
+ # @rcarls/rc-disclosure
2
+
3
+ Native details/summary disclosure web component with light-DOM styling hooks.
@@ -0,0 +1,217 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "readme": "",
4
+ "modules": [
5
+ {
6
+ "kind": "javascript-module",
7
+ "path": "src/define.ts",
8
+ "declarations": [],
9
+ "exports": [
10
+ {
11
+ "kind": "custom-element-definition",
12
+ "name": "rc-disclosure",
13
+ "declaration": {
14
+ "name": "RCDisclosure",
15
+ "module": "/src/index.js"
16
+ }
17
+ },
18
+ {
19
+ "kind": "js",
20
+ "name": "*",
21
+ "declaration": {
22
+ "name": "*",
23
+ "module": "src/index.js"
24
+ }
25
+ }
26
+ ]
27
+ },
28
+ {
29
+ "kind": "javascript-module",
30
+ "path": "src/index.ts",
31
+ "declarations": [],
32
+ "exports": [
33
+ {
34
+ "kind": "js",
35
+ "name": "*",
36
+ "declaration": {
37
+ "name": "*",
38
+ "module": "src/rc-disclosure.js"
39
+ }
40
+ }
41
+ ]
42
+ },
43
+ {
44
+ "kind": "javascript-module",
45
+ "path": "src/rc-disclosure.ts",
46
+ "declarations": [
47
+ {
48
+ "kind": "class",
49
+ "description": "Light behavioral wrapper for a direct child native `<details>` element.\n\nThe browser keeps ownership of open/close behavior, keyboard support, and\naccessibility. This element only mirrors state, emits a consistent custom\nevent, and optionally opens matching fragment targets.",
50
+ "name": "RCDisclosure",
51
+ "members": [
52
+ {
53
+ "kind": "field",
54
+ "name": "_details",
55
+ "type": {
56
+ "text": "HTMLDetailsElement | null"
57
+ },
58
+ "privacy": "private",
59
+ "default": "null"
60
+ },
61
+ {
62
+ "kind": "field",
63
+ "name": "_observer",
64
+ "privacy": "private",
65
+ "default": "new MutationObserver(() => this._setupDetails())"
66
+ },
67
+ {
68
+ "kind": "field",
69
+ "name": "open",
70
+ "type": {
71
+ "text": "boolean"
72
+ },
73
+ "description": "Current open state mirrored to the child `<details>`."
74
+ },
75
+ {
76
+ "kind": "field",
77
+ "name": "fragment",
78
+ "type": {
79
+ "text": "boolean"
80
+ },
81
+ "description": "Enable location-hash opening when the details/summary target matches."
82
+ },
83
+ {
84
+ "kind": "method",
85
+ "name": "_setupDetails",
86
+ "privacy": "private",
87
+ "return": {
88
+ "type": {
89
+ "text": "void"
90
+ }
91
+ }
92
+ },
93
+ {
94
+ "kind": "method",
95
+ "name": "_teardownDetails",
96
+ "privacy": "private",
97
+ "return": {
98
+ "type": {
99
+ "text": "void"
100
+ }
101
+ }
102
+ },
103
+ {
104
+ "kind": "method",
105
+ "name": "_syncDetailsOpen",
106
+ "privacy": "private",
107
+ "return": {
108
+ "type": {
109
+ "text": "void"
110
+ }
111
+ }
112
+ },
113
+ {
114
+ "kind": "method",
115
+ "name": "_setOpen",
116
+ "privacy": "private",
117
+ "return": {
118
+ "type": {
119
+ "text": "void"
120
+ }
121
+ },
122
+ "parameters": [
123
+ {
124
+ "name": "value",
125
+ "type": {
126
+ "text": "boolean"
127
+ }
128
+ },
129
+ {
130
+ "name": "reflect",
131
+ "type": {
132
+ "text": "boolean"
133
+ }
134
+ }
135
+ ]
136
+ },
137
+ {
138
+ "kind": "method",
139
+ "name": "_reflectOpen",
140
+ "privacy": "private",
141
+ "return": {
142
+ "type": {
143
+ "text": "void"
144
+ }
145
+ },
146
+ "parameters": [
147
+ {
148
+ "name": "value",
149
+ "type": {
150
+ "text": "boolean"
151
+ }
152
+ }
153
+ ]
154
+ },
155
+ {
156
+ "kind": "field",
157
+ "name": "_onToggle",
158
+ "privacy": "private"
159
+ },
160
+ {
161
+ "kind": "field",
162
+ "name": "_onHashChange",
163
+ "privacy": "private"
164
+ },
165
+ {
166
+ "kind": "method",
167
+ "name": "_openForCurrentHash",
168
+ "privacy": "private",
169
+ "return": {
170
+ "type": {
171
+ "text": "void"
172
+ }
173
+ }
174
+ }
175
+ ],
176
+ "events": [
177
+ {
178
+ "description": "Fires when the child `<details>` toggles.",
179
+ "name": "rc-disclosure-toggle"
180
+ }
181
+ ],
182
+ "attributes": [
183
+ {
184
+ "name": "open"
185
+ },
186
+ {
187
+ "name": "fragment"
188
+ }
189
+ ],
190
+ "superclass": {
191
+ "name": "HTMLElement"
192
+ },
193
+ "tagName": "rc-disclosure",
194
+ "customElement": true
195
+ }
196
+ ],
197
+ "exports": [
198
+ {
199
+ "kind": "js",
200
+ "name": "RCDisclosure",
201
+ "declaration": {
202
+ "name": "RCDisclosure",
203
+ "module": "src/rc-disclosure.ts"
204
+ }
205
+ },
206
+ {
207
+ "kind": "js",
208
+ "name": "default",
209
+ "declaration": {
210
+ "name": "RCDisclosure",
211
+ "module": "src/rc-disclosure.ts"
212
+ }
213
+ }
214
+ ]
215
+ }
216
+ ]
217
+ }
package/dist/demo.css ADDED
@@ -0,0 +1,85 @@
1
+ *, *::before, *::after {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ :root {
6
+ color-scheme: light dark;
7
+ --rc-accent: Highlight; /* Chrome doesn't support AccentColor on the open web */
8
+ }
9
+
10
+ @supports (color: AccentColor) {
11
+ :root {
12
+ --rc-accent: AccentColor;
13
+ }
14
+ }
15
+
16
+ [data-theme="light"] { color-scheme: light; }
17
+ [data-theme="dark"] { color-scheme: dark; }
18
+
19
+ [data-theme="solarized-light"] {
20
+ color-scheme: light;
21
+ --rc-surface: #fdf6e3;
22
+ --rc-text: #657b83;
23
+ --rc-border: 1px solid #93a1a1;
24
+ --rc-shadow: 0 2px 8px rgba(0, 0, 0, .12);
25
+ }
26
+
27
+ [data-theme="solarized-dark"] {
28
+ color-scheme: dark;
29
+ --rc-surface: #002b36;
30
+ --rc-text: #839496;
31
+ --rc-border: 1px solid #586e75;
32
+ --rc-shadow: 0 2px 8px rgba(0, 0, 0, .3);
33
+ }
34
+
35
+ body {
36
+ font-family: system-ui, sans-serif;
37
+ margin: 0;
38
+ }
39
+
40
+ .demo-page {
41
+ max-width: 60rem;
42
+ margin: 0 auto;
43
+ padding: 2rem 1.5rem;
44
+ }
45
+
46
+ .demo-controls {
47
+ display: flex;
48
+ align-items: center;
49
+ gap: 1rem;
50
+ margin-bottom: 2rem;
51
+ padding-bottom: 1rem;
52
+ border-bottom: 1px solid ButtonBorder;
53
+ }
54
+
55
+ .demo-controls .demo-title {
56
+ flex: 1;
57
+ }
58
+
59
+ .demo-controls h1 {
60
+ margin: 0 0 0.15rem;
61
+ font-size: 1.5rem;
62
+ }
63
+
64
+ .demo-controls a {
65
+ font-size: 0.8rem;
66
+ }
67
+
68
+ .demo-section {
69
+ margin-bottom: 2rem;
70
+ }
71
+
72
+ .demo-section h2 {
73
+ margin: 0 0 0.5rem;
74
+ }
75
+
76
+ .theme-picker {
77
+ padding: 0.35em 0.75em;
78
+ font-family: inherit;
79
+ font-size: 0.8rem;
80
+ cursor: pointer;
81
+ border: 1px solid ButtonBorder;
82
+ border-radius: 4px;
83
+ background: Canvas;
84
+ color: CanvasText;
85
+ }
package/dist/demo.js ADDED
@@ -0,0 +1,40 @@
1
+ const THEMES = ['', 'light', 'dark', 'solarized-light', 'solarized-dark'];
2
+ const LABELS = ['Auto', 'Light', 'Dark', 'Solarized ☀', 'Solarized ☾'];
3
+
4
+ const stored = localStorage.getItem('rc-demo-theme') ?? '';
5
+ applyTheme(stored);
6
+
7
+ function applyTheme(theme) {
8
+ if (theme) {
9
+ document.documentElement.dataset.theme = theme;
10
+ } else {
11
+ delete document.documentElement.dataset.theme;
12
+ }
13
+ }
14
+
15
+ function currentIndex() {
16
+ const current = document.documentElement.dataset.theme ?? '';
17
+ const idx = THEMES.indexOf(current);
18
+ return idx === -1 ? 0 : idx;
19
+ }
20
+
21
+ function updateButtons() {
22
+ const label = LABELS[currentIndex()];
23
+ document.querySelectorAll('.theme-picker').forEach((btn) => {
24
+ btn.textContent = label;
25
+ });
26
+ }
27
+
28
+ window.cycleTheme = function () {
29
+ const next = THEMES[(currentIndex() + 1) % THEMES.length];
30
+ applyTheme(next);
31
+ localStorage.setItem('rc-demo-theme', next);
32
+ updateButtons();
33
+ };
34
+
35
+ document.addEventListener('DOMContentLoaded', () => {
36
+ updateButtons();
37
+ document.querySelectorAll('.theme-picker').forEach((btn) => {
38
+ btn.addEventListener('click', window.cycleTheme);
39
+ });
40
+ });
@@ -0,0 +1,6 @@
1
+ import { RCDisclosure as e } from "./rc-disclosure.js";
2
+ customElements.get("rc-disclosure") || customElements.define("rc-disclosure", e);
3
+ export {
4
+ e as RCDisclosure
5
+ };
6
+ //# sourceMappingURL=rc-disclosure-define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-disclosure-define.js","sources":["../src/define.ts"],"sourcesContent":["import { RCDisclosure } from './index.js';\n\ncustomElements.get('rc-disclosure') || customElements.define('rc-disclosure', RCDisclosure);\n\nexport * from './index.js';\n"],"names":["RCDisclosure"],"mappings":";AAEA,eAAe,IAAI,eAAe,KAAK,eAAe,OAAO,iBAAiBA,CAAY;"}
@@ -0,0 +1,83 @@
1
+ class n extends HTMLElement {
2
+ constructor() {
3
+ super(...arguments), this._details = null, this._observer = new MutationObserver(() => this._setupDetails()), this._onToggle = () => {
4
+ const t = this._details?.open ?? !1;
5
+ this._reflectOpen(t), this.dispatchEvent(
6
+ new CustomEvent("rc-disclosure-toggle", {
7
+ bubbles: !0,
8
+ composed: !0,
9
+ detail: { open: t }
10
+ })
11
+ );
12
+ }, this._onHashChange = () => {
13
+ this._openForCurrentHash();
14
+ };
15
+ }
16
+ static get observedAttributes() {
17
+ return ["open", "fragment"];
18
+ }
19
+ /** Current open state mirrored to the child `<details>`. */
20
+ get open() {
21
+ return this._details?.open ?? this.hasAttribute("open");
22
+ }
23
+ set open(t) {
24
+ this._setOpen(t, !0);
25
+ }
26
+ /** Enable location-hash opening when the details/summary target matches. */
27
+ get fragment() {
28
+ return this.hasAttribute("fragment");
29
+ }
30
+ set fragment(t) {
31
+ this.toggleAttribute("fragment", t);
32
+ }
33
+ connectedCallback() {
34
+ this._observer.observe(this, { childList: !0 }), window.addEventListener("hashchange", this._onHashChange), this._setupDetails(), this._openForCurrentHash();
35
+ }
36
+ disconnectedCallback() {
37
+ this._observer.disconnect(), window.removeEventListener("hashchange", this._onHashChange), this._teardownDetails();
38
+ }
39
+ attributeChangedCallback(t, e, s) {
40
+ e !== s && (t === "open" && this._syncDetailsOpen(), t === "fragment" && this._openForCurrentHash());
41
+ }
42
+ _setupDetails() {
43
+ const t = this.querySelector(":scope > details");
44
+ if (t === this._details || (this._teardownDetails(), this._details = t, !t))
45
+ return;
46
+ t.id || (t.id = crypto.randomUUID());
47
+ const e = t.querySelector(":scope > summary");
48
+ e && e.setAttribute("aria-controls", t.id), t.addEventListener("toggle", this._onToggle), this._syncDetailsOpen(), this._reflectOpen(t.open), this._openForCurrentHash();
49
+ }
50
+ _teardownDetails() {
51
+ this._details?.querySelector(":scope > summary")?.removeAttribute("aria-controls"), this._details?.removeEventListener("toggle", this._onToggle), this._details = null;
52
+ }
53
+ _syncDetailsOpen() {
54
+ const t = this._details;
55
+ if (!t) return;
56
+ const e = this.hasAttribute("open");
57
+ t.open !== e && (t.open = e);
58
+ }
59
+ _setOpen(t, e) {
60
+ const s = this._details;
61
+ s && s.open !== t && (s.open = t), e && this._reflectOpen(t);
62
+ }
63
+ _reflectOpen(t) {
64
+ if (t) {
65
+ this.hasAttribute("open") || this.setAttribute("open", "");
66
+ return;
67
+ }
68
+ this.hasAttribute("open") && this.removeAttribute("open");
69
+ }
70
+ _openForCurrentHash() {
71
+ if (!this.fragment || !location.hash) return;
72
+ const t = decodeURIComponent(location.hash.slice(1));
73
+ if (!t) return;
74
+ const e = this._details;
75
+ if (!e) return;
76
+ const s = e.querySelector(":scope > summary");
77
+ e.id !== t && s?.id !== t || (this.open = !0);
78
+ }
79
+ }
80
+ export {
81
+ n as RCDisclosure
82
+ };
83
+ //# sourceMappingURL=rc-disclosure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-disclosure.js","sources":["../src/rc-disclosure.ts"],"sourcesContent":["export interface RCDisclosureToggleEvent {\n /** Whether the disclosure is now open. */\n open: boolean;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-disclosure': RCDisclosure;\n }\n}\n\n/**\n * Light behavioral wrapper for a direct child native `<details>` element.\n *\n * The browser keeps ownership of open/close behavior, keyboard support, and\n * accessibility. This element only mirrors state, emits a consistent custom\n * event, and optionally opens matching fragment targets.\n *\n * @fires rc-disclosure-toggle - Fires when the child `<details>` toggles.\n */\nexport class RCDisclosure extends HTMLElement {\n private _details: HTMLDetailsElement | null = null;\n private _observer = new MutationObserver(() => this._setupDetails());\n\n static get observedAttributes(): string[] {\n return ['open', 'fragment'];\n }\n\n /** Current open state mirrored to the child `<details>`. */\n get open(): boolean {\n return this._details?.open ?? this.hasAttribute('open');\n }\n\n set open(value: boolean) {\n this._setOpen(value, true);\n }\n\n /** Enable location-hash opening when the details/summary target matches. */\n get fragment(): boolean {\n return this.hasAttribute('fragment');\n }\n\n set fragment(value: boolean) {\n this.toggleAttribute('fragment', value);\n }\n\n connectedCallback(): void {\n this._observer.observe(this, { childList: true });\n window.addEventListener('hashchange', this._onHashChange);\n this._setupDetails();\n this._openForCurrentHash();\n }\n\n disconnectedCallback(): void {\n this._observer.disconnect();\n window.removeEventListener('hashchange', this._onHashChange);\n this._teardownDetails();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n if (name === 'open') this._syncDetailsOpen();\n if (name === 'fragment') this._openForCurrentHash();\n }\n\n private _setupDetails(): void {\n const details = this.querySelector<HTMLDetailsElement>(':scope > details');\n if (details === this._details) return;\n\n this._teardownDetails();\n this._details = details;\n\n if (!details) {\n if (import.meta.env.DEV) {\n console.warn(\n '[rc-disclosure] No direct child <details> found. Place native ' +\n '<details><summary>...</summary>...</details> inside <rc-disclosure>.',\n this,\n );\n }\n return;\n }\n\n if (!details.id) details.id = crypto.randomUUID();\n\n const summary = details.querySelector<HTMLElement>(':scope > summary');\n if (summary) summary.setAttribute('aria-controls', details.id);\n\n details.addEventListener('toggle', this._onToggle);\n this._syncDetailsOpen();\n this._reflectOpen(details.open);\n this._openForCurrentHash();\n }\n\n private _teardownDetails(): void {\n const summary = this._details?.querySelector<HTMLElement>(':scope > summary');\n summary?.removeAttribute('aria-controls');\n\n this._details?.removeEventListener('toggle', this._onToggle);\n this._details = null;\n }\n\n private _syncDetailsOpen(): void {\n const details = this._details;\n if (!details) return;\n\n const nextOpen = this.hasAttribute('open');\n if (details.open !== nextOpen) details.open = nextOpen;\n }\n\n private _setOpen(value: boolean, reflect: boolean): void {\n const details = this._details;\n if (details && details.open !== value) details.open = value;\n // _reflectOpen is called here for programmatic opens because the <details>\n // toggle event is async (queued task per HTML spec). _onToggle also calls\n // _reflectOpen for user-initiated clicks, but that arrives after a tick.\n if (reflect) this._reflectOpen(value);\n }\n\n private _reflectOpen(value: boolean): void {\n if (value) {\n if (!this.hasAttribute('open')) this.setAttribute('open', '');\n return;\n }\n\n if (this.hasAttribute('open')) this.removeAttribute('open');\n }\n\n private _onToggle = (): void => {\n const open = this._details?.open ?? false;\n\n this._reflectOpen(open);\n this.dispatchEvent(\n new CustomEvent<RCDisclosureToggleEvent>('rc-disclosure-toggle', {\n bubbles: true,\n composed: true,\n detail: { open },\n }),\n );\n };\n\n private _onHashChange = (): void => {\n this._openForCurrentHash();\n };\n\n private _openForCurrentHash(): void {\n if (!this.fragment || !location.hash) return;\n\n const targetId = decodeURIComponent(location.hash.slice(1));\n if (!targetId) return;\n\n const details = this._details;\n if (!details) return;\n\n const summary = details.querySelector<HTMLElement>(':scope > summary');\n if (details.id !== targetId && summary?.id !== targetId) return;\n\n this.open = true;\n }\n}\n\nexport default RCDisclosure;\n"],"names":["RCDisclosure","open","value","name","oldValue","newValue","details","summary","nextOpen","reflect","targetId"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export * from './index.js';
@@ -0,0 +1 @@
1
+ export * from './rc-disclosure.js';
@@ -0,0 +1,41 @@
1
+ export interface RCDisclosureToggleEvent {
2
+ /** Whether the disclosure is now open. */
3
+ open: boolean;
4
+ }
5
+ declare global {
6
+ interface HTMLElementTagNameMap {
7
+ 'rc-disclosure': RCDisclosure;
8
+ }
9
+ }
10
+ /**
11
+ * Light behavioral wrapper for a direct child native `<details>` element.
12
+ *
13
+ * The browser keeps ownership of open/close behavior, keyboard support, and
14
+ * accessibility. This element only mirrors state, emits a consistent custom
15
+ * event, and optionally opens matching fragment targets.
16
+ *
17
+ * @fires rc-disclosure-toggle - Fires when the child `<details>` toggles.
18
+ */
19
+ export declare class RCDisclosure extends HTMLElement {
20
+ private _details;
21
+ private _observer;
22
+ static get observedAttributes(): string[];
23
+ /** Current open state mirrored to the child `<details>`. */
24
+ get open(): boolean;
25
+ set open(value: boolean);
26
+ /** Enable location-hash opening when the details/summary target matches. */
27
+ get fragment(): boolean;
28
+ set fragment(value: boolean);
29
+ connectedCallback(): void;
30
+ disconnectedCallback(): void;
31
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
32
+ private _setupDetails;
33
+ private _teardownDetails;
34
+ private _syncDetailsOpen;
35
+ private _setOpen;
36
+ private _reflectOpen;
37
+ private _onToggle;
38
+ private _onHashChange;
39
+ private _openForCurrentHash;
40
+ }
41
+ export default RCDisclosure;
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@rcarls/rc-disclosure",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.1.0",
7
+ "description": "Headless WAI-ARIA disclosure widget built with Lit",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/richardcarls/rc-webcomponents.git",
11
+ "directory": "packages/rc-disclosure"
12
+ },
13
+ "homepage": "https://github.com/richardcarls/rc-webcomponents#readme",
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "module": "./dist/rc-disclosure.js",
20
+ "exports": {
21
+ ".": {
22
+ "import": {
23
+ "types": "./dist/types/packages/rc-disclosure/src/index.d.ts",
24
+ "default": "./dist/rc-disclosure.js"
25
+ }
26
+ },
27
+ "./define": {
28
+ "import": {
29
+ "types": "./dist/types/packages/rc-disclosure/src/define.d.ts",
30
+ "default": "./dist/rc-disclosure-define.js"
31
+ }
32
+ }
33
+ },
34
+ "types": "./dist/types/packages/rc-disclosure/src/index.d.ts",
35
+ "sideEffects": [
36
+ "./dist/rc-disclosure-define.js"
37
+ ],
38
+ "customElements": "dist/custom-elements.json",
39
+ "scripts": {
40
+ "dev": "vite",
41
+ "build": "tsc && vite build && cem analyze",
42
+ "cem:analyze": "cem analyze",
43
+ "preview": "vite preview",
44
+ "test:browser": "vitest",
45
+ "test:browser:chrome": "vitest --project=chromium",
46
+ "test:browser:firefox": "vitest --project=firefox",
47
+ "yalc:publish": "yalc publish --push"
48
+ },
49
+ "devDependencies": {
50
+ "@custom-elements-manifest/analyzer": "0.11.0",
51
+ "@guanghechen/rollup-plugin-copy": "^6.0.9",
52
+ "@vitest/browser-playwright": "4.1.5",
53
+ "lit": "^3.0.0",
54
+ "playwright": "^1.56.0",
55
+ "rollup": "^4.60.2",
56
+ "typescript": "~5.9.3",
57
+ "vite": "^7.1.7",
58
+ "vite-plugin-dts": "^4.5.4",
59
+ "vitest": "^4.0.6",
60
+ "vitest-browser-lit": "^1.0.1"
61
+ },
62
+ "peerDependencies": {
63
+ "lit": "^3.0.0"
64
+ }
65
+ }