@giddaa-housing/ui 0.1.0 → 1.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.
- package/README.md +18 -6
- package/css/base.css +37 -0
- package/css/components.css +135 -0
- package/css/generated/shared.css +1 -0
- package/css/giddaa.css +402 -0
- package/css/preset.css +77 -0
- package/css/styles.css +2 -0
- package/css/theme.css +255 -0
- package/dist/accordion.d.ts +9 -10
- package/dist/accordion.js +41 -117
- package/dist/activity-timeline.d.ts +29 -0
- package/dist/activity-timeline.js +97 -0
- package/dist/alert.d.ts +8 -9
- package/dist/alert.js +53 -141
- package/dist/auth-layout.d.ts +23 -0
- package/dist/auth-layout.js +153 -0
- package/dist/avatar.d.ts +6 -6
- package/dist/avatar.js +40 -142
- package/dist/badge.d.ts +12 -13
- package/dist/badge.js +66 -114
- package/dist/bar-chart.d.ts +136 -114
- package/dist/bar-chart.js +244 -297
- package/dist/breadcrumb.d.ts +5 -5
- package/dist/breadcrumb.js +56 -148
- package/dist/bullet-chart.d.ts +33 -33
- package/dist/bullet-chart.js +123 -183
- package/dist/button-BZIeyUw6.d.ts +17 -0
- package/dist/button-group.d.ts +13 -16
- package/dist/button-group.js +38 -163
- package/dist/button.d.ts +2 -19
- package/dist/button.js +90 -142
- package/dist/calendar.d.ts +6 -6
- package/dist/calendar.js +134 -288
- package/dist/card.d.ts +14 -16
- package/dist/card.js +58 -174
- package/dist/carousel.d.ts +34 -35
- package/dist/carousel.js +319 -378
- package/dist/chart.d.ts +58 -59
- package/dist/chart.js +350 -466
- package/dist/checkbox.d.ts +10 -12
- package/dist/checkbox.js +84 -84
- package/dist/cn-BI_4DMBf.js +23 -0
- package/dist/collapsible.d.ts +7 -8
- package/dist/collapsible.js +23 -77
- package/dist/combobox.d.ts +10 -10
- package/dist/combobox.js +220 -696
- package/dist/cta.d.ts +64 -0
- package/dist/cta.js +192 -0
- package/dist/currency-selector.d.ts +41 -0
- package/dist/currency-selector.js +114 -0
- package/dist/data-table.d.ts +164 -0
- package/dist/data-table.js +673 -0
- package/dist/date-picker.d.ts +27 -20
- package/dist/date-picker.js +122 -520
- package/dist/details-card.d.ts +19 -0
- package/dist/details-card.js +118 -0
- package/dist/dialog.d.ts +13 -14
- package/dist/dialog.js +117 -320
- package/dist/dropdown-menu.d.ts +14 -14
- package/dist/dropdown-menu.js +170 -365
- package/dist/dropzone.d.ts +55 -55
- package/dist/dropzone.js +328 -467
- package/dist/editorial-card.d.ts +27 -0
- package/dist/editorial-card.js +144 -0
- package/dist/empty-state.d.ts +8 -9
- package/dist/empty-state.js +48 -135
- package/dist/field.d.ts +23 -25
- package/dist/field.js +111 -327
- package/dist/filter.d.ts +118 -0
- package/dist/filter.js +775 -0
- package/dist/footer.d.ts +40 -0
- package/dist/footer.js +227 -0
- package/dist/funnel-chart.d.ts +38 -41
- package/dist/funnel-chart.js +168 -563
- package/dist/gallery-indicator.d.ts +17 -0
- package/dist/gallery-indicator.js +75 -0
- package/dist/heatmap-chart.d.ts +95 -0
- package/dist/heatmap-chart.js +197 -0
- package/dist/infotip.d.ts +10 -0
- package/dist/infotip.js +40 -0
- package/dist/input-CfoEJ8A6.d.ts +12 -0
- package/dist/input-group.d.ts +16 -19
- package/dist/input-group.js +92 -372
- package/dist/input-size-context-C-SFx9sx.d.ts +10 -0
- package/dist/input-size-context.d.ts +2 -11
- package/dist/input-size-context.js +14 -20
- package/dist/input.d.ts +2 -13
- package/dist/input.js +23 -79
- package/dist/item.d.ts +7 -8
- package/dist/item.js +22 -89
- package/dist/kpi-card.d.ts +18 -20
- package/dist/kpi-card.js +84 -232
- package/dist/label.d.ts +6 -6
- package/dist/label.js +39 -66
- package/dist/line-chart.d.ts +88 -89
- package/dist/line-chart.js +143 -262
- package/dist/logo.d.ts +4 -4
- package/dist/logo.js +87 -141
- package/dist/map-indicator.d.ts +13 -0
- package/dist/map-indicator.js +83 -0
- package/dist/match.d.ts +15 -0
- package/dist/match.js +68 -0
- package/dist/media-gallery-hero.d.ts +58 -0
- package/dist/media-gallery-hero.js +201 -0
- package/dist/media-player.d.ts +60 -0
- package/dist/media-player.js +919 -0
- package/dist/multi-step-form.d.ts +56 -0
- package/dist/multi-step-form.js +342 -0
- package/dist/number-input.d.ts +34 -34
- package/dist/number-input.js +62 -413
- package/dist/page-header.d.ts +10 -11
- package/dist/page-header.js +34 -107
- package/dist/pagination.d.ts +17 -20
- package/dist/pagination.js +132 -341
- package/dist/password-input.d.ts +5 -12
- package/dist/password-input.js +32 -354
- package/dist/phone-input.d.ts +19 -19
- package/dist/phone-input.js +232 -561
- package/dist/photo-gallery.d.ts +20 -20
- package/dist/photo-gallery.js +314 -746
- package/dist/picker-lzgmsNfG.js +32 -0
- package/dist/pie-chart.d.ts +60 -60
- package/dist/pie-chart.js +141 -184
- package/dist/popover.d.ts +9 -9
- package/dist/popover.js +60 -156
- package/dist/price-tag.d.ts +34 -0
- package/dist/price-tag.js +118 -0
- package/dist/progress.d.ts +18 -16
- package/dist/progress.js +156 -300
- package/dist/property-listing-card.d.ts +29 -0
- package/dist/property-listing-card.js +117 -0
- package/dist/radar-chart.d.ts +108 -0
- package/dist/radar-chart.js +172 -0
- package/dist/radio-group.d.ts +11 -13
- package/dist/radio-group.js +47 -96
- package/dist/review-block.d.ts +18 -0
- package/dist/review-block.js +154 -0
- package/dist/rich-text-editor.d.ts +30 -14
- package/dist/rich-text-editor.js +594 -979
- package/dist/scatter-chart.d.ts +140 -0
- package/dist/scatter-chart.js +248 -0
- package/dist/search-input.d.ts +8 -15
- package/dist/search-input.js +58 -408
- package/dist/select.d.ts +8 -8
- package/dist/select.js +131 -291
- package/dist/separator.d.ts +8 -9
- package/dist/separator.js +31 -70
- package/dist/sheet.d.ts +11 -11
- package/dist/sheet.js +84 -262
- package/dist/sidebar.d.ts +32 -37
- package/dist/sidebar.js +380 -1055
- package/dist/size-context-D4mYvcg8.d.ts +10 -0
- package/dist/size-context.d.ts +2 -11
- package/dist/size-context.js +14 -18
- package/dist/skeleton.d.ts +4 -5
- package/dist/skeleton.js +9 -45
- package/dist/slider.d.ts +27 -0
- package/dist/slider.js +81 -0
- package/dist/sparkline.d.ts +43 -46
- package/dist/sparkline.js +223 -561
- package/dist/stepper.d.ts +23 -24
- package/dist/stepper.js +149 -276
- package/dist/styles.css +9995 -2
- package/dist/switch.d.ts +10 -12
- package/dist/switch.js +49 -106
- package/dist/table-card.d.ts +35 -0
- package/dist/table-card.js +125 -0
- package/dist/table.d.ts +18 -14
- package/dist/table.js +114 -182
- package/dist/tabs.d.ts +23 -15
- package/dist/tabs.js +62 -149
- package/dist/tag.d.ts +22 -23
- package/dist/tag.js +104 -156
- package/dist/testimonial-block.d.ts +33 -0
- package/dist/testimonial-block.js +208 -0
- package/dist/textarea-IS_D8pXf.d.ts +11 -0
- package/dist/textarea.d.ts +2 -12
- package/dist/textarea.js +20 -74
- package/dist/time-picker.d.ts +11 -11
- package/dist/time-picker.js +247 -464
- package/dist/toast.d.ts +16 -18
- package/dist/toast.js +154 -310
- package/dist/tooltip.d.ts +9 -10
- package/dist/tooltip.js +33 -91
- package/dist/tree-map.d.ts +54 -57
- package/dist/tree-map.js +231 -646
- package/dist/use-uncontrolled-CLfM1XbB.js +21 -0
- package/dist/waterfall-chart.d.ts +126 -0
- package/dist/waterfall-chart.js +480 -0
- package/package.json +123 -34
- package/dist/accordion.js.map +0 -1
- package/dist/alert.js.map +0 -1
- package/dist/avatar.js.map +0 -1
- package/dist/badge.js.map +0 -1
- package/dist/bar-chart.js.map +0 -1
- package/dist/base.css +0 -32
- package/dist/breadcrumb.js.map +0 -1
- package/dist/bullet-chart.js.map +0 -1
- package/dist/button-group.js.map +0 -1
- package/dist/button.js.map +0 -1
- package/dist/calendar.js.map +0 -1
- package/dist/card.js.map +0 -1
- package/dist/carousel.js.map +0 -1
- package/dist/chart.js.map +0 -1
- package/dist/checkbox.js.map +0 -1
- package/dist/collapsible.js.map +0 -1
- package/dist/combobox.js.map +0 -1
- package/dist/date-picker.js.map +0 -1
- package/dist/dialog.js.map +0 -1
- package/dist/dropdown-menu.js.map +0 -1
- package/dist/dropzone.js.map +0 -1
- package/dist/empty-state.js.map +0 -1
- package/dist/field.js.map +0 -1
- package/dist/files/red-hat-display-latin-300-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-300-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-300-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-300-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-400-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-400-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-400-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-400-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-500-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-500-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-500-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-500-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-600-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-600-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-600-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-600-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-700-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-700-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-700-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-700-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-800-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-800-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-800-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-800-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-900-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-900-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-900-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-900-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-300-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-300-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-300-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-300-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-400-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-400-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-400-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-400-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-500-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-500-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-500-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-500-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-600-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-600-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-600-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-600-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-700-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-700-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-700-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-700-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-800-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-800-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-800-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-800-normal.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-900-italic.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-900-italic.woff2 +0 -0
- package/dist/files/red-hat-display-latin-ext-900-normal.woff +0 -0
- package/dist/files/red-hat-display-latin-ext-900-normal.woff2 +0 -0
- package/dist/funnel-chart.js.map +0 -1
- package/dist/index.d.ts +0 -87
- package/dist/index.js +0 -10448
- package/dist/index.js.map +0 -1
- package/dist/input-group.js.map +0 -1
- package/dist/input-size-context.js.map +0 -1
- package/dist/input.js.map +0 -1
- package/dist/item.js.map +0 -1
- package/dist/kpi-card.js.map +0 -1
- package/dist/label.js.map +0 -1
- package/dist/line-chart.js.map +0 -1
- package/dist/logo.js.map +0 -1
- package/dist/number-input.js.map +0 -1
- package/dist/page-header.js.map +0 -1
- package/dist/pagination.js.map +0 -1
- package/dist/password-input.js.map +0 -1
- package/dist/phone-input.js.map +0 -1
- package/dist/photo-gallery.js.map +0 -1
- package/dist/pie-chart.js.map +0 -1
- package/dist/popover.js.map +0 -1
- package/dist/progress.js.map +0 -1
- package/dist/radio-group.js.map +0 -1
- package/dist/red-hat-display.css +0 -2
- package/dist/rich-text-editor.js.map +0 -1
- package/dist/search-input.js.map +0 -1
- package/dist/select.js.map +0 -1
- package/dist/separator.js.map +0 -1
- package/dist/sheet.js.map +0 -1
- package/dist/sidebar.js.map +0 -1
- package/dist/size-context.js.map +0 -1
- package/dist/skeleton.js.map +0 -1
- package/dist/sparkline.js.map +0 -1
- package/dist/stepper.js.map +0 -1
- package/dist/switch.js.map +0 -1
- package/dist/tab-list.d.ts +0 -38
- package/dist/tab-list.js +0 -394
- package/dist/tab-list.js.map +0 -1
- package/dist/table.js.map +0 -1
- package/dist/tabs.js.map +0 -1
- package/dist/tag.js.map +0 -1
- package/dist/textarea.js.map +0 -1
- package/dist/theme/giddaa.css +0 -2
- package/dist/time-picker.js.map +0 -1
- package/dist/toast.js.map +0 -1
- package/dist/tooltip.js.map +0 -1
- package/dist/tree-map.js.map +0 -1
package/dist/photo-gallery.js
CHANGED
|
@@ -1,756 +1,324 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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";
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as cn } from "./cn-BI_4DMBf.js";
|
|
3
|
+
import { Button } from "./button.js";
|
|
4
|
+
import { Carousel, CarouselContent, CarouselItem } from "./carousel.js";
|
|
5
|
+
import { ChevronLeftIcon, ChevronRightIcon, Fullscreen, XIcon } from "lucide-react";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
126
7
|
import * as React from "react";
|
|
127
|
-
import {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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";
|
|
8
|
+
import { Dialog } from "@base-ui/react/dialog";
|
|
9
|
+
//#region src/photo-gallery.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Wires an Embla carousel to a controlled/uncontrolled active index. Returns
|
|
12
|
+
* the `setApi` to hand to {@link Carousel} plus the live index and navigation
|
|
13
|
+
* helpers. Both surfaces share this so the inline carousel and the fullscreen
|
|
14
|
+
* viewer can stand alone — or stay in sync by sharing `index`/`onIndexChange`.
|
|
15
|
+
*/
|
|
331
16
|
function useGalleryCarousel(index, onIndexChange) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
initialIndex: initialIndexRef.current,
|
|
367
|
-
scrollTo,
|
|
368
|
-
scrollPrev,
|
|
369
|
-
scrollNext
|
|
370
|
-
};
|
|
17
|
+
const [api, setApi] = React.useState();
|
|
18
|
+
const [selectedIndex, setSelectedIndex] = React.useState(index ?? 0);
|
|
19
|
+
const initialIndexRef = React.useRef(index ?? 0);
|
|
20
|
+
const onIndexChangeRef = React.useRef(onIndexChange);
|
|
21
|
+
React.useEffect(() => {
|
|
22
|
+
onIndexChangeRef.current = onIndexChange;
|
|
23
|
+
});
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
if (!api) return;
|
|
26
|
+
const onSelect = () => {
|
|
27
|
+
const next = api.selectedScrollSnap();
|
|
28
|
+
setSelectedIndex(next);
|
|
29
|
+
onIndexChangeRef.current?.(next);
|
|
30
|
+
};
|
|
31
|
+
onSelect();
|
|
32
|
+
api.on("select", onSelect).on("reInit", onSelect);
|
|
33
|
+
return () => {
|
|
34
|
+
api.off("select", onSelect).off("reInit", onSelect);
|
|
35
|
+
};
|
|
36
|
+
}, [api]);
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
if (api && index !== void 0 && index !== api.selectedScrollSnap()) api.scrollTo(index, false);
|
|
39
|
+
}, [api, index]);
|
|
40
|
+
const scrollTo = React.useCallback((i) => api?.scrollTo(i, false), [api]);
|
|
41
|
+
const scrollPrev = React.useCallback(() => api?.scrollPrev(false), [api]);
|
|
42
|
+
const scrollNext = React.useCallback(() => api?.scrollNext(false), [api]);
|
|
43
|
+
return {
|
|
44
|
+
setApi,
|
|
45
|
+
selectedIndex,
|
|
46
|
+
initialIndex: initialIndexRef.current,
|
|
47
|
+
scrollTo,
|
|
48
|
+
scrollPrev,
|
|
49
|
+
scrollNext
|
|
50
|
+
};
|
|
371
51
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
),
|
|
394
|
-
draggable: false
|
|
395
|
-
}
|
|
396
|
-
) : /* @__PURE__ */ jsx4("span", { className: "text-gdt-sm font-medium text-fg-caption-placeholder", children: "Property Image" })
|
|
397
|
-
}
|
|
398
|
-
);
|
|
52
|
+
/**
|
|
53
|
+
* The image surface. Centres its image within the slide; falls back to a
|
|
54
|
+
* "Property Image" label when `src` is missing. `cover` fills and crops the
|
|
55
|
+
* frame for the listing hero; `contain` clamps the photo to the frame's bounds
|
|
56
|
+
* (`max-w`/`max-h`) so the whole image stays visible — never overflowing the
|
|
57
|
+
* fullscreen viewer's chrome and never stretched out of its aspect ratio.
|
|
58
|
+
*/
|
|
59
|
+
function GalleryFrame({ image, fit = "cover", className }) {
|
|
60
|
+
return /* @__PURE__ */ jsx("div", {
|
|
61
|
+
"data-slot": "photo-gallery-frame",
|
|
62
|
+
className: cn("relative flex size-full items-center justify-center overflow-hidden", className),
|
|
63
|
+
children: image?.src ? /* @__PURE__ */ jsx("img", {
|
|
64
|
+
src: image.src,
|
|
65
|
+
alt: image.alt ?? "Property image",
|
|
66
|
+
className: cn(fit === "contain" ? "max-h-full max-w-full rounded-lg object-contain" : "size-full object-cover"),
|
|
67
|
+
draggable: false
|
|
68
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
69
|
+
className: "text-gdt-sm font-medium text-fg-caption-placeholder",
|
|
70
|
+
children: "Property Image"
|
|
71
|
+
})
|
|
72
|
+
});
|
|
399
73
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
);
|
|
74
|
+
const Images = (props) => /* @__PURE__ */ jsxs("svg", {
|
|
75
|
+
"aria-hidden": "true",
|
|
76
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
77
|
+
width: 15,
|
|
78
|
+
height: 16,
|
|
79
|
+
viewBox: "0 0 15 16",
|
|
80
|
+
fill: "none",
|
|
81
|
+
...props,
|
|
82
|
+
children: [/* @__PURE__ */ jsx("path", {
|
|
83
|
+
fill: "currentColor",
|
|
84
|
+
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"
|
|
85
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
86
|
+
fill: "currentColor",
|
|
87
|
+
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"
|
|
88
|
+
})]
|
|
89
|
+
});
|
|
90
|
+
/** Pill overlay showing `current / total`. Hidden when there is one image. */
|
|
91
|
+
function GalleryCounter({ current, total, className }) {
|
|
92
|
+
if (total <= 1) return null;
|
|
93
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
94
|
+
"data-slot": "photo-gallery-counter",
|
|
95
|
+
className: cn("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", className),
|
|
96
|
+
children: [/* @__PURE__ */ jsx(Images, {
|
|
97
|
+
"aria-hidden": "true",
|
|
98
|
+
className: "size-3.5"
|
|
99
|
+
}), /* @__PURE__ */ jsxs("span", { children: [
|
|
100
|
+
current,
|
|
101
|
+
" / ",
|
|
102
|
+
total
|
|
103
|
+
] })]
|
|
104
|
+
});
|
|
452
105
|
}
|
|
453
|
-
function GalleryButton({
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
);
|
|
106
|
+
function GalleryButton({ className, variant = "ghost", ...rest }) {
|
|
107
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
108
|
+
size: "icon",
|
|
109
|
+
className: cn("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", className),
|
|
110
|
+
variant,
|
|
111
|
+
...rest
|
|
112
|
+
});
|
|
470
113
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
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
|
-
);
|
|
114
|
+
/** Circular floating navigation button. Always present; the track loops. */
|
|
115
|
+
function GalleryArrow({ side, onClick, className }) {
|
|
116
|
+
const Icon = side === "prev" ? ChevronLeftIcon : ChevronRightIcon;
|
|
117
|
+
return /* @__PURE__ */ jsx(GalleryButton, {
|
|
118
|
+
type: "button",
|
|
119
|
+
"data-slot": `photo-gallery-${side}`,
|
|
120
|
+
"aria-label": side === "prev" ? "Previous photo" : "Next photo",
|
|
121
|
+
onClick: (event) => {
|
|
122
|
+
event.stopPropagation();
|
|
123
|
+
onClick();
|
|
124
|
+
},
|
|
125
|
+
className: cn("absolute top-1/2 z-10 grid -translate-y-1/2 place-items-center active:not-aria-[haspopup]:-translate-y-1/2", side === "prev" ? "left-2 sm:left-4" : "right-2 sm:right-4", className),
|
|
126
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "cn-rtl-flip size-4 sm:size-5" })
|
|
127
|
+
});
|
|
495
128
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
draggable: false
|
|
548
|
-
}
|
|
549
|
-
) : null
|
|
550
|
-
},
|
|
551
|
-
index
|
|
552
|
-
);
|
|
553
|
-
})
|
|
554
|
-
}
|
|
555
|
-
);
|
|
129
|
+
/**
|
|
130
|
+
* Equal-sized thumbnail strip. `cols` are sized via CSS vars so exactly N fill
|
|
131
|
+
* the width (5 on mobile, more on web); extras overflow and scroll. The active
|
|
132
|
+
* thumb gets a 2px focus stroke and is kept in view as the index changes.
|
|
133
|
+
*/
|
|
134
|
+
function GalleryThumbnails({ images, selectedIndex, onSelect, colsClassName, className }) {
|
|
135
|
+
const containerRef = React.useRef(null);
|
|
136
|
+
const hasPositionedRef = React.useRef(false);
|
|
137
|
+
const [overflowing, setOverflowing] = React.useState(false);
|
|
138
|
+
React.useEffect(() => {
|
|
139
|
+
const el = containerRef.current;
|
|
140
|
+
if (!el) return;
|
|
141
|
+
const update = () => setOverflowing(el.scrollWidth > el.clientWidth + 1);
|
|
142
|
+
update();
|
|
143
|
+
const observer = new ResizeObserver(update);
|
|
144
|
+
observer.observe(el);
|
|
145
|
+
return () => observer.disconnect();
|
|
146
|
+
}, []);
|
|
147
|
+
React.useEffect(() => {
|
|
148
|
+
const active = containerRef.current?.children[selectedIndex];
|
|
149
|
+
if (!active) return;
|
|
150
|
+
active.scrollIntoView({
|
|
151
|
+
behavior: hasPositionedRef.current ? "smooth" : "auto",
|
|
152
|
+
inline: "center",
|
|
153
|
+
block: "nearest"
|
|
154
|
+
});
|
|
155
|
+
hasPositionedRef.current = true;
|
|
156
|
+
}, [selectedIndex]);
|
|
157
|
+
if (images.length <= 1) return null;
|
|
158
|
+
return /* @__PURE__ */ jsx("div", {
|
|
159
|
+
ref: containerRef,
|
|
160
|
+
"data-slot": "photo-gallery-thumbnails",
|
|
161
|
+
className: cn("flex gap-2 overflow-x-auto [--gallery-gap:0.5rem] scrollbar-none [&::-webkit-scrollbar]:hidden", overflowing ? "justify-start" : "justify-center", colsClassName, className),
|
|
162
|
+
children: images.map((image, index) => {
|
|
163
|
+
const isActive = index === selectedIndex;
|
|
164
|
+
return /* @__PURE__ */ jsx("button", {
|
|
165
|
+
type: "button",
|
|
166
|
+
"aria-label": `View photo ${index + 1}`,
|
|
167
|
+
"aria-current": isActive,
|
|
168
|
+
onClick: () => onSelect(index),
|
|
169
|
+
style: { width: "calc((100% - (var(--gallery-cols) - 1) * var(--gallery-gap)) / var(--gallery-cols))" },
|
|
170
|
+
className: cn("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)]", isActive ? "border-line-focus" : "border-transparent opacity-80 hover:opacity-100"),
|
|
171
|
+
children: image.src ? /* @__PURE__ */ jsx("img", {
|
|
172
|
+
src: image.src,
|
|
173
|
+
alt: image.alt ?? `Property image ${index + 1}`,
|
|
174
|
+
className: "size-full object-cover",
|
|
175
|
+
draggable: false
|
|
176
|
+
}) : null
|
|
177
|
+
}, index);
|
|
178
|
+
})
|
|
179
|
+
});
|
|
556
180
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
})
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
-
);
|
|
181
|
+
/**
|
|
182
|
+
* Inline property gallery: a swipeable hero image (drag / touch / ← →) with a
|
|
183
|
+
* counter pill, navigation arrows, and a thumbnail strip below. Built on
|
|
184
|
+
* {@link Carousel}. Standalone — pass `onImageClick` to open it into a
|
|
185
|
+
* {@link PhotoGalleryFullscreen} viewer, but neither surface needs the other.
|
|
186
|
+
*/
|
|
187
|
+
function PhotoGallery({ images, index, onIndexChange, onImageClick, className, ...props }) {
|
|
188
|
+
const { setApi, selectedIndex, initialIndex, scrollTo, scrollPrev, scrollNext } = useGalleryCarousel(index, onIndexChange);
|
|
189
|
+
const hasMany = images.length > 1;
|
|
190
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
191
|
+
"data-slot": "photo-gallery",
|
|
192
|
+
className: cn("flex w-full flex-col gap-3 rounded-2xl border border-line bg-surface p-3", className),
|
|
193
|
+
...props,
|
|
194
|
+
children: [/* @__PURE__ */ jsxs(Carousel, {
|
|
195
|
+
imageSize: "full",
|
|
196
|
+
setApi,
|
|
197
|
+
opts: {
|
|
198
|
+
loop: true,
|
|
199
|
+
startIndex: initialIndex,
|
|
200
|
+
watchDrag: hasMany
|
|
201
|
+
},
|
|
202
|
+
className: "aspect-video rounded-xl bg-surface-raised",
|
|
203
|
+
children: [
|
|
204
|
+
/* @__PURE__ */ jsx(CarouselContent, { children: images.map((image, i) => /* @__PURE__ */ jsx(CarouselItem, { children: /* @__PURE__ */ jsx(GalleryFrame, { image }) }, i)) }),
|
|
205
|
+
onImageClick && /* @__PURE__ */ jsx(GalleryButton, {
|
|
206
|
+
className: "absolute bottom-2 right-2 sm:bottom-4 sm:right-4",
|
|
207
|
+
onClick: () => onImageClick(selectedIndex),
|
|
208
|
+
children: /* @__PURE__ */ jsx(Fullscreen, { className: "size-4 md:size-5" })
|
|
209
|
+
}),
|
|
210
|
+
/* @__PURE__ */ jsx(GalleryCounter, {
|
|
211
|
+
current: selectedIndex + 1,
|
|
212
|
+
total: images.length,
|
|
213
|
+
className: "top-2 right-2 sm:top-4 md:right-4"
|
|
214
|
+
}),
|
|
215
|
+
hasMany && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(GalleryArrow, {
|
|
216
|
+
side: "prev",
|
|
217
|
+
onClick: scrollPrev
|
|
218
|
+
}), /* @__PURE__ */ jsx(GalleryArrow, {
|
|
219
|
+
side: "next",
|
|
220
|
+
onClick: scrollNext
|
|
221
|
+
})] })
|
|
222
|
+
]
|
|
223
|
+
}), /* @__PURE__ */ jsx(GalleryThumbnails, {
|
|
224
|
+
images,
|
|
225
|
+
selectedIndex,
|
|
226
|
+
onSelect: scrollTo,
|
|
227
|
+
colsClassName: "[--gallery-cols:5] sm:[--gallery-cols:8]"
|
|
228
|
+
})]
|
|
229
|
+
});
|
|
634
230
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}) {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
] }) });
|
|
231
|
+
/**
|
|
232
|
+
* Fullscreen lightbox: takes over the viewport over a dimmed backdrop for
|
|
233
|
+
* distraction-free browsing. Swipeable (drag / touch) with ← → key navigation
|
|
234
|
+
* and ESC to close. Built on {@link Carousel}; controlled via `open` /
|
|
235
|
+
* `onOpenChange` and usable without the inline gallery.
|
|
236
|
+
*/
|
|
237
|
+
function PhotoGalleryFullscreen({ images, open, onOpenChange, index, onIndexChange }) {
|
|
238
|
+
const { setApi, selectedIndex, initialIndex, scrollTo, scrollPrev, scrollNext } = useGalleryCarousel(index, onIndexChange);
|
|
239
|
+
const hasMany = images.length > 1;
|
|
240
|
+
const handleKeyDown = (event) => {
|
|
241
|
+
if (event.defaultPrevented) return;
|
|
242
|
+
if (event.key === "ArrowLeft") {
|
|
243
|
+
event.preventDefault();
|
|
244
|
+
scrollPrev();
|
|
245
|
+
} else if (event.key === "ArrowRight") {
|
|
246
|
+
event.preventDefault();
|
|
247
|
+
scrollNext();
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
return /* @__PURE__ */ jsx(Dialog.Root, {
|
|
251
|
+
open,
|
|
252
|
+
onOpenChange,
|
|
253
|
+
children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(Dialog.Backdrop, {
|
|
254
|
+
"data-slot": "photo-gallery-fullscreen-backdrop",
|
|
255
|
+
className: "fixed inset-0 z-50 bg-black/90 transition-opacity duration-150 data-closed:opacity-0 data-open:opacity-100"
|
|
256
|
+
}), /* @__PURE__ */ jsxs(Dialog.Popup, {
|
|
257
|
+
"data-slot": "photo-gallery-fullscreen",
|
|
258
|
+
onKeyDown: handleKeyDown,
|
|
259
|
+
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",
|
|
260
|
+
children: [
|
|
261
|
+
/* @__PURE__ */ jsx(Dialog.Title, {
|
|
262
|
+
className: "sr-only",
|
|
263
|
+
children: "Photo Gallery"
|
|
264
|
+
}),
|
|
265
|
+
/* @__PURE__ */ jsxs("div", {
|
|
266
|
+
className: "w-full flex items-center justify-between",
|
|
267
|
+
children: [/* @__PURE__ */ jsx(GalleryCounter, {
|
|
268
|
+
current: selectedIndex + 1,
|
|
269
|
+
total: images.length,
|
|
270
|
+
className: "static"
|
|
271
|
+
}), /* @__PURE__ */ jsx(Dialog.Close, {
|
|
272
|
+
"data-slot": "photo-gallery-fullscreen-close",
|
|
273
|
+
"aria-label": "Close gallery",
|
|
274
|
+
render: /* @__PURE__ */ jsx(GalleryButton, {}),
|
|
275
|
+
children: /* @__PURE__ */ jsx(XIcon, { className: "size-4 sm:size-5" })
|
|
276
|
+
})]
|
|
277
|
+
}),
|
|
278
|
+
/* @__PURE__ */ jsxs(Carousel, {
|
|
279
|
+
imageSize: "full",
|
|
280
|
+
setApi,
|
|
281
|
+
opts: {
|
|
282
|
+
loop: true,
|
|
283
|
+
startIndex: initialIndex,
|
|
284
|
+
watchDrag: hasMany
|
|
285
|
+
},
|
|
286
|
+
className: "min-h-0 flex-1 w-full rounded-xl bg-transparent",
|
|
287
|
+
children: [/* @__PURE__ */ jsx(CarouselContent, {
|
|
288
|
+
className: "items-center",
|
|
289
|
+
children: images.map((image, i) => /* @__PURE__ */ jsx(CarouselItem, {
|
|
290
|
+
className: "h-full",
|
|
291
|
+
children: /* @__PURE__ */ jsx(GalleryFrame, {
|
|
292
|
+
image,
|
|
293
|
+
fit: "contain"
|
|
294
|
+
})
|
|
295
|
+
}, i))
|
|
296
|
+
}), hasMany && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(GalleryArrow, {
|
|
297
|
+
side: "prev",
|
|
298
|
+
onClick: scrollPrev,
|
|
299
|
+
className: "left-0!"
|
|
300
|
+
}), /* @__PURE__ */ jsx(GalleryArrow, {
|
|
301
|
+
side: "next",
|
|
302
|
+
onClick: scrollNext,
|
|
303
|
+
className: "right-0!"
|
|
304
|
+
})] })]
|
|
305
|
+
}),
|
|
306
|
+
hasMany && /* @__PURE__ */ jsxs("div", {
|
|
307
|
+
className: "flex flex-col gap-3",
|
|
308
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
309
|
+
className: "hidden text-center text-gdt-xs text-fg-secondary sm:block",
|
|
310
|
+
children: "← → to navigate · ESC to close"
|
|
311
|
+
}), /* @__PURE__ */ jsx(GalleryThumbnails, {
|
|
312
|
+
images,
|
|
313
|
+
selectedIndex,
|
|
314
|
+
onSelect: scrollTo,
|
|
315
|
+
colsClassName: "[--gallery-cols:5] sm:[--gallery-cols:10]",
|
|
316
|
+
className: "mx-auto w-full max-w-3xl"
|
|
317
|
+
})]
|
|
318
|
+
})
|
|
319
|
+
]
|
|
320
|
+
})] })
|
|
321
|
+
});
|
|
751
322
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
PhotoGalleryFullscreen
|
|
755
|
-
};
|
|
756
|
-
//# sourceMappingURL=photo-gallery.js.map
|
|
323
|
+
//#endregion
|
|
324
|
+
export { PhotoGallery, PhotoGalleryFullscreen };
|