@penn-libraries/web 1.4.0-dev.1 → 1.4.0-dev.3

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.
Files changed (61) hide show
  1. package/dist/cjs/{index-DATEsMXP.js → index-DuAtidph.js} +4 -4
  2. package/dist/cjs/index-DuAtidph.js.map +1 -0
  3. package/dist/cjs/index.cjs.js +1 -1
  4. package/dist/cjs/loader.cjs.js +2 -2
  5. package/dist/cjs/pennlibs-accordion.pennlibs-expand-text.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.cjs.js.map +1 -0
  6. package/dist/cjs/{pennlibs-expand-text_4.cjs.entry.js → pennlibs-accordion_5.cjs.entry.js} +168 -2
  7. package/dist/cjs/pennlibs-autocomplete.cjs.entry.js +1 -1
  8. package/dist/cjs/pennlibs-banner.cjs.entry.js +1 -1
  9. package/dist/cjs/pennlibs-chat.cjs.entry.js +1 -1
  10. package/dist/cjs/pennlibs-fallback-img.cjs.entry.js +1 -1
  11. package/dist/cjs/pennlibs-hero.cjs.entry.js +1 -1
  12. package/dist/cjs/pennlibs-iiif-img.cjs.entry.js +1 -1
  13. package/dist/cjs/web.cjs.js +2 -2
  14. package/dist/collection/collection-manifest.json +1 -0
  15. package/dist/collection/components/pennlibs-accordion/pennlibs-accordion.css +9 -0
  16. package/dist/collection/components/pennlibs-accordion/pennlibs-accordion.js +228 -0
  17. package/dist/collection/components/pennlibs-accordion/pennlibs-accordion.js.map +1 -0
  18. package/dist/components/index.d.ts +2 -0
  19. package/dist/components/index.js +1 -0
  20. package/dist/components/index.js.map +1 -1
  21. package/dist/components/pennlibs-accordion.d.ts +11 -0
  22. package/dist/components/pennlibs-accordion.js +192 -0
  23. package/dist/components/pennlibs-accordion.js.map +1 -0
  24. package/dist/docs.json +71 -1
  25. package/dist/{web/p-CgmxDHmi.js → esm/index-BXL8iX4F.js} +4 -4
  26. package/dist/esm/index-BXL8iX4F.js.map +1 -0
  27. package/dist/esm/index.js +1 -1
  28. package/dist/esm/loader.js +3 -3
  29. package/dist/esm/pennlibs-accordion.pennlibs-expand-text.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.js.map +1 -0
  30. package/dist/esm/{pennlibs-expand-text_4.entry.js → pennlibs-accordion_5.entry.js} +168 -3
  31. package/dist/esm/pennlibs-autocomplete.entry.js +1 -1
  32. package/dist/esm/pennlibs-banner.entry.js +1 -1
  33. package/dist/esm/pennlibs-chat.entry.js +1 -1
  34. package/dist/esm/pennlibs-fallback-img.entry.js +1 -1
  35. package/dist/esm/pennlibs-hero.entry.js +1 -1
  36. package/dist/esm/pennlibs-iiif-img.entry.js +1 -1
  37. package/dist/esm/web.js +3 -3
  38. package/dist/types/components/pennlibs-accordion/pennlibs-accordion.d.ts +44 -0
  39. package/dist/types/components.d.ts +89 -0
  40. package/dist/web/index.esm.js +1 -1
  41. package/dist/web/{p-eda436e7.entry.js → p-5bf6e5f8.entry.js} +1 -1
  42. package/dist/web/{p-2d7c03b3.entry.js → p-5f1cef00.entry.js} +1 -1
  43. package/dist/web/{p-7c1febd2.entry.js → p-61ae9fb2.entry.js} +1 -1
  44. package/dist/web/{p-6ff0271c.entry.js → p-64e74684.entry.js} +1 -1
  45. package/dist/web/{p-b674bdca.entry.js → p-8b17cc4d.entry.js} +1 -1
  46. package/dist/web/{p-6eef97ad.entry.js → p-9de28f5c.entry.js} +1 -1
  47. package/dist/{esm/index-CgmxDHmi.js → web/p-BXL8iX4F.js} +4 -4
  48. package/dist/web/p-BXL8iX4F.js.map +1 -0
  49. package/dist/web/{p-ee89e08f.entry.js → p-ed1b5ac4.entry.js} +168 -3
  50. package/dist/web/pennlibs-accordion.pennlibs-expand-text.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.esm.js.map +1 -0
  51. package/dist/web/web.css +86 -5
  52. package/dist/web/web.esm.js +3 -3
  53. package/hydrate/index.js +197 -1
  54. package/hydrate/index.mjs +197 -1
  55. package/package.json +1 -1
  56. package/dist/cjs/index-DATEsMXP.js.map +0 -1
  57. package/dist/cjs/pennlibs-expand-text.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.cjs.js.map +0 -1
  58. package/dist/esm/index-CgmxDHmi.js.map +0 -1
  59. package/dist/esm/pennlibs-expand-text.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.js.map +0 -1
  60. package/dist/web/p-CgmxDHmi.js.map +0 -1
  61. package/dist/web/pennlibs-expand-text.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.esm.js.map +0 -1
@@ -1,6 +1,171 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DATEsMXP.js');
3
+ var index = require('./index-DuAtidph.js');
4
+
5
+ const pennlibsAccordionCss = "pennlibs-accordion{display:contents}";
6
+
7
+ let accordionUid = 0;
8
+ const Accordion = class {
9
+ constructor(hostRef) {
10
+ index.registerInstance(this, hostRef);
11
+ /**
12
+ * When set, only one panel can be open at a time, so opening one closes the
13
+ * others, like a classic accordion.
14
+ * @prop single
15
+ */
16
+ this.single = false;
17
+ this.uid = accordionUid++;
18
+ this.items = [];
19
+ this.summaries = [];
20
+ // Roving focus between headers per the WAI-ARIA Accordion keyboard model.
21
+ // Enter/Space toggling is left to the browser's native <details> behavior.
22
+ this.onKeydown = (event) => {
23
+ var _a;
24
+ const current = event.currentTarget;
25
+ const index = this.summaries.indexOf(current);
26
+ if (index === -1)
27
+ return;
28
+ const count = this.summaries.length;
29
+ let next;
30
+ switch (event.key) {
31
+ case 'ArrowDown':
32
+ next = (index + 1) % count;
33
+ break;
34
+ case 'ArrowUp':
35
+ next = (index - 1 + count) % count;
36
+ break;
37
+ case 'Home':
38
+ next = 0;
39
+ break;
40
+ case 'End':
41
+ next = count - 1;
42
+ break;
43
+ default:
44
+ return;
45
+ }
46
+ event.preventDefault();
47
+ (_a = this.summaries[next]) === null || _a === void 0 ? void 0 : _a.focus();
48
+ };
49
+ this.onToggle = (event) => {
50
+ if (!this.single)
51
+ return;
52
+ const details = event.target;
53
+ if (!details.open)
54
+ return;
55
+ this.items.forEach((other) => {
56
+ if (other !== details && other.open)
57
+ other.open = false;
58
+ });
59
+ };
60
+ }
61
+ componentDidLoad() {
62
+ this.collectItems();
63
+ this.enhanceItems();
64
+ this.syncControls(this.el, this.items.some((details) => details.open));
65
+ }
66
+ disconnectedCallback() {
67
+ this.summaries.forEach((summary) => summary.removeEventListener('keydown', this.onKeydown));
68
+ this.items.forEach((details) => details.removeEventListener('toggle', this.onToggle));
69
+ }
70
+ collectItems() {
71
+ this.items = Array.from(this.el.querySelectorAll(':scope > details, :scope > .pl-details'));
72
+ this.summaries = this.items
73
+ .map((details) => details.querySelector(':scope > summary'))
74
+ .filter((summary) => summary instanceof HTMLElement);
75
+ }
76
+ enhanceItems() {
77
+ this.items.forEach((details, index) => {
78
+ const summary = details.querySelector(':scope > summary');
79
+ if (!summary)
80
+ return;
81
+ const panel = summary.nextElementSibling;
82
+ const baseId = `pl-accordion-${this.uid}-${index}`;
83
+ // Wire the summary (the disclosure button) to its panel as a labelled
84
+ // region. Native <summary> already exposes the button role and
85
+ // aria-expanded, so we only add what it lacks.
86
+ if (!summary.id)
87
+ summary.id = `${baseId}-summary`;
88
+ if (panel) {
89
+ if (!panel.id)
90
+ panel.id = `${baseId}-panel`;
91
+ if (!panel.hasAttribute('role'))
92
+ panel.setAttribute('role', 'region');
93
+ if (!panel.hasAttribute('aria-labelledby'))
94
+ panel.setAttribute('aria-labelledby', summary.id);
95
+ summary.setAttribute('aria-controls', panel.id);
96
+ }
97
+ summary.addEventListener('keydown', this.onKeydown);
98
+ details.addEventListener('toggle', this.onToggle);
99
+ });
100
+ }
101
+ // A declarative toggle-all control: any element marked with
102
+ // `data-pl-accordion-toggle`. With no value it controls the closest enclosing
103
+ // accordion; set the value to an accordion's id to control one from elsewhere
104
+ // on the page. Add `data-pl-accordion-toggle-deep` to also reach nested
105
+ // accordions, or a number for that many levels. `data-pl-expand-label` and
106
+ // `data-pl-collapse-label` swap its text to match the state.
107
+ onControlClick(event) {
108
+ var _a;
109
+ const control = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('[data-pl-accordion-toggle]');
110
+ if (!control || this.controlTarget(control) !== this.el)
111
+ return;
112
+ event.preventDefault();
113
+ const expanded = !this.items.some((details) => details.open);
114
+ this.setAll(this.el, expanded, this.controlDepth(control));
115
+ }
116
+ // The accordion a control points at: the element whose id is in the attribute
117
+ // value, or the closest enclosing accordion when the value is empty.
118
+ controlTarget(control) {
119
+ const id = control.getAttribute('data-pl-accordion-toggle');
120
+ return id ? document.getElementById(id) : control.closest('pennlibs-accordion');
121
+ }
122
+ // How many nested levels a control reaches: absent is none, a bare attribute
123
+ // is every level, and a non-negative number is that many levels down.
124
+ controlDepth(control) {
125
+ if (!control.hasAttribute('data-pl-accordion-toggle-deep'))
126
+ return 0;
127
+ const raw = control.getAttribute('data-pl-accordion-toggle-deep');
128
+ if (raw === null || raw === '')
129
+ return Infinity;
130
+ const levels = parseInt(raw, 10);
131
+ return Number.isFinite(levels) && levels >= 0 ? levels : Infinity;
132
+ }
133
+ // Expand or collapse every item in an accordion, pushing the same direction
134
+ // `levels` deep. Each level reads its own single-open rule, so nothing forces
135
+ // two panels open in a single-open group.
136
+ setAll(accordion, expanded, levels) {
137
+ const items = Array.from(accordion.querySelectorAll(':scope > details, :scope > .pl-details'));
138
+ if (expanded && accordion.single) {
139
+ items.forEach((details, index) => (details.open = index === 0));
140
+ }
141
+ else {
142
+ items.forEach((details) => (details.open = expanded));
143
+ }
144
+ this.syncControls(accordion, expanded);
145
+ if (levels > 0) {
146
+ this.nestedAccordions(accordion).forEach((nested) => this.setAll(nested, expanded, levels - 1));
147
+ }
148
+ }
149
+ // The accordions one level down from the given one (not their descendants).
150
+ nestedAccordions(accordion) {
151
+ return Array.from(accordion.querySelectorAll('pennlibs-accordion')).filter((node) => { var _a; return ((_a = node.parentElement) === null || _a === void 0 ? void 0 : _a.closest('pennlibs-accordion')) === accordion; });
152
+ }
153
+ // Keep every control pointing at an accordion in sync with its state.
154
+ syncControls(accordion, expanded) {
155
+ document.querySelectorAll('[data-pl-accordion-toggle]').forEach((control) => {
156
+ if (this.controlTarget(control) !== accordion)
157
+ return;
158
+ control.setAttribute('aria-expanded', String(expanded));
159
+ const label = expanded
160
+ ? control.getAttribute('data-pl-collapse-label')
161
+ : control.getAttribute('data-pl-expand-label');
162
+ if (label)
163
+ control.textContent = label;
164
+ });
165
+ }
166
+ get el() { return index.getElement(this); }
167
+ };
168
+ Accordion.style = pennlibsAccordionCss;
4
169
 
5
170
  const pennlibsExpandTextCss = ":host{display:inline-block;position:relative;max-width:100%;vertical-align:bottom}.expand-text{display:block}.expand-text--clamped{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--pl-expand-text-lines, 3);line-clamp:var(--pl-expand-text-lines, 3);overflow:hidden}.expand-text--expanded{display:inline}.expand-text__fade{position:absolute;right:0;bottom:0;padding-left:2em;background:linear-gradient(\n to right,\n transparent,\n var(--pl-expand-text-bg, var(--pl-color-bg-default)) 1.5em\n );pointer-events:none}.expand-text__toggle{display:inline-block;vertical-align:baseline;pointer-events:auto;padding:var(--pl-space-3xs) var(--pl-space-s);border:none;background:var(--pl-color-bg-subtle);color:var(--pl-color-fg-default);font-family:var(--pl-font-family);border-radius:var(--pl-border-radius-subtle);font-size:var(--pl-font-size-xs);font-weight:500;line-height:1.5;letter-spacing:0.075rem;text-transform:uppercase;cursor:pointer}.expand-text__toggle:hover{text-decoration:underline;text-decoration-thickness:var(--pl-link-text-decoration-thickness);text-underline-offset:var(--pl-link-text-underline-offset)}.expand-text__toggle:focus-visible{outline:none;box-shadow:var(--pl-focus-box-shadow)}.expand-text__fade--inline{position:static;padding-left:0;background:none;margin-inline-start:var(--pl-space-2xs, 0.25rem)}";
6
171
 
@@ -261,8 +426,9 @@ const Header = class {
261
426
  };
262
427
  Header.style = pennlibsHeaderCss;
263
428
 
429
+ exports.pennlibs_accordion = Accordion;
264
430
  exports.pennlibs_expand_text = ExpandText;
265
431
  exports.pennlibs_feedback = PennlibsFeedback;
266
432
  exports.pennlibs_footer = Footer;
267
433
  exports.pennlibs_header = Header;
268
- //# sourceMappingURL=pennlibs-expand-text.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.cjs.js.map
434
+ //# sourceMappingURL=pennlibs-accordion.pennlibs-expand-text.pennlibs-feedback.pennlibs-footer.pennlibs-header.entry.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DATEsMXP.js');
3
+ var index = require('./index-DuAtidph.js');
4
4
 
5
5
  const pennlibsAutocompleteCss = ":host {\n display: block;\n width: 100%;\n border-radius: 1.25rem;\n padding: 0;\n border-top: 0;\n position: relative;\n}\n\n[role=listbox] {\n position: absolute;\n margin-top: var(--pl-space-xs);\n background: var(--pl-color-bg-default);\n border-radius: 1.25rem;\n box-shadow: rgba(140, 149, 159, 0.3) 0px 8px 24px 0px;\n width: 100%;\n overflow: hidden;\n z-index: 1;\n\n display: flex;\n flex-direction: column;\n}\n\np {\n margin: 0;\n font-size: var(--pl-font-size-s);\n color: var(--pl-color-fg-subtle);\n padding: var(--pl-space-xs) calc(var(--pl-space-m) + var(--pl-space-2xs));\n font-size: var(--pl-font-size-s);\n order: 2;\n font-weight: 500;\n background: var(--pl-color-bg-subtle);\n border-radius: 0 0 1.25rem 1.25rem;\n\n display: flex;\n gap: var(--pl-space-s) var(--pl-space-l);\n flex-wrap: wrap;\n}\n\nol {\n list-style: none;\n margin: 0;\n padding: var(--pl-space-xs) 0;\n order: 1;\n}\n\n[role=option] {\n color: var(--pl-color-fg-default);\n padding: var(--pl-space-s) calc(var(--pl-space-m) + var(--pl-space-2xs));\n text-decoration: none;\n font-weight: 700; \n\n &:hover {\n cursor: pointer;\n }\n\n &:hover,\n &:focus {\n text-decoration-thickness: 2px;\n text-underline-offset: 2px;\n text-decoration: underline;\n }\n}\n\n[aria-selected=true] {\n text-decoration-thickness: 2px;\n text-underline-offset: 2px;\n text-decoration: underline;\n}\n\nmark,\nb {\n background: none;\n font-weight: 400;\n}\n\n.suggestion--border {\n border-bottom: solid 1px rgb(from var(--pl-color-fg-default) r g b / 0.2);\n padding-bottom: calc(var(--pl-space-2xs) + var(--pl-space-s));\n margin-bottom: var(--pl-space-2xs);\n}";
6
6
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DATEsMXP.js');
3
+ var index = require('./index-DuAtidph.js');
4
4
 
5
5
  const pennlibsBannerCss = ":host{font-family:var(--pl-font-family);font-size:var(--pl-font-size);--max-width:1080px}.viewport-margins{max-width:calc(var(--max-width) + 0.5em);margin:0 auto;padding:0 var(--pl-viewport-margins-gutter, 1em)}.skip-to-content-link{position:absolute;transform:translateY(-300%);background:var(--pl-color-bg-default);left:0.5em;padding:0.5em 1em;margin-top:0.5em;position:absolute}.skip-to-content-link:focus{transform:translateY(0%);color:var(--pl-color-fg-default)}.universal-nav{background:var(--pl-color-penn-blue)}.universal-nav ul{display:flex;align-items:baseline;flex-wrap:wrap;scrollbar-color:var(--pl-color-penn-red);list-style:none;padding:0;margin:0}.universal-nav li{display:inline-block}.universal-nav a{display:inline-block;text-transform:uppercase;font-size:0.75em;letter-spacing:0.075em;font-weight:600;color:var(--pl-color-fg-subtle-on-emphasis);padding:0.5em;text-decoration:none}.universal-nav a:hover{text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:.15em}.universal-nav__shield-image{vertical-align:sub;height:1em;padding-right:0.5em;height:auto}";
6
6
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DATEsMXP.js');
3
+ var index = require('./index-DuAtidph.js');
4
4
 
5
5
  const pennlibsChatCss = ":host {\n font-family: var(--pl-font-family);\n font-size: var(--pl-font-size);\n line-height: normal;\n}\n\n@media print {\n :host {\n display: none;\n }\n}\n\na {\n display: flex;\n align-items: center;\n background: linear-gradient(45deg, #faa755, #fcca99);\n border-radius: 1em;\n position: fixed;\n bottom: 0.75em;\n right: 0.75em;\n box-shadow: rgba(0,0,0,0.3) 0px 2px 16px 0px;\n color: var(--pl-color-penn-blue);\n text-decoration: none;\n font-weight: 500;\n padding: 0.15em;\n font-size: 1em;\n\n @media (min-width: 820px) {\n bottom: 1.5em;\n right: 1.5em;\n width: auto;\n padding: 0.15em 0.75em;\n padding-left: 0.5em;\n }\n}\n\na:hover {\n text-decoration: underline;\n text-underline-offset: var(--pl-link-text-underline-offset);\n text-decoration-thickness: var(--pl-link-hover-text-decoration-thickness);\n}\n\na:hover,\na:focus {\n outline: none;\n box-shadow: 0 0 0 2px var(--pl-color-bg-attention),0 0 0 4px var(--pl-color-penn-blue), rgba(0,0,0,0.3) 0px 2px 16px 0px;;\n}\n\nsvg {\n width: 2.5em;\n height: 2.5em;\n}\n\nspan {\n display: none;\n font-size: 1.1em;\n\n @media (min-width: 820px) {\n display: inline;\n }\n}";
6
6
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DATEsMXP.js');
3
+ var index = require('./index-DuAtidph.js');
4
4
 
5
5
  const pennlibsFallbackImgCss = ":host{font-family:var(--pl-font-family);font-size:var(--pl-font-size);display:flex;align-items:center;justify-content:center;padding:1rem;background:var(--pl-color-bg-subtle);aspect-ratio:3/2}.no-image__img{width:100%;max-width:150px;filter:grayscale(1) opacity(0.3)}";
6
6
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DATEsMXP.js');
3
+ var index = require('./index-DuAtidph.js');
4
4
 
5
5
  const pennlibsHeroCss = ":host{--pl-hero-height:clamp(42vh, 32rem, 26rem);--pl-hero-heading-font:var(--pl-font-serif);--pl-hero-color:var(--pl-color-fg-on-emphasis)}*,*:before,*:after{box-sizing:inherit}.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.viewport-margins{width:100%;max-width:var(--pl-viewport-margins-max-width);margin:0 auto;padding:0 var(--pl-viewport-margins-gutter, 1em)}.hero{position:relative;min-height:var(--pl-hero-height);height:100%;background-size:cover;background-repeat:no-repeat;background-position:50% 33%;display:flex;container-type:inline-size}.hero::before{content:\"\";display:flex;width:100%;height:100%;top:0;position:absolute;background:linear-gradient(360deg, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.7) 20%, rgba(0, 0, 0, 0.4) 40%, rgba(0, 0, 0, 0.05) 100%);z-index:0}.hero::after{content:\"\";display:flex;width:100%;height:100%;top:0;position:absolute;background:linear-gradient(180deg, rgba(1, 31, 91, 1) 0%, rgba(1, 31, 91, 0.9) 10%, rgba(1, 31, 91, 0.8) 20%, rgba(1, 31, 91, 0.1) 50%, rgba(1, 31, 91, 0.0) 100%);z-index:0}.hero__content{position:relative;display:flex;flex-direction:column;width:100%;z-index:1}.hero__heading-container{margin-top:auto;padding-top:var(--pl-space-3xl);padding-bottom:var(--pl-space-3xl)}.hero__heading{text-shadow:1px 1px 2px var(--pl-color-fg-default);line-height:1;font-size:var(--pl-font-size-5xl);font-size:clamp(var(--pl-font-size-4xl), 3cqi + 1rem, var(--pl-font-size-5xl));font-weight:400;font-family:var(--pl-hero-heading-font);text-wrap:pretty;max-width:30ch;margin:0;color:var(--pl-hero-color)}.hero__sub-heading{font-size:1.25em;font-family:var(--pl-font-family);font-weight:400;color:var(--pl-hero-color);max-width:52ch;text-wrap:pretty;margin-top:var(--pl-space-s);margin-bottom:0}.hero__sub-heading a{text-decoration:underline;text-underline-offset:var(--pl-link-text-underline-offset);text-decoration-thickness:var(--pl-link-text-decoration-thickness);color:var(--pl-hero-color)}.hero__sub-heading a:hover{text-decoration-thickness:var(--pl-link-hover-text-decoration-thickness)}.hero__sub-heading strong{font-weight:bold}@media (max-width: 620px){.hero__sub-heading{font-size:1em}}";
6
6
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DATEsMXP.js');
3
+ var index = require('./index-DuAtidph.js');
4
4
 
5
5
  const pennlibsIiifImgCss = ":host{display:block;width:100%;max-width:100%;align-self:flex-start;aspect-ratio:var(--aspect-ratio, auto)}picture{display:block;line-height:0}.iiif-img{display:block;width:100%;max-width:100%;height:auto;opacity:0;filter:blur(5px);transition:opacity 0.15s ease-in, filter 0.15s ease-in}.iiif-img.loaded{opacity:1;filter:blur(0)}.fallback-container{display:block;width:100%;height:100%}.fallback-container pennlibs-fallback-img{width:100%;height:100%}pennlibs-fallback-img{display:block;width:100%;height:100%}";
6
6
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DATEsMXP.js');
3
+ var index = require('./index-DuAtidph.js');
4
4
 
5
5
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
6
6
  /*
@@ -18,7 +18,7 @@ var patchBrowser = () => {
18
18
 
19
19
  patchBrowser().then(async (options) => {
20
20
  await index.globalScripts();
21
- return index.bootstrapLazy([["pennlibs-iiif-img.cjs",[[257,"pennlibs-iiif-img",{"uuid":[1],"alt":[1],"region":[513],"rotation":[1],"quality":[1],"loading":[1],"showFallback":[4,"show-fallback"],"isLoaded":[32],"hasError":[32],"imageDimensions":[32],"observedWidth":[32]},null,{"region":["handleRegionChange"]}]]],["pennlibs-expand-text_4.cjs",[[257,"pennlibs-expand-text",{"lines":[2],"expandLabel":[1,"expand-label"],"collapseLabel":[1,"collapse-label"],"expanded":[32],"hasOverflow":[32],"measured":[32]}],[257,"pennlibs-feedback",{"hideQuestion":[4,"hide-question"],"answer":[32],"selected":[32]}],[257,"pennlibs-footer",{"showBuiltWith":[4,"show-built-with"],"navigationByChildren":[32]}],[257,"pennlibs-header",{"serviceName":[1,"service-name"],"serviceLede":[1,"service-lede"],"serviceHref":[1,"service-href"],"theme":[1],"isMenuOpen":[32],"navigation":[32]}]]],["pennlibs-autocomplete.cjs",[[257,"pennlibs-autocomplete",{"for":[1],"showSuggestions":[32],"currentIndex":[32],"originalValue":[32],"options":[32]},[[0,"slotchange","handleSlotChange"],[16,"input","handleInputEvent"],[16,"focus","handleFocusEvent"],[16,"blur","handleBlurEvent"],[0,"focusout","handleFocusOut"],[4,"keydown","handleKeyDown"]]]]],["pennlibs-banner.cjs",[[257,"pennlibs-banner"]]],["pennlibs-chat.cjs",[[257,"pennlibs-chat",{"href":[32]}]]],["pennlibs-hero.cjs",[[257,"pennlibs-hero",{"heroPictureElement":[32],"heroHeadingElement":[32],"heroParagraphElement":[32],"heroSrc":[32]}]]],["pennlibs-fallback-img.cjs",[[257,"pennlibs-fallback-img"]]]], options);
21
+ return index.bootstrapLazy([["pennlibs-iiif-img.cjs",[[257,"pennlibs-iiif-img",{"uuid":[1],"alt":[1],"region":[513],"rotation":[1],"quality":[1],"loading":[1],"showFallback":[4,"show-fallback"],"isLoaded":[32],"hasError":[32],"imageDimensions":[32],"observedWidth":[32]},null,{"region":["handleRegionChange"]}]]],["pennlibs-accordion_5.cjs",[[0,"pennlibs-accordion",{"single":[4]},[[4,"click","onControlClick"]]],[257,"pennlibs-expand-text",{"lines":[2],"expandLabel":[1,"expand-label"],"collapseLabel":[1,"collapse-label"],"expanded":[32],"hasOverflow":[32],"measured":[32]}],[257,"pennlibs-feedback",{"hideQuestion":[4,"hide-question"],"answer":[32],"selected":[32]}],[257,"pennlibs-footer",{"showBuiltWith":[4,"show-built-with"],"navigationByChildren":[32]}],[257,"pennlibs-header",{"serviceName":[1,"service-name"],"serviceLede":[1,"service-lede"],"serviceHref":[1,"service-href"],"theme":[1],"isMenuOpen":[32],"navigation":[32]}]]],["pennlibs-autocomplete.cjs",[[257,"pennlibs-autocomplete",{"for":[1],"showSuggestions":[32],"currentIndex":[32],"originalValue":[32],"options":[32]},[[0,"slotchange","handleSlotChange"],[16,"input","handleInputEvent"],[16,"focus","handleFocusEvent"],[16,"blur","handleBlurEvent"],[0,"focusout","handleFocusOut"],[4,"keydown","handleKeyDown"]]]]],["pennlibs-banner.cjs",[[257,"pennlibs-banner"]]],["pennlibs-chat.cjs",[[257,"pennlibs-chat",{"href":[32]}]]],["pennlibs-hero.cjs",[[257,"pennlibs-hero",{"heroPictureElement":[32],"heroHeadingElement":[32],"heroParagraphElement":[32],"heroSrc":[32]}]]],["pennlibs-fallback-img.cjs",[[257,"pennlibs-fallback-img"]]]], options);
22
22
  });
23
23
 
24
24
  exports.setNonce = index.setNonce;
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "entries": [
3
3
  "components/pennlibs-autocomplete/pennlibs-autocomplete.js",
4
+ "components/pennlibs-accordion/pennlibs-accordion.js",
4
5
  "components/pennlibs-banner/pennlibs-banner.js",
5
6
  "components/pennlibs-chat/pennlibs-chat.js",
6
7
  "components/pennlibs-expand-text/pennlibs-expand-text.js",
@@ -0,0 +1,9 @@
1
+ /*
2
+ The accordion is a behavior wrapper, not a layout box. `display: contents`
3
+ lets its details flow in the surrounding context as if the element were not
4
+ there. (`:host` is not used because this is a non-shadow component, so the
5
+ rule targets the tag, which Stencil injects globally when the component loads.)
6
+ */
7
+ pennlibs-accordion {
8
+ display: contents;
9
+ }
@@ -0,0 +1,228 @@
1
+ let accordionUid = 0;
2
+ /**
3
+ * Composes a group of native `<details class="pl-details">` disclosures into an
4
+ * accordion. The markup is authored as plain styled details elements (see the
5
+ * Details pattern) so it works and is accessible without JavaScript; once this
6
+ * component loads it layers on the WAI-ARIA Accordion behavior: keyboard
7
+ * navigation between headers, region wiring, and an optional single-open mode.
8
+ *
9
+ * To expand or collapse every item at once, add a control with
10
+ * `data-pl-accordion-toggle`. With no value it controls the closest enclosing
11
+ * accordion; set the value to an accordion's id to control one from elsewhere
12
+ * on the page. Add `data-pl-accordion-toggle-deep` to also reach nested
13
+ * accordions, or give it a number to reach that many levels down.
14
+ * `data-pl-expand-label` and `data-pl-collapse-label` swap the control's text
15
+ * to match the state.
16
+ *
17
+ * This is a light-DOM component. It does not render its own template; it only
18
+ * enhances the `<details>` children it is given.
19
+ *
20
+ * @slot - One or more details items, each styled with the pl-details classes.
21
+ */
22
+ export class Accordion {
23
+ constructor() {
24
+ /**
25
+ * When set, only one panel can be open at a time, so opening one closes the
26
+ * others, like a classic accordion.
27
+ * @prop single
28
+ */
29
+ this.single = false;
30
+ this.uid = accordionUid++;
31
+ this.items = [];
32
+ this.summaries = [];
33
+ // Roving focus between headers per the WAI-ARIA Accordion keyboard model.
34
+ // Enter/Space toggling is left to the browser's native <details> behavior.
35
+ this.onKeydown = (event) => {
36
+ var _a;
37
+ const current = event.currentTarget;
38
+ const index = this.summaries.indexOf(current);
39
+ if (index === -1)
40
+ return;
41
+ const count = this.summaries.length;
42
+ let next;
43
+ switch (event.key) {
44
+ case 'ArrowDown':
45
+ next = (index + 1) % count;
46
+ break;
47
+ case 'ArrowUp':
48
+ next = (index - 1 + count) % count;
49
+ break;
50
+ case 'Home':
51
+ next = 0;
52
+ break;
53
+ case 'End':
54
+ next = count - 1;
55
+ break;
56
+ default:
57
+ return;
58
+ }
59
+ event.preventDefault();
60
+ (_a = this.summaries[next]) === null || _a === void 0 ? void 0 : _a.focus();
61
+ };
62
+ this.onToggle = (event) => {
63
+ if (!this.single)
64
+ return;
65
+ const details = event.target;
66
+ if (!details.open)
67
+ return;
68
+ this.items.forEach((other) => {
69
+ if (other !== details && other.open)
70
+ other.open = false;
71
+ });
72
+ };
73
+ }
74
+ componentDidLoad() {
75
+ this.collectItems();
76
+ this.enhanceItems();
77
+ this.syncControls(this.el, this.items.some((details) => details.open));
78
+ }
79
+ disconnectedCallback() {
80
+ this.summaries.forEach((summary) => summary.removeEventListener('keydown', this.onKeydown));
81
+ this.items.forEach((details) => details.removeEventListener('toggle', this.onToggle));
82
+ }
83
+ collectItems() {
84
+ this.items = Array.from(this.el.querySelectorAll(':scope > details, :scope > .pl-details'));
85
+ this.summaries = this.items
86
+ .map((details) => details.querySelector(':scope > summary'))
87
+ .filter((summary) => summary instanceof HTMLElement);
88
+ }
89
+ enhanceItems() {
90
+ this.items.forEach((details, index) => {
91
+ const summary = details.querySelector(':scope > summary');
92
+ if (!summary)
93
+ return;
94
+ const panel = summary.nextElementSibling;
95
+ const baseId = `pl-accordion-${this.uid}-${index}`;
96
+ // Wire the summary (the disclosure button) to its panel as a labelled
97
+ // region. Native <summary> already exposes the button role and
98
+ // aria-expanded, so we only add what it lacks.
99
+ if (!summary.id)
100
+ summary.id = `${baseId}-summary`;
101
+ if (panel) {
102
+ if (!panel.id)
103
+ panel.id = `${baseId}-panel`;
104
+ if (!panel.hasAttribute('role'))
105
+ panel.setAttribute('role', 'region');
106
+ if (!panel.hasAttribute('aria-labelledby'))
107
+ panel.setAttribute('aria-labelledby', summary.id);
108
+ summary.setAttribute('aria-controls', panel.id);
109
+ }
110
+ summary.addEventListener('keydown', this.onKeydown);
111
+ details.addEventListener('toggle', this.onToggle);
112
+ });
113
+ }
114
+ // A declarative toggle-all control: any element marked with
115
+ // `data-pl-accordion-toggle`. With no value it controls the closest enclosing
116
+ // accordion; set the value to an accordion's id to control one from elsewhere
117
+ // on the page. Add `data-pl-accordion-toggle-deep` to also reach nested
118
+ // accordions, or a number for that many levels. `data-pl-expand-label` and
119
+ // `data-pl-collapse-label` swap its text to match the state.
120
+ onControlClick(event) {
121
+ var _a;
122
+ const control = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('[data-pl-accordion-toggle]');
123
+ if (!control || this.controlTarget(control) !== this.el)
124
+ return;
125
+ event.preventDefault();
126
+ const expanded = !this.items.some((details) => details.open);
127
+ this.setAll(this.el, expanded, this.controlDepth(control));
128
+ }
129
+ // The accordion a control points at: the element whose id is in the attribute
130
+ // value, or the closest enclosing accordion when the value is empty.
131
+ controlTarget(control) {
132
+ const id = control.getAttribute('data-pl-accordion-toggle');
133
+ return id ? document.getElementById(id) : control.closest('pennlibs-accordion');
134
+ }
135
+ // How many nested levels a control reaches: absent is none, a bare attribute
136
+ // is every level, and a non-negative number is that many levels down.
137
+ controlDepth(control) {
138
+ if (!control.hasAttribute('data-pl-accordion-toggle-deep'))
139
+ return 0;
140
+ const raw = control.getAttribute('data-pl-accordion-toggle-deep');
141
+ if (raw === null || raw === '')
142
+ return Infinity;
143
+ const levels = parseInt(raw, 10);
144
+ return Number.isFinite(levels) && levels >= 0 ? levels : Infinity;
145
+ }
146
+ // Expand or collapse every item in an accordion, pushing the same direction
147
+ // `levels` deep. Each level reads its own single-open rule, so nothing forces
148
+ // two panels open in a single-open group.
149
+ setAll(accordion, expanded, levels) {
150
+ const items = Array.from(accordion.querySelectorAll(':scope > details, :scope > .pl-details'));
151
+ if (expanded && accordion.single) {
152
+ items.forEach((details, index) => (details.open = index === 0));
153
+ }
154
+ else {
155
+ items.forEach((details) => (details.open = expanded));
156
+ }
157
+ this.syncControls(accordion, expanded);
158
+ if (levels > 0) {
159
+ this.nestedAccordions(accordion).forEach((nested) => this.setAll(nested, expanded, levels - 1));
160
+ }
161
+ }
162
+ // The accordions one level down from the given one (not their descendants).
163
+ nestedAccordions(accordion) {
164
+ return Array.from(accordion.querySelectorAll('pennlibs-accordion')).filter((node) => { var _a; return ((_a = node.parentElement) === null || _a === void 0 ? void 0 : _a.closest('pennlibs-accordion')) === accordion; });
165
+ }
166
+ // Keep every control pointing at an accordion in sync with its state.
167
+ syncControls(accordion, expanded) {
168
+ document.querySelectorAll('[data-pl-accordion-toggle]').forEach((control) => {
169
+ if (this.controlTarget(control) !== accordion)
170
+ return;
171
+ control.setAttribute('aria-expanded', String(expanded));
172
+ const label = expanded
173
+ ? control.getAttribute('data-pl-collapse-label')
174
+ : control.getAttribute('data-pl-expand-label');
175
+ if (label)
176
+ control.textContent = label;
177
+ });
178
+ }
179
+ static get is() { return "pennlibs-accordion"; }
180
+ static get originalStyleUrls() {
181
+ return {
182
+ "$": ["pennlibs-accordion.css"]
183
+ };
184
+ }
185
+ static get styleUrls() {
186
+ return {
187
+ "$": ["pennlibs-accordion.css"]
188
+ };
189
+ }
190
+ static get properties() {
191
+ return {
192
+ "single": {
193
+ "type": "boolean",
194
+ "mutable": false,
195
+ "complexType": {
196
+ "original": "boolean",
197
+ "resolved": "boolean",
198
+ "references": {}
199
+ },
200
+ "required": false,
201
+ "optional": false,
202
+ "docs": {
203
+ "tags": [{
204
+ "name": "prop",
205
+ "text": "single"
206
+ }],
207
+ "text": "When set, only one panel can be open at a time, so opening one closes the\nothers, like a classic accordion."
208
+ },
209
+ "getter": false,
210
+ "setter": false,
211
+ "reflect": false,
212
+ "attribute": "single",
213
+ "defaultValue": "false"
214
+ }
215
+ };
216
+ }
217
+ static get elementRef() { return "el"; }
218
+ static get listeners() {
219
+ return [{
220
+ "name": "click",
221
+ "method": "onControlClick",
222
+ "target": "document",
223
+ "capture": false,
224
+ "passive": false
225
+ }];
226
+ }
227
+ }
228
+ //# sourceMappingURL=pennlibs-accordion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pennlibs-accordion.js","sourceRoot":"","sources":["../../../src/components/pennlibs-accordion/pennlibs-accordion.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEjE,IAAI,YAAY,GAAG,CAAC,CAAC;AAErB;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,MAAM,OAAO,SAAS;IALtB;QAQE;;;;WAIG;QACK,WAAM,GAAY,KAAK,CAAC;QAExB,QAAG,GAAG,YAAY,EAAE,CAAC;QACrB,UAAK,GAAyB,EAAE,CAAC;QACjC,cAAS,GAAkB,EAAE,CAAC;QA6CtC,0EAA0E;QAC1E,2EAA2E;QACnE,cAAS,GAAG,CAAC,KAAoB,EAAE,EAAE;;YAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,aAA4B,CAAC;YACnD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,OAAO;YAEzB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;YACpC,IAAI,IAAY,CAAC;YACjB,QAAQ,KAAK,CAAC,GAAG,EAAE,CAAC;gBAClB,KAAK,WAAW;oBACd,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;oBAC3B,MAAM;gBACR,KAAK,SAAS;oBACZ,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;oBACnC,MAAM;gBACR,KAAK,MAAM;oBACT,IAAI,GAAG,CAAC,CAAC;oBACT,MAAM;gBACR,KAAK,KAAK;oBACR,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC;oBACjB,MAAM;gBACR;oBACE,OAAO;YACX,CAAC;YACD,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,MAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,0CAAE,KAAK,EAAE,CAAC;QAChC,CAAC,CAAC;QAEM,aAAQ,GAAG,CAAC,KAAY,EAAE,EAAE;YAClC,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,OAAO;YACzB,MAAM,OAAO,GAAG,KAAK,CAAC,MAA4B,CAAC;YACnD,IAAI,CAAC,OAAO,CAAC,IAAI;gBAAE,OAAO;YAC1B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBAC3B,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI;oBAAE,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;YAC1D,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;KAsEH;IArJC,gBAAgB;QACd,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,oBAAoB;QAClB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC5F,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxF,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CACrB,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,wCAAwC,CAAC,CAC3C,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK;aACxB,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC;aAC3D,MAAM,CAAC,CAAC,OAAO,EAA0B,EAAE,CAAC,OAAO,YAAY,WAAW,CAAC,CAAC;IACjF,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;YACpC,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,kBAAkB,CAAuB,CAAC;YAChF,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,MAAM,KAAK,GAAG,OAAO,CAAC,kBAAwC,CAAC;YAC/D,MAAM,MAAM,GAAG,gBAAgB,IAAI,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC;YAEnD,sEAAsE;YACtE,+DAA+D;YAC/D,+CAA+C;YAC/C,IAAI,CAAC,OAAO,CAAC,EAAE;gBAAE,OAAO,CAAC,EAAE,GAAG,GAAG,MAAM,UAAU,CAAC;YAClD,IAAI,KAAK,EAAE,CAAC;gBACV,IAAI,CAAC,KAAK,CAAC,EAAE;oBAAE,KAAK,CAAC,EAAE,GAAG,GAAG,MAAM,QAAQ,CAAC;gBAC5C,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC;oBAAE,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBACtE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,iBAAiB,CAAC;oBAAE,KAAK,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC9F,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YAClD,CAAC;YAED,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YACpD,OAAO,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC;IAwCD,4DAA4D;IAC5D,8EAA8E;IAC9E,8EAA8E;IAC9E,wEAAwE;IACxE,2EAA2E;IAC3E,6DAA6D;IAE7D,cAAc,CAAC,KAAiB;;QAC9B,MAAM,OAAO,GAAG,MAAC,KAAK,CAAC,MAAsB,0CAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;QACrF,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE;YAAE,OAAO;QAChE,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,8EAA8E;IAC9E,qEAAqE;IAC7D,aAAa,CAAC,OAAgB;QACpC,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,0BAA0B,CAAC,CAAC;QAC5D,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAClF,CAAC;IAED,6EAA6E;IAC7E,sEAAsE;IAC9D,YAAY,CAAC,OAAgB;QACnC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,+BAA+B,CAAC;YAAE,OAAO,CAAC,CAAC;QACrE,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,+BAA+B,CAAC,CAAC;QAClE,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE;YAAE,OAAO,QAAQ,CAAC;QAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;IACpE,CAAC;IAED,4EAA4E;IAC5E,8EAA8E;IAC9E,0CAA0C;IAClC,MAAM,CAAC,SAAkB,EAAE,QAAiB,EAAE,MAAc;QAClE,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CACtB,SAAS,CAAC,gBAAgB,CAAC,wCAAwC,CAAC,CAC7C,CAAC;QAC1B,IAAI,QAAQ,IAAK,SAA0C,CAAC,MAAM,EAAE,CAAC;YACnE,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACvC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACf,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAClG,CAAC;IACH,CAAC;IAED,4EAA4E;IACpE,gBAAgB,CAAC,SAAkB;QACzC,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,CAAC,MAAM,CACxE,CAAC,IAAI,EAAE,EAAE,WAAC,OAAA,CAAA,MAAA,IAAI,CAAC,aAAa,0CAAE,OAAO,CAAC,oBAAoB,CAAC,MAAK,SAAS,CAAA,EAAA,CAC1E,CAAC;IACJ,CAAC;IAED,sEAAsE;IAC9D,YAAY,CAAC,SAAkB,EAAE,QAAiB;QACxD,QAAQ,CAAC,gBAAgB,CAAC,4BAA4B,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC1E,IAAI,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,SAAS;gBAAE,OAAO;YACtD,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YACxD,MAAM,KAAK,GAAG,QAAQ;gBACpB,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC;gBAChD,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,sBAAsB,CAAC,CAAC;YACjD,IAAI,KAAK;gBAAE,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC;QACzC,CAAC,CAAC,CAAC;IACL,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CACF","sourcesContent":["import { Component, Element, Prop, Listen } from '@stencil/core';\n\nlet accordionUid = 0;\n\n/**\n * Composes a group of native `<details class=\"pl-details\">` disclosures into an\n * accordion. The markup is authored as plain styled details elements (see the\n * Details pattern) so it works and is accessible without JavaScript; once this\n * component loads it layers on the WAI-ARIA Accordion behavior: keyboard\n * navigation between headers, region wiring, and an optional single-open mode.\n *\n * To expand or collapse every item at once, add a control with\n * `data-pl-accordion-toggle`. With no value it controls the closest enclosing\n * accordion; set the value to an accordion's id to control one from elsewhere\n * on the page. Add `data-pl-accordion-toggle-deep` to also reach nested\n * accordions, or give it a number to reach that many levels down.\n * `data-pl-expand-label` and `data-pl-collapse-label` swap the control's text\n * to match the state.\n *\n * This is a light-DOM component. It does not render its own template; it only\n * enhances the `<details>` children it is given.\n *\n * @slot - One or more details items, each styled with the pl-details classes.\n */\n@Component({\n tag: 'pennlibs-accordion',\n styleUrl: 'pennlibs-accordion.css',\n shadow: false,\n})\nexport class Accordion {\n @Element() el!: HTMLElement;\n\n /**\n * When set, only one panel can be open at a time, so opening one closes the\n * others, like a classic accordion.\n * @prop single\n */\n @Prop() single: boolean = false;\n\n private uid = accordionUid++;\n private items: HTMLDetailsElement[] = [];\n private summaries: HTMLElement[] = [];\n\n componentDidLoad() {\n this.collectItems();\n this.enhanceItems();\n this.syncControls(this.el, this.items.some((details) => details.open));\n }\n\n disconnectedCallback() {\n this.summaries.forEach((summary) => summary.removeEventListener('keydown', this.onKeydown));\n this.items.forEach((details) => details.removeEventListener('toggle', this.onToggle));\n }\n\n private collectItems() {\n this.items = Array.from(\n this.el.querySelectorAll(':scope > details, :scope > .pl-details'),\n ) as HTMLDetailsElement[];\n this.summaries = this.items\n .map((details) => details.querySelector(':scope > summary'))\n .filter((summary): summary is HTMLElement => summary instanceof HTMLElement);\n }\n\n private enhanceItems() {\n this.items.forEach((details, index) => {\n const summary = details.querySelector(':scope > summary') as HTMLElement | null;\n if (!summary) return;\n const panel = summary.nextElementSibling as HTMLElement | null;\n const baseId = `pl-accordion-${this.uid}-${index}`;\n\n // Wire the summary (the disclosure button) to its panel as a labelled\n // region. Native <summary> already exposes the button role and\n // aria-expanded, so we only add what it lacks.\n if (!summary.id) summary.id = `${baseId}-summary`;\n if (panel) {\n if (!panel.id) panel.id = `${baseId}-panel`;\n if (!panel.hasAttribute('role')) panel.setAttribute('role', 'region');\n if (!panel.hasAttribute('aria-labelledby')) panel.setAttribute('aria-labelledby', summary.id);\n summary.setAttribute('aria-controls', panel.id);\n }\n\n summary.addEventListener('keydown', this.onKeydown);\n details.addEventListener('toggle', this.onToggle);\n });\n }\n\n // Roving focus between headers per the WAI-ARIA Accordion keyboard model.\n // Enter/Space toggling is left to the browser's native <details> behavior.\n private onKeydown = (event: KeyboardEvent) => {\n const current = event.currentTarget as HTMLElement;\n const index = this.summaries.indexOf(current);\n if (index === -1) return;\n\n const count = this.summaries.length;\n let next: number;\n switch (event.key) {\n case 'ArrowDown':\n next = (index + 1) % count;\n break;\n case 'ArrowUp':\n next = (index - 1 + count) % count;\n break;\n case 'Home':\n next = 0;\n break;\n case 'End':\n next = count - 1;\n break;\n default:\n return;\n }\n event.preventDefault();\n this.summaries[next]?.focus();\n };\n\n private onToggle = (event: Event) => {\n if (!this.single) return;\n const details = event.target as HTMLDetailsElement;\n if (!details.open) return;\n this.items.forEach((other) => {\n if (other !== details && other.open) other.open = false;\n });\n };\n\n // A declarative toggle-all control: any element marked with\n // `data-pl-accordion-toggle`. With no value it controls the closest enclosing\n // accordion; set the value to an accordion's id to control one from elsewhere\n // on the page. Add `data-pl-accordion-toggle-deep` to also reach nested\n // accordions, or a number for that many levels. `data-pl-expand-label` and\n // `data-pl-collapse-label` swap its text to match the state.\n @Listen('click', { target: 'document' })\n onControlClick(event: MouseEvent) {\n const control = (event.target as HTMLElement)?.closest('[data-pl-accordion-toggle]');\n if (!control || this.controlTarget(control) !== this.el) return;\n event.preventDefault();\n const expanded = !this.items.some((details) => details.open);\n this.setAll(this.el, expanded, this.controlDepth(control));\n }\n\n // The accordion a control points at: the element whose id is in the attribute\n // value, or the closest enclosing accordion when the value is empty.\n private controlTarget(control: Element): Element | null {\n const id = control.getAttribute('data-pl-accordion-toggle');\n return id ? document.getElementById(id) : control.closest('pennlibs-accordion');\n }\n\n // How many nested levels a control reaches: absent is none, a bare attribute\n // is every level, and a non-negative number is that many levels down.\n private controlDepth(control: Element): number {\n if (!control.hasAttribute('data-pl-accordion-toggle-deep')) return 0;\n const raw = control.getAttribute('data-pl-accordion-toggle-deep');\n if (raw === null || raw === '') return Infinity;\n const levels = parseInt(raw, 10);\n return Number.isFinite(levels) && levels >= 0 ? levels : Infinity;\n }\n\n // Expand or collapse every item in an accordion, pushing the same direction\n // `levels` deep. Each level reads its own single-open rule, so nothing forces\n // two panels open in a single-open group.\n private setAll(accordion: Element, expanded: boolean, levels: number) {\n const items = Array.from(\n accordion.querySelectorAll(':scope > details, :scope > .pl-details'),\n ) as HTMLDetailsElement[];\n if (expanded && (accordion as HTMLPennlibsAccordionElement).single) {\n items.forEach((details, index) => (details.open = index === 0));\n } else {\n items.forEach((details) => (details.open = expanded));\n }\n this.syncControls(accordion, expanded);\n if (levels > 0) {\n this.nestedAccordions(accordion).forEach((nested) => this.setAll(nested, expanded, levels - 1));\n }\n }\n\n // The accordions one level down from the given one (not their descendants).\n private nestedAccordions(accordion: Element): Element[] {\n return Array.from(accordion.querySelectorAll('pennlibs-accordion')).filter(\n (node) => node.parentElement?.closest('pennlibs-accordion') === accordion,\n );\n }\n\n // Keep every control pointing at an accordion in sync with its state.\n private syncControls(accordion: Element, expanded: boolean) {\n document.querySelectorAll('[data-pl-accordion-toggle]').forEach((control) => {\n if (this.controlTarget(control) !== accordion) return;\n control.setAttribute('aria-expanded', String(expanded));\n const label = expanded\n ? control.getAttribute('data-pl-collapse-label')\n : control.getAttribute('data-pl-expand-label');\n if (label) control.textContent = label;\n });\n }\n}\n"]}
@@ -1,4 +1,6 @@
1
1
  /* web custom elements */
2
+ export { Accordion as PennlibsAccordion } from '../types/components/pennlibs-accordion/pennlibs-accordion';
3
+ export { defineCustomElement as defineCustomElementPennlibsAccordion } from './pennlibs-accordion';
2
4
  export { Autocomplete as PennlibsAutocomplete } from '../types/components/pennlibs-autocomplete/pennlibs-autocomplete';
3
5
  export { defineCustomElement as defineCustomElementPennlibsAutocomplete } from './pennlibs-autocomplete';
4
6
  export { Banner as PennlibsBanner } from '../types/components/pennlibs-banner/pennlibs-banner';
@@ -1,4 +1,5 @@
1
1
  export { getAssetPath, render, setAssetPath, setNonce, setPlatformOptions } from '@stencil/core/internal/client';
2
+ export { PennlibsAccordion, defineCustomElement as defineCustomElementPennlibsAccordion } from './pennlibs-accordion.js';
2
3
  export { PennlibsAutocomplete, defineCustomElement as defineCustomElementPennlibsAutocomplete } from './pennlibs-autocomplete.js';
3
4
  export { PennlibsBanner, defineCustomElement as defineCustomElementPennlibsBanner } from './pennlibs-banner.js';
4
5
  export { PennlibsChat, defineCustomElement as defineCustomElementPennlibsChat } from './pennlibs-chat.js';
@@ -1 +1 @@
1
- {"file":"index.js","mappings":";;;;;;;;;;","names":[],"sources":[],"sourcesContent":[],"version":3}
1
+ {"file":"index.js","mappings":";;;;;;;;;;;","names":[],"sources":[],"sourcesContent":[],"version":3}
@@ -0,0 +1,11 @@
1
+ import type { Components, JSX } from "../types/components";
2
+
3
+ interface PennlibsAccordion extends Components.PennlibsAccordion, HTMLElement {}
4
+ export const PennlibsAccordion: {
5
+ prototype: PennlibsAccordion;
6
+ new (): PennlibsAccordion;
7
+ };
8
+ /**
9
+ * Used to define this component and all nested components recursively.
10
+ */
11
+ export const defineCustomElement: () => void;