@orion-studios/payload-studio 0.3.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/OrionBlocksFieldImpl-QX5GTMQZ.mjs +904 -0
  4. package/dist/admin/client.d.mts +110 -0
  5. package/dist/admin/client.d.ts +110 -0
  6. package/dist/admin/client.js +2853 -0
  7. package/dist/admin/client.mjs +1672 -0
  8. package/dist/admin/index.d.mts +2 -0
  9. package/dist/admin/index.d.ts +2 -0
  10. package/dist/admin/index.js +324 -0
  11. package/dist/admin/index.mjs +13 -0
  12. package/dist/admin.css +327 -0
  13. package/dist/blocks/index.d.mts +3 -0
  14. package/dist/blocks/index.d.ts +3 -0
  15. package/dist/blocks/index.js +844 -0
  16. package/dist/blocks/index.mjs +35 -0
  17. package/dist/chunk-6BWS3CLP.mjs +16 -0
  18. package/dist/chunk-BVN5HKTM.mjs +299 -0
  19. package/dist/chunk-L62FYT57.mjs +829 -0
  20. package/dist/chunk-PGLMIFRY.mjs +209 -0
  21. package/dist/chunk-Q76U4Z53.mjs +249 -0
  22. package/dist/chunk-WLXZDMK3.mjs +154 -0
  23. package/dist/chunk-ZLLNO5FM.mjs +229 -0
  24. package/dist/index-B-5K41Km.d.mts +71 -0
  25. package/dist/index-B-5K41Km.d.ts +71 -0
  26. package/dist/index-BgQSKQKb.d.mts +149 -0
  27. package/dist/index-BgQSKQKb.d.ts +149 -0
  28. package/dist/index-CITGmLG_.d.mts +81 -0
  29. package/dist/index-CITGmLG_.d.ts +81 -0
  30. package/dist/index-DhH3ERbg.d.ts +39 -0
  31. package/dist/index-DtQ0tph5.d.mts +39 -0
  32. package/dist/index-FA2Ep5rj.d.mts +129 -0
  33. package/dist/index-FA2Ep5rj.d.ts +129 -0
  34. package/dist/index.d.mts +7 -0
  35. package/dist/index.d.ts +7 -0
  36. package/dist/index.js +1735 -0
  37. package/dist/index.mjs +23 -0
  38. package/dist/nextjs/index.d.mts +2 -0
  39. package/dist/nextjs/index.d.ts +2 -0
  40. package/dist/nextjs/index.js +244 -0
  41. package/dist/nextjs/index.mjs +15 -0
  42. package/dist/studio/index.d.mts +1 -0
  43. package/dist/studio/index.d.ts +1 -0
  44. package/dist/studio/index.js +169 -0
  45. package/dist/studio/index.mjs +17 -0
  46. package/dist/studio-pages/builder.css +260 -0
  47. package/dist/studio-pages/client.d.mts +23 -0
  48. package/dist/studio-pages/client.d.ts +23 -0
  49. package/dist/studio-pages/client.js +1938 -0
  50. package/dist/studio-pages/client.mjs +1909 -0
  51. package/dist/studio-pages/index.d.mts +2 -0
  52. package/dist/studio-pages/index.d.ts +2 -0
  53. package/dist/studio-pages/index.js +264 -0
  54. package/dist/studio-pages/index.mjs +19 -0
  55. package/package.json +87 -0
@@ -0,0 +1,904 @@
1
+ 'use client';
2
+ "use client";
3
+ import {
4
+ BlockPicker,
5
+ SectionTabs
6
+ } from "./chunk-PGLMIFRY.mjs";
7
+
8
+ // src/admin/components/OrionBlocksFieldImpl.tsx
9
+ import { getTranslation as getTranslation2 } from "@payloadcms/translations";
10
+ import { fieldBaseClass, useDrawerSlug, useModal as useModal2 } from "@payloadcms/ui";
11
+ import { ArrayAction } from "@payloadcms/ui/elements/ArrayAction";
12
+ import { Banner } from "@payloadcms/ui/elements/Banner";
13
+ import { Button } from "@payloadcms/ui/elements/Button";
14
+ import { ClipboardAction } from "@payloadcms/ui/elements/ClipboardAction";
15
+ import { Collapsible } from "@payloadcms/ui/elements/Collapsible";
16
+ import { DraggableSortable } from "@payloadcms/ui/elements/DraggableSortable";
17
+ import { DraggableSortableItem } from "@payloadcms/ui/elements/DraggableSortable/DraggableSortableItem";
18
+ import { DrawerToggler } from "@payloadcms/ui/elements/Drawer";
19
+ import { ErrorPill } from "@payloadcms/ui/elements/ErrorPill";
20
+ import { Pill } from "@payloadcms/ui/elements/Pill";
21
+ import { RenderCustomComponent } from "@payloadcms/ui/elements/RenderCustomComponent";
22
+ import { ShimmerEffect } from "@payloadcms/ui/elements/ShimmerEffect";
23
+ import { useForm, useFormSubmitted } from "@payloadcms/ui/forms/Form";
24
+ import { NullifyLocaleField } from "@payloadcms/ui/forms/NullifyField";
25
+ import { RenderFields } from "@payloadcms/ui/forms/RenderFields";
26
+ import { RowLabel } from "@payloadcms/ui/forms/RowLabel";
27
+ import { useField } from "@payloadcms/ui/forms/useField";
28
+ import { withCondition } from "@payloadcms/ui/forms/withCondition";
29
+ import { FieldDescription } from "@payloadcms/ui/fields/FieldDescription";
30
+ import { FieldError } from "@payloadcms/ui/fields/FieldError";
31
+ import { FieldLabel } from "@payloadcms/ui/fields/FieldLabel";
32
+ import { useThrottledValue } from "@payloadcms/ui/hooks/useThrottledValue";
33
+ import { useConfig } from "@payloadcms/ui/providers/Config";
34
+ import { useDocumentInfo } from "@payloadcms/ui/providers/DocumentInfo";
35
+ import { useLocale } from "@payloadcms/ui/providers/Locale";
36
+ import { useTranslation as useTranslation2 } from "@payloadcms/ui/providers/Translation";
37
+ import { scrollToID } from "@payloadcms/ui/utilities/scrollToID";
38
+ import { Fragment, useCallback, useMemo as useMemo2, useState } from "react";
39
+ import { toast } from "sonner";
40
+
41
+ // src/admin/components/OrionBlocksDrawer.tsx
42
+ import { getTranslation } from "@payloadcms/translations";
43
+ import { Drawer, useModal, useTranslation } from "@payloadcms/ui";
44
+ import { useMemo } from "react";
45
+ import { jsx } from "react/jsx-runtime";
46
+ function normalizeLabel(label, i18n, fallback) {
47
+ if (typeof label === "string" && label.trim().length > 0) {
48
+ return label;
49
+ }
50
+ if (label && typeof label === "object") {
51
+ const translated = getTranslation(label, i18n);
52
+ if (typeof translated === "string" && translated.trim().length > 0) {
53
+ return translated;
54
+ }
55
+ }
56
+ return fallback;
57
+ }
58
+ function getGroupLabel(block, i18n) {
59
+ const group = block.admin?.group;
60
+ return normalizeLabel(group, i18n, "Other");
61
+ }
62
+ function getBlockDescription(block) {
63
+ const custom = block.admin?.custom;
64
+ if (!custom || typeof custom !== "object") {
65
+ return void 0;
66
+ }
67
+ const description = custom.description;
68
+ return typeof description === "string" && description.trim().length > 0 ? description : void 0;
69
+ }
70
+ function OrionBlocksDrawer(props) {
71
+ const { addRow, addRowIndex, blocks, drawerSlug, labels } = props;
72
+ const { closeModal } = useModal();
73
+ const { i18n, t } = useTranslation();
74
+ const groupedBlocks = useMemo(() => {
75
+ const groupMap = /* @__PURE__ */ new Map();
76
+ const allOptions = blocks.map((block) => ({
77
+ slug: block.slug,
78
+ label: normalizeLabel(block.labels?.singular, i18n, block.slug),
79
+ description: getBlockDescription(block),
80
+ imageURL: block.imageURL
81
+ }));
82
+ for (const block of blocks) {
83
+ const groupLabel = getGroupLabel(block, i18n);
84
+ const option = {
85
+ slug: block.slug,
86
+ label: normalizeLabel(block.labels?.singular, i18n, block.slug),
87
+ description: getBlockDescription(block),
88
+ imageURL: block.imageURL
89
+ };
90
+ const existing = groupMap.get(groupLabel);
91
+ if (existing) {
92
+ existing.push(option);
93
+ } else {
94
+ groupMap.set(groupLabel, [option]);
95
+ }
96
+ }
97
+ const groups = [{ label: "All", options: allOptions }];
98
+ const sortedGroupEntries = Array.from(groupMap.entries()).sort(([a], [b]) => a.localeCompare(b));
99
+ for (const [label, options] of sortedGroupEntries) {
100
+ groups.push({ label, options });
101
+ }
102
+ return groups;
103
+ }, [blocks, i18n]);
104
+ return /* @__PURE__ */ jsx(
105
+ Drawer,
106
+ {
107
+ slug: drawerSlug,
108
+ title: t("fields:addLabel", {
109
+ label: normalizeLabel(labels.singular, i18n, "Block")
110
+ }),
111
+ children: /* @__PURE__ */ jsx(
112
+ SectionTabs,
113
+ {
114
+ tabs: groupedBlocks.map((group) => ({
115
+ label: group.label,
116
+ content: /* @__PURE__ */ jsx(
117
+ BlockPicker,
118
+ {
119
+ blocks: group.options,
120
+ onSelect: (slug) => {
121
+ addRow(addRowIndex, slug);
122
+ closeModal(drawerSlug);
123
+ }
124
+ }
125
+ )
126
+ }))
127
+ }
128
+ )
129
+ }
130
+ );
131
+ }
132
+
133
+ // src/admin/components/OrionBlocksFieldImpl.tsx
134
+ import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
135
+ var baseClass = "blocks-field";
136
+ var localStorageClipboardKey = "_payloadClipboard";
137
+ function reduceFormStateByPath({ formState, path, rowIndex }) {
138
+ const filteredState = {};
139
+ const prefix = typeof rowIndex !== "number" ? path : `${path}.${rowIndex}`;
140
+ for (const key in formState) {
141
+ if (!key.startsWith(prefix)) {
142
+ continue;
143
+ }
144
+ const { customComponents: _customComponents, validate: _validate, ...field } = formState[key];
145
+ if (Array.isArray(field.rows)) {
146
+ field.rows = field.rows.map((row) => {
147
+ if (!row || typeof row !== "object") {
148
+ return row;
149
+ }
150
+ const { customComponents: _rowCustomComponents, ...serializableRow } = row;
151
+ return serializableRow;
152
+ });
153
+ }
154
+ filteredState[key] = field;
155
+ }
156
+ return filteredState;
157
+ }
158
+ function mergeFormStateFromClipboard({ dataFromClipboard, formState, path, rowIndex }) {
159
+ const typeFromClipboard = dataFromClipboard.type;
160
+ const clipboardFields = dataFromClipboard.data;
161
+ const pathFromClipboard = dataFromClipboard.path;
162
+ const rowIndexFromClipboard = dataFromClipboard.rowIndex;
163
+ if (!clipboardFields || !pathFromClipboard || !typeFromClipboard) {
164
+ return formState;
165
+ }
166
+ const copyFromField = typeof rowIndexFromClipboard !== "number";
167
+ const pasteIntoField = typeof rowIndex !== "number";
168
+ const fromRowToField = !copyFromField && pasteIntoField;
169
+ const isArray = typeFromClipboard === "array";
170
+ let pathToReplace;
171
+ if (copyFromField && pasteIntoField) {
172
+ pathToReplace = pathFromClipboard;
173
+ } else if (copyFromField) {
174
+ pathToReplace = `${pathFromClipboard}.${rowIndex}`;
175
+ } else {
176
+ pathToReplace = `${pathFromClipboard}.${rowIndexFromClipboard}`;
177
+ }
178
+ let targetSegment;
179
+ if (!pasteIntoField) {
180
+ targetSegment = `${path}.${rowIndex}`;
181
+ } else if (fromRowToField) {
182
+ targetSegment = `${path}.0`;
183
+ } else {
184
+ targetSegment = path;
185
+ }
186
+ if (fromRowToField) {
187
+ const lastRenderedPath = `${path}.0`;
188
+ const rowIDPath = `${pathToReplace}.id`;
189
+ const rowIDField = clipboardFields[rowIDPath];
190
+ const rowIDFromClipboard = rowIDField?.value;
191
+ const pathField = formState[path];
192
+ const existingRows = Array.isArray(pathField?.rows) ? pathField.rows : [];
193
+ formState[path] = {
194
+ ...pathField,
195
+ rows: [
196
+ {
197
+ ...existingRows.length > 0 && isArray ? existingRows[0] : {},
198
+ id: rowIDFromClipboard,
199
+ isLoading: false,
200
+ lastRenderedPath
201
+ }
202
+ ],
203
+ value: 1,
204
+ initialValue: 1,
205
+ disableFormData: true
206
+ };
207
+ for (const fieldPath in formState) {
208
+ if (fieldPath !== path && !fieldPath.startsWith(lastRenderedPath) && fieldPath.startsWith(path)) {
209
+ delete formState[fieldPath];
210
+ }
211
+ }
212
+ }
213
+ for (const clipboardPath in clipboardFields) {
214
+ if (!pasteIntoField && clipboardPath.endsWith(".id") || !clipboardPath.startsWith(pathToReplace)) {
215
+ continue;
216
+ }
217
+ const newPath = clipboardPath.replace(pathToReplace, targetSegment);
218
+ const existing = formState[newPath];
219
+ formState[newPath] = {
220
+ customComponents: isArray ? existing?.customComponents : void 0,
221
+ validate: isArray ? existing?.validate : void 0,
222
+ ...clipboardFields[clipboardPath]
223
+ };
224
+ }
225
+ return formState;
226
+ }
227
+ function clipboardCopy({ getDataToCopy, payload, t }) {
228
+ try {
229
+ const dataToWrite = {
230
+ data: getDataToCopy(),
231
+ ...payload
232
+ };
233
+ localStorage.setItem(localStorageClipboardKey, JSON.stringify(dataToWrite));
234
+ return true;
235
+ } catch {
236
+ return t("error:unableToCopy");
237
+ }
238
+ }
239
+ function clipboardPaste({ onPaste, schemaBlocks, t }) {
240
+ let dataToPaste;
241
+ try {
242
+ const jsonFromClipboard = localStorage.getItem(localStorageClipboardKey);
243
+ if (!jsonFromClipboard) {
244
+ return t("error:invalidClipboardData");
245
+ }
246
+ dataToPaste = JSON.parse(jsonFromClipboard);
247
+ } catch {
248
+ return t("error:invalidClipboardData");
249
+ }
250
+ if (!dataToPaste || dataToPaste.type !== "blocks" || !dataToPaste.path || !dataToPaste.data) {
251
+ return t("error:invalidClipboardData");
252
+ }
253
+ const allowedSlugs = new Set(schemaBlocks.map((block) => block.slug));
254
+ const clipboardBlocks = Array.isArray(dataToPaste.blocks) ? dataToPaste.blocks : [];
255
+ for (const block of clipboardBlocks) {
256
+ if (!allowedSlugs.has(block.slug)) {
257
+ return t("error:invalidClipboardData");
258
+ }
259
+ }
260
+ onPaste(dataToPaste);
261
+ return true;
262
+ }
263
+ function toggleAllRows(rows, collapsed) {
264
+ const updatedRows = rows.map((row) => ({ ...row, collapsed }));
265
+ const collapsedIDs = collapsed ? updatedRows.map((row) => row.id) : [];
266
+ return { collapsedIDs, updatedRows };
267
+ }
268
+ function extractRowsAndCollapsedIDs(rows, rowID, collapsed) {
269
+ const updatedRows = rows.map((row) => row.id === rowID ? { ...row, collapsed } : row);
270
+ const collapsedIDs = updatedRows.filter((row) => row.collapsed).map((row) => row.id);
271
+ return { collapsedIDs, updatedRows };
272
+ }
273
+ function OrionRowActions(props) {
274
+ const {
275
+ addRow,
276
+ blocks,
277
+ copyRow,
278
+ duplicateRow,
279
+ hasMaxRows,
280
+ isSortable,
281
+ labels,
282
+ moveRow,
283
+ pasteRow,
284
+ removeRow,
285
+ rowCount,
286
+ rowIndex,
287
+ drawerSlug
288
+ } = props;
289
+ const { closeModal, openModal } = useModal2();
290
+ const [indexToAdd, setIndexToAdd] = useState(null);
291
+ const addViaDrawer = useCallback(
292
+ (rowToAdd, rowBlockType) => {
293
+ addRow(rowToAdd, rowBlockType);
294
+ closeModal(drawerSlug);
295
+ },
296
+ [addRow, closeModal, drawerSlug]
297
+ );
298
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
299
+ /* @__PURE__ */ jsx2(
300
+ OrionBlocksDrawer,
301
+ {
302
+ addRow: (_, rowBlockType) => {
303
+ addViaDrawer(indexToAdd ?? rowIndex, rowBlockType);
304
+ },
305
+ addRowIndex: indexToAdd ?? rowIndex,
306
+ blocks,
307
+ drawerSlug,
308
+ labels
309
+ }
310
+ ),
311
+ /* @__PURE__ */ jsx2(
312
+ ArrayAction,
313
+ {
314
+ addRow: (index) => {
315
+ setIndexToAdd(index);
316
+ openModal(drawerSlug);
317
+ },
318
+ copyRow,
319
+ duplicateRow,
320
+ hasMaxRows,
321
+ index: rowIndex,
322
+ isSortable,
323
+ moveRow,
324
+ pasteRow,
325
+ removeRow,
326
+ rowCount
327
+ }
328
+ )
329
+ ] });
330
+ }
331
+ function OrionBlockRow(props) {
332
+ const {
333
+ addRow,
334
+ attributes,
335
+ block,
336
+ blocks,
337
+ copyRow,
338
+ duplicateRow,
339
+ drawerSlug,
340
+ errorCount,
341
+ fields,
342
+ hasMaxRows,
343
+ isLoading: isLoadingFromProps,
344
+ isSortable,
345
+ Label,
346
+ labels,
347
+ listeners,
348
+ moveRow,
349
+ parentPath,
350
+ pasteRow,
351
+ path,
352
+ permissions,
353
+ readOnly,
354
+ removeRow,
355
+ row,
356
+ rowCount,
357
+ rowIndex,
358
+ schemaPath,
359
+ setCollapse,
360
+ setNodeRef,
361
+ transform
362
+ } = props;
363
+ const { i18n } = useTranslation2();
364
+ const hasSubmitted = useFormSubmitted();
365
+ const isLoading = useThrottledValue(Boolean(isLoadingFromProps), 500);
366
+ const fieldHasErrors = hasSubmitted && errorCount > 0;
367
+ let blockPermissions;
368
+ if (permissions === true) {
369
+ blockPermissions = true;
370
+ } else {
371
+ const permissionsBlockSpecific = permissions?.blocks?.[block.slug] ?? permissions?.blocks;
372
+ if (permissionsBlockSpecific === true) {
373
+ blockPermissions = true;
374
+ } else if (permissionsBlockSpecific && typeof permissionsBlockSpecific === "object" && "fields" in permissionsBlockSpecific) {
375
+ blockPermissions = permissionsBlockSpecific.fields;
376
+ } else if (permissions && typeof permissions === "object") {
377
+ const hasReadPermission = permissions.read === true;
378
+ const missingCreateOrUpdate = !permissions.create || !permissions.update;
379
+ const hasRestrictiveStructure = hasReadPermission && (missingCreateOrUpdate || Object.keys(permissions).length === 1 && permissions.read === true);
380
+ if (hasRestrictiveStructure) {
381
+ blockPermissions = { read: true };
382
+ }
383
+ }
384
+ }
385
+ return /* @__PURE__ */ jsx2(
386
+ "div",
387
+ {
388
+ id: `${parentPath.split(".").join("-")}-row-${rowIndex}`,
389
+ ref: setNodeRef,
390
+ style: { transform },
391
+ children: /* @__PURE__ */ jsx2(
392
+ Collapsible,
393
+ {
394
+ actions: !readOnly ? /* @__PURE__ */ jsx2(
395
+ OrionRowActions,
396
+ {
397
+ addRow,
398
+ blocks,
399
+ copyRow,
400
+ drawerSlug,
401
+ duplicateRow,
402
+ hasMaxRows,
403
+ isSortable,
404
+ labels,
405
+ moveRow,
406
+ pasteRow,
407
+ removeRow,
408
+ rowCount,
409
+ rowIndex
410
+ }
411
+ ) : void 0,
412
+ className: [
413
+ `${baseClass}__row`,
414
+ fieldHasErrors ? `${baseClass}__row--has-errors` : `${baseClass}__row--no-errors`
415
+ ].filter(Boolean).join(" "),
416
+ collapsibleStyle: fieldHasErrors ? "error" : "default",
417
+ dragHandleProps: isSortable ? {
418
+ id: row.id,
419
+ attributes,
420
+ listeners
421
+ } : void 0,
422
+ header: isLoading ? /* @__PURE__ */ jsx2(ShimmerEffect, { height: "1rem", width: "8rem" }) : /* @__PURE__ */ jsxs("div", { className: `${baseClass}__block-header`, children: [
423
+ /* @__PURE__ */ jsx2(
424
+ RowLabel,
425
+ {
426
+ CustomComponent: Label,
427
+ label: /* @__PURE__ */ jsxs(Fragment2, { children: [
428
+ /* @__PURE__ */ jsx2("span", { className: `${baseClass}__block-number`, children: String(rowIndex + 1).padStart(2, "0") }),
429
+ /* @__PURE__ */ jsx2(
430
+ Pill,
431
+ {
432
+ className: `${baseClass}__block-pill ${baseClass}__block-pill-${row.blockType}`,
433
+ pillStyle: "white",
434
+ size: "small",
435
+ children: getTranslation2(block.labels?.singular, i18n)
436
+ }
437
+ )
438
+ ] }),
439
+ path,
440
+ rowNumber: rowIndex
441
+ }
442
+ ),
443
+ fieldHasErrors && /* @__PURE__ */ jsx2(ErrorPill, { count: errorCount, i18n, withMessage: true })
444
+ ] }),
445
+ isCollapsed: Boolean(row.collapsed),
446
+ onToggle: (collapsed) => {
447
+ setCollapse(row.id, collapsed);
448
+ },
449
+ children: isLoading ? /* @__PURE__ */ jsx2(ShimmerEffect, {}) : /* @__PURE__ */ jsx2(
450
+ RenderFields,
451
+ {
452
+ className: `${baseClass}__fields`,
453
+ fields,
454
+ margins: "small",
455
+ parentIndexPath: "",
456
+ parentPath: path,
457
+ parentSchemaPath: schemaPath,
458
+ permissions: blockPermissions,
459
+ readOnly
460
+ }
461
+ )
462
+ }
463
+ )
464
+ }
465
+ );
466
+ }
467
+ var OrionBlocksFieldConnected = (props) => {
468
+ const { i18n, t } = useTranslation2();
469
+ const fieldFromProps = props.field;
470
+ const {
471
+ name,
472
+ type,
473
+ admin: { className, description, isSortable = true, style } = {},
474
+ blockReferences,
475
+ blocks,
476
+ label,
477
+ labels: labelsFromProps,
478
+ localized,
479
+ maxRows,
480
+ minRows: minRowsProp,
481
+ required
482
+ } = fieldFromProps;
483
+ const pathFromProps = props.path;
484
+ const permissions = props.permissions;
485
+ const readOnly = Boolean(props.readOnly);
486
+ const schemaPathFromProps = props.schemaPath;
487
+ const validate = props.validate;
488
+ const schemaPath = schemaPathFromProps ?? name;
489
+ const minRows = minRowsProp ?? (required ? 1 : 0);
490
+ const { setDocFieldPreferences } = useDocumentInfo();
491
+ const { addFieldRow, dispatchFields, getFields, moveFieldRow, removeFieldRow, replaceState, setModified } = useForm();
492
+ const { code: locale } = useLocale();
493
+ const configContext = useConfig();
494
+ const config = configContext?.config ?? {};
495
+ const localization = config.localization;
496
+ const blocksMap = config.blocksMap ?? {};
497
+ const drawerSlug = useDrawerSlug("orion-blocks-drawer");
498
+ const submitted = useFormSubmitted();
499
+ const labels = {
500
+ plural: t("fields:blocks"),
501
+ singular: t("fields:block"),
502
+ ...labelsFromProps
503
+ };
504
+ const editingDefaultLocale = (() => {
505
+ if (localization && localization.fallback) {
506
+ const defaultLocale = localization.defaultLocale;
507
+ return locale === defaultLocale;
508
+ }
509
+ return true;
510
+ })();
511
+ const memoizedValidate = useCallback(
512
+ (value2, options) => {
513
+ if (!editingDefaultLocale && value2 === null) {
514
+ return true;
515
+ }
516
+ if (typeof validate === "function") {
517
+ return validate(value2, {
518
+ ...options,
519
+ maxRows,
520
+ minRows,
521
+ required
522
+ });
523
+ }
524
+ return true;
525
+ },
526
+ [editingDefaultLocale, maxRows, minRows, required, validate]
527
+ );
528
+ const {
529
+ blocksFilterOptions,
530
+ customComponents: {
531
+ AfterInput,
532
+ BeforeInput,
533
+ Description,
534
+ Error,
535
+ Label
536
+ } = {},
537
+ disabled,
538
+ errorPaths,
539
+ path,
540
+ rows = [],
541
+ showError,
542
+ valid,
543
+ value
544
+ } = useField({
545
+ hasRows: true,
546
+ potentiallyStalePath: pathFromProps,
547
+ validate: memoizedValidate
548
+ });
549
+ const safePath = path ?? pathFromProps ?? name;
550
+ const { clientBlocks, clientBlocksAfterFilter } = useMemo2(() => {
551
+ const resolvedBlocks = [];
552
+ if (!blockReferences) {
553
+ resolvedBlocks.push(...blocks);
554
+ } else {
555
+ for (const blockReference of blockReferences) {
556
+ const block = typeof blockReference === "string" ? blocksMap[blockReference] : blockReference;
557
+ if (block) {
558
+ resolvedBlocks.push(block);
559
+ }
560
+ }
561
+ }
562
+ if (Array.isArray(blocksFilterOptions)) {
563
+ const filteredBlocks = resolvedBlocks.filter((block) => blocksFilterOptions.includes(block.slug));
564
+ return {
565
+ clientBlocks: resolvedBlocks,
566
+ clientBlocksAfterFilter: filteredBlocks
567
+ };
568
+ }
569
+ return {
570
+ clientBlocks: resolvedBlocks,
571
+ clientBlocksAfterFilter: resolvedBlocks
572
+ };
573
+ }, [blockReferences, blocks, blocksFilterOptions, blocksMap]);
574
+ const addRow = useCallback(
575
+ (rowIndex, blockType) => {
576
+ addFieldRow({
577
+ blockType,
578
+ path: safePath,
579
+ rowIndex,
580
+ schemaPath
581
+ });
582
+ setTimeout(() => {
583
+ scrollToID(`${safePath}-row-${rowIndex + 1}`);
584
+ }, 0);
585
+ },
586
+ [addFieldRow, safePath, schemaPath]
587
+ );
588
+ const duplicateRow = useCallback(
589
+ (rowIndex) => {
590
+ dispatchFields({
591
+ type: "DUPLICATE_ROW",
592
+ path: safePath,
593
+ rowIndex
594
+ });
595
+ setModified(true);
596
+ setTimeout(() => {
597
+ scrollToID(`${safePath}-row-${rowIndex + 1}`);
598
+ }, 0);
599
+ },
600
+ [dispatchFields, safePath, setModified]
601
+ );
602
+ const removeRow = useCallback(
603
+ (rowIndex) => {
604
+ removeFieldRow({
605
+ path: safePath,
606
+ rowIndex
607
+ });
608
+ },
609
+ [removeFieldRow, safePath]
610
+ );
611
+ const moveRow = useCallback(
612
+ (moveFromIndex, moveToIndex) => {
613
+ moveFieldRow({
614
+ moveFromIndex,
615
+ moveToIndex,
616
+ path: safePath
617
+ });
618
+ },
619
+ [moveFieldRow, safePath]
620
+ );
621
+ const toggleCollapseAll = useCallback(
622
+ (collapsed) => {
623
+ const { collapsedIDs, updatedRows } = toggleAllRows(rows, collapsed);
624
+ dispatchFields({
625
+ type: "SET_ALL_ROWS_COLLAPSED",
626
+ path: safePath,
627
+ updatedRows
628
+ });
629
+ setDocFieldPreferences(safePath, {
630
+ collapsed: collapsedIDs
631
+ });
632
+ },
633
+ [dispatchFields, rows, safePath, setDocFieldPreferences]
634
+ );
635
+ const setCollapse = useCallback(
636
+ (rowID, collapsed) => {
637
+ const { collapsedIDs, updatedRows } = extractRowsAndCollapsedIDs(rows, rowID, collapsed);
638
+ dispatchFields({
639
+ type: "SET_ROW_COLLAPSED",
640
+ path: safePath,
641
+ updatedRows
642
+ });
643
+ setDocFieldPreferences(safePath, {
644
+ collapsed: collapsedIDs
645
+ });
646
+ },
647
+ [dispatchFields, rows, safePath, setDocFieldPreferences]
648
+ );
649
+ const copyRow = useCallback(
650
+ (rowIndex) => {
651
+ const result = clipboardCopy({
652
+ getDataToCopy: () => reduceFormStateByPath({
653
+ formState: getFields(),
654
+ path: safePath,
655
+ rowIndex
656
+ }),
657
+ payload: {
658
+ type,
659
+ blocks: clientBlocks,
660
+ path: safePath,
661
+ rowIndex
662
+ },
663
+ t
664
+ });
665
+ if (typeof result === "string") {
666
+ toast.error(result);
667
+ } else {
668
+ toast.success(t("general:copied"));
669
+ }
670
+ },
671
+ [clientBlocks, getFields, safePath, t, type]
672
+ );
673
+ const pasteRow = useCallback(
674
+ (rowIndex) => {
675
+ const result = clipboardPaste({
676
+ onPaste: (dataFromClipboard) => {
677
+ const formState = getFields();
678
+ const newState = mergeFormStateFromClipboard({
679
+ dataFromClipboard,
680
+ formState,
681
+ path: safePath,
682
+ rowIndex
683
+ });
684
+ replaceState(newState);
685
+ setModified(true);
686
+ },
687
+ schemaBlocks: clientBlocks,
688
+ t
689
+ });
690
+ if (typeof result === "string") {
691
+ toast.error(result);
692
+ }
693
+ },
694
+ [clientBlocks, getFields, replaceState, safePath, setModified, t]
695
+ );
696
+ const pasteBlocks = useCallback(
697
+ (dataFromClipboard) => {
698
+ const formState = getFields();
699
+ const newState = mergeFormStateFromClipboard({
700
+ dataFromClipboard,
701
+ formState,
702
+ path: safePath
703
+ });
704
+ replaceState(newState);
705
+ setModified(true);
706
+ },
707
+ [getFields, replaceState, safePath, setModified]
708
+ );
709
+ const hasMaxRows = Boolean(maxRows && rows.length >= maxRows);
710
+ const fieldErrorCount = errorPaths.length;
711
+ const fieldHasErrors = submitted && fieldErrorCount + (valid ? 0 : 1) > 0;
712
+ const showMinRows = rows.length < minRows || required && rows.length === 0;
713
+ const showRequired = readOnly && rows.length === 0;
714
+ return /* @__PURE__ */ jsxs(
715
+ "div",
716
+ {
717
+ className: [
718
+ fieldBaseClass,
719
+ baseClass,
720
+ className,
721
+ fieldHasErrors ? `${baseClass}--has-error` : `${baseClass}--has-no-error`
722
+ ].filter(Boolean).join(" "),
723
+ id: `field-${safePath.replace(/\./g, "__")}`,
724
+ style,
725
+ children: [
726
+ showError && /* @__PURE__ */ jsx2(
727
+ RenderCustomComponent,
728
+ {
729
+ CustomComponent: Error,
730
+ Fallback: /* @__PURE__ */ jsx2(FieldError, { path: safePath, showError })
731
+ }
732
+ ),
733
+ /* @__PURE__ */ jsxs("header", { className: `${baseClass}__header`, children: [
734
+ /* @__PURE__ */ jsxs("div", { className: `${baseClass}__header-wrap`, children: [
735
+ /* @__PURE__ */ jsxs("div", { className: `${baseClass}__heading-with-error`, children: [
736
+ /* @__PURE__ */ jsx2("h3", { children: /* @__PURE__ */ jsx2(
737
+ RenderCustomComponent,
738
+ {
739
+ CustomComponent: Label,
740
+ Fallback: /* @__PURE__ */ jsx2(
741
+ FieldLabel,
742
+ {
743
+ as: "span",
744
+ label,
745
+ localized,
746
+ path: safePath,
747
+ required
748
+ }
749
+ )
750
+ }
751
+ ) }),
752
+ fieldHasErrors && fieldErrorCount > 0 && /* @__PURE__ */ jsx2(ErrorPill, { count: fieldErrorCount, i18n, withMessage: true })
753
+ ] }),
754
+ /* @__PURE__ */ jsxs("ul", { className: `${baseClass}__header-actions`, children: [
755
+ rows.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
756
+ /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(
757
+ "button",
758
+ {
759
+ className: `${baseClass}__header-action`,
760
+ onClick: () => toggleCollapseAll(true),
761
+ type: "button",
762
+ children: t("fields:collapseAll")
763
+ }
764
+ ) }),
765
+ /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(
766
+ "button",
767
+ {
768
+ className: `${baseClass}__header-action`,
769
+ onClick: () => toggleCollapseAll(false),
770
+ type: "button",
771
+ children: t("fields:showAll")
772
+ }
773
+ ) })
774
+ ] }),
775
+ /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(
776
+ ClipboardAction,
777
+ {
778
+ allowCopy: rows.length > 0,
779
+ allowPaste: !readOnly,
780
+ blocks: clientBlocks,
781
+ className: `${baseClass}__header-action`,
782
+ disabled,
783
+ getDataToCopy: () => reduceFormStateByPath({
784
+ formState: getFields(),
785
+ path: safePath
786
+ }),
787
+ onPaste: pasteBlocks,
788
+ path: safePath,
789
+ type
790
+ }
791
+ ) })
792
+ ] })
793
+ ] }),
794
+ /* @__PURE__ */ jsx2(
795
+ RenderCustomComponent,
796
+ {
797
+ CustomComponent: Description,
798
+ Fallback: /* @__PURE__ */ jsx2(FieldDescription, { description, path: safePath })
799
+ }
800
+ )
801
+ ] }),
802
+ BeforeInput,
803
+ /* @__PURE__ */ jsx2(NullifyLocaleField, { fieldValue: value, localized, path: safePath, readOnly }),
804
+ (rows.length > 0 || !valid && (showRequired || showMinRows)) && /* @__PURE__ */ jsxs(
805
+ DraggableSortable,
806
+ {
807
+ className: `${baseClass}__rows`,
808
+ ids: rows.map((row) => row.id),
809
+ onDragEnd: ({ moveFromIndex, moveToIndex }) => moveRow(moveFromIndex, moveToIndex),
810
+ children: [
811
+ rows.map((row, index) => {
812
+ const blockType = row.blockType;
813
+ const blockConfig = blocksMap[blockType] ?? clientBlocks.find((block) => block.slug === blockType);
814
+ if (!blockConfig) {
815
+ return null;
816
+ }
817
+ const rowPath = `${safePath}.${index}`;
818
+ const rowErrorCount = errorPaths.filter((errorPath) => errorPath.startsWith(`${rowPath}.`)).length;
819
+ return /* @__PURE__ */ jsx2(DraggableSortableItem, { disabled: readOnly || disabled || !isSortable, id: row.id, children: (draggableSortableItemProps) => /* @__PURE__ */ jsx2(
820
+ OrionBlockRow,
821
+ {
822
+ ...draggableSortableItemProps,
823
+ addRow,
824
+ block: blockConfig,
825
+ blocks: clientBlocks,
826
+ copyRow,
827
+ drawerSlug,
828
+ duplicateRow,
829
+ errorCount: rowErrorCount,
830
+ fields: blockConfig.fields,
831
+ hasMaxRows,
832
+ isLoading: Boolean(row.isLoading),
833
+ isSortable,
834
+ Label: rows[index]?.customComponents?.RowLabel,
835
+ labels,
836
+ moveRow,
837
+ parentPath: safePath,
838
+ pasteRow,
839
+ path: rowPath,
840
+ permissions,
841
+ readOnly: readOnly || Boolean(disabled),
842
+ removeRow,
843
+ row,
844
+ rowCount: rows.length,
845
+ rowIndex: index,
846
+ schemaPath: schemaPath + blockConfig.slug,
847
+ setCollapse
848
+ }
849
+ ) }, row.id);
850
+ }),
851
+ !editingDefaultLocale && /* @__PURE__ */ jsxs(Fragment2, { children: [
852
+ showMinRows && /* @__PURE__ */ jsx2(Banner, { type: "error", children: t("validation:requiresAtLeast", {
853
+ count: minRows,
854
+ label: getTranslation2(minRows > 1 ? labels.plural : labels.singular, i18n) || t(minRows > 1 ? "general:row" : "general:rows")
855
+ }) }),
856
+ showRequired && /* @__PURE__ */ jsx2(Banner, { children: t("validation:fieldHasNo", {
857
+ label: getTranslation2(labels.plural, i18n)
858
+ }) })
859
+ ] })
860
+ ]
861
+ }
862
+ ),
863
+ !hasMaxRows && /* @__PURE__ */ jsxs(Fragment, { children: [
864
+ /* @__PURE__ */ jsx2(DrawerToggler, { className: `${baseClass}__drawer-toggler`, disabled: readOnly || disabled, slug: drawerSlug, children: /* @__PURE__ */ jsx2(
865
+ Button,
866
+ {
867
+ buttonStyle: "icon-label",
868
+ disabled: readOnly || disabled,
869
+ el: "span",
870
+ icon: "plus",
871
+ iconPosition: "left",
872
+ iconStyle: "with-border",
873
+ children: t("fields:addLabel", {
874
+ label: getTranslation2(labels.singular, i18n)
875
+ })
876
+ }
877
+ ) }),
878
+ /* @__PURE__ */ jsx2(
879
+ OrionBlocksDrawer,
880
+ {
881
+ addRow,
882
+ addRowIndex: rows.length || 0,
883
+ blocks: clientBlocksAfterFilter,
884
+ drawerSlug,
885
+ labels
886
+ }
887
+ )
888
+ ] }),
889
+ AfterInput
890
+ ]
891
+ }
892
+ );
893
+ };
894
+ var OrionBlocksFieldComponent = (props) => {
895
+ const configContext = useConfig();
896
+ if (!configContext?.config) {
897
+ return null;
898
+ }
899
+ return /* @__PURE__ */ jsx2(OrionBlocksFieldConnected, { ...props });
900
+ };
901
+ var OrionBlocksFieldImpl = withCondition(OrionBlocksFieldComponent);
902
+ export {
903
+ OrionBlocksFieldImpl
904
+ };