@giddaa-housing/ui 3.2.0 → 3.4.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/css/generated/shared.css +1 -1
- package/css/theme.css +20 -0
- package/dist/accordion.js +1 -0
- package/dist/alert.js +3 -3
- package/dist/badge.js +5 -0
- package/dist/button.js +6 -2
- package/dist/footer.d.ts +8 -3
- package/dist/footer.js +15 -4
- package/dist/media-gallery-hero.js +3 -3
- package/dist/price-tag.d.ts +80 -12
- package/dist/price-tag.js +98 -57
- package/dist/property-listing-card.js +1 -1
- package/dist/scroll-spy.d.ts +76 -0
- package/dist/scroll-spy.js +318 -0
- package/dist/sonar.d.ts +51 -0
- package/dist/sonar.js +117 -0
- package/dist/styles.css +106 -9
- package/dist/tabs.d.ts +13 -1
- package/dist/tabs.js +9 -2
- package/dist/toast.js +1 -0
- package/package.json +9 -1
package/css/theme.css
CHANGED
|
@@ -141,6 +141,7 @@
|
|
|
141
141
|
--animate-accordion-up: accordion-up 0.2s ease-out;
|
|
142
142
|
--animate-sonar-glow: sonar-glow 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
|
|
143
143
|
--animate-round-sonar-glow: round-sonar-glow 1.5s ease-out infinite;
|
|
144
|
+
--animate-sonar-wave: sonar-wave 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
|
|
144
145
|
--animate-spin-slow: spin 3s linear infinite;
|
|
145
146
|
}
|
|
146
147
|
|
|
@@ -175,6 +176,7 @@
|
|
|
175
176
|
--animate-bouncing-loader: none;
|
|
176
177
|
--animate-sonar-glow: none;
|
|
177
178
|
--animate-round-sonar-glow: none;
|
|
179
|
+
--animate-sonar-wave: none;
|
|
178
180
|
--animate-spin-slow: none;
|
|
179
181
|
}
|
|
180
182
|
}
|
|
@@ -280,3 +282,21 @@
|
|
|
280
282
|
opacity: 0;
|
|
281
283
|
}
|
|
282
284
|
}
|
|
285
|
+
|
|
286
|
+
/* Drives `Sonar`. Spread rather than `transform: scale()` on purpose: spread
|
|
287
|
+
grows the ring by the same number of pixels on every side and traces the
|
|
288
|
+
element's own border-radius, so one keyframe fits a 8px dot and a wide
|
|
289
|
+
button alike. Scaling would expand a wide element far more horizontally
|
|
290
|
+
than vertically — which is why sonar-glow above needs its per-axis
|
|
291
|
+
--sonar-glow-scale-x/y knobs. */
|
|
292
|
+
@keyframes sonar-wave {
|
|
293
|
+
from {
|
|
294
|
+
box-shadow: 0 0 0 0 currentColor;
|
|
295
|
+
opacity: 0.55;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
to {
|
|
299
|
+
box-shadow: 0 0 0 var(--sonar-spread, 0.625rem) currentColor;
|
|
300
|
+
opacity: 0;
|
|
301
|
+
}
|
|
302
|
+
}
|
package/dist/accordion.js
CHANGED
package/dist/alert.js
CHANGED
|
@@ -3,7 +3,7 @@ import { t as cn } from "./cn-BI_4DMBf.js";
|
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
//#region src/alert.tsx
|
|
6
|
-
const alertVariants = cva("grid w-full grid-cols-[0_minmax(0,1fr)_auto] items-start gap-y-1 rounded-xl border-2 px-4 py-3.5 text-gdt-sm transition-[background-color,border-color,color,box-shadow] has-[>svg]:grid-cols-[1.25rem_minmax(0,1fr)_auto] has-[>svg]:gap-x-3 [&>svg]:col-start-1 [&>svg]:row-span-2 [&>svg]:mt-0.5 [&>svg]:size-5 [&>svg]:text-current", {
|
|
6
|
+
const alertVariants = cva("group/alert grid w-full grid-cols-[0_minmax(0,1fr)_auto] items-start gap-y-1 rounded-xl border-2 px-4 py-3.5 text-gdt-sm transition-[background-color,border-color,color,box-shadow] has-[>svg]:grid-cols-[1.25rem_minmax(0,1fr)_auto] has-[>svg]:gap-x-3 [&>svg]:col-start-1 [&>svg]:row-span-2 [&>svg]:mt-0.5 [&>svg]:size-5 [&>svg]:text-current", {
|
|
7
7
|
variants: { variant: {
|
|
8
8
|
success: "border-status-success bg-surface-brand-subtle text-status-success",
|
|
9
9
|
info: "border-status-info bg-surface text-status-info",
|
|
@@ -33,14 +33,14 @@ function AlertTitle({ className, ...props }) {
|
|
|
33
33
|
function AlertDescription({ className, ...props }) {
|
|
34
34
|
return /* @__PURE__ */ jsx("div", {
|
|
35
35
|
"data-slot": "alert-description",
|
|
36
|
-
className: cn("col-start-2 row-start-
|
|
36
|
+
className: cn("col-start-2 row-start-1 text-fg-secondary group-has-[[data-slot=alert-title]]/alert:row-start-2 [&_p]:leading-relaxed", className),
|
|
37
37
|
...props
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
function AlertActions({ className, ...props }) {
|
|
41
41
|
return /* @__PURE__ */ jsx("div", {
|
|
42
42
|
"data-slot": "alert-actions",
|
|
43
|
-
className: cn("col-start-2 row-start-
|
|
43
|
+
className: cn("col-start-2 row-start-2 mt-2 flex shrink-0 items-center gap-2 justify-self-start max-sm:group-has-[[data-slot=alert-title]]/alert:row-start-3 sm:col-start-3 sm:row-span-2 sm:row-start-1 sm:mt-[-0.125rem] sm:justify-self-end", className),
|
|
44
44
|
...props
|
|
45
45
|
});
|
|
46
46
|
}
|
package/dist/badge.js
CHANGED
|
@@ -36,6 +36,11 @@ const badgeVariants = cva("group/badge inline-flex w-fit shrink-0 items-center j
|
|
|
36
36
|
false: ""
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
|
+
compoundVariants: [{
|
|
40
|
+
variant: "outline",
|
|
41
|
+
border: false,
|
|
42
|
+
class: "border-line"
|
|
43
|
+
}],
|
|
39
44
|
defaultVariants: {
|
|
40
45
|
variant: "default",
|
|
41
46
|
size: "md",
|
package/dist/button.js
CHANGED
|
@@ -22,8 +22,8 @@ const buttonVariants = cva("group/button inline-flex shrink-0 items-center justi
|
|
|
22
22
|
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",
|
|
23
23
|
whatsapp: "bg-whatsapp-green text-black [&_svg]:text-white hover:brightness-95 active:brightness-90 data-focused:brightness-95 data-pressed:brightness-90",
|
|
24
24
|
"glass-overlay": "bg-surface-glass-overlay-button text-fg-on-brand shadow-2 backdrop-blur-sm active:scale-[0.97] motion-reduce:active:scale-100",
|
|
25
|
-
"link-brand": "border-transparent bg-transparent
|
|
26
|
-
"link-neutral": "border-transparent bg-transparent
|
|
25
|
+
"link-brand": "border-transparent bg-transparent text-fg-brand hover:underline active:underline active:opacity-70",
|
|
26
|
+
"link-neutral": "border-transparent bg-transparent text-fg-secondary hover:underline active:underline active:opacity-70"
|
|
27
27
|
},
|
|
28
28
|
size: {
|
|
29
29
|
sm: "h-8 min-w-8 gap-2 px-3.5 text-gdt-xs [&_svg:not([class*='size-'])]:size-4",
|
|
@@ -37,6 +37,10 @@ const buttonVariants = cva("group/button inline-flex shrink-0 items-center justi
|
|
|
37
37
|
"icon-xl": "size-14 text-gdt-lg [&_svg:not([class*='size-'])]:size-5"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
+
compoundVariants: [{
|
|
41
|
+
variant: ["link-brand", "link-neutral"],
|
|
42
|
+
class: "h-auto min-h-0 w-auto min-w-0 shrink justify-start rounded-none px-0 py-0 text-left whitespace-normal underline-offset-4 active:not-aria-[haspopup]:translate-y-0"
|
|
43
|
+
}],
|
|
40
44
|
defaultVariants: {
|
|
41
45
|
variant: "primary",
|
|
42
46
|
size: "md"
|
package/dist/footer.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { n as InputSize } from "./input-CfoEJ8A6.js";
|
|
2
2
|
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { VariantProps } from "class-variance-authority";
|
|
3
4
|
import * as React from "react";
|
|
4
5
|
//#region src/footer.d.ts
|
|
5
6
|
type FooterRootProps = React.ComponentProps<"footer"> & {
|
|
@@ -20,10 +21,14 @@ declare function FooterSection({ className, ...props }: React.ComponentProps<"di
|
|
|
20
21
|
declare function FooterSectionTitle({ className, ...props }: React.ComponentProps<"h3">): React.JSX.Element;
|
|
21
22
|
declare function FooterLinkList({ className, ...props }: React.ComponentProps<"ul">): React.JSX.Element;
|
|
22
23
|
declare function FooterLinkItem({ className, ...props }: React.ComponentProps<"li">): React.JSX.Element;
|
|
23
|
-
|
|
24
|
+
declare const footerLinkVariants: (props?: ({
|
|
25
|
+
variant?: "brand" | "default" | null | undefined;
|
|
26
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
27
|
+
type FooterLinkVariant = NonNullable<VariantProps<typeof footerLinkVariants>["variant"]>;
|
|
28
|
+
type FooterLinkProps = useRender.ComponentProps<"a"> & VariantProps<typeof footerLinkVariants> & {
|
|
24
29
|
showArrow?: boolean;
|
|
25
30
|
};
|
|
26
|
-
declare function FooterLink({ className, children, render, showArrow, ...props }: FooterLinkProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
31
|
+
declare function FooterLink({ className, children, render, showArrow, variant, ...props }: FooterLinkProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
27
32
|
declare function FooterContactGrid({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
28
33
|
declare function FooterContact({ className, ...props }: React.ComponentProps<"address">): React.JSX.Element;
|
|
29
34
|
declare function FooterContactTitle({ className, ...props }: React.ComponentProps<"h3">): React.JSX.Element;
|
|
@@ -39,4 +44,4 @@ declare function FooterNewsletterControl({ className, children, size, ...props }
|
|
|
39
44
|
declare function FooterBottom({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
40
45
|
declare function FooterLegalLinks({ className, ...props }: React.ComponentProps<"nav">): React.JSX.Element;
|
|
41
46
|
//#endregion
|
|
42
|
-
export { FooterBody, FooterBottom, FooterContact, FooterContactGrid, FooterContactTitle, FooterCta, FooterCtaActions, FooterCtaContent, FooterCtaDescription, FooterCtaTitle, FooterDescription, FooterHeader, FooterLegalLinks, FooterLink, FooterLinkItem, FooterLinkList, type FooterLinkProps, FooterNav, FooterNewsletter, FooterNewsletterControl, FooterNewsletterDescription, FooterNewsletterTitle, FooterRoot, type FooterRootProps, FooterSection, FooterSectionTitle, FooterSocialItem, FooterSocialLink, FooterSocialList, FooterTitle };
|
|
47
|
+
export { FooterBody, FooterBottom, FooterContact, FooterContactGrid, FooterContactTitle, FooterCta, FooterCtaActions, FooterCtaContent, FooterCtaDescription, FooterCtaTitle, FooterDescription, FooterHeader, FooterLegalLinks, FooterLink, FooterLinkItem, FooterLinkList, type FooterLinkProps, type FooterLinkVariant, FooterNav, FooterNewsletter, FooterNewsletterControl, FooterNewsletterDescription, FooterNewsletterTitle, FooterRoot, type FooterRootProps, FooterSection, FooterSectionTitle, FooterSocialItem, FooterSocialLink, FooterSocialList, FooterTitle, footerLinkVariants };
|
package/dist/footer.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Input } from "./input.js";
|
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
8
8
|
import { useRender } from "@base-ui/react/use-render";
|
|
9
|
+
import { cva } from "class-variance-authority";
|
|
9
10
|
//#region src/footer.tsx
|
|
10
11
|
function FooterRoot({ className, containerClassName, children, ...props }) {
|
|
11
12
|
return /* @__PURE__ */ jsx("footer", {
|
|
@@ -117,18 +118,28 @@ function FooterLinkItem({ className, ...props }) {
|
|
|
117
118
|
...props
|
|
118
119
|
});
|
|
119
120
|
}
|
|
120
|
-
|
|
121
|
+
const footerLinkVariants = cva("inline-flex items-center gap-gdt-sm underline-offset-4 transition-colors hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-line-focus focus-visible:ring-offset-2", {
|
|
122
|
+
variants: { variant: {
|
|
123
|
+
default: "text-fg-secondary hover:text-fg-brand",
|
|
124
|
+
brand: "text-fg-brand"
|
|
125
|
+
} },
|
|
126
|
+
defaultVariants: { variant: "default" }
|
|
127
|
+
});
|
|
128
|
+
function FooterLink({ className, children, render, showArrow = false, variant = "default", ...props }) {
|
|
121
129
|
return useRender({
|
|
122
130
|
defaultTagName: "a",
|
|
123
131
|
props: mergeProps({
|
|
124
|
-
className: cn(
|
|
132
|
+
className: cn(footerLinkVariants({ variant }), className),
|
|
125
133
|
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", { children }), showArrow && /* @__PURE__ */ jsx(ChevronRight, {
|
|
126
134
|
"aria-hidden": "true",
|
|
127
135
|
className: "size-3.5"
|
|
128
136
|
})] })
|
|
129
137
|
}, props),
|
|
130
138
|
render,
|
|
131
|
-
state: {
|
|
139
|
+
state: {
|
|
140
|
+
slot: "footer-link",
|
|
141
|
+
variant
|
|
142
|
+
}
|
|
132
143
|
});
|
|
133
144
|
}
|
|
134
145
|
function FooterContactGrid({ className, ...props }) {
|
|
@@ -229,4 +240,4 @@ function FooterLegalLinks({ className, ...props }) {
|
|
|
229
240
|
});
|
|
230
241
|
}
|
|
231
242
|
//#endregion
|
|
232
|
-
export { FooterBody, FooterBottom, FooterContact, FooterContactGrid, FooterContactTitle, FooterCta, FooterCtaActions, FooterCtaContent, FooterCtaDescription, FooterCtaTitle, FooterDescription, FooterHeader, FooterLegalLinks, FooterLink, FooterLinkItem, FooterLinkList, FooterNav, FooterNewsletter, FooterNewsletterControl, FooterNewsletterDescription, FooterNewsletterTitle, FooterRoot, FooterSection, FooterSectionTitle, FooterSocialItem, FooterSocialLink, FooterSocialList, FooterTitle };
|
|
243
|
+
export { FooterBody, FooterBottom, FooterContact, FooterContactGrid, FooterContactTitle, FooterCta, FooterCtaActions, FooterCtaContent, FooterCtaDescription, FooterCtaTitle, FooterDescription, FooterHeader, FooterLegalLinks, FooterLink, FooterLinkItem, FooterLinkList, FooterNav, FooterNewsletter, FooterNewsletterControl, FooterNewsletterDescription, FooterNewsletterTitle, FooterRoot, FooterSection, FooterSectionTitle, FooterSocialItem, FooterSocialLink, FooterSocialList, FooterTitle, footerLinkVariants };
|
|
@@ -8,8 +8,8 @@ import { ReviewBlock } from "./review-block.js";
|
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { cva } from "class-variance-authority";
|
|
10
10
|
//#region src/media-gallery-hero.tsx
|
|
11
|
-
const mediaGalleryHeroVariants = cva("relative isolate h-[350px] w-full overflow-hidden rounded-[32px]
|
|
12
|
-
const mediaGalleryImagesVariants = cva("absolute inset-0 z-0 hidden size-full min-h-0 min-w-0
|
|
11
|
+
const mediaGalleryHeroVariants = cva("relative isolate h-[350px] w-full overflow-hidden rounded-[32px] md:h-[380px] md:rounded-[40px] lg:h-[480px]");
|
|
12
|
+
const mediaGalleryImagesVariants = cva("absolute inset-0 z-0 hidden size-full min-h-0 min-w-0 md:grid", {
|
|
13
13
|
variants: { imageCount: {
|
|
14
14
|
1: "",
|
|
15
15
|
2: "gap-2 grid-cols-[minmax(0,3fr)_minmax(0,2fr)]",
|
|
@@ -77,7 +77,7 @@ function MobileGalleryCounter({ total }) {
|
|
|
77
77
|
function MobileMediaCarousel({ currentMedia, images, mediaTotal, onMediaClick, renderMedia, showMobileIndicator }) {
|
|
78
78
|
const carouselImages = images.length > 0 ? images : [{}];
|
|
79
79
|
return /* @__PURE__ */ jsxs(Carousel, {
|
|
80
|
-
className: "absolute inset-0 z-0 block size-full rounded-[32px]
|
|
80
|
+
className: "absolute inset-0 z-0 block size-full rounded-[32px] md:hidden",
|
|
81
81
|
imageSize: "full",
|
|
82
82
|
opts: {
|
|
83
83
|
align: "start",
|
package/dist/price-tag.d.ts
CHANGED
|
@@ -3,32 +3,100 @@ import * as React from "react";
|
|
|
3
3
|
//#region src/price-tag.d.ts
|
|
4
4
|
declare const priceTagVariants: (props?: ({
|
|
5
5
|
size?: "lg" | "md" | "sm" | "xs" | "xxs" | null | undefined;
|
|
6
|
+
hasDescription?: boolean | null | undefined;
|
|
6
7
|
hasMonthlyPayment?: boolean | null | undefined;
|
|
7
8
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
9
|
type PriceTagSize = NonNullable<VariantProps<typeof priceTagVariants>["size"]>;
|
|
10
|
+
/**
|
|
11
|
+
* A currency in the dropdown.
|
|
12
|
+
*
|
|
13
|
+
* `currency`, `value` and `description` are all PriceTag needs, and none of
|
|
14
|
+
* them are interpreted — `description` in particular is printed as given, so
|
|
15
|
+
* the wording is yours. The rest are legacy and go in the next major.
|
|
16
|
+
*/
|
|
9
17
|
type PriceTagCurrencyOption = {
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
/** Shown at the leading edge, and matched against `selectedCurrency`. */
|
|
19
|
+
currency?: string;
|
|
20
|
+
/** The price, exactly as it should read. */
|
|
21
|
+
value?: React.ReactNode;
|
|
22
|
+
/** A second line under the price. Your words, printed as given. */
|
|
23
|
+
description?: React.ReactNode;
|
|
24
|
+
/** @deprecated Use `currency`. Removed in the next major. */
|
|
25
|
+
code?: string;
|
|
26
|
+
/** @deprecated Use `value`. Removed in the next major. */
|
|
27
|
+
amount?: string;
|
|
28
|
+
/** @deprecated Format before you pass it. Removed in the next major. */
|
|
12
29
|
shortAmount?: string;
|
|
30
|
+
/** @deprecated Use `description`. Removed in the next major. */
|
|
13
31
|
monthlyPayment?: string;
|
|
32
|
+
/** @deprecated Use `description`. Removed in the next major. */
|
|
14
33
|
monthlyPaymentLabel?: string;
|
|
15
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* A currency option after the legacy fields have been folded in — what
|
|
37
|
+
* `onCurrencyChange` and `renderMenuOption` receive. The original object is
|
|
38
|
+
* spread in, so code still reading `code` or `amount` off it keeps working.
|
|
39
|
+
*
|
|
40
|
+
* Generic over your own option type, which `PriceTag` infers from `currencies`.
|
|
41
|
+
* Anything extra you hang on an option — a rate, an id, a flag — arrives here
|
|
42
|
+
* typed, so `renderMenuOption` can use it without a cast.
|
|
43
|
+
*/
|
|
44
|
+
type PriceTagMenuOption<TOption extends PriceTagCurrencyOption = PriceTagCurrencyOption> = TOption & {
|
|
45
|
+
currency: string;
|
|
46
|
+
value: React.ReactNode;
|
|
47
|
+
description?: React.ReactNode;
|
|
48
|
+
/** Whether this is the row `selectedCurrency` points at. */
|
|
49
|
+
selected: boolean;
|
|
50
|
+
};
|
|
51
|
+
/** @deprecated Format before you pass it. Removed in the next major. */
|
|
16
52
|
type PriceTagFormat = "full" | "shortened";
|
|
17
|
-
type PriceTagProps = Omit<React.ComponentProps<"button">, "children"> & {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
53
|
+
type PriceTagProps<TOption extends PriceTagCurrencyOption = PriceTagCurrencyOption> = Omit<React.ComponentProps<"button">, "children" | "value"> & {
|
|
54
|
+
/** The price, exactly as it should read. */
|
|
55
|
+
value?: React.ReactNode;
|
|
56
|
+
/** A second line under the price. Your words, printed as given. */
|
|
57
|
+
description?: React.ReactNode;
|
|
21
58
|
size?: PriceTagSize;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Providing this turns the tag into a dropdown trigger. Options carry
|
|
61
|
+
* whatever else you need — `TOption` is inferred from here, and the
|
|
62
|
+
* callbacks below receive it intact.
|
|
63
|
+
*/
|
|
64
|
+
currencies?: TOption[];
|
|
25
65
|
selectedCurrency?: string;
|
|
26
|
-
onCurrencyChange?: (
|
|
66
|
+
onCurrencyChange?: (option: PriceTagMenuOption<NoInfer<TOption>>) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Replace a row's contents. PriceTag keeps the button, its selected state
|
|
69
|
+
* and its keyboard behaviour, so a custom row can't quietly lose them.
|
|
70
|
+
*/
|
|
71
|
+
renderMenuOption?: (option: PriceTagMenuOption<NoInfer<TOption>>) => React.ReactNode;
|
|
27
72
|
open?: boolean;
|
|
28
73
|
defaultOpen?: boolean;
|
|
29
74
|
onOpenChange?: (open: boolean) => void;
|
|
30
75
|
popoverClassName?: string;
|
|
76
|
+
/** @deprecated Use `value`. Removed in the next major. */
|
|
77
|
+
amount?: string;
|
|
78
|
+
/** @deprecated Format before you pass it. Removed in the next major. */
|
|
79
|
+
shortAmount?: string;
|
|
80
|
+
/** @deprecated Format before you pass it. Removed in the next major. */
|
|
81
|
+
format?: PriceTagFormat;
|
|
82
|
+
/** @deprecated Use `description`. Removed in the next major. */
|
|
83
|
+
monthlyPayment?: string;
|
|
84
|
+
/** @deprecated Use `description`. Removed in the next major. */
|
|
85
|
+
monthlyPaymentLabel?: string;
|
|
31
86
|
};
|
|
32
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Pill-shaped price display, optionally a trigger for a currency dropdown.
|
|
89
|
+
*
|
|
90
|
+
* Display only: it prints `value` and `description` as given and never
|
|
91
|
+
* composes a sentence, picks a format, or decides that a line is too small to
|
|
92
|
+
* be worth showing. Wording, currency conversion and number formatting all
|
|
93
|
+
* belong to the app, which is the only place that knows the locale, the
|
|
94
|
+
* product's voice, and what the second line is actually for.
|
|
95
|
+
*
|
|
96
|
+
* The legacy `amount` / `shortAmount` / `format` / `monthlyPayment` props still
|
|
97
|
+
* work and keep their old behaviour exactly, including the size threshold that
|
|
98
|
+
* hid the monthly line below `md`. They go in the next major.
|
|
99
|
+
*/
|
|
100
|
+
declare function PriceTag<TOption extends PriceTagCurrencyOption = PriceTagCurrencyOption>({ value, description, size, currencies, selectedCurrency, onCurrencyChange, renderMenuOption, open, defaultOpen, onOpenChange, className, popoverClassName, type, amount, shortAmount, format, monthlyPayment, monthlyPaymentLabel, ...props }: PriceTagProps<TOption>): React.JSX.Element;
|
|
33
101
|
//#endregion
|
|
34
|
-
export { PriceTag, type PriceTagCurrencyOption, type PriceTagFormat, type PriceTagProps, type PriceTagSize, priceTagVariants };
|
|
102
|
+
export { PriceTag, type PriceTagCurrencyOption, type PriceTagFormat, type PriceTagMenuOption, type PriceTagProps, type PriceTagSize, priceTagVariants };
|
package/dist/price-tag.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Check, ChevronDown } from "./icons.js";
|
|
3
3
|
import { t as cn } from "./cn-BI_4DMBf.js";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { cva } from "class-variance-authority";
|
|
6
6
|
import { Popover } from "@base-ui/react/popover";
|
|
7
7
|
//#region src/price-tag.tsx
|
|
@@ -14,6 +14,12 @@ const priceTagVariants = cva("group/price-tag inline-flex w-fit shrink-0 items-c
|
|
|
14
14
|
md: "min-h-10.5 gap-1.5 px-5 py-2 text-gdt-md [&>svg]:size-4",
|
|
15
15
|
lg: "min-h-13 gap-2 px-7 py-3 text-gdt-lg [&>svg]:size-4.5"
|
|
16
16
|
},
|
|
17
|
+
/** Two lines sit top-aligned against the chevron rather than centred. */
|
|
18
|
+
hasDescription: {
|
|
19
|
+
true: "items-start",
|
|
20
|
+
false: ""
|
|
21
|
+
},
|
|
22
|
+
/** @deprecated Use `hasDescription`. Removed in the next major. */
|
|
17
23
|
hasMonthlyPayment: {
|
|
18
24
|
true: "items-start",
|
|
19
25
|
false: ""
|
|
@@ -21,37 +27,102 @@ const priceTagVariants = cva("group/price-tag inline-flex w-fit shrink-0 items-c
|
|
|
21
27
|
},
|
|
22
28
|
defaultVariants: {
|
|
23
29
|
size: "md",
|
|
24
|
-
|
|
30
|
+
hasDescription: false
|
|
25
31
|
}
|
|
26
32
|
});
|
|
33
|
+
/** The description is quieter than the price, and quietest at the largest size. */
|
|
34
|
+
const descriptionSizeClassName = {
|
|
35
|
+
xxs: "text-gdt-subtext",
|
|
36
|
+
xs: "text-gdt-subtext",
|
|
37
|
+
sm: "text-gdt-subtext",
|
|
38
|
+
md: "text-gdt-subtext",
|
|
39
|
+
lg: "text-gdt-xs"
|
|
40
|
+
};
|
|
27
41
|
function getDisplayAmount({ amount, shortAmount, format }) {
|
|
28
42
|
return format === "shortened" && shortAmount ? shortAmount : amount;
|
|
29
43
|
}
|
|
30
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Folds the legacy fields into the current three. New fields win outright, so
|
|
46
|
+
* an option can be migrated one at a time.
|
|
47
|
+
*/
|
|
48
|
+
function resolveMenuOption(option, format, selectedCurrency) {
|
|
49
|
+
const currency = option.currency ?? option.code ?? "";
|
|
50
|
+
const value = option.value ?? getDisplayAmount({
|
|
51
|
+
amount: option.amount,
|
|
52
|
+
shortAmount: option.shortAmount,
|
|
53
|
+
format
|
|
54
|
+
});
|
|
55
|
+
const description = option.description ?? (option.monthlyPayment ? `${option.monthlyPaymentLabel ?? "As low as"} ${option.monthlyPayment} per month` : void 0);
|
|
56
|
+
return {
|
|
57
|
+
...option,
|
|
58
|
+
currency,
|
|
59
|
+
value,
|
|
60
|
+
description,
|
|
61
|
+
selected: currency === selectedCurrency
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function DefaultMenuOption({ option }) {
|
|
65
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
66
|
+
/* @__PURE__ */ jsx("span", {
|
|
67
|
+
className: "min-w-10 text-gdt-sm font-semibold text-fg-brand",
|
|
68
|
+
children: option.currency
|
|
69
|
+
}),
|
|
70
|
+
/* @__PURE__ */ jsxs("span", {
|
|
71
|
+
className: "ml-auto flex min-w-0 flex-col items-end",
|
|
72
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
73
|
+
className: "text-gdt-sm font-bold text-fg-primary",
|
|
74
|
+
children: option.value
|
|
75
|
+
}), option.description ? /* @__PURE__ */ jsx("span", {
|
|
76
|
+
className: "text-gdt-xs text-fg-secondary",
|
|
77
|
+
children: option.description
|
|
78
|
+
}) : null]
|
|
79
|
+
}),
|
|
80
|
+
option.selected ? /* @__PURE__ */ jsx(Check, {
|
|
81
|
+
"aria-hidden": "true",
|
|
82
|
+
className: "size-4 shrink-0 text-fg-brand"
|
|
83
|
+
}) : null
|
|
84
|
+
] });
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Pill-shaped price display, optionally a trigger for a currency dropdown.
|
|
88
|
+
*
|
|
89
|
+
* Display only: it prints `value` and `description` as given and never
|
|
90
|
+
* composes a sentence, picks a format, or decides that a line is too small to
|
|
91
|
+
* be worth showing. Wording, currency conversion and number formatting all
|
|
92
|
+
* belong to the app, which is the only place that knows the locale, the
|
|
93
|
+
* product's voice, and what the second line is actually for.
|
|
94
|
+
*
|
|
95
|
+
* The legacy `amount` / `shortAmount` / `format` / `monthlyPayment` props still
|
|
96
|
+
* work and keep their old behaviour exactly, including the size threshold that
|
|
97
|
+
* hid the monthly line below `md`. They go in the next major.
|
|
98
|
+
*/
|
|
99
|
+
function PriceTag({ value, description, size = "md", currencies, selectedCurrency, onCurrencyChange, renderMenuOption, open, defaultOpen, onOpenChange, className, popoverClassName, type = "button", amount, shortAmount, format = "full", monthlyPayment, monthlyPaymentLabel = "Low as", ...props }) {
|
|
31
100
|
const hasDropdown = Boolean(currencies?.length);
|
|
32
|
-
const
|
|
101
|
+
const resolvedValue = value ?? getDisplayAmount({
|
|
102
|
+
amount,
|
|
103
|
+
shortAmount,
|
|
104
|
+
format
|
|
105
|
+
});
|
|
106
|
+
const legacyDescription = monthlyPayment && (size === "md" || size === "lg") ? `${monthlyPaymentLabel} ${monthlyPayment} monthly` : void 0;
|
|
107
|
+
const resolvedDescription = description ?? legacyDescription;
|
|
33
108
|
const pill = /* @__PURE__ */ jsxs("span", {
|
|
34
109
|
className: "flex min-w-0 flex-col gap-1",
|
|
35
|
-
children: [/* @__PURE__ */ jsx("span", { children:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
format
|
|
39
|
-
}) }), displayMonthlyPayment ? /* @__PURE__ */ jsx("span", {
|
|
40
|
-
className: cn("hidden text-gdt-subtext font-medium leading-none opacity-90", {
|
|
41
|
-
"inline-block": size === "md",
|
|
42
|
-
"inline-block text-gdt-xs": size === "lg"
|
|
43
|
-
}),
|
|
44
|
-
children: displayMonthlyPayment
|
|
110
|
+
children: [/* @__PURE__ */ jsx("span", { children: resolvedValue }), resolvedDescription ? /* @__PURE__ */ jsx("span", {
|
|
111
|
+
className: cn("font-medium leading-none opacity-90", descriptionSizeClassName[size]),
|
|
112
|
+
children: resolvedDescription
|
|
45
113
|
}) : null]
|
|
46
114
|
});
|
|
115
|
+
const pillClassName = cn(priceTagVariants({
|
|
116
|
+
size,
|
|
117
|
+
hasDescription: Boolean(resolvedDescription)
|
|
118
|
+
}), className);
|
|
47
119
|
if (!hasDropdown) return /* @__PURE__ */ jsx("span", {
|
|
48
120
|
"data-slot": "price-tag",
|
|
49
|
-
className:
|
|
50
|
-
|
|
51
|
-
hasMonthlyPayment: Boolean(displayMonthlyPayment)
|
|
52
|
-
}), className),
|
|
121
|
+
className: pillClassName,
|
|
122
|
+
...props,
|
|
53
123
|
children: pill
|
|
54
124
|
});
|
|
125
|
+
const options = (currencies ?? []).map((currency) => resolveMenuOption(currency, format, selectedCurrency));
|
|
55
126
|
return /* @__PURE__ */ jsxs(Popover.Root, {
|
|
56
127
|
open,
|
|
57
128
|
defaultOpen,
|
|
@@ -60,10 +131,7 @@ function PriceTag({ amount, shortAmount, format = "full", size = "md", monthlyPa
|
|
|
60
131
|
"data-slot": "price-tag",
|
|
61
132
|
"data-interactive": "true",
|
|
62
133
|
type,
|
|
63
|
-
className:
|
|
64
|
-
size,
|
|
65
|
-
hasMonthlyPayment: Boolean(displayMonthlyPayment)
|
|
66
|
-
}), className),
|
|
134
|
+
className: pillClassName,
|
|
67
135
|
...props,
|
|
68
136
|
children: [pill, /* @__PURE__ */ jsx(ChevronDown, {
|
|
69
137
|
"aria-hidden": "true",
|
|
@@ -75,41 +143,14 @@ function PriceTag({ amount, shortAmount, format = "full", size = "md", monthlyPa
|
|
|
75
143
|
children: /* @__PURE__ */ jsx(Popover.Popup, {
|
|
76
144
|
"data-slot": "price-tag-popover",
|
|
77
145
|
className: cn("z-50 flex w-[17.25rem] max-w-[calc(100vw-2rem)] origin-(--transform-origin) flex-col rounded-xl border border-line-subtle bg-surface-overlay p-2 text-fg-primary shadow-3 outline-hidden transition-[opacity,scale,translate] duration-[var(--duration-motion-popup)] ease-gdt-out data-starting-style:opacity-0 data-starting-style:scale-95 data-ending-style:opacity-0 data-ending-style:scale-95 data-[side=bottom]:data-starting-style:translate-y-2 data-[side=bottom]:data-ending-style:translate-y-2 data-[side=top]:data-starting-style:-translate-y-2 data-[side=top]:data-ending-style:-translate-y-2 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100 motion-reduce:data-[side=bottom]:data-starting-style:translate-y-0 motion-reduce:data-[side=bottom]:data-ending-style:translate-y-0 motion-reduce:data-[side=top]:data-starting-style:translate-y-0 motion-reduce:data-[side=top]:data-ending-style:translate-y-0", popoverClassName),
|
|
78
|
-
children:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
return /* @__PURE__ */ jsxs("button", {
|
|
87
|
-
type: "button",
|
|
88
|
-
"data-active": active ? "true" : void 0,
|
|
89
|
-
className: "flex min-h-14 w-full items-center gap-3 rounded-lg px-3 text-start outline-none transition-colors hover:bg-surface-brand-subtle focus-visible:bg-surface-brand-subtle focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] data-[active=true]:bg-surface-brand-subtle",
|
|
90
|
-
onClick: () => onCurrencyChange?.(currency),
|
|
91
|
-
children: [
|
|
92
|
-
/* @__PURE__ */ jsx("span", {
|
|
93
|
-
className: "min-w-10 text-gdt-sm font-semibold text-fg-brand",
|
|
94
|
-
children: currency.code
|
|
95
|
-
}),
|
|
96
|
-
/* @__PURE__ */ jsxs("span", {
|
|
97
|
-
className: "ml-auto flex min-w-0 flex-col items-end",
|
|
98
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
99
|
-
className: "text-gdt-sm font-bold text-fg-primary",
|
|
100
|
-
children: optionAmount
|
|
101
|
-
}), optionMonthlyPayment ? /* @__PURE__ */ jsx("span", {
|
|
102
|
-
className: "text-gdt-xs text-fg-secondary",
|
|
103
|
-
children: optionMonthlyPayment
|
|
104
|
-
}) : null]
|
|
105
|
-
}),
|
|
106
|
-
active ? /* @__PURE__ */ jsx(Check, {
|
|
107
|
-
"aria-hidden": "true",
|
|
108
|
-
className: "size-4 shrink-0 text-fg-brand"
|
|
109
|
-
}) : null
|
|
110
|
-
]
|
|
111
|
-
}, currency.code);
|
|
112
|
-
})
|
|
146
|
+
children: options.map((option) => /* @__PURE__ */ jsx("button", {
|
|
147
|
+
type: "button",
|
|
148
|
+
"data-slot": "price-tag-option",
|
|
149
|
+
"data-active": option.selected ? "true" : void 0,
|
|
150
|
+
className: "flex min-h-14 w-full items-center gap-3 rounded-lg px-3 text-start outline-none transition-colors hover:bg-surface-brand-subtle focus-visible:bg-surface-brand-subtle focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] data-[active=true]:bg-surface-brand-subtle",
|
|
151
|
+
onClick: () => onCurrencyChange?.(option),
|
|
152
|
+
children: renderMenuOption ? renderMenuOption(option) : /* @__PURE__ */ jsx(DefaultMenuOption, { option })
|
|
153
|
+
}, option.currency))
|
|
113
154
|
})
|
|
114
155
|
}) })]
|
|
115
156
|
});
|
|
@@ -62,7 +62,7 @@ function PropertyListingCardMediaTop({ className, ...props }) {
|
|
|
62
62
|
function PropertyListingCardOptionsBar({ className, render, ...props }) {
|
|
63
63
|
return useRender({
|
|
64
64
|
defaultTagName: "a",
|
|
65
|
-
props: mergeProps({ className: cn("absolute inset-x-0 bottom-0 z-10 flex min-h-9.5 items-center justify-center gap-1 bg-black/
|
|
65
|
+
props: mergeProps({ className: cn("absolute inset-x-0 bottom-0 z-10 flex min-h-9.5 items-center justify-center gap-1 bg-black/70 px-4 py-1.5 text-center text-gdt-subtext font-extrabold tracking-wide text-white uppercase transition-[background-color] hover:bg-black/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-line-focus", "supports-backdrop-filter:bg-black/40 supports-backdrop-filter:backdrop-blur-[15px] supports-backdrop-filter:hover:bg-black/55", className) }, props),
|
|
66
66
|
render,
|
|
67
67
|
state: { slot: "property-listing-card-options-bar" }
|
|
68
68
|
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { t as ComponentSize } from "./size-context-BX6kAdVj.js";
|
|
2
|
+
import { TabsListVariant } from "./tabs.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
//#region src/scroll-spy.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Read and drive the spy from anywhere inside a `ScrollSpy` — an overflow
|
|
7
|
+
* "More" menu, a floating progress rail, a heading that echoes the section
|
|
8
|
+
* being read.
|
|
9
|
+
*
|
|
10
|
+
* Context reaches through portals, so a consumer inside an open `DropdownMenu`
|
|
11
|
+
* or `Popover` works without threading anything down by hand. `registerSection`
|
|
12
|
+
* is deliberately not exposed: sections are `ScrollSpyContent`'s to own.
|
|
13
|
+
*/
|
|
14
|
+
declare function useScrollSpy(): {
|
|
15
|
+
activeValue: string | undefined;
|
|
16
|
+
getSectionId: (value: string) => string;
|
|
17
|
+
getTriggerId: (value: string) => string;
|
|
18
|
+
scrollToSection: (value: string) => void;
|
|
19
|
+
};
|
|
20
|
+
type ScrollSpyProps = Omit<React.ComponentProps<"div">, "onChange"> & {
|
|
21
|
+
orientation?: "horizontal" | "vertical";
|
|
22
|
+
/** Controlled active section. */
|
|
23
|
+
value?: string;
|
|
24
|
+
/** Active section before the first scroll measurement lands. */
|
|
25
|
+
defaultValue?: string;
|
|
26
|
+
onValueChange?: (value: string) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Distance in px between the top of the scrollport and the line a section
|
|
29
|
+
* has to cross to become active. Set it to the height of a sticky header —
|
|
30
|
+
* it also becomes each section's `scroll-margin-top`, so scrolling lands
|
|
31
|
+
* the heading below the header rather than under it.
|
|
32
|
+
*/
|
|
33
|
+
scrollOffset?: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* `Tabs` for a page that shows everything at once: every section stays mounted
|
|
37
|
+
* and visible, the triggers scroll to their section, and the active trigger
|
|
38
|
+
* tracks whichever section the reader is currently on.
|
|
39
|
+
*
|
|
40
|
+
* The parts mirror the `Tabs` API — `ScrollSpy` / `ScrollSpyList` /
|
|
41
|
+
* `ScrollSpyTrigger` / `ScrollSpyContent`, matched by `value` — and the list
|
|
42
|
+
* takes the same `variant` and `size` as `TabsList`, so the two read as one
|
|
43
|
+
* family. Overflow ("More" dropdowns, per-breakpoint trigger counts) is the
|
|
44
|
+
* consuming app's job, exactly as it is with `Tabs`;
|
|
45
|
+
* `scrollSpyTriggerClassName` is exported so an overflow button can match the
|
|
46
|
+
* real triggers.
|
|
47
|
+
*/
|
|
48
|
+
declare function ScrollSpy({ className, orientation, value, defaultValue, onValueChange, scrollOffset, children, ...props }: ScrollSpyProps): React.JSX.Element;
|
|
49
|
+
type ScrollSpyListProps = React.ComponentProps<"nav"> & {
|
|
50
|
+
variant?: TabsListVariant;
|
|
51
|
+
size?: ComponentSize;
|
|
52
|
+
/**
|
|
53
|
+
* Keep the active trigger visible as the reader scrolls. Opt in when the
|
|
54
|
+
* list itself scrolls — without it, a strip narrower than its triggers looks
|
|
55
|
+
* frozen once the active one passes out of view. No-op when nothing
|
|
56
|
+
* overflows, and it never scrolls anything but the list.
|
|
57
|
+
*/
|
|
58
|
+
scrollActiveIntoView?: boolean;
|
|
59
|
+
};
|
|
60
|
+
declare function ScrollSpyList({ className, variant, size, children, scrollActiveIntoView, "aria-label": ariaLabel, ...props }: ScrollSpyListProps): React.JSX.Element;
|
|
61
|
+
/**
|
|
62
|
+
* Trigger styling, exported so an overflow "More" button outside the list can
|
|
63
|
+
* render the same look — the `Tabs` trigger classes verbatim, so the two
|
|
64
|
+
* components stay pixel-identical.
|
|
65
|
+
*/
|
|
66
|
+
declare const scrollSpyTriggerClassName: string;
|
|
67
|
+
type ScrollSpyTriggerProps = Omit<React.ComponentProps<"button">, "value"> & {
|
|
68
|
+
value: string;
|
|
69
|
+
};
|
|
70
|
+
declare function ScrollSpyTrigger({ className, value, type, onClick, ...props }: ScrollSpyTriggerProps): React.JSX.Element;
|
|
71
|
+
type ScrollSpyContentProps = Omit<React.ComponentProps<"section">, "value"> & {
|
|
72
|
+
value: string;
|
|
73
|
+
};
|
|
74
|
+
declare function ScrollSpyContent({ className, value, style, tabIndex, ...props }: ScrollSpyContentProps): React.JSX.Element;
|
|
75
|
+
//#endregion
|
|
76
|
+
export { ScrollSpy, ScrollSpyContent, type ScrollSpyContentProps, ScrollSpyList, type ScrollSpyListProps, type ScrollSpyProps, ScrollSpyTrigger, type ScrollSpyTriggerProps, scrollSpyTriggerClassName, useScrollSpy };
|