@rcarls/rc-accordion 0.5.0 → 0.7.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/CHANGELOG.md +92 -0
- package/dist/custom-elements.json +63 -24
- package/dist/rc-accordion-B6TMttQh.js +120 -0
- package/dist/rc-accordion-B6TMttQh.js.map +1 -0
- package/dist/rc-accordion-define.js +1 -1
- package/dist/rc-accordion-define.js.map +1 -1
- package/dist/rc-accordion.js +4 -115
- package/dist/rc-accordion.js.map +1 -1
- package/dist/types/packages/rc-accordion/src/accordionBaseStyles.d.ts +12 -0
- package/dist/types/packages/rc-accordion/src/index.d.ts +1 -0
- package/dist/types/packages/rc-accordion/src/rc-accordion.d.ts +6 -1
- package/package.json +3 -2
- package/dist/types/packages/rc-accordion/src/rc-accordion.test.d.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,97 @@
|
|
|
1
1
|
# @rcarls/rc-accordion
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4b9bfe6: Give `rc-disclosure` and `rc-accordion` a public styling contract.
|
|
8
|
+
|
|
9
|
+
Both were styled by targeting the native `<details>` element directly, so neither exposed a
|
|
10
|
+
single `--rc-*` property and no theme could change them without rewriting theme CSS. They now
|
|
11
|
+
follow the light-DOM base pattern `rc-button` and `rc-listbox` already use: `rc-disclosure`
|
|
12
|
+
installs one structural stylesheet per containing `Document` or `ShadowRoot`, in `@layer rc-base`
|
|
13
|
+
behind a `data-rc-light-dom-base` sentinel, reading documented properties with UA-like defaults.
|
|
14
|
+
|
|
15
|
+
One recipe covers both supported child forms, a bare `<details>` inside `rc-accordion` and one
|
|
16
|
+
wrapped in `rc-disclosure`, so neither theme repeats the panel rules. `rc-accordion` owns only
|
|
17
|
+
`--rc-accordion-gap`.
|
|
18
|
+
|
|
19
|
+
Motion is opt-in. `--rc-disclosure-duration` defaults to `0ms`, so a disclosure with no theme keeps
|
|
20
|
+
the platform's snap-open behavior, and the height animation runs on `::details-content` behind a
|
|
21
|
+
`@supports selector()` check. This replaces Material's `60rem` `max-block-size` ceiling, which
|
|
22
|
+
clipped content taller than the guess and made every non-summary child its own animation unit.
|
|
23
|
+
|
|
24
|
+
**Fixes a missing expand affordance in the Material theme.** It set `display: grid` on the
|
|
25
|
+
`<summary>` and then styled `summary::marker`. Any `display` other than `list-item` drops the
|
|
26
|
+
marker box, so that rule styled nothing and the theme drew no replacement: a Material disclosure
|
|
27
|
+
had no disclosure triangle at all. The base layer never sets `display` on a summary, so the
|
|
28
|
+
marker survives. A theme may still change it, as Substrate does, but only alongside a
|
|
29
|
+
replacement affordance.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- aee912a: Single-open accordions now close a sibling `rc-disclosure` through its native `<details>`
|
|
34
|
+
instead of its `open` property. Writing the property switched the disclosure into
|
|
35
|
+
controlled mode, so a panel the accordion had closed would not reopen from its summary.
|
|
36
|
+
A controlled sibling now receives the close as an `rc-disclosure-toggle` request and
|
|
37
|
+
keeps the state its owner sets.
|
|
38
|
+
|
|
39
|
+
The Solid JSX types now declare `rc-disclosure`'s `default-open` attribute and its
|
|
40
|
+
`open` and `defaultOpen` properties.
|
|
41
|
+
|
|
42
|
+
- 5c006de: Ship component CSS minified. Package builds now run the CSS in Lit `css` styles
|
|
43
|
+
and light-DOM structural styles through Lightning CSS, removing comments and
|
|
44
|
+
whitespace while keeping source maps that resolve to each template. Rendered
|
|
45
|
+
styles are unchanged.
|
|
46
|
+
|
|
47
|
+
`@rcarls/rc-common` adds `cssText`, an identity tag that marks a plain CSS
|
|
48
|
+
string for minification. `rc-chip`, `rc-disclosure`, and `rc-accordion` now
|
|
49
|
+
depend on `@rcarls/rc-common` for it.
|
|
50
|
+
|
|
51
|
+
- Updated dependencies [e3dfa59]
|
|
52
|
+
- Updated dependencies [50d5610]
|
|
53
|
+
- Updated dependencies [9ee769c]
|
|
54
|
+
- Updated dependencies [1179f24]
|
|
55
|
+
- Updated dependencies [595fe37]
|
|
56
|
+
- Updated dependencies [38cd00c]
|
|
57
|
+
- Updated dependencies [5c006de]
|
|
58
|
+
- Updated dependencies [a6553ff]
|
|
59
|
+
- Updated dependencies [2205478]
|
|
60
|
+
- Updated dependencies [4341311]
|
|
61
|
+
- Updated dependencies [899bb5e]
|
|
62
|
+
- Updated dependencies [25fcfe0]
|
|
63
|
+
- Updated dependencies [4b9bfe6]
|
|
64
|
+
- Updated dependencies [3f19f57]
|
|
65
|
+
- Updated dependencies [51f6403]
|
|
66
|
+
- Updated dependencies [9caca6b]
|
|
67
|
+
- Updated dependencies [b764a3c]
|
|
68
|
+
- Updated dependencies [54d9ead]
|
|
69
|
+
- Updated dependencies [6e548d9]
|
|
70
|
+
- Updated dependencies [b9175e9]
|
|
71
|
+
- Updated dependencies [c8b762b]
|
|
72
|
+
- Updated dependencies [55e1e7f]
|
|
73
|
+
- Updated dependencies [f1ca82d]
|
|
74
|
+
- Updated dependencies [b87f27b]
|
|
75
|
+
- Updated dependencies [32ebead]
|
|
76
|
+
- Updated dependencies [bfefedd]
|
|
77
|
+
- Updated dependencies [c130173]
|
|
78
|
+
- Updated dependencies [11680b6]
|
|
79
|
+
- Updated dependencies [223d36c]
|
|
80
|
+
- Updated dependencies [7b91a44]
|
|
81
|
+
- Updated dependencies [aa04ead]
|
|
82
|
+
- Updated dependencies [ac65b5b]
|
|
83
|
+
- Updated dependencies [afcfef8]
|
|
84
|
+
- Updated dependencies [11ef1ad]
|
|
85
|
+
- @rcarls/rc-common@0.7.0
|
|
86
|
+
- @rcarls/rc-disclosure@0.7.0
|
|
87
|
+
|
|
88
|
+
## 0.6.0
|
|
89
|
+
|
|
90
|
+
### Patch Changes
|
|
91
|
+
|
|
92
|
+
- Updated dependencies [57370e4]
|
|
93
|
+
- @rcarls/rc-disclosure@0.6.0
|
|
94
|
+
|
|
3
95
|
## 0.5.0
|
|
4
96
|
|
|
5
97
|
### Patch Changes
|
|
@@ -2,6 +2,54 @@
|
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
3
|
"readme": "",
|
|
4
4
|
"modules": [
|
|
5
|
+
{
|
|
6
|
+
"kind": "javascript-module",
|
|
7
|
+
"path": "src/accordionBaseStyles.ts",
|
|
8
|
+
"declarations": [
|
|
9
|
+
{
|
|
10
|
+
"kind": "variable",
|
|
11
|
+
"name": "ACCORDION_BASE_CSS",
|
|
12
|
+
"default": "cssText` @layer rc-base { rc-accordion { display: grid; gap: var(--rc-accordion-gap, 0); } rc-accordion > rc-disclosure { display: block; } } `",
|
|
13
|
+
"description": "Light-DOM base styles for the accordion group box.\n\nThe panels themselves are not styled here. Both supported child forms, a\nnative `<details>` directly inside `rc-accordion` and one wrapped in\n`rc-disclosure`, consume the shared `--rc-disclosure-*` properties through the\nrecipe that `rc-disclosure` installs, so there is no parallel accordion-panel\nvocabulary to keep in step."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"kind": "function",
|
|
17
|
+
"name": "ensureAccordionBaseStyles",
|
|
18
|
+
"return": {
|
|
19
|
+
"type": {
|
|
20
|
+
"text": "void"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"parameters": [
|
|
24
|
+
{
|
|
25
|
+
"name": "root",
|
|
26
|
+
"type": {
|
|
27
|
+
"text": "Document | ShadowRoot"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"description": "Installs the accordion group styles once per containing root."
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"exports": [
|
|
35
|
+
{
|
|
36
|
+
"kind": "js",
|
|
37
|
+
"name": "ACCORDION_BASE_CSS",
|
|
38
|
+
"declaration": {
|
|
39
|
+
"name": "ACCORDION_BASE_CSS",
|
|
40
|
+
"module": "src/accordionBaseStyles.ts"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"kind": "js",
|
|
45
|
+
"name": "ensureAccordionBaseStyles",
|
|
46
|
+
"declaration": {
|
|
47
|
+
"name": "ensureAccordionBaseStyles",
|
|
48
|
+
"module": "src/accordionBaseStyles.ts"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
},
|
|
5
53
|
{
|
|
6
54
|
"kind": "javascript-module",
|
|
7
55
|
"path": "src/define.ts",
|
|
@@ -29,6 +77,14 @@
|
|
|
29
77
|
"name": "*",
|
|
30
78
|
"module": "src/rc-accordion.js"
|
|
31
79
|
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"kind": "js",
|
|
83
|
+
"name": "*",
|
|
84
|
+
"declaration": {
|
|
85
|
+
"name": "*",
|
|
86
|
+
"module": "src/accordionBaseStyles.js"
|
|
87
|
+
}
|
|
32
88
|
}
|
|
33
89
|
]
|
|
34
90
|
},
|
|
@@ -40,6 +96,13 @@
|
|
|
40
96
|
"kind": "class",
|
|
41
97
|
"description": "Accordion coordinator for child native <details> panels with single- or multiple-open\nbehavior, following the WAI-ARIA Accordion pattern.\n\nSupports native details and rc-disclosure wrappers.",
|
|
42
98
|
"name": "RCAccordion",
|
|
99
|
+
"cssProperties": [
|
|
100
|
+
{
|
|
101
|
+
"description": "Gap between panels. Panels take the shared `--rc-disclosure-*` properties, documented on `rc-disclosure`, for both supported child forms: a native `<details>` directly inside the accordion, and one wrapped in `rc-disclosure`.",
|
|
102
|
+
"name": "--rc-accordion-gap",
|
|
103
|
+
"default": "0"
|
|
104
|
+
}
|
|
105
|
+
],
|
|
43
106
|
"members": [
|
|
44
107
|
{
|
|
45
108
|
"kind": "field",
|
|
@@ -101,30 +164,6 @@
|
|
|
101
164
|
"name": "_onDetailsToggle",
|
|
102
165
|
"privacy": "private"
|
|
103
166
|
},
|
|
104
|
-
{
|
|
105
|
-
"kind": "method",
|
|
106
|
-
"name": "_setDetailsOpen",
|
|
107
|
-
"privacy": "private",
|
|
108
|
-
"return": {
|
|
109
|
-
"type": {
|
|
110
|
-
"text": "void"
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
"parameters": [
|
|
114
|
-
{
|
|
115
|
-
"name": "$target",
|
|
116
|
-
"type": {
|
|
117
|
-
"text": "HTMLDetailsElement"
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
"name": "open",
|
|
122
|
-
"type": {
|
|
123
|
-
"text": "boolean"
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
]
|
|
127
|
-
},
|
|
128
167
|
{
|
|
129
168
|
"kind": "method",
|
|
130
169
|
"name": "_onChildrenChanged",
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ensureDisclosureBaseStyles as c } from "@rcarls/rc-disclosure";
|
|
2
|
+
import { cssText as u } from "@rcarls/rc-common";
|
|
3
|
+
const d = u`@layer rc-base{rc-accordion{gap:var(--rc-accordion-gap,0);display:grid}rc-accordion>rc-disclosure{display:block}}`, l = /* @__PURE__ */ new WeakSet();
|
|
4
|
+
function m(r) {
|
|
5
|
+
if (l.has(r))
|
|
6
|
+
return;
|
|
7
|
+
l.add(r);
|
|
8
|
+
const e = (r instanceof Document ? r : r.ownerDocument).createElement("style");
|
|
9
|
+
e.setAttribute("data-rc-light-dom-base", "rc-accordion"), e.textContent = d, r instanceof Document ? r.head.append(e) : r.append(e);
|
|
10
|
+
}
|
|
11
|
+
const h = ["ArrowDown", "ArrowUp", "Home", "End"];
|
|
12
|
+
function f(r) {
|
|
13
|
+
return h.includes(r);
|
|
14
|
+
}
|
|
15
|
+
class _ extends HTMLElement {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments), this._$managedGroupNames = /* @__PURE__ */ new Map(), this._childrenObserver = new MutationObserver((e) => this._onChildrenChanged(e)), this._onKeydown = (e) => {
|
|
18
|
+
if (e.altKey || e.ctrlKey || e.metaKey || !f(e.key))
|
|
19
|
+
return;
|
|
20
|
+
const n = this._$summaries();
|
|
21
|
+
if (n.length === 0)
|
|
22
|
+
return;
|
|
23
|
+
const i = document.activeElement, t = i ? n.indexOf(i) : -1;
|
|
24
|
+
if (t === -1)
|
|
25
|
+
return;
|
|
26
|
+
if (e.preventDefault(), e.key === "Home") {
|
|
27
|
+
const a = n.at(0);
|
|
28
|
+
if (!a)
|
|
29
|
+
return;
|
|
30
|
+
a.focus();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (e.key === "End") {
|
|
34
|
+
const a = n.at(-1);
|
|
35
|
+
if (!a)
|
|
36
|
+
return;
|
|
37
|
+
a.focus();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const s = e.key === "ArrowDown" ? (t + 1) % n.length : (t - 1 + n.length) % n.length, o = n.at(s);
|
|
41
|
+
o && o.focus();
|
|
42
|
+
}, this._onDetailsToggle = (e) => {
|
|
43
|
+
const n = e.target;
|
|
44
|
+
n instanceof HTMLDetailsElement && (this.multiple || n.open && this._enforceSingleOpen(n));
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
static get observedAttributes() {
|
|
48
|
+
return ["multiple", "name"];
|
|
49
|
+
}
|
|
50
|
+
/** Allows more than one panel open at a time. */
|
|
51
|
+
get multiple() {
|
|
52
|
+
return this.hasAttribute("multiple");
|
|
53
|
+
}
|
|
54
|
+
set multiple(e) {
|
|
55
|
+
this.toggleAttribute("multiple", e);
|
|
56
|
+
}
|
|
57
|
+
connectedCallback() {
|
|
58
|
+
const e = this.getRootNode();
|
|
59
|
+
m(e), c(e), this._childrenObserver.observe(this, { childList: !0, subtree: !0 }), this.addEventListener("toggle", this._onDetailsToggle, !0), this.addEventListener("keydown", this._onKeydown), this._syncNativeGroupNames(), this._enforceSingleOpen();
|
|
60
|
+
}
|
|
61
|
+
disconnectedCallback() {
|
|
62
|
+
this._childrenObserver.disconnect(), this.removeEventListener("toggle", this._onDetailsToggle, !0), this.removeEventListener("keydown", this._onKeydown);
|
|
63
|
+
}
|
|
64
|
+
attributeChangedCallback(e, n, i) {
|
|
65
|
+
n !== i && ((e === "name" || e === "multiple") && this._syncNativeGroupNames(), e === "multiple" && !this.multiple && this._enforceSingleOpen());
|
|
66
|
+
}
|
|
67
|
+
_$details() {
|
|
68
|
+
return Array.from(
|
|
69
|
+
this.querySelectorAll(
|
|
70
|
+
":scope > details, :scope > rc-disclosure > details"
|
|
71
|
+
)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
_$summaries() {
|
|
75
|
+
return this._$details().map((e) => e.querySelector(":scope > summary")).filter((e) => e !== null);
|
|
76
|
+
}
|
|
77
|
+
_syncNativeGroupNames() {
|
|
78
|
+
const e = this.getAttribute("name"), n = this._$details(), i = new Set(n);
|
|
79
|
+
for (const [t, s] of this._$managedGroupNames)
|
|
80
|
+
(!i.has(t) || this.multiple || !e || s !== e || t.getAttribute("name") !== s) && (t.getAttribute("name") === s && t.removeAttribute("name"), this._$managedGroupNames.delete(t));
|
|
81
|
+
if (!(this.multiple || !e))
|
|
82
|
+
for (const t of n)
|
|
83
|
+
t.hasAttribute("name") && !this._$managedGroupNames.has(t) || (t.setAttribute("name", e), this._$managedGroupNames.set(t, e));
|
|
84
|
+
}
|
|
85
|
+
_onChildrenChanged(e) {
|
|
86
|
+
this._syncNativeGroupNames(), !this.multiple && this._enforceSingleOpen(this._findAddedOpenDetails(e));
|
|
87
|
+
}
|
|
88
|
+
_findAddedOpenDetails(e) {
|
|
89
|
+
const i = this._$details().filter(
|
|
90
|
+
(t) => e.some(
|
|
91
|
+
(s) => Array.from(s.addedNodes).some(
|
|
92
|
+
(o) => o === t || o instanceof Element && o.contains(t)
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
for (let t = i.length - 1; t >= 0; t -= 1) {
|
|
97
|
+
const s = i.at(t);
|
|
98
|
+
if (s && s.open)
|
|
99
|
+
return s;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
_enforceSingleOpen(e) {
|
|
103
|
+
const n = this._$details();
|
|
104
|
+
if (e && !n.includes(e))
|
|
105
|
+
return;
|
|
106
|
+
const i = n.filter((s) => s.open);
|
|
107
|
+
if (i.length <= 1)
|
|
108
|
+
return;
|
|
109
|
+
const t = e ?? i.at(0);
|
|
110
|
+
if (t)
|
|
111
|
+
for (const s of i)
|
|
112
|
+
s !== t && (s.open = !1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
d as A,
|
|
117
|
+
_ as R,
|
|
118
|
+
m as e
|
|
119
|
+
};
|
|
120
|
+
//# sourceMappingURL=rc-accordion-B6TMttQh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-accordion-B6TMttQh.js","sources":["../src/accordionBaseStyles.ts","../src/rc-accordion.ts"],"sourcesContent":["import { cssText } from '@rcarls/rc-common';\n\n/**\n * Light-DOM base styles for the accordion group box.\n *\n * The panels themselves are not styled here. Both supported child forms, a\n * native `<details>` directly inside `rc-accordion` and one wrapped in\n * `rc-disclosure`, consume the shared `--rc-disclosure-*` properties through the\n * recipe that `rc-disclosure` installs, so there is no parallel accordion-panel\n * vocabulary to keep in step.\n */\nexport const ACCORDION_BASE_CSS = cssText`\n@layer rc-base {\n rc-accordion {\n display: grid;\n gap: var(--rc-accordion-gap, 0);\n }\n\n rc-accordion > rc-disclosure {\n display: block;\n }\n}\n`;\n\nconst styledRoots = new WeakSet<Document | ShadowRoot>();\n\n/** Installs the accordion group styles once per containing root. */\nexport function ensureAccordionBaseStyles(root: Document | ShadowRoot): void {\n if (styledRoots.has(root)) {\n return;\n }\n\n styledRoots.add(root);\n\n const $style = (root instanceof Document ? root : root.ownerDocument).createElement('style');\n\n $style.setAttribute('data-rc-light-dom-base', 'rc-accordion');\n $style.textContent = ACCORDION_BASE_CSS;\n\n if (root instanceof Document) {\n root.head.append($style);\n } else {\n root.append($style);\n }\n}\n","import { ensureDisclosureBaseStyles } from '@rcarls/rc-disclosure';\nimport { ensureAccordionBaseStyles } from './accordionBaseStyles.js';\n\nconst NAVIGATION_KEYS = ['ArrowDown', 'ArrowUp', 'Home', 'End'] as const;\n\ntype NavigationKey = (typeof NAVIGATION_KEYS)[number];\n\nfunction isNavigationKey(key: string): key is NavigationKey {\n return NAVIGATION_KEYS.includes(key as NavigationKey);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-accordion': RCAccordion;\n }\n}\n\n/**\n * Accordion coordinator for child native <details> panels with single- or multiple-open\n * behavior, following the WAI-ARIA Accordion pattern.\n *\n * Supports native details and rc-disclosure wrappers.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-accordion rc-accordion docs}\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/accordion/ WAI-ARIA Accordion pattern}\n *\n * @attr multiple - Allows more than one panel open at a time.\n * @attr name - Group name mirrored onto each managed native `<details>` element's own\n * `name` attribute (native details name-grouping), so long as `multiple` is not set.\n * Child `<details>` elements that already declare their own `name` are left alone.\n *\n * @cssprop [--rc-accordion-gap=0] - Gap between panels.\n *\n * Panels take the shared `--rc-disclosure-*` properties, documented on `rc-disclosure`, for both\n * supported child forms: a native `<details>` directly inside the accordion, and one wrapped in\n * `rc-disclosure`.\n */\nexport class RCAccordion extends HTMLElement {\n private _$managedGroupNames = new Map<HTMLDetailsElement, string>();\n\n private _childrenObserver = new MutationObserver((records) => this._onChildrenChanged(records));\n\n static get observedAttributes(): string[] {\n return ['multiple', 'name'];\n }\n\n /** Allows more than one panel open at a time. */\n get multiple(): boolean {\n return this.hasAttribute('multiple');\n }\n\n set multiple(value: boolean) {\n this.toggleAttribute('multiple', value);\n }\n\n connectedCallback(): void {\n const root = this.getRootNode() as Document | ShadowRoot;\n\n ensureAccordionBaseStyles(root);\n // Panels share the disclosure recipe whether or not an rc-disclosure wraps\n // them, so an accordion of bare <details> children is styled the same way.\n ensureDisclosureBaseStyles(root);\n\n this._childrenObserver.observe(this, { childList: true, subtree: true });\n\n this.addEventListener('toggle', this._onDetailsToggle, true);\n this.addEventListener('keydown', this._onKeydown);\n\n this._syncNativeGroupNames();\n this._enforceSingleOpen();\n }\n\n disconnectedCallback(): void {\n this._childrenObserver.disconnect();\n\n this.removeEventListener('toggle', this._onDetailsToggle, true);\n this.removeEventListener('keydown', this._onKeydown);\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) {\n return;\n }\n\n if (name === 'name' || name === 'multiple') {\n this._syncNativeGroupNames();\n }\n\n if (name === 'multiple' && !this.multiple) {\n this._enforceSingleOpen();\n }\n }\n\n private _$details(): HTMLDetailsElement[] {\n return Array.from(\n this.querySelectorAll<HTMLDetailsElement>(\n ':scope > details, :scope > rc-disclosure > details',\n ),\n );\n }\n\n private _$summaries(): HTMLElement[] {\n return this._$details()\n .map(($details) => $details.querySelector<HTMLElement>(':scope > summary'))\n .filter(($summary): $summary is HTMLElement => $summary !== null);\n }\n\n private _syncNativeGroupNames(): void {\n const groupName = this.getAttribute('name');\n const $details = this._$details();\n const $currentDetails = new Set($details);\n\n for (const [$el, managedName] of this._$managedGroupNames) {\n const shouldReleaseName =\n !$currentDetails.has($el) ||\n this.multiple ||\n !groupName ||\n managedName !== groupName ||\n $el.getAttribute('name') !== managedName;\n\n if (!shouldReleaseName) {\n continue;\n }\n\n if ($el.getAttribute('name') === managedName) {\n $el.removeAttribute('name');\n }\n\n this._$managedGroupNames.delete($el);\n }\n\n if (this.multiple || !groupName) {\n return;\n }\n\n for (const $el of $details) {\n if ($el.hasAttribute('name') && !this._$managedGroupNames.has($el)) {\n continue;\n }\n\n $el.setAttribute('name', groupName);\n this._$managedGroupNames.set($el, groupName);\n }\n }\n\n private _onKeydown = (e: KeyboardEvent): void => {\n if (e.altKey || e.ctrlKey || e.metaKey) {\n return;\n }\n\n if (!isNavigationKey(e.key)) {\n return;\n }\n\n const $summaries = this._$summaries();\n\n if ($summaries.length === 0) {\n return;\n }\n\n const $active = document.activeElement as HTMLElement | null;\n const index = $active ? $summaries.indexOf($active) : -1;\n\n if (index === -1) {\n return;\n }\n\n e.preventDefault();\n\n if (e.key === 'Home') {\n const $firstSummary = $summaries.at(0);\n\n if (!$firstSummary) {\n return;\n }\n\n $firstSummary.focus();\n\n return;\n }\n\n if (e.key === 'End') {\n const $lastSummary = $summaries.at(-1);\n\n if (!$lastSummary) {\n return;\n }\n\n $lastSummary.focus();\n\n return;\n }\n\n const next =\n e.key === 'ArrowDown'\n ? (index + 1) % $summaries.length\n : (index - 1 + $summaries.length) % $summaries.length;\n\n const $nextSummary = $summaries.at(next);\n\n if (!$nextSummary) {\n return;\n }\n\n $nextSummary.focus();\n };\n\n private _onDetailsToggle = (e: Event): void => {\n const $opened = e.target;\n\n if (!($opened instanceof HTMLDetailsElement)) {\n return;\n }\n\n if (this.multiple) {\n return;\n }\n\n if (!$opened.open) {\n return;\n }\n\n this._enforceSingleOpen($opened);\n };\n\n private _onChildrenChanged(records: MutationRecord[]): void {\n this._syncNativeGroupNames();\n\n if (this.multiple) {\n return;\n }\n\n this._enforceSingleOpen(this._findAddedOpenDetails(records));\n }\n\n private _findAddedOpenDetails(records: MutationRecord[]): HTMLDetailsElement | undefined {\n const $details = this._$details();\n\n const $added = $details.filter(($el) =>\n records.some((record) =>\n Array.from(record.addedNodes).some(\n ($node) => $node === $el || ($node instanceof Element && $node.contains($el)),\n ),\n ),\n );\n\n for (let i = $added.length - 1; i >= 0; i -= 1) {\n const $el = $added.at(i);\n\n if (!$el) {\n continue;\n }\n\n if ($el.open) {\n return $el;\n }\n }\n\n return undefined;\n }\n\n private _enforceSingleOpen($target?: HTMLDetailsElement): void {\n const $details = this._$details();\n\n if ($target && !$details.includes($target)) {\n return;\n }\n\n const $openDetails = $details.filter(($el) => $el.open);\n\n if ($openDetails.length <= 1) {\n return;\n }\n\n const $keepOpen = $target ?? $openDetails.at(0);\n\n if (!$keepOpen) {\n return;\n }\n\n // Close the native <details> rather than an rc-disclosure wrapper's `open`\n // property: writing that property takes control of the disclosure, which\n // then refuses to reopen from its summary. A wrapper reads the native\n // toggle like any user toggle, so an uncontrolled one stays uncontrolled\n // and a controlled one receives the close as a request.\n for (const $el of $openDetails) {\n if ($el !== $keepOpen) {\n $el.open = false;\n }\n }\n }\n}\n\nexport default RCAccordion;\n"],"names":["ACCORDION_BASE_CSS","cssText","styledRoots","ensureAccordionBaseStyles","root","$style","NAVIGATION_KEYS","isNavigationKey","key","RCAccordion","records","$summaries","$active","index","$firstSummary","$lastSummary","next","$nextSummary","$opened","value","ensureDisclosureBaseStyles","name","oldValue","newValue","$details","$summary","groupName","$currentDetails","$el","managedName","$added","record","$node","i","$target","$openDetails","$keepOpen"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "@rcarls/rc-disclosure/define";
|
|
2
|
-
import {
|
|
2
|
+
import { R as o } from "./rc-accordion-B6TMttQh.js";
|
|
3
3
|
customElements.get("rc-accordion") || customElements.define("rc-accordion", o);
|
|
4
4
|
//# sourceMappingURL=rc-accordion-define.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rc-accordion-define.js","sources":["../src/define.ts"],"sourcesContent":["import '@rcarls/rc-disclosure/define';\
|
|
1
|
+
{"version":3,"file":"rc-accordion-define.js","sources":["../src/define.ts"],"sourcesContent":["import '@rcarls/rc-disclosure/define';\nimport { RCAccordion } from './index.js';\n\nif (!customElements.get('rc-accordion')) {\n customElements.define('rc-accordion', RCAccordion);\n}\n"],"names":["RCAccordion"],"mappings":";;AAGK,eAAe,IAAI,cAAc,KACpC,eAAe,OAAO,gBAAgBA,CAAW;"}
|
package/dist/rc-accordion.js
CHANGED
|
@@ -1,118 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
function u(l) {
|
|
3
|
-
return a.includes(l);
|
|
4
|
-
}
|
|
5
|
-
function c(l) {
|
|
6
|
-
return l?.localName === "rc-disclosure";
|
|
7
|
-
}
|
|
8
|
-
class m extends HTMLElement {
|
|
9
|
-
constructor() {
|
|
10
|
-
super(...arguments), this._$managedGroupNames = /* @__PURE__ */ new Map(), this._childrenObserver = new MutationObserver((e) => this._onChildrenChanged(e)), this._onKeydown = (e) => {
|
|
11
|
-
if (e.altKey || e.ctrlKey || e.metaKey || !u(e.key))
|
|
12
|
-
return;
|
|
13
|
-
const t = this._$summaries();
|
|
14
|
-
if (t.length === 0)
|
|
15
|
-
return;
|
|
16
|
-
const i = document.activeElement, n = i ? t.indexOf(i) : -1;
|
|
17
|
-
if (n === -1)
|
|
18
|
-
return;
|
|
19
|
-
if (e.preventDefault(), e.key === "Home") {
|
|
20
|
-
const o = t.at(0);
|
|
21
|
-
if (!o)
|
|
22
|
-
return;
|
|
23
|
-
o.focus();
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
if (e.key === "End") {
|
|
27
|
-
const o = t.at(-1);
|
|
28
|
-
if (!o)
|
|
29
|
-
return;
|
|
30
|
-
o.focus();
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const s = e.key === "ArrowDown" ? (n + 1) % t.length : (n - 1 + t.length) % t.length, r = t.at(s);
|
|
34
|
-
r && r.focus();
|
|
35
|
-
}, this._onDetailsToggle = (e) => {
|
|
36
|
-
const t = e.target;
|
|
37
|
-
t instanceof HTMLDetailsElement && (this.multiple || t.open && this._enforceSingleOpen(t));
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
static get observedAttributes() {
|
|
41
|
-
return ["multiple", "name"];
|
|
42
|
-
}
|
|
43
|
-
/** Allows more than one panel open at a time. */
|
|
44
|
-
get multiple() {
|
|
45
|
-
return this.hasAttribute("multiple");
|
|
46
|
-
}
|
|
47
|
-
set multiple(e) {
|
|
48
|
-
this.toggleAttribute("multiple", e);
|
|
49
|
-
}
|
|
50
|
-
connectedCallback() {
|
|
51
|
-
this._childrenObserver.observe(this, { childList: !0, subtree: !0 }), this.addEventListener("toggle", this._onDetailsToggle, !0), this.addEventListener("keydown", this._onKeydown), this._syncNativeGroupNames(), this._enforceSingleOpen();
|
|
52
|
-
}
|
|
53
|
-
disconnectedCallback() {
|
|
54
|
-
this._childrenObserver.disconnect(), this.removeEventListener("toggle", this._onDetailsToggle, !0), this.removeEventListener("keydown", this._onKeydown);
|
|
55
|
-
}
|
|
56
|
-
attributeChangedCallback(e, t, i) {
|
|
57
|
-
t !== i && ((e === "name" || e === "multiple") && this._syncNativeGroupNames(), e === "multiple" && !this.multiple && this._enforceSingleOpen());
|
|
58
|
-
}
|
|
59
|
-
_$details() {
|
|
60
|
-
return Array.from(
|
|
61
|
-
this.querySelectorAll(
|
|
62
|
-
":scope > details, :scope > rc-disclosure > details"
|
|
63
|
-
)
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
_$summaries() {
|
|
67
|
-
return this._$details().map((e) => e.querySelector(":scope > summary")).filter((e) => e !== null);
|
|
68
|
-
}
|
|
69
|
-
_syncNativeGroupNames() {
|
|
70
|
-
const e = this.getAttribute("name"), t = this._$details(), i = new Set(t);
|
|
71
|
-
for (const [n, s] of this._$managedGroupNames)
|
|
72
|
-
(!i.has(n) || this.multiple || !e || s !== e || n.getAttribute("name") !== s) && (n.getAttribute("name") === s && n.removeAttribute("name"), this._$managedGroupNames.delete(n));
|
|
73
|
-
if (!(this.multiple || !e))
|
|
74
|
-
for (const n of t)
|
|
75
|
-
n.hasAttribute("name") && !this._$managedGroupNames.has(n) || (n.setAttribute("name", e), this._$managedGroupNames.set(n, e));
|
|
76
|
-
}
|
|
77
|
-
_setDetailsOpen(e, t) {
|
|
78
|
-
const i = e.parentElement;
|
|
79
|
-
if (c(i)) {
|
|
80
|
-
i.open = t;
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
e.open = t;
|
|
84
|
-
}
|
|
85
|
-
_onChildrenChanged(e) {
|
|
86
|
-
this._syncNativeGroupNames(), !this.multiple && this._enforceSingleOpen(this._findAddedOpenDetails(e));
|
|
87
|
-
}
|
|
88
|
-
_findAddedOpenDetails(e) {
|
|
89
|
-
const i = this._$details().filter(
|
|
90
|
-
(n) => e.some(
|
|
91
|
-
(s) => Array.from(s.addedNodes).some(
|
|
92
|
-
(r) => r === n || r instanceof Element && r.contains(n)
|
|
93
|
-
)
|
|
94
|
-
)
|
|
95
|
-
);
|
|
96
|
-
for (let n = i.length - 1; n >= 0; n -= 1) {
|
|
97
|
-
const s = i.at(n);
|
|
98
|
-
if (s && s.open)
|
|
99
|
-
return s;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
_enforceSingleOpen(e) {
|
|
103
|
-
const t = this._$details();
|
|
104
|
-
if (e && !t.includes(e))
|
|
105
|
-
return;
|
|
106
|
-
const i = t.filter((s) => s.open);
|
|
107
|
-
if (i.length <= 1)
|
|
108
|
-
return;
|
|
109
|
-
const n = e ?? i.at(0);
|
|
110
|
-
if (n)
|
|
111
|
-
for (const s of i)
|
|
112
|
-
s !== n && this._setDetailsOpen(s, !1);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
1
|
+
import { A as s, R as r, e as A } from "./rc-accordion-B6TMttQh.js";
|
|
115
2
|
export {
|
|
116
|
-
|
|
3
|
+
s as ACCORDION_BASE_CSS,
|
|
4
|
+
r as RCAccordion,
|
|
5
|
+
A as ensureAccordionBaseStyles
|
|
117
6
|
};
|
|
118
7
|
//# sourceMappingURL=rc-accordion.js.map
|
package/dist/rc-accordion.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rc-accordion.js","sources":["../src/rc-accordion.ts"],"sourcesContent":["const NAVIGATION_KEYS = ['ArrowDown', 'ArrowUp', 'Home', 'End'] as const;\n\ntype NavigationKey = (typeof NAVIGATION_KEYS)[number];\n\ntype RCDisclosureElement = HTMLElement & {\n open: boolean;\n};\n\nfunction isNavigationKey(key: string): key is NavigationKey {\n return NAVIGATION_KEYS.includes(key as NavigationKey);\n}\n\nfunction isRCDisclosureElement($el: Element | null): $el is RCDisclosureElement {\n return $el?.localName === 'rc-disclosure';\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-accordion': RCAccordion;\n }\n}\n\n/**\n * Accordion coordinator for child native <details> panels with single- or multiple-open\n * behavior, following the WAI-ARIA Accordion pattern.\n *\n * Supports native details and rc-disclosure wrappers.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-accordion rc-accordion docs}\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/accordion/ WAI-ARIA Accordion pattern}\n *\n * @attr multiple - Allows more than one panel open at a time.\n * @attr name - Group name mirrored onto each managed native `<details>` element's own\n * `name` attribute (native details name-grouping), so long as `multiple` is not set.\n * Child `<details>` elements that already declare their own `name` are left alone.\n */\nexport class RCAccordion extends HTMLElement {\n private _$managedGroupNames = new Map<HTMLDetailsElement, string>();\n\n private _childrenObserver = new MutationObserver((records) => this._onChildrenChanged(records));\n\n static get observedAttributes(): string[] {\n return ['multiple', 'name'];\n }\n\n /** Allows more than one panel open at a time. */\n get multiple(): boolean {\n return this.hasAttribute('multiple');\n }\n\n set multiple(value: boolean) {\n this.toggleAttribute('multiple', value);\n }\n\n connectedCallback(): void {\n this._childrenObserver.observe(this, { childList: true, subtree: true });\n\n this.addEventListener('toggle', this._onDetailsToggle, true);\n this.addEventListener('keydown', this._onKeydown);\n\n this._syncNativeGroupNames();\n this._enforceSingleOpen();\n }\n\n disconnectedCallback(): void {\n this._childrenObserver.disconnect();\n\n this.removeEventListener('toggle', this._onDetailsToggle, true);\n this.removeEventListener('keydown', this._onKeydown);\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) {\n return;\n }\n\n if (name === 'name' || name === 'multiple') {\n this._syncNativeGroupNames();\n }\n\n if (name === 'multiple' && !this.multiple) {\n this._enforceSingleOpen();\n }\n }\n\n private _$details(): HTMLDetailsElement[] {\n return Array.from(\n this.querySelectorAll<HTMLDetailsElement>(\n ':scope > details, :scope > rc-disclosure > details',\n ),\n );\n }\n\n private _$summaries(): HTMLElement[] {\n return this._$details()\n .map(($details) => $details.querySelector<HTMLElement>(':scope > summary'))\n .filter(($summary): $summary is HTMLElement => $summary !== null);\n }\n\n private _syncNativeGroupNames(): void {\n const groupName = this.getAttribute('name');\n const $details = this._$details();\n const $currentDetails = new Set($details);\n\n for (const [$el, managedName] of this._$managedGroupNames) {\n const shouldReleaseName =\n !$currentDetails.has($el) ||\n this.multiple ||\n !groupName ||\n managedName !== groupName ||\n $el.getAttribute('name') !== managedName;\n\n if (!shouldReleaseName) {\n continue;\n }\n\n if ($el.getAttribute('name') === managedName) {\n $el.removeAttribute('name');\n }\n\n this._$managedGroupNames.delete($el);\n }\n\n if (this.multiple || !groupName) {\n return;\n }\n\n for (const $el of $details) {\n if ($el.hasAttribute('name') && !this._$managedGroupNames.has($el)) {\n continue;\n }\n\n $el.setAttribute('name', groupName);\n this._$managedGroupNames.set($el, groupName);\n }\n }\n\n private _onKeydown = (e: KeyboardEvent): void => {\n if (e.altKey || e.ctrlKey || e.metaKey) {\n return;\n }\n\n if (!isNavigationKey(e.key)) {\n return;\n }\n\n const $summaries = this._$summaries();\n\n if ($summaries.length === 0) {\n return;\n }\n\n const $active = document.activeElement as HTMLElement | null;\n const index = $active ? $summaries.indexOf($active) : -1;\n\n if (index === -1) {\n return;\n }\n\n e.preventDefault();\n\n if (e.key === 'Home') {\n const $firstSummary = $summaries.at(0);\n\n if (!$firstSummary) {\n return;\n }\n\n $firstSummary.focus();\n\n return;\n }\n\n if (e.key === 'End') {\n const $lastSummary = $summaries.at(-1);\n\n if (!$lastSummary) {\n return;\n }\n\n $lastSummary.focus();\n\n return;\n }\n\n const next =\n e.key === 'ArrowDown'\n ? (index + 1) % $summaries.length\n : (index - 1 + $summaries.length) % $summaries.length;\n\n const $nextSummary = $summaries.at(next);\n\n if (!$nextSummary) {\n return;\n }\n\n $nextSummary.focus();\n };\n\n private _onDetailsToggle = (e: Event): void => {\n const $opened = e.target;\n\n if (!($opened instanceof HTMLDetailsElement)) {\n return;\n }\n\n if (this.multiple) {\n return;\n }\n\n if (!$opened.open) {\n return;\n }\n\n this._enforceSingleOpen($opened);\n };\n\n private _setDetailsOpen($target: HTMLDetailsElement, open: boolean): void {\n const $parent = $target.parentElement;\n\n if (isRCDisclosureElement($parent)) {\n $parent.open = open;\n\n return;\n }\n\n $target.open = open;\n }\n\n private _onChildrenChanged(records: MutationRecord[]): void {\n this._syncNativeGroupNames();\n\n if (this.multiple) {\n return;\n }\n\n this._enforceSingleOpen(this._findAddedOpenDetails(records));\n }\n\n private _findAddedOpenDetails(records: MutationRecord[]): HTMLDetailsElement | undefined {\n const $details = this._$details();\n\n const $added = $details.filter(($el) =>\n records.some((record) =>\n Array.from(record.addedNodes).some(\n ($node) => $node === $el || ($node instanceof Element && $node.contains($el)),\n ),\n ),\n );\n\n for (let i = $added.length - 1; i >= 0; i -= 1) {\n const $el = $added.at(i);\n\n if (!$el) {\n continue;\n }\n\n if ($el.open) {\n return $el;\n }\n }\n\n return undefined;\n }\n\n private _enforceSingleOpen($target?: HTMLDetailsElement): void {\n const $details = this._$details();\n\n if ($target && !$details.includes($target)) {\n return;\n }\n\n const $openDetails = $details.filter(($el) => $el.open);\n\n if ($openDetails.length <= 1) {\n return;\n }\n\n const $keepOpen = $target ?? $openDetails.at(0);\n\n if (!$keepOpen) {\n return;\n }\n\n for (const $el of $openDetails) {\n if ($el !== $keepOpen) {\n this._setDetailsOpen($el, false);\n }\n }\n }\n}\n\nexport default RCAccordion;\n"],"names":["NAVIGATION_KEYS","isNavigationKey","key","isRCDisclosureElement","$el","RCAccordion","records","$summaries","$active","index","$firstSummary","$lastSummary","next","$nextSummary","$opened","value","name","oldValue","newValue","$details","$summary","groupName","$currentDetails","managedName","$target","open","$parent","$added","record","$node","i","$openDetails","$keepOpen"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rc-accordion.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Light-DOM base styles for the accordion group box.
|
|
3
|
+
*
|
|
4
|
+
* The panels themselves are not styled here. Both supported child forms, a
|
|
5
|
+
* native `<details>` directly inside `rc-accordion` and one wrapped in
|
|
6
|
+
* `rc-disclosure`, consume the shared `--rc-disclosure-*` properties through the
|
|
7
|
+
* recipe that `rc-disclosure` installs, so there is no parallel accordion-panel
|
|
8
|
+
* vocabulary to keep in step.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ACCORDION_BASE_CSS: string;
|
|
11
|
+
/** Installs the accordion group styles once per containing root. */
|
|
12
|
+
export declare function ensureAccordionBaseStyles(root: Document | ShadowRoot): void;
|
|
@@ -16,6 +16,12 @@ declare global {
|
|
|
16
16
|
* @attr name - Group name mirrored onto each managed native `<details>` element's own
|
|
17
17
|
* `name` attribute (native details name-grouping), so long as `multiple` is not set.
|
|
18
18
|
* Child `<details>` elements that already declare their own `name` are left alone.
|
|
19
|
+
*
|
|
20
|
+
* @cssprop [--rc-accordion-gap=0] - Gap between panels.
|
|
21
|
+
*
|
|
22
|
+
* Panels take the shared `--rc-disclosure-*` properties, documented on `rc-disclosure`, for both
|
|
23
|
+
* supported child forms: a native `<details>` directly inside the accordion, and one wrapped in
|
|
24
|
+
* `rc-disclosure`.
|
|
19
25
|
*/
|
|
20
26
|
export declare class RCAccordion extends HTMLElement {
|
|
21
27
|
private _$managedGroupNames;
|
|
@@ -32,7 +38,6 @@ export declare class RCAccordion extends HTMLElement {
|
|
|
32
38
|
private _syncNativeGroupNames;
|
|
33
39
|
private _onKeydown;
|
|
34
40
|
private _onDetailsToggle;
|
|
35
|
-
private _setDetailsOpen;
|
|
36
41
|
private _onChildrenChanged;
|
|
37
42
|
private _findAddedOpenDetails;
|
|
38
43
|
private _enforceSingleOpen;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.7.0",
|
|
7
7
|
"description": "Accordion coordinator for child native <details> panels with single- or multiple-open behavior.",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"test:browser:firefox": "vitest --run --project=firefox"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@rcarls/rc-
|
|
47
|
+
"@rcarls/rc-common": "0.7.0",
|
|
48
|
+
"@rcarls/rc-disclosure": "0.7.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@custom-elements-manifest/analyzer": "0.11.0",
|
|
File without changes
|