@musecat/uikit 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +51 -0
  3. package/index.ts +78 -0
  4. package/package.json +103 -0
  5. package/packages/Components/Box/Box.tsx +173 -0
  6. package/packages/Components/Box/Box.types.ts +48 -0
  7. package/packages/Components/Button/Button.tsx +275 -0
  8. package/packages/Components/Button/Button.types.ts +44 -0
  9. package/packages/Components/Card/Card.default.tsx +180 -0
  10. package/packages/Components/Card/Card.foldable.tsx +383 -0
  11. package/packages/Components/Card/Card.module.scss +23 -0
  12. package/packages/Components/Card/Card.tsx +8 -0
  13. package/packages/Components/Card/Card.types.ts +91 -0
  14. package/packages/Components/Card/Card.utils.ts +66 -0
  15. package/packages/Components/Checkbox/Checkbox.module.scss +11 -0
  16. package/packages/Components/Checkbox/Checkbox.tsx +104 -0
  17. package/packages/Components/Checkbox/Checkbox.types.ts +24 -0
  18. package/packages/Components/CodeBox/CodeBox.copy.tsx +43 -0
  19. package/packages/Components/CodeBox/CodeBox.module.scss +72 -0
  20. package/packages/Components/CodeBox/CodeBox.tsx +91 -0
  21. package/packages/Components/CodeBox/CodeBox.types.ts +17 -0
  22. package/packages/Components/ContextMenu/ContextMenu.module.scss +8 -0
  23. package/packages/Components/ContextMenu/ContextMenu.options.tsx +126 -0
  24. package/packages/Components/ContextMenu/ContextMenu.tsx +381 -0
  25. package/packages/Components/ContextMenu/ContextMenu.types.ts +46 -0
  26. package/packages/Components/ContributionGraph/ContributionGraph.module.scss +42 -0
  27. package/packages/Components/ContributionGraph/ContributionGraph.tsx +454 -0
  28. package/packages/Components/ContributionGraph/ContributionGraph.types.ts +27 -0
  29. package/packages/Components/DatePicker/DatePicker.calendar.tsx +161 -0
  30. package/packages/Components/DatePicker/DatePicker.core.tsx +582 -0
  31. package/packages/Components/DatePicker/DatePicker.module.scss +26 -0
  32. package/packages/Components/DatePicker/DatePicker.tsx +786 -0
  33. package/packages/Components/DatePicker/DatePicker.types.ts +36 -0
  34. package/packages/Components/DatePicker/DatePicker.utils.ts +80 -0
  35. package/packages/Components/DatePicker/hooks/useCalendar.ts +61 -0
  36. package/packages/Components/DatePicker/hooks/useSelection.ts +208 -0
  37. package/packages/Components/Divider/Divider.tsx +46 -0
  38. package/packages/Components/Divider/Divider.types.ts +12 -0
  39. package/packages/Components/Header/Header.module.scss +74 -0
  40. package/packages/Components/Header/Header.tsx +47 -0
  41. package/packages/Components/Header/Header.types.ts +8 -0
  42. package/packages/Components/Icon/Icon.group.tsx +122 -0
  43. package/packages/Components/Icon/Icon.group.types.ts +20 -0
  44. package/packages/Components/Icon/Icon.tsx +341 -0
  45. package/packages/Components/Icon/Icon.types.ts +59 -0
  46. package/packages/Components/Icon/SVG/data.tsx +232 -0
  47. package/packages/Components/Input/Input.module.scss +5 -0
  48. package/packages/Components/Input/Input.tsx +87 -0
  49. package/packages/Components/Input/Input.types.ts +14 -0
  50. package/packages/Components/Label/Label.module.scss +19 -0
  51. package/packages/Components/Label/Label.tsx +115 -0
  52. package/packages/Components/Label/Label.types.ts +28 -0
  53. package/packages/Components/Layout/Layout.docs.module.scss +42 -0
  54. package/packages/Components/Layout/Layout.docs.tsx +150 -0
  55. package/packages/Components/Layout/Layout.docs.types.ts +18 -0
  56. package/packages/Components/Layout/Layout.module.scss +91 -0
  57. package/packages/Components/Layout/Layout.tsx +306 -0
  58. package/packages/Components/Layout/Layout.types.ts +71 -0
  59. package/packages/Components/Maps/OSM/MapOSM.module.scss +11 -0
  60. package/packages/Components/Maps/OSM/MapOSM.shared.ts +121 -0
  61. package/packages/Components/Maps/OSM/MapOSM.tsx +279 -0
  62. package/packages/Components/Maps/OSM/MapOSM.types.ts +18 -0
  63. package/packages/Components/Nav/Nav.module.scss +137 -0
  64. package/packages/Components/Nav/Nav.tsx +304 -0
  65. package/packages/Components/Nav/Nav.types.ts +32 -0
  66. package/packages/Components/Nav/hooks/useNavDrag.ts +374 -0
  67. package/packages/Components/Nav/hooks/useNavIndicator.ts +137 -0
  68. package/packages/Components/Pagination/Pagination.tsx +254 -0
  69. package/packages/Components/Pagination/Pagination.types.ts +18 -0
  70. package/packages/Components/Pill/Pill.tsx +127 -0
  71. package/packages/Components/Pill/Pill.types.ts +33 -0
  72. package/packages/Components/Profile/Profile.shared.ts +74 -0
  73. package/packages/Components/Profile/Profile.tsx +382 -0
  74. package/packages/Components/Profile/Profile.types.ts +55 -0
  75. package/packages/Components/Progress/Progress.module.scss +15 -0
  76. package/packages/Components/Progress/Progress.tsx +88 -0
  77. package/packages/Components/Progress/Progress.types.ts +22 -0
  78. package/packages/Components/Radio/Radio.module.scss +11 -0
  79. package/packages/Components/Radio/Radio.tsx +121 -0
  80. package/packages/Components/Radio/Radio.types.ts +20 -0
  81. package/packages/Components/Select/Select.keyboard.tsx +76 -0
  82. package/packages/Components/Select/Select.trigger.tsx +88 -0
  83. package/packages/Components/Select/Select.tsx +11 -0
  84. package/packages/Components/Select/Select.types.ts +38 -0
  85. package/packages/Components/Select/Select.utils.ts +84 -0
  86. package/packages/Components/Select/hooks/useSelectNavigation.ts +461 -0
  87. package/packages/Components/Select/hooks/useSelectSelection.ts +142 -0
  88. package/packages/Components/Select/hooks/useSelectState.ts +314 -0
  89. package/packages/Components/Select/select.control.tsx +228 -0
  90. package/packages/Components/Select/select.inner.tsx +312 -0
  91. package/packages/Components/Skeleton/Skeleton.tsx +30 -0
  92. package/packages/Components/Skeleton/Skeleton.types.ts +12 -0
  93. package/packages/Components/Spinner/Spinner.module.scss +78 -0
  94. package/packages/Components/Spinner/Spinner.tsx +125 -0
  95. package/packages/Components/Spinner/Spinner.types.ts +19 -0
  96. package/packages/Components/Text/Text.module.scss +7 -0
  97. package/packages/Components/Text/Text.tsx +49 -0
  98. package/packages/Components/Text/Text.types.ts +33 -0
  99. package/packages/Components/TimePicker/TimePicker.core.tsx +448 -0
  100. package/packages/Components/TimePicker/TimePicker.module.scss +30 -0
  101. package/packages/Components/TimePicker/TimePicker.tsx +511 -0
  102. package/packages/Components/TimePicker/TimePicker.types.ts +22 -0
  103. package/packages/Components/Timeline/Timeline.tsx +111 -0
  104. package/packages/Components/Timeline/Timeline.types.ts +23 -0
  105. package/packages/Components/Title/Title.tsx +305 -0
  106. package/packages/Components/Title/Title.types.ts +64 -0
  107. package/packages/Components/Toggle/Toggle.module.scss +32 -0
  108. package/packages/Components/Toggle/Toggle.tsx +272 -0
  109. package/packages/Components/Toggle/Toggle.types.ts +17 -0
  110. package/packages/Components/Tooltip/Tooltip.module.scss +44 -0
  111. package/packages/Components/Tooltip/Tooltip.tsx +340 -0
  112. package/packages/Components/Tooltip/Tooltip.types.ts +38 -0
  113. package/packages/Frameworks/Dialog/Dialog.background.tsx +88 -0
  114. package/packages/Frameworks/Dialog/Dialog.boot.tsx +32 -0
  115. package/packages/Frameworks/Dialog/Dialog.store.ts +199 -0
  116. package/packages/Frameworks/Dialog/Dialog.tsx +185 -0
  117. package/packages/Frameworks/Dialog/Dialog.types.ts +264 -0
  118. package/packages/Frameworks/Dialog/Dialog.utils.ts +22 -0
  119. package/packages/Frameworks/Dialog/contents/Dialog.footer.tsx +79 -0
  120. package/packages/Frameworks/Dialog/contents/Dialog.funnel.tsx +260 -0
  121. package/packages/Frameworks/Dialog/contents/Dialog.header.tsx +137 -0
  122. package/packages/Frameworks/Dialog/hooks/useDialogPosition.ts +298 -0
  123. package/packages/Frameworks/Dialog/hooks/useSheetDrag.ts +325 -0
  124. package/packages/Frameworks/Dialog/hooks/useSheetGeometry.ts +201 -0
  125. package/packages/Frameworks/Dialog/hooks/useSheetProgressive.ts +98 -0
  126. package/packages/Frameworks/Dialog/renderers/renderModal.tsx +228 -0
  127. package/packages/Frameworks/Dialog/renderers/renderPopover.tsx +220 -0
  128. package/packages/Frameworks/Dialog/renderers/renderSheet.tsx +571 -0
  129. package/packages/Frameworks/EdgeEffect/EdgeEffect.tsx +52 -0
  130. package/packages/Frameworks/EdgeEffect/EdgeEffect.types.ts +5 -0
  131. package/packages/Frameworks/Motion/Motion.presets.ts +49 -0
  132. package/packages/Frameworks/Pressable/Pressable.module.scss +19 -0
  133. package/packages/Frameworks/Pressable/Pressable.tsx +651 -0
  134. package/packages/Frameworks/Pressable/Pressable.types.ts +73 -0
  135. package/packages/Frameworks/Squircle/Squircle.tsx +263 -0
  136. package/packages/Frameworks/Squircle/Squircle.types.ts +15 -0
  137. package/packages/Frameworks/Theme/Radius.types.ts +55 -0
  138. package/packages/Frameworks/Theme/Theme.types.ts +325 -0
  139. package/packages/Frameworks/Toaster/Toaster.boot.tsx +47 -0
  140. package/packages/Frameworks/Toaster/Toaster.module.scss +168 -0
  141. package/packages/Frameworks/Toaster/Toaster.types.ts +13 -0
  142. package/packages/Frameworks/View/DNDView/DNDView.module.scss +8 -0
  143. package/packages/Frameworks/View/DNDView/DNDView.tsx +470 -0
  144. package/packages/Frameworks/View/DNDView/DNDView.types.ts +61 -0
  145. package/packages/Frameworks/View/HScrollView/HScrollView.module.scss +59 -0
  146. package/packages/Frameworks/View/HScrollView/HScrollView.tsx +386 -0
  147. package/packages/Frameworks/View/HScrollView/HScrollView.types.ts +46 -0
  148. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.counter.tsx +27 -0
  149. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.footer.tsx +76 -0
  150. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.header.tsx +49 -0
  151. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.list.tsx +74 -0
  152. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.slide.tsx +71 -0
  153. package/packages/Frameworks/View/ImageView/Dialog/ImageDialog.tsx +175 -0
  154. package/packages/Frameworks/View/ImageView/Image.controls.tsx +39 -0
  155. package/packages/Frameworks/View/ImageView/Image.hooks.ts +82 -0
  156. package/packages/Frameworks/View/ImageView/Image.module.scss +7 -0
  157. package/packages/Frameworks/View/ImageView/Image.tsx +375 -0
  158. package/packages/Frameworks/View/ImageView/Image.types.ts +71 -0
  159. package/packages/Frameworks/View/ImageView/Image.utils.ts +79 -0
  160. package/packages/Frameworks/View/View.module.scss +34 -0
  161. package/packages/Frameworks/View/View.tsx +200 -0
  162. package/packages/Frameworks/View/View.types.ts +36 -0
  163. package/packages/Frameworks/_shared/Padding.types.ts +52 -0
  164. package/packages/Frameworks/_shared/StopParentInteraction.tsx +45 -0
  165. package/packages/Frameworks/_shared/Wind.types.ts +17 -0
  166. package/packages/Frameworks/_shared/bodyScrollLock.ts +127 -0
  167. package/packages/Frameworks/_shared/layer.constants.ts +8 -0
  168. package/packages/Frameworks/_shared/normalize.ts +39 -0
  169. package/packages/Frameworks/_shared/sizing.ts +55 -0
  170. package/packages/Frameworks/_shared/useControllableState.ts +29 -0
  171. package/packages/Styles/_animation.scss +42 -0
  172. package/packages/Styles/_color.scss +131 -0
  173. package/packages/Styles/_font.scss +30 -0
  174. package/packages/Styles/_icon.scss +782 -0
  175. package/packages/Styles/_importer.scss +9 -0
  176. package/packages/Styles/_system.scss +69 -0
  177. package/packages/Styles/_textstyle.scss +111 -0
  178. package/packages/Styles/_theme.scss +452 -0
  179. package/packages/Styles/_viewport-global.scss +99 -0
  180. package/packages/Styles/_viewport.scss +108 -0
@@ -0,0 +1,375 @@
1
+ "use client";
2
+
3
+ import NextImage from "next/image";
4
+ import { useCallback, useMemo, useState } from "react";
5
+ import Dialog from "@/packages/Frameworks/Dialog/Dialog";
6
+ import Pressable from "@/packages/Frameworks/Pressable/Pressable";
7
+ import HScrollView from "@/packages/Frameworks/View/HScrollView/HScrollView";
8
+ import ImageDialog from "@/packages/Frameworks/View/ImageView/Dialog/ImageDialog";
9
+ import {
10
+ ImageLeftControl,
11
+ ImageRightControl,
12
+ } from "@/packages/Frameworks/View/ImageView/Image.controls";
13
+ import type { ImageOverlay, ImageProps } from "@/packages/Frameworks/View/ImageView/Image.types";
14
+ import type { UIKitSizeValue } from "@/packages/Frameworks/_shared/sizing";
15
+ import {
16
+ resolveAtIndex,
17
+ resolveBlurDataURL,
18
+ resolveImageSrc,
19
+ resolveItems,
20
+ resolveOverlay,
21
+ } from "@/packages/Frameworks/View/ImageView/Image.utils";
22
+ import View from "@/packages/Frameworks/View/View";
23
+
24
+ const DEFAULT_GROUP_WIDTH = "auto";
25
+ const DEFAULT_GROUP_HEIGHT = "29rem";
26
+
27
+ const PRESSABLE_STYLE: React.CSSProperties = { position: "relative", overflow: "hidden" };
28
+ const OVERLAY_STYLE: React.CSSProperties = { position: "absolute", inset: 0, pointerEvents: "none" };
29
+ const IMG_STYLE: React.CSSProperties = { height: "100%", objectFit: "cover", objectPosition: "center", display: "block" };
30
+ const NEXT_IMG_STYLE: React.CSSProperties = { objectFit: "cover", objectPosition: "center" };
31
+
32
+ function ImageGroupItem({
33
+ item,
34
+ index,
35
+ dialog,
36
+ className,
37
+ radius,
38
+ width,
39
+ height,
40
+ overlay,
41
+ groupWidth,
42
+ priority,
43
+ openDialog,
44
+ }: {
45
+ item: { id: number; src: string; alt: string; srcDialog?: string; blurDataURL?: string };
46
+ index: number;
47
+ dialog?: ImageProps["dialog"];
48
+ className?: string;
49
+ radius?: string;
50
+ width?: UIKitSizeValue | UIKitSizeValue[];
51
+ height?: UIKitSizeValue | UIKitSizeValue[];
52
+ overlay?: ImageOverlay;
53
+ groupWidth?: string;
54
+ priority?: boolean;
55
+ openDialog: (index: number) => void;
56
+ }) {
57
+ const resolvedWidth = resolveAtIndex(width, index);
58
+ const resolvedHeight = resolveAtIndex(height, index);
59
+ const resolvedOverlay = resolveOverlay(overlay, index);
60
+
61
+ return (
62
+ <Pressable
63
+ key={item.id}
64
+ radius={radius ?? "Regular"}
65
+ background="Base3"
66
+ onClick={dialog ? () => openDialog(index) : undefined}
67
+ style={PRESSABLE_STYLE}
68
+ width={resolvedWidth ?? "fit-content"}
69
+ height={resolvedHeight}
70
+ >
71
+ {(resolvedWidth ?? groupWidth) === "auto" ? (
72
+ <img
73
+ src={resolveImageSrc(item, "inline")}
74
+ alt={item.alt}
75
+ draggable={false}
76
+ className={className}
77
+ style={IMG_STYLE}
78
+ />
79
+ ) : (
80
+ <NextImage
81
+ fill
82
+ priority={priority && index === 0}
83
+ src={resolveImageSrc(item, "inline")}
84
+ alt={item.alt}
85
+ placeholder="blur"
86
+ blurDataURL={resolveBlurDataURL(item, index)}
87
+ draggable={false}
88
+ className={className}
89
+ style={NEXT_IMG_STYLE}
90
+ />
91
+ )}
92
+ {resolvedOverlay && (
93
+ <View style={OVERLAY_STYLE}>
94
+ {resolvedOverlay}
95
+ </View>
96
+ )}
97
+ </Pressable>
98
+ );
99
+ }
100
+
101
+ export default function Image({
102
+ src,
103
+ radius,
104
+ alt,
105
+ width,
106
+ height,
107
+ priority,
108
+ className,
109
+ groupWidth = DEFAULT_GROUP_WIDTH,
110
+ groupHeight = DEFAULT_GROUP_HEIGHT,
111
+ groupGap = 16,
112
+ groupClassName,
113
+ overlay,
114
+ control,
115
+ dialog,
116
+ }: ImageProps) {
117
+ const items = useMemo(() => resolveItems(src, alt), [src, alt]);
118
+
119
+ const [dialogOpen, setDialogOpen] = useState(false);
120
+ const [dialogIndex, setDialogIndex] = useState(0);
121
+
122
+ const openDialog = useCallback((index: number) => {
123
+ setDialogIndex(index);
124
+ setDialogOpen(true);
125
+ }, []);
126
+
127
+ const openDialogZero = useCallback(() => openDialog(0), [openDialog]);
128
+ const closeDialog = useCallback(() => setDialogOpen(false), []);
129
+
130
+ const controlsRenderer = useMemo(() => {
131
+ if (!control) return undefined;
132
+ return ({
133
+ scrollPrev,
134
+ scrollNext,
135
+ canScrollPrev,
136
+ canScrollNext,
137
+ }: {
138
+ scrollPrev: () => void;
139
+ scrollNext: () => void;
140
+ canScrollPrev: boolean;
141
+ canScrollNext: boolean;
142
+ }) => {
143
+ const renderLeft =
144
+ typeof control === "boolean"
145
+ ? control
146
+ : control.left !== false && control.left !== undefined;
147
+ const renderRight =
148
+ typeof control === "boolean"
149
+ ? control
150
+ : control.right !== false && control.right !== undefined;
151
+ const leftSlot =
152
+ typeof control === "object" && typeof control.left === "object"
153
+ ? control.left
154
+ : {};
155
+ const rightSlot =
156
+ typeof control === "object" && typeof control.right === "object"
157
+ ? control.right
158
+ : {};
159
+
160
+ return (
161
+ <View
162
+ gap={8}
163
+ width="100%"
164
+ className={
165
+ typeof control === "object" ? control.groupClassName : undefined
166
+ }
167
+ style={
168
+ typeof control === "object" ? control.groupStyle : undefined
169
+ }
170
+ >
171
+ {renderLeft && (
172
+ <ImageLeftControl
173
+ onClick={scrollPrev}
174
+ disabled={!canScrollPrev}
175
+ className={leftSlot.className}
176
+ style={{ zIndex: 2, ...leftSlot.style }}
177
+ />
178
+ )}
179
+ {renderRight && (
180
+ <ImageRightControl
181
+ onClick={scrollNext}
182
+ disabled={!canScrollNext}
183
+ className={rightSlot.className}
184
+ style={{ zIndex: 2, ...rightSlot.style }}
185
+ />
186
+ )}
187
+ </View>
188
+ );
189
+ };
190
+ }, [control]);
191
+
192
+ if (items.length === 0) return null;
193
+
194
+ if (items.length === 1) {
195
+ const item = items[0]!;
196
+ const resolvedWidth = resolveAtIndex(width, 0);
197
+ const resolvedHeight = resolveAtIndex(height, 0);
198
+ const resolvedOverlay = resolveOverlay(overlay, 0);
199
+
200
+ if (dialog) {
201
+ return (
202
+ <>
203
+ {}
204
+ <Pressable
205
+ radius={radius ?? "Regular"}
206
+ onClick={openDialogZero}
207
+ style={PRESSABLE_STYLE}
208
+ width={resolvedWidth ?? "fit-content"}
209
+ height={resolvedHeight}
210
+ >
211
+ {}
212
+ {(resolvedWidth ?? "auto") === "auto" ? (
213
+ <img
214
+ src={resolveImageSrc(item, "inline")}
215
+ alt={item.alt}
216
+ draggable={false}
217
+ className={className}
218
+ style={{
219
+ height: "100%",
220
+ objectFit: "cover",
221
+ objectPosition: "center",
222
+ display: "block",
223
+ }}
224
+ />
225
+ ) : (
226
+ <NextImage
227
+ fill
228
+ priority={priority}
229
+ src={resolveImageSrc(item, "inline")}
230
+ alt={item.alt}
231
+ placeholder="blur"
232
+ blurDataURL={resolveBlurDataURL(item, 0)}
233
+ draggable={false}
234
+ className={className}
235
+ style={{
236
+ objectFit: "cover",
237
+ objectPosition: "center",
238
+ }}
239
+ />
240
+ )}
241
+
242
+ {}
243
+ {resolvedOverlay && (
244
+ <View
245
+ style={{
246
+ position: "absolute",
247
+ inset: 0,
248
+ pointerEvents: "none",
249
+ }}
250
+ >
251
+ {resolvedOverlay}
252
+ </View>
253
+ )}
254
+ </Pressable>
255
+
256
+ {}
257
+ <Dialog
258
+ mode="modal"
259
+ open={dialogOpen}
260
+ onOpenChange={setDialogOpen}
261
+ modal={{
262
+ immersive: true,
263
+ exit: { overlay: false, escape: true },
264
+ custom: (
265
+ <ImageDialog
266
+ items={items}
267
+ initialIndex={dialogIndex}
268
+ dialog={dialog}
269
+ priority={priority}
270
+ className={className}
271
+ onClose={closeDialog}
272
+ />
273
+ ),
274
+ }}
275
+ />
276
+ </>
277
+ );
278
+ }
279
+
280
+ return (
281
+ <View
282
+ radius={radius ?? "Regular"}
283
+ style={PRESSABLE_STYLE}
284
+ width={resolvedWidth ?? "fit-content"}
285
+ height={resolvedHeight}
286
+ >
287
+ {}
288
+ {(resolvedWidth ?? "auto") === "auto" ? (
289
+ <img
290
+ src={resolveImageSrc(item, "inline")}
291
+ alt={item.alt}
292
+ draggable={false}
293
+ className={className}
294
+ style={IMG_STYLE}
295
+ />
296
+ ) : (
297
+ <NextImage
298
+ fill
299
+ priority={priority}
300
+ src={resolveImageSrc(item, "inline")}
301
+ alt={item.alt}
302
+ placeholder="blur"
303
+ blurDataURL={resolveBlurDataURL(item, 0)}
304
+ draggable={false}
305
+ className={className}
306
+ style={NEXT_IMG_STYLE}
307
+ />
308
+ )}
309
+
310
+ {}
311
+ {resolvedOverlay && (
312
+ <View style={OVERLAY_STYLE}>
313
+ {resolvedOverlay}
314
+ </View>
315
+ )}
316
+ </View>
317
+ );
318
+ }
319
+
320
+ return (
321
+ <>
322
+ {}
323
+ <View column gap={10} fullWidth className={groupClassName}>
324
+ <HScrollView
325
+ active
326
+ gap={groupGap}
327
+ itemWidth={groupWidth}
328
+ itemHeight={groupHeight}
329
+ showEdgeEffect={false}
330
+ renderControls={controlsRenderer}
331
+ >
332
+ {items.map((item, index) => (
333
+ <ImageGroupItem
334
+ key={item.id}
335
+ item={item}
336
+ index={index}
337
+ dialog={dialog}
338
+ className={className}
339
+ radius={radius}
340
+ width={width}
341
+ height={height}
342
+ overlay={overlay}
343
+ groupWidth={groupWidth}
344
+ priority={priority}
345
+ openDialog={openDialog}
346
+ />
347
+ ))}
348
+ </HScrollView>
349
+ </View>
350
+
351
+ {}
352
+ {dialog && (
353
+ <Dialog
354
+ mode="modal"
355
+ open={dialogOpen}
356
+ onOpenChange={setDialogOpen}
357
+ modal={{
358
+ immersive: true,
359
+ exit: { overlay: false, escape: true },
360
+ custom: (
361
+ <ImageDialog
362
+ items={items}
363
+ initialIndex={dialogIndex}
364
+ dialog={dialog}
365
+ priority={priority}
366
+ className={className}
367
+ onClose={closeDialog}
368
+ />
369
+ ),
370
+ }}
371
+ />
372
+ )}
373
+ </>
374
+ );
375
+ }
@@ -0,0 +1,71 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ import type { UIKitSizeValue } from "@/packages/Frameworks/_shared/sizing";
3
+ import type { RadiusProps } from "@/packages/Frameworks/Theme/Radius.types";
4
+
5
+ export type ImageItem = {
6
+ id: number;
7
+ src: string;
8
+ alt: string;
9
+ srcDialog?: string;
10
+ blurDataURL?: string;
11
+ };
12
+
13
+ export type ImageControlSlot = {
14
+ className?: string;
15
+ style?: CSSProperties;
16
+ };
17
+
18
+ export type ImageOverlay = ReactNode | ReactNode[];
19
+
20
+ export interface ImageProps extends RadiusProps {
21
+ src: string | string[] | ImageItem[];
22
+
23
+ alt?: string | string[];
24
+
25
+ width?: UIKitSizeValue | UIKitSizeValue[];
26
+
27
+ height?: UIKitSizeValue | UIKitSizeValue[];
28
+
29
+ priority?: boolean;
30
+
31
+ className?: string;
32
+
33
+ groupWidth?: string;
34
+ groupHeight?: string;
35
+ groupGap?: UIKitSizeValue;
36
+ groupClassName?: string;
37
+
38
+ overlay?: ImageOverlay;
39
+
40
+ control?:
41
+ | boolean
42
+ | {
43
+ left?: ImageControlSlot | boolean;
44
+ right?: ImageControlSlot | boolean;
45
+ groupClassName?: string;
46
+ groupStyle?: CSSProperties;
47
+ };
48
+
49
+ dialog?: {
50
+ className?: string;
51
+ overlay?: ImageOverlay;
52
+ header?: {
53
+ className?: string;
54
+ style?: CSSProperties;
55
+ content?: ReactNode;
56
+ };
57
+ control?:
58
+ | boolean
59
+ | {
60
+ left?: ImageControlSlot | boolean;
61
+ right?: ImageControlSlot | boolean;
62
+ };
63
+ footer?: {
64
+ className?: string;
65
+ style?: CSSProperties;
66
+ content?: (item: ImageItem, index: number) => ReactNode;
67
+ list?: boolean;
68
+ counter?: boolean;
69
+ };
70
+ };
71
+ }
@@ -0,0 +1,79 @@
1
+ import type { ReactNode } from "react";
2
+ import type {
3
+ ImageItem,
4
+ ImageOverlay,
5
+ } from "@/packages/Frameworks/View/ImageView/Image.types";
6
+
7
+ export type ImageMode = "inline" | "dialog";
8
+
9
+ export function resolveItems(
10
+ src: string | string[] | ImageItem[],
11
+ alt?: string | string[],
12
+ ): ImageItem[] {
13
+ if (typeof src === "string") {
14
+ const resolvedAlt =
15
+ typeof alt === "string" ? alt : Array.isArray(alt) ? (alt[0] ?? "") : "";
16
+ return [{ id: 0, src, alt: resolvedAlt }];
17
+ }
18
+
19
+ if (src.length === 0) return [];
20
+
21
+ if (typeof src[0] === "string") {
22
+ return (src as string[]).map((s, i) => ({
23
+ id: i,
24
+ src: s,
25
+ alt: Array.isArray(alt)
26
+ ? (alt[i] ?? "")
27
+ : typeof alt === "string"
28
+ ? alt
29
+ : "",
30
+ }));
31
+ }
32
+
33
+ return src as ImageItem[];
34
+ }
35
+
36
+ export function resolveImageSrc(item: ImageItem, mode: ImageMode): string {
37
+ return mode === "dialog" ? (item.srcDialog ?? item.src) : item.src;
38
+ }
39
+
40
+ export function resolveOverlay(
41
+ overlay: ImageOverlay | undefined,
42
+ index: number,
43
+ ): ReactNode {
44
+ if (overlay == null) return null;
45
+ if (Array.isArray(overlay)) return (overlay as ReactNode[])[index] ?? null;
46
+ return overlay;
47
+ }
48
+
49
+ export function resolveAtIndex<T>(
50
+ value: T | T[] | undefined,
51
+ index: number,
52
+ ): T | undefined {
53
+ if (value === undefined) return undefined;
54
+ if (Array.isArray(value)) return (value as T[])[index];
55
+ return value;
56
+ }
57
+
58
+ const FALLBACK_BLURS = [
59
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjZmVjYWNhIi8+PC9zdmc+",
60
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjZmVkN2FhIi8+PC9zdmc+",
61
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjZmVmMDhhIi8+PC9zdmc+",
62
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjZDlmOTlkIi8+PC9zdmc+",
63
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjYmJmN2QwIi8+PC9zdmc+",
64
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjOTlmNmU0Ii8+PC9zdmc+",
65
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjYmZkYmZlIi8+PC9zdmc+",
66
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjYzdkMmZlIi8+PC9zdmc+",
67
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjZTlkNWZmIi8+PC9zdmc+",
68
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiBmaWxsPSIjZmJjZmU4Ii8+PC9zdmc+",
69
+ ];
70
+
71
+ export function resolveBlurDataURL(item: ImageItem, index: number): string {
72
+ if (item.blurDataURL) return item.blurDataURL;
73
+
74
+ const hash =
75
+ String(item.id)
76
+ .split("")
77
+ .reduce((acc, char) => acc + char.charCodeAt(0), 0) + index;
78
+ return FALLBACK_BLURS[hash % FALLBACK_BLURS.length]!;
79
+ }
@@ -0,0 +1,34 @@
1
+ @use "../../Styles/viewport" as viewport;
2
+
3
+ .View {
4
+ ::-webkit-scrollbar {
5
+ display: none;
6
+ }
7
+ }
8
+
9
+ :where(.View) {
10
+ gap: var(--view-default-gap, 2.4rem);
11
+ }
12
+
13
+ .MobileStrict {
14
+ @media (max-width: 679.98px) {
15
+ display: grid !important;
16
+ grid-template-columns: repeat(2, 1fr);
17
+ }
18
+
19
+ @media (max-width: 469.98px) {
20
+ display: grid !important;
21
+ grid-template-columns: 1fr;
22
+ }
23
+ }
24
+
25
+ @include viewport.down(w3) {
26
+ .MobileOrder {
27
+ order: var(--view-mobile-order);
28
+ }
29
+ }
30
+
31
+ .FullWidth,
32
+ .FullWidth > * {
33
+ width: 100%;
34
+ }