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