@giddaa-housing/ui 3.4.0 → 3.5.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 (131) hide show
  1. package/css/generated/shared.css +1 -1
  2. package/dist/accordion.js +1 -1
  3. package/dist/activity-timeline.js +1 -1
  4. package/dist/alert.js +1 -1
  5. package/dist/auth-layout.js +1 -1
  6. package/dist/avatar.js +1 -1
  7. package/dist/badge.js +1 -1
  8. package/dist/bar-chart.js +1 -1
  9. package/dist/breadcrumb.js +1 -1
  10. package/dist/bullet-chart.js +1 -1
  11. package/dist/button-group.js +1 -1
  12. package/dist/button.js +1 -1
  13. package/dist/calendar.js +2 -2
  14. package/dist/call-card.js +1 -1
  15. package/dist/card.js +1 -1
  16. package/dist/carousel.js +2 -2
  17. package/dist/chart.js +1 -1
  18. package/dist/chat.js +1 -1
  19. package/dist/checkbox.js +1 -1
  20. package/dist/collapsible.js +1 -1
  21. package/dist/combobox-B7cKFJ9V.js +398 -0
  22. package/dist/combobox.d.ts +2 -2
  23. package/dist/combobox.js +1 -308
  24. package/dist/cta.js +1 -1
  25. package/dist/currency-selector.js +2 -2
  26. package/dist/data-table.d.ts +1 -1
  27. package/dist/data-table.js +1 -1
  28. package/dist/date-picker.d.ts +4 -3
  29. package/dist/date-picker.js +7 -17
  30. package/dist/details-card.js +1 -1
  31. package/dist/dialog.js +1 -1
  32. package/dist/dropdown-menu.js +1 -1
  33. package/dist/dropzone.js +1 -1
  34. package/dist/editorial-card.js +2 -4
  35. package/dist/empty-state.js +1 -1
  36. package/dist/field.js +1 -1
  37. package/dist/file-upload.js +1 -1
  38. package/dist/filter.js +1 -1
  39. package/dist/footer.js +1 -1
  40. package/dist/funnel-chart.js +1 -1
  41. package/dist/gallery-indicator.js +1 -1
  42. package/dist/icons-C9JXR5Xv.d.ts +111 -0
  43. package/dist/icons.d.ts +2 -93
  44. package/dist/icons.js +37 -1
  45. package/dist/image.d.ts +59 -0
  46. package/dist/image.js +53 -0
  47. package/dist/infinite-scroll.js +3 -29
  48. package/dist/infotip.js +1 -1
  49. package/dist/input-group.js +1 -1
  50. package/dist/input-otp.js +1 -1
  51. package/dist/input.js +1 -1
  52. package/dist/item.js +1 -1
  53. package/dist/kpi-card.js +1 -1
  54. package/dist/label.js +1 -1
  55. package/dist/line-chart.js +1 -1
  56. package/dist/list-item.js +1 -1
  57. package/dist/map-indicator.js +1 -1
  58. package/dist/match.js +1 -1
  59. package/dist/media-gallery-hero.js +6 -3
  60. package/dist/media-player.js +5 -13
  61. package/dist/message.js +2 -12
  62. package/dist/mobile-sidebar.js +1 -1
  63. package/dist/multi-step-form.js +2 -2
  64. package/dist/number-input.js +1 -1
  65. package/dist/page-header.js +1 -1
  66. package/dist/pagination.js +1 -1
  67. package/dist/password-input.js +1 -1
  68. package/dist/phone-input.js +11 -17
  69. package/dist/photo-gallery.js +6 -3
  70. package/dist/picker-CsnHtbe4.d.ts +14 -0
  71. package/dist/{picker-xSTzeYhc.js → picker-DmIiQKLJ.js} +2 -7
  72. package/dist/pie-chart.js +1 -1
  73. package/dist/popover-DSlPeH7x.d.ts +20 -0
  74. package/dist/popover.d.ts +1 -19
  75. package/dist/popover.js +1 -1
  76. package/dist/price-tag.d.ts +1 -1
  77. package/dist/price-tag.js +50 -22
  78. package/dist/progress.js +1 -1
  79. package/dist/property-listing-card.d.ts +63 -1
  80. package/dist/property-listing-card.js +97 -28
  81. package/dist/purchase-option-card.js +1 -1
  82. package/dist/radar-chart.js +1 -1
  83. package/dist/radio-group.js +1 -1
  84. package/dist/review-block.js +1 -1
  85. package/dist/rich-text-editor.js +1 -1
  86. package/dist/scatter-chart.js +1 -1
  87. package/dist/scroll-spy.js +3 -3
  88. package/dist/search-input.js +1 -1
  89. package/dist/select.js +1 -1
  90. package/dist/separator.js +1 -1
  91. package/dist/sheet.js +1 -1
  92. package/dist/sidebar.js +3 -16
  93. package/dist/skeleton.js +1 -1
  94. package/dist/slider.js +1 -1
  95. package/dist/sonar.js +1 -1
  96. package/dist/sparkline.js +1 -1
  97. package/dist/stepper.js +1 -1
  98. package/dist/styles.css +42 -2
  99. package/dist/switch.js +1 -1
  100. package/dist/table-card.js +1 -1
  101. package/dist/table.js +1 -1
  102. package/dist/tabs.js +1 -1
  103. package/dist/tag.js +1 -1
  104. package/dist/testimonial-block.js +1 -1
  105. package/dist/textarea.js +1 -1
  106. package/dist/theme-switcher.js +1 -1
  107. package/dist/time-picker.d.ts +3 -2
  108. package/dist/time-picker.js +5 -7
  109. package/dist/toast.js +1 -1
  110. package/dist/tooltip.js +1 -1
  111. package/dist/top-navbar.js +1 -1
  112. package/dist/tree-map.js +1 -1
  113. package/dist/utils/cn.d.ts +5 -0
  114. package/dist/utils/cn.js +33 -0
  115. package/dist/utils/format.d.ts +45 -0
  116. package/dist/utils/format.js +70 -0
  117. package/dist/utils/phone.d.ts +98 -0
  118. package/dist/utils/phone.js +93 -0
  119. package/dist/utils/use-intersection.d.ts +9 -0
  120. package/dist/utils/use-intersection.js +29 -0
  121. package/dist/utils/use-is-mobile.d.ts +5 -0
  122. package/dist/utils/use-is-mobile.js +16 -0
  123. package/dist/utils/use-reduced-motion.d.ts +5 -0
  124. package/dist/{use-reduced-motion-BDvOK14x.js → utils/use-reduced-motion.js} +2 -2
  125. package/dist/utils/use-uncontrolled.d.ts +11 -0
  126. package/dist/{use-uncontrolled-CLfM1XbB.js → utils/use-uncontrolled.js} +2 -2
  127. package/dist/video-player-dialog.d.ts +12 -2
  128. package/dist/video-player-dialog.js +47 -7
  129. package/dist/waterfall-chart.js +1 -1
  130. package/package.json +9 -1
  131. package/dist/cn-BI_4DMBf.js +0 -23
@@ -0,0 +1,98 @@
1
+ import { CountryCode } from "libphonenumber-js";
2
+ //#region src/utils/phone.d.ts
3
+ /**
4
+ * Phone number helpers, shared by `PhoneInput` and by the forms around it.
5
+ *
6
+ * These are here because a phone number is validated in more places than it is
7
+ * typed — a zod schema, a server action, a CSV import, a table cell — and every
8
+ * one of those was otherwise reaching for its own regex. They carry no React
9
+ * and no styling, so a server-only module can import them.
10
+ *
11
+ * One rule runs through all of them: **invalid in, empty out**. Anything that
12
+ * is not a real, dialable number formats and parses to `""` rather than to
13
+ * `null`, to `undefined`, or to a half-cleaned version of what you passed. A
14
+ * caller then has one thing to check instead of three.
15
+ */
16
+ /** How a number should read once it is formatted. */
17
+ type PhoneNumberFormat =
18
+ /** `+234 801 234 5678` — the one to show when the country matters. */
19
+ "international" |
20
+ /** `0801 234 5678` — the one to show to someone in that country. */
21
+ "national" |
22
+ /** `+2348012345678` — the one to store. */
23
+ "e164" |
24
+ /** `tel:+234-801-234-5678` — the one to put in an `href`. */
25
+ "rfc3966";
26
+ interface PhoneNumberOptions {
27
+ /**
28
+ * The country to read a number against when it does not carry its own
29
+ * calling code. `"08012345678"` is a valid Nigerian number and nothing at
30
+ * all without this; `"+2348012345678"` needs it either way.
31
+ */
32
+ country?: CountryCode;
33
+ }
34
+ /**
35
+ * Whether a value is a phone number that could actually be dialed. Stricter
36
+ * than "has the right number of digits" — it checks the number against the
37
+ * ranges its country has assigned.
38
+ *
39
+ * Written to be passed by reference to a schema:
40
+ *
41
+ * ```ts
42
+ * z.string().refine(validatePhoneNumber, "Enter a valid phone number");
43
+ * yup.string().test("phone", "Enter a valid phone number", validatePhoneNumber);
44
+ * ```
45
+ *
46
+ * Empty and `null` are invalid. Mark the field optional in the schema rather
47
+ * than asking this to have an opinion about whether a missing number is a
48
+ * problem, which is the schema's business and not this function's.
49
+ */
50
+ declare function validatePhoneNumber(value: unknown, options?: PhoneNumberOptions): boolean;
51
+ /**
52
+ * The E.164 form of a valid number — `+2348012345678`, the one to store — or
53
+ * `""` if the value is not a valid number.
54
+ *
55
+ * ```ts
56
+ * parsePhoneNumber("0801 234 5678", { country: "NG" }); // "+2348012345678"
57
+ * parsePhoneNumber("0801 234"); // ""
58
+ * ```
59
+ */
60
+ declare function parsePhoneNumber(value: unknown, options?: PhoneNumberOptions): string;
61
+ /**
62
+ * A valid number written for a reader, or `""` if the value is not a valid
63
+ * number. Defaults to `international`, which is the form that does not depend
64
+ * on knowing where the reader is.
65
+ *
66
+ * ```ts
67
+ * formatPhoneNumber("+2348012345678"); // "+234 801 234 5678"
68
+ * formatPhoneNumber("+2348012345678", { format: "national" }); // "0801 234 5678"
69
+ * ```
70
+ *
71
+ * Blanking an invalid number is the point rather than an oversight: a value
72
+ * that cannot be formatted is a value that should be fixed, and showing a
73
+ * half-formatted one hides that.
74
+ */
75
+ declare function formatPhoneNumber(value: unknown, options?: PhoneNumberOptions & {
76
+ format?: PhoneNumberFormat;
77
+ }): string;
78
+ /** The country a valid number belongs to, or `undefined`. */
79
+ declare function getPhoneNumberCountry(value: unknown, options?: PhoneNumberOptions): CountryCode | undefined;
80
+ interface PhoneCountry {
81
+ /** ISO 3166-1 alpha-2, e.g. `"NG"`. */
82
+ code: CountryCode;
83
+ /** Localised display name, falling back to the code where `Intl` is absent. */
84
+ name: string;
85
+ /** Calling code without the `+`, e.g. `"234"`. */
86
+ callingCode: string;
87
+ }
88
+ /**
89
+ * Every country a number can be entered for, sorted by name in `locale`.
90
+ *
91
+ * `PhoneInput` builds its own list from this. It is exported because the
92
+ * country a phone number belongs to tends to be asked for elsewhere in the
93
+ * same form — an address, a nationality — and a second, differently-sorted
94
+ * list of countries in one form is a bug people notice.
95
+ */
96
+ declare function getPhoneCountries(locale?: string): PhoneCountry[];
97
+ //#endregion
98
+ export { type CountryCode, type PhoneCountry, type PhoneNumberFormat, type PhoneNumberOptions, formatPhoneNumber, getPhoneCountries, getPhoneNumberCountry, parsePhoneNumber, validatePhoneNumber };
@@ -0,0 +1,93 @@
1
+ import { getCountries, getCountryCallingCode, parsePhoneNumberFromString } from "libphonenumber-js";
2
+ //#region src/utils/phone.ts
3
+ /**
4
+ * Reads a value into a phone number, or `undefined` if it is not one.
5
+ *
6
+ * Deliberately tolerant about its argument: the exported helpers are written
7
+ * to be handed straight to `zod`'s `refine` or `yup`'s `test`, both of which
8
+ * call a validator with a second argument of their own, and to survive the
9
+ * `null` a database hands back.
10
+ */
11
+ function readPhoneNumber(value, options) {
12
+ if (typeof value !== "string" || value.trim() === "") return;
13
+ const country = typeof options === "object" && options !== null && "country" in options ? options.country : void 0;
14
+ const parsed = parsePhoneNumberFromString(value.trim(), country);
15
+ return parsed?.isValid() ? parsed : void 0;
16
+ }
17
+ /**
18
+ * Whether a value is a phone number that could actually be dialed. Stricter
19
+ * than "has the right number of digits" — it checks the number against the
20
+ * ranges its country has assigned.
21
+ *
22
+ * Written to be passed by reference to a schema:
23
+ *
24
+ * ```ts
25
+ * z.string().refine(validatePhoneNumber, "Enter a valid phone number");
26
+ * yup.string().test("phone", "Enter a valid phone number", validatePhoneNumber);
27
+ * ```
28
+ *
29
+ * Empty and `null` are invalid. Mark the field optional in the schema rather
30
+ * than asking this to have an opinion about whether a missing number is a
31
+ * problem, which is the schema's business and not this function's.
32
+ */
33
+ function validatePhoneNumber(value, options) {
34
+ return readPhoneNumber(value, options) !== void 0;
35
+ }
36
+ /**
37
+ * The E.164 form of a valid number — `+2348012345678`, the one to store — or
38
+ * `""` if the value is not a valid number.
39
+ *
40
+ * ```ts
41
+ * parsePhoneNumber("0801 234 5678", { country: "NG" }); // "+2348012345678"
42
+ * parsePhoneNumber("0801 234"); // ""
43
+ * ```
44
+ */
45
+ function parsePhoneNumber(value, options) {
46
+ return readPhoneNumber(value, options)?.number ?? "";
47
+ }
48
+ /**
49
+ * A valid number written for a reader, or `""` if the value is not a valid
50
+ * number. Defaults to `international`, which is the form that does not depend
51
+ * on knowing where the reader is.
52
+ *
53
+ * ```ts
54
+ * formatPhoneNumber("+2348012345678"); // "+234 801 234 5678"
55
+ * formatPhoneNumber("+2348012345678", { format: "national" }); // "0801 234 5678"
56
+ * ```
57
+ *
58
+ * Blanking an invalid number is the point rather than an oversight: a value
59
+ * that cannot be formatted is a value that should be fixed, and showing a
60
+ * half-formatted one hides that.
61
+ */
62
+ function formatPhoneNumber(value, options) {
63
+ const parsed = readPhoneNumber(value, options);
64
+ if (!parsed) return "";
65
+ switch (options?.format ?? "international") {
66
+ case "national": return parsed.formatNational();
67
+ case "e164": return parsed.number;
68
+ case "rfc3966": return parsed.getURI();
69
+ default: return parsed.formatInternational();
70
+ }
71
+ }
72
+ /** The country a valid number belongs to, or `undefined`. */
73
+ function getPhoneNumberCountry(value, options) {
74
+ return readPhoneNumber(value, options)?.country;
75
+ }
76
+ /**
77
+ * Every country a number can be entered for, sorted by name in `locale`.
78
+ *
79
+ * `PhoneInput` builds its own list from this. It is exported because the
80
+ * country a phone number belongs to tends to be asked for elsewhere in the
81
+ * same form — an address, a nationality — and a second, differently-sorted
82
+ * list of countries in one form is a bug people notice.
83
+ */
84
+ function getPhoneCountries(locale = "en") {
85
+ const regionNames = typeof Intl !== "undefined" && "DisplayNames" in Intl ? new Intl.DisplayNames([locale], { type: "region" }) : void 0;
86
+ return getCountries().map((code) => ({
87
+ code,
88
+ name: regionNames?.of(code) ?? code,
89
+ callingCode: getCountryCallingCode(code)
90
+ })).sort((a, b) => a.name.localeCompare(b.name, locale));
91
+ }
92
+ //#endregion
93
+ export { formatPhoneNumber, getPhoneCountries, getPhoneNumberCountry, parsePhoneNumber, validatePhoneNumber };
@@ -0,0 +1,9 @@
1
+ import { RefCallback } from "react";
2
+ //#region src/utils/use-intersection.d.ts
3
+ interface UseIntersectionReturnValue<T> {
4
+ ref: RefCallback<T | null>;
5
+ entry: IntersectionObserverEntry | null;
6
+ }
7
+ declare function useIntersection<T extends Element>(options?: IntersectionObserverInit): UseIntersectionReturnValue<T>;
8
+ //#endregion
9
+ export { UseIntersectionReturnValue, useIntersection };
@@ -0,0 +1,29 @@
1
+ import { useCallback, useRef, useState } from "react";
2
+ //#region src/utils/use-intersection.ts
3
+ function useIntersection(options) {
4
+ const [entry, setEntry] = useState(null);
5
+ const observer = useRef(null);
6
+ return {
7
+ ref: useCallback((element) => {
8
+ if (observer.current) {
9
+ observer.current.disconnect();
10
+ observer.current = null;
11
+ }
12
+ if (element === null) {
13
+ setEntry(null);
14
+ return;
15
+ }
16
+ if (typeof IntersectionObserver === "undefined") {
17
+ setEntry(null);
18
+ return;
19
+ }
20
+ observer.current = new IntersectionObserver(([_entry]) => {
21
+ setEntry(_entry ?? null);
22
+ }, options);
23
+ observer.current.observe(element);
24
+ }, [options]),
25
+ entry
26
+ };
27
+ }
28
+ //#endregion
29
+ export { useIntersection };
@@ -0,0 +1,5 @@
1
+ //#region src/utils/use-is-mobile.d.ts
2
+ declare const DEFAULT_MOBILE_BREAKPOINT = 1024;
3
+ declare function useIsMobile(breakpoint?: number): boolean;
4
+ //#endregion
5
+ export { DEFAULT_MOBILE_BREAKPOINT, useIsMobile };
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ //#region src/utils/use-is-mobile.ts
3
+ const DEFAULT_MOBILE_BREAKPOINT = 1024;
4
+ function useIsMobile(breakpoint = DEFAULT_MOBILE_BREAKPOINT) {
5
+ const [isMobile, setIsMobile] = React.useState(false);
6
+ React.useEffect(() => {
7
+ const mediaQuery = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
8
+ const update = () => setIsMobile(window.innerWidth < breakpoint);
9
+ update();
10
+ mediaQuery.addEventListener("change", update);
11
+ return () => mediaQuery.removeEventListener("change", update);
12
+ }, [breakpoint]);
13
+ return isMobile;
14
+ }
15
+ //#endregion
16
+ export { DEFAULT_MOBILE_BREAKPOINT, useIsMobile };
@@ -0,0 +1,5 @@
1
+ //#region src/utils/use-reduced-motion.d.ts
2
+ declare function subscribeToReducedMotion(onChange: (reduced: boolean) => void): () => void;
3
+ declare function useReducedMotion(): boolean;
4
+ //#endregion
5
+ export { subscribeToReducedMotion, useReducedMotion };
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- //#region src/lib/use-reduced-motion.ts
2
+ //#region src/utils/use-reduced-motion.ts
3
3
  const QUERY = "(prefers-reduced-motion: reduce)";
4
4
  function subscribeToReducedMotion(onChange) {
5
5
  const media = window.matchMedia(QUERY);
@@ -14,4 +14,4 @@ function useReducedMotion() {
14
14
  return reduced;
15
15
  }
16
16
  //#endregion
17
- export { useReducedMotion as t };
17
+ export { subscribeToReducedMotion, useReducedMotion };
@@ -0,0 +1,11 @@
1
+ //#region src/utils/use-uncontrolled.d.ts
2
+ interface UseUncontrolledOptions<T> {
3
+ value?: T;
4
+ defaultValue?: T;
5
+ finalValue?: T;
6
+ onChange?: (value: T, ...payload: unknown[]) => void;
7
+ }
8
+ type UseUncontrolledReturnValue<T> = [T, (value: T, ...payload: unknown[]) => void, boolean];
9
+ declare function useUncontrolled<T>({ value, defaultValue, finalValue, onChange }: UseUncontrolledOptions<T>): UseUncontrolledReturnValue<T>;
10
+ //#endregion
11
+ export { type UseUncontrolledOptions, type UseUncontrolledReturnValue, useUncontrolled };
@@ -1,5 +1,5 @@
1
1
  import { useState } from "react";
2
- //#region src/lib/use-uncontrolled.ts
2
+ //#region src/utils/use-uncontrolled.ts
3
3
  function useUncontrolled({ value, defaultValue, finalValue, onChange = () => {} }) {
4
4
  const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue !== void 0 ? defaultValue : finalValue);
5
5
  const handleUncontrolledChange = (nextValue, ...payload) => {
@@ -18,4 +18,4 @@ function useUncontrolled({ value, defaultValue, finalValue, onChange = () => {}
18
18
  ];
19
19
  }
20
20
  //#endregion
21
- export { useUncontrolled as t };
21
+ export { useUncontrolled };
@@ -34,11 +34,21 @@ type VideoPlayerDialogTitleProps = React.ComponentProps<typeof DialogTitle>;
34
34
  declare function VideoPlayerDialogTitle({ className, ...props }: VideoPlayerDialogTitleProps): React.JSX.Element;
35
35
  type VideoPlayerDialogDescriptionProps = React.ComponentProps<typeof DialogDescription>;
36
36
  declare function VideoPlayerDialogDescription({ className, ...props }: VideoPlayerDialogDescriptionProps): React.JSX.Element;
37
- interface VideoPlayerDialogPlaylistIndicatorProps extends React.ComponentProps<"span"> {
37
+ interface VideoPlayerDialogPlaylistIndicatorProps extends Omit<React.ComponentProps<"button">, "type"> {
38
38
  current: number;
39
39
  total: number;
40
40
  }
41
- declare function VideoPlayerDialogPlaylistIndicator({ current, total, className, ...props }: VideoPlayerDialogPlaylistIndicatorProps): React.JSX.Element;
41
+ /**
42
+ * Position in the playlist, and the way down to it. Clicking scrolls the
43
+ * dialog's surface to the playlist — the count is the only sign the playlist
44
+ * exists when the media fills the viewport, so it should be the thing that
45
+ * takes you there.
46
+ *
47
+ * A consumer's `onClick` runs first; calling `preventDefault` on the event
48
+ * keeps the scroll from happening, for a dialog that reveals its playlist some
49
+ * other way.
50
+ */
51
+ declare function VideoPlayerDialogPlaylistIndicator({ current, total, className, onClick, ...props }: VideoPlayerDialogPlaylistIndicatorProps): React.JSX.Element;
42
52
  type VideoPlayerDialogPlaylistProps = React.ComponentProps<"section">;
43
53
  declare function VideoPlayerDialogPlaylist({ className, ...props }: VideoPlayerDialogPlaylistProps): React.JSX.Element;
44
54
  type VideoPlayerDialogPlaylistHeaderProps = React.ComponentProps<"h3">;
@@ -1,7 +1,9 @@
1
1
  "use client";
2
- import { ListVideo } from "./icons.js";
3
- import { t as cn } from "./cn-BI_4DMBf.js";
2
+ import { ChevronDown, ListVideo } from "./icons.js";
3
+ import { cn } from "./utils/cn.js";
4
+ import { useReducedMotion } from "./utils/use-reduced-motion.js";
4
5
  import { Dialog, DialogContent, DialogDescription, DialogTitle, DialogTrigger } from "./dialog.js";
6
+ import { Image } from "./image.js";
5
7
  import { jsx, jsxs } from "react/jsx-runtime";
6
8
  import { cva } from "class-variance-authority";
7
9
  import * as React from "react";
@@ -105,10 +107,42 @@ function VideoPlayerDialogDescription({ className, ...props }) {
105
107
  ...props
106
108
  });
107
109
  }
108
- function VideoPlayerDialogPlaylistIndicator({ current, total, className, ...props }) {
109
- return /* @__PURE__ */ jsxs("span", {
110
+ /**
111
+ * Brings the playlist into view from the indicator.
112
+ *
113
+ * The playlist is found through the DOM rather than a ref because the two
114
+ * parts sit in unrelated branches of the tree — the indicator is inside the
115
+ * media's controls, the playlist is a sibling of the media — and the search is
116
+ * bounded by the dialog surface, so a second dialog on the page can't be the
117
+ * one that scrolls.
118
+ */
119
+ function scrollToPlaylist(from, smooth) {
120
+ (from.closest("[data-slot=video-player-dialog-surface]") ?? document).querySelector("[data-slot=video-player-dialog-playlist]")?.scrollIntoView({
121
+ behavior: smooth ? "smooth" : "auto",
122
+ block: "start"
123
+ });
124
+ }
125
+ /**
126
+ * Position in the playlist, and the way down to it. Clicking scrolls the
127
+ * dialog's surface to the playlist — the count is the only sign the playlist
128
+ * exists when the media fills the viewport, so it should be the thing that
129
+ * takes you there.
130
+ *
131
+ * A consumer's `onClick` runs first; calling `preventDefault` on the event
132
+ * keeps the scroll from happening, for a dialog that reveals its playlist some
133
+ * other way.
134
+ */
135
+ function VideoPlayerDialogPlaylistIndicator({ current, total, className, onClick, ...props }) {
136
+ const reducedMotion = useReducedMotion();
137
+ return /* @__PURE__ */ jsxs("button", {
138
+ type: "button",
110
139
  "data-slot": "video-player-dialog-playlist-indicator",
111
- className: cn("pointer-events-none absolute top-3 right-3 z-20 inline-flex h-9 items-center gap-2 rounded-full bg-black/40 px-3 text-gdt-xs font-bold whitespace-nowrap text-white shadow-[0_0_4px_rgb(0_0_0/0.1)] backdrop-blur-[15px]", className),
140
+ className: cn("absolute top-3 right-3 z-20 inline-flex h-9 cursor-pointer items-center gap-2 rounded-full bg-black/40 px-3 text-gdt-xs font-bold whitespace-nowrap text-white shadow-[0_0_4px_rgb(0_0_0/0.1)] backdrop-blur-[15px] transition-[background-color,transform] duration-(--duration-motion-press) ease-gdt-out outline-none hover:bg-black/60 focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] active:scale-[0.97] motion-reduce:active:scale-100", className),
141
+ onClick: (event) => {
142
+ onClick?.(event);
143
+ if (event.defaultPrevented) return;
144
+ scrollToPlaylist(event.currentTarget, !reducedMotion);
145
+ },
112
146
  ...props,
113
147
  children: [
114
148
  /* @__PURE__ */ jsx(ListVideo, {
@@ -123,13 +157,18 @@ function VideoPlayerDialogPlaylistIndicator({ current, total, className, ...prop
123
157
  total
124
158
  ]
125
159
  }),
160
+ /* @__PURE__ */ jsx(ChevronDown, {
161
+ "aria-hidden": "true",
162
+ className: "size-4"
163
+ }),
126
164
  /* @__PURE__ */ jsxs("span", {
127
165
  className: "sr-only",
128
166
  children: [
129
167
  "Video ",
130
168
  current,
131
169
  " of ",
132
- total
170
+ total,
171
+ ". Go to the playlist."
133
172
  ]
134
173
  })
135
174
  ]
@@ -168,7 +207,8 @@ function VideoPlayerDialogPlaylistItem({ active = false, index, poster, title, d
168
207
  className: "w-5 shrink-0 text-center text-gdt-xs font-semibold text-fg-caption-placeholder",
169
208
  children: index
170
209
  }) : null,
171
- poster ? /* @__PURE__ */ jsx("img", {
210
+ poster ? /* @__PURE__ */ jsx(Image, {
211
+ slot: "video-player-dialog-playlist-poster",
172
212
  src: poster,
173
213
  alt: "",
174
214
  className: "aspect-video w-20 shrink-0 rounded-md object-cover"
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t as cn } from "./cn-BI_4DMBf.js";
2
+ import { cn } from "./utils/cn.js";
3
3
  import { SizeProvider, useComponentSize } from "./size-context.js";
4
4
  import { ChartCard, ChartContainer, ChartDescription, ChartHeader, ChartTitle, ChartTooltip, ChartTooltipContent } from "./chart.js";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@giddaa-housing/ui",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "Giddaa reusable UI component library.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -17,6 +17,10 @@
17
17
  "types": "./dist/logo.d.ts",
18
18
  "import": "./dist/logo.js"
19
19
  },
20
+ "./utils/*": {
21
+ "types": "./dist/utils/*.d.ts",
22
+ "import": "./dist/utils/*.js"
23
+ },
20
24
  "./accordion": {
21
25
  "types": "./dist/accordion.d.ts",
22
26
  "import": "./dist/accordion.js"
@@ -169,6 +173,10 @@
169
173
  "types": "./dist/icons.d.ts",
170
174
  "import": "./dist/icons.js"
171
175
  },
176
+ "./image": {
177
+ "types": "./dist/image.d.ts",
178
+ "import": "./dist/image.js"
179
+ },
172
180
  "./infotip": {
173
181
  "types": "./dist/infotip.d.ts",
174
182
  "import": "./dist/infotip.js"
@@ -1,23 +0,0 @@
1
- import { clsx } from "clsx";
2
- import { extendTailwindMerge } from "tailwind-merge";
3
- //#region src/lib/cn.ts
4
- const twMerge = extendTailwindMerge({ extend: { classGroups: { "font-size": [{ text: [
5
- "gdt-display",
6
- "gdt-h1",
7
- "gdt-h2",
8
- "gdt-h3",
9
- "gdt-h4",
10
- "gdt-h5",
11
- "gdt-xl",
12
- "gdt-lg",
13
- "gdt-md",
14
- "gdt-sm",
15
- "gdt-xs",
16
- "gdt-subtext",
17
- "gdt-capitalized"
18
- ] }] } } });
19
- function cn(...inputs) {
20
- return twMerge(clsx(inputs));
21
- }
22
- //#endregion
23
- export { cn as t };