@vertebr/react 0.12.0 → 0.15.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/dist/components/admin/AdminSidebar.d.ts.map +1 -1
- package/dist/components/admin/AdminSidebar.js +3 -2
- package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
- package/dist/components/admin/VertebrAdmin.js +72 -11
- package/dist/components/config/DescriptionInput.d.ts +3 -1
- package/dist/components/config/DescriptionInput.d.ts.map +1 -1
- package/dist/components/config/DescriptionInput.js +2 -2
- package/dist/components/config/DocumentAttachmentField.d.ts +17 -0
- package/dist/components/config/DocumentAttachmentField.d.ts.map +1 -0
- package/dist/components/config/DocumentAttachmentField.js +32 -0
- package/dist/components/config/ImagePickerModal.d.ts +4 -1
- package/dist/components/config/ImagePickerModal.d.ts.map +1 -1
- package/dist/components/config/ImagePickerModal.js +45 -23
- package/dist/components/config/MarkdownDocumentModal.d.ts +19 -0
- package/dist/components/config/MarkdownDocumentModal.d.ts.map +1 -0
- package/dist/components/config/MarkdownDocumentModal.js +34 -0
- package/dist/components/config/form/ConfigField.d.ts.map +1 -1
- package/dist/components/config/form/ConfigField.js +95 -5
- package/dist/components/gallery/GalleryBoard.d.ts.map +1 -1
- package/dist/components/gallery/GalleryBoard.js +37 -5
- package/dist/components/ui/icons.d.ts +30 -2
- package/dist/components/ui/icons.d.ts.map +1 -1
- package/dist/components/ui/icons.js +199 -15
- package/dist/lib/documents.d.ts +14 -0
- package/dist/lib/documents.d.ts.map +1 -0
- package/dist/lib/documents.js +42 -0
- package/dist/styles/form.css +15 -0
- package/dist/styles/gallery.css +237 -0
- package/package.json +3 -1
|
@@ -6,10 +6,13 @@ import * as Switch from "@radix-ui/react-switch";
|
|
|
6
6
|
import { VisibilityEyeButton } from "../VisibilityEye";
|
|
7
7
|
import { isDescriptionField } from "../../../lib/description";
|
|
8
8
|
import { collectRecordImages, isImageField, urlsFromValue, } from "../../../lib/images";
|
|
9
|
+
import { isDocumentField } from "../../../lib/documents";
|
|
9
10
|
import { isCentsField, isPriceField } from "../../../lib/price";
|
|
10
11
|
import { FontPickerField } from "../../fonts/FontPickerField";
|
|
11
12
|
import { ImageUploadControl } from "../../resources/ImageUploadControl";
|
|
12
13
|
import { DescriptionInput } from "../DescriptionInput";
|
|
14
|
+
import { DocumentAttachmentField } from "../DocumentAttachmentField";
|
|
15
|
+
import { MarkdownDocumentModal } from "../MarkdownDocumentModal";
|
|
13
16
|
import { DatetimeInput } from "../DatetimeInput";
|
|
14
17
|
import { EnumInput } from "../EnumInput";
|
|
15
18
|
import { EnumListInput } from "../EnumListInput";
|
|
@@ -17,9 +20,39 @@ import { ImagePickerModal } from "../ImagePickerModal";
|
|
|
17
20
|
import { PriceInput } from "../PriceInput";
|
|
18
21
|
import { ReferenceInput } from "../ReferenceInput";
|
|
19
22
|
import { displayLabel, firstImageUrl, isMultilineField, fieldVisibilityKey, spanFor, } from "./helpers";
|
|
23
|
+
function findMarkdownBodyKey(schema, currentKey) {
|
|
24
|
+
const preferred = ["body", "content", "policyBody", "text"];
|
|
25
|
+
for (const key of preferred) {
|
|
26
|
+
const field = schema[key];
|
|
27
|
+
if (!field || key === currentKey)
|
|
28
|
+
continue;
|
|
29
|
+
if (field.type === "richtext" || isDescriptionField(key, field))
|
|
30
|
+
return key;
|
|
31
|
+
}
|
|
32
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
33
|
+
if (key === currentKey)
|
|
34
|
+
continue;
|
|
35
|
+
if (field.type === "richtext" || isDescriptionField(key, field))
|
|
36
|
+
return key;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
20
40
|
export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, hideImagePreview, hideUpload, visibilityGroup, settingsRow, client, }) {
|
|
21
41
|
const [pickerOpen, setPickerOpen] = useState(false);
|
|
42
|
+
const [docModalOpen, setDocModalOpen] = useState(false);
|
|
22
43
|
const label = displayLabel(fieldKey, field, groupLabel);
|
|
44
|
+
const documentField = isDocumentField(fieldKey, field);
|
|
45
|
+
const markdownBodyKey = documentField
|
|
46
|
+
? findMarkdownBodyKey(schema, fieldKey)
|
|
47
|
+
: null;
|
|
48
|
+
const markdownBodyField = markdownBodyKey
|
|
49
|
+
? schema[markdownBodyKey]
|
|
50
|
+
: undefined;
|
|
51
|
+
const markdownBodyValue = markdownBodyKey && typeof watched?.[markdownBodyKey] === "string"
|
|
52
|
+
? watched[markdownBodyKey]
|
|
53
|
+
: markdownBodyKey
|
|
54
|
+
? ""
|
|
55
|
+
: undefined;
|
|
23
56
|
const visibilityKey = fieldVisibilityKey(fieldKey, schema, visibilityGroup ?? groupLabel);
|
|
24
57
|
const visibilityOn = !visibilityKey || watched?.[visibilityKey] !== false;
|
|
25
58
|
const showInlinePreview = !hideImagePreview &&
|
|
@@ -28,10 +61,13 @@ export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watc
|
|
|
28
61
|
Boolean(firstImageUrl(watched?.[fieldKey]));
|
|
29
62
|
const canPickFiles = !field.readOnly &&
|
|
30
63
|
Boolean(onListGallery) &&
|
|
31
|
-
(field.type === "file[]" ||
|
|
64
|
+
(field.type === "file[]" ||
|
|
65
|
+
isImageField(fieldKey, field) ||
|
|
66
|
+
documentField);
|
|
32
67
|
const canLocalUpload = !hideUpload &&
|
|
33
68
|
Boolean(uploads?.[fieldKey] && onUpload) &&
|
|
34
69
|
!field.readOnly;
|
|
70
|
+
const canGalleryUpload = Boolean(onUploadGallery);
|
|
35
71
|
const applyPickedImages = (images) => {
|
|
36
72
|
const urls = images.map((image) => image.url).filter(Boolean);
|
|
37
73
|
if (!urls.length)
|
|
@@ -53,9 +89,38 @@ export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watc
|
|
|
53
89
|
}
|
|
54
90
|
return (_jsxs("div", { className: "vertebr-switch-row", children: [_jsxs("div", { className: "vertebr-switch-row-copy", children: [_jsx(Label.Root, { htmlFor: fieldKey, children: label }), field.description ? (_jsx("p", { className: "vertebr-switch-row-desc", children: field.description })) : null] }), _jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(Switch.Root, { className: "vertebr-switch", id: fieldKey, checked: Boolean(controllerField.value), onCheckedChange: controllerField.onChange, children: _jsx(Switch.Thumb, { className: "vertebr-switch-thumb" }) })) })] }));
|
|
55
91
|
}
|
|
56
|
-
return (_jsxs("div", { className: "vertebr-field", "data-span": spanFor(fieldKey, field, variant), "data-kind": isImageField(fieldKey, field)
|
|
92
|
+
return (_jsxs("div", { className: "vertebr-field", "data-span": spanFor(fieldKey, field, variant), "data-kind": isImageField(fieldKey, field)
|
|
93
|
+
? "image"
|
|
94
|
+
: documentField
|
|
95
|
+
? "document"
|
|
96
|
+
: undefined, "data-storefront": visibilityKey ? (visibilityOn ? "on" : "off") : undefined, children: [_jsxs("div", { className: "vertebr-field-head", children: [_jsx(Label.Root, { htmlFor: fieldKey, children: label }), visibilityKey ? (_jsx(VisibilityEyeButton, { name: visibilityKey, label: label, form: form })) : null] }), showInlinePreview ? (_jsxs("div", { className: "vertebr-field-media", children: [_jsx("img", { src: firstImageUrl(watched[fieldKey]) ?? "", alt: "" }), _jsx("input", { id: fieldKey, type: "text", readOnly: field.readOnly, ...form.register(fieldKey) })] })) : documentField ? (_jsx(DocumentAttachmentField, { id: fieldKey, label: label, value: typeof watched?.[fieldKey] === "string" ? watched[fieldKey] : "", readOnly: field.readOnly, description: field.description, markdownBody: markdownBodyValue, canPickFiles: canPickFiles, canUpload: canLocalUpload || canGalleryUpload, onChangeUrl: (next) => form.setValue(fieldKey, next, {
|
|
97
|
+
shouldDirty: true,
|
|
98
|
+
shouldTouch: true,
|
|
99
|
+
}), onOpenPicker: () => setPickerOpen(true), onUploadFiles: async (files) => {
|
|
100
|
+
if (canLocalUpload) {
|
|
101
|
+
const uploaded = await onUpload(fieldKey, files);
|
|
102
|
+
const url = uploaded[0]?.url;
|
|
103
|
+
if (url) {
|
|
104
|
+
form.setValue(fieldKey, url, {
|
|
105
|
+
shouldDirty: true,
|
|
106
|
+
shouldTouch: true,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (!onUploadGallery)
|
|
112
|
+
return;
|
|
113
|
+
const items = await onUploadGallery(files);
|
|
114
|
+
const url = items[0]?.url;
|
|
115
|
+
if (url) {
|
|
116
|
+
form.setValue(fieldKey, url, {
|
|
117
|
+
shouldDirty: true,
|
|
118
|
+
shouldTouch: true,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}, onEditMarkdown: markdownBodyKey ? () => setDocModalOpen(true) : undefined })) : field.type === "richtext" || isDescriptionField(fieldKey, field) ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(DescriptionInput, { id: fieldKey, name: controllerField.name, inputRef: controllerField.ref, value: typeof controllerField.value === "string"
|
|
57
122
|
? controllerField.value
|
|
58
|
-
: "", readOnly: field.readOnly, recordImages: collectRecordImages(schema, watched), galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, onBlur: controllerField.onBlur, onChange: controllerField.onChange })) })) : field.type === "file[]" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => {
|
|
123
|
+
: "", readOnly: field.readOnly, recordImages: collectRecordImages(schema, watched), galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, onBlur: controllerField.onBlur, onChange: controllerField.onChange, onOpenDocumentView: () => setDocModalOpen(true) })) })) : field.type === "file[]" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => {
|
|
59
124
|
const urls = urlsFromValue(controllerField.value);
|
|
60
125
|
return (_jsx("div", { className: "vertebr-field-files", children: urls.length > 0 ? (_jsx("ul", { className: "vertebr-field-files-list", children: urls.map((url) => (_jsxs("li", { children: [_jsx("img", { src: url, alt: "" }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => controllerField.onChange(urls.filter((item) => item !== url)), children: "Remove" })] }, url))) })) : (_jsx("p", { className: "vertebr-field-files-empty", children: "No photos yet" })) }));
|
|
61
126
|
} })) : field.type === "number" && isPriceField(fieldKey, field) ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(PriceInput, { id: fieldKey, name: controllerField.name, inputRef: controllerField.ref, value: controllerField.value, asCents: isCentsField(fieldKey, field) || /^pricing$/i.test(groupLabel), readOnly: field.readOnly, onBlur: controllerField.onBlur, onChange: controllerField.onChange })) })) : field.type === "number" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx("input", { id: fieldKey, type: "number", readOnly: field.readOnly, value: typeof controllerField.value === "number" &&
|
|
@@ -64,7 +129,9 @@ export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watc
|
|
|
64
129
|
: "", onBlur: controllerField.onBlur, onChange: (event) => {
|
|
65
130
|
const next = event.target.value;
|
|
66
131
|
controllerField.onChange(next === "" ? undefined : Number(next));
|
|
67
|
-
}, name: controllerField.name, ref: controllerField.ref })) })) : field.type === "datetime" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(DatetimeInput, { id: fieldKey, name: controllerField.name, inputRef: controllerField.ref, value: controllerField.value, readOnly: field.readOnly, onBlur: controllerField.onBlur, onChange: controllerField.onChange })) })) : field.type === "enum" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "enum[]" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumListInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "font" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(FontPickerField, { id: fieldKey, value: controllerField.value, label: field.label, description: field.description, readOnly: field.readOnly, fonts: fonts ?? [], loading: fontsLoading, onChange: controllerField.onChange, onRefreshFonts: onRefreshFonts })) })) : field.type === "reference" && client ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(ReferenceInput, { id: fieldKey, value: controllerField.value, field: field, client: client, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : isMultilineField(fieldKey, field) ? (_jsx("textarea", { id: fieldKey, rows: 5, readOnly: field.readOnly, ...form.register(fieldKey) })) : (_jsx("input", { id: fieldKey, type: "text", readOnly: field.readOnly, ...form.register(fieldKey) })), !hideUpload &&
|
|
132
|
+
}, name: controllerField.name, ref: controllerField.ref })) })) : field.type === "datetime" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(DatetimeInput, { id: fieldKey, name: controllerField.name, inputRef: controllerField.ref, value: controllerField.value, readOnly: field.readOnly, onBlur: controllerField.onBlur, onChange: controllerField.onChange })) })) : field.type === "enum" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "enum[]" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumListInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "font" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(FontPickerField, { id: fieldKey, value: controllerField.value, label: field.label, description: field.description, readOnly: field.readOnly, fonts: fonts ?? [], loading: fontsLoading, onChange: controllerField.onChange, onRefreshFonts: onRefreshFonts })) })) : field.type === "reference" && client ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(ReferenceInput, { id: fieldKey, value: controllerField.value, field: field, client: client, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : isMultilineField(fieldKey, field) ? (_jsx("textarea", { id: fieldKey, rows: 5, readOnly: field.readOnly, ...form.register(fieldKey) })) : (_jsx("input", { id: fieldKey, type: "text", readOnly: field.readOnly, ...form.register(fieldKey) })), !hideUpload &&
|
|
133
|
+
!documentField &&
|
|
134
|
+
(canLocalUpload || canPickFiles) ? (_jsxs("div", { className: "vertebr-field-media-actions", children: [canPickFiles ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => setPickerOpen(true), children: "Choose from Files" })) : null, canLocalUpload ? (_jsx(ImageUploadControl, { meta: uploads[fieldKey], multiple: field.type === "file[]", showOptimizeHint: true, onUpload: async (files) => {
|
|
68
135
|
const uploaded = await onUpload(fieldKey, files);
|
|
69
136
|
const urls = uploaded
|
|
70
137
|
.map((item) => item.url)
|
|
@@ -79,5 +146,28 @@ export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watc
|
|
|
79
146
|
shouldDirty: true,
|
|
80
147
|
shouldTouch: true,
|
|
81
148
|
});
|
|
82
|
-
} })) : null] })) : null, pickerOpen && canPickFiles ? (_jsx(ImagePickerModal, { title:
|
|
149
|
+
} })) : null] })) : null, pickerOpen && canPickFiles ? (_jsx(ImagePickerModal, { title: documentField
|
|
150
|
+
? "Choose PDF"
|
|
151
|
+
: field.type === "file[]"
|
|
152
|
+
? "Add product photos"
|
|
153
|
+
: "Choose image", confirmLabel: "Use", defaultTab: "gallery", assetKind: documentField ? "document" : "image", recordImages: documentField ? [] : collectRecordImages(schema, watched), galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, onClose: () => setPickerOpen(false), onInsert: applyPickedImages })) : null, docModalOpen &&
|
|
154
|
+
(markdownBodyKey ||
|
|
155
|
+
field.type === "richtext" ||
|
|
156
|
+
isDescriptionField(fieldKey, field)) ? (_jsx(MarkdownDocumentModal, { title: markdownBodyKey && markdownBodyField
|
|
157
|
+
? displayLabel(markdownBodyKey, markdownBodyField, groupLabel)
|
|
158
|
+
: label, value: markdownBodyKey
|
|
159
|
+
? typeof form.getValues(markdownBodyKey) === "string"
|
|
160
|
+
? form.getValues(markdownBodyKey)
|
|
161
|
+
: ""
|
|
162
|
+
: typeof form.getValues(fieldKey) === "string"
|
|
163
|
+
? form.getValues(fieldKey)
|
|
164
|
+
: "", readOnly: markdownBodyKey
|
|
165
|
+
? Boolean(markdownBodyField?.readOnly)
|
|
166
|
+
: field.readOnly, recordImages: collectRecordImages(schema, watched), galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, onChange: (next) => {
|
|
167
|
+
const key = markdownBodyKey ?? fieldKey;
|
|
168
|
+
form.setValue(key, next, {
|
|
169
|
+
shouldDirty: true,
|
|
170
|
+
shouldTouch: true,
|
|
171
|
+
});
|
|
172
|
+
}, onClose: () => setDocModalOpen(false) })) : null] }));
|
|
83
173
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GalleryBoard.d.ts","sourceRoot":"","sources":["../../../src/components/gallery/GalleryBoard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"GalleryBoard.d.ts","sourceRoot":"","sources":["../../../src/components/gallery/GalleryBoard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,iBAAiB,CAAC;AAIvE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC;AAuBF,wBAAgB,YAAY,CAAC,EAC3B,KAAiB,EACjB,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,EACT,OAAO,EACP,SAAS,EACT,QAAQ,EACR,QAAQ,GACT,EAAE,iBAAiB,+BAgHnB"}
|
|
@@ -1,14 +1,46 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { isPdfAsset, pdfFileName } from "../../lib/documents";
|
|
2
3
|
import { ImageUploadControl } from "../resources/ImageUploadControl";
|
|
4
|
+
function acceptsDocuments(accept) {
|
|
5
|
+
return accept.some((entry) => /pdf/i.test(entry) ||
|
|
6
|
+
entry === "application/*" ||
|
|
7
|
+
entry === "*/*");
|
|
8
|
+
}
|
|
9
|
+
function acceptsImages(accept) {
|
|
10
|
+
return (accept.length === 0 ||
|
|
11
|
+
accept.some((entry) => entry.startsWith("image/") ||
|
|
12
|
+
entry === "image/*" ||
|
|
13
|
+
entry === "*/*"));
|
|
14
|
+
}
|
|
3
15
|
export function GalleryBoard({ label = "Gallery", items, accept, maxBytes, canDelete, loading, onRefresh, onUpload, onDelete, }) {
|
|
4
16
|
const meta = {
|
|
5
17
|
field: "gallery",
|
|
6
18
|
accept,
|
|
7
19
|
maxBytes,
|
|
8
20
|
};
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
const docs = acceptsDocuments(accept);
|
|
22
|
+
const images = acceptsImages(accept);
|
|
23
|
+
const dropLabel = docs && images
|
|
24
|
+
? "Drop images or PDFs here or browse to add to Files"
|
|
25
|
+
: docs
|
|
26
|
+
? "Drop PDFs here or browse to add to Files"
|
|
27
|
+
: "Drop images here or browse to add to the gallery";
|
|
28
|
+
const emptyLabel = docs && images
|
|
29
|
+
? "No files yet."
|
|
30
|
+
: docs
|
|
31
|
+
? "No documents yet."
|
|
32
|
+
: "No images in the gallery yet.";
|
|
33
|
+
return (_jsxs("div", { className: "vertebr-gallery", children: [_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: label }), _jsxs("div", { className: "vertebr-gallery-head-actions", children: [_jsx("span", { className: "vertebr-count", children: items.length }), onRefresh ? (_jsx("button", { type: "button", className: "vertebr-btn", onClick: onRefresh, disabled: loading, children: loading ? "Refreshing…" : "Refresh" })) : null] })] }), _jsxs("div", { className: "vertebr-gallery-body", children: [_jsx(ImageUploadControl, { meta: meta, multiple: true, showOptimizeHint: images, label: dropLabel, onUpload: onUpload }), items.length === 0 ? (_jsx("div", { className: "vertebr-empty", children: emptyLabel })) : (_jsx("div", { className: "vertebr-gallery-grid", children: items.map((item) => {
|
|
34
|
+
const pdf = isPdfAsset(item);
|
|
35
|
+
return (_jsxs("article", { className: "vertebr-gallery-card", children: [_jsx("a", { className: pdf
|
|
36
|
+
? "vertebr-gallery-thumb vertebr-gallery-thumb-file"
|
|
37
|
+
: "vertebr-gallery-thumb", href: item.url, target: "_blank", rel: "noreferrer", title: item.name ?? item.url, children: pdf ? (_jsx("span", { className: "vertebr-gallery-file-badge", children: "PDF" })) : (_jsx("img", { src: item.url, alt: item.name ?? "", loading: "lazy" })) }), _jsxs("div", { className: "vertebr-gallery-meta", children: [_jsx("strong", { title: item.name ?? item.id, children: item.name ??
|
|
38
|
+
(pdf ? pdfFileName(item.url) : item.id) }), _jsx("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.url.replace(/^https?:\/\//i, "") })] }), canDelete && onDelete ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-danger-quiet", onClick: () => {
|
|
39
|
+
if (window.confirm(pdf
|
|
40
|
+
? "Delete this document from Files?"
|
|
41
|
+
: "Delete this image from the gallery?")) {
|
|
42
|
+
void onDelete(item.id);
|
|
43
|
+
}
|
|
44
|
+
}, children: "Delete" })) : null] }, item.id));
|
|
45
|
+
}) }))] })] }));
|
|
14
46
|
}
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import type { SVGProps } from "react";
|
|
1
|
+
import type { ReactElement, SVGProps } from "react";
|
|
2
|
+
import type { CatalogAddonCategory, NavIconName } from "@vertebr/client";
|
|
2
3
|
type IconProps = SVGProps<SVGSVGElement>;
|
|
3
4
|
export declare function IconHome(props: IconProps): import("react").JSX.Element;
|
|
4
5
|
export declare function IconProducts(props: IconProps): import("react").JSX.Element;
|
|
6
|
+
export declare function IconInventory(props: IconProps): import("react").JSX.Element;
|
|
7
|
+
export declare function IconUsers(props: IconProps): import("react").JSX.Element;
|
|
8
|
+
export declare function IconCard(props: IconProps): import("react").JSX.Element;
|
|
9
|
+
export declare function IconTag(props: IconProps): import("react").JSX.Element;
|
|
10
|
+
export declare function IconBolt(props: IconProps): import("react").JSX.Element;
|
|
5
11
|
export declare function IconGallery(props: IconProps): import("react").JSX.Element;
|
|
6
12
|
export declare function IconFonts(props: IconProps): import("react").JSX.Element;
|
|
7
13
|
export declare function IconMail(props: IconProps): import("react").JSX.Element;
|
|
8
14
|
export declare function IconPages(props: IconProps): import("react").JSX.Element;
|
|
15
|
+
export declare function IconPalette(props: IconProps): import("react").JSX.Element;
|
|
16
|
+
export declare function IconTruck(props: IconProps): import("react").JSX.Element;
|
|
17
|
+
export declare function IconClock(props: IconProps): import("react").JSX.Element;
|
|
18
|
+
export declare function IconStar(props: IconProps): import("react").JSX.Element;
|
|
19
|
+
export declare function IconMegaphone(props: IconProps): import("react").JSX.Element;
|
|
20
|
+
export declare function IconLayout(props: IconProps): import("react").JSX.Element;
|
|
21
|
+
export declare function IconList(props: IconProps): import("react").JSX.Element;
|
|
9
22
|
export declare function IconSettings(props: IconProps): import("react").JSX.Element;
|
|
10
23
|
export declare function IconChart(props: IconProps): import("react").JSX.Element;
|
|
11
24
|
export declare function IconChevronLeft(props: IconProps): import("react").JSX.Element;
|
|
@@ -13,6 +26,21 @@ export declare function IconSearch(props: IconProps): import("react").JSX.Elemen
|
|
|
13
26
|
export declare function IconSync(props: IconProps): import("react").JSX.Element;
|
|
14
27
|
export declare function IconMenu(props: IconProps): import("react").JSX.Element;
|
|
15
28
|
export declare function IconAddon(props: IconProps): import("react").JSX.Element;
|
|
16
|
-
export
|
|
29
|
+
export type NavIconComponent = (props: IconProps) => ReactElement;
|
|
30
|
+
export declare const NAV_ICON_COMPONENTS: Record<NavIconName, NavIconComponent>;
|
|
31
|
+
export type ResolveNavIconInput = {
|
|
32
|
+
key: string;
|
|
33
|
+
id?: string;
|
|
34
|
+
label?: string;
|
|
35
|
+
group?: string;
|
|
36
|
+
category?: CatalogAddonCategory;
|
|
37
|
+
section?: "config" | "page" | "addon";
|
|
38
|
+
icon?: NavIconName | null;
|
|
39
|
+
};
|
|
40
|
+
/** Resolve a stable icon name for a sidebar entry. */
|
|
41
|
+
export declare function resolveNavIconName(input: ResolveNavIconInput): NavIconName;
|
|
42
|
+
export declare function resolveNavIcon(input: ResolveNavIconInput): NavIconComponent;
|
|
43
|
+
/** @deprecated Prefer `resolveNavIcon`. */
|
|
44
|
+
export declare function iconForNavKey(key: string): NavIconComponent;
|
|
17
45
|
export {};
|
|
18
46
|
//# sourceMappingURL=icons.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../../src/components/ui/icons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../../src/components/ui/icons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEzE,KAAK,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;AAiBzC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BAMxC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,+BAO5C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,+BAS7C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BASzC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BAQxC;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,+BAOvC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BAMxC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,+BAQ3C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BAOzC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BAOxC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BAOzC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,+BAS3C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BASzC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BAOzC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BAMxC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,+BAQ7C;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,+BAO1C;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BAOxC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,+BAO5C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BAMzC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,+BAM/C;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,+BAO1C;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BASxC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BAQxC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BASzC;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,YAAY,CAAC;AAElE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,gBAAgB,CAuBrE,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,OAAO,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACtC,IAAI,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAC3B,CAAC;AA8FF,sDAAsD;AACtD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,GAAG,WAAW,CAY1E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,gBAAgB,CAE3E;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAE3D"}
|
|
@@ -19,6 +19,21 @@ export function IconHome(props) {
|
|
|
19
19
|
export function IconProducts(props) {
|
|
20
20
|
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M3 6.5 10 2.5l7 4v9H3v-9z" }), _jsx("path", { d: "M7 11.5h6M7 14h4" })] }));
|
|
21
21
|
}
|
|
22
|
+
export function IconInventory(props) {
|
|
23
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M3.5 7h13v9.5H3.5z" }), _jsx("path", { d: "M3.5 10.5h13" }), _jsx("path", { d: "M7 7V5.5h6V7" }), _jsx("path", { d: "M7 13h2M11 13h2" })] }));
|
|
24
|
+
}
|
|
25
|
+
export function IconUsers(props) {
|
|
26
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("circle", { cx: "7.5", cy: "7", r: "2.25" }), _jsx("path", { d: "M3.5 15.5c.4-2.4 2-3.75 4-3.75s3.6 1.35 4 3.75" }), _jsx("circle", { cx: "13.5", cy: "7.25", r: "1.75" }), _jsx("path", { d: "M12.2 11.9c1.35.2 2.55 1.1 2.9 3.1" })] }));
|
|
27
|
+
}
|
|
28
|
+
export function IconCard(props) {
|
|
29
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("rect", { x: "2.5", y: "5", width: "15", height: "10.5", rx: "1.5" }), _jsx("path", { d: "M2.5 8.5h15" }), _jsx("path", { d: "M5.5 12.5h4" })] }));
|
|
30
|
+
}
|
|
31
|
+
export function IconTag(props) {
|
|
32
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M3.5 10.5V4.5H9.5l7 7-5 5-8-6z" }), _jsx("circle", { cx: "7", cy: "7", r: "1", fill: "currentColor", stroke: "none" })] }));
|
|
33
|
+
}
|
|
34
|
+
export function IconBolt(props) {
|
|
35
|
+
return (_jsx("svg", { ...base(props), children: _jsx("path", { d: "M11 2.5 5.5 11h4L9 17.5 14.5 9h-4L11 2.5z" }) }));
|
|
36
|
+
}
|
|
22
37
|
export function IconGallery(props) {
|
|
23
38
|
return (_jsxs("svg", { ...base(props), children: [_jsx("rect", { x: "2.5", y: "4.5", width: "15", height: "11", rx: "1.5" }), _jsx("path", { d: "m3.5 13 3.5-3.5 2.5 2.5L12.5 9l4 4.5" }), _jsx("circle", { cx: "7", cy: "7.5", r: "1", fill: "currentColor", stroke: "none" })] }));
|
|
24
39
|
}
|
|
@@ -26,11 +41,32 @@ export function IconFonts(props) {
|
|
|
26
41
|
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M5 16.5V5.5h10M7.5 16.5V9h5v7.5" }), _jsx("path", { d: "M7 5.5h6" })] }));
|
|
27
42
|
}
|
|
28
43
|
export function IconMail(props) {
|
|
29
|
-
return (_jsxs("svg", { ...base(props), children: [_jsx("rect", { x: "2.5", y: "4.5", width: "15", height: "11", rx: "1.5" }), _jsx("path", { d: "m3.5 6.5 6.5 4.5
|
|
44
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("rect", { x: "2.5", y: "4.5", width: "15", height: "11", rx: "1.5" }), _jsx("path", { d: "m3.5 6.5 6.5 4.5 6.5-4.5" })] }));
|
|
30
45
|
}
|
|
31
46
|
export function IconPages(props) {
|
|
32
47
|
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M6 3.5h5.5L16 8v8.5a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-12a1 1 0 0 1 1-1z" }), _jsx("path", { d: "M11.5 3.5V8H16" })] }));
|
|
33
48
|
}
|
|
49
|
+
export function IconPalette(props) {
|
|
50
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M10 3a7 7 0 1 0 0 14h1.2a1.8 1.8 0 0 0 0-3.6H10a2 2 0 0 1 0-4h3.5" }), _jsx("circle", { cx: "7.2", cy: "8", r: "0.9", fill: "currentColor", stroke: "none" }), _jsx("circle", { cx: "8.4", cy: "11.2", r: "0.9", fill: "currentColor", stroke: "none" }), _jsx("circle", { cx: "12.2", cy: "7.2", r: "0.9", fill: "currentColor", stroke: "none" })] }));
|
|
51
|
+
}
|
|
52
|
+
export function IconTruck(props) {
|
|
53
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M2.5 13.5V6.5h9v7" }), _jsx("path", { d: "M11.5 9.5h4l2 2.5v1.5h-6" }), _jsx("circle", { cx: "6", cy: "14.5", r: "1.5" }), _jsx("circle", { cx: "14.5", cy: "14.5", r: "1.5" })] }));
|
|
54
|
+
}
|
|
55
|
+
export function IconClock(props) {
|
|
56
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("circle", { cx: "10", cy: "10", r: "6.5" }), _jsx("path", { d: "M10 6.5V10l3 2" })] }));
|
|
57
|
+
}
|
|
58
|
+
export function IconStar(props) {
|
|
59
|
+
return (_jsx("svg", { ...base(props), children: _jsx("path", { d: "m10 3.5 1.8 3.7 4.1.6-3 2.9.7 4.1L10 13.2 6.4 14.8l.7-4.1-3-2.9 4.1-.6L10 3.5z" }) }));
|
|
60
|
+
}
|
|
61
|
+
export function IconMegaphone(props) {
|
|
62
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M4.5 11.5v-3l10-3.5v10L4.5 11.5z" }), _jsx("path", { d: "M4.5 8.5v5.5a2 2 0 0 0 2 0V11" }), _jsx("path", { d: "M15.5 7.5v5" })] }));
|
|
63
|
+
}
|
|
64
|
+
export function IconLayout(props) {
|
|
65
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("rect", { x: "3", y: "3.5", width: "14", height: "13", rx: "1.5" }), _jsx("path", { d: "M3 8h14M9 8v8.5" })] }));
|
|
66
|
+
}
|
|
67
|
+
export function IconList(props) {
|
|
68
|
+
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M7 5.5h9.5M7 10h9.5M7 14.5h9.5" }), _jsx("path", { d: "M4 5.5h.01M4 10h.01M4 14.5h.01" })] }));
|
|
69
|
+
}
|
|
34
70
|
export function IconSettings(props) {
|
|
35
71
|
return (_jsxs("svg", { ...base(props), children: [_jsx("circle", { cx: "10", cy: "10", r: "2.25" }), _jsx("path", { d: "M10 2.5v2M10 15.5v2M3.5 10h2M14.5 10h2M5.4 5.4l1.4 1.4M13.2 13.2l1.4 1.4M5.4 14.6l1.4-1.4M13.2 6.8l1.4-1.4" })] }));
|
|
36
72
|
}
|
|
@@ -52,26 +88,174 @@ export function IconMenu(props) {
|
|
|
52
88
|
export function IconAddon(props) {
|
|
53
89
|
return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M8 3.5h4v4H8z" }), _jsx("path", { d: "M3.5 8h4v4h-4z" }), _jsx("path", { d: "M12.5 8h4v4h-4z" }), _jsx("path", { d: "M8 12.5h4v4H8z" })] }));
|
|
54
90
|
}
|
|
55
|
-
export
|
|
91
|
+
export const NAV_ICON_COMPONENTS = {
|
|
92
|
+
home: IconHome,
|
|
93
|
+
products: IconProducts,
|
|
94
|
+
inventory: IconInventory,
|
|
95
|
+
users: IconUsers,
|
|
96
|
+
card: IconCard,
|
|
97
|
+
tag: IconTag,
|
|
98
|
+
bolt: IconBolt,
|
|
99
|
+
gallery: IconGallery,
|
|
100
|
+
fonts: IconFonts,
|
|
101
|
+
mail: IconMail,
|
|
102
|
+
pages: IconPages,
|
|
103
|
+
palette: IconPalette,
|
|
104
|
+
truck: IconTruck,
|
|
105
|
+
clock: IconClock,
|
|
106
|
+
star: IconStar,
|
|
107
|
+
megaphone: IconMegaphone,
|
|
108
|
+
layout: IconLayout,
|
|
109
|
+
list: IconList,
|
|
110
|
+
chart: IconChart,
|
|
111
|
+
sync: IconSync,
|
|
112
|
+
addon: IconAddon,
|
|
113
|
+
settings: IconSettings,
|
|
114
|
+
};
|
|
115
|
+
function normalize(value) {
|
|
116
|
+
return (value ?? "").trim().toLowerCase().replace(/[_-]+/g, " ");
|
|
117
|
+
}
|
|
118
|
+
function haystack(input) {
|
|
119
|
+
return `${normalize(input.id)} ${normalize(input.label)}`.trim();
|
|
120
|
+
}
|
|
121
|
+
function matchHeuristic(text) {
|
|
122
|
+
if (!text)
|
|
123
|
+
return null;
|
|
124
|
+
if (/\binventory\b|\bstock\b|\bsku\b/.test(text))
|
|
125
|
+
return "inventory";
|
|
126
|
+
if (/\bwaitlist\b|\bsignups?\b|\bcustomers?\b|\bsubscribers?\b/.test(text))
|
|
127
|
+
return "users";
|
|
128
|
+
if (/\bwebhooks?\b|\bhooks?\b/.test(text))
|
|
129
|
+
return "bolt";
|
|
130
|
+
if (/\bdiscounts?\b|\bcoupons?\b|\bpromos?\b|\boffers?\b/.test(text))
|
|
131
|
+
return "tag";
|
|
132
|
+
if (/\bsessions?\b|\bpayments?\b|\bcheckout\b/.test(text))
|
|
133
|
+
return "card";
|
|
134
|
+
if (/\bstripe\b/.test(text))
|
|
135
|
+
return "card";
|
|
136
|
+
if (/\btheme\b|\bbrand(?:ing)?\b|\bappearance\b|\bcolors?\b/.test(text))
|
|
137
|
+
return "palette";
|
|
138
|
+
if (/\bshipping\b|\bfulfill\b|\bdelivery\b|\btruck\b/.test(text))
|
|
139
|
+
return "truck";
|
|
140
|
+
if (/\brefunds?\b|\breturns?\b/.test(text))
|
|
141
|
+
return "sync";
|
|
142
|
+
if (/\bterms\b|\bpolic(?:y|ies)\b|\blegal\b|\bprivacy\b/.test(text))
|
|
143
|
+
return "pages";
|
|
144
|
+
if (/\bproduct page\b|\blayout\b|\bblocks?\b/.test(text))
|
|
145
|
+
return "layout";
|
|
146
|
+
if (/\bpre[- ]?order\b|\bcountdown\b|\bschedule\b|\brelease\b/.test(text))
|
|
147
|
+
return "clock";
|
|
148
|
+
if (/\breviews?\b|\bratings?\b|\btestimonials?\b/.test(text))
|
|
149
|
+
return "star";
|
|
150
|
+
if (/\bannouncements?\b|\bbanners?\b|\bmegaphone\b|\balerts?\b/.test(text))
|
|
151
|
+
return "megaphone";
|
|
152
|
+
if (/\blookbook\b|\bpress\b|\blogos?\b|\bsize guide\b|\bguide\b/.test(text))
|
|
153
|
+
return "list";
|
|
154
|
+
if (/\bback in stock\b|\bfree shipping\b|\bthreshold\b/.test(text))
|
|
155
|
+
return "tag";
|
|
156
|
+
if (/\bemails?\b|\bmail\b|\bcampaigns?\b/.test(text))
|
|
157
|
+
return "mail";
|
|
158
|
+
if (/\bfonts?\b|\btypography\b/.test(text))
|
|
159
|
+
return "fonts";
|
|
160
|
+
if (/\bgallery\b|\bfiles?\b|\bmedia\b|\bimages?\b/.test(text))
|
|
161
|
+
return "gallery";
|
|
162
|
+
if (/\baudit\b|\bmetrics?\b|\banalytics\b/.test(text))
|
|
163
|
+
return "chart";
|
|
164
|
+
if (/\bsync\b|\benvironments?\b/.test(text))
|
|
165
|
+
return "sync";
|
|
166
|
+
if (/\bgeneral\b|\bsettings?\b/.test(text))
|
|
167
|
+
return "settings";
|
|
168
|
+
if (/\bproducts?\b|\bcatalog\b/.test(text))
|
|
169
|
+
return "products";
|
|
170
|
+
if (/\bhome\b|\boverview\b/.test(text))
|
|
171
|
+
return "home";
|
|
172
|
+
if (/\bnav(?:igation)?\b|\bmenu\b/.test(text))
|
|
173
|
+
return "list";
|
|
174
|
+
if (/\babout\b|\bcoming soon\b|\bpages?\b/.test(text))
|
|
175
|
+
return "pages";
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
function matchGroup(group) {
|
|
179
|
+
const g = normalize(group);
|
|
180
|
+
if (!g)
|
|
181
|
+
return null;
|
|
182
|
+
if (g.includes("payment"))
|
|
183
|
+
return "card";
|
|
184
|
+
if (g.includes("marketing") || g.includes("growth") || g.includes("customer"))
|
|
185
|
+
return "users";
|
|
186
|
+
if (g.includes("product") || g.includes("catalog"))
|
|
187
|
+
return "products";
|
|
188
|
+
if (g.includes("online store") ||
|
|
189
|
+
g.includes("pages") ||
|
|
190
|
+
g.includes("storefront"))
|
|
191
|
+
return "pages";
|
|
192
|
+
if (g.includes("setting") || g.includes("general"))
|
|
193
|
+
return "settings";
|
|
194
|
+
if (g.includes("content") || g.includes("media"))
|
|
195
|
+
return "gallery";
|
|
196
|
+
if (g.includes("analytic") || g.includes("home"))
|
|
197
|
+
return "chart";
|
|
198
|
+
if (g.includes("add-on") || g.includes("addon"))
|
|
199
|
+
return "addon";
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
function matchCategory(category) {
|
|
203
|
+
if (category === "product")
|
|
204
|
+
return "products";
|
|
205
|
+
if (category === "engagement")
|
|
206
|
+
return "megaphone";
|
|
207
|
+
if (category === "sales")
|
|
208
|
+
return "tag";
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
function kindDefault(key, section) {
|
|
56
212
|
if (key === "overview")
|
|
57
|
-
return
|
|
213
|
+
return "home";
|
|
58
214
|
if (key === "audit")
|
|
59
|
-
return
|
|
60
|
-
if (key.startsWith("resource:"))
|
|
61
|
-
return IconProducts;
|
|
215
|
+
return "chart";
|
|
62
216
|
if (key === "gallery")
|
|
63
|
-
return
|
|
217
|
+
return "gallery";
|
|
64
218
|
if (key === "fonts")
|
|
65
|
-
return
|
|
219
|
+
return "fonts";
|
|
66
220
|
if (key === "emails")
|
|
67
|
-
return
|
|
221
|
+
return "mail";
|
|
68
222
|
if (key === "sync")
|
|
69
|
-
return
|
|
223
|
+
return "sync";
|
|
70
224
|
if (key === "addons:overview")
|
|
71
|
-
return
|
|
225
|
+
return "addon";
|
|
72
226
|
if (key.startsWith("addon:"))
|
|
73
|
-
return
|
|
74
|
-
if (key.startsWith("
|
|
75
|
-
return
|
|
76
|
-
|
|
227
|
+
return "addon";
|
|
228
|
+
if (key.startsWith("resource:"))
|
|
229
|
+
return "products";
|
|
230
|
+
if (key.startsWith("config:")) {
|
|
231
|
+
if (section === "page")
|
|
232
|
+
return "pages";
|
|
233
|
+
return "settings";
|
|
234
|
+
}
|
|
235
|
+
return "settings";
|
|
236
|
+
}
|
|
237
|
+
/** Resolve a stable icon name for a sidebar entry. */
|
|
238
|
+
export function resolveNavIconName(input) {
|
|
239
|
+
if (input.icon && input.icon in NAV_ICON_COMPONENTS)
|
|
240
|
+
return input.icon;
|
|
241
|
+
const text = haystack(input);
|
|
242
|
+
const fromText = matchHeuristic(text);
|
|
243
|
+
if (fromText)
|
|
244
|
+
return fromText;
|
|
245
|
+
if (input.key.startsWith("addon:") || input.section === "addon") {
|
|
246
|
+
const fromCategory = matchCategory(input.category);
|
|
247
|
+
if (fromCategory)
|
|
248
|
+
return fromCategory;
|
|
249
|
+
}
|
|
250
|
+
const fromGroup = matchGroup(input.group);
|
|
251
|
+
if (fromGroup)
|
|
252
|
+
return fromGroup;
|
|
253
|
+
return kindDefault(input.key, input.section);
|
|
254
|
+
}
|
|
255
|
+
export function resolveNavIcon(input) {
|
|
256
|
+
return NAV_ICON_COMPONENTS[resolveNavIconName(input)];
|
|
257
|
+
}
|
|
258
|
+
/** @deprecated Prefer `resolveNavIcon`. */
|
|
259
|
+
export function iconForNavKey(key) {
|
|
260
|
+
return resolveNavIcon({ key });
|
|
77
261
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FieldSchema, GalleryItem } from "@vertebr/client";
|
|
2
|
+
import { looksLikeUrl } from "./images";
|
|
3
|
+
/** True when a gallery/file item is a PDF (by type, name, or URL). */
|
|
4
|
+
export declare function isPdfAsset(item: {
|
|
5
|
+
url?: string;
|
|
6
|
+
name?: string | null;
|
|
7
|
+
contentType?: string | null;
|
|
8
|
+
}): boolean;
|
|
9
|
+
/** Config string fields meant for a policy/document PDF URL. */
|
|
10
|
+
export declare function isDocumentField(key: string, field?: Pick<FieldSchema, "label" | "type">): boolean;
|
|
11
|
+
export declare function pdfFileName(url: string, fallback?: string): string;
|
|
12
|
+
export declare function filterGalleryByKind(items: GalleryItem[], kind: "image" | "document" | "any"): GalleryItem[];
|
|
13
|
+
export { looksLikeUrl };
|
|
14
|
+
//# sourceMappingURL=documents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/lib/documents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GAAG,OAAO,CAMV;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,CAAC,GAC1C,OAAO,CAUT;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAiB,GAAG,MAAM,CAQ1E;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,WAAW,EAAE,EACpB,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,KAAK,GACjC,WAAW,EAAE,CAIf;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { looksLikeUrl } from "./images";
|
|
2
|
+
/** True when a gallery/file item is a PDF (by type, name, or URL). */
|
|
3
|
+
export function isPdfAsset(item) {
|
|
4
|
+
if (item.contentType && /pdf/i.test(item.contentType))
|
|
5
|
+
return true;
|
|
6
|
+
const name = item.name ?? "";
|
|
7
|
+
if (/\.pdf$/i.test(name))
|
|
8
|
+
return true;
|
|
9
|
+
const url = item.url ?? "";
|
|
10
|
+
return /\.pdf(?:$|\?)/i.test(url);
|
|
11
|
+
}
|
|
12
|
+
/** Config string fields meant for a policy/document PDF URL. */
|
|
13
|
+
export function isDocumentField(key, field) {
|
|
14
|
+
if (field?.type === "file[]")
|
|
15
|
+
return false;
|
|
16
|
+
if (field?.type && field.type !== "string")
|
|
17
|
+
return false;
|
|
18
|
+
const hay = `${key} ${field?.label ?? ""}`;
|
|
19
|
+
if (/\bpdf\b|pdfUrl|pdf_url|documentUrl|document_url/i.test(hay))
|
|
20
|
+
return true;
|
|
21
|
+
// Policy-ish labels only when they also look like a file/URL attachment.
|
|
22
|
+
return (/(policy|legal|terms|privacy|refund|shipping)/i.test(hay) &&
|
|
23
|
+
/(pdf|url|file|attach|document)/i.test(hay));
|
|
24
|
+
}
|
|
25
|
+
export function pdfFileName(url, fallback = "document.pdf") {
|
|
26
|
+
try {
|
|
27
|
+
const path = new URL(url).pathname;
|
|
28
|
+
const base = path.split("/").pop() || fallback;
|
|
29
|
+
return decodeURIComponent(base);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return fallback;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function filterGalleryByKind(items, kind) {
|
|
36
|
+
if (kind === "any")
|
|
37
|
+
return items;
|
|
38
|
+
if (kind === "document")
|
|
39
|
+
return items.filter(isPdfAsset);
|
|
40
|
+
return items.filter((item) => !isPdfAsset(item));
|
|
41
|
+
}
|
|
42
|
+
export { looksLikeUrl };
|
package/dist/styles/form.css
CHANGED
|
@@ -1047,6 +1047,21 @@
|
|
|
1047
1047
|
background: var(--vb-surface-tint);
|
|
1048
1048
|
}
|
|
1049
1049
|
|
|
1050
|
+
.vertebr-md-doc-link {
|
|
1051
|
+
appearance: none;
|
|
1052
|
+
border: 0;
|
|
1053
|
+
padding: 0;
|
|
1054
|
+
background: transparent;
|
|
1055
|
+
color: var(--vb-accent);
|
|
1056
|
+
font: inherit;
|
|
1057
|
+
letter-spacing: inherit;
|
|
1058
|
+
cursor: pointer;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
.vertebr-md-doc-link:hover {
|
|
1062
|
+
text-decoration: underline;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1050
1065
|
.vertebr-field input:hover,
|
|
1051
1066
|
.vertebr-field textarea:hover {
|
|
1052
1067
|
border-color: var(--vb-border-strong);
|