@patternmode/aperto 0.1.1
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/LICENSE +21 -0
- package/README.md +56 -0
- package/dist/index.d.ts +224 -0
- package/dist/index.js +1113 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +2 -0
- package/package.json +70 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1113 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/Aperto/ApertoGroup.tsx
|
|
4
|
+
import { ChevronLeft, ChevronRight, X } from "lucide-react";
|
|
5
|
+
import {
|
|
6
|
+
useCallback as useCallback3,
|
|
7
|
+
useId as useId2,
|
|
8
|
+
useLayoutEffect,
|
|
9
|
+
useMemo as useMemo2,
|
|
10
|
+
useRef,
|
|
11
|
+
useState as useState3
|
|
12
|
+
} from "react";
|
|
13
|
+
import { flushSync } from "react-dom";
|
|
14
|
+
|
|
15
|
+
// src/aperto-content.tsx
|
|
16
|
+
import * as Dialog from "@radix-ui/react-dialog";
|
|
17
|
+
import {
|
|
18
|
+
motion,
|
|
19
|
+
useMotionValue,
|
|
20
|
+
useSpring,
|
|
21
|
+
useTransform
|
|
22
|
+
} from "motion/react";
|
|
23
|
+
import {
|
|
24
|
+
forwardRef,
|
|
25
|
+
useCallback,
|
|
26
|
+
useMemo,
|
|
27
|
+
useState
|
|
28
|
+
} from "react";
|
|
29
|
+
|
|
30
|
+
// src/context.ts
|
|
31
|
+
import { createContext, useContext } from "react";
|
|
32
|
+
var ApertoContext = createContext(null);
|
|
33
|
+
function useApertoContext() {
|
|
34
|
+
const ctx = useContext(ApertoContext);
|
|
35
|
+
if (!ctx) {
|
|
36
|
+
throw new Error("Aperto components must be used within <Aperto.Root>");
|
|
37
|
+
}
|
|
38
|
+
return ctx;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/physics.ts
|
|
42
|
+
var RESISTANCE_DIVISOR = 20;
|
|
43
|
+
var RESISTANCE_SCALE = 4;
|
|
44
|
+
var RESISTANCE_MIN = 0.1;
|
|
45
|
+
function calculateResistance(distance) {
|
|
46
|
+
return Math.max(
|
|
47
|
+
RESISTANCE_MIN,
|
|
48
|
+
1 - Math.log(distance / RESISTANCE_DIVISOR + 1) / RESISTANCE_SCALE
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
var SCALE_INPUT = [0, 50, 100];
|
|
52
|
+
var SCALE_OUTPUT = [1, 0.98, 0.95];
|
|
53
|
+
var OPACITY_INPUT = [0, 80];
|
|
54
|
+
var OPACITY_OUTPUT = [1, 0.96];
|
|
55
|
+
var DEFAULT_DISTANCE_THRESHOLD = 100;
|
|
56
|
+
var DEFAULT_VELOCITY_THRESHOLD = 500;
|
|
57
|
+
|
|
58
|
+
// src/motion-tokens.ts
|
|
59
|
+
var durations = {
|
|
60
|
+
snappy: 0.22,
|
|
61
|
+
moderate: 0.4
|
|
62
|
+
};
|
|
63
|
+
var easings = {
|
|
64
|
+
customGentle: [0.25, 0.1, 0.12, 1],
|
|
65
|
+
snappy: [0.22, 1, 0.36, 1]
|
|
66
|
+
};
|
|
67
|
+
var springs = {
|
|
68
|
+
bouncy: { stiffness: 260, damping: 12, mass: 1 },
|
|
69
|
+
natural: { stiffness: 200, damping: 20, mass: 1 },
|
|
70
|
+
snappy: { stiffness: 400, damping: 28, mass: 0.8 },
|
|
71
|
+
stiff: { stiffness: 500, damping: 30, mass: 1 }
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/presets.ts
|
|
75
|
+
var PRESETS = {
|
|
76
|
+
snappy: {
|
|
77
|
+
transition: { ease: easings.snappy, duration: durations.snappy },
|
|
78
|
+
drag: {
|
|
79
|
+
stiffness: springs.stiff.stiffness,
|
|
80
|
+
damping: springs.stiff.damping,
|
|
81
|
+
restDelta: 0.01
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
smooth: {
|
|
85
|
+
transition: { ease: easings.customGentle, duration: durations.moderate },
|
|
86
|
+
drag: {
|
|
87
|
+
stiffness: springs.natural.stiffness,
|
|
88
|
+
damping: springs.natural.damping,
|
|
89
|
+
restDelta: 0.01
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
bouncy: {
|
|
93
|
+
transition: {
|
|
94
|
+
type: "spring",
|
|
95
|
+
stiffness: springs.bouncy.stiffness,
|
|
96
|
+
damping: springs.bouncy.damping,
|
|
97
|
+
mass: springs.bouncy.mass
|
|
98
|
+
},
|
|
99
|
+
drag: {
|
|
100
|
+
stiffness: springs.snappy.stiffness,
|
|
101
|
+
damping: springs.snappy.damping,
|
|
102
|
+
restDelta: 0.01
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
reduced: {
|
|
106
|
+
transition: { ease: "linear", duration: 0.01 },
|
|
107
|
+
drag: { stiffness: 1e3, damping: 50, restDelta: 0.1 }
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
function resolvePreset(componentType, componentMotion, globalPreset, variants, reduceMotion) {
|
|
111
|
+
if (reduceMotion) {
|
|
112
|
+
return PRESETS.reduced;
|
|
113
|
+
}
|
|
114
|
+
if (componentMotion) {
|
|
115
|
+
return PRESETS[componentMotion];
|
|
116
|
+
}
|
|
117
|
+
if (variants?.[componentType]) {
|
|
118
|
+
return PRESETS[variants[componentType]];
|
|
119
|
+
}
|
|
120
|
+
return PRESETS[globalPreset];
|
|
121
|
+
}
|
|
122
|
+
function getDragSpring(preset) {
|
|
123
|
+
return preset.drag;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/aperto-content.tsx
|
|
127
|
+
import { jsx } from "react/jsx-runtime";
|
|
128
|
+
var ApertoContent = forwardRef(
|
|
129
|
+
({
|
|
130
|
+
children,
|
|
131
|
+
className,
|
|
132
|
+
motion: motionOverride,
|
|
133
|
+
placement = "center",
|
|
134
|
+
sharedLayoutId,
|
|
135
|
+
style,
|
|
136
|
+
...props
|
|
137
|
+
}, ref) => {
|
|
138
|
+
const ctx = useApertoContext();
|
|
139
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
140
|
+
const resolved = resolvePreset(
|
|
141
|
+
"content",
|
|
142
|
+
motionOverride,
|
|
143
|
+
ctx.presetName,
|
|
144
|
+
ctx.variants,
|
|
145
|
+
ctx.reduceMotion
|
|
146
|
+
);
|
|
147
|
+
const contentLayoutId = sharedLayoutId === false ? void 0 : sharedLayoutId ?? `${ctx.layoutId}-shared`;
|
|
148
|
+
const dragSpring = getDragSpring(resolved);
|
|
149
|
+
const isDismissible = ctx.dismissible !== false;
|
|
150
|
+
const dismissConfig = typeof ctx.dismissible === "object" ? ctx.dismissible : {};
|
|
151
|
+
const distanceThreshold = dismissConfig.threshold ?? DEFAULT_DISTANCE_THRESHOLD;
|
|
152
|
+
const velocityThreshold = dismissConfig.velocity ?? DEFAULT_VELOCITY_THRESHOLD;
|
|
153
|
+
const dragX = useMotionValue(0);
|
|
154
|
+
const dragY = useMotionValue(0);
|
|
155
|
+
const springX = useSpring(dragX, dragSpring);
|
|
156
|
+
const springY = useSpring(dragY, dragSpring);
|
|
157
|
+
const distance = useTransform(
|
|
158
|
+
() => Math.hypot(springX.get(), springY.get())
|
|
159
|
+
);
|
|
160
|
+
const scaleMotion = useTransform(distance, SCALE_INPUT, SCALE_OUTPUT);
|
|
161
|
+
const opacityMotion = useTransform(distance, OPACITY_INPUT, OPACITY_OUTPUT);
|
|
162
|
+
const resistance = useTransform(distance, calculateResistance);
|
|
163
|
+
const handleDrag = useCallback(
|
|
164
|
+
(_event, info) => {
|
|
165
|
+
if (!isDragging) {
|
|
166
|
+
setIsDragging(true);
|
|
167
|
+
}
|
|
168
|
+
const r = resistance.get();
|
|
169
|
+
dragX.set(info.offset.x * r);
|
|
170
|
+
dragY.set(info.offset.y * r);
|
|
171
|
+
},
|
|
172
|
+
[dragX, dragY, isDragging, resistance]
|
|
173
|
+
);
|
|
174
|
+
const handleDragEnd = useCallback(
|
|
175
|
+
(_event, info) => {
|
|
176
|
+
setIsDragging(false);
|
|
177
|
+
const speed = Math.hypot(info.velocity.x, info.velocity.y);
|
|
178
|
+
const dist = Math.hypot(info.offset.x, info.offset.y);
|
|
179
|
+
if (speed > velocityThreshold || dist > distanceThreshold) {
|
|
180
|
+
ctx.onOpenChange(false);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
dragX.set(0);
|
|
184
|
+
dragY.set(0);
|
|
185
|
+
},
|
|
186
|
+
[ctx.onOpenChange, distanceThreshold, dragX, dragY, velocityThreshold]
|
|
187
|
+
);
|
|
188
|
+
const motionStyle = useMemo(
|
|
189
|
+
() => ({
|
|
190
|
+
cursor: isDragging ? "grabbing" : isDismissible ? "grab" : void 0,
|
|
191
|
+
opacity: opacityMotion,
|
|
192
|
+
scale: scaleMotion,
|
|
193
|
+
x: springX,
|
|
194
|
+
y: springY,
|
|
195
|
+
...placement === "center" ? {
|
|
196
|
+
left: "50%",
|
|
197
|
+
top: "50%",
|
|
198
|
+
translate: "-50% -50%"
|
|
199
|
+
} : {},
|
|
200
|
+
...style
|
|
201
|
+
}),
|
|
202
|
+
[
|
|
203
|
+
isDismissible,
|
|
204
|
+
isDragging,
|
|
205
|
+
opacityMotion,
|
|
206
|
+
placement,
|
|
207
|
+
scaleMotion,
|
|
208
|
+
springX,
|
|
209
|
+
springY,
|
|
210
|
+
style
|
|
211
|
+
]
|
|
212
|
+
);
|
|
213
|
+
return /* @__PURE__ */ jsx(Dialog.Content, { asChild: true, forceMount: true, ...props, children: /* @__PURE__ */ jsx(
|
|
214
|
+
motion.div,
|
|
215
|
+
{
|
|
216
|
+
className,
|
|
217
|
+
"data-slot": "aperto-content",
|
|
218
|
+
drag: isDismissible,
|
|
219
|
+
dragConstraints: { bottom: 0, left: 0, right: 0, top: 0 },
|
|
220
|
+
dragElastic: 0,
|
|
221
|
+
dragMomentum: false,
|
|
222
|
+
dragSnapToOrigin: true,
|
|
223
|
+
layout: contentLayoutId ? true : void 0,
|
|
224
|
+
layoutId: contentLayoutId,
|
|
225
|
+
onDrag: isDismissible ? handleDrag : void 0,
|
|
226
|
+
onDragEnd: isDismissible ? handleDragEnd : void 0,
|
|
227
|
+
ref,
|
|
228
|
+
style: motionStyle,
|
|
229
|
+
transition: resolved.transition,
|
|
230
|
+
children
|
|
231
|
+
}
|
|
232
|
+
) });
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
ApertoContent.displayName = "ApertoContent";
|
|
236
|
+
|
|
237
|
+
// src/aperto-description.tsx
|
|
238
|
+
import * as Dialog2 from "@radix-ui/react-dialog";
|
|
239
|
+
import { forwardRef as forwardRef2 } from "react";
|
|
240
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
241
|
+
var ApertoDescription = forwardRef2((props, ref) => {
|
|
242
|
+
return /* @__PURE__ */ jsx2(Dialog2.Description, { "data-slot": "aperto-description", ref, ...props });
|
|
243
|
+
});
|
|
244
|
+
ApertoDescription.displayName = "ApertoDescription";
|
|
245
|
+
|
|
246
|
+
// src/aperto-group-context.ts
|
|
247
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
248
|
+
var ApertoGroupContext = createContext2(
|
|
249
|
+
null
|
|
250
|
+
);
|
|
251
|
+
function useApertoGroup() {
|
|
252
|
+
const ctx = useContext2(ApertoGroupContext);
|
|
253
|
+
if (!ctx) {
|
|
254
|
+
throw new Error("Aperto.Thumbnail must be used within <Aperto.Group>");
|
|
255
|
+
}
|
|
256
|
+
return ctx;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/aperto-overlay.tsx
|
|
260
|
+
import * as Dialog3 from "@radix-ui/react-dialog";
|
|
261
|
+
import { motion as motion2 } from "motion/react";
|
|
262
|
+
import { forwardRef as forwardRef3 } from "react";
|
|
263
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
264
|
+
var OVERLAY_TRANSITION = {
|
|
265
|
+
duration: 0.3,
|
|
266
|
+
ease: easings.customGentle
|
|
267
|
+
};
|
|
268
|
+
var OVERLAY_TRANSITION_REDUCED = {
|
|
269
|
+
duration: 0.01,
|
|
270
|
+
ease: "linear"
|
|
271
|
+
};
|
|
272
|
+
var ApertoOverlay = forwardRef3(
|
|
273
|
+
({ className, fadeOut, style }, ref) => {
|
|
274
|
+
const ctx = useApertoContext();
|
|
275
|
+
const transition = ctx.reduceMotion ? OVERLAY_TRANSITION_REDUCED : OVERLAY_TRANSITION;
|
|
276
|
+
return /* @__PURE__ */ jsx3(Dialog3.Overlay, { asChild: true, forceMount: true, children: /* @__PURE__ */ jsx3(
|
|
277
|
+
motion2.div,
|
|
278
|
+
{
|
|
279
|
+
animate: { opacity: fadeOut ? 0 : 1 },
|
|
280
|
+
className,
|
|
281
|
+
"data-slot": "aperto-overlay",
|
|
282
|
+
exit: { opacity: 0 },
|
|
283
|
+
initial: { opacity: 0 },
|
|
284
|
+
ref,
|
|
285
|
+
style,
|
|
286
|
+
transition
|
|
287
|
+
}
|
|
288
|
+
) });
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
ApertoOverlay.displayName = "ApertoOverlay";
|
|
292
|
+
|
|
293
|
+
// src/aperto-portal.tsx
|
|
294
|
+
import * as Dialog4 from "@radix-ui/react-dialog";
|
|
295
|
+
import { AnimatePresence } from "motion/react";
|
|
296
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
297
|
+
function ApertoPortal({ children, container }) {
|
|
298
|
+
const { open } = useApertoContext();
|
|
299
|
+
return /* @__PURE__ */ jsx4(AnimatePresence, { children: open && /* @__PURE__ */ jsx4(Dialog4.Portal, { container, forceMount: true, children }) });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/aperto-root.tsx
|
|
303
|
+
import * as Dialog5 from "@radix-ui/react-dialog";
|
|
304
|
+
import { LayoutGroup, useReducedMotion } from "motion/react";
|
|
305
|
+
import {
|
|
306
|
+
useCallback as useCallback2,
|
|
307
|
+
useId,
|
|
308
|
+
useState as useState2
|
|
309
|
+
} from "react";
|
|
310
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
311
|
+
function ApertoRoot({
|
|
312
|
+
children,
|
|
313
|
+
dismissible = true,
|
|
314
|
+
layoutId: layoutIdProp,
|
|
315
|
+
motion: motionProp = "smooth",
|
|
316
|
+
open: controlledOpen,
|
|
317
|
+
onOpenChange: controlledOnOpenChange,
|
|
318
|
+
reduceMotion: reduceMotionProp,
|
|
319
|
+
...dialogProps
|
|
320
|
+
}) {
|
|
321
|
+
const generatedId = useId();
|
|
322
|
+
const layoutId = layoutIdProp ?? `aperto-${generatedId}`;
|
|
323
|
+
const systemReducedMotion = useReducedMotion() ?? false;
|
|
324
|
+
const shouldReduce = reduceMotionProp ?? systemReducedMotion;
|
|
325
|
+
const [internalOpen, setInternalOpen] = useState2(false);
|
|
326
|
+
const isControlled = controlledOpen !== void 0;
|
|
327
|
+
const open = isControlled ? controlledOpen : internalOpen;
|
|
328
|
+
const onOpenChange = useCallback2(
|
|
329
|
+
(nextOpen) => {
|
|
330
|
+
if (!isControlled) {
|
|
331
|
+
setInternalOpen(nextOpen);
|
|
332
|
+
}
|
|
333
|
+
controlledOnOpenChange?.(nextOpen);
|
|
334
|
+
},
|
|
335
|
+
[isControlled, controlledOnOpenChange]
|
|
336
|
+
);
|
|
337
|
+
const globalPresetName = typeof motionProp === "string" ? motionProp : "smooth";
|
|
338
|
+
const variants = typeof motionProp === "object" ? motionProp : void 0;
|
|
339
|
+
const presetName = shouldReduce ? "reduced" : globalPresetName;
|
|
340
|
+
const preset = PRESETS[presetName];
|
|
341
|
+
return /* @__PURE__ */ jsx5(
|
|
342
|
+
ApertoContext.Provider,
|
|
343
|
+
{
|
|
344
|
+
value: {
|
|
345
|
+
dismissible,
|
|
346
|
+
layoutId,
|
|
347
|
+
onOpenChange,
|
|
348
|
+
open,
|
|
349
|
+
preset,
|
|
350
|
+
presetName,
|
|
351
|
+
reduceMotion: shouldReduce,
|
|
352
|
+
variants
|
|
353
|
+
},
|
|
354
|
+
children: /* @__PURE__ */ jsx5(Dialog5.Root, { onOpenChange, open, ...dialogProps, children: /* @__PURE__ */ jsx5(LayoutGroup, { id: layoutId, children }) })
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/aperto-title.tsx
|
|
360
|
+
import * as Dialog6 from "@radix-ui/react-dialog";
|
|
361
|
+
import { forwardRef as forwardRef4 } from "react";
|
|
362
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
363
|
+
var ApertoTitle = forwardRef4((props, ref) => {
|
|
364
|
+
return /* @__PURE__ */ jsx6(Dialog6.Title, { "data-slot": "aperto-title", ref, ...props });
|
|
365
|
+
});
|
|
366
|
+
ApertoTitle.displayName = "ApertoTitle";
|
|
367
|
+
|
|
368
|
+
// src/expanded-media-stage.tsx
|
|
369
|
+
import {
|
|
370
|
+
AnimatePresence as AnimatePresence2,
|
|
371
|
+
motion as motion3
|
|
372
|
+
} from "motion/react";
|
|
373
|
+
|
|
374
|
+
// src/media-rendering.tsx
|
|
375
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
376
|
+
function getMediaLabel(item) {
|
|
377
|
+
return item.title ?? item.alt ?? "media";
|
|
378
|
+
}
|
|
379
|
+
function getMediaKey(item, index) {
|
|
380
|
+
return item.id ?? `${item.type}:${item.src}:${index}`;
|
|
381
|
+
}
|
|
382
|
+
function getDescriptionProps(item) {
|
|
383
|
+
return item.description ? {} : { "aria-describedby": void 0 };
|
|
384
|
+
}
|
|
385
|
+
function renderDefaultImage(props) {
|
|
386
|
+
const { item: _item, variant: _variant, ...imageProps } = props;
|
|
387
|
+
return /* @__PURE__ */ jsx7("img", { ...imageProps, alt: imageProps.alt ?? "" });
|
|
388
|
+
}
|
|
389
|
+
function renderDefaultVideo(props) {
|
|
390
|
+
const { item: _item, variant, ...videoProps } = props;
|
|
391
|
+
if (variant === "thumbnail") {
|
|
392
|
+
return /* @__PURE__ */ jsx7("img", { alt: videoProps["aria-label"] ?? "", src: props.item.thumbnailSrc });
|
|
393
|
+
}
|
|
394
|
+
return /* @__PURE__ */ jsx7("video", { ...videoProps });
|
|
395
|
+
}
|
|
396
|
+
function renderThumbnail(item, renderImage, renderVideo) {
|
|
397
|
+
if (item.type === "image") {
|
|
398
|
+
const imageProps = {
|
|
399
|
+
alt: "",
|
|
400
|
+
height: item.height,
|
|
401
|
+
item,
|
|
402
|
+
src: item.thumbnailSrc ?? item.src,
|
|
403
|
+
variant: "thumbnail",
|
|
404
|
+
width: item.width
|
|
405
|
+
};
|
|
406
|
+
return (renderImage ?? renderDefaultImage)(imageProps);
|
|
407
|
+
}
|
|
408
|
+
const videoProps = {
|
|
409
|
+
"aria-label": item.alt ?? item.title ?? "Video thumbnail",
|
|
410
|
+
height: item.height,
|
|
411
|
+
item,
|
|
412
|
+
poster: item.poster,
|
|
413
|
+
src: item.thumbnailSrc,
|
|
414
|
+
variant: "thumbnail",
|
|
415
|
+
width: item.width
|
|
416
|
+
};
|
|
417
|
+
return (renderVideo ?? renderDefaultVideo)(videoProps);
|
|
418
|
+
}
|
|
419
|
+
function renderExpandedMedia(item, renderImage, renderVideo) {
|
|
420
|
+
if (item.type === "image") {
|
|
421
|
+
const imageProps = {
|
|
422
|
+
alt: item.alt,
|
|
423
|
+
height: item.height,
|
|
424
|
+
item,
|
|
425
|
+
src: item.src,
|
|
426
|
+
variant: "expanded",
|
|
427
|
+
width: item.width
|
|
428
|
+
};
|
|
429
|
+
return (renderImage ?? renderDefaultImage)(imageProps);
|
|
430
|
+
}
|
|
431
|
+
const videoProps = {
|
|
432
|
+
"aria-label": item.alt ?? item.title ?? "Video",
|
|
433
|
+
controls: true,
|
|
434
|
+
height: item.height,
|
|
435
|
+
item,
|
|
436
|
+
poster: item.poster,
|
|
437
|
+
src: item.src,
|
|
438
|
+
variant: "expanded",
|
|
439
|
+
width: item.width
|
|
440
|
+
};
|
|
441
|
+
return (renderVideo ?? renderDefaultVideo)(videoProps);
|
|
442
|
+
}
|
|
443
|
+
function renderTransitionMedia(item) {
|
|
444
|
+
if (item.type === "image") {
|
|
445
|
+
return /* @__PURE__ */ jsx7("img", { alt: "", src: item.src });
|
|
446
|
+
}
|
|
447
|
+
return /* @__PURE__ */ jsx7("img", { alt: "", src: item.poster ?? item.thumbnailSrc });
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/expanded-media-stage.tsx
|
|
451
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
452
|
+
var NAVIGATION_MOTION_PRESETS = {
|
|
453
|
+
float: {
|
|
454
|
+
offset: 96,
|
|
455
|
+
scale: 0.97,
|
|
456
|
+
transition: {
|
|
457
|
+
opacity: { duration: 0.34, ease: [0.45, 0, 0.2, 1] },
|
|
458
|
+
scale: { damping: 26, mass: 1.15, stiffness: 80, type: "spring" },
|
|
459
|
+
x: { damping: 26, mass: 1.15, stiffness: 80, type: "spring" }
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
glide: {
|
|
463
|
+
offset: 58,
|
|
464
|
+
scale: 0.984,
|
|
465
|
+
transition: {
|
|
466
|
+
opacity: { duration: 0.24, ease: [0.4, 0, 0.2, 1] },
|
|
467
|
+
scale: { damping: 28, mass: 0.95, stiffness: 150, type: "spring" },
|
|
468
|
+
x: { damping: 28, mass: 0.95, stiffness: 150, type: "spring" }
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
snap: {
|
|
472
|
+
offset: 38,
|
|
473
|
+
scale: 0.996,
|
|
474
|
+
transition: {
|
|
475
|
+
opacity: { duration: 0.12, ease: [0.2, 0, 0, 1] },
|
|
476
|
+
scale: { damping: 34, mass: 0.7, stiffness: 420, type: "spring" },
|
|
477
|
+
x: { damping: 34, mass: 0.7, stiffness: 420, type: "spring" }
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
var REDUCED_EXPANDED_MEDIA_TRANSITION = {
|
|
482
|
+
duration: 0.12,
|
|
483
|
+
ease: "linear"
|
|
484
|
+
};
|
|
485
|
+
var expandedMediaVariants = {
|
|
486
|
+
center: {
|
|
487
|
+
opacity: 1,
|
|
488
|
+
scale: 1,
|
|
489
|
+
x: 0
|
|
490
|
+
},
|
|
491
|
+
enter: ({
|
|
492
|
+
direction,
|
|
493
|
+
offset,
|
|
494
|
+
scale
|
|
495
|
+
}) => ({
|
|
496
|
+
opacity: 0,
|
|
497
|
+
scale: direction === 0 ? 1 : scale,
|
|
498
|
+
x: direction * offset
|
|
499
|
+
}),
|
|
500
|
+
exit: ({
|
|
501
|
+
direction,
|
|
502
|
+
offset,
|
|
503
|
+
scale
|
|
504
|
+
}) => ({
|
|
505
|
+
opacity: 0,
|
|
506
|
+
scale: direction === 0 ? 1 : scale,
|
|
507
|
+
x: direction * -offset
|
|
508
|
+
})
|
|
509
|
+
};
|
|
510
|
+
var reducedExpandedMediaVariants = {
|
|
511
|
+
center: {
|
|
512
|
+
opacity: 1,
|
|
513
|
+
x: 0
|
|
514
|
+
},
|
|
515
|
+
enter: {
|
|
516
|
+
opacity: 0,
|
|
517
|
+
x: 0
|
|
518
|
+
},
|
|
519
|
+
exit: {
|
|
520
|
+
opacity: 0,
|
|
521
|
+
x: 0
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
function ApertoExpandedMediaStage({
|
|
525
|
+
direction,
|
|
526
|
+
index,
|
|
527
|
+
item,
|
|
528
|
+
navigationMotion,
|
|
529
|
+
renderImage,
|
|
530
|
+
renderVideo
|
|
531
|
+
}) {
|
|
532
|
+
const ctx = useApertoContext();
|
|
533
|
+
const preset = NAVIGATION_MOTION_PRESETS[navigationMotion];
|
|
534
|
+
const transition = ctx.reduceMotion ? REDUCED_EXPANDED_MEDIA_TRANSITION : preset.transition;
|
|
535
|
+
const variants = ctx.reduceMotion ? reducedExpandedMediaVariants : expandedMediaVariants;
|
|
536
|
+
const animationCustom = {
|
|
537
|
+
direction,
|
|
538
|
+
offset: preset.offset,
|
|
539
|
+
scale: preset.scale
|
|
540
|
+
};
|
|
541
|
+
return /* @__PURE__ */ jsx8(AnimatePresence2, { custom: animationCustom, initial: false, mode: "sync", children: /* @__PURE__ */ jsx8(
|
|
542
|
+
motion3.div,
|
|
543
|
+
{
|
|
544
|
+
animate: "center",
|
|
545
|
+
custom: animationCustom,
|
|
546
|
+
"data-navigation-direction": direction,
|
|
547
|
+
"data-navigation-motion": navigationMotion,
|
|
548
|
+
"data-slot": "aperto-media-stage",
|
|
549
|
+
exit: "exit",
|
|
550
|
+
initial: "enter",
|
|
551
|
+
transition,
|
|
552
|
+
variants,
|
|
553
|
+
children: renderExpandedMedia(item, renderImage, renderVideo)
|
|
554
|
+
},
|
|
555
|
+
getMediaKey(item, index)
|
|
556
|
+
) });
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/media-transition.tsx
|
|
560
|
+
import {
|
|
561
|
+
motion as motion4
|
|
562
|
+
} from "motion/react";
|
|
563
|
+
import { useEffect } from "react";
|
|
564
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
565
|
+
function rectFromElement(element) {
|
|
566
|
+
if (!element) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
const rect = element.getBoundingClientRect();
|
|
570
|
+
return {
|
|
571
|
+
height: rect.height,
|
|
572
|
+
left: rect.left,
|
|
573
|
+
top: rect.top,
|
|
574
|
+
width: rect.width
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function rectTarget(rect) {
|
|
578
|
+
return {
|
|
579
|
+
height: rect.height,
|
|
580
|
+
left: rect.left,
|
|
581
|
+
top: rect.top,
|
|
582
|
+
width: rect.width
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function transitionDurationMs(transition) {
|
|
586
|
+
return typeof transition.duration === "number" ? transition.duration * 1e3 : 450;
|
|
587
|
+
}
|
|
588
|
+
function ApertoMediaTransitionClone({
|
|
589
|
+
onComplete,
|
|
590
|
+
transition
|
|
591
|
+
}) {
|
|
592
|
+
const ctx = useApertoContext();
|
|
593
|
+
useEffect(() => {
|
|
594
|
+
if (!transition?.to) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const timer = setTimeout(
|
|
598
|
+
onComplete,
|
|
599
|
+
transitionDurationMs(ctx.preset.transition)
|
|
600
|
+
);
|
|
601
|
+
return () => clearTimeout(timer);
|
|
602
|
+
}, [ctx.preset.transition, onComplete, transition]);
|
|
603
|
+
if (!transition?.to) {
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
return /* @__PURE__ */ jsx9(
|
|
607
|
+
motion4.div,
|
|
608
|
+
{
|
|
609
|
+
animate: rectTarget(transition.to),
|
|
610
|
+
"data-slot": "aperto-transition-media",
|
|
611
|
+
initial: rectTarget(transition.from),
|
|
612
|
+
style: {
|
|
613
|
+
borderRadius: "var(--aperto-radius, 0.5rem)",
|
|
614
|
+
overflow: "hidden",
|
|
615
|
+
pointerEvents: "none",
|
|
616
|
+
position: "fixed",
|
|
617
|
+
willChange: "left, top, width, height",
|
|
618
|
+
zIndex: 1002
|
|
619
|
+
},
|
|
620
|
+
transition: ctx.preset.transition,
|
|
621
|
+
children: renderTransitionMedia(transition.item)
|
|
622
|
+
}
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// src/Aperto/ApertoKeyboard.ts
|
|
627
|
+
function shouldIgnoreKeyboardNavigationTarget(target) {
|
|
628
|
+
if (!(target instanceof Element)) {
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
return target.isContentEditable || Boolean(
|
|
632
|
+
target.closest(
|
|
633
|
+
'input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="textbox"], audio, video'
|
|
634
|
+
)
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/Aperto/ApertoGroup.tsx
|
|
639
|
+
import { Fragment, jsx as jsx10, jsxs } from "react/jsx-runtime";
|
|
640
|
+
function ApertoGroup({
|
|
641
|
+
children,
|
|
642
|
+
classNames,
|
|
643
|
+
dismissible,
|
|
644
|
+
index: controlledIndex,
|
|
645
|
+
initialIndex = 0,
|
|
646
|
+
media,
|
|
647
|
+
motion: motionProp,
|
|
648
|
+
navigationMotion = "glide",
|
|
649
|
+
onIndexChange,
|
|
650
|
+
renderImage,
|
|
651
|
+
renderVideo
|
|
652
|
+
}) {
|
|
653
|
+
const generatedId = useId2();
|
|
654
|
+
const [internalIndex, setInternalIndex] = useState3(initialIndex);
|
|
655
|
+
const [open, setOpen] = useState3(false);
|
|
656
|
+
const [closing, setClosing] = useState3(false);
|
|
657
|
+
const [mediaTransition, setMediaTransition] = useState3(null);
|
|
658
|
+
const [navigationDirection, setNavigationDirection] = useState3(0);
|
|
659
|
+
const isControlled = controlledIndex !== void 0;
|
|
660
|
+
const index = isControlled ? controlledIndex : internalIndex;
|
|
661
|
+
const activeMedia = media[index] ?? media[0];
|
|
662
|
+
const [layoutSourceIndex, setLayoutSourceIndex] = useState3(index);
|
|
663
|
+
const expandedMediaRef = useRef(null);
|
|
664
|
+
const thumbnailRefs = useRef(/* @__PURE__ */ new Map());
|
|
665
|
+
const sharedLayoutIdForIndex = useCallback3(
|
|
666
|
+
(nextIndex) => `aperto-group-${generatedId}-${nextIndex}-shared`,
|
|
667
|
+
[generatedId]
|
|
668
|
+
);
|
|
669
|
+
const sharedLayoutId = sharedLayoutIdForIndex(layoutSourceIndex);
|
|
670
|
+
const measureOpeningTarget = useCallback3((node) => {
|
|
671
|
+
const targetRect = rectFromElement(node);
|
|
672
|
+
if (!targetRect) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
setMediaTransition((current) => {
|
|
676
|
+
if (current?.phase !== "opening" || current.to) {
|
|
677
|
+
return current;
|
|
678
|
+
}
|
|
679
|
+
return { ...current, to: targetRect };
|
|
680
|
+
});
|
|
681
|
+
}, []);
|
|
682
|
+
const setExpandedMediaNode = useCallback3(
|
|
683
|
+
(node) => {
|
|
684
|
+
expandedMediaRef.current = node;
|
|
685
|
+
if (node) {
|
|
686
|
+
measureOpeningTarget(node);
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
[measureOpeningTarget]
|
|
690
|
+
);
|
|
691
|
+
const registerThumbnail = useCallback3(
|
|
692
|
+
(thumbIndex, node) => {
|
|
693
|
+
if (node) {
|
|
694
|
+
thumbnailRefs.current.set(thumbIndex, node);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
thumbnailRefs.current.delete(thumbIndex);
|
|
698
|
+
},
|
|
699
|
+
[]
|
|
700
|
+
);
|
|
701
|
+
useLayoutEffect(() => {
|
|
702
|
+
if (mediaTransition?.phase !== "opening" || mediaTransition.to || !expandedMediaRef.current) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
measureOpeningTarget(expandedMediaRef.current);
|
|
706
|
+
}, [measureOpeningTarget, mediaTransition]);
|
|
707
|
+
const setIndex = useCallback3(
|
|
708
|
+
(nextIndex) => {
|
|
709
|
+
if (!isControlled) {
|
|
710
|
+
setInternalIndex(nextIndex);
|
|
711
|
+
}
|
|
712
|
+
onIndexChange?.(nextIndex);
|
|
713
|
+
},
|
|
714
|
+
[isControlled, onIndexChange]
|
|
715
|
+
);
|
|
716
|
+
const openAtIndex = useCallback3(
|
|
717
|
+
(thumbIndex) => {
|
|
718
|
+
const sourceRect = rectFromElement(
|
|
719
|
+
thumbnailRefs.current.get(thumbIndex) ?? null
|
|
720
|
+
);
|
|
721
|
+
const item = media[thumbIndex];
|
|
722
|
+
flushSync(() => {
|
|
723
|
+
setClosing(false);
|
|
724
|
+
setMediaTransition(
|
|
725
|
+
sourceRect && item ? { from: sourceRect, item, phase: "opening" } : null
|
|
726
|
+
);
|
|
727
|
+
setNavigationDirection(0);
|
|
728
|
+
setLayoutSourceIndex(thumbIndex);
|
|
729
|
+
setIndex(thumbIndex);
|
|
730
|
+
setOpen(true);
|
|
731
|
+
});
|
|
732
|
+
},
|
|
733
|
+
[media, setIndex]
|
|
734
|
+
);
|
|
735
|
+
const startClose = useCallback3(() => {
|
|
736
|
+
if (!open || closing) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
const sourceRect = rectFromElement(expandedMediaRef.current);
|
|
740
|
+
const targetRect = rectFromElement(
|
|
741
|
+
thumbnailRefs.current.get(index) ?? null
|
|
742
|
+
);
|
|
743
|
+
if (!activeMedia || !sourceRect || !targetRect) {
|
|
744
|
+
setMediaTransition(null);
|
|
745
|
+
setClosing(false);
|
|
746
|
+
setOpen(false);
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
flushSync(() => {
|
|
750
|
+
setClosing(true);
|
|
751
|
+
setMediaTransition({
|
|
752
|
+
from: sourceRect,
|
|
753
|
+
item: activeMedia,
|
|
754
|
+
phase: "closing",
|
|
755
|
+
to: targetRect
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
}, [activeMedia, closing, index, open]);
|
|
759
|
+
const handleMediaTransitionComplete = useCallback3(() => {
|
|
760
|
+
if (mediaTransition?.phase === "closing") {
|
|
761
|
+
setOpen(false);
|
|
762
|
+
setClosing(false);
|
|
763
|
+
}
|
|
764
|
+
setMediaTransition(null);
|
|
765
|
+
}, [mediaTransition?.phase]);
|
|
766
|
+
const handleOpenChange = useCallback3(
|
|
767
|
+
(nextOpen) => {
|
|
768
|
+
if (nextOpen) {
|
|
769
|
+
setClosing(false);
|
|
770
|
+
setOpen(true);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
startClose();
|
|
774
|
+
},
|
|
775
|
+
[startClose]
|
|
776
|
+
);
|
|
777
|
+
const value = useMemo2(
|
|
778
|
+
() => ({
|
|
779
|
+
classNames,
|
|
780
|
+
index,
|
|
781
|
+
media,
|
|
782
|
+
open,
|
|
783
|
+
openAtIndex,
|
|
784
|
+
registerThumbnail,
|
|
785
|
+
renderImage,
|
|
786
|
+
renderVideo,
|
|
787
|
+
setIndex,
|
|
788
|
+
sharedLayoutId,
|
|
789
|
+
sharedLayoutIdForIndex
|
|
790
|
+
}),
|
|
791
|
+
[
|
|
792
|
+
classNames,
|
|
793
|
+
index,
|
|
794
|
+
media,
|
|
795
|
+
open,
|
|
796
|
+
openAtIndex,
|
|
797
|
+
registerThumbnail,
|
|
798
|
+
renderImage,
|
|
799
|
+
renderVideo,
|
|
800
|
+
setIndex,
|
|
801
|
+
sharedLayoutId,
|
|
802
|
+
sharedLayoutIdForIndex
|
|
803
|
+
]
|
|
804
|
+
);
|
|
805
|
+
const hasNavigation = media.length > 1;
|
|
806
|
+
const navigateToIndex = useCallback3(
|
|
807
|
+
(nextIndex, direction) => {
|
|
808
|
+
setNavigationDirection(direction);
|
|
809
|
+
setLayoutSourceIndex(nextIndex);
|
|
810
|
+
setIndex(nextIndex);
|
|
811
|
+
},
|
|
812
|
+
[setIndex]
|
|
813
|
+
);
|
|
814
|
+
const goToPrevious = useCallback3(() => {
|
|
815
|
+
navigateToIndex((index - 1 + media.length) % media.length, -1);
|
|
816
|
+
}, [index, media.length, navigateToIndex]);
|
|
817
|
+
const goToNext = useCallback3(() => {
|
|
818
|
+
navigateToIndex((index + 1) % media.length, 1);
|
|
819
|
+
}, [index, media.length, navigateToIndex]);
|
|
820
|
+
const handleContentKeyDown = useCallback3(
|
|
821
|
+
(event) => {
|
|
822
|
+
if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || shouldIgnoreKeyboardNavigationTarget(event.target)) {
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
if (event.key === "ArrowLeft") {
|
|
826
|
+
event.preventDefault();
|
|
827
|
+
goToPrevious();
|
|
828
|
+
}
|
|
829
|
+
if (event.key === "ArrowRight") {
|
|
830
|
+
event.preventDefault();
|
|
831
|
+
goToNext();
|
|
832
|
+
}
|
|
833
|
+
},
|
|
834
|
+
[goToNext, goToPrevious]
|
|
835
|
+
);
|
|
836
|
+
return /* @__PURE__ */ jsx10(ApertoGroupContext.Provider, { value, children: /* @__PURE__ */ jsxs(
|
|
837
|
+
ApertoRoot,
|
|
838
|
+
{
|
|
839
|
+
dismissible,
|
|
840
|
+
motion: motionProp,
|
|
841
|
+
onOpenChange: handleOpenChange,
|
|
842
|
+
open,
|
|
843
|
+
children: [
|
|
844
|
+
children,
|
|
845
|
+
activeMedia ? /* @__PURE__ */ jsxs(ApertoPortal, { children: [
|
|
846
|
+
/* @__PURE__ */ jsx10(ApertoOverlay, { className: classNames?.overlay, fadeOut: closing }),
|
|
847
|
+
/* @__PURE__ */ jsxs(
|
|
848
|
+
ApertoContent,
|
|
849
|
+
{
|
|
850
|
+
className: classNames?.content,
|
|
851
|
+
"data-aperto-transition": mediaTransition?.phase,
|
|
852
|
+
onKeyDown: hasNavigation ? handleContentKeyDown : void 0,
|
|
853
|
+
sharedLayoutId: false,
|
|
854
|
+
...getDescriptionProps(activeMedia),
|
|
855
|
+
children: [
|
|
856
|
+
/* @__PURE__ */ jsxs("div", { "data-slot": "aperto-media", ref: setExpandedMediaNode, children: [
|
|
857
|
+
/* @__PURE__ */ jsx10(
|
|
858
|
+
ApertoExpandedMediaStage,
|
|
859
|
+
{
|
|
860
|
+
direction: navigationDirection,
|
|
861
|
+
index,
|
|
862
|
+
item: activeMedia,
|
|
863
|
+
navigationMotion,
|
|
864
|
+
renderImage,
|
|
865
|
+
renderVideo
|
|
866
|
+
}
|
|
867
|
+
),
|
|
868
|
+
hasNavigation ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
869
|
+
/* @__PURE__ */ jsx10(
|
|
870
|
+
"button",
|
|
871
|
+
{
|
|
872
|
+
"aria-label": "Previous media",
|
|
873
|
+
className: classNames?.previousButton,
|
|
874
|
+
"data-slot": "aperto-previous-button",
|
|
875
|
+
onClick: goToPrevious,
|
|
876
|
+
type: "button",
|
|
877
|
+
children: /* @__PURE__ */ jsx10(
|
|
878
|
+
ChevronLeft,
|
|
879
|
+
{
|
|
880
|
+
"aria-hidden": "true",
|
|
881
|
+
size: 18,
|
|
882
|
+
strokeWidth: 2
|
|
883
|
+
}
|
|
884
|
+
)
|
|
885
|
+
}
|
|
886
|
+
),
|
|
887
|
+
/* @__PURE__ */ jsx10(
|
|
888
|
+
"button",
|
|
889
|
+
{
|
|
890
|
+
"aria-label": "Next media",
|
|
891
|
+
className: classNames?.nextButton,
|
|
892
|
+
"data-slot": "aperto-next-button",
|
|
893
|
+
onClick: goToNext,
|
|
894
|
+
type: "button",
|
|
895
|
+
children: /* @__PURE__ */ jsx10(
|
|
896
|
+
ChevronRight,
|
|
897
|
+
{
|
|
898
|
+
"aria-hidden": "true",
|
|
899
|
+
size: 18,
|
|
900
|
+
strokeWidth: 2
|
|
901
|
+
}
|
|
902
|
+
)
|
|
903
|
+
}
|
|
904
|
+
)
|
|
905
|
+
] }) : null
|
|
906
|
+
] }),
|
|
907
|
+
/* @__PURE__ */ jsx10(ApertoTitle, { children: activeMedia.title ?? getMediaLabel(activeMedia) }),
|
|
908
|
+
activeMedia.description ? /* @__PURE__ */ jsx10(ApertoDescription, { children: activeMedia.description }) : null,
|
|
909
|
+
hasNavigation ? /* @__PURE__ */ jsxs("div", { className: classNames?.counter, "data-slot": "aperto-counter", children: [
|
|
910
|
+
index + 1,
|
|
911
|
+
" / ",
|
|
912
|
+
media.length
|
|
913
|
+
] }) : null,
|
|
914
|
+
/* @__PURE__ */ jsx10(
|
|
915
|
+
"button",
|
|
916
|
+
{
|
|
917
|
+
"aria-label": "Close",
|
|
918
|
+
className: classNames?.closeButton,
|
|
919
|
+
"data-slot": "aperto-close",
|
|
920
|
+
onClick: startClose,
|
|
921
|
+
type: "button",
|
|
922
|
+
children: /* @__PURE__ */ jsx10(X, { "aria-hidden": "true", size: 17, strokeWidth: 2 })
|
|
923
|
+
}
|
|
924
|
+
)
|
|
925
|
+
]
|
|
926
|
+
}
|
|
927
|
+
),
|
|
928
|
+
/* @__PURE__ */ jsx10(
|
|
929
|
+
ApertoMediaTransitionClone,
|
|
930
|
+
{
|
|
931
|
+
onComplete: handleMediaTransitionComplete,
|
|
932
|
+
transition: mediaTransition
|
|
933
|
+
}
|
|
934
|
+
)
|
|
935
|
+
] }) : null
|
|
936
|
+
]
|
|
937
|
+
}
|
|
938
|
+
) });
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// src/aperto-close.tsx
|
|
942
|
+
import * as Dialog7 from "@radix-ui/react-dialog";
|
|
943
|
+
import { X as X2 } from "lucide-react";
|
|
944
|
+
import { forwardRef as forwardRef5 } from "react";
|
|
945
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
946
|
+
var ApertoClose = forwardRef5(({ children, ...props }, ref) => {
|
|
947
|
+
return /* @__PURE__ */ jsx11(Dialog7.Close, { "data-slot": "aperto-close", ref, ...props, children: children ?? /* @__PURE__ */ jsx11(X2, { "aria-hidden": "true", size: 17, strokeWidth: 2 }) });
|
|
948
|
+
});
|
|
949
|
+
ApertoClose.displayName = "ApertoClose";
|
|
950
|
+
|
|
951
|
+
// src/aperto-trigger.tsx
|
|
952
|
+
import * as Dialog8 from "@radix-ui/react-dialog";
|
|
953
|
+
import { motion as motion5 } from "motion/react";
|
|
954
|
+
import {
|
|
955
|
+
forwardRef as forwardRef6,
|
|
956
|
+
useCallback as useCallback4,
|
|
957
|
+
useEffect as useEffect2,
|
|
958
|
+
useState as useState4
|
|
959
|
+
} from "react";
|
|
960
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
961
|
+
var TRIGGER_OPEN_Z_INDEX = 1e3;
|
|
962
|
+
var ApertoTrigger = forwardRef6(
|
|
963
|
+
({ active, children, motion: motionOverride, sharedLayoutId, ...props }, ref) => {
|
|
964
|
+
const ctx = useApertoContext();
|
|
965
|
+
const shouldRaise = ctx.open && (active ?? true);
|
|
966
|
+
const [zIndex, setZIndex] = useState4(
|
|
967
|
+
shouldRaise ? TRIGGER_OPEN_Z_INDEX : 0
|
|
968
|
+
);
|
|
969
|
+
useEffect2(() => {
|
|
970
|
+
if (shouldRaise) {
|
|
971
|
+
setZIndex(TRIGGER_OPEN_Z_INDEX);
|
|
972
|
+
} else if (ctx.open) {
|
|
973
|
+
setZIndex(0);
|
|
974
|
+
}
|
|
975
|
+
}, [ctx.open, shouldRaise]);
|
|
976
|
+
const handleLayoutAnimationComplete = useCallback4(() => {
|
|
977
|
+
if (!shouldRaise) {
|
|
978
|
+
setZIndex(0);
|
|
979
|
+
}
|
|
980
|
+
}, [shouldRaise]);
|
|
981
|
+
const resolved = resolvePreset(
|
|
982
|
+
"trigger",
|
|
983
|
+
motionOverride,
|
|
984
|
+
ctx.presetName,
|
|
985
|
+
ctx.variants,
|
|
986
|
+
ctx.reduceMotion
|
|
987
|
+
);
|
|
988
|
+
const layoutId = sharedLayoutId === false ? void 0 : sharedLayoutId ?? `${ctx.layoutId}-shared`;
|
|
989
|
+
return /* @__PURE__ */ jsx12(Dialog8.Trigger, { asChild: true, ref, ...props, children: /* @__PURE__ */ jsx12(
|
|
990
|
+
motion5.button,
|
|
991
|
+
{
|
|
992
|
+
"data-aperto-active": shouldRaise ? "" : void 0,
|
|
993
|
+
"data-slot": "aperto-trigger",
|
|
994
|
+
layout: true,
|
|
995
|
+
layoutCrossfade: false,
|
|
996
|
+
layoutId,
|
|
997
|
+
onLayoutAnimationComplete: handleLayoutAnimationComplete,
|
|
998
|
+
style: { zIndex },
|
|
999
|
+
transition: resolved.transition,
|
|
1000
|
+
type: "button",
|
|
1001
|
+
children
|
|
1002
|
+
},
|
|
1003
|
+
layoutId ?? "unshared"
|
|
1004
|
+
) });
|
|
1005
|
+
}
|
|
1006
|
+
);
|
|
1007
|
+
ApertoTrigger.displayName = "ApertoTrigger";
|
|
1008
|
+
|
|
1009
|
+
// src/Aperto/ApertoSingle.tsx
|
|
1010
|
+
import { jsx as jsx13, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1011
|
+
function ApertoSingle({
|
|
1012
|
+
classNames,
|
|
1013
|
+
dismissible,
|
|
1014
|
+
media,
|
|
1015
|
+
renderImage,
|
|
1016
|
+
renderVideo
|
|
1017
|
+
}) {
|
|
1018
|
+
const label = getMediaLabel(media);
|
|
1019
|
+
const title = media.title ?? label;
|
|
1020
|
+
return /* @__PURE__ */ jsxs2(ApertoRoot, { dismissible, children: [
|
|
1021
|
+
/* @__PURE__ */ jsx13(
|
|
1022
|
+
ApertoTrigger,
|
|
1023
|
+
{
|
|
1024
|
+
"aria-label": `Open ${label}`,
|
|
1025
|
+
className: classNames?.thumbnail,
|
|
1026
|
+
children: renderThumbnail(media, renderImage, renderVideo)
|
|
1027
|
+
}
|
|
1028
|
+
),
|
|
1029
|
+
/* @__PURE__ */ jsxs2(ApertoPortal, { children: [
|
|
1030
|
+
/* @__PURE__ */ jsx13(ApertoOverlay, { className: classNames?.overlay }),
|
|
1031
|
+
/* @__PURE__ */ jsxs2(
|
|
1032
|
+
ApertoContent,
|
|
1033
|
+
{
|
|
1034
|
+
className: classNames?.content,
|
|
1035
|
+
...getDescriptionProps(media),
|
|
1036
|
+
children: [
|
|
1037
|
+
renderExpandedMedia(media, renderImage, renderVideo),
|
|
1038
|
+
/* @__PURE__ */ jsx13(ApertoTitle, { children: title }),
|
|
1039
|
+
media.description ? /* @__PURE__ */ jsx13(ApertoDescription, { children: media.description }) : null,
|
|
1040
|
+
/* @__PURE__ */ jsx13(
|
|
1041
|
+
ApertoClose,
|
|
1042
|
+
{
|
|
1043
|
+
"aria-label": "Close",
|
|
1044
|
+
className: classNames?.closeButton,
|
|
1045
|
+
type: "button"
|
|
1046
|
+
}
|
|
1047
|
+
)
|
|
1048
|
+
]
|
|
1049
|
+
}
|
|
1050
|
+
)
|
|
1051
|
+
] })
|
|
1052
|
+
] });
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// src/Aperto/ApertoThumbnail.tsx
|
|
1056
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1057
|
+
function ApertoThumbnail({
|
|
1058
|
+
children,
|
|
1059
|
+
className,
|
|
1060
|
+
index
|
|
1061
|
+
}) {
|
|
1062
|
+
const group = useApertoGroup();
|
|
1063
|
+
const media = group.media[index];
|
|
1064
|
+
if (!media) {
|
|
1065
|
+
return null;
|
|
1066
|
+
}
|
|
1067
|
+
return /* @__PURE__ */ jsx14(
|
|
1068
|
+
ApertoTrigger,
|
|
1069
|
+
{
|
|
1070
|
+
active: group.index === index,
|
|
1071
|
+
"aria-label": `Open ${getMediaLabel(media)}`,
|
|
1072
|
+
className: className ?? group.classNames?.thumbnail,
|
|
1073
|
+
onClick: () => group.openAtIndex(index),
|
|
1074
|
+
ref: (node) => group.registerThumbnail(index, node),
|
|
1075
|
+
sharedLayoutId: false,
|
|
1076
|
+
children: children ?? renderThumbnail(media, group.renderImage, group.renderVideo)
|
|
1077
|
+
}
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// src/index.ts
|
|
1082
|
+
var ApertoPrimitive = {
|
|
1083
|
+
Close: ApertoClose,
|
|
1084
|
+
Content: ApertoContent,
|
|
1085
|
+
Description: ApertoDescription,
|
|
1086
|
+
Overlay: ApertoOverlay,
|
|
1087
|
+
Portal: ApertoPortal,
|
|
1088
|
+
Root: ApertoRoot,
|
|
1089
|
+
Title: ApertoTitle,
|
|
1090
|
+
Trigger: ApertoTrigger
|
|
1091
|
+
};
|
|
1092
|
+
var Aperto = Object.assign(ApertoSingle, {
|
|
1093
|
+
Group: ApertoGroup,
|
|
1094
|
+
Primitive: ApertoPrimitive,
|
|
1095
|
+
Thumbnail: ApertoThumbnail
|
|
1096
|
+
});
|
|
1097
|
+
export {
|
|
1098
|
+
Aperto,
|
|
1099
|
+
ApertoClose,
|
|
1100
|
+
ApertoContent,
|
|
1101
|
+
ApertoDescription,
|
|
1102
|
+
ApertoGroup,
|
|
1103
|
+
ApertoOverlay,
|
|
1104
|
+
ApertoPortal,
|
|
1105
|
+
ApertoPrimitive,
|
|
1106
|
+
ApertoRoot,
|
|
1107
|
+
ApertoThumbnail,
|
|
1108
|
+
ApertoTitle,
|
|
1109
|
+
ApertoTrigger,
|
|
1110
|
+
PRESETS,
|
|
1111
|
+
resolvePreset
|
|
1112
|
+
};
|
|
1113
|
+
//# sourceMappingURL=index.js.map
|