@pinfold/react 0.0.0-stage → 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.
Files changed (41) hide show
  1. package/README.md +58 -2
  2. package/accordion/lazy/package.json +6 -0
  3. package/accordion/package.json +6 -0
  4. package/dist/accordion/client.cjs +474 -0
  5. package/dist/accordion/client.d.cts +31 -0
  6. package/dist/accordion/client.d.ts +31 -0
  7. package/dist/accordion/client.js +473 -0
  8. package/dist/accordion/index.cjs +48 -0
  9. package/dist/accordion/index.d.cts +52 -0
  10. package/dist/accordion/index.d.ts +52 -0
  11. package/dist/accordion/index.js +13 -0
  12. package/dist/accordion/lazy.cjs +17 -0
  13. package/dist/accordion/lazy.d.cts +15 -0
  14. package/dist/accordion/lazy.d.ts +15 -0
  15. package/dist/accordion/lazy.js +16 -0
  16. package/dist/chunk-5ZMK623A.js +61 -0
  17. package/dist/chunk-BPPCPHHU.js +70 -0
  18. package/dist/chunk-DQWSDHRY.cjs +71 -0
  19. package/dist/chunk-Q232PTPW.js +115 -0
  20. package/dist/chunk-RJPT3NTP.cjs +62 -0
  21. package/dist/chunk-VPKVOJHA.cjs +116 -0
  22. package/dist/client.cjs +30 -0
  23. package/dist/client.d.cts +121 -0
  24. package/dist/client.d.ts +121 -0
  25. package/dist/client.js +29 -0
  26. package/dist/index.cjs +52 -0
  27. package/dist/index.d.cts +123 -0
  28. package/dist/index.d.ts +123 -0
  29. package/dist/index.js +10 -0
  30. package/dist/types-BZqk8btE.d.cts +278 -0
  31. package/dist/types-BZqk8btE.d.ts +278 -0
  32. package/dist/types-x01Q5sqz.d.cts +278 -0
  33. package/dist/types-x01Q5sqz.d.ts +278 -0
  34. package/dist/validate-D3GJ2276.js +37 -0
  35. package/dist/validate-ELH6OKK3.cjs +38 -0
  36. package/llms-full.txt +133 -0
  37. package/llms.txt +26 -0
  38. package/package.json +92 -4
  39. package/registry/accordion.json +20 -0
  40. package/registry/registry.json +25 -0
  41. package/schema/accordion.json +800 -0
package/README.md CHANGED
@@ -1,3 +1,59 @@
1
- # Temporary Holding Version
1
+ # @pinfold/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Hooks-first, config-driven, accessible React components. One subpath per component, so
4
+ an unused component adds nothing to your bundle. React 18+, TypeScript or JavaScript.
5
+
6
+ ```bash
7
+ npm install @pinfold/react @pinfold/css
8
+ ```
9
+
10
+ ```js
11
+ // app entry, once
12
+ import '@pinfold/css/core.css';
13
+ import '@pinfold/css/accordion.css';
14
+ ```
15
+
16
+ ```jsx
17
+ import { useAccordion } from '@pinfold/react/accordion';
18
+
19
+ function Support() {
20
+ const Faq = useAccordion({
21
+ items: [
22
+ { title: 'Shipping', content: 'Ships in 2 days.' },
23
+ { title: 'Returns', content: '30 days, no questions.', badge: 'New' },
24
+ ],
25
+ });
26
+ return <Faq />;
27
+ }
28
+ ```
29
+
30
+ The value `useAccordion` returns is the component, its controller (`Faq.toggle(id)`,
31
+ `Faq.openAll()`) and its parts (`<Faq.Trigger id>`, `<Faq.Panel id>`).
32
+
33
+ In a Server Component, use the non-hook twin from the same import path:
34
+
35
+ ```jsx
36
+ import { defineAccordion } from '@pinfold/react/accordion';
37
+
38
+ export default async function Page() {
39
+ const Faq = defineAccordion({ items: await getFaq(), fields: { id: 'slug', title: 'question', content: 'answer' } });
40
+ return <Faq />;
41
+ }
42
+ ```
43
+
44
+ Global defaults: `defineConfig({ theme, radius, stylePack, defaults: { accordion: {...} } })`,
45
+ passed once to `<PinfoldProvider config={config}>`.
46
+
47
+ | Import | What |
48
+ | --- | --- |
49
+ | `@pinfold/react` | `PinfoldProvider`, `defineConfig`, `usePinfold`, and helpers for building your own components |
50
+ | `@pinfold/react/accordion` | `useAccordion`, `defineAccordion`, `Accordion` and the types |
51
+ | `@pinfold/react/accordion/lazy` | a code-split `Accordion` |
52
+
53
+ Also in the package, generated from the components' metadata: `llms.txt` and `llms-full.txt`
54
+ (model-readable docs), `schema/<name>.json` (JSON Schema of each config) and `registry/` (the
55
+ shadcn registry).
56
+
57
+ Full spec: [docs/components/accordion.md](../../docs/components/accordion.md).
58
+
59
+ MIT © Indranil Bhattacharya
@@ -0,0 +1,6 @@
1
+ {
2
+ "private": true,
3
+ "main": "../../dist/accordion/lazy.cjs",
4
+ "module": "../../dist/accordion/lazy.js",
5
+ "types": "../../dist/accordion/lazy.d.cts"
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "private": true,
3
+ "main": "../dist/accordion/index.cjs",
4
+ "module": "../dist/accordion/index.js",
5
+ "types": "../dist/accordion/index.d.cts"
6
+ }
@@ -0,0 +1,474 @@
1
+ "use client";
2
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }'use client';
3
+
4
+
5
+
6
+
7
+ var _chunkDQWSDHRYcjs = require('../chunk-DQWSDHRY.cjs');
8
+
9
+
10
+
11
+
12
+
13
+ var _chunkVPKVOJHAcjs = require('../chunk-VPKVOJHA.cjs');
14
+
15
+ // src/accordion/useAccordion.tsx
16
+ var _react = require('react'); var React2 = _interopRequireWildcard(_react); var React = _interopRequireWildcard(_react);
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+ var _core = require('@pinfold/core');
32
+
33
+ // src/accordion/AccordionView.tsx
34
+
35
+
36
+ var B = "pinfold-accordion";
37
+ var GLYPHS = {
38
+ chevron: /* @__PURE__ */ React.createElement("path", { d: "M4 6.5 8 10.5 12 6.5" }),
39
+ caret: /* @__PURE__ */ React.createElement("path", { d: "M6 4l5 4-5 4z", fill: "currentColor", stroke: "none" }),
40
+ arrow: /* @__PURE__ */ React.createElement("path", { d: "M8 3.5v9M4.5 9 8 12.5 11.5 9" }),
41
+ plus: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("path", { d: "M8 3.5v9", className: `${B}__indicator-bar` }), /* @__PURE__ */ React.createElement("path", { d: "M3.5 8h9" }))
42
+ };
43
+ function overflowProps(policy, fallback) {
44
+ const p = policy != null ? policy : fallback;
45
+ if (typeof p === "object") return { "data-overflow": "clamp", style: { "--pinfold-lines": String(p.lines) } };
46
+ return { "data-overflow": p };
47
+ }
48
+ function slot(ctx, name, base, item) {
49
+ var _a, _b, _c, _d;
50
+ const merged = _chunkDQWSDHRYcjs.slotProps.call(void 0, ctx.config, name, base);
51
+ const perItem = item == null ? void 0 : item.config;
52
+ if (!((_a = perItem == null ? void 0 : perItem.classNames) == null ? void 0 : _a[name]) && !((_b = perItem == null ? void 0 : perItem.styles) == null ? void 0 : _b[name])) return merged;
53
+ return _core.mergeProps.call(void 0, merged, {
54
+ className: (_c = perItem.classNames) == null ? void 0 : _c[name],
55
+ style: (_d = perItem.styles) == null ? void 0 : _d[name]
56
+ });
57
+ }
58
+ function renderRoot(ctx, rootProps) {
59
+ var _a, _b;
60
+ const { api, config, a } = ctx;
61
+ const props = api.getRootProps(
62
+ _core.mergeProps.call(void 0,
63
+ slot(ctx, "root", {
64
+ className: a.unstyled ? config.className : _core.cn.call(void 0, "pinfold", B, config.className),
65
+ style: { ..._core.dimensionVars.call(void 0, "accordion", config.dimensions), ...config.style },
66
+ "data-variant": a.variant,
67
+ "data-size": a.size,
68
+ "data-tone": a.tone,
69
+ // Only an explicit radius; otherwise the provider's preset or a style pack reaches the root by inheritance.
70
+ "data-pinfold-radius": config.radius,
71
+ "data-indicator-position": a.indicatorPosition,
72
+ "data-dividers": a.dividers ? "" : void 0,
73
+ "data-animate": a.animate ? "" : void 0,
74
+ "data-unstyled": a.unstyled ? "" : void 0,
75
+ "data-scroll": ((_a = config.dimensions) == null ? void 0 : _a.panelMaxHeight) != null ? "" : void 0,
76
+ "data-clamp": ((_b = config.dimensions) == null ? void 0 : _b.maxHeight) != null ? "" : void 0,
77
+ "data-until-found": a.hiddenUntilFound ? "" : void 0
78
+ }),
79
+ rootProps
80
+ )
81
+ );
82
+ return /* @__PURE__ */ React.createElement("div", { ...props }, api.items.map((item) => renderItem(ctx, item)));
83
+ }
84
+ function renderItem(ctx, item, extra) {
85
+ const { api, config } = ctx;
86
+ const props = api.getItemProps(
87
+ item,
88
+ _core.mergeProps.call(void 0, slot(ctx, "item", { className: cls(ctx, `${B}__item`) }, item), extra != null ? extra : {})
89
+ );
90
+ const header = renderHeader(ctx, item);
91
+ const panel = renderPanel(ctx, item);
92
+ if (config.renderItem) {
93
+ return /* @__PURE__ */ React.createElement(React.Fragment, { key: item.id }, config.renderItem({ state: item, props, header, panel }));
94
+ }
95
+ return /* @__PURE__ */ React.createElement("div", { key: item.id, ...props }, header, panel);
96
+ }
97
+ function renderHeader(ctx, item, extra, children) {
98
+ const { api, a } = ctx;
99
+ const headerProps = api.getHeaderProps(
100
+ item,
101
+ slot(ctx, "header", { className: cls(ctx, `${B}__header`) }, item)
102
+ );
103
+ const triggerProps = api.getTriggerProps(
104
+ item,
105
+ _core.mergeProps.call(void 0, slot(ctx, "trigger", { className: cls(ctx, `${B}__trigger`) }, item), extra != null ? extra : {})
106
+ );
107
+ delete headerProps.role;
108
+ delete headerProps["aria-level"];
109
+ const Heading = "h" + a.headingLevel;
110
+ return /* @__PURE__ */ React.createElement(Heading, { ...headerProps }, _chunkDQWSDHRYcjs.renderSlot.call(void 0,
111
+ ctx.config,
112
+ "trigger",
113
+ triggerProps,
114
+ item,
115
+ children != null ? children : /* @__PURE__ */ React.createElement(TriggerContent, { ctx, item }),
116
+ (props, kids) => /* @__PURE__ */ React.createElement("button", { ...props }, kids)
117
+ ));
118
+ }
119
+ function TriggerContent({ ctx, item }) {
120
+ var _a, _b;
121
+ const { icon, title, description, badge } = item.config;
122
+ const iconNode = _core.resolveNode.call(void 0, icon, item);
123
+ const descriptionNode = _core.resolveNode.call(void 0, description, item);
124
+ const badgeNode = _core.resolveNode.call(void 0, badge, item);
125
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, iconNode ? /* @__PURE__ */ React.createElement("span", { ...slot(ctx, "icon", { className: cls(ctx, `${B}__icon`), "aria-hidden": "true" }, item) }, iconNode) : null, /* @__PURE__ */ React.createElement("span", { ...slot(ctx, "label", { className: cls(ctx, `${B}__label`) }, item) }, /* @__PURE__ */ React.createElement("span", { ...slot(ctx, "title", { className: cls(ctx, `${B}__title`), ...overflowProps((_a = ctx.config.overflow) == null ? void 0 : _a.title, "wrap") }, item) }, _core.resolveNode.call(void 0, title, item)), descriptionNode ? /* @__PURE__ */ React.createElement(
126
+ "span",
127
+ {
128
+ ...slot(
129
+ ctx,
130
+ "description",
131
+ { className: cls(ctx, `${B}__description`), ...overflowProps((_b = ctx.config.overflow) == null ? void 0 : _b.description, { lines: 2 }) },
132
+ item
133
+ )
134
+ },
135
+ descriptionNode
136
+ ) : null), badgeNode ? /* @__PURE__ */ React.createElement("span", { ...slot(ctx, "badge", { className: cls(ctx, `${B}__badge`) }, item) }, badgeNode) : null, renderIndicator(ctx, item));
137
+ }
138
+ function renderIndicator(ctx, item, extra) {
139
+ var _a;
140
+ const chosen = (_a = item.config.indicator) != null ? _a : ctx.a.indicator;
141
+ return _chunkDQWSDHRYcjs.renderSlot.call(void 0,
142
+ ctx.config,
143
+ "indicator",
144
+ _core.mergeProps.call(void 0,
145
+ slot(ctx, "indicator", { className: cls(ctx, `${B}__indicator`), "aria-hidden": "true" }, item),
146
+ extra != null ? extra : {}
147
+ ),
148
+ item,
149
+ null,
150
+ (props) => /* @__PURE__ */ React.createElement(Indicator, { ...props, indicator: chosen, state: item })
151
+ );
152
+ }
153
+ function Indicator({
154
+ indicator,
155
+ state,
156
+ ...props
157
+ }) {
158
+ var _a;
159
+ if (indicator === "none") return null;
160
+ if (typeof indicator !== "string") {
161
+ const node = _core.resolveNode.call(void 0, indicator, state);
162
+ return node ? /* @__PURE__ */ React.createElement("span", { ...props }, node) : null;
163
+ }
164
+ const glyph = (_a = GLYPHS[indicator != null ? indicator : "chevron"]) != null ? _a : GLYPHS.chevron;
165
+ return /* @__PURE__ */ React.createElement("span", { ...props }, /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 16 16", width: "1em", height: "1em", fill: "none", focusable: "false" }, /* @__PURE__ */ React.createElement("g", { stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }, glyph)));
166
+ }
167
+ function renderPanel(ctx, item, extra, children) {
168
+ const { api, a, config } = ctx;
169
+ const panelProps = api.getPanelProps(
170
+ item,
171
+ _core.mergeProps.call(void 0, slot(ctx, "panel", { className: cls(ctx, `${B}__panel`) }, item), extra != null ? extra : {})
172
+ );
173
+ const body = children != null ? children : _core.shouldMountPanel.call(void 0, a.mountPanels, item.open, ctx.everOpened.has(item.id)) ? _core.resolveNode.call(void 0, item.config.content, item) : null;
174
+ return _chunkDQWSDHRYcjs.renderSlot.call(void 0,
175
+ config,
176
+ "panel",
177
+ panelProps,
178
+ item,
179
+ /* @__PURE__ */ React.createElement("div", { ...slot(ctx, "content", { className: cls(ctx, `${B}__content`) }, item) }, body),
180
+ (props, kids) => /* @__PURE__ */ React.createElement("div", { ...props }, /* @__PURE__ */ React.createElement("div", { className: cls(ctx, `${B}__panel-inner`), ref: ctx.findable(item) }, kids))
181
+ );
182
+ }
183
+ var cls = (ctx, name) => ctx.a.unstyled ? void 0 : name;
184
+
185
+ // src/accordion/useAccordion.tsx
186
+ function useAccordion(input) {
187
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
188
+ const ctx = _chunkVPKVOJHAcjs.usePinfold.call(void 0, );
189
+ const config = ctx.defaults.accordion ? { ...ctx.defaults.accordion, ...defined(input) } : input;
190
+ const {
191
+ mode = "single",
192
+ collapsible = true,
193
+ disabled: allDisabled = false,
194
+ value: controlled,
195
+ defaultValue,
196
+ onValueChange,
197
+ onBeforeChange,
198
+ onItemToggle,
199
+ orientation = "vertical",
200
+ dir = ctx.dir,
201
+ loop = true,
202
+ syncWithHash = false,
203
+ id: idProp
204
+ } = config;
205
+ if (process.env.NODE_ENV !== "production") {
206
+ void Promise.resolve().then(() => _interopRequireWildcard(require("../validate-ELH6OKK3.cjs"))).then((m) => m.validateAccordion(input, config));
207
+ }
208
+ const appearance = {
209
+ variant: (_a = config.variant) != null ? _a : "solid",
210
+ size: (_b = config.size) != null ? _b : ctx.size,
211
+ tone: (_c = config.tone) != null ? _c : ctx.tone,
212
+ indicator: (_d = config.indicator) != null ? _d : "chevron",
213
+ indicatorPosition: (_e = config.indicatorPosition) != null ? _e : "end",
214
+ headingLevel: (_f = config.headingLevel) != null ? _f : 3,
215
+ animate: (_g = config.animate) != null ? _g : !ctx.disableAnimation,
216
+ dividers: (_h = config.dividers) != null ? _h : true,
217
+ mountPanels: (_i = config.mountPanels) != null ? _i : config.unmountClosed ? "open" : "always",
218
+ hiddenUntilFound: (_j = config.hiddenUntilFound) != null ? _j : true,
219
+ unstyled: (_k = config.unstyled) != null ? _k : false
220
+ };
221
+ const headingLevel = appearance.headingLevel;
222
+ const baseId = _chunkVPKVOJHAcjs.useId.call(void 0, idProp);
223
+ const triggerRefs = React2.useRef([]);
224
+ const itemConfigs = React2.useMemo(
225
+ () => _core.mapAccordionFields.call(void 0, Array.isArray(config.items) ? config.items : [], config.fields),
226
+ // `fields` is plain data: key it by content so an inline object does not remap every render.
227
+ // eslint-disable-next-line react-hooks/exhaustive-deps
228
+ [config.items, JSON.stringify(config.fields)]
229
+ );
230
+ const items = React2.useMemo(
231
+ () => itemConfigs.map((item, index) => {
232
+ const id = _core.accordionRowId.call(void 0, item, index);
233
+ return { id, index, disabled: allDisabled || !!item.disabled, config: item, ..._core.accordionPartIds.call(void 0, baseId, id) };
234
+ }),
235
+ [itemConfigs, baseId, allDisabled]
236
+ );
237
+ const [value, setValue] = _chunkVPKVOJHAcjs.useControllableState.call(void 0, {
238
+ value: controlled,
239
+ defaultValue: () => _core.initialAccordionValue.call(void 0, itemConfigs, defaultValue, mode)
240
+ });
241
+ const everOpened = React2.useRef(/* @__PURE__ */ new Set());
242
+ value.forEach((id) => everOpened.current.add(id));
243
+ const isOpen = React2.useCallback((id) => value.indexOf(id) !== -1, [value]);
244
+ const rules = { mode, collapsible, disabled: allDisabled };
245
+ const commit = _chunkVPKVOJHAcjs.useEvent.call(void 0, (next, details) => {
246
+ if (!next) return false;
247
+ if ((onBeforeChange == null ? void 0 : onBeforeChange(details)) === false) return false;
248
+ setValue(next);
249
+ onValueChange == null ? void 0 : onValueChange(next, details);
250
+ if (details.id != null) onItemToggle == null ? void 0 : onItemToggle(details.id, details.open);
251
+ if (syncWithHash && details.id != null && (details.reason === "click" || details.reason === "keyboard")) {
252
+ writeHash(details.open ? details.id : null);
253
+ }
254
+ return true;
255
+ });
256
+ const change = _chunkVPKVOJHAcjs.useEvent.call(void 0,
257
+ (id, open, reason, event) => commit(_core.setAccordionItem.call(void 0, value, items, id, open, rules), { id, open, reason, event })
258
+ );
259
+ const focusItem = _chunkVPKVOJHAcjs.useEvent.call(void 0, (index) => {
260
+ var _a2;
261
+ (_a2 = triggerRefs.current[index]) == null ? void 0 : _a2.focus();
262
+ });
263
+ React2.useEffect(() => {
264
+ if (!syncWithHash) return;
265
+ const fromHash = () => {
266
+ var _a2, _b2;
267
+ const id = readHash();
268
+ const row = id != null ? items.find((i) => i.id === id) : void 0;
269
+ if (!row) return;
270
+ change(row.id, true, "hash");
271
+ (_b2 = (_a2 = triggerRefs.current[row.index]) == null ? void 0 : _a2.scrollIntoView) == null ? void 0 : _b2.call(_a2, { block: "start" });
272
+ };
273
+ fromHash();
274
+ window.addEventListener("hashchange", fromHash);
275
+ return () => window.removeEventListener("hashchange", fromHash);
276
+ }, [syncWithHash, items, change]);
277
+ const state = React2.useMemo(
278
+ () => items.map((item) => ({ ...item, open: isOpen(item.id) })),
279
+ [items, isOpen]
280
+ );
281
+ const onTriggerKeyDown = _chunkVPKVOJHAcjs.useEvent.call(void 0, (event, index) => {
282
+ const next = _core.accordionFocusTarget.call(void 0, event.key, index, items.length, { orientation, dir, loop });
283
+ if (next === null) return;
284
+ event.preventDefault();
285
+ focusItem(next);
286
+ });
287
+ const viewRef = React2.useRef(null);
288
+ const panels = React2.useRef(/* @__PURE__ */ new Map());
289
+ const accordionState = {
290
+ items: state,
291
+ value,
292
+ isOpen,
293
+ open: _chunkVPKVOJHAcjs.useEvent.call(void 0, (id) => change(id, true, "api")),
294
+ close: _chunkVPKVOJHAcjs.useEvent.call(void 0, (id) => change(id, false, "api")),
295
+ toggle: _chunkVPKVOJHAcjs.useEvent.call(void 0, (id) => change(id, !isOpen(id), "api")),
296
+ openAll: _chunkVPKVOJHAcjs.useEvent.call(void 0, () => commit(_core.openAllAccordion.call(void 0, value, items, rules), { open: true, reason: "api" })),
297
+ closeAll: _chunkVPKVOJHAcjs.useEvent.call(void 0, () => commit(_core.closeAllAccordion.call(void 0, value, items, rules), { open: false, reason: "api" })),
298
+ focusItem,
299
+ render: (props) => renderRoot(viewRef.current, props),
300
+ getRootProps: (props) => _core.mergeProps.call(void 0,
301
+ {
302
+ "data-pinfold": "accordion",
303
+ "data-orientation": orientation,
304
+ "data-mode": mode,
305
+ "data-disabled": _core.dataAttr.call(void 0, allDisabled),
306
+ dir
307
+ },
308
+ props
309
+ ),
310
+ getItemProps: (item, props) => _core.mergeProps.call(void 0,
311
+ {
312
+ "data-state": item.open ? "open" : "closed",
313
+ "data-disabled": _core.dataAttr.call(void 0, item.disabled),
314
+ "data-index": item.index
315
+ },
316
+ props
317
+ ),
318
+ getHeaderProps: (item, props) => _core.mergeProps.call(void 0,
319
+ {
320
+ role: "heading",
321
+ "aria-level": headingLevel,
322
+ "data-state": item.open ? "open" : "closed"
323
+ },
324
+ props
325
+ ),
326
+ getTriggerProps: (item, props) => _core.mergeProps.call(void 0,
327
+ {
328
+ type: "button",
329
+ id: item.triggerId,
330
+ "aria-expanded": item.open,
331
+ "aria-controls": item.panelId,
332
+ "aria-disabled": _core.ariaAttr.call(void 0, item.disabled),
333
+ "data-state": item.open ? "open" : "closed",
334
+ "data-disabled": _core.dataAttr.call(void 0, item.disabled),
335
+ ref: (node) => {
336
+ triggerRefs.current[item.index] = node;
337
+ },
338
+ onClick: (event) => {
339
+ if (item.disabled) {
340
+ event.preventDefault();
341
+ return;
342
+ }
343
+ change(item.id, !isOpen(item.id), event.detail === 0 ? "keyboard" : "click", event);
344
+ },
345
+ onKeyDown: (event) => onTriggerKeyDown(event, item.index)
346
+ },
347
+ props
348
+ ),
349
+ getPanelProps: (item, props) => _core.mergeProps.call(void 0,
350
+ {
351
+ id: item.panelId,
352
+ role: "region",
353
+ "aria-labelledby": item.triggerId,
354
+ "data-state": item.open ? "open" : "closed",
355
+ ref: (node) => trackPanel(panels.current, item, node, appearance.animate)
356
+ },
357
+ props
358
+ )
359
+ };
360
+ const buildParts = () => {
361
+ const pick = (props) => {
362
+ var _a2;
363
+ const rows = viewRef.current.api.items;
364
+ const found = props.index != null ? rows[props.index] : props.id != null ? rows.find((row) => row.id === props.id) : rows[0];
365
+ _core.warnOnce.call(void 0,
366
+ _core.__DEV__ && !found,
367
+ `Accordion: no row matches ${JSON.stringify((_a2 = props.id) != null ? _a2 : props.index)}. Sub-components address a row by \`id\` or \`index\`.`
368
+ );
369
+ return found;
370
+ };
371
+ const box = (props) => ({ className: props.className, style: props.style });
372
+ return {
373
+ Item: (props) => {
374
+ const item = pick(props);
375
+ return item ? renderItem(viewRef.current, item, box(props)) : null;
376
+ },
377
+ Trigger: (props) => {
378
+ const item = pick(props);
379
+ return item ? renderHeader(viewRef.current, item, box(props), props.children) : null;
380
+ },
381
+ Panel: (props) => {
382
+ const item = pick(props);
383
+ return item ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, renderPanel(viewRef.current, item, box(props), props.children)) : null;
384
+ },
385
+ Indicator: (props) => {
386
+ const item = pick(props);
387
+ return item ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, renderIndicator(viewRef.current, item, box(props))) : null;
388
+ }
389
+ };
390
+ };
391
+ const api = _chunkDQWSDHRYcjs.usePinfoldComponent.call(void 0,
392
+ "Accordion",
393
+ accordionState,
394
+ (props) => renderRoot(viewRef.current, props),
395
+ buildParts
396
+ );
397
+ const onFound = _chunkVPKVOJHAcjs.useEvent.call(void 0, (id) => change(id, true, "find-in-page"));
398
+ const findable2 = (item) => (node) => untilFound(node, appearance.hiddenUntilFound && !item.open, appearance.animate, () => onFound(item.id));
399
+ viewRef.current = { api, config, a: appearance, everOpened: everOpened.current, findable: findable2 };
400
+ return api;
401
+ }
402
+ function trackPanel(map, item, node, animate) {
403
+ if (!node) return;
404
+ const prev = map.get(item.id);
405
+ map.set(item.id, { node, open: item.open, timer: prev == null ? void 0 : prev.timer });
406
+ if (!prev || prev.node !== node || prev.open === item.open || !animate) return;
407
+ if (prev.timer) clearTimeout(prev.timer);
408
+ node.setAttribute("data-transition", item.open ? "opening" : "closing");
409
+ const done = () => node.removeAttribute("data-transition");
410
+ map.get(item.id).timer = setTimeout(done, transitionMs(node));
411
+ }
412
+ var findable = /* @__PURE__ */ new WeakMap();
413
+ function untilFound(node, hide, animate, onFound) {
414
+ var _a;
415
+ if (!node) return;
416
+ const prev = findable.get(node);
417
+ findable.set(node, { onFound, hide });
418
+ if (!prev) node.addEventListener("beforematch", () => {
419
+ var _a2;
420
+ return (_a2 = findable.get(node)) == null ? void 0 : _a2.onFound();
421
+ });
422
+ if (!hide) {
423
+ if (node.hasAttribute("hidden")) node.removeAttribute("hidden");
424
+ return;
425
+ }
426
+ if (node.getAttribute("hidden") === "until-found") return;
427
+ const apply = () => {
428
+ var _a2;
429
+ if ((_a2 = findable.get(node)) == null ? void 0 : _a2.hide) node.setAttribute("hidden", "until-found");
430
+ };
431
+ const wait = animate && prev && !prev.hide ? transitionMs((_a = node.parentElement) != null ? _a : node) : 0;
432
+ if (wait) setTimeout(apply, wait);
433
+ else apply();
434
+ }
435
+ function transitionMs(node) {
436
+ if (typeof getComputedStyle === "undefined") return 0;
437
+ const style = getComputedStyle(node);
438
+ const parse = (list) => list.split(",").map((s) => (s.trim().endsWith("ms") ? parseFloat(s) : parseFloat(s) * 1e3) || 0);
439
+ const durations = parse(style.transitionDuration || "0s");
440
+ const delays = parse(style.transitionDelay || "0s");
441
+ return Math.max(0, ...durations.map((d, i) => {
442
+ var _a;
443
+ return d + ((_a = delays[i]) != null ? _a : 0);
444
+ }));
445
+ }
446
+ function readHash() {
447
+ const raw = typeof location !== "undefined" ? location.hash.slice(1) : "";
448
+ if (!raw) return null;
449
+ try {
450
+ return decodeURIComponent(raw);
451
+ } catch (e) {
452
+ return raw;
453
+ }
454
+ }
455
+ function writeHash(id) {
456
+ if (typeof history === "undefined") return;
457
+ const url = location.pathname + location.search + (id != null ? "#" + encodeURIComponent(id) : "");
458
+ history.replaceState(history.state, "", url);
459
+ }
460
+ function defined(obj) {
461
+ const out = {};
462
+ for (const k in obj) if (obj[k] !== void 0) out[k] = obj[k];
463
+ return out;
464
+ }
465
+
466
+ // src/accordion/Accordion.tsx
467
+ function Accordion(config) {
468
+ return useAccordion(config).render();
469
+ }
470
+ Accordion.displayName = "Accordion";
471
+
472
+
473
+
474
+ exports.Accordion = Accordion; exports.useAccordion = useAccordion;
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { A as AccordionConfig, a as AccordionApi } from '../types-BZqk8btE.cjs';
3
+ import '@pinfold/core';
4
+
5
+ /**
6
+ * Thin JSX wrapper over `useAccordion()`, for one-off accordions that never
7
+ * need the controller. It adds nothing the hook does not already do.
8
+ */
9
+ declare function Accordion(config: AccordionConfig): React.ReactElement;
10
+ declare namespace Accordion {
11
+ var displayName: string;
12
+ }
13
+
14
+ /**
15
+ * The accordion, as a hook that hands back a component.
16
+ *
17
+ * const Faq = useAccordion({ items });
18
+ * return <Faq className="mt-8" />;
19
+ *
20
+ * The same value is the controller (`Faq.toggle(id)`, `Faq.value`) and carries
21
+ * its own parts (`<Faq.Trigger id="a" />`) for layouts the config cannot
22
+ * express. One object, three levels of commitment.
23
+ *
24
+ * Implements the WAI-ARIA Accordion pattern: button triggers inside headings,
25
+ * `aria-expanded` / `aria-controls`, panels as labelled regions, and
26
+ * Arrow/Home/End navigation between headers. Behaviour rules live in
27
+ * `@pinfold/core` (accordion.logic.ts); this hook wires them to React.
28
+ */
29
+ declare function useAccordion(input: AccordionConfig): AccordionApi;
30
+
31
+ export { Accordion, AccordionApi, AccordionConfig, useAccordion };
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { A as AccordionConfig, a as AccordionApi } from '../types-BZqk8btE.js';
3
+ import '@pinfold/core';
4
+
5
+ /**
6
+ * Thin JSX wrapper over `useAccordion()`, for one-off accordions that never
7
+ * need the controller. It adds nothing the hook does not already do.
8
+ */
9
+ declare function Accordion(config: AccordionConfig): React.ReactElement;
10
+ declare namespace Accordion {
11
+ var displayName: string;
12
+ }
13
+
14
+ /**
15
+ * The accordion, as a hook that hands back a component.
16
+ *
17
+ * const Faq = useAccordion({ items });
18
+ * return <Faq className="mt-8" />;
19
+ *
20
+ * The same value is the controller (`Faq.toggle(id)`, `Faq.value`) and carries
21
+ * its own parts (`<Faq.Trigger id="a" />`) for layouts the config cannot
22
+ * express. One object, three levels of commitment.
23
+ *
24
+ * Implements the WAI-ARIA Accordion pattern: button triggers inside headings,
25
+ * `aria-expanded` / `aria-controls`, panels as labelled regions, and
26
+ * Arrow/Home/End navigation between headers. Behaviour rules live in
27
+ * `@pinfold/core` (accordion.logic.ts); this hook wires them to React.
28
+ */
29
+ declare function useAccordion(input: AccordionConfig): AccordionApi;
30
+
31
+ export { Accordion, AccordionApi, AccordionConfig, useAccordion };