@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.
- package/css/components.css +13 -0
- package/css/generated/shared.css +1 -1
- package/dist/badge.d.ts +5 -1
- package/dist/badge.js +19 -5
- package/dist/bar-chart.d.ts +1 -1
- package/dist/bullet-chart.d.ts +1 -1
- package/dist/button-BS5KCibF.d.ts +33 -0
- package/dist/button-group.d.ts +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/button.js +49 -21
- package/dist/calendar.js +1 -1
- package/dist/carousel.d.ts +4 -2
- package/dist/carousel.js +11 -14
- package/dist/chart.d.ts +1 -1
- package/dist/chat.d.ts +1 -1
- package/dist/checkbox.d.ts +2 -2
- package/dist/checkbox.js +20 -6
- package/dist/{combobox-B7cKFJ9V.js → combobox-CACVdp1R.js} +41 -9
- package/dist/combobox.d.ts +8 -3
- package/dist/combobox.js +2 -2
- package/dist/cta.d.ts +1 -1
- package/dist/data-table.d.ts +2 -2
- package/dist/date-picker.d.ts +3 -3
- package/dist/date-picker.js +1 -1
- package/dist/dialog.d.ts +3 -1
- package/dist/dialog.js +9 -5
- package/dist/dropdown-menu.d.ts +1 -1
- package/dist/field.d.ts +4 -1
- package/dist/field.js +28 -11
- package/dist/footer.d.ts +2 -2
- package/dist/funnel-chart.d.ts +1 -1
- package/dist/heatmap-chart.d.ts +1 -1
- package/dist/input-group.d.ts +6 -6
- package/dist/input-group.js +38 -13
- package/dist/input-otp.d.ts +2 -2
- package/dist/input-otp.js +31 -14
- package/dist/input-size-context-BMhves-J.d.ts +13 -0
- package/dist/input-size-context.d.ts +2 -2
- package/dist/input-size-context.js +9 -2
- package/dist/input.d.ts +20 -2
- package/dist/input.js +30 -11
- package/dist/line-chart.d.ts +1 -1
- package/dist/mask-input.d.ts +1 -1
- package/dist/match.d.ts +5 -2
- package/dist/match.js +182 -26
- package/dist/media-gallery-hero.js +9 -7
- package/dist/mobile-sidebar.js +1 -0
- package/dist/number-input.d.ts +2 -2
- package/dist/number-input.js +2 -2
- package/dist/pagination.d.ts +1 -1
- package/dist/phone-input.d.ts +2 -2
- package/dist/phone-input.js +5 -4
- package/dist/picker-BTXBrsl4.js +62 -0
- package/dist/{picker-CsnHtbe4.d.ts → picker-j3txxA5t.d.ts} +2 -2
- package/dist/pie-chart.d.ts +1 -1
- package/dist/{popover-DSlPeH7x.d.ts → popover-CNymD33m.d.ts} +1 -1
- package/dist/popover.d.ts +1 -1
- package/dist/property-listing-card.d.ts +7 -3
- package/dist/property-listing-card.js +25 -8
- package/dist/radar-chart.d.ts +1 -1
- package/dist/radio-group.d.ts +2 -2
- package/dist/radio-group.js +23 -9
- package/dist/rich-text-editor.d.ts +1 -1
- package/dist/scatter-chart.d.ts +1 -1
- package/dist/scroll-spy.d.ts +8 -2
- package/dist/scroll-spy.js +13 -13
- package/dist/section-jumper.d.ts +31 -0
- package/dist/section-jumper.js +276 -0
- package/dist/select.d.ts +9 -4
- package/dist/select.js +112 -19
- package/dist/sheet.d.ts +3 -1
- package/dist/sheet.js +9 -5
- package/dist/sidebar.d.ts +2 -2
- package/dist/sidebar.js +1 -0
- package/dist/size-context-BVhHduLi.d.ts +33 -0
- package/dist/size-context.d.ts +2 -2
- package/dist/size-context.js +30 -1
- package/dist/slider.d.ts +5 -3
- package/dist/slider.js +25 -10
- package/dist/sonar.d.ts +1 -1
- package/dist/sonar.js +1 -1
- package/dist/sparkline.d.ts +1 -1
- package/dist/styles.css +1576 -258
- package/dist/switch.d.ts +2 -2
- package/dist/switch.js +20 -6
- package/dist/tabs.d.ts +1 -1
- package/dist/tag.d.ts +1 -1
- package/dist/textarea.d.ts +17 -1
- package/dist/textarea.js +23 -9
- package/dist/time-picker.d.ts +3 -3
- package/dist/time-picker.js +1 -1
- package/dist/tree-map.d.ts +1 -1
- package/dist/video-player-dialog.js +3 -2
- package/dist/waterfall-chart.d.ts +1 -1
- package/package.json +5 -1
- package/dist/button-DW5OijoR.d.ts +0 -17
- package/dist/input-CfoEJ8A6.d.ts +0 -12
- package/dist/input-size-context-C-SFx9sx.d.ts +0 -10
- package/dist/picker-DmIiQKLJ.js +0 -45
- package/dist/size-context-BX6kAdVj.d.ts +0 -11
- package/dist/textarea-IS_D8pXf.d.ts +0 -11
package/dist/radio-group.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "./utils/cn.js";
|
|
3
|
-
import {
|
|
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
|
|
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 =
|
|
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:
|
|
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 };
|
package/dist/scatter-chart.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ComponentSize } from "./size-context-
|
|
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";
|
package/dist/scroll-spy.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ComponentSize } from "./size-context-
|
|
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 };
|
package/dist/scroll-spy.js
CHANGED
|
@@ -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
|
|
54
|
-
|
|
55
|
-
|
|
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 {
|
|
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 =
|
|
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">
|
|
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 };
|