concertina 0.3.0 → 0.4.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/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,18 +17,73 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ ConcertinaContext: () => ConcertinaContext,
34
+ ConcertinaStore: () => ConcertinaStore,
35
+ Content: () => Content2,
36
+ Header: () => import_react_accordion.Header,
37
+ Item: () => Item2,
38
+ Root: () => Root2,
39
+ Trigger: () => import_react_accordion.Trigger,
23
40
  pinToScrollTop: () => pinToScrollTop,
24
- useConcertina: () => useConcertina
41
+ useConcertina: () => useConcertina,
42
+ useExpanded: () => useExpanded
25
43
  });
26
44
  module.exports = __toCommonJS(index_exports);
27
45
 
28
- // src/use-concertina.ts
46
+ // src/root.tsx
47
+ var import_react2 = require("react");
48
+ var Accordion = __toESM(require("@radix-ui/react-accordion"), 1);
49
+
50
+ // src/store.ts
29
51
  var import_react = require("react");
52
+ var ConcertinaStore = class {
53
+ constructor() {
54
+ this._value = "";
55
+ this._switching = false;
56
+ this._itemRefs = {};
57
+ this._listeners = /* @__PURE__ */ new Set();
58
+ this.subscribe = (listener) => {
59
+ this._listeners.add(listener);
60
+ return () => this._listeners.delete(listener);
61
+ };
62
+ this.getValue = () => this._value;
63
+ this.getSwitching = () => this._switching;
64
+ }
65
+ _notify() {
66
+ for (const listener of this._listeners) listener();
67
+ }
68
+ setValue(newValue) {
69
+ const wasSwitching = !!this._value && this._value !== newValue && !!newValue;
70
+ this._switching = wasSwitching;
71
+ this._value = newValue || "";
72
+ this._notify();
73
+ }
74
+ clearSwitching() {
75
+ if (!this._switching) return;
76
+ this._switching = false;
77
+ this._notify();
78
+ }
79
+ getItemRef(id) {
80
+ return this._itemRefs[id] ?? null;
81
+ }
82
+ setItemRef(id, el) {
83
+ this._itemRefs[id] = el;
84
+ }
85
+ };
86
+ var ConcertinaContext = (0, import_react.createContext)(null);
30
87
 
31
88
  // src/pin-to-scroll-top.ts
32
89
  function pinToScrollTop(el) {
@@ -58,12 +115,110 @@ function pinToScrollTop(el) {
58
115
  }
59
116
  }
60
117
 
118
+ // src/root.tsx
119
+ var import_jsx_runtime = require("react/jsx-runtime");
120
+ var Root2 = (0, import_react2.forwardRef)(
121
+ function Root3({ collapsible = true, children, ...props }, forwardedRef) {
122
+ const storeRef = (0, import_react2.useRef)(null);
123
+ if (!storeRef.current) {
124
+ storeRef.current = new ConcertinaStore();
125
+ }
126
+ const store = storeRef.current;
127
+ const value = (0, import_react2.useSyncExternalStore)(
128
+ store.subscribe,
129
+ store.getValue,
130
+ store.getValue
131
+ );
132
+ const switching = (0, import_react2.useSyncExternalStore)(
133
+ store.subscribe,
134
+ store.getSwitching,
135
+ store.getSwitching
136
+ );
137
+ const onValueChange = (0, import_react2.useCallback)(
138
+ (newValue) => store.setValue(newValue),
139
+ [store]
140
+ );
141
+ (0, import_react2.useLayoutEffect)(() => {
142
+ if (!value) return;
143
+ pinToScrollTop(store.getItemRef(value));
144
+ }, [value, store]);
145
+ (0, import_react2.useEffect)(() => {
146
+ if (switching) store.clearSwitching();
147
+ }, [switching, store]);
148
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConcertinaContext.Provider, { value: store, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
149
+ Accordion.Root,
150
+ {
151
+ ref: forwardedRef,
152
+ type: "single",
153
+ collapsible,
154
+ value,
155
+ onValueChange,
156
+ "data-switching": switching || void 0,
157
+ ...props,
158
+ children
159
+ }
160
+ ) });
161
+ }
162
+ );
163
+
164
+ // src/item.tsx
165
+ var import_react3 = require("react");
166
+ var Accordion2 = __toESM(require("@radix-ui/react-accordion"), 1);
167
+ var import_jsx_runtime2 = require("react/jsx-runtime");
168
+ var Item2 = (0, import_react3.forwardRef)(function Item3({ value, ...props }, forwardedRef) {
169
+ const store = (0, import_react3.useContext)(ConcertinaContext);
170
+ const mergedRef = (0, import_react3.useCallback)(
171
+ (el) => {
172
+ if (typeof forwardedRef === "function") {
173
+ forwardedRef(el);
174
+ } else if (forwardedRef) {
175
+ forwardedRef.current = el;
176
+ }
177
+ store?.setItemRef(value, el);
178
+ },
179
+ [forwardedRef, store, value]
180
+ );
181
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Accordion2.Item, { ref: mergedRef, value, ...props });
182
+ });
183
+
184
+ // src/content.tsx
185
+ var import_react4 = require("react");
186
+ var Accordion3 = __toESM(require("@radix-ui/react-accordion"), 1);
187
+ var import_jsx_runtime3 = require("react/jsx-runtime");
188
+ var Content2 = (0, import_react4.forwardRef)(function Content3({ className, ...props }, ref) {
189
+ const merged = className ? `concertina-content ${className}` : "concertina-content";
190
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Accordion3.Content, { ref, className: merged, ...props });
191
+ });
192
+
193
+ // src/use-expanded.ts
194
+ var import_react5 = require("react");
195
+ function useStore() {
196
+ const store = (0, import_react5.useContext)(ConcertinaContext);
197
+ if (!store) {
198
+ throw new Error("useExpanded must be used inside <Concertina.Root>");
199
+ }
200
+ return store;
201
+ }
202
+ function useExpanded(id) {
203
+ const store = useStore();
204
+ return (0, import_react5.useSyncExternalStore)(
205
+ store.subscribe,
206
+ () => store.getValue() === id,
207
+ () => false
208
+ // server snapshot
209
+ );
210
+ }
211
+
212
+ // src/index.ts
213
+ var import_react_accordion = require("@radix-ui/react-accordion");
214
+
61
215
  // src/use-concertina.ts
216
+ var import_react6 = require("react");
62
217
  function useConcertina() {
63
- const [value, setValue] = (0, import_react.useState)("");
64
- const [switching, setSwitching] = (0, import_react.useState)(false);
65
- const itemRefs = (0, import_react.useRef)({});
66
- const onValueChange = (0, import_react.useCallback)(
218
+ const [value, setValue] = (0, import_react6.useState)("");
219
+ const [switching, setSwitching] = (0, import_react6.useState)(false);
220
+ const itemRefs = (0, import_react6.useRef)({});
221
+ const onValueChange = (0, import_react6.useCallback)(
67
222
  (newValue) => {
68
223
  if (!newValue) {
69
224
  setSwitching(false);
@@ -75,14 +230,14 @@ function useConcertina() {
75
230
  },
76
231
  [value]
77
232
  );
78
- (0, import_react.useLayoutEffect)(() => {
233
+ (0, import_react6.useLayoutEffect)(() => {
79
234
  if (!value) return;
80
235
  pinToScrollTop(itemRefs.current[value]);
81
236
  }, [value]);
82
- (0, import_react.useEffect)(() => {
237
+ (0, import_react6.useEffect)(() => {
83
238
  if (switching) setSwitching(false);
84
239
  }, [switching]);
85
- const getItemRef = (0, import_react.useCallback)(
240
+ const getItemRef = (0, import_react6.useCallback)(
86
241
  (id) => (el) => {
87
242
  itemRefs.current[id] = el;
88
243
  },
@@ -97,6 +252,14 @@ function useConcertina() {
97
252
  }
98
253
  // Annotate the CommonJS export names for ESM import in node:
99
254
  0 && (module.exports = {
255
+ ConcertinaContext,
256
+ ConcertinaStore,
257
+ Content,
258
+ Header,
259
+ Item,
260
+ Root,
261
+ Trigger,
100
262
  pinToScrollTop,
101
- useConcertina
263
+ useConcertina,
264
+ useExpanded
102
265
  });
package/dist/index.d.cts CHANGED
@@ -1,3 +1,46 @@
1
+ import * as react from 'react';
2
+ import * as Accordion from '@radix-ui/react-accordion';
3
+ export { Header, Trigger } from '@radix-ui/react-accordion';
4
+
5
+ declare const Root: react.ForwardRefExoticComponent<Omit<Omit<Accordion.AccordionSingleProps & react.RefAttributes<HTMLDivElement>, "ref"> & {
6
+ type?: "single";
7
+ collapsible?: boolean;
8
+ }, "type"> & react.RefAttributes<HTMLDivElement>>;
9
+
10
+ declare const Item: react.ForwardRefExoticComponent<Omit<Accordion.AccordionItemProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
11
+
12
+ declare const Content: react.ForwardRefExoticComponent<Omit<Accordion.AccordionContentProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
13
+
14
+ /**
15
+ * Per-item expansion hook. Returns true only when this item is expanded.
16
+ *
17
+ * Uses useSyncExternalStore so the component only re-renders when
18
+ * its own boolean flips — not on every accordion state change.
19
+ */
20
+ declare function useExpanded(id: string): boolean;
21
+
22
+ type Listener = () => void;
23
+ /**
24
+ * External store for concertina accordion state.
25
+ * Lives outside React — one instance per Root.
26
+ * Subscribers get notified on value or switching changes.
27
+ */
28
+ declare class ConcertinaStore {
29
+ private _value;
30
+ private _switching;
31
+ private _itemRefs;
32
+ private _listeners;
33
+ subscribe: (listener: Listener) => (() => void);
34
+ private _notify;
35
+ getValue: () => string;
36
+ getSwitching: () => boolean;
37
+ setValue(newValue: string): void;
38
+ clearSwitching(): void;
39
+ getItemRef(id: string): HTMLElement | null;
40
+ setItemRef(id: string, el: HTMLElement | null): void;
41
+ }
42
+ declare const ConcertinaContext: react.Context<ConcertinaStore | null>;
43
+
1
44
  interface ConcertinaRootProps {
2
45
  value: string;
3
46
  onValueChange: (value: string) => void;
@@ -40,4 +83,4 @@ declare function useConcertina(): UseConcertinaReturn;
40
83
  */
41
84
  declare function pinToScrollTop(el: HTMLElement | null): void;
42
85
 
43
- export { type ConcertinaRootProps, type UseConcertinaReturn, pinToScrollTop, useConcertina };
86
+ export { ConcertinaContext, type ConcertinaRootProps, ConcertinaStore, Content, Item, Root, type UseConcertinaReturn, pinToScrollTop, useConcertina, useExpanded };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,46 @@
1
+ import * as react from 'react';
2
+ import * as Accordion from '@radix-ui/react-accordion';
3
+ export { Header, Trigger } from '@radix-ui/react-accordion';
4
+
5
+ declare const Root: react.ForwardRefExoticComponent<Omit<Omit<Accordion.AccordionSingleProps & react.RefAttributes<HTMLDivElement>, "ref"> & {
6
+ type?: "single";
7
+ collapsible?: boolean;
8
+ }, "type"> & react.RefAttributes<HTMLDivElement>>;
9
+
10
+ declare const Item: react.ForwardRefExoticComponent<Omit<Accordion.AccordionItemProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
11
+
12
+ declare const Content: react.ForwardRefExoticComponent<Omit<Accordion.AccordionContentProps & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
13
+
14
+ /**
15
+ * Per-item expansion hook. Returns true only when this item is expanded.
16
+ *
17
+ * Uses useSyncExternalStore so the component only re-renders when
18
+ * its own boolean flips — not on every accordion state change.
19
+ */
20
+ declare function useExpanded(id: string): boolean;
21
+
22
+ type Listener = () => void;
23
+ /**
24
+ * External store for concertina accordion state.
25
+ * Lives outside React — one instance per Root.
26
+ * Subscribers get notified on value or switching changes.
27
+ */
28
+ declare class ConcertinaStore {
29
+ private _value;
30
+ private _switching;
31
+ private _itemRefs;
32
+ private _listeners;
33
+ subscribe: (listener: Listener) => (() => void);
34
+ private _notify;
35
+ getValue: () => string;
36
+ getSwitching: () => boolean;
37
+ setValue(newValue: string): void;
38
+ clearSwitching(): void;
39
+ getItemRef(id: string): HTMLElement | null;
40
+ setItemRef(id: string, el: HTMLElement | null): void;
41
+ }
42
+ declare const ConcertinaContext: react.Context<ConcertinaStore | null>;
43
+
1
44
  interface ConcertinaRootProps {
2
45
  value: string;
3
46
  onValueChange: (value: string) => void;
@@ -40,4 +83,4 @@ declare function useConcertina(): UseConcertinaReturn;
40
83
  */
41
84
  declare function pinToScrollTop(el: HTMLElement | null): void;
42
85
 
43
- export { type ConcertinaRootProps, type UseConcertinaReturn, pinToScrollTop, useConcertina };
86
+ export { ConcertinaContext, type ConcertinaRootProps, ConcertinaStore, Content, Item, Root, type UseConcertinaReturn, pinToScrollTop, useConcertina, useExpanded };
package/dist/index.js CHANGED
@@ -1,11 +1,51 @@
1
- // src/use-concertina.ts
1
+ // src/root.tsx
2
2
  import {
3
- useState,
4
- useCallback,
3
+ forwardRef,
5
4
  useRef,
6
5
  useLayoutEffect,
7
- useEffect
6
+ useEffect,
7
+ useSyncExternalStore,
8
+ useCallback
8
9
  } from "react";
10
+ import * as Accordion from "@radix-ui/react-accordion";
11
+
12
+ // src/store.ts
13
+ import { createContext } from "react";
14
+ var ConcertinaStore = class {
15
+ constructor() {
16
+ this._value = "";
17
+ this._switching = false;
18
+ this._itemRefs = {};
19
+ this._listeners = /* @__PURE__ */ new Set();
20
+ this.subscribe = (listener) => {
21
+ this._listeners.add(listener);
22
+ return () => this._listeners.delete(listener);
23
+ };
24
+ this.getValue = () => this._value;
25
+ this.getSwitching = () => this._switching;
26
+ }
27
+ _notify() {
28
+ for (const listener of this._listeners) listener();
29
+ }
30
+ setValue(newValue) {
31
+ const wasSwitching = !!this._value && this._value !== newValue && !!newValue;
32
+ this._switching = wasSwitching;
33
+ this._value = newValue || "";
34
+ this._notify();
35
+ }
36
+ clearSwitching() {
37
+ if (!this._switching) return;
38
+ this._switching = false;
39
+ this._notify();
40
+ }
41
+ getItemRef(id) {
42
+ return this._itemRefs[id] ?? null;
43
+ }
44
+ setItemRef(id, el) {
45
+ this._itemRefs[id] = el;
46
+ }
47
+ };
48
+ var ConcertinaContext = createContext(null);
9
49
 
10
50
  // src/pin-to-scroll-top.ts
11
51
  function pinToScrollTop(el) {
@@ -37,12 +77,120 @@ function pinToScrollTop(el) {
37
77
  }
38
78
  }
39
79
 
80
+ // src/root.tsx
81
+ import { jsx } from "react/jsx-runtime";
82
+ var Root2 = forwardRef(
83
+ function Root3({ collapsible = true, children, ...props }, forwardedRef) {
84
+ const storeRef = useRef(null);
85
+ if (!storeRef.current) {
86
+ storeRef.current = new ConcertinaStore();
87
+ }
88
+ const store = storeRef.current;
89
+ const value = useSyncExternalStore(
90
+ store.subscribe,
91
+ store.getValue,
92
+ store.getValue
93
+ );
94
+ const switching = useSyncExternalStore(
95
+ store.subscribe,
96
+ store.getSwitching,
97
+ store.getSwitching
98
+ );
99
+ const onValueChange = useCallback(
100
+ (newValue) => store.setValue(newValue),
101
+ [store]
102
+ );
103
+ useLayoutEffect(() => {
104
+ if (!value) return;
105
+ pinToScrollTop(store.getItemRef(value));
106
+ }, [value, store]);
107
+ useEffect(() => {
108
+ if (switching) store.clearSwitching();
109
+ }, [switching, store]);
110
+ return /* @__PURE__ */ jsx(ConcertinaContext.Provider, { value: store, children: /* @__PURE__ */ jsx(
111
+ Accordion.Root,
112
+ {
113
+ ref: forwardedRef,
114
+ type: "single",
115
+ collapsible,
116
+ value,
117
+ onValueChange,
118
+ "data-switching": switching || void 0,
119
+ ...props,
120
+ children
121
+ }
122
+ ) });
123
+ }
124
+ );
125
+
126
+ // src/item.tsx
127
+ import {
128
+ forwardRef as forwardRef2,
129
+ useContext,
130
+ useCallback as useCallback2
131
+ } from "react";
132
+ import * as Accordion2 from "@radix-ui/react-accordion";
133
+ import { jsx as jsx2 } from "react/jsx-runtime";
134
+ var Item2 = forwardRef2(function Item3({ value, ...props }, forwardedRef) {
135
+ const store = useContext(ConcertinaContext);
136
+ const mergedRef = useCallback2(
137
+ (el) => {
138
+ if (typeof forwardedRef === "function") {
139
+ forwardedRef(el);
140
+ } else if (forwardedRef) {
141
+ forwardedRef.current = el;
142
+ }
143
+ store?.setItemRef(value, el);
144
+ },
145
+ [forwardedRef, store, value]
146
+ );
147
+ return /* @__PURE__ */ jsx2(Accordion2.Item, { ref: mergedRef, value, ...props });
148
+ });
149
+
150
+ // src/content.tsx
151
+ import { forwardRef as forwardRef3 } from "react";
152
+ import * as Accordion3 from "@radix-ui/react-accordion";
153
+ import { jsx as jsx3 } from "react/jsx-runtime";
154
+ var Content2 = forwardRef3(function Content3({ className, ...props }, ref) {
155
+ const merged = className ? `concertina-content ${className}` : "concertina-content";
156
+ return /* @__PURE__ */ jsx3(Accordion3.Content, { ref, className: merged, ...props });
157
+ });
158
+
159
+ // src/use-expanded.ts
160
+ import { useContext as useContext2, useSyncExternalStore as useSyncExternalStore2 } from "react";
161
+ function useStore() {
162
+ const store = useContext2(ConcertinaContext);
163
+ if (!store) {
164
+ throw new Error("useExpanded must be used inside <Concertina.Root>");
165
+ }
166
+ return store;
167
+ }
168
+ function useExpanded(id) {
169
+ const store = useStore();
170
+ return useSyncExternalStore2(
171
+ store.subscribe,
172
+ () => store.getValue() === id,
173
+ () => false
174
+ // server snapshot
175
+ );
176
+ }
177
+
178
+ // src/index.ts
179
+ import { Trigger, Header } from "@radix-ui/react-accordion";
180
+
40
181
  // src/use-concertina.ts
182
+ import {
183
+ useState,
184
+ useCallback as useCallback3,
185
+ useRef as useRef2,
186
+ useLayoutEffect as useLayoutEffect2,
187
+ useEffect as useEffect2
188
+ } from "react";
41
189
  function useConcertina() {
42
190
  const [value, setValue] = useState("");
43
191
  const [switching, setSwitching] = useState(false);
44
- const itemRefs = useRef({});
45
- const onValueChange = useCallback(
192
+ const itemRefs = useRef2({});
193
+ const onValueChange = useCallback3(
46
194
  (newValue) => {
47
195
  if (!newValue) {
48
196
  setSwitching(false);
@@ -54,14 +202,14 @@ function useConcertina() {
54
202
  },
55
203
  [value]
56
204
  );
57
- useLayoutEffect(() => {
205
+ useLayoutEffect2(() => {
58
206
  if (!value) return;
59
207
  pinToScrollTop(itemRefs.current[value]);
60
208
  }, [value]);
61
- useEffect(() => {
209
+ useEffect2(() => {
62
210
  if (switching) setSwitching(false);
63
211
  }, [switching]);
64
- const getItemRef = useCallback(
212
+ const getItemRef = useCallback3(
65
213
  (id) => (el) => {
66
214
  itemRefs.current[id] = el;
67
215
  },
@@ -75,6 +223,14 @@ function useConcertina() {
75
223
  return { value, onValueChange, switching, rootProps, getItemRef };
76
224
  }
77
225
  export {
226
+ ConcertinaContext,
227
+ ConcertinaStore,
228
+ Content2 as Content,
229
+ Header,
230
+ Item2 as Item,
231
+ Root2 as Root,
232
+ Trigger,
78
233
  pinToScrollTop,
79
- useConcertina
234
+ useConcertina,
235
+ useExpanded
80
236
  };
package/dist/styles.css CHANGED
@@ -40,15 +40,15 @@
40
40
  }
41
41
  }
42
42
 
43
- /* When switching between items, suppress all animations so the layout
44
- is immediately in its final state for scroll pinning.
43
+ /* When switching between items, run animations instantly so layout
44
+ is in its final state for scroll pinning. Uses duration: 0s rather
45
+ than animation: none to avoid re-triggering the close animation
46
+ when data-switching is cleared after paint.
45
47
  data-switching is set by useConcertina(), cleared after paint. */
46
48
  [data-switching] .concertina-content[data-state="closed"] {
47
- animation: none;
48
- height: 0;
49
- opacity: 0;
49
+ animation-duration: 0s;
50
50
  }
51
51
 
52
52
  [data-switching] .concertina-content[data-state="open"] {
53
- animation: none;
53
+ animation-duration: 0s;
54
54
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "concertina",
3
- "version": "0.3.0",
4
- "description": "React hook for scroll-pinned Radix Accordion panels. Zero dependencies.",
3
+ "version": "0.4.0",
4
+ "description": "Component API + hook for scroll-pinned Radix Accordion panels with per-item memoization.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -31,11 +31,14 @@
31
31
  "prepublishOnly": "npm run build"
32
32
  },
33
33
  "peerDependencies": {
34
- "react": ">=16.8.0"
34
+ "react": ">=18.0.0",
35
+ "@radix-ui/react-accordion": ">=1.0.0"
35
36
  },
36
37
  "devDependencies": {
38
+ "@radix-ui/react-accordion": "^1.2.0",
37
39
  "@types/react": "^19.0.0",
38
40
  "react": "^19.0.0",
41
+ "react-dom": "^19.0.0",
39
42
  "tsup": "^8.0.0",
40
43
  "typescript": "^5.0.0"
41
44
  },
@@ -45,7 +48,9 @@
45
48
  "scroll",
46
49
  "pin",
47
50
  "react",
48
- "hook"
51
+ "hook",
52
+ "memoization",
53
+ "useSyncExternalStore"
49
54
  ],
50
55
  "license": "MIT",
51
56
  "repository": {