@patternmode/aperto 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,6 +35,40 @@ export function MediaGroupExample() {
35
35
  }
36
36
  ```
37
37
 
38
+ ## Optimized images
39
+
40
+ Aperto's built-in image renderer uses a plain `img` so the package stays
41
+ framework agnostic. If your app uses Next.js, pass `renderImage` and return
42
+ your own `next/image` `Image` component:
43
+
44
+ ```tsx
45
+ import Image from "next/image";
46
+
47
+ const leadSrc = media[0]?.src;
48
+
49
+ <Aperto.Group
50
+ media={media}
51
+ renderImage={({ alt, item, src, variant }) => {
52
+ const isLeadImage = variant === "expanded" || item.src === leadSrc;
53
+
54
+ return (
55
+ <Image
56
+ alt={alt ?? ""}
57
+ fetchPriority={isLeadImage ? "high" : "auto"}
58
+ fill
59
+ loading={isLeadImage || variant === "thumbnail" ? "eager" : "lazy"}
60
+ sizes={variant === "thumbnail" ? "(max-width: 640px) 50vw, 320px" : "90vw"}
61
+ src={String(src)}
62
+ />
63
+ );
64
+ }}
65
+ >
66
+ {media.map((item, index) => (
67
+ <Aperto.Thumbnail key={item.id ?? item.src} index={index} />
68
+ ))}
69
+ </Aperto.Group>;
70
+ ```
71
+
38
72
  ## Install
39
73
 
40
74
  ```bash
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
1
2
  import * as react from 'react';
2
- import { ImgHTMLAttributes, ReactNode, VideoHTMLAttributes, ComponentPropsWithoutRef } from 'react';
3
+ import { ImgHTMLAttributes, ReactNode, VideoHTMLAttributes, ComponentPropsWithRef, Ref, CSSProperties, ComponentPropsWithoutRef } from 'react';
3
4
  import * as Dialog from '@radix-ui/react-dialog';
4
- import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { Transition } from 'motion/react';
6
6
 
7
7
  /**
8
- * @patternmode/aperto
8
+ * Package: @patternmode/aperto
9
9
  *
10
10
  * Shared element transitions with physics-based drag dismissal.
11
11
  * Built on Radix Dialog + Motion.
@@ -23,22 +23,42 @@ interface BaseMediaItem {
23
23
  title?: string;
24
24
  width?: number;
25
25
  }
26
+ /** Image media item rendered as a thumbnail and expanded image. */
26
27
  interface ApertoImageItem extends BaseMediaItem {
28
+ /** Required alt text for the image renderer. */
27
29
  alt: string;
28
30
  type: "image";
29
31
  }
32
+ /** Video media item rendered with a thumbnail and expanded player. */
30
33
  interface ApertoVideoItem extends BaseMediaItem {
31
34
  alt?: string;
35
+ /** Optional captions track URL for the expanded video. */
32
36
  captionsSrc?: string;
33
37
  poster?: string;
38
+ /** Required thumbnail image shown before expansion. */
34
39
  thumbnailSrc: string;
35
40
  type: "video";
36
41
  }
42
+ /** Media item supported by Aperto's grouped media API. */
37
43
  type ApertoMediaItem = ApertoImageItem | ApertoVideoItem;
44
+ /**
45
+ * Custom image renderer for image media.
46
+ *
47
+ * Aperto's default renderer is a plain `img` so the package stays framework
48
+ * agnostic. Next.js consumers should return their own `next/image` `Image`
49
+ * component here, using `variant` to choose thumbnail vs expanded sizing,
50
+ * loading, or fetch priority behavior.
51
+ */
38
52
  type RenderImage = (props: ImgHTMLAttributes<HTMLImageElement> & {
39
53
  item: ApertoImageItem;
40
54
  variant: "expanded" | "thumbnail";
41
55
  }) => ReactNode;
56
+ /**
57
+ * Custom video renderer for video media.
58
+ *
59
+ * Use this to replace the default `video` element while preserving the supplied
60
+ * accessibility, poster, and control props.
61
+ */
42
62
  type RenderVideo = (props: VideoHTMLAttributes<HTMLVideoElement> & {
43
63
  item: ApertoVideoItem;
44
64
  variant: "expanded" | "thumbnail";
@@ -77,50 +97,98 @@ interface MotionPreset {
77
97
  }
78
98
  /** Dismissal behaviour config */
79
99
  interface DismissibleConfig {
80
- /** Distance in px to trigger dismissal (default: 100) */
100
+ /**
101
+ * Distance in px to trigger dismissal.
102
+ *
103
+ * Default `100`.
104
+ */
81
105
  threshold?: number;
82
- /** Velocity in px/s to trigger dismissal (default: 500) */
106
+ /**
107
+ * Velocity in px/s to trigger dismissal.
108
+ *
109
+ * Default `500`.
110
+ */
83
111
  velocity?: number;
84
112
  }
85
113
 
86
114
  interface ApertoProps {
87
115
  classNames?: ApertoClassNames;
88
- /** Whether dragging can dismiss the expanded media (default: true). */
116
+ /**
117
+ * Whether dragging can dismiss the expanded media.
118
+ *
119
+ * Default `true`.
120
+ */
89
121
  dismissible?: boolean | DismissibleConfig;
122
+ /** Single media item rendered by the standalone Aperto component. */
90
123
  media: ApertoMediaItem;
124
+ /**
125
+ * Overrides Aperto's plain `img` renderer for image media.
126
+ *
127
+ * Use this to render framework-specific image components such as Next.js
128
+ * `Image` while keeping Aperto itself framework agnostic.
129
+ */
91
130
  renderImage?: RenderImage;
92
131
  renderVideo?: RenderVideo;
93
132
  }
94
133
  interface ApertoGroupProps {
95
134
  children: ReactNode;
96
135
  classNames?: ApertoClassNames;
97
- /** Whether dragging can dismiss the expanded media (default: true). */
136
+ /**
137
+ * Whether dragging can dismiss the expanded media.
138
+ *
139
+ * Default `true`.
140
+ */
98
141
  dismissible?: boolean | DismissibleConfig;
142
+ /** Controlled active media index. Pair with `onIndexChange`. */
99
143
  index?: number;
144
+ /**
145
+ * Initial active media index for uncontrolled groups.
146
+ *
147
+ * Default `0`.
148
+ */
100
149
  initialIndex?: number;
101
150
  media: ApertoMediaItem[];
102
- /** Motion preset for open/close transitions */
151
+ /**
152
+ * Motion preset for open/close transitions, or per-part motion overrides.
153
+ *
154
+ * Default `"smooth"`.
155
+ */
103
156
  motion?: MotionPresetName | MotionVariants;
157
+ /**
158
+ * Motion preset for next/previous navigation inside expanded media.
159
+ *
160
+ * Default `"glide"`.
161
+ */
104
162
  navigationMotion?: NavigationMotionPresetName;
163
+ /** Called whenever grouped media navigation changes the active index. */
105
164
  onIndexChange?: (index: number) => void;
165
+ /**
166
+ * Overrides Aperto's plain `img` renderer for image media.
167
+ *
168
+ * Use this to render framework-specific image components such as Next.js
169
+ * `Image` while keeping Aperto itself framework agnostic.
170
+ */
106
171
  renderImage?: RenderImage;
107
172
  renderVideo?: RenderVideo;
108
173
  }
109
174
  interface ApertoThumbnailProps {
175
+ /** Optional custom thumbnail content; defaults to media thumbnail rendering. */
110
176
  children?: ReactNode;
111
177
  className?: string;
178
+ /** Index into the parent group's `media` array. */
112
179
  index: number;
113
180
  }
114
181
 
115
- declare function ApertoGroup({ children, classNames, dismissible, index: controlledIndex, initialIndex, media, motion: motionProp, navigationMotion, onIndexChange, renderImage, renderVideo, }: ApertoGroupProps): react_jsx_runtime.JSX.Element;
116
-
117
- declare function ApertoSingle({ classNames, dismissible, media, renderImage, renderVideo, }: ApertoProps): react_jsx_runtime.JSX.Element;
182
+ declare const ApertoGroup: ({ children, classNames, dismissible, index: controlledIndex, initialIndex, media, motion: motionProp, navigationMotion, onIndexChange, renderImage, renderVideo, }: ApertoGroupProps) => react_jsx_runtime.JSX.Element;
118
183
 
119
- declare function ApertoThumbnail({ children, className, index, }: ApertoThumbnailProps): react_jsx_runtime.JSX.Element | null;
184
+ declare const ApertoThumbnail: ({ children, className, index }: ApertoThumbnailProps) => react_jsx_runtime.JSX.Element | null;
120
185
 
121
- declare const ApertoClose: react.ForwardRefExoticComponent<Omit<Dialog.DialogCloseProps & react.RefAttributes<HTMLButtonElement>, "ref"> & react.RefAttributes<HTMLButtonElement>>;
186
+ declare const ApertoClose: {
187
+ ({ children, ref, ...props }: ComponentPropsWithRef<typeof Dialog.Close>): react_jsx_runtime.JSX.Element;
188
+ displayName: string;
189
+ };
122
190
 
123
- interface ApertoContentProps extends Omit<ComponentPropsWithoutRef<typeof Dialog.Content>, "asChild" | "forceMount"> {
191
+ interface ApertoContentProps extends Omit<ComponentPropsWithRef<typeof Dialog.Content>, "asChild" | "forceMount"> {
124
192
  /** Motion preset override for this content panel, independent of the root preset. */
125
193
  motion?: MotionPresetName;
126
194
  /** Built-in positioning strategy. Use "none" for custom primitive compositions. */
@@ -128,24 +196,34 @@ interface ApertoContentProps extends Omit<ComponentPropsWithoutRef<typeof Dialog
128
196
  /** Internal shared layout ID override for grouped media. */
129
197
  sharedLayoutId?: string | false;
130
198
  }
131
- declare const ApertoContent: react.ForwardRefExoticComponent<ApertoContentProps & react.RefAttributes<HTMLDivElement>>;
199
+ declare const ApertoContent: {
200
+ ({ children, className, motion: motionOverride, placement, ref, sharedLayoutId, style, ...props }: ApertoContentProps): react_jsx_runtime.JSX.Element;
201
+ displayName: string;
202
+ };
132
203
 
133
- declare const ApertoDescription: react.ForwardRefExoticComponent<Omit<Dialog.DialogDescriptionProps & react.RefAttributes<HTMLParagraphElement>, "ref"> & react.RefAttributes<HTMLParagraphElement>>;
204
+ declare const ApertoDescription: {
205
+ ({ ref, ...props }: ComponentPropsWithRef<typeof Dialog.Description>): react_jsx_runtime.JSX.Element;
206
+ displayName: string;
207
+ };
134
208
 
135
209
  interface ApertoOverlayProps {
136
210
  className?: string;
137
211
  /** Internal flag used to fade the overlay during measured close transitions. */
138
212
  fadeOut?: boolean;
139
- style?: React.CSSProperties;
213
+ ref?: Ref<HTMLDivElement>;
214
+ style?: CSSProperties;
140
215
  }
141
- declare const ApertoOverlay: react.ForwardRefExoticComponent<ApertoOverlayProps & react.RefAttributes<HTMLDivElement>>;
216
+ declare const ApertoOverlay: {
217
+ ({ className, fadeOut, ref, style }: ApertoOverlayProps): react_jsx_runtime.JSX.Element;
218
+ displayName: string;
219
+ };
142
220
 
143
221
  interface ApertoPortalProps {
144
222
  children: ReactNode;
145
223
  /** Container element for the portal (default: document.body) */
146
224
  container?: HTMLElement;
147
225
  }
148
- declare function ApertoPortal({ children, container }: ApertoPortalProps): react_jsx_runtime.JSX.Element;
226
+ declare const ApertoPortal: ({ children, container }: ApertoPortalProps) => react_jsx_runtime.JSX.Element;
149
227
 
150
228
  interface ApertoRootProps extends Omit<ComponentPropsWithoutRef<typeof Dialog.Root>, "open" | "onOpenChange"> {
151
229
  children: ReactNode;
@@ -162,11 +240,14 @@ interface ApertoRootProps extends Omit<ComponentPropsWithoutRef<typeof Dialog.Ro
162
240
  /** Force reduced motion regardless of system preference */
163
241
  reduceMotion?: boolean;
164
242
  }
165
- declare function ApertoRoot({ children, dismissible, layoutId: layoutIdProp, motion: motionProp, open: controlledOpen, onOpenChange: controlledOnOpenChange, reduceMotion: reduceMotionProp, ...dialogProps }: ApertoRootProps): react_jsx_runtime.JSX.Element;
243
+ declare const ApertoRoot: ({ children, dismissible, layoutId: layoutIdProp, motion: motionProp, open: controlledOpen, onOpenChange: controlledOnOpenChange, reduceMotion: reduceMotionProp, ...dialogProps }: ApertoRootProps) => react_jsx_runtime.JSX.Element;
166
244
 
167
- declare const ApertoTitle: react.ForwardRefExoticComponent<Omit<Dialog.DialogTitleProps & react.RefAttributes<HTMLHeadingElement>, "ref"> & react.RefAttributes<HTMLHeadingElement>>;
245
+ declare const ApertoTitle: {
246
+ ({ ref, ...props }: ComponentPropsWithRef<typeof Dialog.Title>): react_jsx_runtime.JSX.Element;
247
+ displayName: string;
248
+ };
168
249
 
169
- interface ApertoTriggerProps extends ComponentPropsWithoutRef<typeof Dialog.Trigger> {
250
+ interface ApertoTriggerProps extends ComponentPropsWithRef<typeof Dialog.Trigger> {
170
251
  /** Internal flag used by grouped thumbnails to raise only the active trigger. */
171
252
  active?: boolean;
172
253
  /** Override motion preset for this trigger */
@@ -174,7 +255,10 @@ interface ApertoTriggerProps extends ComponentPropsWithoutRef<typeof Dialog.Trig
174
255
  /** Internal shared layout ID override for grouped thumbnails. */
175
256
  sharedLayoutId?: string | false;
176
257
  }
177
- declare const ApertoTrigger: react.ForwardRefExoticComponent<ApertoTriggerProps & react.RefAttributes<HTMLButtonElement>>;
258
+ declare const ApertoTrigger: {
259
+ ({ active, children, motion: motionOverride, ref, sharedLayoutId, ...props }: ApertoTriggerProps): react_jsx_runtime.JSX.Element;
260
+ displayName: string;
261
+ };
178
262
 
179
263
  /**
180
264
  * Motion presets — each bundles transition timing AND drag physics
@@ -193,33 +277,69 @@ type ComponentType = "trigger" | "content" | "backdrop";
193
277
  *
194
278
  * If `prefers-reduced-motion` is active, always returns "reduced".
195
279
  */
196
- declare function resolvePreset(componentType: ComponentType, componentMotion: MotionPresetName | undefined, globalPreset: MotionPresetName, variants: MotionVariants | undefined, reduceMotion: boolean): MotionPreset;
280
+ declare const resolvePreset: (componentType: ComponentType, componentMotion: MotionPresetName | undefined, globalPreset: MotionPresetName, variants: MotionVariants | undefined, reduceMotion: boolean) => MotionPreset;
197
281
 
198
282
  /** Lower-level compound component namespace for advanced composition. */
199
283
  declare const ApertoPrimitive: {
200
- Close: react.ForwardRefExoticComponent<Omit<Dialog.DialogCloseProps & react.RefAttributes<HTMLButtonElement>, "ref"> & react.RefAttributes<HTMLButtonElement>>;
201
- Content: react.ForwardRefExoticComponent<ApertoContentProps & react.RefAttributes<HTMLDivElement>>;
202
- Description: react.ForwardRefExoticComponent<Omit<Dialog.DialogDescriptionProps & react.RefAttributes<HTMLParagraphElement>, "ref"> & react.RefAttributes<HTMLParagraphElement>>;
203
- Overlay: react.ForwardRefExoticComponent<ApertoOverlayProps & react.RefAttributes<HTMLDivElement>>;
204
- Portal: typeof ApertoPortal;
205
- Root: typeof ApertoRoot;
206
- Title: react.ForwardRefExoticComponent<Omit<Dialog.DialogTitleProps & react.RefAttributes<HTMLHeadingElement>, "ref"> & react.RefAttributes<HTMLHeadingElement>>;
207
- Trigger: react.ForwardRefExoticComponent<ApertoTriggerProps & react.RefAttributes<HTMLButtonElement>>;
284
+ Close: {
285
+ ({ children, ref, ...props }: react.ComponentPropsWithRef<react.ForwardRefExoticComponent<Dialog.DialogCloseProps & react.RefAttributes<HTMLButtonElement>>>): react_jsx_runtime.JSX.Element;
286
+ displayName: string;
287
+ };
288
+ Content: {
289
+ ({ children, className, motion: motionOverride, placement, ref, sharedLayoutId, style, ...props }: ApertoContentProps): react_jsx_runtime.JSX.Element;
290
+ displayName: string;
291
+ };
292
+ Description: {
293
+ ({ ref, ...props }: react.ComponentPropsWithRef<react.ForwardRefExoticComponent<Dialog.DialogDescriptionProps & react.RefAttributes<HTMLParagraphElement>>>): react_jsx_runtime.JSX.Element;
294
+ displayName: string;
295
+ };
296
+ Overlay: {
297
+ ({ className, fadeOut, ref, style }: ApertoOverlayProps): react_jsx_runtime.JSX.Element;
298
+ displayName: string;
299
+ };
300
+ Portal: ({ children, container }: ApertoPortalProps) => react_jsx_runtime.JSX.Element;
301
+ Root: ({ children, dismissible, layoutId: layoutIdProp, motion: motionProp, open: controlledOpen, onOpenChange: controlledOnOpenChange, reduceMotion: reduceMotionProp, ...dialogProps }: ApertoRootProps) => react_jsx_runtime.JSX.Element;
302
+ Title: {
303
+ ({ ref, ...props }: react.ComponentPropsWithRef<react.ForwardRefExoticComponent<Dialog.DialogTitleProps & react.RefAttributes<HTMLHeadingElement>>>): react_jsx_runtime.JSX.Element;
304
+ displayName: string;
305
+ };
306
+ Trigger: {
307
+ ({ active, children, motion: motionOverride, ref, sharedLayoutId, ...props }: ApertoTriggerProps): react_jsx_runtime.JSX.Element;
308
+ displayName: string;
309
+ };
208
310
  };
209
311
  /** Primary media-first component namespace. */
210
- declare const Aperto: typeof ApertoSingle & {
211
- Group: typeof ApertoGroup;
312
+ declare const Aperto: (({ classNames, dismissible, media, renderImage, renderVideo, }: ApertoProps) => react_jsx_runtime.JSX.Element) & {
313
+ Group: ({ children, classNames, dismissible, index: controlledIndex, initialIndex, media, motion: motionProp, navigationMotion, onIndexChange, renderImage, renderVideo, }: ApertoGroupProps) => react_jsx_runtime.JSX.Element;
212
314
  Primitive: {
213
- Close: react.ForwardRefExoticComponent<Omit<Dialog.DialogCloseProps & react.RefAttributes<HTMLButtonElement>, "ref"> & react.RefAttributes<HTMLButtonElement>>;
214
- Content: react.ForwardRefExoticComponent<ApertoContentProps & react.RefAttributes<HTMLDivElement>>;
215
- Description: react.ForwardRefExoticComponent<Omit<Dialog.DialogDescriptionProps & react.RefAttributes<HTMLParagraphElement>, "ref"> & react.RefAttributes<HTMLParagraphElement>>;
216
- Overlay: react.ForwardRefExoticComponent<ApertoOverlayProps & react.RefAttributes<HTMLDivElement>>;
217
- Portal: typeof ApertoPortal;
218
- Root: typeof ApertoRoot;
219
- Title: react.ForwardRefExoticComponent<Omit<Dialog.DialogTitleProps & react.RefAttributes<HTMLHeadingElement>, "ref"> & react.RefAttributes<HTMLHeadingElement>>;
220
- Trigger: react.ForwardRefExoticComponent<ApertoTriggerProps & react.RefAttributes<HTMLButtonElement>>;
315
+ Close: {
316
+ ({ children, ref, ...props }: react.ComponentPropsWithRef<react.ForwardRefExoticComponent<Dialog.DialogCloseProps & react.RefAttributes<HTMLButtonElement>>>): react_jsx_runtime.JSX.Element;
317
+ displayName: string;
318
+ };
319
+ Content: {
320
+ ({ children, className, motion: motionOverride, placement, ref, sharedLayoutId, style, ...props }: ApertoContentProps): react_jsx_runtime.JSX.Element;
321
+ displayName: string;
322
+ };
323
+ Description: {
324
+ ({ ref, ...props }: react.ComponentPropsWithRef<react.ForwardRefExoticComponent<Dialog.DialogDescriptionProps & react.RefAttributes<HTMLParagraphElement>>>): react_jsx_runtime.JSX.Element;
325
+ displayName: string;
326
+ };
327
+ Overlay: {
328
+ ({ className, fadeOut, ref, style }: ApertoOverlayProps): react_jsx_runtime.JSX.Element;
329
+ displayName: string;
330
+ };
331
+ Portal: ({ children, container }: ApertoPortalProps) => react_jsx_runtime.JSX.Element;
332
+ Root: ({ children, dismissible, layoutId: layoutIdProp, motion: motionProp, open: controlledOpen, onOpenChange: controlledOnOpenChange, reduceMotion: reduceMotionProp, ...dialogProps }: ApertoRootProps) => react_jsx_runtime.JSX.Element;
333
+ Title: {
334
+ ({ ref, ...props }: react.ComponentPropsWithRef<react.ForwardRefExoticComponent<Dialog.DialogTitleProps & react.RefAttributes<HTMLHeadingElement>>>): react_jsx_runtime.JSX.Element;
335
+ displayName: string;
336
+ };
337
+ Trigger: {
338
+ ({ active, children, motion: motionOverride, ref, sharedLayoutId, ...props }: ApertoTriggerProps): react_jsx_runtime.JSX.Element;
339
+ displayName: string;
340
+ };
221
341
  };
222
- Thumbnail: typeof ApertoThumbnail;
342
+ Thumbnail: ({ children, className, index }: ApertoThumbnailProps) => react_jsx_runtime.JSX.Element | null;
223
343
  };
224
344
 
225
345
  export { Aperto, type ApertoClassNames, ApertoClose, ApertoContent, type ApertoContentProps, ApertoDescription, ApertoGroup, type ApertoGroupProps, type ApertoImageItem, type ApertoMediaItem, ApertoOverlay, type ApertoOverlayProps, ApertoPortal, type ApertoPortalProps, ApertoPrimitive, type ApertoProps, ApertoRoot, type ApertoRootProps, ApertoThumbnail, type ApertoThumbnailProps, ApertoTitle, ApertoTrigger, type ApertoTriggerProps, type ApertoVideoItem, type DismissibleConfig, type DragSpringConfig, type MotionPreset, type MotionPresetName, type MotionProp, type MotionVariants, type NavigationMotionPresetName, PRESETS, type RenderImage, type RenderVideo, resolvePreset };