dune-react 0.0.14 → 0.0.16
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/dist/components/puck-base/button.js +1 -1
- package/dist/components/puck-base/core/fields.d.ts +10 -10
- package/dist/components/puck-base/core/fields.js +18 -3
- package/dist/components/puck-base/core/with-editable.js +33 -21
- package/dist/components/puck-base/fields/action-field.js +19 -43
- package/dist/components/puck-base/fields/auto-field.js +27 -214
- package/dist/components/puck-base/fields/color-field.d.ts +6 -0
- package/dist/components/puck-base/fields/color-field.js +37 -0
- package/dist/components/puck-base/fields/index.d.ts +7 -0
- package/dist/components/puck-base/fields/object-field.d.ts +8 -0
- package/dist/components/puck-base/fields/object-field.js +30 -0
- package/dist/components/puck-base/fields/radio-toggle-field.d.ts +10 -0
- package/dist/components/puck-base/fields/radio-toggle-field.js +53 -0
- package/dist/components/puck-base/fields/virtualized-select-field.d.ts +13 -0
- package/dist/components/puck-base/fields/virtualized-select-field.js +146 -0
- package/dist/components/puck-base/image.js +175 -104
- package/dist/components/puck-base/index.d.ts +2 -3
- package/dist/components/puck-block/text-sections/rich-text-1/rich-text.js +1 -0
- package/dist/components/shadcn/slider.js +4 -1
- package/package.json +4 -2
|
@@ -4,11 +4,11 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { SafeDynamicIcon } from "./safe-dynamic-icon.js";
|
|
5
5
|
import { Button, buttonVariants } from "../shadcn/button.js";
|
|
6
6
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "../shadcn/dialog.js";
|
|
7
|
-
import { AutoField } from "./fields/auto-field.js";
|
|
8
7
|
import { button } from "./core/fields.js";
|
|
9
8
|
import { cn } from "../../utils/css-utils.js";
|
|
10
9
|
import { resolveActionUrl } from "./core/types.js";
|
|
11
10
|
import { withEditable } from "./core/with-editable.js";
|
|
11
|
+
import { AutoField } from "./fields/auto-field.js";
|
|
12
12
|
const BUTTON_FIELDS = button.objectFields;
|
|
13
13
|
function ButtonEditDialog({
|
|
14
14
|
open,
|
|
@@ -13,9 +13,9 @@ export declare const action: {
|
|
|
13
13
|
render: typeof ActionField;
|
|
14
14
|
};
|
|
15
15
|
export declare const actionDefaults: {
|
|
16
|
-
readonly type: "
|
|
16
|
+
readonly type: "page";
|
|
17
17
|
readonly pageUrl: "";
|
|
18
|
-
readonly externalUrl: "
|
|
18
|
+
readonly externalUrl: "";
|
|
19
19
|
readonly openInNewTab: "false";
|
|
20
20
|
readonly email: "";
|
|
21
21
|
readonly subject: "";
|
|
@@ -139,9 +139,9 @@ export declare const buttons: {
|
|
|
139
139
|
readonly defaultItemProps: {
|
|
140
140
|
readonly label: "Button";
|
|
141
141
|
readonly action: {
|
|
142
|
-
readonly type: "
|
|
142
|
+
readonly type: "page";
|
|
143
143
|
readonly pageUrl: "";
|
|
144
|
-
readonly externalUrl: "
|
|
144
|
+
readonly externalUrl: "";
|
|
145
145
|
readonly openInNewTab: "false";
|
|
146
146
|
readonly email: "";
|
|
147
147
|
readonly subject: "";
|
|
@@ -354,9 +354,9 @@ export declare const contentFields: {
|
|
|
354
354
|
readonly defaultItemProps: {
|
|
355
355
|
readonly label: "Button";
|
|
356
356
|
readonly action: {
|
|
357
|
-
readonly type: "
|
|
357
|
+
readonly type: "page";
|
|
358
358
|
readonly pageUrl: "";
|
|
359
|
-
readonly externalUrl: "
|
|
359
|
+
readonly externalUrl: "";
|
|
360
360
|
readonly openInNewTab: "false";
|
|
361
361
|
readonly email: "";
|
|
362
362
|
readonly subject: "";
|
|
@@ -496,9 +496,9 @@ export declare const contentFieldsWithFeatures: {
|
|
|
496
496
|
readonly defaultItemProps: {
|
|
497
497
|
readonly label: "Button";
|
|
498
498
|
readonly action: {
|
|
499
|
-
readonly type: "
|
|
499
|
+
readonly type: "page";
|
|
500
500
|
readonly pageUrl: "";
|
|
501
|
-
readonly externalUrl: "
|
|
501
|
+
readonly externalUrl: "";
|
|
502
502
|
readonly openInNewTab: "false";
|
|
503
503
|
readonly email: "";
|
|
504
504
|
readonly subject: "";
|
|
@@ -668,9 +668,9 @@ export declare const cards: {
|
|
|
668
668
|
readonly button: {
|
|
669
669
|
readonly label: "Button";
|
|
670
670
|
readonly action: {
|
|
671
|
-
readonly type: "
|
|
671
|
+
readonly type: "page";
|
|
672
672
|
readonly pageUrl: "";
|
|
673
|
-
readonly externalUrl: "
|
|
673
|
+
readonly externalUrl: "";
|
|
674
674
|
readonly openInNewTab: "false";
|
|
675
675
|
readonly email: "";
|
|
676
676
|
readonly subject: "";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { fieldTypes } from "../field.js";
|
|
2
2
|
import { ActionField } from "../fields/action-field.js";
|
|
3
3
|
import { IconPickerField } from "../icon-picker-field.js";
|
|
4
|
+
import { backgroundColor, backgroundImage, bannerStylesDefaults, bannerStylesField, createStylesDefaults, createStylesField, heroStylesDefaults, padding, paddingDefaults, paddingLevel, sectionBaseStyleDefaults, sectionBaseStyleFields, sectionOverlay, sectionStyle } from "./styles.js";
|
|
4
5
|
const formMethods = ["get", "post", "put", "patch", "delete"];
|
|
5
6
|
const icon = {
|
|
6
7
|
type: "custom",
|
|
@@ -12,9 +13,9 @@ const action = {
|
|
|
12
13
|
render: ActionField
|
|
13
14
|
};
|
|
14
15
|
const actionDefaults = {
|
|
15
|
-
type: "
|
|
16
|
+
type: "page",
|
|
16
17
|
pageUrl: "",
|
|
17
|
-
externalUrl: "
|
|
18
|
+
externalUrl: "",
|
|
18
19
|
openInNewTab: "false",
|
|
19
20
|
email: "",
|
|
20
21
|
subject: "",
|
|
@@ -243,13 +244,19 @@ const formDefaults = {
|
|
|
243
244
|
export {
|
|
244
245
|
action,
|
|
245
246
|
actionDefaults,
|
|
247
|
+
backgroundColor,
|
|
248
|
+
backgroundImage,
|
|
246
249
|
badge,
|
|
250
|
+
bannerStylesDefaults,
|
|
251
|
+
bannerStylesField,
|
|
247
252
|
button,
|
|
248
253
|
buttons,
|
|
249
254
|
card,
|
|
250
255
|
cards,
|
|
251
256
|
contentFields,
|
|
252
257
|
contentFieldsWithFeatures,
|
|
258
|
+
createStylesDefaults,
|
|
259
|
+
createStylesField,
|
|
253
260
|
description,
|
|
254
261
|
features,
|
|
255
262
|
field,
|
|
@@ -258,10 +265,18 @@ export {
|
|
|
258
265
|
formDefaults,
|
|
259
266
|
getPlaceholderImageUrl,
|
|
260
267
|
heading,
|
|
268
|
+
heroStylesDefaults,
|
|
261
269
|
icon,
|
|
262
270
|
image,
|
|
263
271
|
image16x9Placeholder,
|
|
264
272
|
image1x1Placeholder,
|
|
265
273
|
image9x16Placeholder,
|
|
266
|
-
images
|
|
274
|
+
images,
|
|
275
|
+
padding,
|
|
276
|
+
paddingDefaults,
|
|
277
|
+
paddingLevel,
|
|
278
|
+
sectionBaseStyleDefaults,
|
|
279
|
+
sectionBaseStyleFields,
|
|
280
|
+
sectionOverlay,
|
|
281
|
+
sectionStyle
|
|
267
282
|
};
|
|
@@ -17,11 +17,13 @@ function withEditable(Component, config) {
|
|
|
17
17
|
const dispatch = usePuckDispatch();
|
|
18
18
|
const appState = usePuckAppState();
|
|
19
19
|
const wrapperRef = useRef(null);
|
|
20
|
+
const portalRef = useRef(null);
|
|
20
21
|
const isBlock = BLOCK_LEVEL_TYPES.has(config.type);
|
|
21
22
|
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const target = isBlock ? portalRef.current : wrapperRef.current;
|
|
24
|
+
if (!target) return;
|
|
25
|
+
return registerOverlayPortal(target, { disableDrag: true });
|
|
26
|
+
}, [isBlock]);
|
|
25
27
|
const getLocation = useCallback(() => {
|
|
26
28
|
const componentId = getComponentIdFromDOM(wrapperRef.current);
|
|
27
29
|
if (!componentId || !(appState == null ? void 0 : appState.data)) return null;
|
|
@@ -53,6 +55,8 @@ function withEditable(Component, config) {
|
|
|
53
55
|
},
|
|
54
56
|
[getLocation, dispatch]
|
|
55
57
|
);
|
|
58
|
+
const { previewScale = 1 } = useEditorContext();
|
|
59
|
+
const inverseScale = 1 / previewScale;
|
|
56
60
|
const handleEdit = useCallback(
|
|
57
61
|
(e) => {
|
|
58
62
|
e.preventDefault();
|
|
@@ -63,24 +67,32 @@ function withEditable(Component, config) {
|
|
|
63
67
|
},
|
|
64
68
|
[props, handleSave]
|
|
65
69
|
);
|
|
66
|
-
|
|
67
|
-
"span",
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
70
|
+
if (isBlock) {
|
|
71
|
+
return /* @__PURE__ */ jsxs("span", { ref: wrapperRef, className: "group relative block h-full w-full", children: [
|
|
72
|
+
/* @__PURE__ */ jsx(Component, { ...props }),
|
|
73
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-0 z-40 flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
74
|
+
"span",
|
|
75
|
+
{
|
|
76
|
+
ref: portalRef,
|
|
77
|
+
onClick: handleEdit,
|
|
78
|
+
style: { transform: `scale(${inverseScale})`, transformOrigin: "center center" },
|
|
79
|
+
className: "flex h-8 w-8 items-center justify-center rounded-full bg-black/50 shadow-md backdrop-blur-sm",
|
|
80
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4 text-white" })
|
|
81
|
+
}
|
|
82
|
+
) })
|
|
83
|
+
] });
|
|
84
|
+
}
|
|
85
|
+
return /* @__PURE__ */ jsxs("span", { ref: wrapperRef, className: "group relative inline-flex", children: [
|
|
86
|
+
/* @__PURE__ */ jsx(Component, { ...props }),
|
|
87
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-0 z-40 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100", children: /* @__PURE__ */ jsx(
|
|
88
|
+
"span",
|
|
89
|
+
{
|
|
90
|
+
onClick: handleEdit,
|
|
91
|
+
className: "flex h-8 w-8 items-center justify-center rounded-full bg-black/50 shadow-md backdrop-blur-sm",
|
|
92
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4 text-white" })
|
|
93
|
+
}
|
|
94
|
+
) })
|
|
95
|
+
] });
|
|
84
96
|
}
|
|
85
97
|
return function EditableComponent(props) {
|
|
86
98
|
const { isEditor: isEditorMode } = useEditorContext();
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { memo, useCallback } from "react";
|
|
4
4
|
import { Input } from "../../shadcn/input.js";
|
|
5
|
-
import { Button } from "../../shadcn/button.js";
|
|
6
5
|
import { Label } from "../../shadcn/label.js";
|
|
7
6
|
import { Tabs, TabsList, TabsTrigger } from "../../shadcn/tabs.js";
|
|
8
|
-
import { LinkIcon, XIcon,
|
|
7
|
+
import { LinkIcon, XIcon, GlobeIcon } from "lucide-react";
|
|
9
8
|
import { useEditorContext } from "../editor-context.js";
|
|
10
9
|
const ACTION_TYPE_OPTIONS = [
|
|
11
10
|
{ label: "Page Link", value: "page" },
|
|
@@ -25,53 +24,30 @@ const PageActionFields = memo(function PageActionFields2({
|
|
|
25
24
|
const pages = sitePages ?? [];
|
|
26
25
|
const pageUrl = value.pageUrl ?? "";
|
|
27
26
|
const selectedSlug = pageUrl.startsWith("/") ? pageUrl.slice(1) : null;
|
|
28
|
-
const handlePaste = useCallback(async () => {
|
|
29
|
-
try {
|
|
30
|
-
const text = await navigator.clipboard.readText();
|
|
31
|
-
if (text) onChange({ pageUrl: text.trim() });
|
|
32
|
-
} catch {
|
|
33
|
-
}
|
|
34
|
-
}, [onChange]);
|
|
35
27
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
36
28
|
/* @__PURE__ */ jsx(Label, { children: "Page URL" }),
|
|
37
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
38
|
-
/* @__PURE__ */
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"button",
|
|
52
|
-
{
|
|
53
|
-
type: "button",
|
|
54
|
-
className: "text-muted-foreground hover:text-foreground shrink-0",
|
|
55
|
-
onClick: () => onChange({ pageUrl: "" }),
|
|
56
|
-
children: /* @__PURE__ */ jsx(XIcon, { className: "size-3.5" })
|
|
57
|
-
}
|
|
58
|
-
)
|
|
59
|
-
] }),
|
|
60
|
-
/* @__PURE__ */ jsxs(
|
|
61
|
-
Button,
|
|
29
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-2 rounded-lg border px-3 py-2", children: [
|
|
30
|
+
/* @__PURE__ */ jsx(LinkIcon, { className: "text-muted-foreground size-4 shrink-0" }),
|
|
31
|
+
/* @__PURE__ */ jsx(
|
|
32
|
+
"input",
|
|
33
|
+
{
|
|
34
|
+
name: `${name}.pageUrl`,
|
|
35
|
+
className: "w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground",
|
|
36
|
+
placeholder: "/about",
|
|
37
|
+
value: pageUrl,
|
|
38
|
+
onChange: (e) => onChange({ pageUrl: e.target.value })
|
|
39
|
+
}
|
|
40
|
+
),
|
|
41
|
+
pageUrl && /* @__PURE__ */ jsx(
|
|
42
|
+
"button",
|
|
62
43
|
{
|
|
63
44
|
type: "button",
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
className: "
|
|
67
|
-
onClick: handlePaste,
|
|
68
|
-
children: [
|
|
69
|
-
/* @__PURE__ */ jsx(ClipboardIcon, { className: "size-3.5" }),
|
|
70
|
-
"Paste"
|
|
71
|
-
]
|
|
45
|
+
className: "text-muted-foreground hover:text-foreground shrink-0",
|
|
46
|
+
onClick: () => onChange({ pageUrl: "" }),
|
|
47
|
+
children: /* @__PURE__ */ jsx(XIcon, { className: "size-3.5" })
|
|
72
48
|
}
|
|
73
49
|
)
|
|
74
|
-
] }),
|
|
50
|
+
] }) }),
|
|
75
51
|
pages.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
76
52
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Site pages" }),
|
|
77
53
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: pages.map((page) => {
|
|
@@ -1,225 +1,33 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { memo
|
|
2
|
+
import { memo } from "react";
|
|
3
3
|
import { Input } from "../../shadcn/input.js";
|
|
4
4
|
import { Textarea } from "../../shadcn/textarea.js";
|
|
5
5
|
import { Label } from "../../shadcn/label.js";
|
|
6
|
-
import { Switch } from "../../shadcn/switch.js";
|
|
7
|
-
import { Button } from "../../shadcn/button.js";
|
|
8
6
|
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "../../shadcn/select.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}) {
|
|
22
|
-
const isBooleanToggle = options.length === 2 && options.some((o) => o.value === true) && options.some((o) => o.value === false);
|
|
23
|
-
if (isBooleanToggle) {
|
|
24
|
-
return /* @__PURE__ */ jsxs("div", { className: "bg-muted/50 mb-3 flex items-center justify-between rounded-xl p-3", children: [
|
|
25
|
-
/* @__PURE__ */ jsx(Label, { children: label }),
|
|
26
|
-
/* @__PURE__ */ jsx(
|
|
27
|
-
Switch,
|
|
28
|
-
{
|
|
29
|
-
checked: !!value,
|
|
30
|
-
onCheckedChange: (checked) => onChange(checked)
|
|
31
|
-
}
|
|
32
|
-
)
|
|
33
|
-
] });
|
|
34
|
-
}
|
|
35
|
-
return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
|
|
36
|
-
/* @__PURE__ */ jsx(Label, { children: label }),
|
|
37
|
-
/* @__PURE__ */ jsx(
|
|
38
|
-
ToggleGroup,
|
|
39
|
-
{
|
|
40
|
-
type: "single",
|
|
41
|
-
value: String(value ?? ""),
|
|
42
|
-
onValueChange: (val) => {
|
|
43
|
-
if (!val) return;
|
|
44
|
-
const opt = options.find((o) => String(o.value) === val);
|
|
45
|
-
if (opt) onChange(opt.value);
|
|
46
|
-
},
|
|
47
|
-
className: "justify-start",
|
|
48
|
-
children: options.map((opt) => /* @__PURE__ */ jsx(
|
|
49
|
-
ToggleGroupItem,
|
|
50
|
-
{
|
|
51
|
-
value: String(opt.value),
|
|
52
|
-
size: "sm",
|
|
53
|
-
children: opt.label
|
|
54
|
-
},
|
|
55
|
-
String(opt.value)
|
|
56
|
-
))
|
|
57
|
-
}
|
|
58
|
-
)
|
|
59
|
-
] });
|
|
60
|
-
});
|
|
61
|
-
const ObjectField = memo(function ObjectField2({
|
|
62
|
-
field,
|
|
63
|
-
name,
|
|
64
|
-
value,
|
|
65
|
-
onChange
|
|
66
|
-
}) {
|
|
67
|
-
const objectFields = field.objectFields || {};
|
|
68
|
-
const label = field.label || name;
|
|
69
|
-
const objValue = value && typeof value === "object" ? value : {};
|
|
70
|
-
return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
|
|
71
|
-
/* @__PURE__ */ jsx(Label, { children: label }),
|
|
72
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-lg border p-3", children: Object.entries(objectFields).map(([key, subField]) => /* @__PURE__ */ jsx(
|
|
73
|
-
AutoField,
|
|
74
|
-
{
|
|
75
|
-
field: subField,
|
|
76
|
-
name: key,
|
|
77
|
-
value: objValue[key],
|
|
78
|
-
onChange: (val) => onChange({ ...objValue, [key]: val })
|
|
79
|
-
},
|
|
80
|
-
key
|
|
81
|
-
)) })
|
|
82
|
-
] });
|
|
83
|
-
});
|
|
84
|
-
const VirtualizedSelectField = memo(function VirtualizedSelectField2({
|
|
85
|
-
label,
|
|
86
|
-
options,
|
|
87
|
-
value,
|
|
88
|
-
onChange
|
|
89
|
-
}) {
|
|
90
|
-
const [open, setOpen] = useState(false);
|
|
91
|
-
const [search, setSearch] = useState("");
|
|
92
|
-
const [scrollTop, setScrollTop] = useState(0);
|
|
93
|
-
const viewportRef = useRef(null);
|
|
94
|
-
const normalizedOptions = useMemo(
|
|
95
|
-
() => options.map((opt) => ({
|
|
96
|
-
...opt,
|
|
97
|
-
stringValue: String(opt.value)
|
|
98
|
-
})),
|
|
99
|
-
[options]
|
|
100
|
-
);
|
|
101
|
-
const selectedString = String(value ?? "");
|
|
102
|
-
const filteredOptions = useMemo(() => {
|
|
103
|
-
const query = search.trim().toLowerCase();
|
|
104
|
-
if (!query) return normalizedOptions;
|
|
105
|
-
return normalizedOptions.filter(
|
|
106
|
-
(opt) => opt.label.toLowerCase().includes(query) || opt.stringValue.toLowerCase().includes(query)
|
|
107
|
-
);
|
|
108
|
-
}, [normalizedOptions, search]);
|
|
109
|
-
const selectedLabel = useMemo(() => {
|
|
110
|
-
const selected = normalizedOptions.find(
|
|
111
|
-
(opt) => opt.stringValue === selectedString
|
|
112
|
-
);
|
|
113
|
-
return (selected == null ? void 0 : selected.label) ?? selectedString;
|
|
114
|
-
}, [normalizedOptions, selectedString]);
|
|
115
|
-
const totalHeight = filteredOptions.length * VIRTUAL_ITEM_HEIGHT;
|
|
116
|
-
const startIndex = Math.max(
|
|
117
|
-
0,
|
|
118
|
-
Math.floor(scrollTop / VIRTUAL_ITEM_HEIGHT) - VIRTUAL_OVERSCAN
|
|
119
|
-
);
|
|
120
|
-
const visibleCount = Math.ceil(VIRTUAL_VIEWPORT_HEIGHT / VIRTUAL_ITEM_HEIGHT) + VIRTUAL_OVERSCAN * 2;
|
|
121
|
-
const endIndex = Math.min(filteredOptions.length, startIndex + visibleCount);
|
|
122
|
-
const visibleOptions = filteredOptions.slice(startIndex, endIndex);
|
|
123
|
-
const handleSelect = useCallback(
|
|
124
|
-
(stringValue) => {
|
|
125
|
-
const selected = normalizedOptions.find(
|
|
126
|
-
(opt) => opt.stringValue === stringValue
|
|
127
|
-
);
|
|
128
|
-
onChange(selected ? selected.value : stringValue);
|
|
129
|
-
setOpen(false);
|
|
130
|
-
},
|
|
131
|
-
[normalizedOptions, onChange]
|
|
132
|
-
);
|
|
133
|
-
useEffect(() => {
|
|
134
|
-
if (!open || !viewportRef.current) return;
|
|
135
|
-
const selectedIndex = filteredOptions.findIndex(
|
|
136
|
-
(opt) => opt.stringValue === selectedString
|
|
137
|
-
);
|
|
138
|
-
if (selectedIndex < 0) return;
|
|
139
|
-
const targetTop = selectedIndex * VIRTUAL_ITEM_HEIGHT - VIRTUAL_VIEWPORT_HEIGHT / 2;
|
|
140
|
-
const clampedTop = Math.max(
|
|
141
|
-
0,
|
|
142
|
-
Math.min(targetTop, totalHeight - VIRTUAL_VIEWPORT_HEIGHT)
|
|
143
|
-
);
|
|
144
|
-
viewportRef.current.scrollTop = clampedTop;
|
|
145
|
-
setScrollTop(clampedTop);
|
|
146
|
-
}, [open, filteredOptions, selectedString, totalHeight]);
|
|
147
|
-
return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
|
|
148
|
-
/* @__PURE__ */ jsx(Label, { children: label }),
|
|
149
|
-
/* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
150
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
151
|
-
Button,
|
|
152
|
-
{
|
|
153
|
-
type: "button",
|
|
154
|
-
variant: "outline",
|
|
155
|
-
className: "w-full justify-between font-normal",
|
|
156
|
-
children: [
|
|
157
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: selectedLabel || "Select..." }),
|
|
158
|
-
/* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4 opacity-60" })
|
|
159
|
-
]
|
|
160
|
-
}
|
|
161
|
-
) }),
|
|
162
|
-
/* @__PURE__ */ jsxs(PopoverContent, { className: "w-(--radix-popover-trigger-width) p-2", children: [
|
|
163
|
-
/* @__PURE__ */ jsx(
|
|
164
|
-
Input,
|
|
165
|
-
{
|
|
166
|
-
value: search,
|
|
167
|
-
onChange: (e) => setSearch(e.target.value),
|
|
168
|
-
placeholder: "Search options...",
|
|
169
|
-
className: "mb-2"
|
|
170
|
-
}
|
|
171
|
-
),
|
|
172
|
-
/* @__PURE__ */ jsx(
|
|
173
|
-
"div",
|
|
174
|
-
{
|
|
175
|
-
ref: viewportRef,
|
|
176
|
-
className: "relative touch-pan-y overflow-y-auto overscroll-contain rounded-md border",
|
|
177
|
-
style: {
|
|
178
|
-
height: VIRTUAL_VIEWPORT_HEIGHT,
|
|
179
|
-
WebkitOverflowScrolling: "touch"
|
|
180
|
-
},
|
|
181
|
-
onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
|
|
182
|
-
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(
|
|
183
|
-
"div",
|
|
184
|
-
{
|
|
185
|
-
style: {
|
|
186
|
-
position: "absolute",
|
|
187
|
-
top: startIndex * VIRTUAL_ITEM_HEIGHT,
|
|
188
|
-
left: 0,
|
|
189
|
-
right: 0
|
|
190
|
-
},
|
|
191
|
-
children: visibleOptions.map((opt) => {
|
|
192
|
-
const isSelected = opt.stringValue === selectedString;
|
|
193
|
-
return /* @__PURE__ */ jsxs(
|
|
194
|
-
"button",
|
|
195
|
-
{
|
|
196
|
-
type: "button",
|
|
197
|
-
className: "hover:bg-accent hover:text-accent-foreground flex h-8 w-full items-center justify-between px-2 text-left text-sm",
|
|
198
|
-
onClick: () => handleSelect(opt.stringValue),
|
|
199
|
-
children: [
|
|
200
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: opt.label }),
|
|
201
|
-
isSelected ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-primary ml-2 size-4 shrink-0" }) : null
|
|
202
|
-
]
|
|
203
|
-
},
|
|
204
|
-
opt.stringValue
|
|
205
|
-
);
|
|
206
|
-
})
|
|
207
|
-
}
|
|
208
|
-
) })
|
|
209
|
-
}
|
|
210
|
-
)
|
|
211
|
-
] })
|
|
212
|
-
] })
|
|
213
|
-
] });
|
|
214
|
-
});
|
|
7
|
+
import { ColorField } from "./color-field.js";
|
|
8
|
+
import { RadioToggleField } from "./radio-toggle-field.js";
|
|
9
|
+
import { ObjectField } from "./object-field.js";
|
|
10
|
+
import { LARGE_SELECT_THRESHOLD, VirtualizedSelectField } from "./virtualized-select-field.js";
|
|
11
|
+
const KEY_FIELD_MAP = {
|
|
12
|
+
color: ColorField,
|
|
13
|
+
backgroundColor: ColorField,
|
|
14
|
+
"background-color": ColorField,
|
|
15
|
+
bgColor: ColorField,
|
|
16
|
+
borderColor: ColorField,
|
|
17
|
+
"border-color": ColorField
|
|
18
|
+
};
|
|
215
19
|
const AutoField = memo(function AutoField2({
|
|
216
20
|
field,
|
|
217
21
|
name,
|
|
218
22
|
value,
|
|
219
23
|
onChange
|
|
220
24
|
}) {
|
|
221
|
-
var _a, _b;
|
|
25
|
+
var _a, _b, _c;
|
|
222
26
|
const label = field.label || name;
|
|
27
|
+
const KeyOverride = KEY_FIELD_MAP[name];
|
|
28
|
+
if (KeyOverride) {
|
|
29
|
+
return /* @__PURE__ */ jsx(KeyOverride, { label, value, onChange });
|
|
30
|
+
}
|
|
223
31
|
switch (field.type) {
|
|
224
32
|
case "text":
|
|
225
33
|
return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
|
|
@@ -266,7 +74,7 @@ const AutoField = memo(function AutoField2({
|
|
|
266
74
|
onChange
|
|
267
75
|
}
|
|
268
76
|
);
|
|
269
|
-
case "select":
|
|
77
|
+
case "select": {
|
|
270
78
|
if ((((_a = field.options) == null ? void 0 : _a.length) || 0) > LARGE_SELECT_THRESHOLD) {
|
|
271
79
|
return /* @__PURE__ */ jsx(
|
|
272
80
|
VirtualizedSelectField,
|
|
@@ -278,24 +86,29 @@ const AutoField = memo(function AutoField2({
|
|
|
278
86
|
}
|
|
279
87
|
);
|
|
280
88
|
}
|
|
89
|
+
const stringValue = String(value ?? "");
|
|
90
|
+
const selectedOption = (_b = field.options) == null ? void 0 : _b.find(
|
|
91
|
+
(o) => String(o.value) === stringValue
|
|
92
|
+
);
|
|
281
93
|
return /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
|
|
282
94
|
/* @__PURE__ */ jsx(Label, { children: label }),
|
|
283
95
|
/* @__PURE__ */ jsxs(
|
|
284
96
|
Select,
|
|
285
97
|
{
|
|
286
|
-
value:
|
|
98
|
+
value: stringValue,
|
|
287
99
|
onValueChange: (val) => {
|
|
288
100
|
var _a2;
|
|
289
101
|
const opt = (_a2 = field.options) == null ? void 0 : _a2.find((o) => String(o.value) === val);
|
|
290
102
|
onChange(opt ? opt.value : val);
|
|
291
103
|
},
|
|
292
104
|
children: [
|
|
293
|
-
/* @__PURE__ */ jsx(SelectTrigger, { className: "w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
294
|
-
/* @__PURE__ */ jsx(SelectContent, { children: (
|
|
105
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "w-full", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: label, children: (selectedOption == null ? void 0 : selectedOption.label) ?? stringValue }) }),
|
|
106
|
+
/* @__PURE__ */ jsx(SelectContent, { children: (_c = field.options) == null ? void 0 : _c.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: String(opt.value), children: opt.label }, String(opt.value))) })
|
|
295
107
|
]
|
|
296
108
|
}
|
|
297
109
|
)
|
|
298
110
|
] });
|
|
111
|
+
}
|
|
299
112
|
case "object":
|
|
300
113
|
return /* @__PURE__ */ jsx(
|
|
301
114
|
ObjectField,
|
|
@@ -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,7 @@
|
|
|
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 { ActionField, ACTION_TYPE_FIELD_MAP, ACTION_TYPE_OPTIONS, PageActionFields, ExternalActionFields, EmailActionFields, PhoneActionFields, SectionActionFields, DownloadActionFields, type ActionTypeFieldProps, } from "./action-field";
|
|
@@ -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,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
|
+
};
|
|
@@ -33,7 +33,11 @@ const getCroppedImg = async (imageSrc, pixelCrop, rotation) => {
|
|
|
33
33
|
ctx.translate(safeArea / 2, safeArea / 2);
|
|
34
34
|
ctx.rotate(rotation * Math.PI / 180);
|
|
35
35
|
ctx.translate(-safeArea / 2, -safeArea / 2);
|
|
36
|
-
ctx.drawImage(
|
|
36
|
+
ctx.drawImage(
|
|
37
|
+
image2,
|
|
38
|
+
safeArea / 2 - image2.width * 0.5,
|
|
39
|
+
safeArea / 2 - image2.height * 0.5
|
|
40
|
+
);
|
|
37
41
|
const data = ctx.getImageData(0, 0, safeArea, safeArea);
|
|
38
42
|
canvas.width = pixelCrop.width;
|
|
39
43
|
canvas.height = pixelCrop.height;
|
|
@@ -48,7 +52,14 @@ const getCroppedImg = async (imageSrc, pixelCrop, rotation) => {
|
|
|
48
52
|
};
|
|
49
53
|
const DEFAULT_LIBRARY_IMAGES = [];
|
|
50
54
|
function CompoundImageBase(props) {
|
|
51
|
-
return /* @__PURE__ */ jsx(
|
|
55
|
+
return /* @__PURE__ */ jsx(
|
|
56
|
+
"img",
|
|
57
|
+
{
|
|
58
|
+
src: props.src,
|
|
59
|
+
alt: props.alt,
|
|
60
|
+
className: cn("block h-full w-full object-cover", props.className)
|
|
61
|
+
}
|
|
62
|
+
);
|
|
52
63
|
}
|
|
53
64
|
function ImageEditDialog({
|
|
54
65
|
open,
|
|
@@ -58,7 +69,9 @@ function ImageEditDialog({
|
|
|
58
69
|
}) {
|
|
59
70
|
const [tab, setTab] = useState("library");
|
|
60
71
|
const [alt, setAlt] = useState(initialData.alt ?? "");
|
|
61
|
-
const [libraryImages, setLibraryImages] = useState(
|
|
72
|
+
const [libraryImages, setLibraryImages] = useState(
|
|
73
|
+
DEFAULT_LIBRARY_IMAGES
|
|
74
|
+
);
|
|
62
75
|
const [isLoadingImages, setIsLoadingImages] = useState(false);
|
|
63
76
|
const { fetchLibraryImages } = useEditorContext();
|
|
64
77
|
useEffect(() => {
|
|
@@ -97,7 +110,11 @@ function ImageEditDialog({
|
|
|
97
110
|
const handleCropSave = useCallback(async () => {
|
|
98
111
|
if (!croppedAreaPixels || !initialData.src) return;
|
|
99
112
|
try {
|
|
100
|
-
const croppedBlob = await getCroppedImg(
|
|
113
|
+
const croppedBlob = await getCroppedImg(
|
|
114
|
+
initialData.src,
|
|
115
|
+
croppedAreaPixels,
|
|
116
|
+
rotation
|
|
117
|
+
);
|
|
101
118
|
const file = new File([croppedBlob], "cropped-image.jpg", {
|
|
102
119
|
type: "image/jpeg"
|
|
103
120
|
});
|
|
@@ -122,112 +139,166 @@ function ImageEditDialog({
|
|
|
122
139
|
}, [selectedImg, alt, onSave, handleClose]);
|
|
123
140
|
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (o) => !o && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "flex max-h-[80vh] max-w-lg flex-col gap-0 overflow-hidden p-0", children: [
|
|
124
141
|
/* @__PURE__ */ jsx(DialogHeader, { className: "px-6 pt-5 pb-0", children: /* @__PURE__ */ jsx(DialogTitle, { className: "text-base font-semibold", children: "Edit Image" }) }),
|
|
125
|
-
/* @__PURE__ */ jsxs(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/* @__PURE__ */
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex-1 overflow-hidden", children: [
|
|
141
|
-
/* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", tab !== "adjust" && "hidden"), children: [
|
|
142
|
-
/* @__PURE__ */ jsx("div", { className: "bg-muted relative h-64", children: /* @__PURE__ */ jsx(
|
|
143
|
-
Cropper,
|
|
144
|
-
{
|
|
145
|
-
image: initialData.src,
|
|
146
|
-
crop,
|
|
147
|
-
zoom,
|
|
148
|
-
rotation,
|
|
149
|
-
aspect: 16 / 9,
|
|
150
|
-
onCropChange: setCrop,
|
|
151
|
-
onZoomChange: setZoom,
|
|
152
|
-
onRotationChange: setRotation,
|
|
153
|
-
onCropComplete: (_, pixels) => setCroppedAreaPixels(pixels)
|
|
154
|
-
}
|
|
155
|
-
) }),
|
|
156
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 px-6 py-3", children: [
|
|
157
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
158
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-gray-500", children: "Zoom" }),
|
|
159
|
-
/* @__PURE__ */ jsx(Slider, { value: [zoom], onValueChange: (vals) => setZoom(Array.isArray(vals) ? vals[0] ?? 1 : vals), min: 1, max: 3, step: 0.1 })
|
|
142
|
+
/* @__PURE__ */ jsxs(
|
|
143
|
+
Tabs,
|
|
144
|
+
{
|
|
145
|
+
value: tab,
|
|
146
|
+
onValueChange: (v) => setTab(v),
|
|
147
|
+
className: "flex flex-1 flex-col overflow-hidden",
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ jsxs(TabsList, { className: "mx-6 mt-3 bg-transparent", children: [
|
|
150
|
+
/* @__PURE__ */ jsxs(TabsTrigger, { value: "adjust", children: [
|
|
151
|
+
/* @__PURE__ */ jsx(Pencil, {}),
|
|
152
|
+
"Crop"
|
|
153
|
+
] }),
|
|
154
|
+
/* @__PURE__ */ jsxs(TabsTrigger, { value: "library", children: [
|
|
155
|
+
/* @__PURE__ */ jsx(Search, {}),
|
|
156
|
+
"Library"
|
|
160
157
|
] }),
|
|
161
|
-
/* @__PURE__ */ jsxs(
|
|
162
|
-
/* @__PURE__ */ jsx(
|
|
163
|
-
|
|
158
|
+
/* @__PURE__ */ jsxs(TabsTrigger, { value: "upload", children: [
|
|
159
|
+
/* @__PURE__ */ jsx(Upload, {}),
|
|
160
|
+
"Upload"
|
|
164
161
|
] })
|
|
165
|
-
] })
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-gray-400", children: "Try uploading an image instead" })
|
|
171
|
-
] }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2", children: libraryImages.map((src) => {
|
|
172
|
-
const isSelected = selectedImg === src;
|
|
173
|
-
return /* @__PURE__ */ jsxs(
|
|
174
|
-
"button",
|
|
175
|
-
{
|
|
176
|
-
type: "button",
|
|
177
|
-
onClick: () => setSelectedImg(src),
|
|
178
|
-
className: `relative aspect-4/3 overflow-hidden rounded-lg border-2 transition-all ${isSelected ? "border-blue-500 ring-2 ring-blue-100" : "border-transparent hover:border-gray-300"}`,
|
|
179
|
-
children: [
|
|
180
|
-
/* @__PURE__ */ jsx("img", { src, alt: "", className: "h-full w-full object-cover" }),
|
|
181
|
-
isSelected && /* @__PURE__ */ jsx("div", { className: "absolute top-1.5 right-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-blue-500 shadow-sm", children: /* @__PURE__ */ jsx(Check, { className: "h-3 w-3 text-white" }) })
|
|
182
|
-
]
|
|
183
|
-
},
|
|
184
|
-
src
|
|
185
|
-
);
|
|
186
|
-
}) }) }) }),
|
|
187
|
-
/* @__PURE__ */ jsxs(
|
|
188
|
-
"div",
|
|
189
|
-
{
|
|
190
|
-
className: cn("flex flex-col items-center justify-center gap-4 px-6 py-14", tab !== "upload" && "hidden"),
|
|
191
|
-
children: [
|
|
192
|
-
/* @__PURE__ */ jsx("div", { className: "flex h-14 w-14 items-center justify-center rounded-full bg-gray-100", children: /* @__PURE__ */ jsx(Upload, { className: "h-6 w-6 text-gray-400" }) }),
|
|
193
|
-
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
194
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "Upload a new image to replace the current one" }),
|
|
195
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-gray-400", children: "Supports JPG, PNG, WebP formats" })
|
|
196
|
-
] }),
|
|
197
|
-
/* @__PURE__ */ jsxs(
|
|
198
|
-
Button,
|
|
162
|
+
] }),
|
|
163
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex-1 overflow-hidden", children: [
|
|
164
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", tab !== "adjust" && "hidden"), children: [
|
|
165
|
+
/* @__PURE__ */ jsx("div", { className: "bg-muted relative h-64", children: /* @__PURE__ */ jsx(
|
|
166
|
+
Cropper,
|
|
199
167
|
{
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
"Choose File"
|
|
210
|
-
]
|
|
168
|
+
image: initialData.src,
|
|
169
|
+
crop,
|
|
170
|
+
zoom,
|
|
171
|
+
rotation,
|
|
172
|
+
aspect: 16 / 9,
|
|
173
|
+
onCropChange: setCrop,
|
|
174
|
+
onZoomChange: setZoom,
|
|
175
|
+
onRotationChange: setRotation,
|
|
176
|
+
onCropComplete: (_, pixels) => setCroppedAreaPixels(pixels)
|
|
211
177
|
}
|
|
212
|
-
),
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
-
"
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
178
|
+
) }),
|
|
179
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 px-6 py-3", children: [
|
|
180
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
181
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-gray-500", children: "Zoom" }),
|
|
182
|
+
/* @__PURE__ */ jsx(
|
|
183
|
+
Slider,
|
|
184
|
+
{
|
|
185
|
+
className: "flex",
|
|
186
|
+
value: [zoom],
|
|
187
|
+
onValueChange: (vals) => setZoom(Array.isArray(vals) ? vals[0] ?? 1 : vals),
|
|
188
|
+
min: 1,
|
|
189
|
+
max: 3,
|
|
190
|
+
step: 0.1
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
] }),
|
|
194
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
195
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-gray-500", children: "Rotation" }),
|
|
196
|
+
/* @__PURE__ */ jsx(
|
|
197
|
+
Slider,
|
|
198
|
+
{
|
|
199
|
+
className: "flex",
|
|
200
|
+
value: [rotation],
|
|
201
|
+
onValueChange: (vals) => setRotation(Array.isArray(vals) ? vals[0] ?? 0 : vals),
|
|
202
|
+
min: 0,
|
|
203
|
+
max: 360,
|
|
204
|
+
step: 1
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
] })
|
|
208
|
+
] })
|
|
209
|
+
] }),
|
|
210
|
+
/* @__PURE__ */ jsx(
|
|
211
|
+
"div",
|
|
212
|
+
{
|
|
213
|
+
className: cn(
|
|
214
|
+
"flex flex-col gap-3 px-6 py-4",
|
|
215
|
+
tab !== "library" && "hidden"
|
|
216
|
+
),
|
|
217
|
+
children: /* @__PURE__ */ jsx(ScrollArea, { className: "h-[280px]", children: isLoadingImages ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center py-20", children: /* @__PURE__ */ jsx(Loader2, { className: "h-6 w-6 animate-spin text-gray-400" }) }) : libraryImages.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center py-20 text-center", children: [
|
|
218
|
+
/* @__PURE__ */ jsx(Search, { className: "h-8 w-8 text-gray-300" }),
|
|
219
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 text-sm font-medium text-gray-500", children: "No images available" }),
|
|
220
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-gray-400", children: "Try uploading an image instead" })
|
|
221
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2", children: libraryImages.map((src) => {
|
|
222
|
+
const isSelected = selectedImg === src;
|
|
223
|
+
return /* @__PURE__ */ jsxs(
|
|
224
|
+
"button",
|
|
225
|
+
{
|
|
226
|
+
type: "button",
|
|
227
|
+
onClick: () => setSelectedImg(src),
|
|
228
|
+
className: `relative aspect-4/3 overflow-hidden rounded-lg border-2 transition-all ${isSelected ? "border-blue-500 ring-2 ring-blue-100" : "border-transparent hover:border-gray-300"}`,
|
|
229
|
+
children: [
|
|
230
|
+
/* @__PURE__ */ jsx(
|
|
231
|
+
"img",
|
|
232
|
+
{
|
|
233
|
+
src,
|
|
234
|
+
alt: "",
|
|
235
|
+
className: "h-full w-full object-cover"
|
|
236
|
+
}
|
|
237
|
+
),
|
|
238
|
+
isSelected && /* @__PURE__ */ jsx("div", { className: "absolute top-1.5 right-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-blue-500 shadow-sm", children: /* @__PURE__ */ jsx(Check, { className: "h-3 w-3 text-white" }) })
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
src
|
|
242
|
+
);
|
|
243
|
+
}) }) })
|
|
244
|
+
}
|
|
245
|
+
),
|
|
246
|
+
/* @__PURE__ */ jsxs(
|
|
247
|
+
"div",
|
|
248
|
+
{
|
|
249
|
+
className: cn(
|
|
250
|
+
"flex flex-col items-center justify-center gap-4 px-6 py-14",
|
|
251
|
+
tab !== "upload" && "hidden"
|
|
252
|
+
),
|
|
253
|
+
children: [
|
|
254
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-14 w-14 items-center justify-center rounded-full bg-gray-100", children: /* @__PURE__ */ jsx(Upload, { className: "h-6 w-6 text-gray-400" }) }),
|
|
255
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
256
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "Upload a new image to replace the current one" }),
|
|
257
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-gray-400", children: "Supports JPG, PNG, WebP formats" })
|
|
258
|
+
] }),
|
|
259
|
+
/* @__PURE__ */ jsxs(
|
|
260
|
+
Button,
|
|
261
|
+
{
|
|
262
|
+
variant: "outline",
|
|
263
|
+
size: "sm",
|
|
264
|
+
onClick: () => {
|
|
265
|
+
var _a;
|
|
266
|
+
return (_a = replaceInputRef.current) == null ? void 0 : _a.click();
|
|
267
|
+
},
|
|
268
|
+
disabled: isReplaceUploading,
|
|
269
|
+
children: [
|
|
270
|
+
isReplaceUploading ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Upload, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
271
|
+
"Choose File"
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
),
|
|
275
|
+
/* @__PURE__ */ jsx(
|
|
276
|
+
"input",
|
|
277
|
+
{
|
|
278
|
+
ref: replaceInputRef,
|
|
279
|
+
type: "file",
|
|
280
|
+
accept: "image/*",
|
|
281
|
+
onChange: handleFileReplace,
|
|
282
|
+
className: "hidden"
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
]
|
|
286
|
+
}
|
|
287
|
+
)
|
|
288
|
+
] })
|
|
289
|
+
]
|
|
290
|
+
}
|
|
291
|
+
),
|
|
228
292
|
/* @__PURE__ */ jsx("div", { className: "border-t px-6 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
229
293
|
/* @__PURE__ */ jsx(Label, { className: "flex-none", children: "Alt Text" }),
|
|
230
|
-
/* @__PURE__ */ jsx(
|
|
294
|
+
/* @__PURE__ */ jsx(
|
|
295
|
+
Input,
|
|
296
|
+
{
|
|
297
|
+
value: alt,
|
|
298
|
+
onChange: (e) => setAlt(e.target.value),
|
|
299
|
+
placeholder: "Image description..."
|
|
300
|
+
}
|
|
301
|
+
)
|
|
231
302
|
] }) }),
|
|
232
303
|
/* @__PURE__ */ jsxs(DialogFooter, { className: "border-t px-6 py-3", children: [
|
|
233
304
|
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: handleClose, children: "Cancel" }),
|
|
@@ -8,7 +8,7 @@ export { CompoundForm, type CompoundFormProps, formMethods, type FormMethods } f
|
|
|
8
8
|
export { CompoundImage, type CompoundImageProps } from "./image";
|
|
9
9
|
export { CompoundArticleCard, type CompoundArticleCardProps } from "./article-card";
|
|
10
10
|
export { ImageUploadField } from "./image-upload-field";
|
|
11
|
-
export { ActionField, ACTION_TYPE_FIELD_MAP, ACTION_TYPE_OPTIONS, PageActionFields, ExternalActionFields, EmailActionFields, PhoneActionFields, SectionActionFields, DownloadActionFields, type ActionTypeFieldProps, } from "./fields
|
|
11
|
+
export { ActionField, ACTION_TYPE_FIELD_MAP, ACTION_TYPE_OPTIONS, PageActionFields, ExternalActionFields, EmailActionFields, PhoneActionFields, SectionActionFields, DownloadActionFields, type ActionTypeFieldProps, } from "./fields";
|
|
12
12
|
export { ErrorBoundary } from "./error-boundary";
|
|
13
13
|
export { GradientText, type GradientTextProps } from "./gradient-text";
|
|
14
14
|
export { InlineEditable } from "./inline-editable";
|
|
@@ -18,6 +18,5 @@ export { usePuckDispatch, usePuckAppState, usePuckHistory, usePuckConfig, usePuc
|
|
|
18
18
|
export { findComponentById, getRandomAdjective } from "./core/utils";
|
|
19
19
|
export { icon, action, actionDefaults, button, buttons, badge, image, images, padding, paddingDefaults, paddingLevel, heading, description, features, contentFields, contentFieldsWithFeatures, card, cards, field, fieldDefaults, form, formDefaults, getPlaceholderImageUrl, image16x9Placeholder, image1x1Placeholder, image9x16Placeholder, sectionStyle, sectionOverlay, backgroundColor, backgroundImage, } from "./core/fields";
|
|
20
20
|
export { EditorContextProvider, useEditorContext } from "./editor-context";
|
|
21
|
-
export { AutoField } from "./fields
|
|
22
|
-
export { type FieldDef, type AutoFieldProps, type FieldsPanelProps } from "./fields/types";
|
|
21
|
+
export { AutoField, type FieldDef, type AutoFieldProps, type FieldsPanelProps } from "./fields";
|
|
23
22
|
export { default as useUpload, type UploadFileItem, type UploadStatus, type UseUploadOptions } from "./use-upload";
|
|
@@ -8,6 +8,7 @@ const MAX_WIDTH_MAP = {
|
|
|
8
8
|
full: "max-w-none"
|
|
9
9
|
};
|
|
10
10
|
function sanitizeHtml(html) {
|
|
11
|
+
if (typeof html !== "string") return "";
|
|
11
12
|
return html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/<iframe\b[^>]*>/gi, "").replace(/\bon\w+\s*=/gi, "data-removed=");
|
|
12
13
|
}
|
|
13
14
|
const RichText = ({
|
|
@@ -18,7 +18,10 @@ function Slider({
|
|
|
18
18
|
return /* @__PURE__ */ jsx(
|
|
19
19
|
Slider$1.Root,
|
|
20
20
|
{
|
|
21
|
-
className: cn(
|
|
21
|
+
className: cn(
|
|
22
|
+
"data-horizontal:w-full data-vertical:h-full flex items-center",
|
|
23
|
+
className
|
|
24
|
+
),
|
|
22
25
|
"data-slot": "slider",
|
|
23
26
|
defaultValue,
|
|
24
27
|
value,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dune-react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"preview-storybook": "npx http-server storybook-static",
|
|
16
16
|
"build-storybook": "storybook build && cp -r skills storybook-static/skills",
|
|
17
17
|
"deploy-storybook": "storybook-to-ghpages -- --existing-output-dir=storybook-static",
|
|
18
|
-
"ondeploy": "pnpm build-storybook && pnpm deploy-storybook"
|
|
18
|
+
"ondeploy": "pnpm build-storybook && pnpm deploy-storybook",
|
|
19
|
+
"postpublish": "bash scripts/postpublish.sh"
|
|
19
20
|
},
|
|
20
21
|
"sideEffects": [
|
|
21
22
|
"*.css"
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
}
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
46
|
+
"@aws-sdk/client-s3": "3.1017.0",
|
|
45
47
|
"@puckeditor/core": "0.21.1",
|
|
46
48
|
"@storybook/addon-docs": "^10.2.19",
|
|
47
49
|
"@storybook/addon-links": "^10.2.19",
|