@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.
- package/README.md +58 -2
- package/accordion/lazy/package.json +6 -0
- package/accordion/package.json +6 -0
- package/dist/accordion/client.cjs +491 -0
- package/dist/accordion/client.d.cts +34 -0
- package/dist/accordion/client.d.ts +34 -0
- package/dist/accordion/client.js +490 -0
- package/dist/accordion/index.cjs +49 -0
- package/dist/accordion/index.d.cts +56 -0
- package/dist/accordion/index.d.ts +56 -0
- package/dist/accordion/index.js +14 -0
- package/dist/accordion/lazy.cjs +17 -0
- package/dist/accordion/lazy.d.cts +16 -0
- package/dist/accordion/lazy.d.ts +16 -0
- package/dist/accordion/lazy.js +16 -0
- package/dist/chunk-5ZMK623A.js +61 -0
- package/dist/chunk-BPPCPHHU.js +70 -0
- package/dist/chunk-DQWSDHRY.cjs +71 -0
- package/dist/chunk-Q232PTPW.js +115 -0
- package/dist/chunk-RJPT3NTP.cjs +62 -0
- package/dist/chunk-VPKVOJHA.cjs +116 -0
- package/dist/client.cjs +30 -0
- package/dist/client.d.cts +120 -0
- package/dist/client.d.ts +120 -0
- package/dist/client.js +29 -0
- package/dist/index.cjs +52 -0
- package/dist/index.d.cts +123 -0
- package/dist/index.d.ts +123 -0
- package/dist/index.js +10 -0
- package/dist/types-CkcgnWwL.d.cts +367 -0
- package/dist/types-CkcgnWwL.d.ts +367 -0
- package/dist/types-N0Yo4zGa.d.cts +374 -0
- package/dist/types-N0Yo4zGa.d.ts +374 -0
- package/dist/validate-EQHBH4NH.cjs +42 -0
- package/dist/validate-WKHQNNGD.js +41 -0
- package/llms-full.txt +140 -0
- package/llms.txt +26 -0
- package/package.json +92 -4
- package/registry/accordion.json +20 -0
- package/registry/registry.json +25 -0
- package/schema/accordion.json +903 -0
package/README.md
CHANGED
|
@@ -1,3 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @pinfold/react
|
|
2
2
|
|
|
3
|
-
|
|
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,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 };
|