@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
@@ -0,0 +1,278 @@
1
+ import * as React from 'react';
2
+ import { Slottable as Slottable$1, PropsBag, AccordionFieldMap, AccordionChangeDetails, PinfoldOrientation, PinfoldDir, AccordionMountPanels, PinfoldLazy, PinfoldSize, PinfoldTone, PinfoldRadius, Length } from '@pinfold/core';
3
+
4
+ /**
5
+ * A component that is also its own controller.
6
+ *
7
+ * const Accordion = useAccordion(config);
8
+ * return <Accordion className="mt-6" />; // renders
9
+ * Accordion.toggle('faq-1'); // controls
10
+ * <Accordion.Panel id="faq-1" /> // composes
11
+ */
12
+ type PinfoldComponent<Api, Props> = ((props: Props) => React.ReactElement | null) & Api & {
13
+ displayName?: string;
14
+ };
15
+ type Part = (props: any) => React.ReactElement | null;
16
+ /**
17
+ * Turn a render function plus an API object (and optional parts) into a
18
+ * stable component.
19
+ *
20
+ * Identity stability: a component created fresh on every render is a *new
21
+ * element type* to React, which remounts the whole subtree on each parent
22
+ * update, losing focus, selection, scroll position and running transitions.
23
+ * So the component and its parts are created once and read the current
24
+ * render through a ref.
25
+ *
26
+ * Freshness: because the identity never changes, a memoising parent (the
27
+ * React Compiler, `React.memo`, a cached element) may skip re-rendering it.
28
+ * So every hook render bumps a version, and after commit the component and
29
+ * each part re-render through `useSyncExternalStore` if they have not yet
30
+ * seen it. In the ordinary case they already rendered with the parent, the
31
+ * snapshot matches, and React does no extra work.
32
+ */
33
+ declare function usePinfoldComponent<Api extends object, Props extends object, Parts extends {
34
+ [K in keyof Parts]: Part;
35
+ } = {}>(displayName: string, api: Api, render: (props: Props) => React.ReactElement | null, buildParts?: () => Parts): PinfoldComponent<Api & Parts, Props>;
36
+
37
+ /** A config field that is a node, or a function of live state returning one. */
38
+ type Slottable<State, T = React.ReactNode> = Slottable$1<State, T>;
39
+ /**
40
+ * Per-slot escape hatches. Every Pinfold component exposes the same four, so
41
+ * learning one component teaches all of them.
42
+ *
43
+ * - `classNames` — append a class to one slot.
44
+ * - `slotProps` — merge arbitrary DOM props into one slot (event handlers chain).
45
+ * - `styles` — inline style for one slot.
46
+ * - `render` — replace one slot's element entirely, keeping its behaviour props.
47
+ */
48
+ interface SlotOverrides<Slot extends string, State = unknown> {
49
+ classNames?: Partial<Record<Slot, string>>;
50
+ styles?: Partial<Record<Slot, React.CSSProperties>>;
51
+ slotProps?: Partial<Record<Slot, PropsBag>>;
52
+ render?: Partial<Record<Slot, SlotRenderer<State>>>;
53
+ }
54
+ type SlotRenderer<State = unknown> = (args: {
55
+ /** Behaviour props (ARIA, handlers, refs). Spread these or accessibility breaks. */
56
+ props: PropsBag;
57
+ /** Live component state for this slot. */
58
+ state: State;
59
+ children: React.ReactNode;
60
+ }) => React.ReactNode;
61
+
62
+ type AccordionSlot = 'root' | 'item' | 'header' | 'trigger' | 'icon' | 'label' | 'title' | 'description' | 'badge' | 'indicator' | 'panel' | 'content';
63
+ type AccordionVariant = 'solid' | 'outline' | 'separated' | 'filled' | 'ghost';
64
+ type AccordionIndicator = 'chevron' | 'plus' | 'caret' | 'arrow' | 'none';
65
+ /** Runtime state of a single row. Passed to every function-valued config field. */
66
+ interface AccordionItemState {
67
+ id: string;
68
+ index: number;
69
+ open: boolean;
70
+ disabled: boolean;
71
+ config: AccordionItemConfig;
72
+ triggerId: string;
73
+ panelId: string;
74
+ }
75
+ /** Anything renderable in a row: a node, or a function of the row's live state. */
76
+ type AccordionNode = Slottable<AccordionItemState>;
77
+ /** One row of the accordion. This is the JSON a consumer — or a model — writes. */
78
+ interface AccordionItemConfig {
79
+ /** Stable key. Falls back to the array index when omitted. */
80
+ id?: string;
81
+ title: AccordionNode;
82
+ content?: AccordionNode;
83
+ /** Secondary line under the title. */
84
+ description?: AccordionNode;
85
+ /** Leading visual — icon, avatar, anything. */
86
+ icon?: AccordionNode;
87
+ /** Trailing chip rendered before the indicator. */
88
+ badge?: AccordionNode;
89
+ /** Overrides the accordion-wide indicator for this row only. */
90
+ indicator?: AccordionIndicator | AccordionNode;
91
+ disabled?: boolean;
92
+ /** Open on first render (uncontrolled only). */
93
+ defaultOpen?: boolean;
94
+ /** Per-row class and style overrides, same slot names as the root config. */
95
+ classNames?: Partial<Record<AccordionSlot, string>>;
96
+ styles?: Partial<Record<AccordionSlot, React.CSSProperties>>;
97
+ }
98
+ /** Text overflow for one slot: wrap, one line with an ellipsis, or clamp to n lines. */
99
+ type AccordionOverflow = 'wrap' | 'truncate' | {
100
+ lines: number;
101
+ };
102
+ type AccordionValueDetails = AccordionChangeDetails<Event | React.SyntheticEvent>;
103
+ interface AccordionBehaviourConfig {
104
+ /**
105
+ * The rows. The only required option. With `fields`, any record shape works:
106
+ * `items: faqRows, fields: { id: 'slug', title: 'question', content: 'answer' }`.
107
+ */
108
+ items: AccordionItemConfig[] | ReadonlyArray<Record<string, any>>;
109
+ /** Maps raw records onto rows, so CMS or database results need no `.map()`. */
110
+ fields?: AccordionFieldMap;
111
+ /** `single` closes siblings on open. Default `single`. */
112
+ mode?: 'single' | 'multiple';
113
+ /** In `single` mode, allow closing the open item. Default true. */
114
+ collapsible?: boolean;
115
+ /** Controlled open ids. */
116
+ value?: string[];
117
+ defaultValue?: string[];
118
+ /** `details.reason`: 'click' | 'keyboard' | 'api' | 'find-in-page' | 'hash'. */
119
+ onValueChange?: (value: string[], details: AccordionValueDetails) => void;
120
+ /** Return `false` to block a change (e.g. unsaved edits in the open panel). */
121
+ onBeforeChange?: (details: AccordionValueDetails) => boolean | void;
122
+ /** Fires for one item, after `onValueChange`. */
123
+ onItemToggle?: (id: string, open: boolean) => void;
124
+ /** Arrow-key axis for moving between headers. Default `vertical`. */
125
+ orientation?: PinfoldOrientation;
126
+ dir?: PinfoldDir;
127
+ /** Wrap focus at the ends. Default true. */
128
+ loop?: boolean;
129
+ /** Prefix for generated ids — set it for deterministic SSR markup. */
130
+ id?: string;
131
+ /** Disables every row. Rows stay focusable. Default false. */
132
+ disabled?: boolean;
133
+ /**
134
+ * Closed panels use `hidden="until-found"`, so the browser's find-in-page
135
+ * searches them and opens the match. Default true.
136
+ */
137
+ hiddenUntilFound?: boolean;
138
+ /**
139
+ * When panel content is in the DOM. `always` (default) keeps it searchable
140
+ * and indexable; `open` mounts only open panels; `first-open` mounts on
141
+ * first open and keeps it.
142
+ */
143
+ mountPanels?: AccordionMountPanels;
144
+ /** @deprecated Use `mountPanels: 'open'`. */
145
+ unmountClosed?: boolean;
146
+ /** `#id` in the URL opens that row and scrolls to it; opening a row updates the hash. Default false. */
147
+ syncWithHash?: boolean;
148
+ /** Code loading for the `/lazy` entry. The accordion is light, so `auto` loads eagerly. */
149
+ lazy?: PinfoldLazy;
150
+ }
151
+ /**
152
+ * Explicit sizing. Numbers are px, strings pass through (`'50%'`, `'18rem'`,
153
+ * `'clamp(20rem, 40vw, 32rem)'`). Each key overrides the value the `size`
154
+ * preset would have chosen, so you can take one dimension and leave the rest.
155
+ */
156
+ interface AccordionDimensions {
157
+ width?: Length;
158
+ minWidth?: Length;
159
+ maxWidth?: Length;
160
+ height?: Length;
161
+ maxHeight?: Length;
162
+ /** Space between rows (outline / separated / filled variants). */
163
+ gap?: Length;
164
+ /** Trigger padding. */
165
+ paddingX?: Length;
166
+ paddingY?: Length;
167
+ /** Panel padding. Defaults to the trigger padding. */
168
+ panelPaddingX?: Length;
169
+ panelPaddingY?: Length;
170
+ /** Cap the open panel and let it scroll past that. */
171
+ panelMaxHeight?: Length;
172
+ fontSize?: Length;
173
+ titleSize?: Length;
174
+ descriptionSize?: Length;
175
+ iconSize?: Length;
176
+ indicatorSize?: Length;
177
+ borderWidth?: Length;
178
+ /** Corner radius as a length — overrides the `radius` token preset. */
179
+ radius?: Length;
180
+ /** Open/close transition duration, e.g. `'320ms'`. */
181
+ duration?: Length;
182
+ }
183
+ interface AccordionAppearanceConfig {
184
+ variant?: AccordionVariant;
185
+ size?: PinfoldSize;
186
+ tone?: PinfoldTone;
187
+ radius?: PinfoldRadius;
188
+ /** Built-in glyph, your own node, or a function of the row's state. */
189
+ indicator?: AccordionIndicator | AccordionNode;
190
+ /** Which edge the indicator sits on. Default `end`. */
191
+ indicatorPosition?: 'start' | 'end';
192
+ /** Heading level wrapping each trigger. Default 3. */
193
+ headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
194
+ /** Animate open/close. Default true; honours prefers-reduced-motion regardless. */
195
+ animate?: boolean;
196
+ /** Full-width dividers between rows (solid/ghost variants). Default true. */
197
+ dividers?: boolean;
198
+ /** Text overflow per slot. Default: title `wrap`, description `{ lines: 2 }`. */
199
+ overflow?: {
200
+ title?: AccordionOverflow;
201
+ description?: AccordionOverflow;
202
+ };
203
+ /** Explicit sizing, per dimension. Overrides the `size` preset. */
204
+ dimensions?: AccordionDimensions;
205
+ /**
206
+ * Drop every Pinfold class so no bundled CSS applies, keeping the behaviour,
207
+ * ARIA and `data-*` state hooks. For Tailwind or your own stylesheet:
208
+ * style entirely through `classNames`.
209
+ */
210
+ unstyled?: boolean;
211
+ className?: string;
212
+ style?: React.CSSProperties;
213
+ }
214
+ interface AccordionConfig extends AccordionBehaviourConfig, AccordionAppearanceConfig, SlotOverrides<AccordionSlot, AccordionItemState> {
215
+ /** Replace an entire row. The last resort before dropping to prop getters. */
216
+ renderItem?: (args: {
217
+ state: AccordionItemState;
218
+ /** Root props for the row. Spread them or state styling breaks. */
219
+ props: PropsBag;
220
+ /** The default header, if you only want to swap the body around it. */
221
+ header: React.ReactNode;
222
+ /** The default panel. */
223
+ panel: React.ReactNode;
224
+ }) => React.ReactNode;
225
+ }
226
+ /** Props accepted by `<Accordion />` itself, merged onto the root element. */
227
+ interface AccordionRootProps {
228
+ className?: string;
229
+ style?: React.CSSProperties;
230
+ children?: never;
231
+ [key: `data-${string}`]: unknown;
232
+ }
233
+ /** Addressing a single row from a sub-component. */
234
+ interface AccordionPartProps {
235
+ id?: string;
236
+ index?: number;
237
+ className?: string;
238
+ style?: React.CSSProperties;
239
+ children?: React.ReactNode;
240
+ }
241
+ interface AccordionState {
242
+ items: AccordionItemState[];
243
+ value: string[];
244
+ isOpen: (id: string) => boolean;
245
+ open: (id: string) => void;
246
+ close: (id: string) => void;
247
+ toggle: (id: string) => void;
248
+ openAll: () => void;
249
+ closeAll: () => void;
250
+ /** Move DOM focus to a header by index. */
251
+ focusItem: (index: number) => void;
252
+ /** Equivalent to `<Accordion />`, for callers who prefer a call. */
253
+ render: (props?: AccordionRootProps) => React.ReactElement;
254
+ getRootProps: (props?: PropsBag) => PropsBag;
255
+ getItemProps: (item: AccordionItemState, props?: PropsBag) => PropsBag;
256
+ getHeaderProps: (item: AccordionItemState, props?: PropsBag) => PropsBag;
257
+ getTriggerProps: (item: AccordionItemState, props?: PropsBag) => PropsBag;
258
+ getPanelProps: (item: AccordionItemState, props?: PropsBag) => PropsBag;
259
+ }
260
+ /** The sub-components for hand-laid-out accordions. */
261
+ interface AccordionParts {
262
+ /** One complete row: header plus panel. */
263
+ Item: (props: AccordionPartProps) => React.ReactElement | null;
264
+ /** Just the heading and its button. `children` replaces the default label. */
265
+ Trigger: (props: AccordionPartProps) => React.ReactElement | null;
266
+ /** Just the collapsible region. `children` replaces the configured content. */
267
+ Panel: (props: AccordionPartProps) => React.ReactElement | null;
268
+ /** Just the open/closed glyph. */
269
+ Indicator: (props: AccordionPartProps) => React.ReactElement | null;
270
+ }
271
+ /**
272
+ * What `useAccordion()` returns: a component you render, that is also the
273
+ * controller, that also carries its parts.
274
+ */
275
+ type AccordionApi = PinfoldComponent<AccordionState & AccordionParts, AccordionRootProps>;
276
+
277
+ export { usePinfoldComponent as u };
278
+ export type { AccordionConfig as A, PinfoldComponent as P, SlotOverrides as S, AccordionApi as a, SlotRenderer as b, Slottable as c };
@@ -0,0 +1,278 @@
1
+ import * as React from 'react';
2
+ import { Slottable as Slottable$1, PropsBag, AccordionFieldMap, AccordionChangeDetails, PinfoldOrientation, PinfoldDir, AccordionMountPanels, PinfoldLazy, PinfoldSize, PinfoldTone, PinfoldRadius, Length } from '@pinfold/core';
3
+
4
+ /**
5
+ * A component that is also its own controller.
6
+ *
7
+ * const Accordion = useAccordion(config);
8
+ * return <Accordion className="mt-6" />; // renders
9
+ * Accordion.toggle('faq-1'); // controls
10
+ * <Accordion.Panel id="faq-1" /> // composes
11
+ */
12
+ type PinfoldComponent<Api, Props> = ((props: Props) => React.ReactElement | null) & Api & {
13
+ displayName?: string;
14
+ };
15
+ type Part = (props: any) => React.ReactElement | null;
16
+ /**
17
+ * Turn a render function plus an API object (and optional parts) into a
18
+ * stable component.
19
+ *
20
+ * Identity stability: a component created fresh on every render is a *new
21
+ * element type* to React, which remounts the whole subtree on each parent
22
+ * update, losing focus, selection, scroll position and running transitions.
23
+ * So the component and its parts are created once and read the current
24
+ * render through a ref.
25
+ *
26
+ * Freshness: because the identity never changes, a memoising parent (the
27
+ * React Compiler, `React.memo`, a cached element) may skip re-rendering it.
28
+ * So every hook render bumps a version, and after commit the component and
29
+ * each part re-render through `useSyncExternalStore` if they have not yet
30
+ * seen it. In the ordinary case they already rendered with the parent, the
31
+ * snapshot matches, and React does no extra work.
32
+ */
33
+ declare function usePinfoldComponent<Api extends object, Props extends object, Parts extends {
34
+ [K in keyof Parts]: Part;
35
+ } = {}>(displayName: string, api: Api, render: (props: Props) => React.ReactElement | null, buildParts?: () => Parts): PinfoldComponent<Api & Parts, Props>;
36
+
37
+ /** A config field that is a node, or a function of live state returning one. */
38
+ type Slottable<State, T = React.ReactNode> = Slottable$1<State, T>;
39
+ /**
40
+ * Per-slot escape hatches. Every Pinfold component exposes the same four, so
41
+ * learning one component teaches all of them.
42
+ *
43
+ * - `classNames` — append a class to one slot.
44
+ * - `slotProps` — merge arbitrary DOM props into one slot (event handlers chain).
45
+ * - `styles` — inline style for one slot.
46
+ * - `render` — replace one slot's element entirely, keeping its behaviour props.
47
+ */
48
+ interface SlotOverrides<Slot extends string, State = unknown> {
49
+ classNames?: Partial<Record<Slot, string>>;
50
+ styles?: Partial<Record<Slot, React.CSSProperties>>;
51
+ slotProps?: Partial<Record<Slot, PropsBag>>;
52
+ render?: Partial<Record<Slot, SlotRenderer<State>>>;
53
+ }
54
+ type SlotRenderer<State = unknown> = (args: {
55
+ /** Behaviour props (ARIA, handlers, refs). Spread these or accessibility breaks. */
56
+ props: PropsBag;
57
+ /** Live component state for this slot. */
58
+ state: State;
59
+ children: React.ReactNode;
60
+ }) => React.ReactNode;
61
+
62
+ type AccordionSlot = 'root' | 'item' | 'header' | 'trigger' | 'icon' | 'label' | 'title' | 'description' | 'badge' | 'indicator' | 'panel' | 'content';
63
+ type AccordionVariant = 'solid' | 'outline' | 'separated' | 'filled' | 'ghost';
64
+ type AccordionIndicator = 'chevron' | 'plus' | 'caret' | 'arrow' | 'none';
65
+ /** Runtime state of a single row. Passed to every function-valued config field. */
66
+ interface AccordionItemState {
67
+ id: string;
68
+ index: number;
69
+ open: boolean;
70
+ disabled: boolean;
71
+ config: AccordionItemConfig;
72
+ triggerId: string;
73
+ panelId: string;
74
+ }
75
+ /** Anything renderable in a row: a node, or a function of the row's live state. */
76
+ type AccordionNode = Slottable<AccordionItemState>;
77
+ /** One row of the accordion. This is the JSON a consumer — or a model — writes. */
78
+ interface AccordionItemConfig {
79
+ /** Stable key. Falls back to the array index when omitted. */
80
+ id?: string;
81
+ title: AccordionNode;
82
+ content?: AccordionNode;
83
+ /** Secondary line under the title. */
84
+ description?: AccordionNode;
85
+ /** Leading visual — icon, avatar, anything. */
86
+ icon?: AccordionNode;
87
+ /** Trailing chip rendered before the indicator. */
88
+ badge?: AccordionNode;
89
+ /** Overrides the accordion-wide indicator for this row only. */
90
+ indicator?: AccordionIndicator | AccordionNode;
91
+ disabled?: boolean;
92
+ /** Open on first render (uncontrolled only). */
93
+ defaultOpen?: boolean;
94
+ /** Per-row class and style overrides, same slot names as the root config. */
95
+ classNames?: Partial<Record<AccordionSlot, string>>;
96
+ styles?: Partial<Record<AccordionSlot, React.CSSProperties>>;
97
+ }
98
+ /** Text overflow for one slot: wrap, one line with an ellipsis, or clamp to n lines. */
99
+ type AccordionOverflow = 'wrap' | 'truncate' | {
100
+ lines: number;
101
+ };
102
+ type AccordionValueDetails = AccordionChangeDetails<Event | React.SyntheticEvent>;
103
+ interface AccordionBehaviourConfig {
104
+ /**
105
+ * The rows. The only required option. With `fields`, any record shape works:
106
+ * `items: faqRows, fields: { id: 'slug', title: 'question', content: 'answer' }`.
107
+ */
108
+ items: AccordionItemConfig[] | ReadonlyArray<Record<string, any>>;
109
+ /** Maps raw records onto rows, so CMS or database results need no `.map()`. */
110
+ fields?: AccordionFieldMap;
111
+ /** `single` closes siblings on open. Default `single`. */
112
+ mode?: 'single' | 'multiple';
113
+ /** In `single` mode, allow closing the open item. Default true. */
114
+ collapsible?: boolean;
115
+ /** Controlled open ids. */
116
+ value?: string[];
117
+ defaultValue?: string[];
118
+ /** `details.reason`: 'click' | 'keyboard' | 'api' | 'find-in-page' | 'hash'. */
119
+ onValueChange?: (value: string[], details: AccordionValueDetails) => void;
120
+ /** Return `false` to block a change (e.g. unsaved edits in the open panel). */
121
+ onBeforeChange?: (details: AccordionValueDetails) => boolean | void;
122
+ /** Fires for one item, after `onValueChange`. */
123
+ onItemToggle?: (id: string, open: boolean) => void;
124
+ /** Arrow-key axis for moving between headers. Default `vertical`. */
125
+ orientation?: PinfoldOrientation;
126
+ dir?: PinfoldDir;
127
+ /** Wrap focus at the ends. Default true. */
128
+ loop?: boolean;
129
+ /** Prefix for generated ids — set it for deterministic SSR markup. */
130
+ id?: string;
131
+ /** Disables every row. Rows stay focusable. Default false. */
132
+ disabled?: boolean;
133
+ /**
134
+ * Closed panels use `hidden="until-found"`, so the browser's find-in-page
135
+ * searches them and opens the match. Default true.
136
+ */
137
+ hiddenUntilFound?: boolean;
138
+ /**
139
+ * When panel content is in the DOM. `always` (default) keeps it searchable
140
+ * and indexable; `open` mounts only open panels; `first-open` mounts on
141
+ * first open and keeps it.
142
+ */
143
+ mountPanels?: AccordionMountPanels;
144
+ /** @deprecated Use `mountPanels: 'open'`. */
145
+ unmountClosed?: boolean;
146
+ /** `#id` in the URL opens that row and scrolls to it; opening a row updates the hash. Default false. */
147
+ syncWithHash?: boolean;
148
+ /** Code loading for the `/lazy` entry. The accordion is light, so `auto` loads eagerly. */
149
+ lazy?: PinfoldLazy;
150
+ }
151
+ /**
152
+ * Explicit sizing. Numbers are px, strings pass through (`'50%'`, `'18rem'`,
153
+ * `'clamp(20rem, 40vw, 32rem)'`). Each key overrides the value the `size`
154
+ * preset would have chosen, so you can take one dimension and leave the rest.
155
+ */
156
+ interface AccordionDimensions {
157
+ width?: Length;
158
+ minWidth?: Length;
159
+ maxWidth?: Length;
160
+ height?: Length;
161
+ maxHeight?: Length;
162
+ /** Space between rows (outline / separated / filled variants). */
163
+ gap?: Length;
164
+ /** Trigger padding. */
165
+ paddingX?: Length;
166
+ paddingY?: Length;
167
+ /** Panel padding. Defaults to the trigger padding. */
168
+ panelPaddingX?: Length;
169
+ panelPaddingY?: Length;
170
+ /** Cap the open panel and let it scroll past that. */
171
+ panelMaxHeight?: Length;
172
+ fontSize?: Length;
173
+ titleSize?: Length;
174
+ descriptionSize?: Length;
175
+ iconSize?: Length;
176
+ indicatorSize?: Length;
177
+ borderWidth?: Length;
178
+ /** Corner radius as a length — overrides the `radius` token preset. */
179
+ radius?: Length;
180
+ /** Open/close transition duration, e.g. `'320ms'`. */
181
+ duration?: Length;
182
+ }
183
+ interface AccordionAppearanceConfig {
184
+ variant?: AccordionVariant;
185
+ size?: PinfoldSize;
186
+ tone?: PinfoldTone;
187
+ radius?: PinfoldRadius;
188
+ /** Built-in glyph, your own node, or a function of the row's state. */
189
+ indicator?: AccordionIndicator | AccordionNode;
190
+ /** Which edge the indicator sits on. Default `end`. */
191
+ indicatorPosition?: 'start' | 'end';
192
+ /** Heading level wrapping each trigger. Default 3. */
193
+ headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
194
+ /** Animate open/close. Default true; honours prefers-reduced-motion regardless. */
195
+ animate?: boolean;
196
+ /** Full-width dividers between rows (solid/ghost variants). Default true. */
197
+ dividers?: boolean;
198
+ /** Text overflow per slot. Default: title `wrap`, description `{ lines: 2 }`. */
199
+ overflow?: {
200
+ title?: AccordionOverflow;
201
+ description?: AccordionOverflow;
202
+ };
203
+ /** Explicit sizing, per dimension. Overrides the `size` preset. */
204
+ dimensions?: AccordionDimensions;
205
+ /**
206
+ * Drop every Pinfold class so no bundled CSS applies, keeping the behaviour,
207
+ * ARIA and `data-*` state hooks. For Tailwind or your own stylesheet:
208
+ * style entirely through `classNames`.
209
+ */
210
+ unstyled?: boolean;
211
+ className?: string;
212
+ style?: React.CSSProperties;
213
+ }
214
+ interface AccordionConfig extends AccordionBehaviourConfig, AccordionAppearanceConfig, SlotOverrides<AccordionSlot, AccordionItemState> {
215
+ /** Replace an entire row. The last resort before dropping to prop getters. */
216
+ renderItem?: (args: {
217
+ state: AccordionItemState;
218
+ /** Root props for the row. Spread them or state styling breaks. */
219
+ props: PropsBag;
220
+ /** The default header, if you only want to swap the body around it. */
221
+ header: React.ReactNode;
222
+ /** The default panel. */
223
+ panel: React.ReactNode;
224
+ }) => React.ReactNode;
225
+ }
226
+ /** Props accepted by `<Accordion />` itself, merged onto the root element. */
227
+ interface AccordionRootProps {
228
+ className?: string;
229
+ style?: React.CSSProperties;
230
+ children?: never;
231
+ [key: `data-${string}`]: unknown;
232
+ }
233
+ /** Addressing a single row from a sub-component. */
234
+ interface AccordionPartProps {
235
+ id?: string;
236
+ index?: number;
237
+ className?: string;
238
+ style?: React.CSSProperties;
239
+ children?: React.ReactNode;
240
+ }
241
+ interface AccordionState {
242
+ items: AccordionItemState[];
243
+ value: string[];
244
+ isOpen: (id: string) => boolean;
245
+ open: (id: string) => void;
246
+ close: (id: string) => void;
247
+ toggle: (id: string) => void;
248
+ openAll: () => void;
249
+ closeAll: () => void;
250
+ /** Move DOM focus to a header by index. */
251
+ focusItem: (index: number) => void;
252
+ /** Equivalent to `<Accordion />`, for callers who prefer a call. */
253
+ render: (props?: AccordionRootProps) => React.ReactElement;
254
+ getRootProps: (props?: PropsBag) => PropsBag;
255
+ getItemProps: (item: AccordionItemState, props?: PropsBag) => PropsBag;
256
+ getHeaderProps: (item: AccordionItemState, props?: PropsBag) => PropsBag;
257
+ getTriggerProps: (item: AccordionItemState, props?: PropsBag) => PropsBag;
258
+ getPanelProps: (item: AccordionItemState, props?: PropsBag) => PropsBag;
259
+ }
260
+ /** The sub-components for hand-laid-out accordions. */
261
+ interface AccordionParts {
262
+ /** One complete row: header plus panel. */
263
+ Item: (props: AccordionPartProps) => React.ReactElement | null;
264
+ /** Just the heading and its button. `children` replaces the default label. */
265
+ Trigger: (props: AccordionPartProps) => React.ReactElement | null;
266
+ /** Just the collapsible region. `children` replaces the configured content. */
267
+ Panel: (props: AccordionPartProps) => React.ReactElement | null;
268
+ /** Just the open/closed glyph. */
269
+ Indicator: (props: AccordionPartProps) => React.ReactElement | null;
270
+ }
271
+ /**
272
+ * What `useAccordion()` returns: a component you render, that is also the
273
+ * controller, that also carries its parts.
274
+ */
275
+ type AccordionApi = PinfoldComponent<AccordionState & AccordionParts, AccordionRootProps>;
276
+
277
+ export { usePinfoldComponent as u };
278
+ export type { AccordionConfig as A, PinfoldComponent as P, SlotOverrides as S, AccordionApi as a, SlotRenderer as b, Slottable as c };