@giddaa-housing/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/README.md +41 -0
  2. package/dist/accordion.d.ts +11 -0
  3. package/dist/accordion.js +124 -0
  4. package/dist/accordion.js.map +1 -0
  5. package/dist/alert.d.ts +18 -0
  6. package/dist/alert.js +152 -0
  7. package/dist/alert.js.map +1 -0
  8. package/dist/avatar.d.ts +14 -0
  9. package/dist/avatar.js +152 -0
  10. package/dist/avatar.js.map +1 -0
  11. package/dist/badge.d.ts +14 -0
  12. package/dist/badge.js +119 -0
  13. package/dist/badge.js.map +1 -0
  14. package/dist/bar-chart.d.ts +136 -0
  15. package/dist/bar-chart.js +302 -0
  16. package/dist/bar-chart.js.map +1 -0
  17. package/dist/base.css +32 -0
  18. package/dist/breadcrumb.d.ts +12 -0
  19. package/dist/breadcrumb.js +161 -0
  20. package/dist/breadcrumb.js.map +1 -0
  21. package/dist/bullet-chart.d.ts +98 -0
  22. package/dist/bullet-chart.js +189 -0
  23. package/dist/bullet-chart.js.map +1 -0
  24. package/dist/button-group.d.ts +18 -0
  25. package/dist/button-group.js +170 -0
  26. package/dist/button-group.js.map +1 -0
  27. package/dist/button.d.ts +19 -0
  28. package/dist/button.js +146 -0
  29. package/dist/button.js.map +1 -0
  30. package/dist/calendar.d.ts +9 -0
  31. package/dist/calendar.js +293 -0
  32. package/dist/calendar.js.map +1 -0
  33. package/dist/card.d.ts +18 -0
  34. package/dist/card.js +186 -0
  35. package/dist/card.js.map +1 -0
  36. package/dist/carousel.d.ts +53 -0
  37. package/dist/carousel.js +396 -0
  38. package/dist/carousel.js.map +1 -0
  39. package/dist/chart.d.ts +122 -0
  40. package/dist/chart.js +494 -0
  41. package/dist/chart.js.map +1 -0
  42. package/dist/checkbox.d.ts +73 -0
  43. package/dist/checkbox.js +87 -0
  44. package/dist/checkbox.js.map +1 -0
  45. package/dist/collapsible.d.ts +8 -0
  46. package/dist/collapsible.js +82 -0
  47. package/dist/collapsible.js.map +1 -0
  48. package/dist/combobox.d.ts +30 -0
  49. package/dist/combobox.js +713 -0
  50. package/dist/combobox.js.map +1 -0
  51. package/dist/date-picker.d.ts +22 -0
  52. package/dist/date-picker.js +528 -0
  53. package/dist/date-picker.js.map +1 -0
  54. package/dist/dialog.d.ts +30 -0
  55. package/dist/dialog.js +339 -0
  56. package/dist/dialog.js.map +1 -0
  57. package/dist/dropdown-menu.d.ts +35 -0
  58. package/dist/dropdown-menu.js +376 -0
  59. package/dist/dropdown-menu.js.map +1 -0
  60. package/dist/dropzone.d.ts +128 -0
  61. package/dist/dropzone.js +490 -0
  62. package/dist/dropzone.js.map +1 -0
  63. package/dist/empty-state.d.ts +17 -0
  64. package/dist/empty-state.js +146 -0
  65. package/dist/empty-state.js.map +1 -0
  66. package/dist/field.d.ts +29 -0
  67. package/dist/field.js +345 -0
  68. package/dist/field.js.map +1 -0
  69. package/dist/files/red-hat-display-latin-300-italic.woff +0 -0
  70. package/dist/files/red-hat-display-latin-300-italic.woff2 +0 -0
  71. package/dist/files/red-hat-display-latin-300-normal.woff +0 -0
  72. package/dist/files/red-hat-display-latin-300-normal.woff2 +0 -0
  73. package/dist/files/red-hat-display-latin-400-italic.woff +0 -0
  74. package/dist/files/red-hat-display-latin-400-italic.woff2 +0 -0
  75. package/dist/files/red-hat-display-latin-400-normal.woff +0 -0
  76. package/dist/files/red-hat-display-latin-400-normal.woff2 +0 -0
  77. package/dist/files/red-hat-display-latin-500-italic.woff +0 -0
  78. package/dist/files/red-hat-display-latin-500-italic.woff2 +0 -0
  79. package/dist/files/red-hat-display-latin-500-normal.woff +0 -0
  80. package/dist/files/red-hat-display-latin-500-normal.woff2 +0 -0
  81. package/dist/files/red-hat-display-latin-600-italic.woff +0 -0
  82. package/dist/files/red-hat-display-latin-600-italic.woff2 +0 -0
  83. package/dist/files/red-hat-display-latin-600-normal.woff +0 -0
  84. package/dist/files/red-hat-display-latin-600-normal.woff2 +0 -0
  85. package/dist/files/red-hat-display-latin-700-italic.woff +0 -0
  86. package/dist/files/red-hat-display-latin-700-italic.woff2 +0 -0
  87. package/dist/files/red-hat-display-latin-700-normal.woff +0 -0
  88. package/dist/files/red-hat-display-latin-700-normal.woff2 +0 -0
  89. package/dist/files/red-hat-display-latin-800-italic.woff +0 -0
  90. package/dist/files/red-hat-display-latin-800-italic.woff2 +0 -0
  91. package/dist/files/red-hat-display-latin-800-normal.woff +0 -0
  92. package/dist/files/red-hat-display-latin-800-normal.woff2 +0 -0
  93. package/dist/files/red-hat-display-latin-900-italic.woff +0 -0
  94. package/dist/files/red-hat-display-latin-900-italic.woff2 +0 -0
  95. package/dist/files/red-hat-display-latin-900-normal.woff +0 -0
  96. package/dist/files/red-hat-display-latin-900-normal.woff2 +0 -0
  97. package/dist/files/red-hat-display-latin-ext-300-italic.woff +0 -0
  98. package/dist/files/red-hat-display-latin-ext-300-italic.woff2 +0 -0
  99. package/dist/files/red-hat-display-latin-ext-300-normal.woff +0 -0
  100. package/dist/files/red-hat-display-latin-ext-300-normal.woff2 +0 -0
  101. package/dist/files/red-hat-display-latin-ext-400-italic.woff +0 -0
  102. package/dist/files/red-hat-display-latin-ext-400-italic.woff2 +0 -0
  103. package/dist/files/red-hat-display-latin-ext-400-normal.woff +0 -0
  104. package/dist/files/red-hat-display-latin-ext-400-normal.woff2 +0 -0
  105. package/dist/files/red-hat-display-latin-ext-500-italic.woff +0 -0
  106. package/dist/files/red-hat-display-latin-ext-500-italic.woff2 +0 -0
  107. package/dist/files/red-hat-display-latin-ext-500-normal.woff +0 -0
  108. package/dist/files/red-hat-display-latin-ext-500-normal.woff2 +0 -0
  109. package/dist/files/red-hat-display-latin-ext-600-italic.woff +0 -0
  110. package/dist/files/red-hat-display-latin-ext-600-italic.woff2 +0 -0
  111. package/dist/files/red-hat-display-latin-ext-600-normal.woff +0 -0
  112. package/dist/files/red-hat-display-latin-ext-600-normal.woff2 +0 -0
  113. package/dist/files/red-hat-display-latin-ext-700-italic.woff +0 -0
  114. package/dist/files/red-hat-display-latin-ext-700-italic.woff2 +0 -0
  115. package/dist/files/red-hat-display-latin-ext-700-normal.woff +0 -0
  116. package/dist/files/red-hat-display-latin-ext-700-normal.woff2 +0 -0
  117. package/dist/files/red-hat-display-latin-ext-800-italic.woff +0 -0
  118. package/dist/files/red-hat-display-latin-ext-800-italic.woff2 +0 -0
  119. package/dist/files/red-hat-display-latin-ext-800-normal.woff +0 -0
  120. package/dist/files/red-hat-display-latin-ext-800-normal.woff2 +0 -0
  121. package/dist/files/red-hat-display-latin-ext-900-italic.woff +0 -0
  122. package/dist/files/red-hat-display-latin-ext-900-italic.woff2 +0 -0
  123. package/dist/files/red-hat-display-latin-ext-900-normal.woff +0 -0
  124. package/dist/files/red-hat-display-latin-ext-900-normal.woff2 +0 -0
  125. package/dist/funnel-chart.d.ts +84 -0
  126. package/dist/funnel-chart.js +569 -0
  127. package/dist/funnel-chart.js.map +1 -0
  128. package/dist/index.d.ts +87 -0
  129. package/dist/index.js +10448 -0
  130. package/dist/index.js.map +1 -0
  131. package/dist/input-group.d.ts +31 -0
  132. package/dist/input-group.js +380 -0
  133. package/dist/input-group.js.map +1 -0
  134. package/dist/input-size-context.d.ts +11 -0
  135. package/dist/input-size-context.js +24 -0
  136. package/dist/input-size-context.js.map +1 -0
  137. package/dist/input.d.ts +13 -0
  138. package/dist/input.js +83 -0
  139. package/dist/input.js.map +1 -0
  140. package/dist/item.d.ts +8 -0
  141. package/dist/item.js +93 -0
  142. package/dist/item.js.map +1 -0
  143. package/dist/kpi-card.d.ts +24 -0
  144. package/dist/kpi-card.js +243 -0
  145. package/dist/kpi-card.js.map +1 -0
  146. package/dist/label.d.ts +8 -0
  147. package/dist/label.js +68 -0
  148. package/dist/label.js.map +1 -0
  149. package/dist/line-chart.d.ts +155 -0
  150. package/dist/line-chart.js +269 -0
  151. package/dist/line-chart.js.map +1 -0
  152. package/dist/logo.d.ts +6 -0
  153. package/dist/logo.js +146 -0
  154. package/dist/logo.js.map +1 -0
  155. package/dist/number-input.d.ts +41 -0
  156. package/dist/number-input.js +418 -0
  157. package/dist/number-input.js.map +1 -0
  158. package/dist/page-header.d.ts +13 -0
  159. package/dist/page-header.js +113 -0
  160. package/dist/page-header.js.map +1 -0
  161. package/dist/pagination.d.ts +35 -0
  162. package/dist/pagination.js +352 -0
  163. package/dist/pagination.js.map +1 -0
  164. package/dist/password-input.d.ts +13 -0
  165. package/dist/password-input.js +356 -0
  166. package/dist/password-input.js.map +1 -0
  167. package/dist/phone-input.d.ts +21 -0
  168. package/dist/phone-input.js +573 -0
  169. package/dist/phone-input.js.map +1 -0
  170. package/dist/photo-gallery.d.ts +42 -0
  171. package/dist/photo-gallery.js +756 -0
  172. package/dist/photo-gallery.js.map +1 -0
  173. package/dist/pie-chart.d.ts +125 -0
  174. package/dist/pie-chart.js +192 -0
  175. package/dist/pie-chart.js.map +1 -0
  176. package/dist/popover.d.ts +20 -0
  177. package/dist/popover.js +169 -0
  178. package/dist/popover.js.map +1 -0
  179. package/dist/progress.d.ts +28 -0
  180. package/dist/progress.js +315 -0
  181. package/dist/progress.js.map +1 -0
  182. package/dist/radio-group.d.ts +32 -0
  183. package/dist/radio-group.js +100 -0
  184. package/dist/radio-group.js.map +1 -0
  185. package/dist/red-hat-display.css +2 -0
  186. package/dist/rich-text-editor.d.ts +16 -0
  187. package/dist/rich-text-editor.js +1000 -0
  188. package/dist/rich-text-editor.js.map +1 -0
  189. package/dist/search-input.d.ts +19 -0
  190. package/dist/search-input.js +411 -0
  191. package/dist/search-input.js.map +1 -0
  192. package/dist/select.d.ts +21 -0
  193. package/dist/select.js +305 -0
  194. package/dist/select.js.map +1 -0
  195. package/dist/separator.d.ts +10 -0
  196. package/dist/separator.js +72 -0
  197. package/dist/separator.js.map +1 -0
  198. package/dist/sheet.d.ts +24 -0
  199. package/dist/sheet.js +282 -0
  200. package/dist/sheet.js.map +1 -0
  201. package/dist/sidebar.d.ts +70 -0
  202. package/dist/sidebar.js +1078 -0
  203. package/dist/sidebar.js.map +1 -0
  204. package/dist/size-context.d.ts +11 -0
  205. package/dist/size-context.js +22 -0
  206. package/dist/size-context.js.map +1 -0
  207. package/dist/skeleton.d.ts +5 -0
  208. package/dist/skeleton.js +48 -0
  209. package/dist/skeleton.js.map +1 -0
  210. package/dist/sparkline.d.ts +94 -0
  211. package/dist/sparkline.js +568 -0
  212. package/dist/sparkline.js.map +1 -0
  213. package/dist/stepper.d.ts +43 -0
  214. package/dist/stepper.js +296 -0
  215. package/dist/stepper.js.map +1 -0
  216. package/dist/styles.css +2 -0
  217. package/dist/switch.d.ts +29 -0
  218. package/dist/switch.js +108 -0
  219. package/dist/switch.js.map +1 -0
  220. package/dist/tab-list.d.ts +38 -0
  221. package/dist/tab-list.js +394 -0
  222. package/dist/tab-list.js.map +1 -0
  223. package/dist/table.d.ts +69 -0
  224. package/dist/table.js +197 -0
  225. package/dist/table.js.map +1 -0
  226. package/dist/tabs.d.ts +18 -0
  227. package/dist/tabs.js +159 -0
  228. package/dist/tabs.js.map +1 -0
  229. package/dist/tag.d.ts +25 -0
  230. package/dist/tag.js +160 -0
  231. package/dist/tag.js.map +1 -0
  232. package/dist/textarea.d.ts +12 -0
  233. package/dist/textarea.js +78 -0
  234. package/dist/textarea.js.map +1 -0
  235. package/dist/theme/giddaa.css +2 -0
  236. package/dist/time-picker.d.ts +13 -0
  237. package/dist/time-picker.js +472 -0
  238. package/dist/time-picker.js.map +1 -0
  239. package/dist/toast.d.ts +23 -0
  240. package/dist/toast.js +330 -0
  241. package/dist/toast.js.map +1 -0
  242. package/dist/tooltip.d.ts +11 -0
  243. package/dist/tooltip.js +96 -0
  244. package/dist/tooltip.js.map +1 -0
  245. package/dist/tree-map.d.ts +127 -0
  246. package/dist/tree-map.js +652 -0
  247. package/dist/tree-map.js.map +1 -0
  248. package/package.json +328 -0
@@ -0,0 +1,756 @@
1
+ // src/components/ui/photo-gallery.tsx
2
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
3
+ import {
4
+ ChevronLeftIcon as ChevronLeftIcon2,
5
+ ChevronRightIcon as ChevronRightIcon2,
6
+ Fullscreen,
7
+ XIcon
8
+ } from "lucide-react";
9
+ import * as React2 from "react";
10
+
11
+ // src/lib/cn.ts
12
+ import { clsx } from "clsx";
13
+ import { extendTailwindMerge } from "tailwind-merge";
14
+ var twMerge = extendTailwindMerge({
15
+ extend: {
16
+ classGroups: {
17
+ "font-size": [
18
+ {
19
+ text: [
20
+ "gdt-display",
21
+ "gdt-h1",
22
+ "gdt-h2",
23
+ "gdt-h3",
24
+ "gdt-h4",
25
+ "gdt-h5",
26
+ "gdt-xl",
27
+ "gdt-lg",
28
+ "gdt-md",
29
+ "gdt-sm",
30
+ "gdt-xs",
31
+ "gdt-subtext",
32
+ "gdt-capitalized"
33
+ ]
34
+ }
35
+ ]
36
+ }
37
+ }
38
+ });
39
+ function cn(...inputs) {
40
+ return twMerge(clsx(inputs));
41
+ }
42
+
43
+ // src/components/ui/button.tsx
44
+ import { Button as ButtonPrimitive } from "@base-ui/react/button";
45
+ import { cva } from "class-variance-authority";
46
+ import { Loader } from "lucide-react";
47
+
48
+ // src/components/ui/size-context.tsx
49
+ import { createContext, useContext } from "react";
50
+ import { Fragment, jsx } from "react/jsx-runtime";
51
+ var SizeContext = createContext(void 0);
52
+ function useComponentSize(size, fallback = "md") {
53
+ const inheritedSize = useContext(SizeContext);
54
+ return size ?? inheritedSize ?? fallback;
55
+ }
56
+
57
+ // src/components/ui/button.tsx
58
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
59
+ var buttonVariants = cva(
60
+ "group/button inline-flex shrink-0 items-center justify-center rounded-full bg-clip-padding whitespace-nowrap font-bold transition-[background-color,color,border-color,box-shadow,transform] outline-none select-none focus-visible:border-line-focus focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-invalid:border-line-danger aria-invalid:ring-3 aria-invalid:ring-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
61
+ {
62
+ variants: {
63
+ variant: {
64
+ primary: "bg-action-primary text-fg-on-brand hover:bg-action-primary-hover active:bg-action-primary-pressed",
65
+ "primary-outline": "border-2 border-action-primary bg-transparent text-fg-brand hover:bg-surface-brand-subtle active:bg-surface-brand-subtle data-focused:bg-surface-brand-subtle data-pressed:bg-surface-brand-subtle",
66
+ secondary: "bg-surface-brand-subtle text-fg-brand hover:bg-action-primary-hover hover:text-fg-on-brand active:bg-action-primary-pressed active:text-fg-on-brand data-focused:bg-action-primary-hover data-pressed:bg-action-primary-pressed data-focused:text-fg-on-brand data-pressed:text-fg-on-brand",
67
+ "secondary-outline": "border-2 border-action-secondary bg-transparent text-fg-brand hover:bg-surface-brand-subtle active:bg-surface-brand-subtle data-focused:bg-surface-brand-subtle data-pressed:bg-surface-brand-subtle",
68
+ tertiary: "bg-action-tertiary text-fg-primary hover:bg-action-tertiary-hover active:bg-action-tertiary-pressed data-focused:bg-action-tertiary-hover data-pressed:bg-action-tertiary-pressed",
69
+ "tertiary-outline": "border border-line-strong bg-transparent text-fg-primary hover:bg-surface active:bg-surface-raised data-focused:bg-surface data-pressed:bg-surface-raised",
70
+ ghost: "bg-transparent text-fg-primary hover:bg-surface active:bg-surface-raised data-focused:bg-surface data-pressed:bg-surface-raised",
71
+ danger: "bg-action-danger text-fg-on-brand hover:bg-action-danger-hover active:bg-action-danger-pressed data-focused:bg-action-danger-hover data-pressed:bg-action-danger-pressed",
72
+ "glass-overlay": "bg-surface-glass-overlay-button text-fg-on-brand",
73
+ "link-brand": "border-transparent bg-transparent px-0 text-fg-brand hover:underline active:underline active:opacity-70",
74
+ "link-neutral": "border-transparent bg-transparent px-0 text-fg-secondary hover:underline active:underline active:opacity-70"
75
+ },
76
+ size: {
77
+ sm: "h-8 gap-2 px-3.5 text-gdt-xs has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3 [&_svg:not([class*='size-'])]:size-3.5",
78
+ md: "h-10 gap-2 px-4.5 text-gdt-sm has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4",
79
+ lg: "h-12.5 gap-2.5 px-5.5 text-gdt-md has-data-[icon=inline-end]:pr-5 has-data-[icon=inline-start]:pl-5 [&_svg:not([class*='size-'])]:size-4.5",
80
+ xl: "h-14 gap-3 px-6.5 text-gdt-lg has-data-[icon=inline-end]:pr-6 has-data-[icon=inline-start]:pl-6 [&_svg:not([class*='size-'])]:size-5",
81
+ icon: "size-10",
82
+ "icon-xs": "size-6 text-gdt-xs [&_svg:not([class*='size-'])]:size-3",
83
+ "icon-sm": "size-8 text-gdt-sm [&_svg:not([class*='size-'])]:size-3.5",
84
+ "icon-lg": "size-12.5 text-gdt-md [&_svg:not([class*='size-'])]:size-4.5",
85
+ "icon-xl": "size-14 text-gdt-lg [&_svg:not([class*='size-'])]:size-5"
86
+ }
87
+ },
88
+ defaultVariants: {
89
+ variant: "primary",
90
+ size: "md"
91
+ }
92
+ }
93
+ );
94
+ function Button({
95
+ className,
96
+ variant = "primary",
97
+ size,
98
+ isLoading = false,
99
+ children,
100
+ disabled = false,
101
+ ...props
102
+ }) {
103
+ const inheritedSize = useComponentSize();
104
+ const resolvedSize = size ?? inheritedSize;
105
+ const _disabled = disabled || isLoading;
106
+ return /* @__PURE__ */ jsxs(
107
+ ButtonPrimitive,
108
+ {
109
+ "data-slot": "button",
110
+ className: cn(buttonVariants({ variant, size: resolvedSize, className })),
111
+ disabled: _disabled,
112
+ ...props,
113
+ children: [
114
+ isLoading && /* @__PURE__ */ jsx2(Loader, { className: "size-4 animate-spin" }),
115
+ " ",
116
+ children
117
+ ]
118
+ }
119
+ );
120
+ }
121
+
122
+ // src/components/ui/carousel.tsx
123
+ import { cva as cva2 } from "class-variance-authority";
124
+ import useEmblaCarousel from "embla-carousel-react";
125
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
126
+ import * as React from "react";
127
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
128
+ var glassChrome = "border border-fg-on-brand/[0.18] bg-surface-glass-overlay-button backdrop-blur-[16px]";
129
+ var carouselVariants = cva2(
130
+ "relative overflow-hidden rounded-2xl bg-surface-brand-subtle",
131
+ {
132
+ variants: {
133
+ imageSize: {
134
+ sm: "w-[320px] max-w-full aspect-[320/220]",
135
+ md: "w-[520px] max-w-full aspect-[520/340]",
136
+ lg: "w-[800px] max-w-full aspect-[800/500]",
137
+ full: "w-full"
138
+ }
139
+ },
140
+ defaultVariants: {
141
+ imageSize: "md"
142
+ }
143
+ }
144
+ );
145
+ var CarouselContext = React.createContext(null);
146
+ function useCarousel() {
147
+ const context = React.useContext(CarouselContext);
148
+ if (!context) {
149
+ throw new Error("useCarousel must be used within a <Carousel />");
150
+ }
151
+ return context;
152
+ }
153
+ function Carousel({
154
+ orientation = "horizontal",
155
+ opts,
156
+ setApi,
157
+ plugins,
158
+ size,
159
+ imageSize,
160
+ className,
161
+ children,
162
+ ...props
163
+ }) {
164
+ const resolvedSize = useComponentSize(size);
165
+ const [carouselRef, api] = useEmblaCarousel(
166
+ {
167
+ ...opts,
168
+ axis: orientation === "horizontal" ? "x" : "y"
169
+ },
170
+ plugins
171
+ );
172
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false);
173
+ const [canScrollNext, setCanScrollNext] = React.useState(false);
174
+ const [selectedIndex, setSelectedIndex] = React.useState(0);
175
+ const [scrollSnaps, setScrollSnaps] = React.useState([]);
176
+ const onSelect = React.useCallback((api2) => {
177
+ if (!api2) return;
178
+ setCanScrollPrev(api2.canScrollPrev());
179
+ setCanScrollNext(api2.canScrollNext());
180
+ setSelectedIndex(api2.selectedScrollSnap());
181
+ }, []);
182
+ const onReInit = React.useCallback(
183
+ (api2) => {
184
+ if (!api2) return;
185
+ setScrollSnaps(api2.scrollSnapList());
186
+ onSelect(api2);
187
+ },
188
+ [onSelect]
189
+ );
190
+ const scrollPrev = React.useCallback(() => {
191
+ api?.scrollPrev();
192
+ }, [api]);
193
+ const scrollNext = React.useCallback(() => {
194
+ api?.scrollNext();
195
+ }, [api]);
196
+ const scrollTo = React.useCallback(
197
+ (index) => {
198
+ api?.scrollTo(index);
199
+ },
200
+ [api]
201
+ );
202
+ const handleKeyDown = React.useCallback(
203
+ (event) => {
204
+ if (event.key === "ArrowLeft") {
205
+ event.preventDefault();
206
+ scrollPrev();
207
+ } else if (event.key === "ArrowRight") {
208
+ event.preventDefault();
209
+ scrollNext();
210
+ }
211
+ },
212
+ [scrollPrev, scrollNext]
213
+ );
214
+ React.useEffect(() => {
215
+ if (!api || !setApi) return;
216
+ setApi(api);
217
+ }, [api, setApi]);
218
+ React.useEffect(() => {
219
+ if (!api) return;
220
+ onReInit(api);
221
+ api.on("reInit", onReInit);
222
+ api.on("select", onSelect);
223
+ return () => {
224
+ api?.off("reInit", onReInit);
225
+ api?.off("select", onSelect);
226
+ };
227
+ }, [api, onReInit, onSelect]);
228
+ return /* @__PURE__ */ jsx3(
229
+ CarouselContext.Provider,
230
+ {
231
+ value: {
232
+ carouselRef,
233
+ api,
234
+ orientation,
235
+ size: resolvedSize,
236
+ scrollPrev,
237
+ scrollNext,
238
+ scrollTo,
239
+ canScrollPrev,
240
+ canScrollNext,
241
+ selectedIndex,
242
+ scrollSnaps
243
+ },
244
+ children: /* @__PURE__ */ jsx3(
245
+ "div",
246
+ {
247
+ onKeyDownCapture: handleKeyDown,
248
+ className: cn(carouselVariants({ imageSize }), className),
249
+ role: "region",
250
+ "aria-roledescription": "carousel",
251
+ "data-slot": "carousel",
252
+ ...props,
253
+ children
254
+ }
255
+ )
256
+ }
257
+ );
258
+ }
259
+ function CarouselContent({ className, ...props }) {
260
+ const { carouselRef, orientation } = useCarousel();
261
+ return /* @__PURE__ */ jsx3(
262
+ "div",
263
+ {
264
+ ref: carouselRef,
265
+ className: "h-full overflow-hidden",
266
+ "data-slot": "carousel-content",
267
+ children: /* @__PURE__ */ jsx3(
268
+ "div",
269
+ {
270
+ className: cn(
271
+ "flex h-full",
272
+ orientation === "horizontal" ? "" : "flex-col",
273
+ className
274
+ ),
275
+ ...props
276
+ }
277
+ )
278
+ }
279
+ );
280
+ }
281
+ function CarouselItem({ className, ...props }) {
282
+ return (
283
+ // biome-ignore lint/a11y/useSemanticElements: a carousel slide is a role="group" with aria-roledescription per the W3C pattern, not a fieldset.
284
+ /* @__PURE__ */ jsx3(
285
+ "div",
286
+ {
287
+ role: "group",
288
+ "aria-roledescription": "slide",
289
+ "data-slot": "carousel-item",
290
+ className: cn("min-w-0 shrink-0 grow-0 basis-full", className),
291
+ ...props
292
+ }
293
+ )
294
+ );
295
+ }
296
+ var arrowVariants = cva2(
297
+ cn(
298
+ "absolute top-1/2 z-10 grid -translate-y-1/2 place-items-center rounded-full text-fg-on-brand/95 shadow-1 transition-[transform,opacity] outline-none",
299
+ glassChrome,
300
+ "hover:scale-105 focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] active:scale-95 disabled:pointer-events-none disabled:opacity-40"
301
+ ),
302
+ {
303
+ variants: {
304
+ size: {
305
+ sm: "size-8 [&_svg]:size-4",
306
+ md: "size-10 [&_svg]:size-[18px]",
307
+ lg: "size-12 [&_svg]:size-5"
308
+ },
309
+ side: {
310
+ prev: "",
311
+ next: ""
312
+ }
313
+ },
314
+ compoundVariants: [
315
+ { size: "sm", side: "prev", className: "left-3" },
316
+ { size: "sm", side: "next", className: "right-3" },
317
+ { size: "md", side: "prev", className: "left-4" },
318
+ { size: "md", side: "next", className: "right-4" },
319
+ { size: "lg", side: "prev", className: "left-5" },
320
+ { size: "lg", side: "next", className: "right-5" }
321
+ ],
322
+ defaultVariants: {
323
+ size: "md",
324
+ side: "prev"
325
+ }
326
+ }
327
+ );
328
+
329
+ // src/components/ui/photo-gallery.tsx
330
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
331
+ function useGalleryCarousel(index, onIndexChange) {
332
+ const [api, setApi] = React2.useState();
333
+ const [selectedIndex, setSelectedIndex] = React2.useState(index ?? 0);
334
+ const initialIndexRef = React2.useRef(index ?? 0);
335
+ const onIndexChangeRef = React2.useRef(onIndexChange);
336
+ React2.useEffect(() => {
337
+ onIndexChangeRef.current = onIndexChange;
338
+ });
339
+ React2.useEffect(() => {
340
+ if (!api) return;
341
+ const onSelect = () => {
342
+ const next = api.selectedScrollSnap();
343
+ setSelectedIndex(next);
344
+ onIndexChangeRef.current?.(next);
345
+ };
346
+ onSelect();
347
+ api.on("select", onSelect).on("reInit", onSelect);
348
+ return () => {
349
+ api.off("select", onSelect).off("reInit", onSelect);
350
+ };
351
+ }, [api]);
352
+ React2.useEffect(() => {
353
+ if (api && index !== void 0 && index !== api.selectedScrollSnap()) {
354
+ api.scrollTo(index, false);
355
+ }
356
+ }, [api, index]);
357
+ const scrollTo = React2.useCallback(
358
+ (i) => api?.scrollTo(i, false),
359
+ [api]
360
+ );
361
+ const scrollPrev = React2.useCallback(() => api?.scrollPrev(false), [api]);
362
+ const scrollNext = React2.useCallback(() => api?.scrollNext(false), [api]);
363
+ return {
364
+ setApi,
365
+ selectedIndex,
366
+ initialIndex: initialIndexRef.current,
367
+ scrollTo,
368
+ scrollPrev,
369
+ scrollNext
370
+ };
371
+ }
372
+ function GalleryFrame({
373
+ image,
374
+ fit = "cover",
375
+ className
376
+ }) {
377
+ return /* @__PURE__ */ jsx4(
378
+ "div",
379
+ {
380
+ "data-slot": "photo-gallery-frame",
381
+ className: cn(
382
+ "relative flex size-full items-center justify-center overflow-hidden",
383
+ className
384
+ ),
385
+ children: image?.src ? /* @__PURE__ */ jsx4(
386
+ "img",
387
+ {
388
+ src: image.src,
389
+ alt: image.alt ?? "Property image",
390
+ className: cn(
391
+ "size-full",
392
+ fit === "contain" ? "object-contain" : "object-cover"
393
+ ),
394
+ draggable: false
395
+ }
396
+ ) : /* @__PURE__ */ jsx4("span", { className: "text-gdt-sm font-medium text-fg-caption-placeholder", children: "Property Image" })
397
+ }
398
+ );
399
+ }
400
+ var Images = (props) => /* @__PURE__ */ jsxs3(
401
+ "svg",
402
+ {
403
+ "aria-hidden": "true",
404
+ xmlns: "http://www.w3.org/2000/svg",
405
+ width: 15,
406
+ height: 16,
407
+ viewBox: "0 0 15 16",
408
+ fill: "none",
409
+ ...props,
410
+ children: [
411
+ /* @__PURE__ */ jsx4(
412
+ "path",
413
+ {
414
+ fill: "currentColor",
415
+ d: "M6.72 14.332c-.705 0-1.38-.3-1.878-.83a2.935 2.935 0 0 1-.779-2.003V2.012l-3.02.738c-.36.102-.667.353-.853.697a1.587 1.587 0 0 0-.143 1.136l2.589 10.309c.083.318.261.598.507.798.246.2.546.309.856.31.115 0 .233-.015.349-.048l6.3-1.62H6.72ZM8.125 6c.69 0 1.25-.597 1.25-1.333 0-.735-.56-1.333-1.25-1.333s-1.25.598-1.25 1.333c0 .736.56 1.333 1.25 1.333Z"
416
+ }
417
+ ),
418
+ /* @__PURE__ */ jsx4(
419
+ "path",
420
+ {
421
+ fill: "currentColor",
422
+ d: "M15 2a2.07 2.07 0 0 0-.55-1.414A1.818 1.818 0 0 0 13.126 0h-6.25C6.376 0 5.9.21 5.547.586A2.07 2.07 0 0 0 4.998 2v9.332c0 .53.198 1.04.55 1.414.352.375.829.586 1.326.586h6.25c.498 0 .975-.21 1.327-.586.351-.375.549-.883.549-1.414V2Zm-8.126-.667h6.25c.166 0 .325.07.443.195A.69.69 0 0 1 13.75 2v4.292l-.475-.507a1.094 1.094 0 0 0-.356-.253 1.037 1.037 0 0 0-.839 0 1.094 1.094 0 0 0-.355.253L8.749 8.96l-.787-.84a1.094 1.094 0 0 0-.356-.254 1.037 1.037 0 0 0-.839 0 1.094 1.094 0 0 0-.355.254l-.163.173V2a.69.69 0 0 1 .183-.472.606.606 0 0 1 .442-.195Z"
423
+ }
424
+ )
425
+ ]
426
+ }
427
+ );
428
+ function GalleryCounter({
429
+ current,
430
+ total,
431
+ className
432
+ }) {
433
+ if (total <= 1) return null;
434
+ return /* @__PURE__ */ jsxs3(
435
+ "div",
436
+ {
437
+ "data-slot": "photo-gallery-counter",
438
+ className: cn(
439
+ "pointer-events-none absolute z-10 inline-flex items-center gap-1.5 rounded-full border border-line bg-surface-overlay px-2.5 py-1 text-gdt-xs font-bold text-fg-primary shadow-2",
440
+ className
441
+ ),
442
+ children: [
443
+ /* @__PURE__ */ jsx4(Images, { "aria-hidden": "true", className: "size-3.5" }),
444
+ /* @__PURE__ */ jsxs3("span", { children: [
445
+ current,
446
+ " / ",
447
+ total
448
+ ] })
449
+ ]
450
+ }
451
+ );
452
+ }
453
+ function GalleryButton({
454
+ className,
455
+ variant = "ghost",
456
+ ...rest
457
+ }) {
458
+ return /* @__PURE__ */ jsx4(
459
+ Button,
460
+ {
461
+ size: "icon",
462
+ className: cn(
463
+ "border border-line bg-surface-overlay text-fg-primary shadow-2 transition-[background-color] outline-none hover:bg-surface-raised focus-visible:border-line-focus focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] size-9 sm:size-11",
464
+ className
465
+ ),
466
+ variant,
467
+ ...rest
468
+ }
469
+ );
470
+ }
471
+ function GalleryArrow({
472
+ side,
473
+ onClick,
474
+ className
475
+ }) {
476
+ const Icon = side === "prev" ? ChevronLeftIcon2 : ChevronRightIcon2;
477
+ return /* @__PURE__ */ jsx4(
478
+ GalleryButton,
479
+ {
480
+ type: "button",
481
+ "data-slot": `photo-gallery-${side}`,
482
+ "aria-label": side === "prev" ? "Previous photo" : "Next photo",
483
+ onClick: (event) => {
484
+ event.stopPropagation();
485
+ onClick();
486
+ },
487
+ className: cn(
488
+ "absolute top-1/2 z-10 grid -translate-y-1/2 place-items-center active:not-aria-[haspopup]:-translate-y-1/2",
489
+ side === "prev" ? "left-2 sm:left-4" : "right-2 sm:right-4",
490
+ className
491
+ ),
492
+ children: /* @__PURE__ */ jsx4(Icon, { className: "cn-rtl-flip size-4 sm:size-5" })
493
+ }
494
+ );
495
+ }
496
+ function GalleryThumbnails({
497
+ images,
498
+ selectedIndex,
499
+ onSelect,
500
+ colsClassName,
501
+ className,
502
+ activeVariant = "focus"
503
+ }) {
504
+ const containerRef = React2.useRef(null);
505
+ React2.useEffect(() => {
506
+ const active = containerRef.current?.children[selectedIndex];
507
+ active?.scrollIntoView({
508
+ behavior: "smooth",
509
+ inline: "center",
510
+ block: "nearest"
511
+ });
512
+ }, [selectedIndex]);
513
+ if (images.length <= 1) return null;
514
+ return /* @__PURE__ */ jsx4(
515
+ "div",
516
+ {
517
+ ref: containerRef,
518
+ "data-slot": "photo-gallery-thumbnails",
519
+ className: cn(
520
+ "flex gap-2 overflow-x-auto [--gallery-gap:0.5rem] scrollbar-none [&::-webkit-scrollbar]:hidden",
521
+ colsClassName,
522
+ className
523
+ ),
524
+ children: images.map((image, index) => {
525
+ const isActive = index === selectedIndex;
526
+ return /* @__PURE__ */ jsx4(
527
+ "button",
528
+ {
529
+ type: "button",
530
+ "aria-label": `View photo ${index + 1}`,
531
+ "aria-current": isActive,
532
+ onClick: () => onSelect(index),
533
+ style: {
534
+ width: "calc((100% - (var(--gallery-cols) - 1) * var(--gallery-gap)) / var(--gallery-cols))"
535
+ },
536
+ className: cn(
537
+ "relative aspect-4/3 shrink-0 overflow-hidden rounded-lg border-2 bg-surface-raised transition-[border-color,opacity] outline-none focus-visible:border-line-focus focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)]",
538
+ activeVariant === "subtle" && "focus-visible:border-line-subtle focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-subtle)]",
539
+ isActive ? activeVariant === "subtle" ? "border-line-subtle" : "border-line-focus" : "border-transparent opacity-80 hover:opacity-100"
540
+ ),
541
+ children: image.src ? /* @__PURE__ */ jsx4(
542
+ "img",
543
+ {
544
+ src: image.src,
545
+ alt: image.alt ?? `Property image ${index + 1}`,
546
+ className: "size-full object-cover",
547
+ draggable: false
548
+ }
549
+ ) : null
550
+ },
551
+ index
552
+ );
553
+ })
554
+ }
555
+ );
556
+ }
557
+ function PhotoGallery({
558
+ images,
559
+ index,
560
+ onIndexChange,
561
+ onImageClick,
562
+ className,
563
+ ...props
564
+ }) {
565
+ const {
566
+ setApi,
567
+ selectedIndex,
568
+ initialIndex,
569
+ scrollTo,
570
+ scrollPrev,
571
+ scrollNext
572
+ } = useGalleryCarousel(index, onIndexChange);
573
+ const hasMany = images.length > 1;
574
+ return /* @__PURE__ */ jsxs3(
575
+ "div",
576
+ {
577
+ "data-slot": "photo-gallery",
578
+ className: cn(
579
+ "flex w-full flex-col gap-3 rounded-2xl border border-line bg-surface p-3",
580
+ className
581
+ ),
582
+ ...props,
583
+ children: [
584
+ /* @__PURE__ */ jsxs3(
585
+ Carousel,
586
+ {
587
+ imageSize: "full",
588
+ setApi,
589
+ opts: { loop: true, startIndex: initialIndex, watchDrag: hasMany },
590
+ className: "aspect-video rounded-xl bg-surface-raised",
591
+ children: [
592
+ /* @__PURE__ */ jsx4(CarouselContent, { children: images.map((image, i) => /* @__PURE__ */ jsx4(
593
+ CarouselItem,
594
+ {
595
+ children: /* @__PURE__ */ jsx4(GalleryFrame, { image })
596
+ },
597
+ i
598
+ )) }),
599
+ onImageClick && /* @__PURE__ */ jsx4(
600
+ GalleryButton,
601
+ {
602
+ className: "absolute bottom-2 right-2 sm:bottom-4 sm:right-4",
603
+ onClick: () => onImageClick(selectedIndex),
604
+ children: /* @__PURE__ */ jsx4(Fullscreen, { className: "size-4 md:size-5" })
605
+ }
606
+ ),
607
+ /* @__PURE__ */ jsx4(
608
+ GalleryCounter,
609
+ {
610
+ current: selectedIndex + 1,
611
+ total: images.length,
612
+ className: "top-2 right-2 sm:top-4 md:right-4"
613
+ }
614
+ ),
615
+ hasMany && /* @__PURE__ */ jsxs3(Fragment2, { children: [
616
+ /* @__PURE__ */ jsx4(GalleryArrow, { side: "prev", onClick: scrollPrev }),
617
+ /* @__PURE__ */ jsx4(GalleryArrow, { side: "next", onClick: scrollNext })
618
+ ] })
619
+ ]
620
+ }
621
+ ),
622
+ /* @__PURE__ */ jsx4(
623
+ GalleryThumbnails,
624
+ {
625
+ images,
626
+ selectedIndex,
627
+ onSelect: scrollTo,
628
+ colsClassName: "[--gallery-cols:5] sm:[--gallery-cols:8]"
629
+ }
630
+ )
631
+ ]
632
+ }
633
+ );
634
+ }
635
+ function PhotoGalleryFullscreen({
636
+ images,
637
+ open,
638
+ onOpenChange,
639
+ index,
640
+ onIndexChange
641
+ }) {
642
+ const {
643
+ setApi,
644
+ selectedIndex,
645
+ initialIndex,
646
+ scrollTo,
647
+ scrollPrev,
648
+ scrollNext
649
+ } = useGalleryCarousel(index, onIndexChange);
650
+ const hasMany = images.length > 1;
651
+ const handleKeyDown = (event) => {
652
+ if (event.defaultPrevented) return;
653
+ if (event.key === "ArrowLeft") {
654
+ event.preventDefault();
655
+ scrollPrev();
656
+ } else if (event.key === "ArrowRight") {
657
+ event.preventDefault();
658
+ scrollNext();
659
+ }
660
+ };
661
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs3(DialogPrimitive.Portal, { children: [
662
+ /* @__PURE__ */ jsx4(
663
+ DialogPrimitive.Backdrop,
664
+ {
665
+ "data-slot": "photo-gallery-fullscreen-backdrop",
666
+ className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-150 data-closed:opacity-0 data-open:opacity-100"
667
+ }
668
+ ),
669
+ /* @__PURE__ */ jsxs3(
670
+ DialogPrimitive.Popup,
671
+ {
672
+ "data-slot": "photo-gallery-fullscreen",
673
+ onKeyDown: handleKeyDown,
674
+ className: "fixed inset-0 z-50 flex flex-col gap-4 p-4 sm:p-6 xl:p-8 outline-none transition-opacity duration-150 data-closed:opacity-0 data-open:opacity-100",
675
+ children: [
676
+ /* @__PURE__ */ jsx4(DialogPrimitive.Title, { className: "sr-only", children: "Photo Gallery" }),
677
+ /* @__PURE__ */ jsxs3("div", { className: "w-full flex items-center justify-between", children: [
678
+ /* @__PURE__ */ jsx4(
679
+ GalleryCounter,
680
+ {
681
+ current: selectedIndex + 1,
682
+ total: images.length,
683
+ className: "static"
684
+ }
685
+ ),
686
+ /* @__PURE__ */ jsx4(
687
+ DialogPrimitive.Close,
688
+ {
689
+ "data-slot": "photo-gallery-fullscreen-close",
690
+ "aria-label": "Close gallery",
691
+ render: /* @__PURE__ */ jsx4(GalleryButton, {}),
692
+ children: /* @__PURE__ */ jsx4(XIcon, { className: "size-4 sm:size-5" })
693
+ }
694
+ )
695
+ ] }),
696
+ /* @__PURE__ */ jsxs3(
697
+ Carousel,
698
+ {
699
+ imageSize: "full",
700
+ setApi,
701
+ opts: { loop: true, startIndex: initialIndex, watchDrag: hasMany },
702
+ className: "flex-1 w-full rounded-xl bg-transparent",
703
+ children: [
704
+ /* @__PURE__ */ jsx4(CarouselContent, { className: "items-center", children: images.map((image, i) => /* @__PURE__ */ jsx4(
705
+ CarouselItem,
706
+ {
707
+ className: "h-full",
708
+ children: /* @__PURE__ */ jsx4(GalleryFrame, { image, fit: "contain" })
709
+ },
710
+ i
711
+ )) }),
712
+ hasMany && /* @__PURE__ */ jsxs3(Fragment2, { children: [
713
+ /* @__PURE__ */ jsx4(
714
+ GalleryArrow,
715
+ {
716
+ side: "prev",
717
+ onClick: scrollPrev,
718
+ className: "left-0!"
719
+ }
720
+ ),
721
+ /* @__PURE__ */ jsx4(
722
+ GalleryArrow,
723
+ {
724
+ side: "next",
725
+ onClick: scrollNext,
726
+ className: "right-0!"
727
+ }
728
+ )
729
+ ] })
730
+ ]
731
+ }
732
+ ),
733
+ hasMany && /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-3", children: [
734
+ /* @__PURE__ */ jsx4("p", { className: "hidden text-center text-gdt-xs text-fg-secondary sm:block", children: "\u2190 \u2192 to navigate \xB7 ESC to close" }),
735
+ /* @__PURE__ */ jsx4(
736
+ GalleryThumbnails,
737
+ {
738
+ images,
739
+ selectedIndex,
740
+ onSelect: scrollTo,
741
+ colsClassName: "[--gallery-cols:5] sm:[--gallery-cols:10]",
742
+ className: "mx-auto w-full max-w-3xl",
743
+ activeVariant: "subtle"
744
+ }
745
+ )
746
+ ] })
747
+ ]
748
+ }
749
+ )
750
+ ] }) });
751
+ }
752
+ export {
753
+ PhotoGallery,
754
+ PhotoGalleryFullscreen
755
+ };
756
+ //# sourceMappingURL=photo-gallery.js.map