react-motion-gallery 2.0.77 → 2.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/README.md +1129 -776
  2. package/dist/FullscreenRuntime-N5ZHMS3K.mjs +4 -0
  3. package/dist/FullscreenRuntime-YZWI2HL2.css +1 -0
  4. package/dist/chunk-2AHQQNSJ.mjs +1 -0
  5. package/dist/chunk-2AM3S2QN.mjs +1 -0
  6. package/dist/{chunk-Y33KEQVI.mjs → chunk-3HVF63UG.mjs} +1 -1
  7. package/dist/chunk-3XLFID7Y.mjs +1 -0
  8. package/dist/{chunk-6G2EJHW6.mjs → chunk-3YNFTUKE.mjs} +1 -1
  9. package/dist/chunk-6POQO2IQ.mjs +1 -0
  10. package/dist/chunk-6W5FJX7R.mjs +1 -0
  11. package/dist/chunk-7QDG32H7.mjs +4 -0
  12. package/dist/chunk-A2YEDYJQ.mjs +1 -0
  13. package/dist/chunk-AEGJQGLQ.mjs +6 -0
  14. package/dist/chunk-AO4U6D24.mjs +1 -0
  15. package/dist/chunk-AZKAPHY5.mjs +1 -0
  16. package/dist/{chunk-7CUGPJMX.mjs → chunk-B73WDXEU.mjs} +1 -1
  17. package/dist/chunk-BNMFSBHJ.mjs +1 -0
  18. package/dist/chunk-C2BBODLN.mjs +1 -0
  19. package/dist/chunk-COVYZ7TI.mjs +1 -0
  20. package/dist/chunk-D2ERSLYS.mjs +1 -0
  21. package/dist/chunk-DCCJH5R3.mjs +1 -0
  22. package/dist/chunk-DECESBPG.mjs +1 -0
  23. package/dist/chunk-G2MIO7SA.mjs +2 -0
  24. package/dist/chunk-GKN6CVVQ.mjs +3 -0
  25. package/dist/{chunk-B3L6FFUP.mjs → chunk-GKR35A24.mjs} +1 -1
  26. package/dist/{chunk-IFU7FJFX.mjs → chunk-GWYW2V72.mjs} +1 -1
  27. package/dist/chunk-H3ALL5BC.mjs +1 -0
  28. package/dist/{chunk-7G5QBCV3.mjs → chunk-HVO7H4XB.mjs} +1 -1
  29. package/dist/chunk-JIVW5KRY.mjs +3 -0
  30. package/dist/chunk-L4NJDMCU.mjs +1 -0
  31. package/dist/{chunk-YTIARAWT.mjs → chunk-LZ53EONL.mjs} +1 -1
  32. package/dist/{chunk-JYIGG2JK.mjs → chunk-NE64XTCU.mjs} +1 -1
  33. package/dist/chunk-NJBA4372.mjs +1 -0
  34. package/dist/chunk-ODBPQRVS.mjs +1 -0
  35. package/dist/chunk-P55ODH2L.mjs +4 -0
  36. package/dist/chunk-PUQSKJHB.mjs +4 -0
  37. package/dist/{chunk-UBCYWKG3.mjs → chunk-Q67WUAF3.mjs} +2 -2
  38. package/dist/{chunk-PAX6EZMX.mjs → chunk-RA7HIQFJ.mjs} +1 -1
  39. package/dist/chunk-RD4L57DG.mjs +1 -0
  40. package/dist/chunk-ROSKTQWL.mjs +1 -0
  41. package/dist/chunk-SPCWUWPZ.mjs +1 -0
  42. package/dist/chunk-VKROQSDU.mjs +1 -0
  43. package/dist/chunk-WJKG7QRD.mjs +1 -0
  44. package/dist/chunk-X5T5MXVQ.mjs +1 -0
  45. package/dist/chunk-ZCVS5WHL.mjs +1 -0
  46. package/dist/core.d.mts +4 -4
  47. package/dist/dataPlugins-DzaWlM6f.d.mts +170 -0
  48. package/dist/entries-cache.css +1 -1
  49. package/dist/entries-cache.d.mts +22 -16
  50. package/dist/entries-cache.mjs +1 -1
  51. package/dist/entries-infinite-scroll.d.mts +19 -0
  52. package/dist/entries-infinite-scroll.mjs +1 -0
  53. package/dist/entries-load-more.d.mts +35 -0
  54. package/dist/entries-load-more.mjs +1 -0
  55. package/dist/entries-pagination.d.mts +34 -0
  56. package/dist/entries-pagination.mjs +1 -0
  57. package/dist/entries-ready.d.mts +25 -0
  58. package/dist/entries-ready.mjs +1 -0
  59. package/dist/entries-virtualization.d.mts +19 -0
  60. package/dist/entries-virtualization.mjs +1 -0
  61. package/dist/entries.css +1 -1
  62. package/dist/entries.d.mts +29 -16
  63. package/dist/entries.mjs +1 -1
  64. package/dist/fullscreen-captions.css +1 -1
  65. package/dist/fullscreen-captions.d.mts +4 -4
  66. package/dist/fullscreen-captions.mjs +1 -1
  67. package/dist/fullscreen-controls.d.mts +4 -4
  68. package/dist/fullscreen-crossfade.d.mts +4 -4
  69. package/dist/fullscreen-lazy-load.css +1 -1
  70. package/dist/fullscreen-lazy-load.d.mts +4 -4
  71. package/dist/fullscreen-lazy-load.mjs +1 -1
  72. package/dist/fullscreen-slider.css +1 -1
  73. package/dist/fullscreen-slider.d.mts +4 -4
  74. package/dist/fullscreen-slider.mjs +1 -1
  75. package/dist/fullscreen-thumbnails.d.mts +4 -4
  76. package/dist/fullscreen-video.css +1 -1
  77. package/dist/fullscreen-video.d.mts +4 -4
  78. package/dist/fullscreen-video.mjs +1 -1
  79. package/dist/fullscreen-zoom-pan.d.mts +4 -4
  80. package/dist/fullscreen-zoom-pan.mjs +1 -1
  81. package/dist/fullscreen.css +1 -1
  82. package/dist/fullscreen.d.mts +5 -5
  83. package/dist/fullscreen.mjs +1 -1
  84. package/dist/fullscreenThumbnails.css +1 -1
  85. package/dist/fullscreenThumbnails.d.mts +4 -4
  86. package/dist/fullscreenThumbnails.mjs +1 -1
  87. package/dist/grid-fullscreen.d.mts +6 -1
  88. package/dist/grid-fullscreen.mjs +1 -1
  89. package/dist/grid-infinite-scroll.d.mts +15 -0
  90. package/dist/grid-infinite-scroll.mjs +1 -0
  91. package/dist/grid-lazy-load.css +1 -1
  92. package/dist/grid-lazy-load.d.mts +5 -1
  93. package/dist/grid-lazy-load.mjs +1 -1
  94. package/dist/grid-load-more.d.mts +15 -0
  95. package/dist/grid-load-more.mjs +1 -0
  96. package/dist/grid-pagination.d.mts +28 -0
  97. package/dist/grid-pagination.mjs +1 -0
  98. package/dist/grid-ready.d.mts +5 -1
  99. package/dist/grid-virtualization.d.mts +15 -0
  100. package/dist/grid-virtualization.mjs +1 -0
  101. package/dist/grid.css +1 -1
  102. package/dist/grid.d.mts +29 -5
  103. package/dist/grid.mjs +1 -1
  104. package/dist/{index-C-bgM_aR.d.mts → index-CCmwUMAS.d.mts} +7 -4
  105. package/dist/index.css +1 -1
  106. package/dist/index.d.mts +23 -12
  107. package/dist/index.mjs +1 -1
  108. package/dist/{masonry-BHq_xi-F.d.mts → masonry-BOnLW8R5.d.mts} +6 -2
  109. package/dist/masonry-fullscreen.d.mts +1 -1
  110. package/dist/masonry-fullscreen.mjs +1 -1
  111. package/dist/masonry-infinite-scroll.d.mts +17 -0
  112. package/dist/masonry-infinite-scroll.mjs +1 -0
  113. package/dist/masonry-lazy-load.css +1 -1
  114. package/dist/masonry-lazy-load.d.mts +6 -2
  115. package/dist/masonry-lazy-load.mjs +1 -1
  116. package/dist/masonry-load-more.d.mts +17 -0
  117. package/dist/masonry-load-more.mjs +1 -0
  118. package/dist/masonry-measured-ready.d.mts +5 -1
  119. package/dist/masonry-measured-ready.mjs +1 -1
  120. package/dist/masonry-measured.css +1 -1
  121. package/dist/masonry-measured.d.mts +12 -2
  122. package/dist/masonry-measured.mjs +1 -1
  123. package/dist/{masonry-C7eQ6whX.d.mts → masonry-miqLvs3X.d.mts} +12 -5
  124. package/dist/masonry-pagination.d.mts +30 -0
  125. package/dist/masonry-pagination.mjs +1 -0
  126. package/dist/masonry-ready.d.mts +5 -1
  127. package/dist/masonry-ready.mjs +1 -1
  128. package/dist/masonry-virtualization.d.mts +17 -0
  129. package/dist/masonry-virtualization.mjs +1 -0
  130. package/dist/masonry.css +1 -1
  131. package/dist/masonry.d.mts +51 -6
  132. package/dist/masonry.mjs +1 -1
  133. package/dist/metafile-esm.json +1 -1
  134. package/dist/rating-stars.d.mts +26 -0
  135. package/dist/rating-stars.mjs +1 -0
  136. package/dist/{responsive-DTXfqDUt.d.mts → responsive-Bw0ub6Hv.d.mts} +59 -3
  137. package/dist/responsive.d.mts +4 -4
  138. package/dist/skeleton-base.css +1 -1
  139. package/dist/skeleton-base.d.mts +4 -2
  140. package/dist/skeleton-base.mjs +1 -1
  141. package/dist/skeleton-cache-base.css +1 -1
  142. package/dist/skeleton-cache-base.d.mts +1 -1
  143. package/dist/skeleton-cache-base.mjs +1 -1
  144. package/dist/skeleton-cache-grid.css +1 -1
  145. package/dist/skeleton-cache-grid.d.mts +2 -3
  146. package/dist/skeleton-cache-grid.mjs +1 -1
  147. package/dist/skeleton-cache-masonry-structured.css +1 -1
  148. package/dist/skeleton-cache-masonry-structured.d.mts +2 -3
  149. package/dist/skeleton-cache-masonry-structured.mjs +1 -1
  150. package/dist/skeleton-cache-masonry.css +1 -1
  151. package/dist/skeleton-cache-masonry.d.mts +2 -3
  152. package/dist/skeleton-cache-masonry.mjs +1 -1
  153. package/dist/skeleton-cache-slider.css +1 -1
  154. package/dist/skeleton-cache-slider.d.mts +1 -1
  155. package/dist/skeleton-cache-slider.mjs +1 -1
  156. package/dist/skeleton-grid.css +1 -1
  157. package/dist/skeleton-grid.d.mts +4 -4
  158. package/dist/skeleton-grid.mjs +1 -1
  159. package/dist/skeleton-masonry-structured.css +1 -1
  160. package/dist/skeleton-masonry-structured.d.mts +4 -4
  161. package/dist/skeleton-masonry-structured.mjs +1 -1
  162. package/dist/skeleton-masonry.css +1 -1
  163. package/dist/skeleton-masonry.d.mts +2 -3
  164. package/dist/skeleton-masonry.mjs +1 -1
  165. package/dist/skeleton-slider-restore.css +1 -1
  166. package/dist/skeleton-slider-restore.d.mts +1 -1
  167. package/dist/skeleton-slider-restore.mjs +1 -1
  168. package/dist/skeleton-slider.css +1 -1
  169. package/dist/skeleton-slider.d.mts +1 -1
  170. package/dist/skeleton-slider.mjs +1 -1
  171. package/dist/slider-dots.css +1 -1
  172. package/dist/slider-dots.mjs +1 -1
  173. package/dist/slider-lazy-load.css +1 -1
  174. package/dist/slider-lazy-load.mjs +1 -1
  175. package/dist/slider-loading.css +1 -1
  176. package/dist/slider-loading.mjs +1 -1
  177. package/dist/slider-scrollbar.css +1 -1
  178. package/dist/slider-scrollbar.mjs +1 -1
  179. package/dist/slider.css +1 -1
  180. package/dist/slider.mjs +1 -1
  181. package/dist/styles.css +5 -5
  182. package/dist/thumbnails.css +1 -1
  183. package/dist/thumbnails.d.mts +3 -3
  184. package/dist/thumbnails.mjs +1 -1
  185. package/dist/{transitions-DU3ftmIq.d.mts → transitions-ChhEdSB6.d.mts} +1 -0
  186. package/dist/types-DcUQOXvS.d.mts +117 -0
  187. package/dist/types-L2pRy8k4.d.mts +136 -0
  188. package/dist/{types-Skhqh1RQ.d.mts → types-bZ-lDlKM.d.mts} +1 -1
  189. package/dist/{types-0ntfoMKP.d.mts → types-qMg7LB37.d.mts} +2 -1
  190. package/dist/{types-CYTSYIwL.d.mts → types-uhDRb0mo.d.mts} +1 -1
  191. package/dist/video.css +1 -1
  192. package/dist/video.mjs +1 -1
  193. package/dist/zoomPan.mjs +1 -1
  194. package/package.json +57 -1
  195. package/dist/FullscreenRuntime-GNSZ2OS3.mjs +0 -4
  196. package/dist/FullscreenRuntime-OE6CYK62.css +0 -1
  197. package/dist/GridSkeleton-DHuqcIFL.d.mts +0 -23
  198. package/dist/MasonrySkeleton-C7WkAbFh.d.mts +0 -29
  199. package/dist/chunk-2WM2A4RK.mjs +0 -4
  200. package/dist/chunk-2YTRLSWQ.mjs +0 -1
  201. package/dist/chunk-3GLW5FCD.mjs +0 -1
  202. package/dist/chunk-3TFKVS63.mjs +0 -6
  203. package/dist/chunk-5OYXAAEQ.mjs +0 -1
  204. package/dist/chunk-6BPMRW6K.mjs +0 -1
  205. package/dist/chunk-6MX5FBOB.mjs +0 -3
  206. package/dist/chunk-A5ERE5BW.mjs +0 -3
  207. package/dist/chunk-A6MPGIEJ.mjs +0 -1
  208. package/dist/chunk-EFXHC36P.mjs +0 -0
  209. package/dist/chunk-F443J6CZ.mjs +0 -4
  210. package/dist/chunk-GVW2FBCS.mjs +0 -1
  211. package/dist/chunk-HNMR7ZQY.mjs +0 -1
  212. package/dist/chunk-IGSEIMAQ.mjs +0 -1
  213. package/dist/chunk-KOI77DGH.mjs +0 -1
  214. package/dist/chunk-MUYJLDYM.mjs +0 -1
  215. package/dist/chunk-S23S23HP.mjs +0 -2
  216. package/dist/chunk-SMR2CHXT.mjs +0 -1
  217. package/dist/chunk-UUAWLGWO.mjs +0 -1
  218. package/dist/chunk-WUB37KRW.mjs +0 -4
  219. package/dist/chunk-Y7NUGXTR.mjs +0 -1
  220. package/dist/chunk-ZCCYTID7.mjs +0 -1
  221. package/dist/chunk-ZSA3W246.mjs +0 -1
  222. package/dist/chunk-ZVFKCJM2.mjs +0 -1
  223. package/dist/index-k9JcWxFa.d.mts +0 -21
  224. package/dist/placement-BWKxkHD8.d.mts +0 -5
  225. package/dist/types-CYB4fl6-.d.mts +0 -69
  226. package/dist/types-Cc37Drgz.d.mts +0 -62
package/README.md CHANGED
@@ -11,35 +11,49 @@ This table reports local gzip measurements for selected runtime surfaces. Type-o
11
11
  <!-- bundle-size:start -->
12
12
  | Surface | JS gzip |
13
13
  | --- | --- |
14
- | `Entries` | 12.3kB |
15
- | `entries/cache` | 17.0kB |
16
- | `FullscreenThumbnailSlider` | 20.3kB |
14
+ | `Entries` | 16.2kB |
15
+ | `entries/cache` | 21.0kB |
16
+ | `entries/ready` | 360.0B |
17
+ | `entries/pagination` | 240.0B |
18
+ | `entries/load-more` | 198.0B |
19
+ | `entries/infinite-scroll` | 208.0B |
20
+ | `entries/virtualization` | 236.0B |
21
+ | `rating-stars` | 1.3kB |
22
+ | `FullscreenThumbnailSlider` | 20.4kB |
17
23
  | `GalleryCore` | 2.6kB |
18
- | `Grid` | 5.4kB |
24
+ | `Grid` | 22.2kB |
19
25
  | `grid/ready` | 323.0B |
20
- | `grid/lazy-load` | 3.3kB |
26
+ | `grid/lazy-load` | 3.7kB |
21
27
  | `grid/fullscreen` | 1.6kB |
22
- | `Masonry` | 4.2kB |
28
+ | `grid/pagination` | 246.0B |
29
+ | `grid/load-more` | 236.0B |
30
+ | `grid/infinite-scroll` | 667.0B |
31
+ | `grid/virtualization` | 256.0B |
32
+ | `Masonry` | 11.2kB |
23
33
  | `masonry/ready` | 323.0B |
24
34
  | `masonry/fullscreen` | 1.1kB |
25
- | `masonry/measured` | 7.2kB |
35
+ | `masonry/measured` | 35.2kB |
26
36
  | `masonry/measured/ready` | 323.0B |
27
- | `masonry/lazy-load` | 3.3kB |
28
- | `Skeleton base` | 8.9kB |
29
- | `skeleton/cache/base` | 13.6kB |
30
- | `skeleton/slider` | 14.6kB |
31
- | `skeleton/cache/slider` | 19.3kB |
32
- | `skeleton/slider/restore` | 25.2kB |
33
- | `skeleton/grid` | 11.2kB |
37
+ | `masonry/lazy-load` | 3.6kB |
38
+ | `masonry/pagination` | 239.0B |
39
+ | `masonry/load-more` | 227.0B |
40
+ | `masonry/infinite-scroll` | 630.0B |
41
+ | `masonry/virtualization` | 252.0B |
42
+ | `Skeleton base` | 9.1kB |
43
+ | `skeleton/cache/base` | 13.7kB |
44
+ | `skeleton/slider` | 14.7kB |
45
+ | `skeleton/cache/slider` | 19.5kB |
46
+ | `skeleton/slider/restore` | 25.4kB |
47
+ | `skeleton/grid` | 11.4kB |
34
48
  | `skeleton/cache/grid` | 16.0kB |
35
49
  | `skeleton/masonry` | 4.4kB |
36
50
  | `skeleton/cache/masonry` | 4.4kB |
37
- | `skeleton/masonry/structured` | 20.2kB |
38
- | `skeleton/cache/masonry/structured` | 25.3kB |
39
- | `Slider core` | 19.0kB |
51
+ | `skeleton/masonry/structured` | 20.9kB |
52
+ | `skeleton/cache/masonry/structured` | 25.9kB |
53
+ | `Slider core` | 19.1kB |
40
54
  | `slider/ready` | 894.0B |
41
55
  | `slider/arrows` | 1.2kB |
42
- | `slider/dots` | 933.0B |
56
+ | `slider/dots` | 934.0B |
43
57
  | `slider/progress` | 892.0B |
44
58
  | `slider/scrollbar` | 1.2kB |
45
59
  | `slider/auto-height` | 1.3kB |
@@ -51,15 +65,15 @@ This table reports local gzip measurements for selected runtime surfaces. Type-o
51
65
  | `slider/fullscreen` | 996.0B |
52
66
  | `ThumbnailSlider` | 18.9kB |
53
67
  | `useFullscreenController` | 5.0kB |
54
- | `fullscreen/slider` | 39.5kB |
68
+ | `fullscreen/slider` | 39.7kB |
55
69
  | `fullscreen/controls` | 173.0B |
56
- | `fullscreen/captions` | 13.2kB |
70
+ | `fullscreen/captions` | 13.5kB |
57
71
  | `fullscreen/zoom-pan` | 12.4kB |
58
- | `fullscreen/video` | 16.5kB |
59
- | `fullscreen/lazy-load` | 13.2kB |
72
+ | `fullscreen/video` | 16.7kB |
73
+ | `fullscreen/lazy-load` | 13.5kB |
60
74
  | `fullscreen/crossfade` | 181.0B |
61
75
  | `fullscreen/thumbnails` | 160.0B |
62
- | `Video` | 12.8kB |
76
+ | `Video` | 13.0kB |
63
77
  | `ZoomPanImage` | 11.0kB |
64
78
  | `zoomPan/hover` | 124.0B |
65
79
  | `media / toMediaItems` | 260.0B |
@@ -150,69 +164,97 @@ The package root exports the primary public components, helper functions, and co
150
164
 
151
165
  Subpaths give bundlers a smaller graph than the root. Less JS to transfer, parse, evaluate, and hydrate can improve first loads, cache misses, slower devices, and perceived speed.
152
166
 
153
- | Entry point | Main surface |
154
- | --- | --- |
155
- | `react-motion-gallery/media` | `toMediaItems`, `MediaItem`, `MediaInput` |
156
- | `react-motion-gallery/media/ready` | `useImageDecodeReady` |
157
- | `react-motion-gallery/responsive` | `BREAKPOINT_MAP` and responsive value types |
158
- | `react-motion-gallery/reveal` | `Reveal`, `useReveal`, reveal types |
159
- | `react-motion-gallery/core` | `GalleryCore`, `GalleryCoreProvider`, `useGalleryCore` |
160
- | `react-motion-gallery/slider` | `Slider`, `createSliderIndexChannel`, slider types |
161
- | `react-motion-gallery/slider/ready` | `useSliderReady` |
162
- | `react-motion-gallery/slider/arrows` | `sliderArrows` |
163
- | `react-motion-gallery/slider/dots` | `sliderDots` |
164
- | `react-motion-gallery/slider/progress` | `sliderProgress` |
165
- | `react-motion-gallery/slider/scrollbar` | `sliderScrollbar` |
166
- | `react-motion-gallery/slider/ripple` | `sliderRipple` |
167
- | `react-motion-gallery/slider/auto-play` | `sliderAutoPlay` |
168
- | `react-motion-gallery/slider/auto-scroll` | `sliderAutoScroll` |
169
- | `react-motion-gallery/slider/auto-height` | `sliderAutoHeight` |
170
- | `react-motion-gallery/slider/lazy-load` | `sliderLazyLoad` |
171
- | `react-motion-gallery/slider/parallax` | `sliderParallax` |
172
- | `react-motion-gallery/slider/scale` | `sliderScale` |
173
- | `react-motion-gallery/slider/fade` | `sliderFade` |
174
- | `react-motion-gallery/slider/crossfade` | `sliderCrossfade` |
175
- | `react-motion-gallery/slider/fullscreen` | `sliderFullscreen` |
176
- | `react-motion-gallery/slider/loading` | `sliderLoading` |
177
- | `react-motion-gallery/grid` | `Grid`, `Grid.Item`, grid types |
178
- | `react-motion-gallery/grid/ready` | `useGridReady` |
179
- | `react-motion-gallery/grid/lazy-load` | `gridLazyLoad` |
180
- | `react-motion-gallery/grid/fullscreen` | `gridFullscreen` for Grid + `GalleryCore` |
181
- | `react-motion-gallery/masonry` | `Masonry`, `Masonry.Item`, masonry types |
182
- | `react-motion-gallery/masonry/ready` | `useMasonryReady` |
183
- | `react-motion-gallery/masonry/fullscreen` | `masonryFullscreen` for light Masonry + `GalleryCore` |
184
- | `react-motion-gallery/masonry/measured` | Measured `Masonry`, `Masonry.Item`, plugins/reveal/fullscreen types |
185
- | `react-motion-gallery/masonry/measured/ready` | `useMasonryReady` for measured masonry |
186
- | `react-motion-gallery/masonry/lazy-load` | `masonryLazyLoad` |
187
- | `react-motion-gallery/entries` | `Entries`, `flattenEntries`, entry media container helpers |
188
- | `react-motion-gallery/entries/cache` | `CachedEntries` with `entries.loading.cache` |
189
- | `react-motion-gallery/skeleton/base` | Standalone `Skeleton` and generic skeleton authoring types |
190
- | `react-motion-gallery/skeleton/slider` | `SliderSkeleton` and slider skeleton authoring types |
191
- | `react-motion-gallery/skeleton/grid` | `GridSkeleton` and grid skeleton authoring types |
192
- | `react-motion-gallery/skeleton/masonry` | Lightweight `MasonrySkeleton` for dimensioned placeholders |
193
- | `react-motion-gallery/skeleton/masonry/structured` | Structured `MasonrySkeleton` and masonry skeleton authoring types |
194
- | `react-motion-gallery/skeleton/cache` | Server-safe skeleton cookie cache helpers and types |
195
- | `react-motion-gallery/skeleton/cache/provider` | Client `SkeletonCacheProvider` for SSR snapshots and client cookie refresh |
196
- | `react-motion-gallery/skeleton/cache/base` | `CachedSkeleton` with `cache` |
197
- | `react-motion-gallery/skeleton/cache/slider` | `CachedSliderSkeleton` with `cache` |
198
- | `react-motion-gallery/skeleton/cache/grid` | `CachedGridSkeleton` with `cache` |
199
- | `react-motion-gallery/skeleton/cache/masonry` | Lightweight `CachedMasonrySkeleton` |
200
- | `react-motion-gallery/skeleton/cache/masonry/structured` | Structured `CachedMasonrySkeleton` with `cache` |
201
- | `react-motion-gallery/skeleton/slider/restore` | `RestoredSliderSkeleton` with `restore` and optional `cache` |
202
- | `react-motion-gallery/fullscreen` | `useFullscreenController` and fullscreen types |
203
- | `react-motion-gallery/fullscreen/slider` | `fullscreenSlider` |
204
- | `react-motion-gallery/fullscreen/controls` | `fullscreenControls` |
205
- | `react-motion-gallery/fullscreen/captions` | `fullscreenCaptions` |
206
- | `react-motion-gallery/fullscreen/zoom-pan` | `fullscreenZoomPan` |
207
- | `react-motion-gallery/fullscreen/video` | `fullscreenVideo` |
208
- | `react-motion-gallery/fullscreen/lazy-load` | `fullscreenLazyLoad` |
209
- | `react-motion-gallery/fullscreen/crossfade` | `fullscreenCrossfade` |
210
- | `react-motion-gallery/fullscreen/thumbnails` | `fullscreenThumbnails` |
211
- | `react-motion-gallery/thumbnails` | `ThumbnailSlider`, thumbnail sync helpers |
212
- | `react-motion-gallery/fullscreenThumbnails` | `FullscreenThumbnailSlider` |
213
- | `react-motion-gallery/video` | `Video` and optional Plyr-backed video types |
214
- | `react-motion-gallery/zoomPan` | `ZoomPanImage` and zoom/pan types |
215
- | `react-motion-gallery/zoomPan/hover` | `zoomPanHover` |
167
+ | Entry point | Main surface |
168
+ | -------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
169
+ | `react-motion-gallery/media` | `toMediaItems`, `MediaItem`, `MediaInput` |
170
+ | `react-motion-gallery/media/ready` | `useImageDecodeReady` |
171
+ | `react-motion-gallery/responsive` | `BREAKPOINT_MAP` and responsive value types |
172
+ | `react-motion-gallery/reveal` | `Reveal`, `useReveal`, reveal types |
173
+ | `react-motion-gallery/rating-stars` | `RatingStars` |
174
+ | `react-motion-gallery/core` | `GalleryCore`, `GalleryCoreProvider`, `useGalleryCore` |
175
+ | `react-motion-gallery/slider` | `Slider`, `createSliderIndexChannel`, slider types |
176
+ | `react-motion-gallery/slider/ready` | `useSliderReady` |
177
+ | `react-motion-gallery/slider/arrows` | `sliderArrows` |
178
+ | `react-motion-gallery/slider/dots` | `sliderDots` |
179
+ | `react-motion-gallery/slider/progress` | `sliderProgress` |
180
+ | `react-motion-gallery/slider/scrollbar` | `sliderScrollbar` |
181
+ | `react-motion-gallery/slider/ripple` | `sliderRipple` |
182
+ | `react-motion-gallery/slider/auto-play` | `sliderAutoPlay` |
183
+ | `react-motion-gallery/slider/auto-scroll` | `sliderAutoScroll` |
184
+ | `react-motion-gallery/slider/auto-height` | `sliderAutoHeight` |
185
+ | `react-motion-gallery/slider/lazy-load` | `sliderLazyLoad` |
186
+ | `react-motion-gallery/slider/parallax` | `sliderParallax` |
187
+ | `react-motion-gallery/slider/scale` | `sliderScale` |
188
+ | `react-motion-gallery/slider/fade` | `sliderFade` |
189
+ | `react-motion-gallery/slider/crossfade` | `sliderCrossfade` |
190
+ | `react-motion-gallery/slider/fullscreen` | `sliderFullscreen` |
191
+ | `react-motion-gallery/slider/loading` | `sliderLoading` |
192
+ | `react-motion-gallery/grid` | `Grid`, `Grid.Item`, grid types |
193
+ | `react-motion-gallery/grid/ready` | `useGridReady` |
194
+ | `react-motion-gallery/grid/lazy-load` | `gridLazyLoad` |
195
+ | `react-motion-gallery/grid/fullscreen` | `gridFullscreen` for Grid + `GalleryCore` |
196
+ | `react-motion-gallery/grid/pagination` | `gridPagination`, `useGridPagination`, `GridPaginationControls`, page range helpers |
197
+ | `react-motion-gallery/grid/load-more` | `gridLoadMore`, `useGridLoadMore` |
198
+ | `react-motion-gallery/grid/infinite-scroll` | `gridInfiniteScroll`, `useGridInfiniteScroll` |
199
+ | `react-motion-gallery/grid/virtualization` | `gridVirtualization`, `useGridVirtualizer` |
200
+ | `react-motion-gallery/masonry` | `Masonry`, `Masonry.Item`, masonry types |
201
+ | `react-motion-gallery/masonry/ready` | `useMasonryReady` |
202
+ | `react-motion-gallery/masonry/fullscreen` | `masonryFullscreen` for light Masonry + `GalleryCore` |
203
+ | `react-motion-gallery/masonry/measured` | Measured `Masonry`, `Masonry.Item`, plugins/reveal/fullscreen types |
204
+ | `react-motion-gallery/masonry/measured/ready` | `useMasonryReady` for measured masonry |
205
+ | `react-motion-gallery/masonry/lazy-load` | `masonryLazyLoad` |
206
+ | `react-motion-gallery/masonry/pagination` | `masonryPagination`, `useMasonryPagination`, `MasonryPaginationControls`, page range helpers |
207
+ | `react-motion-gallery/masonry/load-more` | `masonryLoadMore`, `useMasonryLoadMore` |
208
+ | `react-motion-gallery/masonry/infinite-scroll` | `masonryInfiniteScroll`, `useMasonryInfiniteScroll` |
209
+ | `react-motion-gallery/masonry/virtualization` | `masonryVirtualization`, `useMasonryVirtualizer` |
210
+ | `react-motion-gallery/entries` | `Entries`, `flattenEntries`, entry layout/media container helpers |
211
+ | `react-motion-gallery/entries/cache` | `CachedEntries` with `entries.loading.cache` |
212
+ | `react-motion-gallery/entries/ready` | `useEntriesReady` |
213
+ | `react-motion-gallery/entries/pagination` | `entriesPagination`, `useEntriesPagination`, `EntriesPaginationControls`, page range helpers |
214
+ | `react-motion-gallery/entries/load-more` | `entriesLoadMore`, `useEntriesLoadMore` |
215
+ | `react-motion-gallery/entries/infinite-scroll` | `entriesInfiniteScroll`, `useEntriesInfiniteScroll` |
216
+ | `react-motion-gallery/entries/virtualization` | `entriesVirtualization`, `useEntriesVirtualizer` |
217
+ | `react-motion-gallery/skeleton/base` | Standalone `Skeleton` and generic skeleton authoring types |
218
+ | `react-motion-gallery/skeleton/slider` | `SliderSkeleton` and slider skeleton authoring types |
219
+ | `react-motion-gallery/skeleton/grid` | `GridSkeleton` and grid skeleton authoring types |
220
+ | `react-motion-gallery/skeleton/masonry` | Lightweight `MasonrySkeleton` for dimensioned placeholders |
221
+ | `react-motion-gallery/skeleton/masonry/structured` | Structured `MasonrySkeleton` and masonry skeleton authoring types |
222
+ | `react-motion-gallery/skeleton/cache` | Server-safe skeleton cookie cache helpers and types |
223
+ | `react-motion-gallery/skeleton/cache/provider` | Client `SkeletonCacheProvider` for SSR snapshots and client cookie refresh |
224
+ | `react-motion-gallery/skeleton/cache/base` | `CachedSkeleton` with `cache` |
225
+ | `react-motion-gallery/skeleton/cache/slider` | `CachedSliderSkeleton` with `cache` |
226
+ | `react-motion-gallery/skeleton/cache/grid` | `CachedGridSkeleton` with `cache` |
227
+ | `react-motion-gallery/skeleton/cache/masonry` | Lightweight `CachedMasonrySkeleton` |
228
+ | `react-motion-gallery/skeleton/cache/masonry/structured` | Structured `CachedMasonrySkeleton` with `cache` |
229
+ | `react-motion-gallery/skeleton/slider/restore` | `RestoredSliderSkeleton` with `restore` and optional `cache` |
230
+ | `react-motion-gallery/fullscreen` | `useFullscreenController` and fullscreen types |
231
+ | `react-motion-gallery/fullscreen/slider` | `fullscreenSlider` |
232
+ | `react-motion-gallery/fullscreen/controls` | `fullscreenControls` |
233
+ | `react-motion-gallery/fullscreen/captions` | `fullscreenCaptions` |
234
+ | `react-motion-gallery/fullscreen/zoom-pan` | `fullscreenZoomPan` |
235
+ | `react-motion-gallery/fullscreen/video` | `fullscreenVideo` |
236
+ | `react-motion-gallery/fullscreen/lazy-load` | `fullscreenLazyLoad` |
237
+ | `react-motion-gallery/fullscreen/crossfade` | `fullscreenCrossfade` |
238
+ | `react-motion-gallery/fullscreen/thumbnails` | `fullscreenThumbnails` |
239
+ | `react-motion-gallery/thumbnails` | `ThumbnailSlider`, thumbnail sync helpers |
240
+ | `react-motion-gallery/fullscreenThumbnails` | `FullscreenThumbnailSlider` |
241
+ | `react-motion-gallery/video` | `Video` and optional Plyr-backed video types |
242
+ | `react-motion-gallery/zoomPan` | `ZoomPanImage` and zoom/pan types |
243
+ | `react-motion-gallery/zoomPan/hover` | `zoomPanHover` |
244
+
245
+ Data plugin imports are intentionally split by surface and behavior:
246
+
247
+ | Surface | Pagination | Load more | Infinite scroll | Virtualization |
248
+ | ------- | ----------------------------------------- | ---------------------------------------- | ---------------------------------------------- | --------------------------------------------- |
249
+ | Grid | `react-motion-gallery/grid/pagination` | `react-motion-gallery/grid/load-more` | `react-motion-gallery/grid/infinite-scroll` | `react-motion-gallery/grid/virtualization` |
250
+ | Masonry | `react-motion-gallery/masonry/pagination` | `react-motion-gallery/masonry/load-more` | `react-motion-gallery/masonry/infinite-scroll` | `react-motion-gallery/masonry/virtualization` |
251
+ | Entries | `react-motion-gallery/entries/pagination` | `react-motion-gallery/entries/load-more` | `react-motion-gallery/entries/infinite-scroll` | `react-motion-gallery/entries/virtualization` |
252
+
253
+ ```typescript
254
+ import { useGridPagination } from "react-motion-gallery/grid/pagination";
255
+ import { useMasonryLoadMore } from "react-motion-gallery/masonry/load-more";
256
+ import { entriesVirtualization } from "react-motion-gallery/entries/virtualization";
257
+ ```
216
258
 
217
259
  ## MCP server
218
260
 
@@ -394,32 +436,32 @@ The file-writing tools default to dry runs unless `apply: true` is passed, and t
394
436
 
395
437
  ### `GalleryCore` props
396
438
 
397
- | Option | Type | Default | Notes |
398
- | --- | --- | --- | --- |
399
- | `children` | `React.ReactNode` | `—` | The gallery tree using the shared core. |
400
- | `layout` | `"slider" \| "grid" \| "masonry" \| "entries"` | `—` | Declares the owning base layout. Omit it for standalone fullscreen/core usage. |
401
- | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Breakpoint map shared with descendants. |
402
- | `fullscreenItems` | `MediaItem[] \| string[]` | `[]` | Normalized fullscreen media list. |
403
- | `nodes` | `ReactNode \| ReactNode[]` | `—` | Advanced initial node list used by the slider-backed imperative state. |
439
+ | Option | Type | Default | Notes |
440
+ | ----------------- | ---------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------ |
441
+ | `children` | `React.ReactNode` | `—` | The gallery tree using the shared core. |
442
+ | `layout` | `"slider" \| "grid" \| "masonry" \| "entries"` | `—` | Declares the owning base layout. Omit it for standalone fullscreen/core usage. |
443
+ | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Breakpoint map shared with descendants. |
444
+ | `fullscreenItems` | `MediaItem[] \| string[]` | `[]` | Normalized fullscreen media list. |
445
+ | `nodes` | `ReactNode \| ReactNode[]` | `—` | Advanced initial node list used by the slider-backed imperative state. |
404
446
 
405
447
  ### `useGalleryCore` API
406
448
 
407
449
  `GalleryApi` is the public alias for `GalleryCoreApi`. It covers core fullscreen state and programmatic fullscreen opening. Slider item mutation lives on `SliderHandle` and `SliderApi`.
408
450
 
409
- | Field / Method | Type | Notes |
410
- | --- | --- | --- |
411
- | `layout` | `"slider" \| "grid" \| "masonry" \| "entries" \| null` | Current owning layout, or `null` for standalone fullscreen/core usage. |
412
- | `effectiveBreakpoints` | `Record<string, number>` | Breakpoint map after merging custom `GalleryCore.breakpoints` with defaults. |
413
- | `normalizedItems` | `MediaItem[]` | Fullscreen item list normalized from `fullscreenItems`. |
414
- | `fsEnabled` | `boolean` | `true` when a mounted fullscreen controller has enabled fullscreen behavior. |
415
- | `setFsEnabled` | `(enabled: boolean) => void` | Enables or disables fullscreen behavior. Usually handled by `useFullscreenController`. |
416
- | `isFullscreenOpen` | `boolean` | `true` while fullscreen is open. |
417
- | `isFullscreenOpenRef` | `React.RefObject<boolean>` | Ref mirror for handlers that need the current fullscreen-open state. |
418
- | `setFullscreenOpen` | `(open: boolean) => void` | Updates fullscreen-open state. Usually handled by the fullscreen runtime. |
419
- | `openFullscreenAt` | `({ index, method?, event? }) => void` | Opens fullscreen at a normalized fullscreen item index. Pass the source event for scale-origin detection. |
420
- | `notifyBaseVisibleIndex` | `(index: number) => void` | Emits the visible base media index for fullscreen lazy-load/prewarm coordination. |
421
- | `notifyFsVisibleIndex` | `(index: number) => void` | Emits the active fullscreen index back to base media. |
422
- | `registerExpandableImage` | `(index: number, node: HTMLElement \| null) => void` | Registers an origin surface for layoutless scale transitions. |
451
+ | Field / Method | Type | Notes |
452
+ | ------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
453
+ | `layout` | `"slider" \| "grid" \| "masonry" \| "entries" \| null` | Current owning layout, or `null` for standalone fullscreen/core usage. |
454
+ | `effectiveBreakpoints` | `Record<string, number>` | Breakpoint map after merging custom `GalleryCore.breakpoints` with defaults. |
455
+ | `normalizedItems` | `MediaItem[]` | Fullscreen item list normalized from `fullscreenItems`. |
456
+ | `fsEnabled` | `boolean` | `true` when a mounted fullscreen controller has enabled fullscreen behavior. |
457
+ | `setFsEnabled` | `(enabled: boolean) => void` | Enables or disables fullscreen behavior. Usually handled by `useFullscreenController`. |
458
+ | `isFullscreenOpen` | `boolean` | `true` while fullscreen is open. |
459
+ | `isFullscreenOpenRef` | `React.RefObject<boolean>` | Ref mirror for handlers that need the current fullscreen-open state. |
460
+ | `setFullscreenOpen` | `(open: boolean) => void` | Updates fullscreen-open state. Usually handled by the fullscreen runtime. |
461
+ | `openFullscreenAt` | `({ index, method?, event? }) => void` | Opens fullscreen at a normalized fullscreen item index. Pass the source event for scale-origin detection. |
462
+ | `notifyBaseVisibleIndex` | `(index: number) => void` | Emits the visible base media index for fullscreen lazy-load/prewarm coordination. |
463
+ | `notifyFsVisibleIndex` | `(index: number) => void` | Emits the active fullscreen index back to base media. |
464
+ | `registerExpandableImage` | `(index: number, node: HTMLElement \| null) => void` | Registers an origin surface for layoutless scale transitions. |
423
465
 
424
466
  ## Skeleton
425
467
 
@@ -448,17 +490,17 @@ export function LoadingShell({ ready, children }: { ready: boolean; children: Re
448
490
 
449
491
  `Skeleton` can render a standalone placeholder by itself, or it can wrap real content and own the loading transition. Wrapper mode is enabled when `children` are provided.
450
492
 
451
- | Option | Type | Default | Notes |
452
- | --- | --- | --- | --- |
453
- | `layout` | `SkeletonNode` | `—` | Structured placeholder layout tree. |
454
- | `children` | `React.ReactNode` | `—` | Real content. When present, `Skeleton` renders content and loading layers. |
455
- | `ready` | `boolean` | `false` | Reveals content and exits the skeleton once true. |
456
- | `enabled` | `boolean` | `true` | Set false to render content immediately with no skeleton layer. |
457
- | `force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `false` | Keeps the skeleton visible. Set `showContent: true` to preview ready content under the skeleton, and tune the overlay with `skeletonOpacity`. |
458
- | `timing.exitMs` | `number` | `600` | Keeps the skeleton layer mounted for this long after exit starts and controls the opacity transition. |
459
- | `timing.minVisibleMs` | `number` | `220` | Minimum time the skeleton stays visible before exit can begin. |
460
- | `shellClassName` / `shellStyle` | `string` / `CSSProperties` | `—` | Wrapper-layer class and style for content+skeleton mode. |
461
- | `contentClassName` / `contentStyle` | `string` / `CSSProperties` | `—` | Content-layer class and style for wrapper mode. |
493
+ | Option | Type | Default | Notes |
494
+ | ----------------------------------- | ----------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
495
+ | `layout` | `SkeletonNode` | `—` | Structured placeholder layout tree. |
496
+ | `children` | `React.ReactNode` | `—` | Real content. When present, `Skeleton` renders content and loading layers. |
497
+ | `ready` | `boolean` | `false` | Reveals content and exits the skeleton once true. |
498
+ | `enabled` | `boolean` | `true` | Set false to render content immediately with no skeleton layer. |
499
+ | `force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `false` | Keeps the skeleton visible. Set `showContent: true` to preview ready content under the skeleton, and tune the overlay with `skeletonOpacity`. |
500
+ | `timing.exitMs` | `number` | `600` | Keeps the skeleton layer mounted for this long after exit starts and controls the opacity transition. |
501
+ | `timing.minVisibleMs` | `number` | `220` | Minimum time the skeleton stays visible before exit can begin. |
502
+ | `shellClassName` / `shellStyle` | `string` / `CSSProperties` | `—` | Wrapper-layer class and style for content+skeleton mode. |
503
+ | `contentClassName` / `contentStyle` | `string` / `CSSProperties` | `—` | Content-layer class and style for wrapper mode. |
462
504
 
463
505
  The wrapper timing model matches the gallery loading layers: content begins fading in as soon as the skeleton exit starts; it does not wait for the skeleton to unmount.
464
506
 
@@ -538,7 +580,7 @@ import {
538
580
  import { GalleryPageClient } from "./GalleryPageClient";
539
581
 
540
582
  function readSkeletonCacheSnapshots(
541
- cookieStore: Awaited<ReturnType<typeof cookies>>
583
+ cookieStore: Awaited<ReturnType<typeof cookies>>,
542
584
  ) {
543
585
  const snapshots: Record<string, SkeletonCacheSnapshot> = {};
544
586
 
@@ -574,7 +616,10 @@ import { useMasonryReady } from "react-motion-gallery/masonry/measured/ready";
574
616
  export function GalleryPageClient({
575
617
  skeletonCacheSnapshots,
576
618
  }: {
577
- skeletonCacheSnapshots: Record<string, SkeletonCacheSnapshot | null | undefined>;
619
+ skeletonCacheSnapshots: Record<
620
+ string,
621
+ SkeletonCacheSnapshot | null | undefined
622
+ >;
578
623
  }) {
579
624
  const { ref, ready } = useMasonryReady();
580
625
 
@@ -621,7 +666,7 @@ import { CachedEntries as Entries } from "react-motion-gallery/entries/cache";
621
666
  skeleton: entrySkeleton,
622
667
  },
623
668
  }}
624
- />
669
+ />;
625
670
  ```
626
671
 
627
672
  Cookie options can be tuned per skeleton:
@@ -643,7 +688,7 @@ import { CachedGridSkeleton as GridSkeleton } from "react-motion-gallery/skeleto
643
688
  },
644
689
  }}
645
690
  layout={productGridSkeleton}
646
- />
691
+ />;
647
692
  ```
648
693
 
649
694
  ## Reveal
@@ -665,7 +710,10 @@ export function ImageSectionReveal({ src, alt }: { src: string; alt: string }) {
665
710
  ready={image.ready}
666
711
  transform={{ y: 18, scale: 0.98 }}
667
712
  durationMs={{ opacity: 220, transform: 680 }}
668
- easing={{ opacity: "ease-out", transform: "cubic-bezier(0.2, 0.7, 0.2, 1)" }}
713
+ easing={{
714
+ opacity: "ease-out",
715
+ transform: "cubic-bezier(0.2, 0.7, 0.2, 1)",
716
+ }}
669
717
  staggerIndex={1}
670
718
  >
671
719
  <img src={src} alt={alt} loading="eager" decoding="async" />
@@ -705,58 +753,58 @@ export function DecodedImageReveal({ src, alt }: { src: string; alt: string }) {
705
753
 
706
754
  `Reveal` is polymorphic through `as`; element-specific props are forwarded to the rendered element after the reveal options are removed. `useReveal(options)` accepts the same `RevealOptions` behavior props and returns props you can spread onto an element you own.
707
755
 
708
- | Prop | Type | Default | Notes |
709
- | --- | --- | --- | --- |
710
- | `as` | `React.ElementType` | `"div"` | Render as another element or component, such as `"section"`, `"figure"`, or a custom component. |
711
- | `children` | `React.ReactNode` | `—` | Content rendered inside the revealed element. |
712
- | `className` | `string` | `—` | Merged with the internal reveal class. |
713
- | `style` | `React.CSSProperties` | `—` | Merged onto the rendered element. A string `style.transform` becomes the final resting transform for transform reveals. |
714
- | `ref` | `React.Ref<HTMLElement>` | `—` | Forwarded to the rendered element. |
715
- | `variant` | `"fade" \| "transform"` | `"transform"` | `fade` animates opacity only; `transform` also animates from the configured transform. |
716
- | `transform` | `RevealTransform` | `{ y: 14 }` | Typed transform object or raw CSS transform string used as the hidden/from transform. |
717
- | `once` | `boolean` | `true` | Keeps the element revealed after the first reveal. Set `false` for reversible in-view reveals. |
718
- | `ready` | `boolean` | `true` | Gates the reveal until external readiness, such as image decode, has completed. With `once: false`, dropping `ready` back to `false` can hide the element again. |
719
- | `threshold` | `number` | `0.12` | Intersection ratio required before reveal. Values are clamped between `0` and `1`. |
720
- | `rootMargin` | `string` | `"0px 0px -8% 0px"` | IntersectionObserver root margin used to tune when the element enters view. |
721
- | `durationMs` | `RevealDuration` | `520` | Scalar timing applies to opacity and transform. Object timing can set `{ opacity, transform }` separately. |
722
- | `delayMs` | `number` | `0` | Base delay before the reveal animation starts. Negative values are clamped to `0`. |
723
- | `staggerIndex` | `number` | `0` | Multiplies `staggerMs` and adds to `delayMs`; useful for repeated items. Negative values are clamped to `0`. |
724
- | `staggerMs` | `number` | `70` | Delay step used with `staggerIndex`. Negative values are clamped to `0`. |
725
- | `easing` | `RevealEasing` | `"cubic-bezier(0.2, 0.7, 0.2, 1)"` | Scalar easing applies to both channels. Object easing can set `{ opacity, transform }` separately. |
726
- | `disabled` | `boolean` | `false` | Bypasses observer gating and renders as revealed. |
727
- | `onReveal` | `() => void` | `—` | Called when the element transitions from hidden to revealed. Not called for disabled reveals. |
728
- | Other element props | `Omit<React.ComponentPropsWithoutRef<E>, reveal-owned keys>` | `—` | Forwarded to the rendered element, including ARIA attributes and event handlers, after reveal-owned props are removed. |
756
+ | Prop | Type | Default | Notes |
757
+ | ------------------- | ------------------------------------------------------------ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
758
+ | `as` | `React.ElementType` | `"div"` | Render as another element or component, such as `"section"`, `"figure"`, or a custom component. |
759
+ | `children` | `React.ReactNode` | `—` | Content rendered inside the revealed element. |
760
+ | `className` | `string` | `—` | Merged with the internal reveal class. |
761
+ | `style` | `React.CSSProperties` | `—` | Merged onto the rendered element. A string `style.transform` becomes the final resting transform for transform reveals. |
762
+ | `ref` | `React.Ref<HTMLElement>` | `—` | Forwarded to the rendered element. |
763
+ | `variant` | `"fade" \| "transform"` | `"transform"` | `fade` animates opacity only; `transform` also animates from the configured transform. |
764
+ | `transform` | `RevealTransform` | `{ y: 14 }` | Typed transform object or raw CSS transform string used as the hidden/from transform. |
765
+ | `once` | `boolean` | `true` | Keeps the element revealed after the first reveal. Set `false` for reversible in-view reveals. |
766
+ | `ready` | `boolean` | `true` | Gates the reveal until external readiness, such as image decode, has completed. With `once: false`, dropping `ready` back to `false` can hide the element again. |
767
+ | `threshold` | `number` | `0.12` | Intersection ratio required before reveal. Values are clamped between `0` and `1`. |
768
+ | `rootMargin` | `string` | `"0px 0px -8% 0px"` | IntersectionObserver root margin used to tune when the element enters view. |
769
+ | `durationMs` | `RevealDuration` | `520` | Scalar timing applies to opacity and transform. Object timing can set `{ opacity, transform }` separately. |
770
+ | `delayMs` | `number` | `0` | Base delay before the reveal animation starts. Negative values are clamped to `0`. |
771
+ | `staggerIndex` | `number` | `0` | Multiplies `staggerMs` and adds to `delayMs`; useful for repeated items. Negative values are clamped to `0`. |
772
+ | `staggerMs` | `number` | `70` | Delay step used with `staggerIndex`. Negative values are clamped to `0`. |
773
+ | `easing` | `RevealEasing` | `"cubic-bezier(0.2, 0.7, 0.2, 1)"` | Scalar easing applies to both channels. Object easing can set `{ opacity, transform }` separately. |
774
+ | `disabled` | `boolean` | `false` | Bypasses observer gating and renders as revealed. |
775
+ | `onReveal` | `() => void` | `—` | Called when the element transitions from hidden to revealed. Not called for disabled reveals. |
776
+ | Other element props | `Omit<React.ComponentPropsWithoutRef<E>, reveal-owned keys>` | `—` | Forwarded to the rendered element, including ARIA attributes and event handlers, after reveal-owned props are removed. |
729
777
 
730
778
  ### Reveal transform fields
731
779
 
732
780
  Numbers in length fields become `px`; numbers in angle fields become `deg`. Pass a raw string as `transform` when you need complete CSS control.
733
781
 
734
- | Field | Type | Default | Notes |
735
- | --- | --- | --- | --- |
736
- | `x`, `y`, `z` | `RevealLength` | `0px` when any translate field is set | Builds `translate3d(x, y, z)`. |
737
- | `scale` | `number` | `1` | Uniform scale used by both axes unless `scaleX` or `scaleY` is provided. |
738
- | `scaleX`, `scaleY` | `number` | `scale ?? 1` | Axis-specific scale values. |
739
- | `rotate`, `rotateX`, `rotateY` | `RevealAngle` | `—` | Rotation transforms. |
740
- | `skewX`, `skewY` | `RevealAngle` | `—` | Skew transforms. |
741
- | `perspective` | `RevealLength` | `—` | Prepended as `perspective(...)` when provided. |
742
- | `raw` | `string` | `—` | Appended to the generated transform string. |
782
+ | Field | Type | Default | Notes |
783
+ | ------------------------------ | -------------- | ------------------------------------- | ------------------------------------------------------------------------ |
784
+ | `x`, `y`, `z` | `RevealLength` | `0px` when any translate field is set | Builds `translate3d(x, y, z)`. |
785
+ | `scale` | `number` | `1` | Uniform scale used by both axes unless `scaleX` or `scaleY` is provided. |
786
+ | `scaleX`, `scaleY` | `number` | `scale ?? 1` | Axis-specific scale values. |
787
+ | `rotate`, `rotateX`, `rotateY` | `RevealAngle` | `—` | Rotation transforms. |
788
+ | `skewX`, `skewY` | `RevealAngle` | `—` | Skew transforms. |
789
+ | `perspective` | `RevealLength` | `—` | Prepended as `perspective(...)` when provided. |
790
+ | `raw` | `string` | `—` | Appended to the generated transform string. |
743
791
 
744
792
  ### Reveal exported types
745
793
 
746
- | Type | Definition | Notes |
747
- | --- | --- | --- |
748
- | `RevealVariant` | `"fade" \| "transform"` | Reveal animation mode. |
749
- | `RevealLength` | `number \| string` | Numeric values resolve to pixel lengths. |
750
- | `RevealAngle` | `number \| string` | Numeric values resolve to degree angles. |
751
- | `RevealMotionChannel` | `"opacity" \| "transform"` | Channels used by duration and easing objects. |
752
- | `RevealChannelOptions<T>` | `Partial<Record<RevealMotionChannel, T>>` | Per-channel option helper. |
753
- | `RevealDuration` | `number \| RevealChannelOptions<number>` | Shared or per-channel duration. |
754
- | `RevealEasing` | `string \| RevealChannelOptions<string>` | Shared or per-channel easing. |
755
- | `RevealTransformObject` | transform fields table above | Object form for generated from-transforms. |
756
- | `RevealTransform` | `RevealTransformObject \| string` | Object or raw CSS transform string. |
757
- | `RevealOptions` | behavior options above | Options accepted by `useReveal`. |
758
- | `RevealProps<E>` | `RevealOptions` plus polymorphic element props | Props accepted by `<Reveal>`. |
759
- | `UseRevealResult<T>` | `{ ref, revealed, inView, revealProps }` | Return value from `useReveal`; spread `revealProps` and attach `ref` to your element. |
794
+ | Type | Definition | Notes |
795
+ | ------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------- |
796
+ | `RevealVariant` | `"fade" \| "transform"` | Reveal animation mode. |
797
+ | `RevealLength` | `number \| string` | Numeric values resolve to pixel lengths. |
798
+ | `RevealAngle` | `number \| string` | Numeric values resolve to degree angles. |
799
+ | `RevealMotionChannel` | `"opacity" \| "transform"` | Channels used by duration and easing objects. |
800
+ | `RevealChannelOptions<T>` | `Partial<Record<RevealMotionChannel, T>>` | Per-channel option helper. |
801
+ | `RevealDuration` | `number \| RevealChannelOptions<number>` | Shared or per-channel duration. |
802
+ | `RevealEasing` | `string \| RevealChannelOptions<string>` | Shared or per-channel easing. |
803
+ | `RevealTransformObject` | transform fields table above | Object form for generated from-transforms. |
804
+ | `RevealTransform` | `RevealTransformObject \| string` | Object or raw CSS transform string. |
805
+ | `RevealOptions` | behavior options above | Options accepted by `useReveal`. |
806
+ | `RevealProps<E>` | `RevealOptions` plus polymorphic element props | Props accepted by `<Reveal>`. |
807
+ | `UseRevealResult<T>` | `{ ref, revealed, inView, revealProps }` | Return value from `useReveal`; spread `revealProps` and attach `ref` to your element. |
760
808
 
761
809
  ## Slider
762
810
 
@@ -785,29 +833,29 @@ export function BasicSlider() {
785
833
 
786
834
  ### Slider component props
787
835
 
788
- | Option | Type | Default | Notes |
789
- | --- | --- | --- | --- |
790
- | `children` | `React.ReactNode` | `—` | Slide content rendered in order. |
791
- | `initialIndex` | `number` | `0` | Selects the slide index used for the first layout and reveal fade-in. |
792
- | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Merged with the internal breakpoint map for responsive values. |
793
- | `indexChannel` | `SliderIndexChannel` | internal channel | Share index state with thumbnails or sibling sliders. |
794
- | `plugins` | `SliderPlugin[]` | `[]` | Explicit first-party slider features such as arrows, dots, auto-height, effects, fullscreen, or lazy-load. |
836
+ | Option | Type | Default | Notes |
837
+ | -------------- | ------------------------ | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
838
+ | `children` | `React.ReactNode` | `—` | Slide content rendered in order. |
839
+ | `initialIndex` | `number` | `0` | Selects the slide index used for the first layout and reveal fade-in. |
840
+ | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Merged with the internal breakpoint map for responsive values. |
841
+ | `indexChannel` | `SliderIndexChannel` | internal channel | Share index state with thumbnails or sibling sliders. |
842
+ | `plugins` | `SliderPlugin[]` | `[]` | Explicit first-party slider features such as arrows, dots, auto-height, effects, fullscreen, or lazy-load. |
795
843
 
796
844
  ### Slider layout and scroll options
797
845
 
798
- | Option | Type | Default | Notes |
799
- | --- | --- | --- | --- |
800
- | `layout.gap` | `number \| Record<string, number>` | `20` | Responsive gap between cells. |
801
- | `layout.cellsPerSlide` | `number \| Record<string, number>` | `—` | Groups multiple cells into a slide page. |
802
- | `direction.dir` | `"ltr" \| "rtl"` | `"ltr"` | Text direction and arrow direction. |
803
- | `direction.axis` | `"x" \| "y"` | `"x"` | Horizontal or vertical slider axis. |
804
- | `align` | `"start" \| "center"` | `"start"` | Slide alignment inside the viewport. |
805
- | `scroll.groupCells` | `boolean \| number \| Record<string, number>` | `false` | `true` groups each snap by the cells that fit in the viewport. A number groups exactly that many cells per snap without changing cell sizing. Responsive number maps use the same `breakpoints` prop as other slider responsive values. |
806
- | `scroll.skipSnaps` | `boolean \| { enabled?: boolean; threshold?: number }` | `false` | Allows momentum to skip snap points. Object form enables skip snaps by default and `threshold` requires release force to reach a multiple of the adjacent snap distance before multi-snap momentum is used. |
807
- | `scroll.strictSnaps` | `boolean` | `false` | Prevents one drag release from settling more than one snap away from where the drag started. Overrides `scroll.skipSnaps`. |
808
- | `scroll.freeScroll` | `boolean` | `false` | Enables free dragging instead of strict snapping. |
809
- | `scroll.loop` | `boolean` | `false` | Wraps around at the ends. |
810
- | `scroll.containScroll` | `boolean` | `false` | Clamps start/end snaps so non-looping variable-width or centered sliders do not leave excess empty space at the track edges. |
846
+ | Option | Type | Default | Notes |
847
+ | ---------------------- | ------------------------------------------------------ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
848
+ | `layout.gap` | `number \| Record<string, number>` | `20` | Responsive gap between cells. |
849
+ | `layout.cellsPerSlide` | `number \| Record<string, number>` | `—` | Groups multiple cells into a slide page. |
850
+ | `direction.dir` | `"ltr" \| "rtl"` | `"ltr"` | Text direction and arrow direction. |
851
+ | `direction.axis` | `"x" \| "y"` | `"x"` | Horizontal or vertical slider axis. |
852
+ | `align` | `"start" \| "center"` | `"start"` | Slide alignment inside the viewport. |
853
+ | `scroll.groupCells` | `boolean \| number \| Record<string, number>` | `false` | `true` groups each snap by the cells that fit in the viewport. A number groups exactly that many cells per snap without changing cell sizing. Responsive number maps use the same `breakpoints` prop as other slider responsive values. |
854
+ | `scroll.skipSnaps` | `boolean \| { enabled?: boolean; threshold?: number }` | `false` | Allows momentum to skip snap points. Object form enables skip snaps by default and `threshold` requires release force to reach a multiple of the adjacent snap distance before multi-snap momentum is used. |
855
+ | `scroll.strictSnaps` | `boolean` | `false` | Prevents one drag release from settling more than one snap away from where the drag started. Overrides `scroll.skipSnaps`. |
856
+ | `scroll.freeScroll` | `boolean` | `false` | Enables free dragging instead of strict snapping. |
857
+ | `scroll.loop` | `boolean` | `false` | Wraps around at the ends. |
858
+ | `scroll.containScroll` | `boolean` | `false` | Clamps start/end snaps so non-looping variable-width or centered sliders do not leave excess empty space at the track edges. |
811
859
 
812
860
  `scroll.groupCells` affects the snap pages used by drag, wheel, arrows, dots, and imperative navigation. Use `true` for automatic fit-to-viewport grouping, or a count for explicit snap pages:
813
861
 
@@ -831,14 +879,14 @@ Numeric values are truncated and clamped to the available slide count. `1`, `0`,
831
879
 
832
880
  `elements`, `motion`, and `reveal` stay in the core slider. Controls, autoplay, lazy media, effects, auto-height, fullscreen, and loading overlays are explicit plugin imports.
833
881
 
834
- | Option | Type | Default | Notes |
835
- | --- | --- | --- | --- |
836
- | `elements.viewport` | `ElementStyle` | `—` | Class and inline style for the viewport element. |
837
- | `elements.container` | `ElementStyle` | `—` | Class and inline style for the moving slider container. |
838
- | `reveal.renderReveal` | `({ active, containerProps }, content) => ReactNode` | `—` | Custom reveal wrapper. |
839
- | `reveal.staggerMs` | `number` | `—` | Delay between item reveal fades. |
840
- | `reveal.durationMs` | `number` | `—` | Reveal fade duration. |
841
- | `reveal.easing` | `string` | `—` | Reveal fade easing. |
882
+ | Option | Type | Default | Notes |
883
+ | --------------------- | ---------------------------------------------------- | ------- | ------------------------------------------------------- |
884
+ | `elements.viewport` | `ElementStyle` | `—` | Class and inline style for the viewport element. |
885
+ | `elements.container` | `ElementStyle` | `—` | Class and inline style for the moving slider container. |
886
+ | `reveal.renderReveal` | `({ active, containerProps }, content) => ReactNode` | `—` | Custom reveal wrapper. |
887
+ | `reveal.staggerMs` | `number` | `—` | Delay between item reveal fades. |
888
+ | `reveal.durationMs` | `number` | `—` | Reveal fade duration. |
889
+ | `reveal.easing` | `string` | `—` | Reveal fade easing. |
842
890
 
843
891
  ### Slider plugins
844
892
 
@@ -854,23 +902,23 @@ import { sliderParallax } from "react-motion-gallery/slider/parallax";
854
902
  </Slider>;
855
903
  ```
856
904
 
857
- | Import | Factory | Notes |
858
- | --- | --- | --- |
859
- | `react-motion-gallery/slider/arrows` | `sliderArrows(options)` | Previous/next arrows. |
860
- | `react-motion-gallery/slider/dots` | `sliderDots(options)` | Pagination dots. |
861
- | `react-motion-gallery/slider/progress` | `sliderProgress(options)` | Progress bar or custom progress renderer. |
862
- | `react-motion-gallery/slider/scrollbar` | `sliderScrollbar(options)` | Range-style position control. |
863
- | `react-motion-gallery/slider/ripple` | `sliderRipple(options)` | Enables ripple feedback for controls that call `createRipple`. |
864
- | `react-motion-gallery/slider/auto-play` | `sliderAutoPlay(options)` | Timed slide changes. |
865
- | `react-motion-gallery/slider/auto-scroll` | `sliderAutoScroll(options)` | Timed continuous advancement. |
866
- | `react-motion-gallery/slider/auto-height` | `sliderAutoHeight(options)` | Measures active slide height and gates slider readiness until measured. |
867
- | `react-motion-gallery/slider/lazy-load` | `sliderLazyLoad(options)` | Adds lazy media attributes to slide images and videos. |
868
- | `react-motion-gallery/slider/parallax` | `sliderParallax(options)` | Parallax slide wrapper. |
869
- | `react-motion-gallery/slider/scale` | `sliderScale(options)` | Scales non-active slides. |
870
- | `react-motion-gallery/slider/fade` | `sliderFade(options)` | Fades non-active slides. |
871
- | `react-motion-gallery/slider/crossfade` | `sliderCrossfade(options)` | Enables crossfade-aware control navigation. |
872
- | `react-motion-gallery/slider/fullscreen` | `sliderFullscreen()` | Bridges a `GalleryCore layout="slider"` slider to fullscreen. |
873
- | `react-motion-gallery/slider/loading` | `sliderLoading(options)` | Basic custom loading overlay. Prefer `SliderSkeleton` for structured skeleton and restore. |
905
+ | Import | Factory | Notes |
906
+ | ----------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------ |
907
+ | `react-motion-gallery/slider/arrows` | `sliderArrows(options)` | Previous/next arrows. |
908
+ | `react-motion-gallery/slider/dots` | `sliderDots(options)` | Pagination dots. |
909
+ | `react-motion-gallery/slider/progress` | `sliderProgress(options)` | Progress bar or custom progress renderer. |
910
+ | `react-motion-gallery/slider/scrollbar` | `sliderScrollbar(options)` | Range-style position control. |
911
+ | `react-motion-gallery/slider/ripple` | `sliderRipple(options)` | Enables ripple feedback for controls that call `createRipple`. |
912
+ | `react-motion-gallery/slider/auto-play` | `sliderAutoPlay(options)` | Timed slide changes. |
913
+ | `react-motion-gallery/slider/auto-scroll` | `sliderAutoScroll(options)` | Timed continuous advancement. |
914
+ | `react-motion-gallery/slider/auto-height` | `sliderAutoHeight(options)` | Measures active slide height and gates slider readiness until measured. |
915
+ | `react-motion-gallery/slider/lazy-load` | `sliderLazyLoad(options)` | Adds lazy media attributes to slide images and videos. |
916
+ | `react-motion-gallery/slider/parallax` | `sliderParallax(options)` | Parallax slide wrapper. |
917
+ | `react-motion-gallery/slider/scale` | `sliderScale(options)` | Scales non-active slides. |
918
+ | `react-motion-gallery/slider/fade` | `sliderFade(options)` | Fades non-active slides. |
919
+ | `react-motion-gallery/slider/crossfade` | `sliderCrossfade(options)` | Enables crossfade-aware control navigation. |
920
+ | `react-motion-gallery/slider/fullscreen` | `sliderFullscreen()` | Bridges a `GalleryCore layout="slider"` slider to fullscreen. |
921
+ | `react-motion-gallery/slider/loading` | `sliderLoading(options)` | Basic custom loading overlay. Prefer `SliderSkeleton` for structured skeleton and restore. |
874
922
 
875
923
  ### Slider loading skeletons
876
924
 
@@ -991,122 +1039,122 @@ export function VariableWidthSkeletonSlider() {
991
1039
 
992
1040
  #### `SliderSkeletonSpec`
993
1041
 
994
- | Field | Type | Notes |
995
- | --- | --- | --- |
996
- | `mode` | `"fit" \| "peek"` | `"peek"` preserves partial next or previous slide visibility in the loading state. |
997
- | `centering` | `"first"` | Adds the leading spacer needed for the first visible slot when using the built-in centered peek skeleton flow. |
998
- | `visibleCount` | `number \| Record<string, number>` | Responsive count of visible skeleton slots. |
999
- | `className` | `string \| undefined` | Applied to the skeleton overlay root. |
1000
- | `style` | `React.CSSProperties \| undefined` | Inline styles for the skeleton overlay root. |
1001
- | `layout` | `SliderSkeletonNode \| undefined` | Structured placeholder layout tree. Use `kind: "slider"` to model slide tracks. |
1002
- | `backgroundColor` | `string \| undefined` | Overrides the shared skeleton background color token. |
1003
- | `radius` | `number \| string \| undefined` | Overrides the shared skeleton radius token. |
1004
- | `shimmer` | `SkeletonShimmer \| undefined` | Shared shimmer settings for the entire skeleton tree. |
1042
+ | Field | Type | Notes |
1043
+ | ----------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------- |
1044
+ | `mode` | `"fit" \| "peek"` | `"peek"` preserves partial next or previous slide visibility in the loading state. |
1045
+ | `centering` | `"first"` | Adds the leading spacer needed for the first visible slot when using the built-in centered peek skeleton flow. |
1046
+ | `visibleCount` | `number \| Record<string, number>` | Responsive count of visible skeleton slots. |
1047
+ | `className` | `string \| undefined` | Applied to the skeleton overlay root. |
1048
+ | `style` | `React.CSSProperties \| undefined` | Inline styles for the skeleton overlay root. |
1049
+ | `layout` | `SliderSkeletonNode \| undefined` | Structured placeholder layout tree. Use `kind: "slider"` to model slide tracks. |
1050
+ | `backgroundColor` | `string \| undefined` | Overrides the shared skeleton background color token. |
1051
+ | `radius` | `number \| string \| undefined` | Overrides the shared skeleton radius token. |
1052
+ | `shimmer` | `SkeletonShimmer \| undefined` | Shared shimmer settings for the entire skeleton tree. |
1005
1053
 
1006
1054
  #### `SliderSkeletonSliderNode`
1007
1055
 
1008
- | Field | Type | Notes |
1009
- | --- | --- | --- |
1010
- | `kind` | `"slider"` | Slider-specific skeleton layout root. |
1011
- | `style` | `SkeletonContainerStyle \| Record<string, SkeletonContainerStyle>` | Track-level container styles such as `gap`, `padding`, `align`, `justify`, `width`, and `maxWidth`. |
1012
- | `count` | `number \| undefined` | Optional explicit slot count for the layout. Falls back to `visibleCount` on the surrounding slider skeleton spec. |
1013
- | `item` | `SkeletonNode` | Default placeholder node rendered in each slot. |
1014
- | `itemWrapStyle` | `SliderSkeletonWrapStyle \| undefined` | Shared wrapper size, margin, border, and box-shadow rules for every slot. Border sizing is border-box. |
1015
- | `slots` | `SliderSkeletonSlot[] \| undefined` | Per-slot overrides for variable widths, heights, aspect ratios, or custom placeholder nodes. |
1016
- | `direction` | `"row" \| "col" \| undefined` | Slot flow direction. `centering: "first"` only affects row layouts. |
1017
- | `children` | `SkeletonNode[] \| undefined` | Optional extra skeleton content rendered after the slider row. It does not affect `--rmg-slider-initial-height` or reserve live layout space. |
1056
+ | Field | Type | Notes |
1057
+ | --------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
1058
+ | `kind` | `"slider"` | Slider-specific skeleton layout root. |
1059
+ | `style` | `SkeletonContainerStyle \| Record<string, SkeletonContainerStyle>` | Track-level container styles such as `gap`, `padding`, `align`, `justify`, `width`, and `maxWidth`. |
1060
+ | `count` | `number \| undefined` | Optional explicit slot count for the layout. Falls back to `visibleCount` on the surrounding slider skeleton spec. |
1061
+ | `item` | `SkeletonNode` | Default placeholder node rendered in each slot. |
1062
+ | `itemWrapStyle` | `SliderSkeletonWrapStyle \| undefined` | Shared wrapper size, margin, border, and box-shadow rules for every slot. Border sizing is border-box. |
1063
+ | `slots` | `SliderSkeletonSlot[] \| undefined` | Per-slot overrides for variable widths, heights, aspect ratios, or custom placeholder nodes. |
1064
+ | `direction` | `"row" \| "col" \| undefined` | Slot flow direction. `centering: "first"` only affects row layouts. |
1065
+ | `children` | `SkeletonNode[] \| undefined` | Optional extra skeleton content rendered after the slider row. It does not affect `--rmg-slider-initial-height` or reserve live layout space. |
1018
1066
 
1019
1067
  #### `SliderSkeletonSlot`
1020
1068
 
1021
- | Field | Type | Notes |
1022
- | --- | --- | --- |
1023
- | `item` | `SkeletonNode \| undefined` | Replaces the base `layout.item` for one slot. |
1069
+ | Field | Type | Notes |
1070
+ | --------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------- |
1071
+ | `item` | `SkeletonNode \| undefined` | Replaces the base `layout.item` for one slot. |
1024
1072
  | `itemWrapStyle` | `SliderSkeletonWrapStyle \| undefined` | Merges on top of the base `layout.itemWrapStyle` for one slot, including wrapper borders and shadows. |
1025
1073
 
1026
1074
  `SkeletonNode` supports these building blocks: `rect`, `square`, `circle`, `text`, `media`, `row`, `col`, and `stack`. `text.barHeight` controls the bar height, `text.lines` controls how many wrapped skeleton rows render for that text block, and `text.lastBarWidth` controls the trailing bar width.
1027
1075
 
1028
1076
  ### Slider motion options
1029
1077
 
1030
- | Option | Type | Default | Notes |
1031
- | --- | --- | --- | --- |
1032
- | `motion.selectDuration` | `number` | `25` | Duration for snapped selection motion. |
1033
- | `motion.freeScrollDuration` | `number` | `43` | Duration for free-scroll settling. |
1034
- | `motion.friction` | `number` | `0.68` | Drag and settling friction. |
1078
+ | Option | Type | Default | Notes |
1079
+ | --------------------------- | -------- | ------- | -------------------------------------- |
1080
+ | `motion.selectDuration` | `number` | `25` | Duration for snapped selection motion. |
1081
+ | `motion.freeScrollDuration` | `number` | `43` | Duration for free-scroll settling. |
1082
+ | `motion.friction` | `number` | `0.68` | Drag and settling friction. |
1035
1083
 
1036
1084
  ### Slider render callback args
1037
1085
 
1038
1086
  #### `ArrowRenderArgs`
1039
1087
 
1040
- | Field | Type | Notes |
1041
- | --- | --- | --- |
1042
- | `ref` | `React.RefObject<HTMLDivElement \| null>` | Attach to the arrow root. |
1043
- | `onClick` | `() => void` | Calls the built-in previous or next action. |
1044
- | `hidden` | `boolean` | `true` when the arrow should not render visually. |
1045
- | `disabled` | `boolean` | `true` when navigation is unavailable. |
1046
- | `createRipple` | `(el: HTMLElement) => void` | Triggers the built-in ripple effect manually. |
1047
- | `className` | `string \| undefined` | Resolved class name for the arrow root. |
1088
+ | Field | Type | Notes |
1089
+ | -------------- | ----------------------------------------- | ------------------------------------------------- |
1090
+ | `ref` | `React.RefObject<HTMLDivElement \| null>` | Attach to the arrow root. |
1091
+ | `onClick` | `() => void` | Calls the built-in previous or next action. |
1092
+ | `hidden` | `boolean` | `true` when the arrow should not render visually. |
1093
+ | `disabled` | `boolean` | `true` when navigation is unavailable. |
1094
+ | `createRipple` | `(el: HTMLElement) => void` | Triggers the built-in ripple effect manually. |
1095
+ | `className` | `string \| undefined` | Resolved class name for the arrow root. |
1048
1096
 
1049
1097
  #### `DotsRenderArgs`
1050
1098
 
1051
- | Field | Type | Notes |
1052
- | --- | --- | --- |
1053
- | `ref` | `React.RefObject<HTMLDivElement \| null>` | Attach to the dots root. |
1054
- | `count` | `number` | Dot count. |
1055
- | `activeIndex` | `number` | Current selected slide index. |
1056
- | `hidden` | `boolean` | `true` when dots should be hidden. |
1057
- | `goTo` | `(index: number) => void` | Navigate to a slide. |
1058
- | `getDotRef` | `(index: number) => (el: HTMLDivElement \| null) => void` | Ref factory for each dot. |
1059
- | `createRipple` | `(el: HTMLElement) => void` | Manual ripple trigger. |
1060
- | `classNameContainer` | `string \| undefined` | Resolved root class name. |
1061
- | `classNameDot` | `string \| undefined` | Resolved dot class name. |
1099
+ | Field | Type | Notes |
1100
+ | -------------------- | --------------------------------------------------------- | ---------------------------------- |
1101
+ | `ref` | `React.RefObject<HTMLDivElement \| null>` | Attach to the dots root. |
1102
+ | `count` | `number` | Dot count. |
1103
+ | `activeIndex` | `number` | Current selected slide index. |
1104
+ | `hidden` | `boolean` | `true` when dots should be hidden. |
1105
+ | `goTo` | `(index: number) => void` | Navigate to a slide. |
1106
+ | `getDotRef` | `(index: number) => (el: HTMLDivElement \| null) => void` | Ref factory for each dot. |
1107
+ | `createRipple` | `(el: HTMLElement) => void` | Manual ripple trigger. |
1108
+ | `classNameContainer` | `string \| undefined` | Resolved root class name. |
1109
+ | `classNameDot` | `string \| undefined` | Resolved dot class name. |
1062
1110
 
1063
1111
  #### `ProgressRenderArgs`
1064
1112
 
1065
- | Field | Type | Notes |
1066
- | --- | --- | --- |
1067
- | `ref` | `React.Ref<HTMLDivElement>` | Attach to the progress root. |
1068
- | `innerRef` | `React.Ref<HTMLDivElement> \| undefined` | Attach to the fill element. |
1069
- | `hidden` | `boolean` | `true` when the progress bar should be hidden. |
1070
- | `progress` | `number` | Progress value from `0` to `1`. |
1071
- | `axis` | `"x" \| "y"` | Fill direction. |
1072
- | `className` | `string \| undefined` | Root class name. |
1073
- | `style` | `React.CSSProperties \| undefined` | Root inline style. |
1074
- | `innerClassName` | `string \| undefined` | Fill class name. |
1075
- | `innerStyle` | `React.CSSProperties \| undefined` | Fill inline style. |
1113
+ | Field | Type | Notes |
1114
+ | ---------------- | ---------------------------------------- | ---------------------------------------------- |
1115
+ | `ref` | `React.Ref<HTMLDivElement>` | Attach to the progress root. |
1116
+ | `innerRef` | `React.Ref<HTMLDivElement> \| undefined` | Attach to the fill element. |
1117
+ | `hidden` | `boolean` | `true` when the progress bar should be hidden. |
1118
+ | `progress` | `number` | Progress value from `0` to `1`. |
1119
+ | `axis` | `"x" \| "y"` | Fill direction. |
1120
+ | `className` | `string \| undefined` | Root class name. |
1121
+ | `style` | `React.CSSProperties \| undefined` | Root inline style. |
1122
+ | `innerClassName` | `string \| undefined` | Fill class name. |
1123
+ | `innerStyle` | `React.CSSProperties \| undefined` | Fill inline style. |
1076
1124
 
1077
1125
  ### `SliderHandle` methods
1078
1126
 
1079
- | Method | Signature | Notes |
1080
- | --- | --- | --- |
1081
- | `centerSlider` | `() => void` | Re-centers the slider after layout changes. |
1082
- | `getIndex` | `() => number` | Current active slide index. |
1083
- | `setIndex` | `(i: number, mode?: IndexMode) => void` | Jumps or animates to a slide. |
1084
- | `subscribeIndex` | `(fn: () => void) => () => void` | Subscribes to index changes. |
1085
- | `slideIndexForCell` | `(cellIndex: number) => number` | Maps a cell index to its slide index when using grouped cells. |
1086
- | `getRootNode` | `() => HTMLElement \| null` | Outer slider root. |
1087
- | `getContainerNode` | `() => HTMLElement \| null` | Moving slide container. |
1088
- | `getSlideNodes` | `() => HTMLElement[]` | Current slide elements. |
1089
- | `getViewportNode` | `() => HTMLDivElement \| null` | Scroll viewport. |
1090
- | `onSlidesBuilt` | `(cb: (nodes: HTMLElement[]) => void) => () => void` | Runs when slide nodes are ready. |
1091
- | `whenSlidesBuilt` | `() => Promise<HTMLElement[]>` | Promise form of `onSlidesBuilt`. |
1092
- | `isSlidesBuilt` | `() => boolean` | `true` once the slide list is ready. |
1093
- | `onReady` | `(cb: (nodes: HTMLElement[]) => void) => () => void` | Runs when the slider has built, measured, committed its index, and all plugin ready gates have cleared. |
1094
- | `whenReady` | `() => Promise<HTMLElement[]>` | Promise form of `onReady`. |
1095
- | `isReady` | `() => boolean` | `true` once the settled slider ready signal has fired. |
1096
- | `scrollNext` | `(mode?: IndexMode) => void` | Advances one step. |
1097
- | `scrollPrev` | `(mode?: IndexMode) => void` | Moves backward one step. |
1098
- | `canScrollNext` | `() => boolean` | Whether next navigation is available. |
1099
- | `canScrollPrev` | `() => boolean` | Whether previous navigation is available. |
1100
- | `scrollProgress` | `() => number` | Current progress from `0` to `1`. |
1101
- | `cellsInView` | `() => number[]` | Canonical cell indexes currently visible. |
1102
- | `append` | `(nodes: ReactNode \| ReactNode[]) => number` | Appends nodes and returns the new total count. |
1103
- | `prepend` | `(nodes: ReactNode \| ReactNode[]) => number` | Prepends nodes and returns the new total count. |
1104
- | `insert` | `(index: number, nodes: ReactNode \| ReactNode[]) => number` | Inserts nodes and returns the new total count. |
1105
- | `remove` | `(indexOrPredicate: number \| ((i: number) => boolean)) => number` | Removes items and returns the new total count. |
1106
- | `replace` | `(index: number, node: ReactNode) => void` | Replaces a node at an index. |
1107
- | `setItems` | `(nodes: ReactNode[]) => number` | Replaces all nodes and returns the new total count. |
1108
- | `onIndexChange` | `(cb: (i: number, meta: { mode: IndexMode }) => void) => () => void` | Subscribes to index changes. |
1109
- | `getInternals` | `() => { slides, slider, visibleImages, selectedIndex, sliderX, sliderVelocity, isWrapping }` | Low-level internals used by fullscreen and advanced sync code. |
1127
+ | Method | Signature | Notes |
1128
+ | ------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
1129
+ | `centerSlider` | `() => void` | Re-centers the slider after layout changes. |
1130
+ | `getIndex` | `() => number` | Current active slide index. |
1131
+ | `setIndex` | `(i: number, mode?: IndexMode) => void` | Jumps or animates to a slide. |
1132
+ | `subscribeIndex` | `(fn: () => void) => () => void` | Subscribes to index changes. |
1133
+ | `slideIndexForCell` | `(cellIndex: number) => number` | Maps a cell index to its slide index when using grouped cells. |
1134
+ | `getRootNode` | `() => HTMLElement \| null` | Outer slider root. |
1135
+ | `getContainerNode` | `() => HTMLElement \| null` | Moving slide container. |
1136
+ | `getSlideNodes` | `() => HTMLElement[]` | Current slide elements. |
1137
+ | `getViewportNode` | `() => HTMLDivElement \| null` | Scroll viewport. |
1138
+ | `onSlidesBuilt` | `(cb: (nodes: HTMLElement[]) => void) => () => void` | Runs when slide nodes are ready. |
1139
+ | `whenSlidesBuilt` | `() => Promise<HTMLElement[]>` | Promise form of `onSlidesBuilt`. |
1140
+ | `isSlidesBuilt` | `() => boolean` | `true` once the slide list is ready. |
1141
+ | `onReady` | `(cb: (nodes: HTMLElement[]) => void) => () => void` | Runs when the slider has built, measured, committed its index, and all plugin ready gates have cleared. |
1142
+ | `whenReady` | `() => Promise<HTMLElement[]>` | Promise form of `onReady`. |
1143
+ | `isReady` | `() => boolean` | `true` once the settled slider ready signal has fired. |
1144
+ | `scrollNext` | `(mode?: IndexMode) => void` | Advances one step. |
1145
+ | `scrollPrev` | `(mode?: IndexMode) => void` | Moves backward one step. |
1146
+ | `canScrollNext` | `() => boolean` | Whether next navigation is available. |
1147
+ | `canScrollPrev` | `() => boolean` | Whether previous navigation is available. |
1148
+ | `scrollProgress` | `() => number` | Current progress from `0` to `1`. |
1149
+ | `cellsInView` | `() => number[]` | Canonical cell indexes currently visible. |
1150
+ | `append` | `(nodes: ReactNode \| ReactNode[]) => number` | Appends nodes and returns the new total count. |
1151
+ | `prepend` | `(nodes: ReactNode \| ReactNode[]) => number` | Prepends nodes and returns the new total count. |
1152
+ | `insert` | `(index: number, nodes: ReactNode \| ReactNode[]) => number` | Inserts nodes and returns the new total count. |
1153
+ | `remove` | `(indexOrPredicate: number \| ((i: number) => boolean)) => number` | Removes items and returns the new total count. |
1154
+ | `replace` | `(index: number, node: ReactNode) => void` | Replaces a node at an index. |
1155
+ | `setItems` | `(nodes: ReactNode[]) => number` | Replaces all nodes and returns the new total count. |
1156
+ | `onIndexChange` | `(cb: (i: number, meta: { mode: IndexMode }) => void) => () => void` | Subscribes to index changes. |
1157
+ | `getInternals` | `() => { slides, slider, visibleImages, selectedIndex, sliderX, sliderVelocity, isWrapping }` | Low-level internals used by fullscreen and advanced sync code. |
1110
1158
 
1111
1159
  ### `createSliderIndexChannel`
1112
1160
 
@@ -1126,16 +1174,16 @@ export function SharedIndexSlider() {
1126
1174
  }
1127
1175
  ```
1128
1176
 
1129
- | Method | Signature | Notes |
1130
- | --- | --- | --- |
1131
- | `createSliderIndexChannel` | `(initialIndex = 0, initialMode = "animated") => SliderIndexChannel` | Creates a shared index event bus. |
1132
- | `get` | `() => { index: number; mode: IndexMode }` | Reads the stored index and mode. |
1133
- | `set` | `(next: number, mode?: IndexMode, opts?: { silent?: boolean }) => void` | Sets the current index and emits a `"set"` event unless silenced. |
1134
- | `bump` | `(delta: number, mode?: IndexMode, opts?: { silent?: boolean }) => void` | Emits a relative index change event. |
1135
- | `subscribe` | `(fn: () => void) => () => void` | Subscribes to channel updates. |
1136
- | `onEvent` | `(fn: (ev: IndexEvent) => void) => () => void` | Receives the last `"set"` or `"bump"` event payload. |
1137
- | `onBasePointerDown` | `(fn: () => void) => () => void` | Subscribes to base slider pointer-down events. |
1138
- | `emitBasePointerDown` | `() => void` | Broadcasts a pointer-down event to subscribers. |
1177
+ | Method | Signature | Notes |
1178
+ | -------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------- |
1179
+ | `createSliderIndexChannel` | `(initialIndex = 0, initialMode = "animated") => SliderIndexChannel` | Creates a shared index event bus. |
1180
+ | `get` | `() => { index: number; mode: IndexMode }` | Reads the stored index and mode. |
1181
+ | `set` | `(next: number, mode?: IndexMode, opts?: { silent?: boolean }) => void` | Sets the current index and emits a `"set"` event unless silenced. |
1182
+ | `bump` | `(delta: number, mode?: IndexMode, opts?: { silent?: boolean }) => void` | Emits a relative index change event. |
1183
+ | `subscribe` | `(fn: () => void) => () => void` | Subscribes to channel updates. |
1184
+ | `onEvent` | `(fn: (ev: IndexEvent) => void) => () => void` | Receives the last `"set"` or `"bump"` event payload. |
1185
+ | `onBasePointerDown` | `(fn: () => void) => () => void` | Subscribes to base slider pointer-down events. |
1186
+ | `emitBasePointerDown` | `() => void` | Broadcasts a pointer-down event to subscribers. |
1139
1187
 
1140
1188
  ### ThumbnailSlider
1141
1189
 
@@ -1190,74 +1238,74 @@ The component forwards a ref to its outer thumbnail shell.
1190
1238
 
1191
1239
  ### ThumbnailSlider component props
1192
1240
 
1193
- | Option | Type | Default | Notes |
1194
- | --- | --- | --- | --- |
1195
- | `children` | `React.ReactNode` | `—` | Thumbnail nodes rendered in order. Overrides `options.children` when both are provided. |
1196
- | `options` | `ThumbnailsOptions` | `—` | Base thumbnail configuration object. |
1197
- | `indexChannel` | `SliderIndexChannel` | internal channel | Share the same channel as a base `Slider` to keep selection in sync. |
1198
- | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Used to resolve `layout.position` and responsive loading counts. |
1199
- | `onThumbnailClick` | `(index: number) => void` | `—` | Fired when a thumbnail click publishes a selection to the shared channel. |
1200
- | `onReadyChange` | `(ready: boolean) => void` | `—` | Fired when the thumbnail rail finishes or re-enters its loading/layout cycle. |
1201
- | `direction` | `"ltr" \| "rtl"` | `"ltr"` | Affects horizontal arrow direction and RTL scroll behavior. |
1241
+ | Option | Type | Default | Notes |
1242
+ | ------------------ | -------------------------- | --------------------------------------------- | --------------------------------------------------------------------------------------- |
1243
+ | `children` | `React.ReactNode` | `—` | Thumbnail nodes rendered in order. Overrides `options.children` when both are provided. |
1244
+ | `options` | `ThumbnailsOptions` | `—` | Base thumbnail configuration object. |
1245
+ | `indexChannel` | `SliderIndexChannel` | internal channel | Share the same channel as a base `Slider` to keep selection in sync. |
1246
+ | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Used to resolve `layout.position` and responsive loading counts. |
1247
+ | `onThumbnailClick` | `(index: number) => void` | `—` | Fired when a thumbnail click publishes a selection to the shared channel. |
1248
+ | `onReadyChange` | `(ready: boolean) => void` | `—` | Fired when the thumbnail rail finishes or re-enters its loading/layout cycle. |
1249
+ | `direction` | `"ltr" \| "rtl"` | `"ltr"` | Affects horizontal arrow direction and RTL scroll behavior. |
1202
1250
 
1203
1251
  ### Thumbnail layout and scroll options
1204
1252
 
1205
- | Option | Type | Default | Notes |
1206
- | --- | --- | --- | --- |
1207
- | `children` | `React.ReactNode` | `—` | Fallback thumbnail content when component children are omitted. |
1208
- | `layout.position` | `ResponsivePosition` | `"bottom"` | Thumbnail rail position: `"top"`, `"right"`, `"bottom"`, or `"left"`. |
1209
- | `layout.gap` | `number` | `8` | Gap between thumbnails. |
1210
- | `layout.center` | `boolean` | `false` | Centers the overall rail content within its container when possible. |
1211
- | `layout.thumbnail.width` | `number \| string` | `—` | Width for each thumbnail item. |
1212
- | `layout.thumbnail.height` | `number \| string` | `—` | Height for each thumbnail item. |
1213
- | `layout.container.width` | `number \| string` | `—` | Width for the outer thumbnail container. |
1214
- | `layout.container.height` | `number \| string` | `—` | Height for the outer thumbnail container. |
1215
- | `scroll.freeScroll` | `boolean` | `true` | Enables drag or wheel movement without strict snapping. |
1216
- | `scroll.groupCells` | `boolean` | `false` | Pages the rail by grouped thumbnail cells. |
1217
- | `scroll.loop` | `boolean` | `false` | Wraps thumbnails at the ends. |
1218
- | `scroll.skipSnaps` | `boolean` | `false` | Allows momentum to skip snap points. |
1219
- | `scroll.centerActiveThumb` | `boolean` | `false` | Repositions the rail to keep the active thumbnail centered. |
1253
+ | Option | Type | Default | Notes |
1254
+ | -------------------------- | -------------------- | ---------- | --------------------------------------------------------------------- |
1255
+ | `children` | `React.ReactNode` | `—` | Fallback thumbnail content when component children are omitted. |
1256
+ | `layout.position` | `ResponsivePosition` | `"bottom"` | Thumbnail rail position: `"top"`, `"right"`, `"bottom"`, or `"left"`. |
1257
+ | `layout.gap` | `number` | `8` | Gap between thumbnails. |
1258
+ | `layout.center` | `boolean` | `false` | Centers the overall rail content within its container when possible. |
1259
+ | `layout.thumbnail.width` | `number \| string` | `—` | Width for each thumbnail item. |
1260
+ | `layout.thumbnail.height` | `number \| string` | `—` | Height for each thumbnail item. |
1261
+ | `layout.container.width` | `number \| string` | `—` | Width for the outer thumbnail container. |
1262
+ | `layout.container.height` | `number \| string` | `—` | Height for the outer thumbnail container. |
1263
+ | `scroll.freeScroll` | `boolean` | `true` | Enables drag or wheel movement without strict snapping. |
1264
+ | `scroll.groupCells` | `boolean` | `false` | Pages the rail by grouped thumbnail cells. |
1265
+ | `scroll.loop` | `boolean` | `false` | Wraps thumbnails at the ends. |
1266
+ | `scroll.skipSnaps` | `boolean` | `false` | Allows momentum to skip snap points. |
1267
+ | `scroll.centerActiveThumb` | `boolean` | `false` | Repositions the rail to keep the active thumbnail centered. |
1220
1268
 
1221
1269
  `ResponsivePosition` accepts a single side, an array, or a breakpoint map. For arrays, the first entry is used.
1222
1270
 
1223
1271
  ### Thumbnail element, control, and motion options
1224
1272
 
1225
- | Option | Type | Default | Notes |
1226
- | --- | --- | --- | --- |
1227
- | `elements.container` | `ElementStyle` | `—` | Class and inline style for the outer thumbnail container. |
1228
- | `elements.thumbnail` | `ElementStyle` | `—` | Class and inline style for each thumbnail item shell. |
1229
- | `controls.enabled` | `boolean` | `false` | Shows previous and next arrows when the rail overflows. |
1230
- | `controls.arrow` | `ElementStyle` | `—` | Shared arrow class and style. |
1231
- | `controls.prev` | `ElementStyle` | `—` | Previous-arrow override. |
1232
- | `controls.next` | `ElementStyle` | `—` | Next-arrow override. |
1233
- | `controls.render` | `(args: ArrowRenderArgs & { dir: "prev" \| "next" }) => ReactNode` | `—` | Custom renderer for both thumbnail arrows. |
1234
- | `controls.renderPrev` | `(args: ArrowRenderArgs) => ReactNode` | `—` | Custom previous arrow. |
1235
- | `controls.renderNext` | `(args: ArrowRenderArgs) => ReactNode` | `—` | Custom next arrow. |
1236
- | `controls.ripple.enabled` | `boolean` | `true` | Enables ripple feedback for thumbnail arrows. |
1237
- | `controls.ripple.className` | `string` | `—` | Custom ripple class for the arrow feedback element. |
1238
- | `motion.selectDuration` | `number` | `25` | Duration for snapped thumbnail selection motion. |
1239
- | `motion.freeScrollDuration` | `number` | `43` | Duration for free-scroll settling. |
1240
- | `motion.friction` | `number` | `0.68` | Drag and settling friction. |
1241
- | `breakpointMap` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Override map used for responsive thumbnail positions and loading counts. |
1273
+ | Option | Type | Default | Notes |
1274
+ | --------------------------- | ------------------------------------------------------------------ | --------------------------------------------- | ------------------------------------------------------------------------ |
1275
+ | `elements.container` | `ElementStyle` | `—` | Class and inline style for the outer thumbnail container. |
1276
+ | `elements.thumbnail` | `ElementStyle` | `—` | Class and inline style for each thumbnail item shell. |
1277
+ | `controls.enabled` | `boolean` | `false` | Shows previous and next arrows when the rail overflows. |
1278
+ | `controls.arrow` | `ElementStyle` | `—` | Shared arrow class and style. |
1279
+ | `controls.prev` | `ElementStyle` | `—` | Previous-arrow override. |
1280
+ | `controls.next` | `ElementStyle` | `—` | Next-arrow override. |
1281
+ | `controls.render` | `(args: ArrowRenderArgs & { dir: "prev" \| "next" }) => ReactNode` | `—` | Custom renderer for both thumbnail arrows. |
1282
+ | `controls.renderPrev` | `(args: ArrowRenderArgs) => ReactNode` | `—` | Custom previous arrow. |
1283
+ | `controls.renderNext` | `(args: ArrowRenderArgs) => ReactNode` | `—` | Custom next arrow. |
1284
+ | `controls.ripple.enabled` | `boolean` | `true` | Enables ripple feedback for thumbnail arrows. |
1285
+ | `controls.ripple.className` | `string` | `—` | Custom ripple class for the arrow feedback element. |
1286
+ | `motion.selectDuration` | `number` | `25` | Duration for snapped thumbnail selection motion. |
1287
+ | `motion.freeScrollDuration` | `number` | `43` | Duration for free-scroll settling. |
1288
+ | `motion.friction` | `number` | `0.68` | Drag and settling friction. |
1289
+ | `breakpointMap` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Override map used for responsive thumbnail positions and loading counts. |
1242
1290
 
1243
1291
  ### Thumbnail transition options
1244
1292
 
1245
- | Option | Type | Default | Notes |
1246
- | --- | --- | --- | --- |
1247
- | `transitions.loading.enabled` | `boolean` | `true` | Enables the thumbnail loading layer. |
1248
- | `transitions.loading.force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `false` | Forces the loading layer to remain visible. Set `showContent: true` to preview the real thumbnails under the skeleton, and tune the loading overlay with `skeletonOpacity`. |
1249
- | `transitions.loading.skeletonCount` | `number \| Record<string, number>` | `—` | Responsive count for the built-in loading placeholders. |
1250
- | `transitions.loading.mode` | `"fit" \| "peek"` | `"peek"` | `"peek"` keeps fixed-size thumbnail placeholders when width or height is explicitly set; `"fit"` divides the rail evenly across the visible count. |
1251
- | `transitions.loading.elements.container` | `ElementStyle` | `—` | Class and inline style for the built-in loading overlay container. |
1252
- | `transitions.loading.elements.row` | `ElementStyle` | `—` | Class and inline style for the built-in skeleton row or column wrapper. |
1253
- | `transitions.loading.elements.thumbnail` | `ElementStyle` | `—` | Class and inline style for each built-in thumbnail placeholder. |
1254
- | `transitions.loading.renderLoading` | `({ count }) => ReactNode` | `—` | Replaces the built-in thumbnail loading skeleton and receives the resolved responsive count. |
1255
- | `transitions.loading.timing.exitMs` | `number` | `600` | Keeps the thumbnail loading layer mounted for this long after exit starts. |
1256
- | `transitions.loading.timing.minVisibleMs` | `number` | `220` | Minimum time the loading layer stays visible before exit can begin. |
1257
- | `reveal.renderReveal` | `({ active, containerProps }, inner) => ReactNode` | `—` | Custom reveal wrapper for the thumbnail rail. |
1258
- | `reveal.staggerMs` | `number` | `40` | Delay between thumbnail reveal fades. |
1259
- | `reveal.durationMs` | `number` | `300` | Reveal fade duration. |
1260
- | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
1293
+ | Option | Type | Default | Notes |
1294
+ | ----------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1295
+ | `transitions.loading.enabled` | `boolean` | `true` | Enables the thumbnail loading layer. |
1296
+ | `transitions.loading.force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `false` | Forces the loading layer to remain visible. Set `showContent: true` to preview the real thumbnails under the skeleton, and tune the loading overlay with `skeletonOpacity`. |
1297
+ | `transitions.loading.skeletonCount` | `number \| Record<string, number>` | `—` | Responsive count for the built-in loading placeholders. |
1298
+ | `transitions.loading.mode` | `"fit" \| "peek"` | `"peek"` | `"peek"` keeps fixed-size thumbnail placeholders when width or height is explicitly set; `"fit"` divides the rail evenly across the visible count. |
1299
+ | `transitions.loading.elements.container` | `ElementStyle` | `—` | Class and inline style for the built-in loading overlay container. |
1300
+ | `transitions.loading.elements.row` | `ElementStyle` | `—` | Class and inline style for the built-in skeleton row or column wrapper. |
1301
+ | `transitions.loading.elements.thumbnail` | `ElementStyle` | `—` | Class and inline style for each built-in thumbnail placeholder. |
1302
+ | `transitions.loading.renderLoading` | `({ count }) => ReactNode` | `—` | Replaces the built-in thumbnail loading skeleton and receives the resolved responsive count. |
1303
+ | `transitions.loading.timing.exitMs` | `number` | `600` | Keeps the thumbnail loading layer mounted for this long after exit starts. |
1304
+ | `transitions.loading.timing.minVisibleMs` | `number` | `220` | Minimum time the loading layer stays visible before exit can begin. |
1305
+ | `reveal.renderReveal` | `({ active, containerProps }, inner) => ReactNode` | `—` | Custom reveal wrapper for the thumbnail rail. |
1306
+ | `reveal.staggerMs` | `number` | `40` | Delay between thumbnail reveal fades. |
1307
+ | `reveal.durationMs` | `number` | `300` | Reveal fade duration. |
1308
+ | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
1261
1309
 
1262
1310
  `transitions.loading.elements.*` only applies to the built-in thumbnail skeleton. If you provide `transitions.loading.renderLoading`, you fully own the loading markup instead.
1263
1311
 
@@ -1269,12 +1317,12 @@ For thumbnails, `transitions.loading.timing.exitMs` controls both the mounted ex
1269
1317
 
1270
1318
  `ThumbnailSlider` creates and starts this bridge for you internally when you pass `indexChannel`. Reach for `createThumbnailSyncBridge()` only when you need to wire a local thumbnail rail to an external slider channel manually.
1271
1319
 
1272
- | Method | Signature | Notes |
1273
- | --- | --- | --- |
1320
+ | Method | Signature | Notes |
1321
+ | --------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
1274
1322
  | `createThumbnailSyncBridge` | `(args: { localChannel, externalChannel?, clampIndex? }) => ThumbnailSyncBridge` | Creates a bridge between local thumbnail state and an optional external slider channel. |
1275
- | `start` | `() => () => void` | Starts syncing and returns a cleanup function. |
1276
- | `stop` | `() => void` | Stops syncing without disposing the channels. |
1277
- | `publishThumbnailClick` | `(index: number, mode?: IndexMode) => void` | Publishes a thumbnail click to the external slider channel. |
1323
+ | `start` | `() => () => void` | Starts syncing and returns a cleanup function. |
1324
+ | `stop` | `() => void` | Stops syncing without disposing the channels. |
1325
+ | `publishThumbnailClick` | `(index: number, mode?: IndexMode) => void` | Publishes a thumbnail click to the external slider channel. |
1278
1326
 
1279
1327
  ## Grid
1280
1328
 
@@ -1299,41 +1347,42 @@ export function BasicGrid() {
1299
1347
 
1300
1348
  ### Grid component props
1301
1349
 
1302
- | Option | Type | Default | Notes |
1303
- | --- | --- | --- | --- |
1304
- | `children` | `React.ReactNode` | `—` | Grid items rendered in order. Wrap individual cards in `Grid.Item` when they need custom spans or wrapper props. |
1305
- | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Used to resolve responsive columns and gaps. |
1306
- | `gridItemBaseClass` | `string` | `"rmg__grid-item"` | Internal item base class override. |
1307
- | `renderMode` | `"wrap" \| "passthrough"` | `"wrap"` | `wrap` adds an item wrapper; `passthrough` keeps child structure closer to the source node. |
1350
+ | Option | Type | Default | Notes |
1351
+ | ------------------- | ------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
1352
+ | `children` | `React.ReactNode` | `—` | Grid items rendered in order. Wrap individual cards in `Grid.Item` when they need custom spans or wrapper props. |
1353
+ | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Used to resolve responsive columns and gaps. |
1354
+ | `gridItemBaseClass` | `string` | `"rmg__grid-item"` | Internal item base class override. |
1355
+ | `renderMode` | `"wrap" \| "passthrough"` | `"wrap"` | `wrap` adds an item wrapper; `passthrough` keeps child structure closer to the source node. |
1308
1356
 
1309
1357
  ### Grid.Item props
1310
1358
 
1311
1359
  `Grid.Item` is a metadata wrapper. It renders only its children, while Grid reads the wrapper props and applies them to the generated item shell.
1312
1360
 
1313
- | Option | Type | Default | Notes |
1314
- | --- | --- | --- | --- |
1315
- | `children` | `React.ReactNode` | `—` | The grid card content. |
1316
- | `span` | `number \| "full" \| Record<string, number \| "full">` | `1` | Per-item track span. `"full"` renders `grid-column: 1 / -1`; numeric values render `grid-column: span n / span n`. |
1317
- | `className` | `string` | `—` | Extra class name merged onto the grid item wrapper. |
1318
- | `style` | `React.CSSProperties` | `—` | Inline styles merged onto the grid item wrapper. |
1361
+ | Option | Type | Default | Notes |
1362
+ | ----------- | ------------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------ |
1363
+ | `children` | `React.ReactNode` | `—` | The grid card content. |
1364
+ | `span` | `number \| "full" \| Record<string, number \| "full">` | `1` | Per-item track span. `"full"` renders `grid-column: 1 / -1`; numeric values render `grid-column: span n / span n`. |
1365
+ | `className` | `string` | `—` | Extra class name merged onto the grid item wrapper. |
1366
+ | `style` | `React.CSSProperties` | `—` | Inline styles merged onto the grid item wrapper. |
1319
1367
 
1320
1368
  ### Grid options
1321
1369
 
1322
- | Option | Type | Default | Notes |
1323
- | --- | --- | --- | --- |
1324
- | `columns` | `number \| Record<string, number>` | `—` | Fixed responsive column count. When omitted, Grid auto-fits using `minColumnWidth`. |
1325
- | `templateColumns` | `string \| Record<string, string>` | `—` | Explicit `grid-template-columns` value. Takes precedence over `columns` and `minColumnWidth`. |
1326
- | `minColumnWidth` | `number \| string` | `160` | Minimum width used by auto-fit mode. |
1327
- | `gap` | `number \| Record<string, number>` | `8` | Responsive grid gap. |
1328
- | `rootClassName` | `string` | `—` | Class name for the grid root. |
1329
- | `itemClassName` | `string` | `—` | Class name added to each wrapped grid item. |
1330
- | `fullscreenTrigger` | `"item" \| "media"` | `"media"` | When `gridFullscreen()` is active, opens fullscreen from the clicked media node or the entire item shell. |
1331
- | `plugins` | `GridPlugin[]` | `[]` | Explicit first-party Grid features such as lazy-load and fullscreen. |
1332
- | `reveal.renderReveal` | `({ active, containerProps }, content) => ReactNode` | `—` | Custom reveal wrapper. |
1333
- | `reveal.staggerMs` | `number` | `60` | Reveal stagger for the fade-in. |
1334
- | `reveal.durationMs` | `number` | `600` | Reveal fade duration. |
1335
- | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
1336
- | `reveal.staggerLimit` | `number` | `—` | Optional cap on how many items stagger. |
1370
+ | Option | Type | Default | Notes |
1371
+ | --------------------- | ---------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1372
+ | `columns` | `number \| Record<string, number>` | `—` | Fixed responsive column count. When omitted, Grid auto-fits using `minColumnWidth`. |
1373
+ | `templateColumns` | `string \| Record<string, string>` | `—` | Explicit `grid-template-columns` value. Takes precedence over `columns` and `minColumnWidth`. |
1374
+ | `minColumnWidth` | `number \| string` | `160` | Minimum width used by auto-fit mode. |
1375
+ | `gap` | `number \| Record<string, number>` | `8` | Responsive grid gap. |
1376
+ | `rootClassName` | `string` | `—` | Class name for the grid root. |
1377
+ | `itemClassName` | `string` | `—` | Class name added to each wrapped grid item. |
1378
+ | `fullscreenTrigger` | `"item" \| "media"` | `"media"` | When `gridFullscreen()` is active, opens fullscreen from the clicked media node or the entire item shell. |
1379
+ | `plugins` | `GridPlugin[]` | `[]` | Explicit first-party Grid features such as lazy-load, fullscreen, pagination, load-more, infinite scroll, and virtualization. |
1380
+ | `loading` | `GridLoadingOptions` | `—` | Core per-item skeleton/reveal lifecycle. Supports `skeleton`, `cache`, `active`, `count`, media decode waiting, forced compare mode, skeleton timing, and reveal identity memory. |
1381
+ | `reveal.staggerMs` | `number` | `60` | Reveal stagger for the fade-in. |
1382
+ | `reveal.durationMs` | `number` | `600` | Reveal fade duration. |
1383
+ | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
1384
+ | `reveal.staggerLimit` | `number` | `—` | Optional cap on how many items stagger. |
1385
+ | `reveal.disabled` | `boolean` | `false` | Disables the per-item reveal animation when `loading` is enabled. |
1337
1386
 
1338
1387
  ### Grid plugins
1339
1388
 
@@ -1343,17 +1392,35 @@ Import Grid plugins from their own subpaths and pass them to `plugins`.
1343
1392
  import { Grid } from "react-motion-gallery/grid";
1344
1393
  import { gridLazyLoad } from "react-motion-gallery/grid/lazy-load";
1345
1394
  import { gridFullscreen } from "react-motion-gallery/grid/fullscreen";
1395
+ import { useGridPagination } from "react-motion-gallery/grid/pagination";
1396
+ import { useGridLoadMore } from "react-motion-gallery/grid/load-more";
1397
+ import { useGridInfiniteScroll } from "react-motion-gallery/grid/infinite-scroll";
1398
+ import { gridVirtualization } from "react-motion-gallery/grid/virtualization";
1399
+
1400
+ function ProductGrid({ items, total }) {
1401
+ const pagination = useGridPagination({ pageSize: 12, total });
1346
1402
 
1347
- <Grid plugins={[gridLazyLoad({ spinner: true }), gridFullscreen()]}>{items}</Grid>;
1403
+ return (
1404
+ <Grid plugins={[pagination.plugin, gridLazyLoad({ spinner: true }), gridFullscreen()]}>
1405
+ {items}
1406
+ </Grid>
1407
+ );
1408
+ }
1348
1409
  ```
1349
1410
 
1350
- | Import | Factory | Notes |
1351
- | --- | --- | --- |
1352
- | `react-motion-gallery/grid/lazy-load` | `gridLazyLoad(options)` | Rewrites trackable image `src` values into `data-rmg-lazy-src`, reveals them on viewport intersection, then fades them in after decode and spinner exit. |
1353
- | `react-motion-gallery/grid/fullscreen` | `gridFullscreen()` | Opens `GalleryCore` fullscreen from Grid items without adding fullscreen code to the default grid import. |
1411
+ | Import | Factory | Notes |
1412
+ | ------------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
1413
+ | `react-motion-gallery/grid/lazy-load` | `gridLazyLoad(options)` | Rewrites trackable image `src` values into `data-rmg-lazy-src`, reveals them on viewport intersection, then fades them in after decode and spinner exit. |
1414
+ | `react-motion-gallery/grid/fullscreen` | `gridFullscreen()` | Opens `GalleryCore` fullscreen from Grid items without adding fullscreen code to the default grid import. |
1415
+ | `react-motion-gallery/grid/pagination` | `gridPagination(options)` | Windows children by `pageIndex` and `pageSize`; `useGridPagination()` also returns page state and `GridPaginationControls`. |
1416
+ | `react-motion-gallery/grid/load-more` | `gridLoadMore(options)` | Windows children by `visibleCount`; `useGridLoadMore()` owns and increments the visible count. |
1417
+ | `react-motion-gallery/grid/infinite-scroll` | `gridInfiniteScroll(options)` | Renders a sentinel after the grid root and calls `onLoadMore` when it intersects. |
1418
+ | `react-motion-gallery/grid/virtualization` | `gridVirtualization(options)` | Runs after pagination/load-more and mounts only rows near the viewport, using row spacers and measured row heights. |
1354
1419
 
1355
1420
  `gridLazyLoad()` enables lazy loading by default. Pass `{ enabled: false }` to make the plugin inert.
1356
1421
 
1422
+ Grid pagination, load-more, infinite-scroll, and virtualization APIs are documented in the shared data-plugin sections after Entries. Pagination and load-more window the child list before CSS grid layout, so hidden items do not reserve tracks. In `"server"` mode the supplied children and `fullscreenItems` are treated as the current server window.
1423
+
1357
1424
  Grid fullscreen behavior is provided by `GalleryCore`, `useFullscreenController`, and the opt-in `gridFullscreen()` plugin. Grid itself does not expose a ref-based imperative API.
1358
1425
 
1359
1426
  Wrap a card in `Grid.Item` when it should span tracks or needs wrapper styling:
@@ -1391,17 +1458,17 @@ Use `templateColumns` when the tracks themselves need custom proportions:
1391
1458
  </Grid>
1392
1459
  ```
1393
1460
 
1394
- Grid no longer owns loading UI. Use `useGridReady` and wrap Grid with `GridSkeleton`, the same composition pattern used by Slider and Masonry.
1461
+ Grid owns a per-item loading lifecycle through `loading`. Each item paints hidden, enters the viewport, waits for its images/video and lazy-loaded media to be ready, exits its skeleton, then reveals. Below-fold items reveal when they enter view and do not block the initial grid ready state.
1395
1462
 
1396
- Grid skeletons live in `react-motion-gallery/skeleton/grid`. Their `text` nodes use the same wrapped-line treatment as slider skeletons, including responsive `barHeight` and `lines` maps plus the configurable trailing `lastBarWidth`.
1463
+ Grid skeleton specs live in `react-motion-gallery/skeleton/grid`. Their `text` nodes use the same wrapped-line treatment as slider skeletons, including responsive `barHeight` and `lines` maps plus the configurable trailing `lastBarWidth`.
1397
1464
 
1398
- Grid skeletons inherit real item spans by default. Slot overrides in the Skeleton layout can change individual placeholder nodes or wrapper styles without losing the span applied by `Grid.Item`.
1465
+ Grid skeleton slots inherit real item spans by default. Slot overrides in the skeleton layout can change individual placeholder nodes or wrapper styles without losing the span applied by `Grid.Item`.
1399
1466
 
1400
- When Grid is wrapped in `GridSkeleton`, `GridSkeleton.timing.exitMs` controls both how long the loading layer stays mounted after exit starts and its opacity transition, and the real grid reveal begins as soon as exit starts.
1467
+ Use `loading.active` to hold reveals while async data is pending, `loading.timing.exitMs` to control the per-item skeleton opacity fade-out duration, and `loading.keepSkeletonMounted` when a settled skeleton layer should stay mounted at opacity 0 so a later loading transition can fade it back in smoothly. Set `rememberRevealed: false` for server-paged slots that should animate again when an item leaves and later re-enters the current data window. If an item should keep the same grid slot while its backing data changes, pass `revealKey` on `Grid.Item` or `data-rmg-grid-reveal-key` on a raw child.
1401
1468
 
1402
1469
  ```typescript
1403
- import { Grid, useGridReady } from "react-motion-gallery";
1404
- import { GridSkeleton, type GridSkeletonSpec } from "react-motion-gallery/skeleton/grid";
1470
+ import { Grid } from "react-motion-gallery";
1471
+ import type { GridSkeletonSpec } from "react-motion-gallery/skeleton/grid";
1405
1472
 
1406
1473
  const gridSkeleton: GridSkeletonSpec = {
1407
1474
  radius: 14,
@@ -1416,29 +1483,19 @@ const gridSkeleton: GridSkeletonSpec = {
1416
1483
  };
1417
1484
 
1418
1485
  function GridWithSkeleton({ images }: { images: { src: string; alt: string }[] }) {
1419
- const { ref: gridRef, ready: gridReady } = useGridReady();
1420
-
1421
1486
  return (
1422
- <GridSkeleton
1423
- layout={gridSkeleton}
1424
- ready={gridReady}
1425
- timing={{ minVisibleMs: 220, exitMs: 600 }}
1426
- grid={{
1427
- count: images.length,
1428
- columns: { 0: 1, 640: 2, 960: 3 },
1429
- gap: { 0: 12, 960: 20 },
1487
+ <Grid
1488
+ columns={{ 0: 1, 640: 2, 960: 3 }}
1489
+ gap={{ 0: 12, 960: 20 }}
1490
+ loading={{
1491
+ skeleton: gridSkeleton,
1492
+ timing: { minVisibleMs: 220, exitMs: 600 },
1430
1493
  }}
1431
1494
  >
1432
- <Grid
1433
- ref={gridRef}
1434
- columns={{ 0: 1, 640: 2, 960: 3 }}
1435
- gap={{ 0: 12, 960: 20 }}
1436
- >
1437
- {images.map((image) => (
1438
- <img key={image.src} src={image.src} alt={image.alt} />
1439
- ))}
1440
- </Grid>
1441
- </GridSkeleton>
1495
+ {images.map((image) => (
1496
+ <img key={image.src} src={image.src} alt={image.alt} />
1497
+ ))}
1498
+ </Grid>
1442
1499
  );
1443
1500
  }
1444
1501
  ```
@@ -1479,61 +1536,81 @@ export function BasicMasonry() {
1479
1536
 
1480
1537
  ### Masonry component props
1481
1538
 
1482
- | Option | Type | Default | Notes |
1483
- | --- | --- | --- | --- |
1484
- | `children` | `React.ReactNode` | `—` | Dimensioned masonry items. Each item should be wrapped in `Masonry.Item`. |
1485
- | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Used to resolve responsive columns and gaps. |
1539
+ | Option | Type | Default | Notes |
1540
+ | ------------- | ------------------------ | --------------------------------------------- | ------------------------------------------------------------------------- |
1541
+ | `children` | `React.ReactNode` | `—` | Dimensioned masonry items. Each item should be wrapped in `Masonry.Item`. |
1542
+ | `breakpoints` | `Record<string, number>` | `xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536` | Used to resolve responsive columns and gaps. |
1486
1543
 
1487
1544
  ### Masonry.Item props
1488
1545
 
1489
- | Option | Type | Default | Notes |
1490
- | --- | --- | --- | --- |
1491
- | `children` | `React.ReactNode` | `—` | The masonry card content. |
1492
- | `width` | `number` | `—` | Intrinsic item width used for aspect-ratio layout. |
1493
- | `height` | `number` | `—` | Intrinsic item height used for aspect-ratio layout. |
1494
- | `span` | `number \| "full" \| Record<string, number \| "full">` | `1` | Per-item track span. `"full"` resolves to the active column count and numeric values clamp to the current track count. |
1495
- | `className` | `string` | `—` | Extra class name merged onto the masonry item wrapper. |
1496
- | `style` | `React.CSSProperties` | `—` | Inline styles merged onto the masonry item wrapper. |
1546
+ | Option | Type | Default | Notes |
1547
+ | ----------- | ------------------------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------- |
1548
+ | `children` | `React.ReactNode` | `—` | The masonry card content. |
1549
+ | `width` | `number` | `—` | Intrinsic item width used for aspect-ratio layout. |
1550
+ | `height` | `number` | `—` | Intrinsic item height used for aspect-ratio layout. |
1551
+ | `span` | `number \| "full" \| Record<string, number \| "full">` | `1` | Per-item track span. `"full"` resolves to the active column count and numeric values clamp to the current track count. |
1552
+ | `className` | `string` | `—` | Extra class name merged onto the masonry item wrapper. |
1553
+ | `style` | `React.CSSProperties` | `—` | Inline styles merged onto the masonry item wrapper. |
1497
1554
 
1498
1555
  ### Masonry options
1499
1556
 
1500
- | Option | Type | Default | Notes |
1501
- | --- | --- | --- | --- |
1502
- | `columns` | `number \| Record<string, number>` | `—` | Responsive column count. |
1503
- | `gap` | `number \| Record<string, number>` | `—` | Responsive gap between columns and items. |
1504
- | `placement` | `"balanced" \| "roundRobin" \| "horizontalOrder"` | `"balanced"` | `balanced` packs into the shortest fitting column group, `roundRobin` cycles start columns deterministically, and `horizontalOrder` preserves a stronger left-to-right scan when spans are involved. |
1505
- | `as` | `React.ElementType` | `"div"` | Root HTML element or custom component. |
1506
- | `rootRef` | `React.Ref<HTMLDivElement>` | `—` | Ref to the masonry root. |
1507
- | `classNames.root` | `string` | `—` | Root class name. |
1508
- | `classNames.item` | `string` | `—` | Item class name. |
1509
- | `plugins` | `MasonryPlugin[]` | `[]` | Opt-in light Masonry plugins, currently `masonryFullscreen()` from `react-motion-gallery/masonry/fullscreen`. |
1510
- | `reveal.staggerMs` | `number` | `160` | Reveal stagger for the fade-in. |
1511
- | `reveal.durationMs` | `number` | `600` | Reveal fade duration. |
1512
- | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
1513
- | `reveal.disabled` | `boolean` | `false` | Disables the built-in reveal classes. |
1514
- | `revealReady` | `boolean` | `true` | Holds the reveal until external loading or viewport orchestration is ready. |
1557
+ | Option | Type | Default | Notes |
1558
+ | ------------------- | ------------------------------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1559
+ | `columns` | `number \| Record<string, number>` | `—` | Responsive column count. |
1560
+ | `gap` | `number \| Record<string, number>` | `—` | Responsive gap between columns and items. |
1561
+ | `placement` | `"balanced" \| "roundRobin" \| "horizontalOrder"` | `"balanced"` | `balanced` packs into the shortest fitting column group, `roundRobin` cycles start columns deterministically, and `horizontalOrder` preserves a stronger left-to-right scan when spans are involved. |
1562
+ | `as` | `React.ElementType` | `"div"` | Root HTML element or custom component. |
1563
+ | `rootRef` | `React.Ref<HTMLDivElement>` | `—` | Ref to the masonry root. |
1564
+ | `classNames.root` | `string` | `—` | Root class name. |
1565
+ | `classNames.item` | `string` | `—` | Item class name. |
1566
+ | `plugins` | `MasonryPlugin[]` | `[]` | Opt-in Masonry plugins for fullscreen, lazy-load, pagination, load-more, infinite scroll, and virtualization. |
1567
+ | `reveal.staggerMs` | `number` | `160` | Reveal stagger for the fade-in. |
1568
+ | `reveal.durationMs` | `number` | `600` | Reveal fade duration. |
1569
+ | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
1570
+ | `reveal.disabled` | `boolean` | `false` | Disables the built-in reveal classes. |
1571
+ | `revealReady` | `boolean` | `true` | Holds the reveal until external loading or viewport orchestration is ready. |
1515
1572
 
1516
1573
  ### Masonry plugins
1517
1574
 
1518
- Import Masonry plugins from their own subpaths and pass them to `plugins`. The default dimensioned core supports the fullscreen bridge through an opt-in subpath.
1575
+ Import Masonry plugins from their own subpaths and pass them to `plugins`. The data plugin factories work across both the default dimensioned Masonry and measured Masonry.
1519
1576
 
1520
1577
  ```typescript
1521
1578
  import { GalleryCore } from "react-motion-gallery/core";
1522
1579
  import { Masonry } from "react-motion-gallery/masonry";
1523
1580
  import { masonryFullscreen } from "react-motion-gallery/masonry/fullscreen";
1581
+ import { useMasonryPagination } from "react-motion-gallery/masonry/pagination";
1582
+ import { useMasonryLoadMore } from "react-motion-gallery/masonry/load-more";
1583
+ import { useMasonryInfiniteScroll } from "react-motion-gallery/masonry/infinite-scroll";
1584
+ import { masonryVirtualization } from "react-motion-gallery/masonry/virtualization";
1585
+
1586
+ function ProductMasonry({ children, items }) {
1587
+ const loadMore = useMasonryLoadMore({
1588
+ initialVisibleCount: 12,
1589
+ pageSize: 12,
1590
+ total: items.length,
1591
+ });
1524
1592
 
1525
- <GalleryCore layout="masonry" fullscreenItems={items}>
1526
- <Masonry plugins={[masonryFullscreen()]}>{children}</Masonry>
1527
- </GalleryCore>;
1593
+ return (
1594
+ <GalleryCore layout="masonry" fullscreenItems={items}>
1595
+ <Masonry plugins={[loadMore.plugin, masonryFullscreen()]}>{children}</Masonry>
1596
+ </GalleryCore>
1597
+ );
1598
+ }
1528
1599
  ```
1529
1600
 
1530
- | Import | Factory | Notes |
1531
- | --- | --- | --- |
1532
- | `react-motion-gallery/masonry/fullscreen` | `masonryFullscreen()` | Opens `GalleryCore` fullscreen from light dimensioned Masonry items without adding fullscreen code to the default masonry import. |
1533
- | `react-motion-gallery/masonry/lazy-load` | `masonryLazyLoad(options)` | Uses the same image shell behavior as Slider: trackable image `src` values move into `data-rmg-lazy-src`, real images load on intersection, and items fade in after decode and spinner exit. |
1601
+ | Import | Factory | Notes |
1602
+ | ---------------------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1603
+ | `react-motion-gallery/masonry/fullscreen` | `masonryFullscreen()` | Opens `GalleryCore` fullscreen from light dimensioned Masonry items without adding fullscreen code to the default masonry import. |
1604
+ | `react-motion-gallery/masonry/lazy-load` | `masonryLazyLoad(options)` | Uses the same image shell behavior as Slider: trackable image `src` values move into `data-rmg-lazy-src`, real images load on intersection, and items fade in after decode and spinner exit. |
1605
+ | `react-motion-gallery/masonry/pagination` | `masonryPagination(options)` | Windows child items by `pageIndex` and `pageSize`; `useMasonryPagination()` also returns page state and `MasonryPaginationControls`. |
1606
+ | `react-motion-gallery/masonry/load-more` | `masonryLoadMore(options)` | Windows child items by `visibleCount`; `useMasonryLoadMore()` owns and increments the visible count. |
1607
+ | `react-motion-gallery/masonry/infinite-scroll` | `masonryInfiniteScroll(options)` | Renders a sentinel after the masonry root so it does not disturb absolute item positioning. |
1608
+ | `react-motion-gallery/masonry/virtualization` | `masonryVirtualization(options)` | Runs after pagination/load-more. Default Masonry uses known item positions; measured Masonry starts from estimated heights and refines with `ResizeObserver`. |
1534
1609
 
1535
1610
  `masonryLazyLoad()` is for measured Masonry and enables lazy loading by default. Pass `{ enabled: false }` to make the plugin inert.
1536
1611
 
1612
+ Masonry pagination, load-more, infinite-scroll, and virtualization APIs are documented in the shared data-plugin sections after Entries. Pagination and load-more apply before layout so hidden items do not reserve columns or masonry positions. In `"server"` mode the supplied children and `fullscreenItems` are treated as the current server window.
1613
+
1537
1614
  Measured Masonry accepts arbitrary React children, including text-containing JSX. The wrapper props are only for styling the built-in masonry item shell.
1538
1615
 
1539
1616
  In the measured subpath, wrap a card in `Masonry.Item` when it needs its own span, wrapper `className`, or wrapper `style`:
@@ -1652,7 +1729,41 @@ function MasonryWithSkeleton({ items }: { items: React.ReactNode[] }) {
1652
1729
 
1653
1730
  ## Entries
1654
1731
 
1655
- `Entries` is the structured-data surface. You pass entry objects, render each media item however you want, and provide a `renderMediaContainer` function that decides whether an entrys media should be laid out as a slider, grid, or masonry block.
1732
+ `Entries` is the structured-data surface. You pass entry objects, choose whether the entry rows themselves render as a vertical list or a card grid, render each media item however you want, and provide a `renderMediaContainer` function that decides whether an entry's media should be laid out as a slider, grid, or masonry block.
1733
+
1734
+ ### Entries layout
1735
+
1736
+ `entries.layout` controls the outer list of entries. The default is `"list"`, which stacks full-width rows with a vertical gap. Use `"grid"` when each entry should behave like a card in a responsive grid. This is separate from `entries.mediaLayout`: `layout` controls entry rows, while `mediaLayout` describes the media block inside each entry.
1737
+
1738
+ ```tsx
1739
+ <Entries
1740
+ entries={{
1741
+ items,
1742
+ layout: "list",
1743
+ mediaLayout: "slider",
1744
+ }}
1745
+ renderMediaContainer={renderEntryMedia}
1746
+ />
1747
+ ```
1748
+
1749
+ ```tsx
1750
+ <Entries
1751
+ entries={{
1752
+ items,
1753
+ layout: "grid",
1754
+ mediaLayout: "slider",
1755
+ entryList: {
1756
+ style: {
1757
+ gridTemplateColumns: "repeat(auto-fit, minmax(18rem, 1fr))",
1758
+ gap: 16,
1759
+ },
1760
+ },
1761
+ }}
1762
+ renderMediaContainer={renderEntryMedia}
1763
+ />
1764
+ ```
1765
+
1766
+ When `layout: "grid"` is set, `Entries` uses a responsive CSS grid by default and makes infinite-scroll sentinels and virtualization spacers span every column. Use `entryList` and `entryRow` for class names or inline styles when a product grid needs exact tracks, gaps, or row styling.
1656
1767
 
1657
1768
  ```typescript
1658
1769
  import * as React from "react";
@@ -1758,126 +1869,368 @@ With `loading.waitForDecode` enabled, an entry does not reveal as soon as it int
1758
1869
 
1759
1870
  Reveal timing is assigned when each entry becomes ready, so entries fade in by actual load/decode completion order as well as viewport intersection. A later row that loads quickly can take the next reveal slot while a slower row keeps its skeleton visible until its media is ready.
1760
1871
 
1872
+ Entry reveal state is remembered by default. Set `loading.rememberRevealed: false` when rows that leave the rendered window should fade in again if they return, such as client-paginated entries revisiting an earlier page.
1873
+
1761
1874
  Fullscreen close has a matching entry-aware path. If the user closes fullscreen from a slide whose owning entry has not been viewed yet, the runtime resolves the flattened fullscreen index back to the owner entry, shows a temporary loading spinner while that row mounts and decodes, scrolls the owner entry into view, forces the skeleton/content layers to their final revealed state, and then runs the close animation back to the now-visible entry media. This keeps the close animation from landing on an unrevealed skeleton or an offscreen row.
1762
1875
 
1763
1876
  ### `Entries` component props
1764
1877
 
1765
- | Option | Type | Default | Notes |
1766
- | --- | --- | --- | --- |
1767
- | `enabled` | `boolean` | `true` | Master switch for rendering entry content and transitions. |
1768
- | `entries` | `EntriesOptions` | `—` | Structured entry configuration. |
1769
- | `fullscreen.enabled` | `boolean` | `true` | Enables fullscreen opening for entry media. |
1770
- | `fullscreen.items` | `MediaItem[] \| string[]` | flattened entry media | Optional fullscreen media override. |
1771
- | `renderMediaContainer` | `({ entryIndex, mediaNodes, entrySliderRefs }) => ReactNode` | `—` | Chooses how each entry’s media nodes are laid out. |
1772
- | `nodeFromMedia` | `(media: MediaItem) => ReactNode` | built-in image/video renderer | Fallback renderer when `entries.render.media` is omitted. |
1773
- | `entryFlatIndexRef` | `React.RefObject<number[][] \| null>` | internal ref | Receives per-entry local-to-global media index maps. |
1774
- | `entryMapRef` | `React.RefObject<MediaEntryLink[] \| null>` | internal ref | Receives the flattened media-to-entry map. |
1775
- | `fsOwnersRef` | `React.RefObject<SlideOwner[]>` | internal ref | Receives the fullscreen slide owner list. |
1776
- | `entrySliderRefs` | `React.RefObject<(SliderHandle \| null)[]>` | internal ref | Lets `renderMediaContainer` wire fullscreen back to per-entry sliders. |
1878
+ | Option | Type | Default | Notes |
1879
+ | ---------------------- | ------------------------------------------------------------ | ----------------------------- | ---------------------------------------------------------------------- |
1880
+ | `enabled` | `boolean` | `true` | Master switch for rendering entry content and transitions. |
1881
+ | `entries` | `EntriesOptions` | `—` | Structured entry configuration. |
1882
+ | `fullscreen.enabled` | `boolean` | `true` | Enables fullscreen opening for entry media. |
1883
+ | `fullscreen.items` | `MediaItem[] \| string[]` | flattened entry media | Optional fullscreen media override. |
1884
+ | `renderMediaContainer` | `({ entryIndex, mediaNodes, entrySliderRefs }) => ReactNode` | `—` | Chooses how each entry’s media nodes are laid out. |
1885
+ | `nodeFromMedia` | `(media: MediaItem) => ReactNode` | built-in image/video renderer | Fallback renderer when `entries.render.media` is omitted. |
1886
+ | `entryFlatIndexRef` | `React.RefObject<number[][] \| null>` | internal ref | Receives per-entry local-to-global media index maps. |
1887
+ | `entryMapRef` | `React.RefObject<MediaEntryLink[] \| null>` | internal ref | Receives the flattened media-to-entry map. |
1888
+ | `fsOwnersRef` | `React.RefObject<SlideOwner[]>` | internal ref | Receives the fullscreen slide owner list. |
1889
+ | `entrySliderRefs` | `React.RefObject<(SliderHandle \| null)[]>` | internal ref | Lets `renderMediaContainer` wire fullscreen back to per-entry sliders. |
1777
1890
 
1778
1891
  ### `EntriesOptions`
1779
1892
 
1780
- | Option | Type | Default | Notes |
1781
- | --- | --- | --- | --- |
1782
- | `items` | `EntryItem[]` | `—` | Entry records. Each item can hold arbitrary fields plus `media`. |
1783
- | `mediaLayout` | `"slider" \| "grid" \| "masonry"` | `"slider"` | Declares the intended media layout. |
1784
- | `render.card` | `({ entry, entryIndex, media }) => ReactNode` | `—` | Wraps the media container in custom card UI. |
1785
- | `render.media` | `({ entry, entryIndex, media, mediaIndex }) => ReactNode` | `—` | Custom media renderer per media item. |
1786
- | `render.overlay` | `({ entry, entryIndex, media, mediaIndex, link, opacity, fsIndex, style, containerProps }) => ReactNode` | `—` | Renders fullscreen overlay content for the active entry slide. |
1787
- | `render.skeleton` | `({ entry, entryIndex }) => ReactNode` | `—` | Declared in the type, but the current runtime uses `loading.skeleton` instead. |
1788
- | `overlay` | `ElementStyle` | `—` | Styles the fullscreen overlay container that wraps `render.overlay`. |
1789
- | `overlay.overlayCrossfadeTarget` | `"content" \| "overlay"` | `"overlay"` | Selects whether fullscreen entry changes fade only the rendered overlay content or the whole overlay layer. |
1790
- | `overlay.overlayCrossfadeDurationMs` | `number` | `300` | Duration for fullscreen entry overlay crossfades. |
1791
- | `overlay.overlayCrossfadeEasing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Easing for fullscreen entry overlay crossfades. |
1792
- | `loading.enabled` | `boolean` | `—` | Enables entry loading and decode gating. |
1793
- | `loading.force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `—` | Forces entry skeletons to remain visible. Set `showContent: true` to preview mounted, ready entry content under the skeleton, and tune the loading overlay with `skeletonOpacity`. |
1794
- | `loading.skeleton` | `EntrySkeletonSpec \| ((args) => EntrySkeletonSpec \| null \| undefined)` | `—` | Built-in skeleton spec or resolver. |
1795
- | `loading.cache` | `SkeletonCacheOptions` | `—` | Opts entry skeleton text into the cookie snapshot cache. |
1796
- | `loading.minHeight` | `number \| string` | `"260px"` | Minimum reserved height while loading. |
1797
- | `loading.nearMargin` | `string` | `"700px 0px"` | Preload margin used before entries enter view. |
1798
- | `loading.viewMargin` | `string` | `"0px 0px"` | Margin used for the actual in-view gate. |
1799
- | `loading.threshold` | `number` | `0.01` | Intersection threshold for view detection. |
1800
- | `loading.waitForDecode` | `boolean` | `true` | Waits for image decode before revealing an entry. |
1801
- | `loading.decodeTimeoutMs` | `number` | `8000` | Decode timeout fallback. |
1802
- | `loading.skeletonWrap` | `ElementStyle` | `—` | Styles the skeleton wrapper. |
1803
- | `reveal.renderReveal` | `({ active, containerProps }, content) => ReactNode` | `—` | Custom reveal wrapper. |
1804
- | `reveal.staggerMs` | `number` | `200` | Delay between entry reveal fades. |
1805
- | `reveal.durationMs` | `number` | `700` | Entry reveal fade duration. |
1806
- | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Entry reveal fade easing. |
1807
- | `reveal.staggerLimit` | `number` | `6` | Maximum number of entries that receive staggered delays. |
1808
- | `entryList` | `ElementStyle` | `—` | Styles the entry list container. |
1809
- | `entryRow` | `ElementStyle` | `—` | Styles each entry row container. |
1893
+ | Option | Type | Default | Notes |
1894
+ | ------------------------------------ | -------------------------------------------------------------------------------------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1895
+ | `items` | `EntryItem[]` | `—` | Entry records. Each item can hold arbitrary fields plus `media`. |
1896
+ | `layout` | `"list" \| "grid"` | `"list"` | Controls the outer entry row layout. `list` stacks rows; `grid` arranges entries as responsive cards. |
1897
+ | `mediaLayout` | `"slider" \| "grid" \| "masonry"` | `"slider"` | Declares the intended media layout. |
1898
+ | `plugins` | `EntriesPlugin[]` | `[]` | Optional data plugins for pagination, load-more, infinite scroll, and virtualization. |
1899
+ | `render.card` | `({ entry, entryIndex, media }) => ReactNode` | `—` | Wraps the media container in custom card UI. |
1900
+ | `render.media` | `({ entry, entryIndex, media, mediaIndex }) => ReactNode` | `—` | Custom media renderer per media item. |
1901
+ | `render.overlay` | `({ entry, entryIndex, media, mediaIndex, link, opacity, fsIndex, style, containerProps }) => ReactNode` | `—` | Renders fullscreen overlay content for the active entry slide. |
1902
+ | `render.skeleton` | `({ entry, entryIndex }) => ReactNode` | `—` | Declared in the type, but the current runtime uses `loading.skeleton` instead. |
1903
+ | `overlay` | `ElementStyle` | `—` | Styles the fullscreen overlay container that wraps `render.overlay`. |
1904
+ | `overlay.overlayCrossfadeTarget` | `"content" \| "overlay"` | `"overlay"` | Selects whether fullscreen entry changes fade only the rendered overlay content or the whole overlay layer. |
1905
+ | `overlay.overlayCrossfadeDurationMs` | `number` | `300` | Duration for fullscreen entry overlay crossfades. |
1906
+ | `overlay.overlayCrossfadeEasing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Easing for fullscreen entry overlay crossfades. |
1907
+ | `loading.enabled` | `boolean` | `—` | Enables entry loading and decode gating. |
1908
+ | `loading.force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `—` | Forces entry skeletons to remain visible. Set `showContent: true` to preview mounted, ready entry content under the skeleton, and tune the loading overlay with `skeletonOpacity`. |
1909
+ | `loading.skeleton` | `EntrySkeletonSpec \| ((args) => EntrySkeletonSpec \| null \| undefined)` | `—` | Built-in skeleton spec or resolver. |
1910
+ | `loading.cache` | `SkeletonCacheOptions` | `—` | Opts entry skeleton text into the cookie snapshot cache. |
1911
+ | `loading.minHeight` | `number \| string` | `"260px"` | Minimum reserved height while loading. |
1912
+ | `loading.exitMs` | `number` | `220` | Entry skeleton opacity fade-out duration. |
1913
+ | `loading.nearMargin` | `string` | `"700px 0px"` | Preload margin used before entries enter view. |
1914
+ | `loading.viewMargin` | `string` | `"0px 0px"` | Margin used for the actual in-view gate. |
1915
+ | `loading.threshold` | `number` | `0.01` | Intersection threshold for view detection. |
1916
+ | `loading.waitForDecode` | `boolean` | `true` | Waits for image decode before revealing an entry. |
1917
+ | `loading.decodeTimeoutMs` | `number` | `8000` | Decode timeout fallback. |
1918
+ | `loading.skeletonWrap` | `ElementStyle` | `—` | Styles the skeleton wrapper. |
1919
+ | `loading.rememberRevealed` | `boolean` | `true` | Keeps revealed entry rows revealed while they remain known. Set false to reveal rows again after they leave and later re-enter the rendered window. |
1920
+ | `reveal.renderReveal` | `({ active, containerProps }, content) => ReactNode` | `—` | Custom reveal wrapper. |
1921
+ | `reveal.staggerMs` | `number` | `200` | Delay between entry reveal fades. |
1922
+ | `reveal.durationMs` | `number` | `700` | Entry reveal fade duration. |
1923
+ | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Entry reveal fade easing. |
1924
+ | `reveal.staggerLimit` | `number` | `6` | Maximum number of entries that receive staggered delays. |
1925
+ | `entryList` | `ElementStyle` | `—` | Styles the entry list container. |
1926
+ | `entryRow` | `ElementStyle` | `—` | Styles each entry row container. |
1810
1927
 
1811
1928
  Entry skeleton `text` nodes also render wrapped line bars via `lines`, matching the slider and grid skeleton behavior, including responsive `barHeight` and line counts plus configurable trailing `lastBarWidth`.
1812
1929
 
1930
+ ### Entries data plugins
1931
+
1932
+ Entries data plugins are passed through `entries.plugins`. They do not fetch data for you; they describe how the current `entries.items` array should be windowed, observed, or virtualized while your app owns network requests and state.
1933
+
1934
+ Prefer the granular subpaths when a route only needs one plugin. The `react-motion-gallery/entries` subpath also re-exports these helpers for modules that already import the full Entries surface. Full pagination, load-more, infinite-scroll, and virtualization APIs for Entries, Grid, and Masonry are documented in the shared data-plugin sections after `flattenEntries`.
1935
+
1813
1936
  ### Entry-related callback and helper types
1814
1937
 
1815
1938
  #### `EntryItem`
1816
1939
 
1817
- | Field | Type | Notes |
1818
- | --- | --- | --- |
1819
- | `media` | `MediaItem[] \| undefined` | Optional list of media items for the entry. |
1820
- | `[key: string]` | `any` | Additional entry fields are allowed. |
1940
+ | Field | Type | Notes |
1941
+ | --------------- | -------------------------- | ------------------------------------------- |
1942
+ | `media` | `MediaItem[] \| undefined` | Optional list of media items for the entry. |
1943
+ | `[key: string]` | `any` | Additional entry fields are allowed. |
1821
1944
 
1822
1945
  #### `EntryMediaRenderArgs`
1823
1946
 
1824
- | Field | Type | Notes |
1825
- | --- | --- | --- |
1826
- | `entry` | `EntryItem` | Current entry object. |
1827
- | `entryIndex` | `number` | Entry index. |
1828
- | `media` | `MediaItem` | Current media item. |
1829
- | `mediaIndex` | `number` | Media index within the entry. |
1947
+ | Field | Type | Notes |
1948
+ | ------------ | ----------- | ----------------------------- |
1949
+ | `entry` | `EntryItem` | Current entry object. |
1950
+ | `entryIndex` | `number` | Entry index. |
1951
+ | `media` | `MediaItem` | Current media item. |
1952
+ | `mediaIndex` | `number` | Media index within the entry. |
1830
1953
 
1831
1954
  #### `EntryCardRenderArgs`
1832
1955
 
1833
- | Field | Type | Notes |
1834
- | --- | --- | --- |
1835
- | `entry` | `EntryItem` | Current entry object. |
1836
- | `entryIndex` | `number` | Entry index. |
1837
- | `media` | `ReactNode` | The rendered media container returned by `renderMediaContainer`. |
1956
+ | Field | Type | Notes |
1957
+ | ------------ | ----------- | ---------------------------------------------------------------- |
1958
+ | `entry` | `EntryItem` | Current entry object. |
1959
+ | `entryIndex` | `number` | Entry index. |
1960
+ | `media` | `ReactNode` | The rendered media container returned by `renderMediaContainer`. |
1838
1961
 
1839
1962
  #### `EntryOverlayRenderArgs`
1840
1963
 
1841
- | Field | Type | Notes |
1842
- | --- | --- | --- |
1843
- | `entry` | `EntryItem` | Entry owning the active fullscreen slide. |
1844
- | `entryIndex` | `number` | Entry index. |
1845
- | `media` | `MediaItem \| null` | Media item for the active fullscreen slide, when available. |
1846
- | `mediaIndex` | `number \| null` | Media index inside the entry when available. |
1847
- | `link` | `MediaEntryLink \| null` | Flattened link back to the entry/media pair. |
1848
- | `opacity` | `number` | Overlay opacity supplied by the runtime. |
1849
- | `fsIndex` | `number` | Current fullscreen slide index. |
1850
- | `style` | `React.CSSProperties` | Overlay positioning and animation style. |
1851
- | `containerProps` | `React.HTMLAttributes<HTMLDivElement>` | Props to spread onto the overlay root. |
1964
+ | Field | Type | Notes |
1965
+ | ---------------- | -------------------------------------- | ----------------------------------------------------------- |
1966
+ | `entry` | `EntryItem` | Entry owning the active fullscreen slide. |
1967
+ | `entryIndex` | `number` | Entry index. |
1968
+ | `media` | `MediaItem \| null` | Media item for the active fullscreen slide, when available. |
1969
+ | `mediaIndex` | `number \| null` | Media index inside the entry when available. |
1970
+ | `link` | `MediaEntryLink \| null` | Flattened link back to the entry/media pair. |
1971
+ | `opacity` | `number` | Overlay opacity supplied by the runtime. |
1972
+ | `fsIndex` | `number` | Current fullscreen slide index. |
1973
+ | `style` | `React.CSSProperties` | Overlay positioning and animation style. |
1974
+ | `containerProps` | `React.HTMLAttributes<HTMLDivElement>` | Props to spread onto the overlay root. |
1852
1975
 
1853
1976
  #### `EntrySkeletonRenderArgs`
1854
1977
 
1855
- | Field | Type | Notes |
1856
- | --- | --- | --- |
1857
- | `entry` | `EntryItem` | Current entry object. |
1858
- | `entryIndex` | `number` | Entry index. |
1978
+ | Field | Type | Notes |
1979
+ | ------------ | ----------- | --------------------- |
1980
+ | `entry` | `EntryItem` | Current entry object. |
1981
+ | `entryIndex` | `number` | Entry index. |
1859
1982
 
1860
1983
  #### `MediaEntryLink`
1861
1984
 
1862
- | Field | Type | Notes |
1863
- | --- | --- | --- |
1864
- | `entryIndex` | `number` | Entry index. |
1985
+ | Field | Type | Notes |
1986
+ | ------------ | -------- | ----------------------------- |
1987
+ | `entryIndex` | `number` | Entry index. |
1865
1988
  | `mediaIndex` | `number` | Media index inside the entry. |
1866
1989
 
1867
1990
  #### `SlideOwner`
1868
1991
 
1869
- | Field | Type | Notes |
1870
- | --- | --- | --- |
1992
+ | Field | Type | Notes |
1993
+ | ------------ | -------- | ----------------------------------- |
1871
1994
  | `entryIndex` | `number` | Entry that owns a fullscreen slide. |
1872
1995
 
1873
1996
  ### `flattenEntries`
1874
1997
 
1875
- | Field | Type | Notes |
1876
- | --- | --- | --- |
1877
- | `flattenedMedia` | `MediaItem[]` | One flat media array, in fullscreen order. |
1878
- | `flattenedMap` | `MediaEntryLink[]` | Global slide index back to `entryIndex` and `mediaIndex`. |
1998
+ | Field | Type | Notes |
1999
+ | ---------------- | -------------------- | -------------------------------------------------------------- |
2000
+ | `flattenedMedia` | `MediaItem[]` | One flat media array, in fullscreen order. |
2001
+ | `flattenedMap` | `MediaEntryLink[]` | Global slide index back to `entryIndex` and `mediaIndex`. |
1879
2002
  | `entryFlatIndex` | `number[][] \| null` | Per-entry lookup from local media index to global slide index. |
1880
- | `owners` | `SlideOwner[]` | Owner metadata for each flattened slide. |
2003
+ | `owners` | `SlideOwner[]` | Owner metadata for each flattened slide. |
2004
+
2005
+ ## Pagination
2006
+
2007
+ Pagination, load-more, infinite-scroll, and virtualization are data plugins for Grid, Masonry, and Entries. Your app still owns fetching, caching, URL state, and append behavior; the plugins describe how the current child or entry list should be windowed, observed, or virtualized.
2008
+
2009
+ Pagination creates fixed page windows. In `"client"` mode it slices Grid children, Masonry children, or `entries.items` before rendering. In `"server"` mode it leaves the supplied records untouched so your API response can already represent the current page. Grid and Masonry also treat `fullscreenItems` as the current server window in server mode.
2010
+
2011
+ | Surface | Entry point | Factory | Hook | Controls and helpers | Common exported types |
2012
+ | ------- | --------------------------------------------- | ------------------------------ | ------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
2013
+ | Grid | `react-motion-gallery/grid/pagination` | `gridPagination(options)` | `useGridPagination(options)` | `GridPaginationControls`, `getGridPageRange()`, `getGridPageItems()` | `GridPaginationOptions`, `UseGridPaginationOptions`, `GridPaginationControlsProps`, `GridItemsPerPageOption` |
2014
+ | Masonry | `react-motion-gallery/masonry/pagination` | `masonryPagination(options)` | `useMasonryPagination(options)` | `MasonryPaginationControls`, `getMasonryPageRange()`, `getMasonryPageItems()` | `MasonryPaginationOptions`, `UseMasonryPaginationOptions`, `MasonryPaginationControlsProps`, `MasonryItemsPerPageOption` |
2015
+ | Entries | `react-motion-gallery/entries/pagination` | `entriesPagination(options)` | `useEntriesPagination(options)` | `EntriesPaginationControls`, `getEntriesPageRange()`, `getEntriesPageItems()` | `EntriesPaginationOptions`, `UseEntriesPaginationOptions`, `EntriesPaginationControlsProps`, `EntriesItemsPerPageOption` |
2016
+
2017
+ Pagination subpaths also export surface-prefixed page item/range types, URL sync types, session storage types, and ripple types: for example `GridPageControlItem`, `MasonryPaginationUrlSyncOptions`, and `EntriesPaginationRippleOptions`. Entries also exports `EntriesPaginationController`.
2018
+
2019
+ ### Pagination plugin options
2020
+
2021
+ These options apply to `gridPagination()`, `masonryPagination()`, and `entriesPagination()`.
2022
+
2023
+ | Option | Type | Default | Notes |
2024
+ | ----------- | ---------------------- | ---------- | ------------------------------------------------------------------------------------- |
2025
+ | `enabled` | `boolean` | `true` | Disables the plugin when false. |
2026
+ | `mode` | `"client" \| "server"` | `"client"` | Client mode slices records by page. Server mode leaves supplied records unchanged. |
2027
+ | `pageIndex` | `number` | required | Zero-based current page. Values below zero clamp to zero. |
2028
+ | `pageSize` | `number` | required | Items per page. Values below one clamp to one. |
2029
+ | `total` | `number` | `—` | Total record count for controls and loading state. |
2030
+ | `loading` | `boolean` | `—` | Marks the surface busy. Entries pagination also keeps current rows under the skeleton overlay during page transitions. |
2031
+
2032
+ ### Pagination hook options
2033
+
2034
+ These options apply to `useGridPagination()`, `useMasonryPagination()`, and `useEntriesPagination()`.
2035
+
2036
+ | Option | Type | Default | Notes |
2037
+ | ------------------ | --------------------------------------------------------------------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------- |
2038
+ | `pageSize` | `number` | `initialPageSize` | Controlled items per page. |
2039
+ | `initialPageSize` | `number` | `1` | Uncontrolled initial items per page when `pageSize` is omitted. |
2040
+ | `onPageSizeChange` | `(pageSize: number) => void` | `—` | Called from `setPageSize`, including when stored page size is restored into a controlled hook. |
2041
+ | `total` | `number` | `0` | Used to derive `pageCount`. |
2042
+ | `initialPageIndex` | `number` | `0` | Initial zero-based page when URL sync or session storage does not provide one. |
2043
+ | `mode` | `"client" \| "server"` | `"client"` | Passed through to the plugin. |
2044
+ | `loading` | `boolean` | `—` | Passed through to the plugin. |
2045
+ | `enabled` | `boolean` | `true` | Passed through to the plugin. |
2046
+ | `urlSync` | `boolean \| { enabled?, param?, history?, omitFirstPage?, basePath?, preserveSearch? }` | `false` | Reads and writes a one-based page query param. |
2047
+ | `sessionStorage` | `boolean \| { enabled?: boolean; key?: string }` | `false` | Restores and writes `pageIndex` and `pageSize` in `window.sessionStorage`. |
2048
+
2049
+ The pagination controller returns `pageIndex`, `pageSize`, `pageCount`, `offset`, `canPrevPage`, `canNextPage`, `setPageIndex`, `setPageSize`, `nextPage`, `prevPage`, `plugin`, and optional `getPageHref`. Calling `setPageSize` resets the current page to zero.
2050
+
2051
+ `urlSync: true` uses `?page=2`, pushes history entries, omits the first page from the URL, and preserves the current search string. Pass `{ param, history, omitFirstPage, basePath, preserveSearch }` to customize those defaults. `basePath` lets server-rendered controls build stable hrefs before `window.location` is available.
2052
+
2053
+ `sessionStorage: true` uses a default key based on the current path and page query param; pass `{ key: "products-pagination" }` when a page has multiple paginated surfaces. If URL sync is also enabled, the URL page wins and storage fills in when the query param is absent. Session storage is client-only persistence for pagination state; it does not fetch or cache records.
2054
+
2055
+ ### Pagination controls props
2056
+
2057
+ `GridPaginationControls`, `MasonryPaginationControls`, and `EntriesPaginationControls` share the same prop surface. They render buttons by default. When `getPageHref` returns a URL, page controls render anchors, preserve normal modified-click browser behavior, and intercept plain clicks to call `onPageChange`.
2058
+
2059
+ | Prop | Type | Default | Notes |
2060
+ | ------------------------ | ------------------------------------------------------------------ | ------------ | ------------------------------------------------------------------------------------------------------ |
2061
+ | `pageIndex` | `number` | required | Zero-based selected page. |
2062
+ | `pageCount` | `number` | required | Total number of pages. |
2063
+ | `pageRangeDisplayed` | `number` | `5` | Number of center pages to show around the selected page. |
2064
+ | `marginPagesDisplayed` | `number` | `1` | Number of edge pages to keep visible on each side. |
2065
+ | `disabled` | `boolean` | `false` | Disables previous, next, and page controls. |
2066
+ | `previousLabel` | `React.ReactNode` | `"Previous"` | Previous-control content. |
2067
+ | `nextLabel` | `React.ReactNode` | `"Next"` | Next-control content. |
2068
+ | `breakLabel` | `React.ReactNode` | `"..."` | Content for compact pagination breaks. |
2069
+ | `getPageLabel` | `(pageIndex: number) => React.ReactNode` | page number | Returns content for page buttons or anchors. |
2070
+ | `onPageChange` | `(pageIndex: number) => void` | required | Called when a page, previous, or next control is activated. |
2071
+ | `getPageHref` | `(pageIndex, item) => string \| undefined` | `—` | Returns anchor URLs for page controls. The hook controller's `getPageHref` can be passed directly. |
2072
+ | `renderItem` | `(item, defaultNode) => React.ReactNode` | `—` | Customizes each computed page, previous, next, or break item. |
2073
+ | `disableSelected` | `boolean` | `false` | Disables the selected page control. |
2074
+ | `ariaLabel` | `string` | `"Pagination"` | Label for the wrapping `nav`. |
2075
+ | `ripple` | `boolean \| { enabled?, color?, duration?, easing?, opacity?, className? }` | enabled | Click-position ripple for page, previous, and next controls. Pass `false` to disable. |
2076
+ | `pageSize` | `number` | `—` | Current items per page. Required with `itemsPerPageOptions` and `onItemsPerPageChange` to show the selector. |
2077
+ | `itemsPerPageOptions` | `readonly (number \| { value: number; label: React.ReactNode })[]` | `[]` | Listbox choices. Number options use the number as their label; duplicate normalized values are removed. |
2078
+ | `itemsPerPageLabel` | `React.ReactNode` | `"Items per page"` | Visible label rendered before the selector trigger. |
2079
+ | `itemsPerPageSelectLabel` | `string` | `"Items per page"` | Accessible label for the selector trigger and listbox. |
2080
+ | `onItemsPerPageChange` | `(pageSize: number) => void` | `—` | Called when a different page size is selected. Pair with `pagination.setPageSize`. |
2081
+ | `className` | `string` | `—` | Class applied to the wrapping `nav`. |
2082
+ | `pageItemsClassName` | `string` | `—` | Class applied to the page-control group with `data-rmg-page-items`. |
2083
+ | `itemClassName` | `string` | `—` | Base class for every page, previous, next, or break item. |
2084
+ | `pageClassName` | `string` | `—` | Class applied to numbered page items. |
2085
+ | `controlClassName` | `string` | `—` | Class applied to previous and next items. |
2086
+ | `breakClassName` | `string` | `—` | Class applied to break items. |
2087
+ | `selectedClassName` | `string` | `—` | Class applied to the selected page item. |
2088
+ | `itemsPerPageClassName` | `string` | `—` | Class applied to the selector wrapper with `data-rmg-items-per-page`. |
2089
+ | `itemsPerPageLabelClassName` | `string` | `—` | Class applied to the visible selector label. |
2090
+ | `itemsPerPageSelectClassName` | `string` | `—` | Class applied to the selector trigger button. |
2091
+
2092
+ The items-per-page selector renders as a separate group before page controls. It is shown only when `pageSize`, `itemsPerPageOptions`, and `onItemsPerPageChange` are all provided. If the current `pageSize` is not present in `itemsPerPageOptions`, the controls add it as the selected option.
2093
+
2094
+ ### Pagination helper APIs
2095
+
2096
+ | Surface | Page range helper | Page items helper | Controls component |
2097
+ | ------- | -------------------- | -------------------- | ---------------------------- |
2098
+ | Grid | `getGridPageRange()` | `getGridPageItems()` | `GridPaginationControls` |
2099
+ | Masonry | `getMasonryPageRange()` | `getMasonryPageItems()` | `MasonryPaginationControls` |
2100
+ | Entries | `getEntriesPageRange()` | `getEntriesPageItems()` | `EntriesPaginationControls` |
2101
+
2102
+ `get*PageRange({ pageIndex, pageCount, pageRangeDisplayed, marginPagesDisplayed })` returns page and break items for compact pagination. `get*PageItems(options)` adds previous/next controls, disabled state, and labels around that page range.
2103
+
2104
+ ## Load More
2105
+
2106
+ Load-more plugins reveal or append a growing record window. In `"client"` mode they render the first `visibleCount` records. In `"server"` mode they leave supplied records unchanged, which is useful when your app appends API results to the current list.
2107
+
2108
+ | Surface | Entry point | Factory | Hook | Common exported types |
2109
+ | ------- | -------------------------------------------- | ----------------------------- | ------------------------------ | ---------------------------------------------------------------------------- |
2110
+ | Grid | `react-motion-gallery/grid/load-more` | `gridLoadMore(options)` | `useGridLoadMore(options)` | `GridLoadMoreOptions`, `UseGridLoadMoreOptions` |
2111
+ | Masonry | `react-motion-gallery/masonry/load-more` | `masonryLoadMore(options)` | `useMasonryLoadMore(options)` | `MasonryLoadMoreOptions`, `UseMasonryLoadMoreOptions`, `MasonryLoadMorePlugin` |
2112
+ | Entries | `react-motion-gallery/entries/load-more` | `entriesLoadMore(options)` | `useEntriesLoadMore(options)` | `EntriesLoadMoreOptions`, `UseEntriesLoadMoreOptions`, `EntriesLoadMoreController` |
2113
+
2114
+ ### Load-more plugin options
2115
+
2116
+ These options apply to `gridLoadMore()`, `masonryLoadMore()`, and `entriesLoadMore()`.
2117
+
2118
+ | Option | Type | Default | Notes |
2119
+ | -------------- | ---------------------- | -------- | ------------------------------------------------------------------------------------------------ |
2120
+ | `enabled` | `boolean` | `true` | Disables the plugin when false. |
2121
+ | `mode` | `"client" \| "server"` | `"client"` | Client mode renders the first `visibleCount` records. Server mode leaves supplied records unchanged. |
2122
+ | `visibleCount` | `number` | required | Number of currently visible records. Values below zero clamp to zero. |
2123
+ | `total` | `number` | `—` | Total record count for controls and loading state. |
2124
+ | `loading` | `boolean` | `—` | Marks the surface busy. |
2125
+
2126
+ ### Load-more hook options
2127
+
2128
+ These options apply to `useGridLoadMore()`, `useMasonryLoadMore()`, and `useEntriesLoadMore()`.
2129
+
2130
+ | Option | Type | Default | Notes |
2131
+ | --------------------- | ---------------------- | --------------------- | --------------------------------------------------------------- |
2132
+ | `initialVisibleCount` | `number` | `pageSize` | Uncontrolled initial visible count. Values below zero clamp to zero. |
2133
+ | `pageSize` | `number` | required | Number of records added by each `loadMore()` call. Values below one clamp to one. |
2134
+ | `total` | `number` | `initialVisibleCount` | Used to compute `canLoadMore` and clamp increments. |
2135
+ | `mode` | `"client" \| "server"` | `"client"` | Passed through to the plugin. |
2136
+ | `loading` | `boolean` | `—` | Passed through to the plugin. |
2137
+ | `enabled` | `boolean` | `true` | Passed through to the plugin. |
2138
+
2139
+ The load-more controller returns `visibleCount`, `pageSize`, `canLoadMore`, `setVisibleCount`, `loadMore`, `reset`, and `plugin`.
2140
+
2141
+ Pagination and load-more are both data-window plugins. When both are enabled on the same surface, the first one in the plugin list supplies the rendered data window.
2142
+
2143
+ ## Infinite Scroll
2144
+
2145
+ Infinite scroll renders a sentinel and calls your append function when that sentinel intersects. It does not fetch or append records itself, so it is commonly paired with server-mode load-more or your own list state.
2146
+
2147
+ | Surface | Entry point | Factory | Hook | Common exported types |
2148
+ | ------- | ------------------------------------------------- | ---------------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------- |
2149
+ | Grid | `react-motion-gallery/grid/infinite-scroll` | `gridInfiniteScroll(options)` | `useGridInfiniteScroll(options)` | `GridInfiniteScrollOptions`, `UseGridInfiniteScrollOptions` |
2150
+ | Masonry | `react-motion-gallery/masonry/infinite-scroll` | `masonryInfiniteScroll(options)` | `useMasonryInfiniteScroll(options)` | `MasonryInfiniteScrollOptions`, `UseMasonryInfiniteScrollOptions`, `MasonryInfiniteScrollPlugin` |
2151
+ | Entries | `react-motion-gallery/entries/infinite-scroll` | `entriesInfiniteScroll(options)` | `useEntriesInfiniteScroll(options)` | `EntriesInfiniteScrollOptions`, `UseEntriesInfiniteScrollOptions` |
2152
+
2153
+ ### Infinite-scroll options
2154
+
2155
+ These options apply to `gridInfiniteScroll()`, `masonryInfiniteScroll()`, `entriesInfiniteScroll()`, and their hooks.
2156
+
2157
+ | Option | Type | Default | Notes |
2158
+ | ------------ | ----------------- | ------------- | ---------------------------------------------------------------------------- |
2159
+ | `enabled` | `boolean` | `true` | Disables sentinel rendering when false. |
2160
+ | `hasMore` | `boolean` | `true` | Removes the sentinel when false. |
2161
+ | `loading` | `boolean` | `—` | Prevents repeated `onLoadMore` calls while a request is active. |
2162
+ | `rootMargin` | `string` | `"600px 0px"` | IntersectionObserver preload margin. |
2163
+ | `threshold` | `number` | `0` | IntersectionObserver threshold. |
2164
+ | `onLoadMore` | `() => void` | `—` | Called when the sentinel intersects and loading gates allow another request. |
2165
+ | `sentinel` | `React.ReactNode` | `—` | Optional visual content inside the sentinel element. |
2166
+
2167
+ The hook form memoizes the factory call and returns the plugin. Grid and Masonry sentinels render after the layout root so they do not disturb CSS grid tracks or masonry positioning. Entries sentinels render after the entry rows and span every column when `entries.layout` is `"grid"`.
2168
+
2169
+ ## Virtualization
2170
+
2171
+ Virtualization mounts only the records near the viewport. It runs after pagination or load-more windowing, so the virtualized set is the current page or visible window.
2172
+
2173
+ | Surface | Entry point | Factory | Hook | Common exported types |
2174
+ | ------- | ------------------------------------------------ | --------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------- |
2175
+ | Grid | `react-motion-gallery/grid/virtualization` | `gridVirtualization(options)` | `useGridVirtualizer(options)` | `GridVirtualizationOptions`, `UseGridVirtualizerOptions` |
2176
+ | Masonry | `react-motion-gallery/masonry/virtualization` | `masonryVirtualization(options)` | `useMasonryVirtualizer(options)` | `MasonryVirtualizationOptions`, `UseMasonryVirtualizerOptions`, `MasonryVirtualizationPlugin` |
2177
+ | Entries | `react-motion-gallery/entries/virtualization` | `entriesVirtualization(options)` | `useEntriesVirtualizer(options)` | `EntriesVirtualizationOptions`, `UseEntriesVirtualizerOptions` |
2178
+
2179
+ ### Virtualization options
2180
+
2181
+ | Option | Surfaces | Type | Default | Notes |
2182
+ | -------------- | -------- | ------------------ | ---------------- | ------------------------------------------------------------------------------------------------ |
2183
+ | `enabled` | all | `boolean` | `true` | Disables virtualization when false. |
2184
+ | `layout` | Entries | `"list" \| "grid"` | `entries.layout` | Tells Entries whether to virtualize one entry per row or grid rows with multiple entries. |
2185
+ | `estimateSize` | all | `number` | `420` | Initial row or item height estimate in pixels. Values below one clamp to one. |
2186
+ | `gap` | all | `number` | `24` | Vertical gap included in virtual range and spacer calculations. Values below zero clamp to zero. |
2187
+ | `overscan` | all | `number` | `3` | Extra rows or items to mount before and after the visible range. Values below zero clamp to zero. |
2188
+
2189
+ The hook form memoizes the factory call and returns the plugin. Grid virtualizes by rows and inserts top and bottom spacers that span every grid column. Entries virtualizes list rows by default and grid rows when `entries.layout: "grid"` or `entriesVirtualization({ layout: "grid" })` is used. Default Masonry uses known item positions; measured Masonry starts from estimated or seeded heights and refines its range as items are measured with `ResizeObserver`.
2190
+
2191
+ ## RatingStars
2192
+
2193
+ `RatingStars` renders accessible star ratings with optional value and review-count labels. It is useful inside entry card renderers for product, review, or catalog metadata. Fractional ratings are shown by default with `fillMode="partial"`; the component maps fractional fill through the star shape so a `4.5` rating fills half the visual star area instead of clipping a naive rectangle. Use `fillMode="floor"`, `"round"`, or `"ceil"` when the visual stars should snap to whole stars.
2194
+
2195
+ ```tsx
2196
+ import { RatingStars } from "react-motion-gallery/rating-stars";
2197
+
2198
+ export function ProductRating() {
2199
+ return (
2200
+ <RatingStars
2201
+ value={4.35}
2202
+ precision={1}
2203
+ reviewCount={128}
2204
+ activeColor="#f5a524"
2205
+ />
2206
+ );
2207
+ }
2208
+ ```
2209
+
2210
+ `value` is clamped between `0` and `max`, `precision` formats the numeric label, and `reviewCount` automatically enables the review label unless `showReviewCount={false}` is passed. Keep `fillMode="partial"` for fractional source data, and choose a whole-star mode only when your product UI intentionally rounds ratings.
2211
+
2212
+ ### `RatingStars` props
2213
+
2214
+ | Option | Type | Default | Notes |
2215
+ | ------------------- | ------------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------- |
2216
+ | `value` | `number` | `—` | Rating value. Non-finite values render as `0`, and finite values are clamped between `0` and `max`. |
2217
+ | `max` | `number` | `5` | Number of stars to render. Values are coerced to an integer of at least `1`. |
2218
+ | `precision` | `number` | `1` | Decimal places used by the default numeric value label. |
2219
+ | `fillMode` | `"partial" \| "floor" \| "round" \| "ceil"` | `"partial"` | Controls whether fractional ratings fill the star shape or snap to whole stars. |
2220
+ | `reviewCount` | `number` | `—` | Review total used by the default review-count label. |
2221
+ | `showValue` | `boolean` | `true` | Shows the formatted rating value beside the stars. |
2222
+ | `showReviewCount` | `boolean` | `reviewCount != null` | Shows the review-count label when `reviewCount` is provided. |
2223
+ | `formatValue` | `(value: number) => ReactNode` | `—` | Custom formatter for the clamped rating value. |
2224
+ | `formatReviewCount` | `(count: number) => ReactNode` | `—` | Custom formatter for the review count. |
2225
+ | `className` | `string` | `—` | Class name for the root inline-flex element. |
2226
+ | `starsClassName` | `string` | `—` | Class name for the star row. |
2227
+ | `starClassName` | `string` | `—` | Class name for each star wrapper. |
2228
+ | `labelClassName` | `string` | `—` | Class name for the value and review-count label. |
2229
+ | `style` | `React.CSSProperties` | `—` | Inline styles for the root element. |
2230
+ | `activeColor` | `string` | `"#f5a524"` | Fill color for active stars. |
2231
+ | `emptyColor` | `string` | `"#d6dee4"` | Fill color for empty stars. |
2232
+ | `gap` | `number \| string` | `"0.08em"` | Gap between individual stars. |
2233
+ | `ariaLabel` | `string` | computed label | Accessible label override for the root `role="img"` element. |
1881
2234
 
1882
2235
  ## Fullscreen
1883
2236
 
@@ -2083,115 +2436,115 @@ useFullscreenController({
2083
2436
 
2084
2437
  ### `useFullscreenController` args
2085
2438
 
2086
- | Option | Type | Default | Notes |
2087
- | --- | --- | --- | --- |
2088
- | `plugins` | `FullscreenPlugin[]` | `[]` | Explicit first-party fullscreen features. At minimum, import `fullscreenSlider()` to mount the fullscreen runtime. |
2089
- | `fullscreen` | `FullscreenOptions` | `—` | Fullscreen behavior and rendering options. |
2090
-
2091
- | Import | Factory | Notes |
2092
- | --- | --- | --- |
2093
- | `react-motion-gallery/fullscreen/slider` | `fullscreenSlider(options)` | Mounts the fullscreen slider runtime and accepts `fullscreen.slider` options. |
2094
- | `react-motion-gallery/fullscreen/controls` | `fullscreenControls(options)` | Option plugin for close, arrows, and counter options. Use with `fullscreenSlider()`. |
2095
- | `react-motion-gallery/fullscreen/captions` | `fullscreenCaptions(options)` | Adds caption rendering, placement, and caption motion runtime. Use with `fullscreenSlider()`. |
2096
- | `react-motion-gallery/fullscreen/zoom-pan` | `fullscreenZoomPan(options)` | Adds fullscreen click zoom, pan, and pinch runtime. Use with `fullscreenSlider()`. |
2097
- | `react-motion-gallery/fullscreen/video` | `fullscreenVideo(options)` | Adds fullscreen Plyr rendering, source/options, and `playOnOpen` runtime. Use with `fullscreenSlider()`. |
2098
- | `react-motion-gallery/fullscreen/lazy-load` | `fullscreenLazyLoad(options)` | Adds fullscreen image and video lazy-load gates. Use with `fullscreenSlider()`. |
2099
- | `react-motion-gallery/fullscreen/crossfade` | `fullscreenCrossfade(options)` | Option plugin for fullscreen crossfade controls, drag, and wheel behavior. Use with `fullscreenSlider()`. |
2100
- | `react-motion-gallery/fullscreen/thumbnails` | `fullscreenThumbnails()` | Option-only plugin for fullscreen thumbnail bridge behavior. Use with `fullscreenSlider()`. |
2439
+ | Option | Type | Default | Notes |
2440
+ | ------------ | -------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
2441
+ | `plugins` | `FullscreenPlugin[]` | `[]` | Explicit first-party fullscreen features. At minimum, import `fullscreenSlider()` to mount the fullscreen runtime. |
2442
+ | `fullscreen` | `FullscreenOptions` | `—` | Fullscreen behavior and rendering options. |
2443
+
2444
+ | Import | Factory | Notes |
2445
+ | -------------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------- |
2446
+ | `react-motion-gallery/fullscreen/slider` | `fullscreenSlider(options)` | Mounts the fullscreen slider runtime and accepts `fullscreen.slider` options. |
2447
+ | `react-motion-gallery/fullscreen/controls` | `fullscreenControls(options)` | Option plugin for close, arrows, and counter options. Use with `fullscreenSlider()`. |
2448
+ | `react-motion-gallery/fullscreen/captions` | `fullscreenCaptions(options)` | Adds caption rendering, placement, and caption motion runtime. Use with `fullscreenSlider()`. |
2449
+ | `react-motion-gallery/fullscreen/zoom-pan` | `fullscreenZoomPan(options)` | Adds fullscreen click zoom, pan, and pinch runtime. Use with `fullscreenSlider()`. |
2450
+ | `react-motion-gallery/fullscreen/video` | `fullscreenVideo(options)` | Adds fullscreen Plyr rendering, source/options, and `playOnOpen` runtime. Use with `fullscreenSlider()`. |
2451
+ | `react-motion-gallery/fullscreen/lazy-load` | `fullscreenLazyLoad(options)` | Adds fullscreen image and video lazy-load gates. Use with `fullscreenSlider()`. |
2452
+ | `react-motion-gallery/fullscreen/crossfade` | `fullscreenCrossfade(options)` | Option plugin for fullscreen crossfade controls, drag, and wheel behavior. Use with `fullscreenSlider()`. |
2453
+ | `react-motion-gallery/fullscreen/thumbnails` | `fullscreenThumbnails()` | Option-only plugin for fullscreen thumbnail bridge behavior. Use with `fullscreenSlider()`. |
2101
2454
 
2102
2455
  ### Recommended `useFullscreenController` return values
2103
2456
 
2104
- | Field | Type | Notes |
2105
- | --- | --- | --- |
2106
- | `fullscreenNode` | `ReactNode` | The fullscreen portal UI. Render this once inside the `GalleryCore` tree. |
2107
- | `fullscreenThumbnailBridge` | `FullscreenThumbnailBridge` | Bridge consumed by `FullscreenThumbnailSlider`. |
2108
- | `openFullscreenAt` | `(source, index, originEl?, requestedMethod?) => void` | Programmatic fullscreen open helper returned by the controller. |
2109
- | `showFullscreenModal` | `boolean` | `true` while the fullscreen modal is mounted and open. |
2110
- | `showFullscreenSlider` | `boolean` | `true` once the slider portion is visible. |
2111
- | `fsFadeOpening` | `boolean` | `true` while a fade-based open animation is running. |
2112
- | `closingModal` | `boolean` | `true` while the close animation is running. |
2457
+ | Field | Type | Notes |
2458
+ | --------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------- |
2459
+ | `fullscreenNode` | `ReactNode` | The fullscreen portal UI. Render this once inside the `GalleryCore` tree. |
2460
+ | `fullscreenThumbnailBridge` | `FullscreenThumbnailBridge` | Bridge consumed by `FullscreenThumbnailSlider`. |
2461
+ | `openFullscreenAt` | `(source, index, originEl?, requestedMethod?) => void` | Programmatic fullscreen open helper returned by the controller. |
2462
+ | `showFullscreenModal` | `boolean` | `true` while the fullscreen modal is mounted and open. |
2463
+ | `showFullscreenSlider` | `boolean` | `true` once the slider portion is visible. |
2464
+ | `fsFadeOpening` | `boolean` | `true` while a fade-based open animation is running. |
2465
+ | `closingModal` | `boolean` | `true` while the close animation is running. |
2113
2466
 
2114
2467
  The hook returns additional refs and setters for the internal fullscreen runtime. Those values are implementation plumbing and are not the recommended consumer-facing surface for app code.
2115
2468
 
2116
2469
  ### `FullscreenOptions`
2117
2470
 
2118
- | Option | Type | Default | Notes |
2119
- | --- | --- | --- | --- |
2120
- | `enabled` | `boolean` | `false` | Master switch for fullscreen UI. |
2121
- | `items` | `MediaItem[] \| string[]` | `—` | Declared in the type, but current fullscreen media resolution comes from `GalleryCore.fullscreenItems`. |
2122
- | `renderImage` | `({ item, index, isZoomed, className, baseStyle }) => ReactNode` | `—` | Custom fullscreen image renderer. Must render a real descendant `<img>`. With `lazyLoad.images.enabled`, the renderer is mounted only when the slide is allowed and the runtime watches that descendant image for load/decode readiness. |
2123
- | `closeScroll` | `boolean \| FullscreenCloseScrollOptions` | `false` | Scrolls the matching base item into the center of the viewport when fullscreen closes. `true` enables the default before-close scroll; object form defaults `enabled` to `true`. |
2124
- | `closeScroll.enabled` | `boolean \| "desktop-only" \| "mobile-only" \| ((context) => boolean)` | `true` in object form | Enables close-scroll conditionally. Function form receives the current fullscreen index, layout, target element, viewport and pointer details, and the resolved `isMobile` flag. |
2125
- | `closeScroll.timing` | `"before-close" \| "after-close"` | `"before-close"` | Chooses whether to scroll before the close animation starts or after the modal has closed. |
2126
- | `closeScroll.mobileDetection` | `(context: FullscreenMobileDetectionContext) => boolean` | built-in heuristic | Overrides mobile detection used by `"desktop-only"`, `"mobile-only"`, and the resolver context. The built-in heuristic treats narrow touch/no-hover viewports as mobile. |
2127
- | `video.source` | `(item: MediaItem, index: number) => Plyr.SourceInfo` | `—` | Builds fullscreen Plyr sources for video items. |
2128
- | `video.options` | `Plyr.Options \| ((item: MediaItem, index: number) => Plyr.Options)` | `—` | Builds fullscreen Plyr options. |
2129
- | `video.playOnOpen` | `boolean` | `false` | Attempts to play the fullscreen Plyr video when fullscreen opens directly onto a video slide. Browser autoplay rules still apply. |
2130
- | `video.style` | `React.CSSProperties` | `—` | Fullscreen player inline style. |
2131
- | `video.className` | `string` | `—` | Fullscreen player class. |
2132
- | `controls.close.enabled` | `boolean` | `true` | Toggles the close button. |
2133
- | `controls.close.style` | `React.CSSProperties` | `{}` | Close button inline style. |
2134
- | `controls.close.className` | `string` | `""` | Close button class. |
2135
- | `controls.close.render` | `() => ReactNode` | `—` | Custom close button renderer. |
2136
- | `controls.arrows.enabled` | `boolean` | `true` | Toggles fullscreen arrows. |
2137
- | `controls.arrows.arrow` | `ElementStyle` | `{}` | Shared arrow style. |
2138
- | `controls.arrows.prev` | `ElementStyle` | `{}` | Previous-arrow override. |
2139
- | `controls.arrows.next` | `ElementStyle` | `{}` | Next-arrow override. |
2140
- | `controls.arrows.render` | `({ dir }) => ReactNode` | `—` | Custom renderer for both arrows. |
2141
- | `controls.arrows.renderPrev` | `() => ReactNode` | `—` | Custom previous arrow. |
2142
- | `controls.arrows.renderNext` | `() => ReactNode` | `—` | Custom next arrow. |
2143
- | `controls.counter.enabled` | `boolean` | `true` | Toggles the index counter. |
2144
- | `controls.counter.style` | `React.CSSProperties` | `{}` | Counter inline style. |
2145
- | `controls.counter.className` | `string` | `""` | Counter class. |
2146
- | `controls.counter.render` | `({ index, count }) => ReactNode` | `—` | Custom counter renderer. |
2147
- | `caption.className` | `string` | `—` | Caption root class. |
2148
- | `caption.style` | `React.CSSProperties` | `—` | Caption root style. |
2149
- | `caption.placement` | `FsCaptionPlacement \| FsCaptionPlacement[] \| Record<string, FsCaptionPlacement>` | `—` | Preferred caption placement. Responsive maps use the `GalleryCore.breakpoints` keys such as `xs`, `md`, and `lg`. |
2150
- | `caption.width` | `number \| string \| Record<string, number \| string>` | `—` | Caption area width. Strings can use `px` or `%`; percentages are viewport-relative in fullscreen. Responsive maps use breakpoint keys. |
2151
- | `caption.height` | `number \| string \| Record<string, number \| string>` | `—` | Caption area height. Strings can use `px` or `%`; percentages are viewport-relative in fullscreen. Responsive maps use breakpoint keys. |
2152
- | `caption.breakpoint` | `number` | `—` | Viewport cutoff for switching placement logic. |
2153
- | `caption.render` | `({ item, index, isZoomed }) => ReactNode` | `—` | Custom caption renderer. |
2154
- | `caption.layout` | `"overlay" \| "slide"` | `—` | Chooses whether the caption overlays the media or lives in the slide layout. |
2155
- | `caption.overlayCrossfadeTarget` | `"content" \| "overlay"` | `"content"` | Selects whether overlay caption changes fade only the rendered caption content or the whole overlay layer. |
2156
- | `caption.overlayCrossfadeDurationMs` | `number` | `300` | Duration for fullscreen overlay caption crossfades. |
2157
- | `caption.overlayCrossfadeEasing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Easing for fullscreen overlay caption crossfades. |
2158
- | `caption.zoomFade` | `boolean` | `true` | Fades captions out on fullscreen zoom-in and back in on zoom-out. |
2159
- | `caption.zoomFadeDurationMs` | `number` | `300` | Duration for fullscreen caption zoom fades. |
2160
- | `caption.zoomFadeEasing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Easing for fullscreen caption zoom fades. |
2161
- | `caption.zoomInTransform` | `string` | `""` | Optional transform applied while captions fade out on zoom-in. |
2162
- | `caption.zoomOutTransform` | `string` | `""` | Optional transform used as the starting point when captions fade back in on zoom-out. |
2163
- | `slider.duration` | `number` | `25` | Fullscreen slider motion duration. |
2164
- | `slider.friction` | `number` | `0.68` | Fullscreen slider friction. |
2165
- | `slider.direction` | `"ltr" \| "rtl"` | `"ltr"` | Fullscreen slider interaction direction. |
2166
- | `slider.gap` | `number \| Record<string, number>` | `0` | Responsive pixel gap between fullscreen slides. Named keys resolve from `GalleryCore.breakpoints`. |
2167
- | `slider.skipSnaps` | `boolean \| { enabled?: boolean; threshold?: number }` | `false` | Allows fullscreen drag momentum to skip snap points. Object form matches the base slider `scroll.skipSnaps` behavior. |
2168
- | `slider.strictSnaps` | `boolean` | `false` | Prevents one fullscreen drag release from settling more than one snap away from where the drag started. Overrides `slider.skipSnaps`. |
2169
- | `zoom.clickZoomLevel` | `number` | `2.5` | Zoom level used for click-to-zoom. |
2170
- | `zoom.maxZoomLevel` | `number` | `3` | Maximum allowed zoom level. |
2171
- | `zoom.panDuration` | `number` | `43` | Pan settling duration. |
2172
- | `zoom.panFriction` | `number` | `0.68` | Pan friction. |
2173
- | `effects.introDuration` | `number \| { transform?: number; fade?: number }` | `{ transform: 300, fade: 500 }` | Open and close intro timing. A scalar applies to both paths. Use `transform` for scale/FLIP handoffs and `fade` for opacity-only paths. |
2174
- | `effects.introEasing` | `string \| { transform?: string; fade?: string }` | `"cubic-bezier(.4,0,.22,1)"` | Open and close intro easing. A scalar applies to both paths. Object keys mirror `introDuration`. |
2175
- | `effects.introFade` | `boolean` | `false` | Forces fade intro behavior. |
2176
- | `effects.introStickyNavSelector` | `string` | `—` | Selector for a sticky navigation element that may cover the source image during scale intro or close path calculations. |
2177
- | `effects.crossfade.controls` | `boolean` | `false` | Uses crossfade transitions for fullscreen arrow navigation and animated slide requests. Also enables wheel crossfade unless `effects.crossfade.wheel` is provided. |
2178
- | `effects.crossfade.drag` | `boolean` | `false` | Scrubs adjacent fullscreen slides with crossfade during drag instead of moving the track. |
2179
- | `effects.crossfade.wheel` | `boolean \| CrossFadeWheelOptions` | `effects.crossfade.controls` | Uses wheel or touchpad travel as a one-slide-at-a-time fullscreen crossfade gesture. Set `false` to keep arrow crossfades while using normal wheel scrolling. |
2180
- | `effects.crossfade.wheel.enabled` | `boolean` | `true` when object form is used | Enables or disables fullscreen wheel crossfade when using the object form. |
2181
- | `effects.crossfade.wheel.sensitivity` | `number` | `5` | Multiplies wheel delta into virtual drag progress. Higher values reach the commit threshold sooner. |
2182
- | `effects.crossfade.wheel.commitThreshold` | `number` | `0.38` | Progress needed to commit to the previous or next fullscreen slide. Values are clamped from `0` to below `0.5`. |
2183
- | `effects.crossfade.wheel.durationMs` | `number` | `effects.crossfade.durationMs` | Fade duration after fullscreen wheel crossfade commits. |
2184
- | `effects.crossfade.wheel.sessionGapMs` | `number` | `24` | Short quiet window used to distinguish same-direction touchpad tail from a fresh fullscreen wheel gesture after a committed wheel crossfade. |
2185
- | `effects.crossfade.durationMs` | `number` | `120` | Shared fullscreen crossfade duration for controls, drag release, and wheel commit unless wheel overrides it. |
2186
- | `effects.crossfade.easing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Shared fullscreen crossfade easing. |
2187
- | `lazyLoad.images.enabled` | `boolean` | `—` | Enables fullscreen image lazy loading. Base-visible indices predecode matching fullscreen images, and fullscreen index changes allow the active image slide to mount or apply its source. |
2188
- | `lazyLoad.images.spinner` | `boolean \| ReactNode \| ((args) => ReactNode)` | `—` | Spinner override for fullscreen images. |
2189
- | `lazyLoad.images.spinnerClassName` | `string` | `—` | Spinner class for image slides. |
2190
- | `lazyLoad.images.spinnerStyle` | `React.CSSProperties` | `—` | Spinner style for image slides. |
2191
- | `lazyLoad.videos.enabled` | `boolean` | `—` | Opts fullscreen videos into lazy mounting. Base-visible indices prewarm matching video posters/sources and fullscreen index changes mount the active or already-prepared video slide. By default fullscreen Plyr videos mount eagerly in the hidden fullscreen tree. |
2192
- | `lazyLoad.videos.spinner` | `boolean \| ReactNode \| ((args) => ReactNode)` | `—` | Spinner override for fullscreen videos. |
2193
- | `lazyLoad.videos.spinnerClassName` | `string` | `—` | Spinner class for video slides. |
2194
- | `lazyLoad.videos.spinnerStyle` | `React.CSSProperties` | `—` | Spinner style for video slides. |
2471
+ | Option | Type | Default | Notes |
2472
+ | ----------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2473
+ | `enabled` | `boolean` | `false` | Master switch for fullscreen UI. |
2474
+ | `items` | `MediaItem[] \| string[]` | `—` | Declared in the type, but current fullscreen media resolution comes from `GalleryCore.fullscreenItems`. |
2475
+ | `renderImage` | `({ item, index, isZoomed, className, baseStyle }) => ReactNode` | `—` | Custom fullscreen image renderer. Must render a real descendant `<img>`. With `lazyLoad.images.enabled`, the renderer is mounted only when the slide is allowed and the runtime watches that descendant image for load/decode readiness. |
2476
+ | `closeScroll` | `boolean \| FullscreenCloseScrollOptions` | `false` | Scrolls the matching base item into the center of the viewport when fullscreen closes. `true` enables the default before-close scroll; object form defaults `enabled` to `true`. |
2477
+ | `closeScroll.enabled` | `boolean \| "desktop-only" \| "mobile-only" \| ((context) => boolean)` | `true` in object form | Enables close-scroll conditionally. Function form receives the current fullscreen index, layout, target element, viewport and pointer details, and the resolved `isMobile` flag. |
2478
+ | `closeScroll.timing` | `"before-close" \| "after-close"` | `"before-close"` | Chooses whether to scroll before the close animation starts or after the modal has closed. |
2479
+ | `closeScroll.mobileDetection` | `(context: FullscreenMobileDetectionContext) => boolean` | built-in heuristic | Overrides mobile detection used by `"desktop-only"`, `"mobile-only"`, and the resolver context. The built-in heuristic treats narrow touch/no-hover viewports as mobile. |
2480
+ | `video.source` | `(item: MediaItem, index: number) => Plyr.SourceInfo` | `—` | Builds fullscreen Plyr sources for video items. |
2481
+ | `video.options` | `Plyr.Options \| ((item: MediaItem, index: number) => Plyr.Options)` | `—` | Builds fullscreen Plyr options. |
2482
+ | `video.playOnOpen` | `boolean` | `false` | Attempts to play the fullscreen Plyr video when fullscreen opens directly onto a video slide. Browser autoplay rules still apply. |
2483
+ | `video.style` | `React.CSSProperties` | `—` | Fullscreen player inline style. |
2484
+ | `video.className` | `string` | `—` | Fullscreen player class. |
2485
+ | `controls.close.enabled` | `boolean` | `true` | Toggles the close button. |
2486
+ | `controls.close.style` | `React.CSSProperties` | `{}` | Close button inline style. |
2487
+ | `controls.close.className` | `string` | `""` | Close button class. |
2488
+ | `controls.close.render` | `() => ReactNode` | `—` | Custom close button renderer. |
2489
+ | `controls.arrows.enabled` | `boolean` | `true` | Toggles fullscreen arrows. |
2490
+ | `controls.arrows.arrow` | `ElementStyle` | `{}` | Shared arrow style. |
2491
+ | `controls.arrows.prev` | `ElementStyle` | `{}` | Previous-arrow override. |
2492
+ | `controls.arrows.next` | `ElementStyle` | `{}` | Next-arrow override. |
2493
+ | `controls.arrows.render` | `({ dir }) => ReactNode` | `—` | Custom renderer for both arrows. |
2494
+ | `controls.arrows.renderPrev` | `() => ReactNode` | `—` | Custom previous arrow. |
2495
+ | `controls.arrows.renderNext` | `() => ReactNode` | `—` | Custom next arrow. |
2496
+ | `controls.counter.enabled` | `boolean` | `true` | Toggles the index counter. |
2497
+ | `controls.counter.style` | `React.CSSProperties` | `{}` | Counter inline style. |
2498
+ | `controls.counter.className` | `string` | `""` | Counter class. |
2499
+ | `controls.counter.render` | `({ index, count }) => ReactNode` | `—` | Custom counter renderer. |
2500
+ | `caption.className` | `string` | `—` | Caption root class. |
2501
+ | `caption.style` | `React.CSSProperties` | `—` | Caption root style. |
2502
+ | `caption.placement` | `FsCaptionPlacement \| FsCaptionPlacement[] \| Record<string, FsCaptionPlacement>` | `—` | Preferred caption placement. Responsive maps use the `GalleryCore.breakpoints` keys such as `xs`, `md`, and `lg`. |
2503
+ | `caption.width` | `number \| string \| Record<string, number \| string>` | `—` | Caption area width. Strings can use `px` or `%`; percentages are viewport-relative in fullscreen. Responsive maps use breakpoint keys. |
2504
+ | `caption.height` | `number \| string \| Record<string, number \| string>` | `—` | Caption area height. Strings can use `px` or `%`; percentages are viewport-relative in fullscreen. Responsive maps use breakpoint keys. |
2505
+ | `caption.breakpoint` | `number` | `—` | Viewport cutoff for switching placement logic. |
2506
+ | `caption.render` | `({ item, index, isZoomed }) => ReactNode` | `—` | Custom caption renderer. |
2507
+ | `caption.layout` | `"overlay" \| "slide"` | `—` | Chooses whether the caption overlays the media or lives in the slide layout. |
2508
+ | `caption.overlayCrossfadeTarget` | `"content" \| "overlay"` | `"content"` | Selects whether overlay caption changes fade only the rendered caption content or the whole overlay layer. |
2509
+ | `caption.overlayCrossfadeDurationMs` | `number` | `300` | Duration for fullscreen overlay caption crossfades. |
2510
+ | `caption.overlayCrossfadeEasing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Easing for fullscreen overlay caption crossfades. |
2511
+ | `caption.zoomFade` | `boolean` | `true` | Fades captions out on fullscreen zoom-in and back in on zoom-out. |
2512
+ | `caption.zoomFadeDurationMs` | `number` | `300` | Duration for fullscreen caption zoom fades. |
2513
+ | `caption.zoomFadeEasing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Easing for fullscreen caption zoom fades. |
2514
+ | `caption.zoomInTransform` | `string` | `""` | Optional transform applied while captions fade out on zoom-in. |
2515
+ | `caption.zoomOutTransform` | `string` | `""` | Optional transform used as the starting point when captions fade back in on zoom-out. |
2516
+ | `slider.duration` | `number` | `25` | Fullscreen slider motion duration. |
2517
+ | `slider.friction` | `number` | `0.68` | Fullscreen slider friction. |
2518
+ | `slider.direction` | `"ltr" \| "rtl"` | `"ltr"` | Fullscreen slider interaction direction. |
2519
+ | `slider.gap` | `number \| Record<string, number>` | `0` | Responsive pixel gap between fullscreen slides. Named keys resolve from `GalleryCore.breakpoints`. |
2520
+ | `slider.skipSnaps` | `boolean \| { enabled?: boolean; threshold?: number }` | `false` | Allows fullscreen drag momentum to skip snap points. Object form matches the base slider `scroll.skipSnaps` behavior. |
2521
+ | `slider.strictSnaps` | `boolean` | `false` | Prevents one fullscreen drag release from settling more than one snap away from where the drag started. Overrides `slider.skipSnaps`. |
2522
+ | `zoom.clickZoomLevel` | `number` | `2.5` | Zoom level used for click-to-zoom. |
2523
+ | `zoom.maxZoomLevel` | `number` | `3` | Maximum allowed zoom level. |
2524
+ | `zoom.panDuration` | `number` | `43` | Pan settling duration. |
2525
+ | `zoom.panFriction` | `number` | `0.68` | Pan friction. |
2526
+ | `effects.introDuration` | `number \| { transform?: number; fade?: number }` | `{ transform: 300, fade: 500 }` | Open and close intro timing. A scalar applies to both paths. Use `transform` for scale/FLIP handoffs and `fade` for opacity-only paths. |
2527
+ | `effects.introEasing` | `string \| { transform?: string; fade?: string }` | `"cubic-bezier(.4,0,.22,1)"` | Open and close intro easing. A scalar applies to both paths. Object keys mirror `introDuration`. |
2528
+ | `effects.introFade` | `boolean` | `false` | Forces fade intro behavior. |
2529
+ | `effects.introStickyNavSelector` | `string` | `—` | Selector for a sticky navigation element that may cover the source image during scale intro or close path calculations. |
2530
+ | `effects.crossfade.controls` | `boolean` | `false` | Uses crossfade transitions for fullscreen arrow navigation and animated slide requests. Also enables wheel crossfade unless `effects.crossfade.wheel` is provided. |
2531
+ | `effects.crossfade.drag` | `boolean` | `false` | Scrubs adjacent fullscreen slides with crossfade during drag instead of moving the track. |
2532
+ | `effects.crossfade.wheel` | `boolean \| CrossFadeWheelOptions` | `effects.crossfade.controls` | Uses wheel or touchpad travel as a one-slide-at-a-time fullscreen crossfade gesture. Set `false` to keep arrow crossfades while using normal wheel scrolling. |
2533
+ | `effects.crossfade.wheel.enabled` | `boolean` | `true` when object form is used | Enables or disables fullscreen wheel crossfade when using the object form. |
2534
+ | `effects.crossfade.wheel.sensitivity` | `number` | `5` | Multiplies wheel delta into virtual drag progress. Higher values reach the commit threshold sooner. |
2535
+ | `effects.crossfade.wheel.commitThreshold` | `number` | `0.38` | Progress needed to commit to the previous or next fullscreen slide. Values are clamped from `0` to below `0.5`. |
2536
+ | `effects.crossfade.wheel.durationMs` | `number` | `effects.crossfade.durationMs` | Fade duration after fullscreen wheel crossfade commits. |
2537
+ | `effects.crossfade.wheel.sessionGapMs` | `number` | `24` | Short quiet window used to distinguish same-direction touchpad tail from a fresh fullscreen wheel gesture after a committed wheel crossfade. |
2538
+ | `effects.crossfade.durationMs` | `number` | `120` | Shared fullscreen crossfade duration for controls, drag release, and wheel commit unless wheel overrides it. |
2539
+ | `effects.crossfade.easing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Shared fullscreen crossfade easing. |
2540
+ | `lazyLoad.images.enabled` | `boolean` | `—` | Enables fullscreen image lazy loading. Base-visible indices predecode matching fullscreen images, and fullscreen index changes allow the active image slide to mount or apply its source. |
2541
+ | `lazyLoad.images.spinner` | `boolean \| ReactNode \| ((args) => ReactNode)` | `—` | Spinner override for fullscreen images. |
2542
+ | `lazyLoad.images.spinnerClassName` | `string` | `—` | Spinner class for image slides. |
2543
+ | `lazyLoad.images.spinnerStyle` | `React.CSSProperties` | `—` | Spinner style for image slides. |
2544
+ | `lazyLoad.videos.enabled` | `boolean` | `—` | Opts fullscreen videos into lazy mounting. Base-visible indices prewarm matching video posters/sources and fullscreen index changes mount the active or already-prepared video slide. By default fullscreen Plyr videos mount eagerly in the hidden fullscreen tree. |
2545
+ | `lazyLoad.videos.spinner` | `boolean \| ReactNode \| ((args) => ReactNode)` | `—` | Spinner override for fullscreen videos. |
2546
+ | `lazyLoad.videos.spinnerClassName` | `string` | `—` | Spinner class for video slides. |
2547
+ | `lazyLoad.videos.spinnerStyle` | `React.CSSProperties` | `—` | Spinner style for video slides. |
2195
2548
 
2196
2549
  Fullscreen uses the transform close path only when the matching base image is actually exposed in the viewport. If that image is missing, offscreen, or fully covered by another page element, fullscreen falls back to the opacity close path.
2197
2550
 
@@ -2290,109 +2643,109 @@ For overlay captions, style the rendered caption content to fill the reserved ca
2290
2643
 
2291
2644
  #### `FsCounterArgs`
2292
2645
 
2293
- | Field | Type | Notes |
2294
- | --- | --- | --- |
2646
+ | Field | Type | Notes |
2647
+ | ------- | -------- | ------------------------- |
2295
2648
  | `index` | `number` | Current fullscreen index. |
2296
- | `count` | `number` | Total slide count. |
2649
+ | `count` | `number` | Total slide count. |
2297
2650
 
2298
2651
  #### `FsCaptionRenderArgs`
2299
2652
 
2300
- | Field | Type | Notes |
2301
- | --- | --- | --- |
2302
- | `item` | `MediaItem` | Active fullscreen item. |
2303
- | `index` | `number` | Active fullscreen index. |
2304
- | `isZoomed` | `boolean` | `true` when the active slide is zoomed. |
2653
+ | Field | Type | Notes |
2654
+ | ---------- | ----------- | --------------------------------------- |
2655
+ | `item` | `MediaItem` | Active fullscreen item. |
2656
+ | `index` | `number` | Active fullscreen index. |
2657
+ | `isZoomed` | `boolean` | `true` when the active slide is zoomed. |
2305
2658
 
2306
2659
  #### `FsCaptionPlacement`
2307
2660
 
2308
- | Value | Notes |
2309
- | --- | --- |
2310
- | `"top"` | Places the caption above the media. |
2311
- | `"right"` | Places the caption to the right of the media. |
2312
- | `"bottom"` | Places the caption below the media. |
2313
- | `"left"` | Places the caption to the left of the media. |
2661
+ | Value | Notes |
2662
+ | ---------- | --------------------------------------------- |
2663
+ | `"top"` | Places the caption above the media. |
2664
+ | `"right"` | Places the caption to the right of the media. |
2665
+ | `"bottom"` | Places the caption below the media. |
2666
+ | `"left"` | Places the caption to the left of the media. |
2314
2667
 
2315
2668
  #### `FsIntroRequest`
2316
2669
 
2317
- | Field | Type | Notes |
2318
- | --- | --- | --- |
2319
- | `originalImage` | `HTMLImageElement \| null` | Origin image used for scale transitions. |
2320
- | `index` | `number` | Target fullscreen index. |
2321
- | `method` | `"fade" \| "scale"` | Requested intro method. |
2322
- | `closestSelector` | `string \| undefined` | Selector used to resolve the source slide element. |
2670
+ | Field | Type | Notes |
2671
+ | ----------------- | -------------------------- | -------------------------------------------------- |
2672
+ | `originalImage` | `HTMLImageElement \| null` | Origin image used for scale transitions. |
2673
+ | `index` | `number` | Target fullscreen index. |
2674
+ | `method` | `"fade" \| "scale"` | Requested intro method. |
2675
+ | `closestSelector` | `string \| undefined` | Selector used to resolve the source slide element. |
2323
2676
 
2324
2677
  #### `FullscreenLazyLoadArgs`
2325
2678
 
2326
- | Field | Type | Notes |
2327
- | --- | --- | --- |
2328
- | `kind` | `"image" \| "video"` | Media kind currently loading. |
2679
+ | Field | Type | Notes |
2680
+ | --------- | ---------------------- | ---------------------------------------------- |
2681
+ | `kind` | `"image" \| "video"` | Media kind currently loading. |
2329
2682
  | `isClone` | `boolean \| undefined` | `true` for cloned looped slides when relevant. |
2330
2683
 
2331
2684
  #### `FullscreenThumbnailSlider` props
2332
2685
 
2333
2686
  `FullscreenThumbnailSliderProps` is exported from both the package root and `react-motion-gallery/fullscreenThumbnails`. The table below summarizes the prop surface.
2334
2687
 
2335
- | Option | Type | Default | Notes |
2336
- | --- | --- | --- | --- |
2337
- | `bridge` | `FullscreenThumbnailBridge` | `—` | Bridge returned from `useFullscreenController`. |
2338
- | `items` | `{ thumbSrc: string; alt?: string }[]` | `—` | Thumbnail list. |
2339
- | `position` | `"top" \| "right" \| "bottom" \| "left"` | `—` | Thumbnail rail position. |
2340
- | `containerClassName` | `string` | `—` | Thumbnail container class. |
2341
- | `containerStyle` | `React.CSSProperties` | `—` | Thumbnail container style. |
2342
- | `thumbnailWidth` | `number \| string` | `—` | Individual thumbnail width. |
2343
- | `thumbnailHeight` | `number \| string` | `—` | Individual thumbnail height. |
2344
- | `thumbnailsCenter` | `boolean` | `—` | Centers the thumbnail strip within its container. |
2345
- | `thumbnailsContainerWidth` | `number \| string` | `—` | Explicit strip width. |
2346
- | `thumbnailsContainerHeight` | `number \| string` | `—` | Explicit strip height. |
2347
- | `fadeDurationMs` | `number` | `300` | Mount and unmount fade duration. |
2348
- | `fadeEasing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Fade easing. |
2349
- | `thumbnailItemClassName` | `string` | `—` | Thumbnail item class. |
2350
- | `thumbnailItemStyle` | `React.CSSProperties` | `—` | Thumbnail item style. |
2351
- | `gap` | `number` | `—` | Gap between thumbnails. |
2352
- | `freeScroll` | `boolean` | `—` | Enables free thumbnail dragging. |
2353
- | `groupCells` | `boolean` | `—` | Groups thumbnail cells into snaps. |
2354
- | `loop` | `boolean` | `—` | Loops the thumbnail slider. |
2355
- | `axis` | `"x" \| "y"` | `—` | Declared in the prop type, but the current implementation does not wire it through. |
2356
- | `skipSnaps` | `boolean` | `—` | Allows momentum to skip snaps. |
2357
- | `centerActiveThumb` | `boolean` | `—` | Keeps the active thumbnail centered. |
2358
- | `selectDuration` | `number` | `—` | Selection motion duration. |
2359
- | `freeScrollDuration` | `number` | `—` | Free-scroll settling duration. |
2360
- | `sliderFriction` | `number` | `—` | Thumbnail slider friction. |
2361
- | `breakpointMap` | `Record<string, number>` | `{ xs: 0, sm: 640, md: 768, lg: 1024, xl: 1280 }` | Breakpoints used by the thumbnail strip. |
2362
- | `rippleEnabled` | `boolean` | `—` | Enables thumbnail arrow ripples. |
2363
- | `rippleClassName` | `string` | `—` | Ripple class name. |
2364
- | `showArrows` | `boolean` | `false` | Toggles thumbnail arrows. |
2365
- | `arrowStyles` | `React.CSSProperties` | `—` | Shared arrow styles. |
2366
- | `arrowClassName` | `string` | `—` | Shared arrow class. |
2367
- | `prevArrowStyles` | `React.CSSProperties` | `—` | Previous-arrow styles. |
2368
- | `prevArrowClassName` | `string` | `—` | Previous-arrow class. |
2369
- | `nextArrowStyles` | `React.CSSProperties` | `—` | Next-arrow styles. |
2370
- | `nextArrowClassName` | `string` | `—` | Next-arrow class. |
2371
- | `renderArrows` | `(args) => ReactNode` | `—` | Custom renderer for both arrows. |
2372
- | `renderPrevArrow` | `(args) => ReactNode` | `—` | Custom previous arrow. |
2373
- | `renderNextArrow` | `(args) => ReactNode` | `—` | Custom next arrow. |
2688
+ | Option | Type | Default | Notes |
2689
+ | --------------------------- | ---------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------- |
2690
+ | `bridge` | `FullscreenThumbnailBridge` | `—` | Bridge returned from `useFullscreenController`. |
2691
+ | `items` | `{ thumbSrc: string; alt?: string }[]` | `—` | Thumbnail list. |
2692
+ | `position` | `"top" \| "right" \| "bottom" \| "left"` | `—` | Thumbnail rail position. |
2693
+ | `containerClassName` | `string` | `—` | Thumbnail container class. |
2694
+ | `containerStyle` | `React.CSSProperties` | `—` | Thumbnail container style. |
2695
+ | `thumbnailWidth` | `number \| string` | `—` | Individual thumbnail width. |
2696
+ | `thumbnailHeight` | `number \| string` | `—` | Individual thumbnail height. |
2697
+ | `thumbnailsCenter` | `boolean` | `—` | Centers the thumbnail strip within its container. |
2698
+ | `thumbnailsContainerWidth` | `number \| string` | `—` | Explicit strip width. |
2699
+ | `thumbnailsContainerHeight` | `number \| string` | `—` | Explicit strip height. |
2700
+ | `fadeDurationMs` | `number` | `300` | Mount and unmount fade duration. |
2701
+ | `fadeEasing` | `string` | `"cubic-bezier(.4,0,.22,1)"` | Fade easing. |
2702
+ | `thumbnailItemClassName` | `string` | `—` | Thumbnail item class. |
2703
+ | `thumbnailItemStyle` | `React.CSSProperties` | `—` | Thumbnail item style. |
2704
+ | `gap` | `number` | `—` | Gap between thumbnails. |
2705
+ | `freeScroll` | `boolean` | `—` | Enables free thumbnail dragging. |
2706
+ | `groupCells` | `boolean` | `—` | Groups thumbnail cells into snaps. |
2707
+ | `loop` | `boolean` | `—` | Loops the thumbnail slider. |
2708
+ | `axis` | `"x" \| "y"` | `—` | Declared in the prop type, but the current implementation does not wire it through. |
2709
+ | `skipSnaps` | `boolean` | `—` | Allows momentum to skip snaps. |
2710
+ | `centerActiveThumb` | `boolean` | `—` | Keeps the active thumbnail centered. |
2711
+ | `selectDuration` | `number` | `—` | Selection motion duration. |
2712
+ | `freeScrollDuration` | `number` | `—` | Free-scroll settling duration. |
2713
+ | `sliderFriction` | `number` | `—` | Thumbnail slider friction. |
2714
+ | `breakpointMap` | `Record<string, number>` | `{ xs: 0, sm: 640, md: 768, lg: 1024, xl: 1280 }` | Breakpoints used by the thumbnail strip. |
2715
+ | `rippleEnabled` | `boolean` | `—` | Enables thumbnail arrow ripples. |
2716
+ | `rippleClassName` | `string` | `—` | Ripple class name. |
2717
+ | `showArrows` | `boolean` | `false` | Toggles thumbnail arrows. |
2718
+ | `arrowStyles` | `React.CSSProperties` | `—` | Shared arrow styles. |
2719
+ | `arrowClassName` | `string` | `—` | Shared arrow class. |
2720
+ | `prevArrowStyles` | `React.CSSProperties` | `—` | Previous-arrow styles. |
2721
+ | `prevArrowClassName` | `string` | `—` | Previous-arrow class. |
2722
+ | `nextArrowStyles` | `React.CSSProperties` | `—` | Next-arrow styles. |
2723
+ | `nextArrowClassName` | `string` | `—` | Next-arrow class. |
2724
+ | `renderArrows` | `(args) => ReactNode` | `—` | Custom renderer for both arrows. |
2725
+ | `renderPrevArrow` | `(args) => ReactNode` | `—` | Custom previous arrow. |
2726
+ | `renderNextArrow` | `(args) => ReactNode` | `—` | Custom next arrow. |
2374
2727
 
2375
2728
  #### `FullscreenThumbnailBridge`
2376
2729
 
2377
- | Field | Type | Notes |
2378
- | --- | --- | --- |
2379
- | `mountEl` | `HTMLDivElement \| null` | Portal mount node for the thumbnail strip. |
2380
- | `fsSub` | `FullscreenSliderSub` | Fullscreen slider index channel used internally. |
2381
- | `visible` | `boolean` | `true` when the strip should be visible. |
2382
- | `invisible` | `boolean` | `true` during hidden transitional states. |
2383
- | `direction` | `"ltr" \| "rtl"` | Fullscreen direction. |
2384
- | `registerLayout` | `(layout: FullscreenThumbnailSlotLayout) => void` | Registers the slot layout metadata. |
2385
- | `clearLayout` | `() => void` | Clears the current slot layout. |
2730
+ | Field | Type | Notes |
2731
+ | ---------------- | ------------------------------------------------- | ------------------------------------------------ |
2732
+ | `mountEl` | `HTMLDivElement \| null` | Portal mount node for the thumbnail strip. |
2733
+ | `fsSub` | `FullscreenSliderSub` | Fullscreen slider index channel used internally. |
2734
+ | `visible` | `boolean` | `true` when the strip should be visible. |
2735
+ | `invisible` | `boolean` | `true` during hidden transitional states. |
2736
+ | `direction` | `"ltr" \| "rtl"` | Fullscreen direction. |
2737
+ | `registerLayout` | `(layout: FullscreenThumbnailSlotLayout) => void` | Registers the slot layout metadata. |
2738
+ | `clearLayout` | `() => void` | Clears the current slot layout. |
2386
2739
 
2387
2740
  #### `FullscreenThumbnailSlotLayout`
2388
2741
 
2389
- | Field | Type | Notes |
2390
- | --- | --- | --- |
2391
- | `position` | `"top" \| "right" \| "bottom" \| "left"` | Thumbnail rail position. |
2392
- | `className` | `string \| undefined` | Slot container class. |
2393
- | `style` | `React.CSSProperties \| undefined` | Slot container style. |
2394
- | `fadeDurationMs` | `number \| undefined` | Slot fade duration. |
2395
- | `fadeEasing` | `string \| undefined` | Slot fade easing. |
2742
+ | Field | Type | Notes |
2743
+ | ---------------- | ---------------------------------------- | ------------------------ |
2744
+ | `position` | `"top" \| "right" \| "bottom" \| "left"` | Thumbnail rail position. |
2745
+ | `className` | `string \| undefined` | Slot container class. |
2746
+ | `style` | `React.CSSProperties \| undefined` | Slot container style. |
2747
+ | `fadeDurationMs` | `number \| undefined` | Slot fade duration. |
2748
+ | `fadeEasing` | `string \| undefined` | Slot fade easing. |
2396
2749
 
2397
2750
  ## ZoomPanImage
2398
2751
 
@@ -2422,38 +2775,38 @@ export function ZoomPanCard() {
2422
2775
 
2423
2776
  `ZoomPanImage` forwards its ref to the root `HTMLDivElement`. Standard image attributes are passed to the inner `<img>` except `children`, `className`, and `style`; use `className` / `style` for the root and `imageClassName` / `imageStyle` for the rendered image.
2424
2777
 
2425
- | Prop | Type | Default | Notes |
2426
- | --- | --- | --- | --- |
2427
- | `ref` | `React.Ref<HTMLDivElement>` | `—` | Forwarded to the root clipping container. |
2428
- | `src` | `string \| undefined` | `—` | Forwarded to the inner image. |
2429
- | `alt` | `string \| undefined` | `—` | Forwarded to the inner image. |
2430
- | `className` | `string` | `—` | Class name for the root clipping container. |
2431
- | `style` | `React.CSSProperties` | built-in root styles | Merged onto the root clipping container after the built-in `position`, `overflow`, sizing, and touch-action styles. |
2432
- | `imageClassName` | `string` | `—` | Class name for the inner image element. |
2433
- | `imageStyle` | `React.CSSProperties` | built-in image styles | Merged onto the inner image after the built-in `display`, `objectFit`, transform, selection, and cursor styles. |
2434
- | `zoom` | `ZoomPanOptions` | default zoom options | Configures click zoom, wheel/pinch zoom limits, pan motion, and optional plugins. |
2435
- | `disabled` | `boolean` | `false` | Disables zoom, pan, pinch, wheel, and hover behavior and resets the image to identity while disabled. |
2436
- | `onDragStart` | `React.DragEventHandler<HTMLImageElement>` | `—` | Called after native image dragging is prevented. |
2437
- | Other image attributes | `Omit<React.ImgHTMLAttributes<HTMLImageElement>, "children" \| "className" \| "style">` | `—` | Forwarded to the inner image, including `loading`, `decoding`, `srcSet`, `sizes`, ARIA attributes, and image event handlers. |
2778
+ | Prop | Type | Default | Notes |
2779
+ | ---------------------- | --------------------------------------------------------------------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
2780
+ | `ref` | `React.Ref<HTMLDivElement>` | `—` | Forwarded to the root clipping container. |
2781
+ | `src` | `string \| undefined` | `—` | Forwarded to the inner image. |
2782
+ | `alt` | `string \| undefined` | `—` | Forwarded to the inner image. |
2783
+ | `className` | `string` | `—` | Class name for the root clipping container. |
2784
+ | `style` | `React.CSSProperties` | built-in root styles | Merged onto the root clipping container after the built-in `position`, `overflow`, sizing, and touch-action styles. |
2785
+ | `imageClassName` | `string` | `—` | Class name for the inner image element. |
2786
+ | `imageStyle` | `React.CSSProperties` | built-in image styles | Merged onto the inner image after the built-in `display`, `objectFit`, transform, selection, and cursor styles. |
2787
+ | `zoom` | `ZoomPanOptions` | default zoom options | Configures click zoom, wheel/pinch zoom limits, pan motion, and optional plugins. |
2788
+ | `disabled` | `boolean` | `false` | Disables zoom, pan, pinch, wheel, and hover behavior and resets the image to identity while disabled. |
2789
+ | `onDragStart` | `React.DragEventHandler<HTMLImageElement>` | `—` | Called after native image dragging is prevented. |
2790
+ | Other image attributes | `Omit<React.ImgHTMLAttributes<HTMLImageElement>, "children" \| "className" \| "style">` | `—` | Forwarded to the inner image, including `loading`, `decoding`, `srcSet`, `sizes`, ARIA attributes, and image event handlers. |
2438
2791
 
2439
2792
  ### Zoom options
2440
2793
 
2441
- | Option | Type | Default | Notes |
2442
- | --- | --- | --- | --- |
2443
- | `clickZoomLevel` | `number` | `2.5` | Target scale for click/tap zoom and the default hover zoom level. |
2444
- | `maxZoomLevel` | `number` | `3` | Upper scale limit for click, wheel, pinch, and hover zoom. |
2445
- | `panDuration` | `number` | `43` | Motion duration used by pan and zoom settling. |
2446
- | `panFriction` | `number` | `0.68` | Friction used by drag-pan momentum. |
2447
- | `plugins` | `ZoomPanPlugin[]` | `—` | First-party feature plugins for the zoom surface. Currently includes `zoomPanHover()`. |
2794
+ | Option | Type | Default | Notes |
2795
+ | ---------------- | ----------------- | ------- | -------------------------------------------------------------------------------------- |
2796
+ | `clickZoomLevel` | `number` | `2.5` | Target scale for click/tap zoom and the default hover zoom level. |
2797
+ | `maxZoomLevel` | `number` | `3` | Upper scale limit for click, wheel, pinch, and hover zoom. |
2798
+ | `panDuration` | `number` | `43` | Motion duration used by pan and zoom settling. |
2799
+ | `panFriction` | `number` | `0.68` | Friction used by drag-pan momentum. |
2800
+ | `plugins` | `ZoomPanPlugin[]` | `—` | First-party feature plugins for the zoom surface. Currently includes `zoomPanHover()`. |
2448
2801
 
2449
2802
  ### `zoomPanHover()` options
2450
2803
 
2451
- | Option | Type | Default | Notes |
2452
- | --- | --- | --- | --- |
2453
- | `enabled` | `boolean` | `true` | Set `false` to keep the plugin in the list while disabling hover behavior. |
2454
- | `zoomLevel` | `number` | `zoom.clickZoomLevel` | Target hover scale, clamped by `zoom.maxZoomLevel`. |
2455
- | `zoomInDurationMs` | `number` | `zoomOutDurationMs` | Duration for the hover-in zoom animation. |
2456
- | `zoomOutDurationMs` | `number` | `260` | Duration for the hover-out reset animation. |
2804
+ | Option | Type | Default | Notes |
2805
+ | ------------------- | --------- | --------------------- | -------------------------------------------------------------------------- |
2806
+ | `enabled` | `boolean` | `true` | Set `false` to keep the plugin in the list while disabling hover behavior. |
2807
+ | `zoomLevel` | `number` | `zoom.clickZoomLevel` | Target hover scale, clamped by `zoom.maxZoomLevel`. |
2808
+ | `zoomInDurationMs` | `number` | `zoomOutDurationMs` | Duration for the hover-in zoom animation. |
2809
+ | `zoomOutDurationMs` | `number` | `260` | Duration for the hover-out reset animation. |
2457
2810
 
2458
2811
  ## Video
2459
2812
 
@@ -2480,32 +2833,32 @@ export function BasicVideo() {
2480
2833
 
2481
2834
  `VideoProps` is exported from both the package root and `react-motion-gallery/video`. The table below summarizes the prop surface.
2482
2835
 
2483
- | Option | Type | Default | Notes |
2484
- | --- | --- | --- | --- |
2485
- | `src` | `string` | `—` | Source URL used to build the default Plyr source. |
2486
- | `poster` | `string` | `—` | Poster image. |
2487
- | `alt` | `string` | `—` | Optional metadata label; the `Video` component itself does not render a visible alt attribute. |
2488
- | `source` | `Plyr.SourceInfo` | auto-built MP4 source | Direct Plyr source object. Overrides `sourceBuilder`. |
2489
- | `sourceBuilder` | `({ src: string }) => Plyr.SourceInfo` | `—` | Builds the Plyr source from `src`. |
2490
- | `options` | `Plyr.Options \| (({ src, index }) => Plyr.Options)` | `—` | Direct or computed Plyr options. When omitted, the component still applies `autoplay: false` and `preload: "none"` defaults internally. |
2491
- | `className` | `string` | `—` | Player wrapper class. |
2492
- | `style` | `React.CSSProperties` | `—` | Player wrapper style. |
2493
- | `onApi` | `(api: APITypes \| null) => void` | `—` | Called whenever the Plyr API ref changes. |
2494
- | `registerApiByIndex` | `(index: number, api: APITypes \| null) => void` | `—` | Registers the API by canonical gallery index. |
2495
- | `lazyLoad.enabled` | `boolean` | `true` | `false` mounts immediately after reveal. |
2496
- | `lazyLoad.spinner` | `boolean \| ReactNode \| ((args) => ReactNode)` | `true` | `false` disables the spinner; `true` uses the built-in spinner. |
2497
- | `lazyLoad.spinnerClassName` | `string` | `—` | Spinner wrapper class. |
2498
- | `lazyLoad.spinnerStyle` | `React.CSSProperties` | `—` | Spinner wrapper style. |
2836
+ | Option | Type | Default | Notes |
2837
+ | --------------------------- | ---------------------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
2838
+ | `src` | `string` | `—` | Source URL used to build the default Plyr source. |
2839
+ | `poster` | `string` | `—` | Poster image. |
2840
+ | `alt` | `string` | `—` | Optional metadata label; the `Video` component itself does not render a visible alt attribute. |
2841
+ | `source` | `Plyr.SourceInfo` | auto-built MP4 source | Direct Plyr source object. Overrides `sourceBuilder`. |
2842
+ | `sourceBuilder` | `({ src: string }) => Plyr.SourceInfo` | `—` | Builds the Plyr source from `src`. |
2843
+ | `options` | `Plyr.Options \| (({ src, index }) => Plyr.Options)` | `—` | Direct or computed Plyr options. When omitted, the component still applies `autoplay: false` and `preload: "none"` defaults internally. |
2844
+ | `className` | `string` | `—` | Player wrapper class. |
2845
+ | `style` | `React.CSSProperties` | `—` | Player wrapper style. |
2846
+ | `onApi` | `(api: APITypes \| null) => void` | `—` | Called whenever the Plyr API ref changes. |
2847
+ | `registerApiByIndex` | `(index: number, api: APITypes \| null) => void` | `—` | Registers the API by canonical gallery index. |
2848
+ | `lazyLoad.enabled` | `boolean` | `true` | `false` mounts immediately after reveal. |
2849
+ | `lazyLoad.spinner` | `boolean \| ReactNode \| ((args) => ReactNode)` | `true` | `false` disables the spinner; `true` uses the built-in spinner. |
2850
+ | `lazyLoad.spinnerClassName` | `string` | `—` | Spinner wrapper class. |
2851
+ | `lazyLoad.spinnerStyle` | `React.CSSProperties` | `—` | Spinner wrapper style. |
2499
2852
 
2500
2853
  ### Supporting video types
2501
2854
 
2502
2855
  These helper type names are available from both the package root and `react-motion-gallery/video`.
2503
2856
 
2504
- | Type | Shape | Notes |
2505
- | --- | --- | --- |
2506
- | `RmgPlyrSourceBuilder` | `({ src: string }) => Plyr.SourceInfo` | Used by `sourceBuilder`. |
2507
- | `RmgPlyrOptionsResolver` | `Plyr.Options \| (({ src, index }) => Plyr.Options)` | Used by `options`. |
2508
- | `RmgVideoLazyLoadOptions` | `{ enabled?, spinner?, spinnerClassName?, spinnerStyle? }` | Used by `lazyLoad`. |
2857
+ | Type | Shape | Notes |
2858
+ | ------------------------- | ---------------------------------------------------------- | ------------------------ |
2859
+ | `RmgPlyrSourceBuilder` | `({ src: string }) => Plyr.SourceInfo` | Used by `sourceBuilder`. |
2860
+ | `RmgPlyrOptionsResolver` | `Plyr.Options \| (({ src, index }) => Plyr.Options)` | Used by `options`. |
2861
+ | `RmgVideoLazyLoadOptions` | `{ enabled?, spinner?, spinnerClassName?, spinnerStyle? }` | Used by `lazyLoad`. |
2509
2862
 
2510
2863
  If you do not use `Video`, you do not need `plyr` or `plyr-react`. Install those optional peer dependencies only for video playback.
2511
2864