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 +173 -10
- package/dist/index.d.cts +44 -1
- package/dist/index.d.ts +44 -1
- package/dist/index.js +166 -10
- package/dist/styles.css +6 -6
- package/package.json +9 -4
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/
|
|
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,
|
|
64
|
-
const [switching, setSwitching] = (0,
|
|
65
|
-
const itemRefs = (0,
|
|
66
|
-
const onValueChange = (0,
|
|
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,
|
|
233
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
79
234
|
if (!value) return;
|
|
80
235
|
pinToScrollTop(itemRefs.current[value]);
|
|
81
236
|
}, [value]);
|
|
82
|
-
(0,
|
|
237
|
+
(0, import_react6.useEffect)(() => {
|
|
83
238
|
if (switching) setSwitching(false);
|
|
84
239
|
}, [switching]);
|
|
85
|
-
const getItemRef = (0,
|
|
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/
|
|
1
|
+
// src/root.tsx
|
|
2
2
|
import {
|
|
3
|
-
|
|
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 =
|
|
45
|
-
const onValueChange =
|
|
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
|
-
|
|
205
|
+
useLayoutEffect2(() => {
|
|
58
206
|
if (!value) return;
|
|
59
207
|
pinToScrollTop(itemRefs.current[value]);
|
|
60
208
|
}, [value]);
|
|
61
|
-
|
|
209
|
+
useEffect2(() => {
|
|
62
210
|
if (switching) setSwitching(false);
|
|
63
211
|
}, [switching]);
|
|
64
|
-
const getItemRef =
|
|
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,
|
|
44
|
-
is
|
|
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:
|
|
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:
|
|
53
|
+
animation-duration: 0s;
|
|
54
54
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "concertina",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
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": ">=
|
|
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": {
|