dune-react 0.0.15 → 0.0.18

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 (34) hide show
  1. package/dist/components/puck-base/button.js +1 -1
  2. package/dist/components/puck-base/core/fields.d.ts +33 -10
  3. package/dist/components/puck-base/core/fields.js +27 -3
  4. package/dist/components/puck-base/core/styles.d.ts +1 -5
  5. package/dist/components/puck-base/core/styles.js +1 -5
  6. package/dist/components/puck-base/core/with-editable.js +33 -21
  7. package/dist/components/puck-base/editor-context.d.ts +2 -0
  8. package/dist/components/puck-base/fields/action-field.js +19 -43
  9. package/dist/components/puck-base/fields/auto-field.js +27 -214
  10. package/dist/components/puck-base/fields/color-field.d.ts +6 -0
  11. package/dist/components/puck-base/fields/color-field.js +37 -0
  12. package/dist/components/puck-base/fields/index.d.ts +8 -0
  13. package/dist/components/puck-base/fields/location-field.d.ts +44 -0
  14. package/dist/components/puck-base/fields/location-field.js +207 -0
  15. package/dist/components/puck-base/fields/object-field.d.ts +8 -0
  16. package/dist/components/puck-base/fields/object-field.js +30 -0
  17. package/dist/components/puck-base/fields/radio-toggle-field.d.ts +10 -0
  18. package/dist/components/puck-base/fields/radio-toggle-field.js +53 -0
  19. package/dist/components/puck-base/fields/virtualized-select-field.d.ts +13 -0
  20. package/dist/components/puck-base/fields/virtualized-select-field.js +146 -0
  21. package/dist/components/puck-base/image.js +175 -104
  22. package/dist/components/puck-base/index.d.ts +2 -3
  23. package/dist/components/puck-block/location-sections/location-1/index.js +17 -22
  24. package/dist/components/puck-block/location-sections/location-1/location.d.ts +5 -7
  25. package/dist/components/puck-block/location-sections/location-1/location.js +15 -12
  26. package/dist/components/puck-block/location-sections/location-2/index.js +28 -24
  27. package/dist/components/puck-block/location-sections/location-2/location.d.ts +6 -8
  28. package/dist/components/puck-block/location-sections/location-2/location.js +18 -15
  29. package/dist/components/puck-block/location-sections/location-3/index.js +43 -20
  30. package/dist/components/puck-block/location-sections/location-3/location.d.ts +5 -6
  31. package/dist/components/puck-block/location-sections/location-3/location.js +96 -86
  32. package/dist/components/puck-block/location-sections/props.d.ts +9 -10
  33. package/dist/components/shadcn/slider.js +4 -1
  34. package/package.json +4 -2
@@ -0,0 +1,37 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { memo } from "react";
3
+ import { Input } from "../../shadcn/input.js";
4
+ import { Label } from "../../shadcn/label.js";
5
+ const ColorField = memo(function ColorField2({
6
+ label,
7
+ value,
8
+ onChange
9
+ }) {
10
+ const colorValue = String(value ?? "#000000");
11
+ return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
12
+ /* @__PURE__ */ jsx(Label, { children: label }),
13
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
14
+ /* @__PURE__ */ jsx(
15
+ "input",
16
+ {
17
+ type: "color",
18
+ value: colorValue.startsWith("#") ? colorValue : "#000000",
19
+ onChange: (e) => onChange(e.target.value),
20
+ className: "h-9 w-9 shrink-0 cursor-pointer rounded-md border p-0.5"
21
+ }
22
+ ),
23
+ /* @__PURE__ */ jsx(
24
+ Input,
25
+ {
26
+ value: colorValue,
27
+ onChange: (e) => onChange(e.target.value),
28
+ placeholder: "#000000",
29
+ className: "flex-1"
30
+ }
31
+ )
32
+ ] })
33
+ ] });
34
+ });
35
+ export {
36
+ ColorField
37
+ };
@@ -0,0 +1,8 @@
1
+ export type { FieldDef, AutoFieldProps, FieldsPanelProps } from "./types";
2
+ export { AutoField } from "./auto-field";
3
+ export { ColorField } from "./color-field";
4
+ export { RadioToggleField } from "./radio-toggle-field";
5
+ export { ObjectField } from "./object-field";
6
+ export { VirtualizedSelectField, LARGE_SELECT_THRESHOLD, } from "./virtualized-select-field";
7
+ export { LocationField } from "./location-field";
8
+ export { ActionField, ACTION_TYPE_FIELD_MAP, ACTION_TYPE_OPTIONS, PageActionFields, ExternalActionFields, EmailActionFields, PhoneActionFields, SectionActionFields, DownloadActionFields, type ActionTypeFieldProps, } from "./action-field";
@@ -0,0 +1,44 @@
1
+ import type { MapLocation } from "../core/fields";
2
+ interface GLatLng {
3
+ lat(): number;
4
+ lng(): number;
5
+ }
6
+ interface GGeometry {
7
+ location?: GLatLng;
8
+ }
9
+ interface GPlaceResult {
10
+ formatted_address?: string;
11
+ geometry?: GGeometry;
12
+ place_id?: string;
13
+ address_components?: unknown[];
14
+ }
15
+ interface GAutocomplete {
16
+ getPlace(): GPlaceResult;
17
+ addListener(event: string, handler: () => void): void;
18
+ }
19
+ interface GAutocompleteConstructor {
20
+ new (input: HTMLInputElement, opts?: Record<string, unknown>): GAutocomplete;
21
+ }
22
+ interface GGoogleMaps {
23
+ places: {
24
+ Autocomplete: GAutocompleteConstructor;
25
+ };
26
+ event: {
27
+ clearInstanceListeners(instance: unknown): void;
28
+ };
29
+ }
30
+ interface GGoogle {
31
+ maps: GGoogleMaps;
32
+ }
33
+ declare global {
34
+ interface Window {
35
+ google?: GGoogle;
36
+ }
37
+ }
38
+ export interface LocationFieldProps {
39
+ name: string;
40
+ value: MapLocation | undefined;
41
+ onChange: (value: MapLocation) => void;
42
+ }
43
+ export declare const LocationField: import("react").NamedExoticComponent<LocationFieldProps>;
44
+ export {};
@@ -0,0 +1,207 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import { memo, useState, useRef, useEffect, useCallback } from "react";
4
+ import { Input } from "../../shadcn/input.js";
5
+ import { Label } from "../../shadcn/label.js";
6
+ import { Loader2, Search, X, MapPin } from "lucide-react";
7
+ import { useEditorContext } from "../editor-context.js";
8
+ let googleLoadPromise = null;
9
+ function loadGooglePlaces(apiKey) {
10
+ var _a, _b;
11
+ if (googleLoadPromise) return googleLoadPromise;
12
+ if (typeof window !== "undefined" && ((_b = (_a = window.google) == null ? void 0 : _a.maps) == null ? void 0 : _b.places)) {
13
+ return Promise.resolve();
14
+ }
15
+ googleLoadPromise = new Promise((resolve, reject) => {
16
+ const script = document.createElement("script");
17
+ script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`;
18
+ script.async = true;
19
+ script.onload = () => resolve();
20
+ script.onerror = () => {
21
+ googleLoadPromise = null;
22
+ reject(new Error("Failed to load Google Maps API"));
23
+ };
24
+ document.head.appendChild(script);
25
+ });
26
+ return googleLoadPromise;
27
+ }
28
+ const LocationField = memo(function LocationField2({
29
+ value,
30
+ onChange
31
+ }) {
32
+ const { googleMapsApiKey } = useEditorContext();
33
+ const apiKey = googleMapsApiKey || "";
34
+ const [inputValue, setInputValue] = useState((value == null ? void 0 : value.address) || "");
35
+ const [isLoaded, setIsLoaded] = useState(false);
36
+ const [loadError, setLoadError] = useState(false);
37
+ const inputRef = useRef(null);
38
+ const autocompleteRef = useRef(null);
39
+ useEffect(() => {
40
+ if ((value == null ? void 0 : value.address) && value.address !== inputValue) {
41
+ setInputValue(value.address);
42
+ }
43
+ }, [value == null ? void 0 : value.address]);
44
+ useEffect(() => {
45
+ if (!apiKey) {
46
+ setLoadError(true);
47
+ return;
48
+ }
49
+ loadGooglePlaces(apiKey).then(() => setIsLoaded(true)).catch(() => setLoadError(true));
50
+ }, [apiKey]);
51
+ useEffect(() => {
52
+ if (!isLoaded || !inputRef.current || autocompleteRef.current) return;
53
+ const G = window.google;
54
+ const autocomplete = new G.maps.places.Autocomplete(inputRef.current, {
55
+ fields: [
56
+ "formatted_address",
57
+ "geometry",
58
+ "place_id",
59
+ "address_components"
60
+ ]
61
+ });
62
+ autocomplete.addListener("place_changed", () => {
63
+ var _a;
64
+ const place = autocomplete.getPlace();
65
+ if (!((_a = place.geometry) == null ? void 0 : _a.location)) return;
66
+ const lat = place.geometry.location.lat();
67
+ const lng = place.geometry.location.lng();
68
+ const address = place.formatted_address || "";
69
+ const newLocation = {
70
+ address,
71
+ lat,
72
+ lng,
73
+ zoom: (value == null ? void 0 : value.zoom) ?? 14,
74
+ placeId: place.place_id || ""
75
+ };
76
+ setInputValue(address);
77
+ onChange(newLocation);
78
+ });
79
+ autocompleteRef.current = autocomplete;
80
+ return () => {
81
+ var _a;
82
+ (_a = window.google) == null ? void 0 : _a.maps.event.clearInstanceListeners(autocomplete);
83
+ autocompleteRef.current = null;
84
+ };
85
+ }, [isLoaded]);
86
+ const handleClear = useCallback(() => {
87
+ var _a;
88
+ setInputValue("");
89
+ onChange({
90
+ address: "",
91
+ lat: 0,
92
+ lng: 0,
93
+ zoom: 14,
94
+ placeId: ""
95
+ });
96
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
97
+ }, [onChange]);
98
+ const handleZoomChange = useCallback(
99
+ (e) => {
100
+ if (!value) return;
101
+ onChange({ ...value, zoom: Number(e.target.value) });
102
+ },
103
+ [value, onChange]
104
+ );
105
+ const embedUrl = value && value.lat !== 0 ? `https://maps.google.com/maps?q=${value.lat},${value.lng}&z=${value.zoom ?? 14}&output=embed` : null;
106
+ return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-3", children: [
107
+ /* @__PURE__ */ jsx(Label, { children: "Location" }),
108
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
109
+ /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3", children: !isLoaded && !loadError ? /* @__PURE__ */ jsx(Loader2, { className: "text-muted-foreground size-4 animate-spin" }) : /* @__PURE__ */ jsx(Search, { className: "text-muted-foreground size-4" }) }),
110
+ /* @__PURE__ */ jsx(
111
+ Input,
112
+ {
113
+ ref: inputRef,
114
+ value: inputValue,
115
+ onChange: (e) => setInputValue(e.target.value),
116
+ placeholder: loadError ? "Enter address manually..." : "Search address or place...",
117
+ className: "pl-9 pr-8"
118
+ }
119
+ ),
120
+ inputValue && /* @__PURE__ */ jsx(
121
+ "button",
122
+ {
123
+ type: "button",
124
+ onClick: handleClear,
125
+ className: "text-muted-foreground hover:text-foreground absolute inset-y-0 right-0 flex items-center pr-3",
126
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
127
+ }
128
+ )
129
+ ] }),
130
+ loadError && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
131
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
132
+ /* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Latitude" }),
133
+ /* @__PURE__ */ jsx(
134
+ Input,
135
+ {
136
+ type: "number",
137
+ step: "any",
138
+ value: (value == null ? void 0 : value.lat) ?? "",
139
+ onChange: (e) => onChange({
140
+ ...value || { address: "", lng: 0, zoom: 14 },
141
+ lat: Number(e.target.value)
142
+ }),
143
+ placeholder: "37.7749",
144
+ className: "h-8 text-xs"
145
+ }
146
+ )
147
+ ] }),
148
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
149
+ /* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Longitude" }),
150
+ /* @__PURE__ */ jsx(
151
+ Input,
152
+ {
153
+ type: "number",
154
+ step: "any",
155
+ value: (value == null ? void 0 : value.lng) ?? "",
156
+ onChange: (e) => onChange({
157
+ ...value || { address: "", lat: 0, zoom: 14 },
158
+ lng: Number(e.target.value)
159
+ }),
160
+ placeholder: "-122.4194",
161
+ className: "h-8 text-xs"
162
+ }
163
+ )
164
+ ] })
165
+ ] }),
166
+ value && value.lat !== 0 && /* @__PURE__ */ jsxs("div", { className: "border-border bg-muted/50 space-y-2 rounded-lg border p-3", children: [
167
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
168
+ /* @__PURE__ */ jsx(MapPin, { className: "text-primary mt-0.5 size-3.5 shrink-0" }),
169
+ /* @__PURE__ */ jsx("span", { className: "text-foreground text-sm font-medium leading-tight", children: value.address })
170
+ ] }),
171
+ /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground pl-5.5 text-xs", children: [
172
+ value.lat.toFixed(4),
173
+ ", ",
174
+ value.lng.toFixed(4)
175
+ ] })
176
+ ] }),
177
+ embedUrl && /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsx(
178
+ "iframe",
179
+ {
180
+ src: embedUrl,
181
+ title: "Location preview",
182
+ className: "h-[160px] w-full border-0",
183
+ loading: "lazy",
184
+ referrerPolicy: "no-referrer-when-downgrade"
185
+ }
186
+ ) }),
187
+ value && value.lat !== 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
188
+ /* @__PURE__ */ jsx(Label, { className: "shrink-0 text-xs", children: "Zoom" }),
189
+ /* @__PURE__ */ jsx(
190
+ "input",
191
+ {
192
+ type: "range",
193
+ min: 1,
194
+ max: 20,
195
+ step: 1,
196
+ value: value.zoom ?? 14,
197
+ onChange: handleZoomChange,
198
+ className: "h-1.5 flex-1 cursor-pointer appearance-none rounded-full bg-gray-200 accent-current"
199
+ }
200
+ ),
201
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground w-6 text-right text-xs tabular-nums", children: value.zoom ?? 14 })
202
+ ] })
203
+ ] });
204
+ });
205
+ export {
206
+ LocationField
207
+ };
@@ -0,0 +1,8 @@
1
+ import type { FieldDef } from "./types";
2
+ /** 嵌套对象字段:按 objectFields 递归渲染子 AutoField,onChange 合并回对象 */
3
+ export declare const ObjectField: import("react").NamedExoticComponent<{
4
+ field: FieldDef;
5
+ name: string;
6
+ value: any;
7
+ onChange: (value: any) => void;
8
+ }>;
@@ -0,0 +1,30 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { memo } from "react";
3
+ import { Label } from "../../shadcn/label.js";
4
+ import { AutoField } from "./auto-field.js";
5
+ const ObjectField = memo(function ObjectField2({
6
+ field,
7
+ name,
8
+ value,
9
+ onChange
10
+ }) {
11
+ const objectFields = field.objectFields || {};
12
+ const label = field.label || name;
13
+ const objValue = value && typeof value === "object" ? value : {};
14
+ return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
15
+ /* @__PURE__ */ jsx(Label, { children: label }),
16
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border p-3", children: Object.entries(objectFields).map(([key, subField]) => /* @__PURE__ */ jsx(
17
+ AutoField,
18
+ {
19
+ field: subField,
20
+ name: key,
21
+ value: objValue[key],
22
+ onChange: (val) => onChange({ ...objValue, [key]: val })
23
+ },
24
+ key
25
+ )) })
26
+ ] });
27
+ });
28
+ export {
29
+ ObjectField
30
+ };
@@ -0,0 +1,10 @@
1
+ /** 单选/切换:若选项恰好为 true/false 两个值则渲染开关,否则为 ToggleGroup */
2
+ export declare const RadioToggleField: import("react").NamedExoticComponent<{
3
+ label: string;
4
+ options: {
5
+ label: string;
6
+ value: any;
7
+ }[];
8
+ value: any;
9
+ onChange: (value: any) => void;
10
+ }>;
@@ -0,0 +1,53 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { memo } from "react";
3
+ import { Label } from "../../shadcn/label.js";
4
+ import { Switch } from "../../shadcn/switch.js";
5
+ import { ToggleGroup, ToggleGroupItem } from "../../shadcn/toggle-group.js";
6
+ const RadioToggleField = memo(function RadioToggleField2({
7
+ label,
8
+ options,
9
+ value,
10
+ onChange
11
+ }) {
12
+ const isBooleanToggle = options.length === 2 && options.some((o) => o.value === true) && options.some((o) => o.value === false);
13
+ if (isBooleanToggle) {
14
+ return /* @__PURE__ */ jsxs("div", { className: "bg-muted/50 mb-3 flex items-center justify-between rounded-xl p-3", children: [
15
+ /* @__PURE__ */ jsx(Label, { children: label }),
16
+ /* @__PURE__ */ jsx(
17
+ Switch,
18
+ {
19
+ checked: !!value,
20
+ onCheckedChange: (checked) => onChange(checked)
21
+ }
22
+ )
23
+ ] });
24
+ }
25
+ return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
26
+ /* @__PURE__ */ jsx(Label, { children: label }),
27
+ /* @__PURE__ */ jsx(
28
+ ToggleGroup,
29
+ {
30
+ type: "single",
31
+ value: String(value ?? ""),
32
+ onValueChange: (val) => {
33
+ if (!val) return;
34
+ const opt = options.find((o) => String(o.value) === val);
35
+ if (opt) onChange(opt.value);
36
+ },
37
+ className: "justify-start",
38
+ children: options.map((opt) => /* @__PURE__ */ jsx(
39
+ ToggleGroupItem,
40
+ {
41
+ value: String(opt.value),
42
+ size: "sm",
43
+ children: opt.label
44
+ },
45
+ String(opt.value)
46
+ ))
47
+ }
48
+ )
49
+ ] });
50
+ });
51
+ export {
52
+ RadioToggleField
53
+ };
@@ -0,0 +1,13 @@
1
+ export declare const LARGE_SELECT_THRESHOLD = 200;
2
+ type SelectOption = {
3
+ label: string;
4
+ value: any;
5
+ };
6
+ /** 超大下拉:选项超过阈值时使用虚拟列表 + 搜索,减轻 DOM 压力 */
7
+ export declare const VirtualizedSelectField: import("react").NamedExoticComponent<{
8
+ label: string;
9
+ options: SelectOption[];
10
+ value: any;
11
+ onChange: (value: any) => void;
12
+ }>;
13
+ export {};
@@ -0,0 +1,146 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { memo, useState, useRef, useMemo, useCallback, useEffect } from "react";
3
+ import { Input } from "../../shadcn/input.js";
4
+ import { Label } from "../../shadcn/label.js";
5
+ import { Button } from "../../shadcn/button.js";
6
+ import { Popover, PopoverTrigger, PopoverContent } from "../../shadcn/popover.js";
7
+ import { ChevronDownIcon, CheckIcon } from "lucide-react";
8
+ const LARGE_SELECT_THRESHOLD = 200;
9
+ const VIRTUAL_ITEM_HEIGHT = 32;
10
+ const VIRTUAL_VIEWPORT_HEIGHT = 256;
11
+ const VIRTUAL_OVERSCAN = 6;
12
+ const VirtualizedSelectField = memo(function VirtualizedSelectField2({
13
+ label,
14
+ options,
15
+ value,
16
+ onChange
17
+ }) {
18
+ const [open, setOpen] = useState(false);
19
+ const [search, setSearch] = useState("");
20
+ const [scrollTop, setScrollTop] = useState(0);
21
+ const viewportRef = useRef(null);
22
+ const normalizedOptions = useMemo(
23
+ () => options.map((opt) => ({
24
+ ...opt,
25
+ stringValue: String(opt.value)
26
+ })),
27
+ [options]
28
+ );
29
+ const selectedString = String(value ?? "");
30
+ const filteredOptions = useMemo(() => {
31
+ const query = search.trim().toLowerCase();
32
+ if (!query) return normalizedOptions;
33
+ return normalizedOptions.filter(
34
+ (opt) => opt.label.toLowerCase().includes(query) || opt.stringValue.toLowerCase().includes(query)
35
+ );
36
+ }, [normalizedOptions, search]);
37
+ const selectedLabel = useMemo(() => {
38
+ const selected = normalizedOptions.find(
39
+ (opt) => opt.stringValue === selectedString
40
+ );
41
+ return (selected == null ? void 0 : selected.label) ?? selectedString;
42
+ }, [normalizedOptions, selectedString]);
43
+ const totalHeight = filteredOptions.length * VIRTUAL_ITEM_HEIGHT;
44
+ const startIndex = Math.max(
45
+ 0,
46
+ Math.floor(scrollTop / VIRTUAL_ITEM_HEIGHT) - VIRTUAL_OVERSCAN
47
+ );
48
+ const visibleCount = Math.ceil(VIRTUAL_VIEWPORT_HEIGHT / VIRTUAL_ITEM_HEIGHT) + VIRTUAL_OVERSCAN * 2;
49
+ const endIndex = Math.min(filteredOptions.length, startIndex + visibleCount);
50
+ const visibleOptions = filteredOptions.slice(startIndex, endIndex);
51
+ const handleSelect = useCallback(
52
+ (stringValue) => {
53
+ const selected = normalizedOptions.find(
54
+ (opt) => opt.stringValue === stringValue
55
+ );
56
+ onChange(selected ? selected.value : stringValue);
57
+ setOpen(false);
58
+ },
59
+ [normalizedOptions, onChange]
60
+ );
61
+ useEffect(() => {
62
+ if (!open || !viewportRef.current) return;
63
+ const selectedIndex = filteredOptions.findIndex(
64
+ (opt) => opt.stringValue === selectedString
65
+ );
66
+ if (selectedIndex < 0) return;
67
+ const targetTop = selectedIndex * VIRTUAL_ITEM_HEIGHT - VIRTUAL_VIEWPORT_HEIGHT / 2;
68
+ const clampedTop = Math.max(
69
+ 0,
70
+ Math.min(targetTop, totalHeight - VIRTUAL_VIEWPORT_HEIGHT)
71
+ );
72
+ viewportRef.current.scrollTop = clampedTop;
73
+ setScrollTop(clampedTop);
74
+ }, [open, filteredOptions, selectedString, totalHeight]);
75
+ return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
76
+ /* @__PURE__ */ jsx(Label, { children: label }),
77
+ /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
78
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
79
+ Button,
80
+ {
81
+ type: "button",
82
+ variant: "outline",
83
+ className: "w-full justify-between font-normal",
84
+ children: [
85
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: selectedLabel || "Select..." }),
86
+ /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4 opacity-60" })
87
+ ]
88
+ }
89
+ ) }),
90
+ /* @__PURE__ */ jsxs(PopoverContent, { className: "w-(--radix-popover-trigger-width) p-2", children: [
91
+ /* @__PURE__ */ jsx(
92
+ Input,
93
+ {
94
+ value: search,
95
+ onChange: (e) => setSearch(e.target.value),
96
+ placeholder: "Search options...",
97
+ className: "mb-2"
98
+ }
99
+ ),
100
+ /* @__PURE__ */ jsx(
101
+ "div",
102
+ {
103
+ ref: viewportRef,
104
+ className: "relative touch-pan-y overflow-y-auto overscroll-contain rounded-md border",
105
+ style: {
106
+ height: VIRTUAL_VIEWPORT_HEIGHT,
107
+ WebkitOverflowScrolling: "touch"
108
+ },
109
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
110
+ children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-muted-foreground p-3 text-sm", children: "No options found." }) : /* @__PURE__ */ jsx("div", { style: { height: totalHeight, position: "relative" }, children: /* @__PURE__ */ jsx(
111
+ "div",
112
+ {
113
+ style: {
114
+ position: "absolute",
115
+ top: startIndex * VIRTUAL_ITEM_HEIGHT,
116
+ left: 0,
117
+ right: 0
118
+ },
119
+ children: visibleOptions.map((opt) => {
120
+ const isSelected = opt.stringValue === selectedString;
121
+ return /* @__PURE__ */ jsxs(
122
+ "button",
123
+ {
124
+ type: "button",
125
+ className: "hover:bg-accent hover:text-accent-foreground flex h-8 w-full items-center justify-between px-2 text-left text-sm",
126
+ onClick: () => handleSelect(opt.stringValue),
127
+ children: [
128
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: opt.label }),
129
+ isSelected ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-primary ml-2 size-4 shrink-0" }) : null
130
+ ]
131
+ },
132
+ opt.stringValue
133
+ );
134
+ })
135
+ }
136
+ ) })
137
+ }
138
+ )
139
+ ] })
140
+ ] })
141
+ ] });
142
+ });
143
+ export {
144
+ LARGE_SELECT_THRESHOLD,
145
+ VirtualizedSelectField
146
+ };