@giddaa-housing/ui 3.5.0 → 3.6.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 (101) hide show
  1. package/css/components.css +13 -0
  2. package/css/generated/shared.css +1 -1
  3. package/dist/badge.d.ts +5 -1
  4. package/dist/badge.js +19 -5
  5. package/dist/bar-chart.d.ts +1 -1
  6. package/dist/bullet-chart.d.ts +1 -1
  7. package/dist/button-BS5KCibF.d.ts +33 -0
  8. package/dist/button-group.d.ts +1 -1
  9. package/dist/button.d.ts +1 -1
  10. package/dist/button.js +49 -21
  11. package/dist/calendar.js +1 -1
  12. package/dist/carousel.d.ts +4 -2
  13. package/dist/carousel.js +11 -14
  14. package/dist/chart.d.ts +1 -1
  15. package/dist/chat.d.ts +1 -1
  16. package/dist/checkbox.d.ts +2 -2
  17. package/dist/checkbox.js +20 -6
  18. package/dist/{combobox-B7cKFJ9V.js → combobox-CACVdp1R.js} +41 -9
  19. package/dist/combobox.d.ts +8 -3
  20. package/dist/combobox.js +2 -2
  21. package/dist/cta.d.ts +1 -1
  22. package/dist/data-table.d.ts +2 -2
  23. package/dist/date-picker.d.ts +3 -3
  24. package/dist/date-picker.js +1 -1
  25. package/dist/dialog.d.ts +3 -1
  26. package/dist/dialog.js +9 -5
  27. package/dist/dropdown-menu.d.ts +1 -1
  28. package/dist/field.d.ts +4 -1
  29. package/dist/field.js +28 -11
  30. package/dist/footer.d.ts +2 -2
  31. package/dist/funnel-chart.d.ts +1 -1
  32. package/dist/heatmap-chart.d.ts +1 -1
  33. package/dist/input-group.d.ts +6 -6
  34. package/dist/input-group.js +38 -13
  35. package/dist/input-otp.d.ts +2 -2
  36. package/dist/input-otp.js +31 -14
  37. package/dist/input-size-context-BMhves-J.d.ts +13 -0
  38. package/dist/input-size-context.d.ts +2 -2
  39. package/dist/input-size-context.js +9 -2
  40. package/dist/input.d.ts +20 -2
  41. package/dist/input.js +30 -11
  42. package/dist/line-chart.d.ts +1 -1
  43. package/dist/mask-input.d.ts +1 -1
  44. package/dist/match.d.ts +5 -2
  45. package/dist/match.js +182 -26
  46. package/dist/media-gallery-hero.js +9 -7
  47. package/dist/mobile-sidebar.js +1 -0
  48. package/dist/number-input.d.ts +2 -2
  49. package/dist/number-input.js +2 -2
  50. package/dist/pagination.d.ts +1 -1
  51. package/dist/phone-input.d.ts +2 -2
  52. package/dist/phone-input.js +5 -4
  53. package/dist/picker-BTXBrsl4.js +62 -0
  54. package/dist/{picker-CsnHtbe4.d.ts → picker-j3txxA5t.d.ts} +2 -2
  55. package/dist/pie-chart.d.ts +1 -1
  56. package/dist/{popover-DSlPeH7x.d.ts → popover-CNymD33m.d.ts} +1 -1
  57. package/dist/popover.d.ts +1 -1
  58. package/dist/property-listing-card.d.ts +7 -3
  59. package/dist/property-listing-card.js +25 -8
  60. package/dist/radar-chart.d.ts +1 -1
  61. package/dist/radio-group.d.ts +2 -2
  62. package/dist/radio-group.js +23 -9
  63. package/dist/rich-text-editor.d.ts +1 -1
  64. package/dist/scatter-chart.d.ts +1 -1
  65. package/dist/scroll-spy.d.ts +8 -2
  66. package/dist/scroll-spy.js +13 -13
  67. package/dist/section-jumper.d.ts +31 -0
  68. package/dist/section-jumper.js +276 -0
  69. package/dist/select.d.ts +9 -4
  70. package/dist/select.js +112 -19
  71. package/dist/sheet.d.ts +3 -1
  72. package/dist/sheet.js +9 -5
  73. package/dist/sidebar.d.ts +2 -2
  74. package/dist/sidebar.js +1 -0
  75. package/dist/size-context-BVhHduLi.d.ts +33 -0
  76. package/dist/size-context.d.ts +2 -2
  77. package/dist/size-context.js +30 -1
  78. package/dist/slider.d.ts +5 -3
  79. package/dist/slider.js +25 -10
  80. package/dist/sonar.d.ts +1 -1
  81. package/dist/sonar.js +1 -1
  82. package/dist/sparkline.d.ts +1 -1
  83. package/dist/styles.css +1576 -258
  84. package/dist/switch.d.ts +2 -2
  85. package/dist/switch.js +20 -6
  86. package/dist/tabs.d.ts +1 -1
  87. package/dist/tag.d.ts +1 -1
  88. package/dist/textarea.d.ts +17 -1
  89. package/dist/textarea.js +23 -9
  90. package/dist/time-picker.d.ts +3 -3
  91. package/dist/time-picker.js +1 -1
  92. package/dist/tree-map.d.ts +1 -1
  93. package/dist/video-player-dialog.js +3 -2
  94. package/dist/waterfall-chart.d.ts +1 -1
  95. package/package.json +5 -1
  96. package/dist/button-DW5OijoR.d.ts +0 -17
  97. package/dist/input-CfoEJ8A6.d.ts +0 -12
  98. package/dist/input-size-context-C-SFx9sx.d.ts +0 -10
  99. package/dist/picker-DmIiQKLJ.js +0 -45
  100. package/dist/size-context-BX6kAdVj.d.ts +0 -11
  101. package/dist/textarea-IS_D8pXf.d.ts +0 -11
@@ -1,11 +1,16 @@
1
1
  "use client";
2
2
  import { cn } from "./utils/cn.js";
3
- import { useComponentSize } from "./size-context.js";
3
+ import { responsiveValueClasses, useComponentSizeValue } from "./size-context.js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import { cva } from "class-variance-authority";
6
6
  import { Radio } from "@base-ui/react/radio";
7
7
  import { RadioGroup as RadioGroup$1 } from "@base-ui/react/radio-group";
8
8
  //#region src/radio-group.tsx
9
+ const radioSizeVariants = {
10
+ sm: "size-4 [--radio-dot-size:0.625rem]",
11
+ md: "size-5 [--radio-dot-size:0.75rem]",
12
+ lg: "size-6 [--radio-dot-size:0.875rem]"
13
+ };
9
14
  function RadioGroup({ className, ...props }) {
10
15
  return /* @__PURE__ */ jsx(RadioGroup$1, {
11
16
  "data-slot": "radio-group",
@@ -13,11 +18,7 @@ function RadioGroup({ className, ...props }) {
13
18
  ...props
14
19
  });
15
20
  }
16
- const radioVariants = cva("group/radio-group-item peer relative flex aspect-square shrink-0 rounded-full border-2 bg-transparent text-transparent outline-none transition-[background-color,border-color,box-shadow,color,scale] duration-[var(--duration-motion-press)] ease-gdt-out active:scale-[0.97] motion-reduce:active:scale-100 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-line-focus focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] aria-invalid:border-line-danger aria-invalid:ring-3 aria-invalid:ring-line-danger/20 aria-invalid:focus-visible:border-line-danger aria-invalid:focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-danger)] border-line-strong data-checked:border-action-primary data-checked:text-action-primary data-disabled:cursor-not-allowed data-disabled:border-line-subtle data-disabled:bg-surface data-disabled:text-fg-caption-placeholder data-disabled:active:scale-100 size-5 [--radio-dot-size:0.75rem] group-data-[size=sm]/field:size-4 group-data-[size=sm]/field:[--radio-dot-size:0.625rem] group-data-[size=md]/field:size-5 group-data-[size=md]/field:[--radio-dot-size:0.75rem] group-data-[size=lg]/field:size-6 group-data-[size=lg]/field:[--radio-dot-size:0.875rem]", { variants: { size: {
17
- sm: "size-4 [--radio-dot-size:0.625rem]",
18
- md: "size-5 [--radio-dot-size:0.75rem]",
19
- lg: "size-6 [--radio-dot-size:0.875rem]"
20
- } } });
21
+ const radioVariants = cva("group/radio-group-item peer relative flex aspect-square shrink-0 rounded-full border-2 bg-transparent text-transparent outline-none transition-[background-color,border-color,box-shadow,color,scale] duration-[var(--duration-motion-press)] ease-gdt-out active:scale-[0.97] motion-reduce:active:scale-100 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-line-focus focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] aria-invalid:border-line-danger aria-invalid:ring-3 aria-invalid:ring-line-danger/20 aria-invalid:focus-visible:border-line-danger aria-invalid:focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-danger)] border-line-strong data-checked:border-action-primary data-checked:text-action-primary data-disabled:cursor-not-allowed data-disabled:border-line-subtle data-disabled:bg-surface data-disabled:text-fg-caption-placeholder data-disabled:active:scale-100", { variants: { size: radioSizeVariants } });
21
22
  /**
22
23
  * Compose each radio item with the shared field primitives.
23
24
  *
@@ -34,11 +35,11 @@ const radioVariants = cva("group/radio-group-item peer relative flex aspect-squa
34
35
  * ```
35
36
  */
36
37
  function RadioGroupItem({ className, size, ...props }) {
37
- const resolvedSize = useComponentSize(size);
38
+ const resolvedSize = responsiveValueClasses(useComponentSizeValue(size), responsiveRadioSizes);
38
39
  return /* @__PURE__ */ jsx(Radio.Root, {
39
40
  "data-slot": "radio-group-item",
40
- "data-size": resolvedSize,
41
- className: cn(radioVariants({ size: resolvedSize }), className),
41
+ "data-size": resolvedSize.base,
42
+ className: cn(radioVariants({ size: null }), resolvedSize.className, className),
42
43
  ...props,
43
44
  children: /* @__PURE__ */ jsx(Radio.Indicator, {
44
45
  "data-slot": "radio-group-indicator",
@@ -47,5 +48,18 @@ function RadioGroupItem({ className, size, ...props }) {
47
48
  })
48
49
  });
49
50
  }
51
+ const responsiveRadioSizes = {
52
+ base: radioSizeVariants,
53
+ md: {
54
+ sm: "md:size-4 md:[--radio-dot-size:0.625rem]",
55
+ md: "md:size-5 md:[--radio-dot-size:0.75rem]",
56
+ lg: "md:size-6 md:[--radio-dot-size:0.875rem]"
57
+ },
58
+ lg: {
59
+ sm: "lg:size-4 lg:[--radio-dot-size:0.625rem]",
60
+ md: "lg:size-5 lg:[--radio-dot-size:0.75rem]",
61
+ lg: "lg:size-6 lg:[--radio-dot-size:0.875rem]"
62
+ }
63
+ };
50
64
  //#endregion
51
65
  export { RadioGroup, RadioGroupItem, radioVariants };
@@ -1,4 +1,4 @@
1
- import { t as ComponentSize } from "./size-context-BX6kAdVj.js";
1
+ import { t as ComponentSize } from "./size-context-BVhHduLi.js";
2
2
  import * as React from "react";
3
3
  //#region src/rich-text-editor.d.ts
4
4
  interface RichTextEditorProps {
@@ -1,4 +1,4 @@
1
- import { t as ComponentSize } from "./size-context-BX6kAdVj.js";
1
+ import { t as ComponentSize } from "./size-context-BVhHduLi.js";
2
2
  import { ChartConfig } from "./chart.js";
3
3
  import * as React from "react";
4
4
  import { CartesianGrid, Scatter, ScatterChart as ScatterChart$1, XAxis, YAxis } from "recharts";
@@ -1,4 +1,4 @@
1
- import { t as ComponentSize } from "./size-context-BX6kAdVj.js";
1
+ import { t as ComponentSize } from "./size-context-BVhHduLi.js";
2
2
  import { TabsListVariant } from "./tabs.js";
3
3
  import * as React from "react";
4
4
  //#region src/scroll-spy.d.ts
@@ -11,6 +11,12 @@ import * as React from "react";
11
11
  * or `Popover` works without threading anything down by hand. `registerSection`
12
12
  * is deliberately not exposed: sections are `ScrollSpyContent`'s to own.
13
13
  */
14
+ declare function useOptionalScrollSpy(): {
15
+ activeValue: string | undefined;
16
+ getSectionId: (value: string) => string;
17
+ getTriggerId: (value: string) => string;
18
+ scrollToSection: (value: string) => void;
19
+ } | null;
14
20
  declare function useScrollSpy(): {
15
21
  activeValue: string | undefined;
16
22
  getSectionId: (value: string) => string;
@@ -73,4 +79,4 @@ type ScrollSpyContentProps = Omit<React.ComponentProps<"section">, "value"> & {
73
79
  };
74
80
  declare function ScrollSpyContent({ className, value, style, tabIndex, ...props }: ScrollSpyContentProps): React.JSX.Element;
75
81
  //#endregion
76
- export { ScrollSpy, ScrollSpyContent, type ScrollSpyContentProps, ScrollSpyList, type ScrollSpyListProps, type ScrollSpyProps, ScrollSpyTrigger, type ScrollSpyTriggerProps, scrollSpyTriggerClassName, useScrollSpy };
82
+ export { ScrollSpy, ScrollSpyContent, type ScrollSpyContentProps, ScrollSpyList, type ScrollSpyListProps, type ScrollSpyProps, ScrollSpyTrigger, type ScrollSpyTriggerProps, scrollSpyTriggerClassName, useOptionalScrollSpy, useScrollSpy };
@@ -49,19 +49,19 @@ function useScrollSpyContext(part) {
49
49
  * or `Popover` works without threading anything down by hand. `registerSection`
50
50
  * is deliberately not exposed: sections are `ScrollSpyContent`'s to own.
51
51
  */
52
+ function useOptionalScrollSpy() {
53
+ const context = React.useContext(ScrollSpyContext);
54
+ return React.useMemo(() => context ? {
55
+ activeValue: context.activeValue,
56
+ getSectionId: context.getSectionId,
57
+ getTriggerId: context.getTriggerId,
58
+ scrollToSection: context.scrollToSection
59
+ } : null, [context]);
60
+ }
52
61
  function useScrollSpy() {
53
- const { activeValue, getSectionId, getTriggerId, scrollToSection } = useScrollSpyContext("useScrollSpy");
54
- return React.useMemo(() => ({
55
- activeValue,
56
- getSectionId,
57
- getTriggerId,
58
- scrollToSection
59
- }), [
60
- activeValue,
61
- getSectionId,
62
- getTriggerId,
63
- scrollToSection
64
- ]);
62
+ const value = useOptionalScrollSpy();
63
+ if (!value) throw new Error("`useScrollSpy` must be used inside `ScrollSpy`.");
64
+ return value;
65
65
  }
66
66
  /**
67
67
  * `Tabs` for a page that shows everything at once: every section stays mounted
@@ -315,4 +315,4 @@ function ScrollSpyContent({ className, value, style, tabIndex = -1, ...props })
315
315
  });
316
316
  }
317
317
  //#endregion
318
- export { ScrollSpy, ScrollSpyContent, ScrollSpyList, ScrollSpyTrigger, scrollSpyTriggerClassName, useScrollSpy };
318
+ export { ScrollSpy, ScrollSpyContent, ScrollSpyList, ScrollSpyTrigger, scrollSpyTriggerClassName, useOptionalScrollSpy, useScrollSpy };
@@ -0,0 +1,31 @@
1
+ import * as React from "react";
2
+ //#region src/section-jumper.d.ts
3
+ type SectionJumperItem = {
4
+ value: string;
5
+ label: React.ReactNode;
6
+ /** DOM id of a standalone destination. Defaults to `value`. */
7
+ targetId?: string;
8
+ };
9
+ type SectionJumperProviderProps = React.ComponentProps<"div"> & {
10
+ /** Heading selectors to discover inside the provider. */
11
+ selectors?: string[];
12
+ /** Prefix used for IDs generated on headings that do not already have one. */
13
+ idPrefix?: string;
14
+ /** Sticky-header offset used when tracking and scrolling to headings. */
15
+ scrollOffset?: number;
16
+ };
17
+ declare function SectionJumperProvider({ children, selectors, idPrefix, scrollOffset, ...props }: SectionJumperProviderProps): React.JSX.Element;
18
+ type SectionJumperProps = Omit<React.ComponentProps<"div">, "children"> & {
19
+ /** Explicit sections. Omit inside `SectionJumperProvider` to use discovered headings. */
20
+ items?: SectionJumperItem[];
21
+ label?: string;
22
+ open?: boolean;
23
+ defaultOpen?: boolean;
24
+ onOpenChange?: (open: boolean) => void;
25
+ mobileBreakpoint?: number;
26
+ /** Sticky-header offset used by standalone target-id mode. */
27
+ scrollOffset?: number;
28
+ };
29
+ declare function SectionJumper({ className, items: itemsProp, label, open, defaultOpen, onOpenChange, mobileBreakpoint, scrollOffset: scrollOffsetProp, ...props }: SectionJumperProps): React.JSX.Element | null;
30
+ //#endregion
31
+ export { SectionJumper, type SectionJumperItem, type SectionJumperProps, SectionJumperProvider, type SectionJumperProviderProps };
@@ -0,0 +1,276 @@
1
+ "use client";
2
+ import { ChevronDown } from "./icons.js";
3
+ import { cn } from "./utils/cn.js";
4
+ import { useReducedMotion } from "./utils/use-reduced-motion.js";
5
+ import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
6
+ import { useOptionalScrollSpy } from "./scroll-spy.js";
7
+ import { useIsMobile } from "./utils/use-is-mobile.js";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import * as React from "react";
10
+ //#region src/section-jumper.tsx
11
+ const SectionJumperProviderContext = React.createContext(null);
12
+ function SectionJumperProvider({ children, selectors = ["h2"], idPrefix = "section-jumper-heading", scrollOffset = 0, ...props }) {
13
+ const rootRef = React.useRef(null);
14
+ const [items, setItems] = React.useState([]);
15
+ const selector = selectors.join(",");
16
+ React.useEffect(() => {
17
+ const root = rootRef.current;
18
+ if (!root || !selector) return;
19
+ const generatedIds = /* @__PURE__ */ new Set();
20
+ const discover = () => {
21
+ const usedIds = /* @__PURE__ */ new Set();
22
+ const next = [...root.querySelectorAll(selector)].reduce((result, heading, index) => {
23
+ const label = heading.textContent?.trim();
24
+ if (!label) return result;
25
+ let targetId = heading.id;
26
+ if (!targetId) {
27
+ targetId = `${idPrefix}-${index + 1}`;
28
+ let suffix = 2;
29
+ while (usedIds.has(targetId) || document.getElementById(targetId) && document.getElementById(targetId) !== heading) targetId = `${idPrefix}-${index + 1}-${suffix++}`;
30
+ heading.id = targetId;
31
+ generatedIds.add(targetId);
32
+ }
33
+ usedIds.add(targetId);
34
+ result.push({
35
+ value: targetId,
36
+ label,
37
+ targetId
38
+ });
39
+ return result;
40
+ }, []);
41
+ setItems((current) => current.length === next.length && current.every((item, index) => item.value === next[index]?.value && item.label === next[index]?.label) ? current : next);
42
+ };
43
+ discover();
44
+ const observer = new MutationObserver(discover);
45
+ observer.observe(root, {
46
+ childList: true,
47
+ subtree: true,
48
+ characterData: true
49
+ });
50
+ return () => {
51
+ observer.disconnect();
52
+ for (const id of generatedIds) {
53
+ const heading = document.getElementById(id);
54
+ if (heading && root.contains(heading)) heading.removeAttribute("id");
55
+ }
56
+ };
57
+ }, [idPrefix, selector]);
58
+ const value = React.useMemo(() => ({
59
+ items,
60
+ scrollOffset
61
+ }), [items, scrollOffset]);
62
+ return /* @__PURE__ */ jsx(SectionJumperProviderContext.Provider, {
63
+ value,
64
+ children: /* @__PURE__ */ jsx("div", {
65
+ ref: rootRef,
66
+ "data-slot": "section-jumper-provider",
67
+ ...props,
68
+ children
69
+ })
70
+ });
71
+ }
72
+ function getSectionJumperScrollport(element) {
73
+ let parent = element.parentElement;
74
+ while (parent && parent !== document.documentElement) {
75
+ const { overflowY } = getComputedStyle(parent);
76
+ if (overflowY === "auto" || overflowY === "scroll") return parent;
77
+ parent = parent.parentElement;
78
+ }
79
+ return null;
80
+ }
81
+ function useStandaloneSectionJumper(items, scrollOffset, enabled) {
82
+ const reducedMotion = useReducedMotion();
83
+ const [activeValue, setActiveValue] = React.useState(items[0]?.value);
84
+ const itemsRef = React.useRef(items);
85
+ itemsRef.current = items;
86
+ const targetKey = items.map((item) => `${item.value}:${item.targetId ?? item.value}`).join("|");
87
+ React.useEffect(() => {
88
+ if (!enabled) return;
89
+ let frame = 0;
90
+ const measure = () => {
91
+ frame = 0;
92
+ const targets = itemsRef.current.map((item) => ({
93
+ value: item.value,
94
+ element: document.getElementById(item.targetId ?? item.value)
95
+ })).filter((target) => Boolean(target.element));
96
+ if (targets.length === 0) return;
97
+ const scrollport = getSectionJumperScrollport(targets[0].element);
98
+ const originTop = scrollport?.getBoundingClientRect().top ?? 0;
99
+ let next = targets[0]?.value;
100
+ for (const target of targets) if (target.element.getBoundingClientRect().top - originTop <= scrollOffset + 2) next = target.value;
101
+ const root = scrollport ?? document.scrollingElement;
102
+ if (root && root.scrollTop + root.clientHeight >= root.scrollHeight - 2) next = targets.at(-1)?.value ?? next;
103
+ setActiveValue(next);
104
+ };
105
+ const scheduleMeasure = () => {
106
+ if (!frame) frame = requestAnimationFrame(measure);
107
+ };
108
+ const firstTarget = itemsRef.current.map((item) => document.getElementById(item.targetId ?? item.value)).find((element) => Boolean(element));
109
+ const scrollTarget = (firstTarget ? getSectionJumperScrollport(firstTarget) : null) ?? window;
110
+ measure();
111
+ scrollTarget.addEventListener("scroll", scheduleMeasure, { passive: true });
112
+ window.addEventListener("resize", scheduleMeasure);
113
+ return () => {
114
+ if (frame) cancelAnimationFrame(frame);
115
+ scrollTarget.removeEventListener("scroll", scheduleMeasure);
116
+ window.removeEventListener("resize", scheduleMeasure);
117
+ };
118
+ }, [
119
+ enabled,
120
+ scrollOffset,
121
+ targetKey
122
+ ]);
123
+ const getTargetId = React.useCallback((value) => {
124
+ return itemsRef.current.find((candidate) => candidate.value === value)?.targetId ?? value;
125
+ }, []);
126
+ const scrollToSection = React.useCallback((value) => {
127
+ const element = document.getElementById(getTargetId(value));
128
+ if (!element) return;
129
+ setActiveValue(value);
130
+ const scrollport = getSectionJumperScrollport(element);
131
+ const behavior = reducedMotion ? "auto" : "smooth";
132
+ if (scrollport) scrollport.scrollTo({
133
+ top: element.getBoundingClientRect().top - scrollport.getBoundingClientRect().top + scrollport.scrollTop - scrollOffset,
134
+ behavior
135
+ });
136
+ else window.scrollTo({
137
+ top: element.getBoundingClientRect().top + window.scrollY - scrollOffset,
138
+ behavior
139
+ });
140
+ }, [
141
+ getTargetId,
142
+ reducedMotion,
143
+ scrollOffset
144
+ ]);
145
+ return React.useMemo(() => ({
146
+ activeValue,
147
+ getSectionId: getTargetId,
148
+ getTriggerId: (value) => `section-jumper-${value}`,
149
+ scrollToSection
150
+ }), [
151
+ activeValue,
152
+ getTargetId,
153
+ scrollToSection
154
+ ]);
155
+ }
156
+ function SectionJumperDot({ active, variant }) {
157
+ return /* @__PURE__ */ jsx("span", {
158
+ "aria-hidden": "true",
159
+ className: cn("block shrink-0 bg-line transition-[width,height,background-color] duration-(--duration-motion-surface) ease-gdt-out motion-reduce:transition-none", !active && "size-2 rounded-full", active && variant === "desktop" && "h-6 w-2 rounded-sm bg-surface-brand", active && variant === "menu" && "size-2 rounded-full bg-surface-brand", active && variant === "mobile" && "h-2 w-6 rounded-sm bg-surface-brand")
160
+ });
161
+ }
162
+ function SectionJumperMenu({ items, onSelect, controller }) {
163
+ const { activeValue, getSectionId, getTriggerId } = controller;
164
+ return /* @__PURE__ */ jsx("nav", {
165
+ "aria-label": "Page sections",
166
+ className: "flex flex-col gap-0.5",
167
+ children: items.map((item) => {
168
+ const active = item.value === activeValue;
169
+ return /* @__PURE__ */ jsxs("button", {
170
+ id: `${getTriggerId(item.value)}-jumper`,
171
+ type: "button",
172
+ "aria-controls": getSectionId(item.value),
173
+ "aria-current": active ? "location" : void 0,
174
+ "data-active": active ? "" : void 0,
175
+ className: cn("flex w-full cursor-pointer items-center gap-2.5 rounded-lg px-2.5 py-2 text-left text-gdt-sm font-medium text-fg-secondary outline-none transition-colors hover:bg-surface-raised focus-visible:shadow-[0_0_0_2px_var(--color-line-focus)]", active && "bg-surface-brand-subtle font-semibold text-fg-brand"),
176
+ onClick: () => onSelect(item.value),
177
+ children: [/* @__PURE__ */ jsx(SectionJumperDot, {
178
+ active,
179
+ variant: "menu"
180
+ }), /* @__PURE__ */ jsx("span", {
181
+ className: "min-w-0 flex-1 truncate",
182
+ children: item.label
183
+ })]
184
+ }, item.value);
185
+ })
186
+ });
187
+ }
188
+ function SectionJumper({ className, items: itemsProp, label = "On this page", open, defaultOpen = false, onOpenChange, mobileBreakpoint = 1024, scrollOffset: scrollOffsetProp, ...props }) {
189
+ const provider = React.useContext(SectionJumperProviderContext);
190
+ const items = itemsProp ?? provider?.items ?? [];
191
+ const scrollOffset = scrollOffsetProp ?? provider?.scrollOffset ?? 0;
192
+ const scrollSpy = useOptionalScrollSpy();
193
+ const hasExplicitTargets = items.some((item) => item.targetId !== void 0);
194
+ const standalone = useStandaloneSectionJumper(items, scrollOffset, hasExplicitTargets || !scrollSpy);
195
+ const controller = hasExplicitTargets || !scrollSpy ? standalone : scrollSpy;
196
+ const { activeValue, scrollToSection } = controller;
197
+ const isMobile = useIsMobile(mobileBreakpoint);
198
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
199
+ const resolvedOpen = open ?? internalOpen;
200
+ const activeItem = items.find((item) => item.value === activeValue);
201
+ const setOpen = React.useCallback((next) => {
202
+ if (open === void 0) setInternalOpen(next);
203
+ onOpenChange?.(next);
204
+ }, [onOpenChange, open]);
205
+ const select = React.useCallback((value) => {
206
+ scrollToSection(value);
207
+ setOpen(false);
208
+ }, [scrollToSection, setOpen]);
209
+ if (items.length === 0) return null;
210
+ const desktopTrigger = /* @__PURE__ */ jsx("button", {
211
+ type: "button",
212
+ "aria-label": `${label}${activeItem ? `, current section: ${String(activeItem.label)}` : ""}`,
213
+ className: "flex w-fit cursor-pointer flex-col items-center gap-3.5 rounded-2xl border border-transparent p-2 outline-none focus-visible:shadow-[0_0_0_2px_var(--color-line-focus)]",
214
+ children: items.map((item) => {
215
+ return /* @__PURE__ */ jsx(SectionJumperDot, {
216
+ active: item.value === activeValue,
217
+ variant: "desktop"
218
+ }, item.value);
219
+ })
220
+ });
221
+ const mobileTrigger = /* @__PURE__ */ jsxs("button", {
222
+ type: "button",
223
+ className: "flex cursor-pointer items-center gap-3 rounded-full border border-line-subtle bg-surface-overlay px-4 py-1 text-gdt-xs font-medium text-fg-brand shadow-2 outline-none focus-visible:shadow-[0_0_0_2px_var(--color-line-focus)]",
224
+ children: [
225
+ /* @__PURE__ */ jsx("span", {
226
+ "aria-hidden": true,
227
+ className: "flex items-center gap-1",
228
+ children: items.map((item) => /* @__PURE__ */ jsx(SectionJumperDot, {
229
+ active: item.value === activeValue,
230
+ variant: "mobile"
231
+ }, item.value))
232
+ }),
233
+ /* @__PURE__ */ jsx("span", {
234
+ className: "max-w-32 truncate",
235
+ children: activeItem?.label ?? label
236
+ }),
237
+ /* @__PURE__ */ jsx(ChevronDown, {
238
+ "aria-hidden": true,
239
+ className: cn("size-3 transition-transform", resolvedOpen && "rotate-180")
240
+ })
241
+ ]
242
+ });
243
+ return /* @__PURE__ */ jsx("div", {
244
+ "data-slot": "section-jumper",
245
+ className: cn("w-fit", className),
246
+ ...props,
247
+ children: /* @__PURE__ */ jsxs(Popover, {
248
+ open: resolvedOpen,
249
+ onOpenChange: setOpen,
250
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
251
+ render: isMobile ? mobileTrigger : desktopTrigger,
252
+ openOnHover: !isMobile,
253
+ closeDelay: 100
254
+ }), /* @__PURE__ */ jsxs(PopoverContent, {
255
+ align: isMobile ? "center" : "end",
256
+ side: isMobile ? "top" : "left",
257
+ sideOffset: 8,
258
+ showArrow: false,
259
+ className: cn("border border-line-subtle shadow-2 px-4 pb-4 pt-3", isMobile ? "w-90 rounded-2xl" : "w-60 rounded-2xl"),
260
+ children: [/* @__PURE__ */ jsx("p", {
261
+ className: cn("border-b border-line-subtle pb-2 font-semibold text-fg-primary", isMobile ? "text-gdt-md" : "text-gdt-sm"),
262
+ children: label
263
+ }), /* @__PURE__ */ jsx("div", {
264
+ className: "pt-2",
265
+ children: /* @__PURE__ */ jsx(SectionJumperMenu, {
266
+ items,
267
+ onSelect: select,
268
+ controller
269
+ })
270
+ })]
271
+ })]
272
+ })
273
+ });
274
+ }
275
+ //#endregion
276
+ export { SectionJumper, SectionJumperProvider };
package/dist/select.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { t as ComponentSize } from "./size-context-BX6kAdVj.js";
1
+ import { n as ComponentSizeValue } from "./size-context-BVhHduLi.js";
2
2
  import * as React from "react";
3
3
  import { Select as Select$1 } from "@base-ui/react/select";
4
4
  //#region src/select.d.ts
5
- type SelectSize = ComponentSize;
5
+ type SelectSize = ComponentSizeValue;
6
6
  declare function Select<Value, Multiple extends boolean | undefined = false>({ multiple, children, size, ...props }: Select$1.Root.Props<Value, Multiple> & {
7
7
  size?: SelectSize;
8
8
  }): React.JSX.Element;
@@ -11,11 +11,16 @@ declare function SelectValue({ className, ...props }: Select$1.Value.Props): Rea
11
11
  declare function SelectTrigger({ className, children, unstyled, ...props }: Select$1.Trigger.Props & {
12
12
  unstyled?: boolean;
13
13
  }): React.JSX.Element;
14
- declare function SelectContent({ className, children, side, sideOffset, align, alignOffset, alignItemWithTrigger, ...props }: Select$1.Popup.Props & Pick<Select$1.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger">): React.JSX.Element;
14
+ declare function SelectContent({ className, children, header, footer, side, sideOffset, align, alignOffset, alignItemWithTrigger, ...props }: Select$1.Popup.Props & Pick<Select$1.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger"> & {
15
+ header?: React.ReactNode;
16
+ footer?: React.ReactNode;
17
+ }): React.JSX.Element;
18
+ declare function SelectHeader({ className, type, ...props }: React.ComponentProps<"button">): React.JSX.Element;
19
+ declare function SelectFooter({ className, type, ...props }: React.ComponentProps<"button">): React.JSX.Element;
15
20
  declare function SelectLabel({ className, ...props }: Select$1.GroupLabel.Props): React.JSX.Element;
16
21
  declare function SelectItem({ className, children, ...props }: Select$1.Item.Props): React.JSX.Element;
17
22
  declare function SelectSeparator({ className, ...props }: Select$1.Separator.Props): React.JSX.Element;
18
23
  declare function SelectScrollUpButton({ className, ...props }: React.ComponentProps<typeof Select$1.ScrollUpArrow>): React.JSX.Element;
19
24
  declare function SelectScrollDownButton({ className, ...props }: React.ComponentProps<typeof Select$1.ScrollDownArrow>): React.JSX.Element;
20
25
  //#endregion
21
- export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, type SelectSize, SelectTrigger, SelectValue };
26
+ export { Select, SelectContent, SelectFooter, SelectGroup, SelectHeader, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, type SelectSize, SelectTrigger, SelectValue };