UNPKG

@firecms/core

Version:

Awesome Firebase/Firestore-based headless open-source CMS

1,459 lines 1.63 MB
import { jsx, jsxs, Fragment } from "react/jsx-runtime"; import { c } from "react-compiler-runtime"; import * as React from "react"; import React__default, { useState, useRef, useEffect, useContext, useCallback, useMemo, createElement, createRef, createContext, forwardRef, memo, useLayoutEffect } from "react"; import { getColorSchemeForSeed, CHIP_COLORS, FunctionsIcon, CircleIcon, iconKeys, coolIconKeys, Icon, Tooltip, ErrorIcon, Typography, IconButton, ContentCopyIcon, OpenInNewIcon, DescriptionIcon, cls, Skeleton, Chip, defaultBorderMixin, KeyboardTabIcon, Checkbox, AccountCircleIcon, Markdown, TextareaAutosize, focusedDisabled, MultiSelect, MultiSelectItem, Select, SelectItem, BooleanSwitch, DateTimeField, paperMixin, EditIcon, DoNotDisturbOnIcon, Menu, MenuItem, MoreVertIcon, Badge, SearchBar, CircularProgress, ArrowUpwardIcon, Popover, FilterListIcon, Button, CenteredView, AssignmentIcon, Label, CloseIcon, TextField, BooleanSwitchWithLabel, useOutsideAlerter, Dialog, DialogTitle, DialogContent, DialogActions, FileCopyIcon, DeleteIcon, AddIcon, StarIcon, Collapse, ExpandablePanel, ArrowForwardIcon, Card, cardMixin, cardClickableMixin, Container, getColorSchemeForKey, RefreshIcon, ListIcon, AppsIcon, ViewKanbanIcon, ToggleButtonGroup, ViewColumnIcon, LoadingButton, WarningIcon, KeyboardArrowDownIcon, VisibilityIcon, CheckIcon, CancelIcon, Alert, NotesIcon, InfoIcon, fieldBackgroundMixin, RemoveIcon, fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, ArrowDropDownIcon, TextFieldsIcon, CheckBoxIcon, LooksOneIcon, LooksTwoIcon, Looks3Icon, FormatListBulletedIcon, FormatListNumberedIcon, FormatQuoteIcon, CodeIcon, ImageIcon, TableChartIcon, AutoFixHighIcon, FormatBoldIcon, FormatItalicIcon, FormatUnderlinedIcon, FormatStrikethroughIcon, useInjectStyles, Separator, FilterListOffIcon, SearchIcon, DarkModeIcon, LightModeIcon, BrightnessMediumIcon, LogoutIcon, Avatar, HandleIcon, KeyboardArrowUpIcon, debounce, AutoAwesomeIcon, TranslateIcon, OpenInFullIcon, Sheet, Tab, Tabs, ExpandMoreIcon, ViewStreamIcon, RepeatIcon, BallotIcon, ScheduleIcon, AddLinkIcon, LinkIcon, DriveFolderUploadIcon, UploadFileIcon, NumbersIcon, PersonIcon, ListAltIcon, FlagIcon, MailIcon, HttpIcon, SubjectIcon, ShortTextIcon, MenuIcon, ChevronLeftIcon } from "@firecms/ui"; import { SnackbarProvider as SnackbarProvider$1, useSnackbar } from "notistack"; import hash from "object-hash"; import { getIn, setIn, useFormex, useCreateFormex, Formex, Field } from "@firecms/formex"; import { useLocation, useNavigate, Link, NavLink, Routes, Route, createBrowserRouter, RouterProvider } from "react-router-dom"; import Fuse from "fuse.js"; import equal from "react-fast-compare"; import jsonLogic from "json-logic-js"; import { useTranslation as useTranslation$1, initReactI18next, I18nextProvider } from "react-i18next"; import { format } from "date-fns"; import * as locales from "date-fns/locale"; import useMeasure from "react-use-measure"; import * as yup from "yup"; import { FixedSizeList } from "react-window"; import { useSensors, useSensor, PointerSensor, DndContext, closestCenter, MouseSensor, TouchSensor, KeyboardSensor, pointerWithin, closestCorners, getFirstCollision, rectIntersection, useDroppable, useDndMonitor, MeasuringStrategy, DragOverlay } from "@dnd-kit/core"; import { useSortable, arrayMove as arrayMove$1, SortableContext, horizontalListSortingStrategy, defaultAnimateLayoutChanges, rectSortingStrategy, verticalListSortingStrategy, sortableKeyboardCoordinates } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { restrictToVerticalAxis, restrictToWindowEdges } from "@dnd-kit/modifiers"; import { useDropzone } from "react-dropzone"; import Compressor from "compressorjs"; import i18next from "i18next"; import { autoUpdate, computePosition, offset, flip, shift } from "@floating-ui/dom"; import { NodeSelection, Plugin, PluginKey, TextSelection, EditorState } from "prosemirror-state"; import { Slot } from "@radix-ui/react-slot"; import { Schema, Fragment as Fragment$1, DOMParser, DOMSerializer } from "prosemirror-model"; import { setBlockType, wrapIn, toggleMark, baseKeymap } from "prosemirror-commands"; import { wrapInList, sinkListItem, liftListItem, splitListItem } from "prosemirror-schema-list"; import { tableNodes, addRowBefore, addRowAfter, deleteRow, addColumnBefore, addColumnAfter, deleteColumn, deleteTable, goToNextCell, columnResizing, tableEditing } from "prosemirror-tables"; import { DecorationSet, Decoration, EditorView } from "prosemirror-view"; import { MarkdownParser, MarkdownSerializer, defaultMarkdownParser, defaultMarkdownSerializer } from "prosemirror-markdown"; import markdownIt from "markdown-it"; import markdownItTaskLists from "markdown-it-task-lists"; import markdownItMark from "markdown-it-mark"; import markdownItIns from "markdown-it-ins"; import { keymap } from "prosemirror-keymap"; import { history, redo, undo } from "prosemirror-history"; import { dropPoint } from "prosemirror-transform"; import { gapCursor } from "prosemirror-gapcursor"; import { inputRules, smartQuotes, ellipsis, emDash, InputRule, wrappingInputRule, textblockTypeInputRule } from "prosemirror-inputrules"; import { createRoot } from "react-dom/client"; import { themes, Highlight } from "prism-react-renderer"; import { useLocation as useLocation$1, useBlocker } from "react-router"; import * as Portal from "@radix-ui/react-portal"; const SnackbarProvider = (t0) => { const $ = c(2); const { children } = t0; let t1; if ($[0] !== children) { t1 = /* @__PURE__ */ jsx(SnackbarProvider$1, { maxSnack: 3, autoHideDuration: 3500, children }); $[0] = children; $[1] = t1; } else { t1 = $[1]; } return t1; }; const DEFAULT_MODE_STATE = { mode: "light", setMode: (mode) => { } }; const ModeControllerContext = React__default.createContext(DEFAULT_MODE_STATE); const ModeControllerProvider = ModeControllerContext.Provider; const AuthControllerContext = React__default.createContext({}); const DataSourceContext = React__default.createContext({}); const NavigationContext = React__default.createContext({}); const CustomizationControllerContext = React__default.createContext({}); const SideEntityControllerContext = React__default.createContext({}); const SideDialogsControllerContext = React__default.createContext({}); const AnalyticsContext = React__default.createContext({}); const StorageSourceContext = React__default.createContext({}); const UserConfigurationPersistenceContext = React__default.createContext(void 0); const DialogsControllerContext = React__default.createContext({}); const DialogsProvider = (t0) => { const $ = c(16); const { children } = t0; let t1; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = []; $[0] = t1; } else { t1 = $[0]; } const [dialogEntries, setDialogEntries] = useState(t1); const dialogEntriesRef = useRef(dialogEntries); let t2; if ($[1] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = (newPanels) => { dialogEntriesRef.current = newPanels; setDialogEntries(newPanels); }; $[1] = t2; } else { t2 = $[1]; } const updateDialogEntries = t2; let t3; if ($[2] !== dialogEntries.length) { t3 = () => { if (dialogEntries.length === 0) { return; } const updatedPanels = [...dialogEntriesRef.current.slice(0, -1)]; updateDialogEntries(updatedPanels); }; $[2] = dialogEntries.length; $[3] = t3; } else { t3 = $[3]; } const close = t3; let t4; if ($[4] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = (dialogEntry) => { const updatedPanels_0 = [...dialogEntriesRef.current, dialogEntry]; updateDialogEntries(updatedPanels_0); return { closeDialog: () => { const updatedPanels_1 = dialogEntriesRef.current.filter((e) => e.key !== dialogEntry.key); updateDialogEntries(updatedPanels_1); } }; }; $[4] = t4; } else { t4 = $[4]; } const open = t4; let t5; if ($[5] !== close) { t5 = { open, close }; $[5] = close; $[6] = t5; } else { t5 = $[6]; } let t6; if ($[7] !== close || $[8] !== dialogEntries) { let t72; if ($[10] !== close) { t72 = (entry, i) => /* @__PURE__ */ jsx(entry.Component, { open: true, closeDialog: close, ...entry.props }, `dialog_${i}`); $[10] = close; $[11] = t72; } else { t72 = $[11]; } t6 = dialogEntries.map(t72); $[7] = close; $[8] = dialogEntries; $[9] = t6; } else { t6 = $[9]; } let t7; if ($[12] !== children || $[13] !== t5 || $[14] !== t6) { t7 = /* @__PURE__ */ jsxs(DialogsControllerContext.Provider, { value: t5, children: [ children, t6 ] }); $[12] = children; $[13] = t5; $[14] = t6; $[15] = t7; } else { t7 = $[15]; } return t7; }; const InternalUserManagementContext = React__default.createContext({}); function removeInitialAndTrailingSlashes(s) { return removeInitialSlash(removeTrailingSlash(s)); } function removeInitialSlash(s) { if (s.startsWith("/")) return s.slice(1); else return s; } function removeTrailingSlash(s) { if (s.endsWith("/")) return s.slice(0, -1); else return s; } function addInitialSlash(s) { if (s.startsWith("/")) return s; else return `/${s}`; } function getLastSegment(path) { const cleanPath = removeInitialAndTrailingSlashes(path); if (cleanPath.includes("/")) { const segments = cleanPath.split("/"); return segments[segments.length - 1]; } return cleanPath; } function resolveCollectionPathIds(path, allCollections) { let remainingPath = removeInitialAndTrailingSlashes(path); if (!remainingPath) { return ""; } let currentCollections = allCollections; const resolvedPathParts = []; while (remainingPath.length > 0) { if (!currentCollections || currentCollections.length === 0) { console.warn(`resolveCollectionPathIds: Path structure implies subcollections, but none found before segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`); resolvedPathParts.push(remainingPath); remainingPath = ""; break; } let foundMatch = false; const potentialMatches = currentCollections.flatMap((col) => [{ col, match: col.path }, { col, match: col.id }]).filter((p) => p.match && remainingPath.startsWith(p.match)).sort((a, b) => b.match.length - a.match.length); if (potentialMatches.length > 0) { const { col: foundCollection, match: matchString } = potentialMatches[0]; resolvedPathParts.push(foundCollection.path); remainingPath = removeInitialSlash(remainingPath.substring(matchString.length)); if (remainingPath.length === 0) { foundMatch = true; break; } const idSeparatorIndex = remainingPath.indexOf("/"); let entityId; if (idSeparatorIndex > -1) { entityId = remainingPath.substring(0, idSeparatorIndex); remainingPath = remainingPath.substring(idSeparatorIndex + 1); } else { entityId = remainingPath; remainingPath = ""; console.warn(`resolveCollectionPathIds: Path seems to end with an entity ID "${entityId}" instead of a collection segment in original path "${path}". This might indicate an invalid input path.`); } resolvedPathParts.push(entityId); currentCollections = foundCollection.subcollections; foundMatch = true; if (!currentCollections && remainingPath.length > 0) { console.warn(`resolveCollectionPathIds: Path continues after entity ID "${entityId}", but no subcollections are defined for the preceding collection "${foundCollection.path}" in path "${path}". Appending remaining original path.`); resolvedPathParts.push(remainingPath); remainingPath = ""; break; } } if (!foundMatch) { console.warn(`resolveCollectionPathIds: Collection definition not found for segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`); resolvedPathParts.push(remainingPath); remainingPath = ""; break; } } return resolvedPathParts.join("/"); } function getCollectionByPathOrId(pathOrId, collections) { const subpaths = removeInitialAndTrailingSlashes(pathOrId).split("/"); if (subpaths.length % 2 === 0) { throw Error(`getCollectionByPathOrId: Collection paths must have an odd number of segments: ${pathOrId}`); } const subpathCombinations = getCollectionPathsCombinations(subpaths); let result; for (let i = 0; i < subpathCombinations.length; i++) { const subpathCombination = subpathCombinations[i]; const navigationEntry = collections && collections.sort((a, b) => (a.id ?? "").localeCompare(b.id ?? "")).find((entry) => entry.id === subpathCombination || entry.path === subpathCombination); if (navigationEntry) { if (subpathCombination === pathOrId) { result = navigationEntry; } else if (navigationEntry.subcollections) { const newPath = pathOrId.replace(subpathCombination, "").split("/").slice(2).join("/"); if (newPath.length > 0) result = getCollectionByPathOrId(newPath, navigationEntry.subcollections); } } if (result) break; } return result; } function getCollectionPathsCombinations(subpaths) { const entries = subpaths.length > 0 && subpaths.length % 2 === 0 ? subpaths.splice(0, subpaths.length - 1) : subpaths; const length = entries.length; const result = []; for (let i = length; i > 0; i = i - 2) { result.push(entries.slice(0, i).join("/")); } return result; } function navigateToEntity({ openEntityMode, collection, entityId, copy, path, fullIdPath, selectedTab, sideEntityController, onClose, navigation }) { if (openEntityMode === "side_panel") { sideEntityController.open({ entityId, path, fullIdPath, copy, selectedTab, collection, updateUrl: true, onClose }); } else { let to = navigation.buildUrlCollectionPath(entityId ? `${fullIdPath ?? path}/${entityId}` : fullIdPath ?? path); if (entityId && selectedTab) { to += `/${selectedTab}`; } if (!entityId) { to += "#new"; } if (copy) { to += "#copy"; } navigation.navigate(to); } } class EntityReference { /** * ID of the entity */ id; /** * A string representing the path of the referenced document (relative * to the root of the database). */ path; /** * Optional database ID where the entity is stored (if multiple databases are used) */ databaseId; constructor(id, path, databaseId) { this.id = id; this.path = path; this.databaseId = databaseId; } get pathWithId() { return `${this.path}/${this.id}`; } get pathWithIdAndDatabase() { if (this.databaseId) { if (this.databaseId === "(default)") { return this.pathWithId; } return `${this.databaseId}:::${this.path}/${this.id}`; } return this.pathWithId; } isEntityReference() { return true; } } class GeoPoint { /** * The latitude of this GeoPoint instance. */ latitude; /** * The longitude of this GeoPoint instance. */ longitude; constructor(latitude, longitude) { this.latitude = latitude; this.longitude = longitude; } } class Vector { value; constructor(value) { this.value = value; } } const pick = (obj, ...args) => ({ ...args.reduce((res, key) => ({ ...res, [key]: obj[key] }), {}) }); function isObject(item) { return item && typeof item === "object" && !Array.isArray(item); } function isPlainObject(obj) { if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { return false; } const proto = Object.getPrototypeOf(obj); return proto === Object.prototype; } function mergeDeep(target, source, ignoreUndefined = false) { if (!isObject(target)) { return target; } const output = { ...target }; if (!isObject(source)) { return output; } for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { const sourceValue = source[key]; const outputValue = output[key]; if (ignoreUndefined && sourceValue === void 0) { continue; } if (sourceValue instanceof Date) { output[key] = new Date(sourceValue.getTime()); } else if (Array.isArray(sourceValue)) { if (Array.isArray(outputValue)) { const newArray = []; const maxLength = Math.max(outputValue.length, sourceValue.length); for (let i = 0; i < maxLength; i++) { const sourceItem = sourceValue[i]; const targetItem = outputValue[i]; if (i >= sourceValue.length) { newArray[i] = targetItem; } else if (i >= outputValue.length) { newArray[i] = sourceItem; } else if (sourceItem === null) { newArray[i] = targetItem; } else if (isPlainObject(sourceItem) && isPlainObject(targetItem)) { newArray[i] = mergeDeep(targetItem, sourceItem, ignoreUndefined); } else { newArray[i] = sourceItem; } } output[key] = newArray; } else { output[key] = [...sourceValue]; } } else if (isPlainObject(sourceValue)) { if (isPlainObject(outputValue)) { output[key] = mergeDeep(outputValue, sourceValue, ignoreUndefined); } else { output[key] = sourceValue; } } else if (isObject(sourceValue)) { output[key] = sourceValue; } else { output[key] = sourceValue; } } } return output; } function getValueInPath(o, path) { if (!o) return void 0; if (typeof o === "object") { if (path in o) { return o[path]; } if (path.includes(".") || path.includes("[")) { let pathSegments = path.split(/[.[]/); if (path.includes("[")) { pathSegments = pathSegments.map((segment) => segment.replace("]", "")); } const firstSegment = pathSegments[0]; const isArrayAndIndexExists = Array.isArray(o[firstSegment]) && !isNaN(parseInt(pathSegments[1])); const nextObject = isArrayAndIndexExists ? o[firstSegment][parseInt(pathSegments[1])] : o[firstSegment]; const nextPath = pathSegments.slice(isArrayAndIndexExists ? 2 : 1).join("."); if (nextPath === "") return nextObject; return getValueInPath(nextObject, nextPath); } } return void 0; } function removeInPath(o, path) { let currentObject = { ...o }; const parts = path.split("."); const last = parts.pop(); for (const part of parts) { currentObject = currentObject[part]; } if (last) delete currentObject[last]; return currentObject; } function removeFunctions(o) { if (o === void 0) return void 0; if (o === null) return null; if (typeof o === "object") { if (Array.isArray(o)) { return o.map((v) => removeFunctions(v)); } if (!isPlainObject(o)) { return o; } return Object.entries(o).filter(([_, value]) => typeof value !== "function").map(([key, value]) => { if (Array.isArray(value)) { return { [key]: value.map((v) => removeFunctions(v)) }; } else if (typeof value === "object") { return { [key]: removeFunctions(value) }; } else return { [key]: value }; }).reduce((a, b) => ({ ...a, ...b }), {}); } return o; } function getHashValue(v) { if (!v) return null; if (typeof v === "object") { if ("id" in v) return v.id; else if (v instanceof Date) return v.toLocaleString(); else if (v instanceof GeoPoint) return hash(v); } return hash(v, { ignoreUnknown: true }); } function removeUndefined(value, removeEmptyStrings) { if (typeof value === "function") { return value; } if (Array.isArray(value)) { return value.map((v) => removeUndefined(v, removeEmptyStrings)); } if (typeof value === "object") { if (value === null) return value; if (!isPlainObject(value)) { return value; } const res = {}; Object.keys(value).forEach((key) => { if (!isEmptyObject(value)) { const childRes = removeUndefined(value[key], removeEmptyStrings); const isString = typeof childRes === "string"; const shouldKeepIfString = !removeEmptyStrings || removeEmptyStrings && !isString || removeEmptyStrings && isString && childRes !== ""; if (childRes !== void 0 && !isEmptyObject(childRes) && shouldKeepIfString) res[key] = childRes; } }); return res; } return value; } function removeNulls(value) { if (typeof value === "function") { return value; } if (Array.isArray(value)) { return value.map((v) => removeNulls(v)); } if (typeof value === "object") { if (value === null) return value; if (!isPlainObject(value)) { return value; } const res = {}; Object.keys(value).forEach((key) => { if (value[key] !== null) res[key] = removeNulls(value[key]); }); return res; } return value; } function isEmptyObject(obj) { return obj && Object.getPrototypeOf(obj) === Object.prototype && Object.keys(obj).length === 0; } function removePropsIfExisting(source, comparison) { const isObject2 = (val) => typeof val === "object" && val !== null; const isArray = (val) => Array.isArray(val); if (!isObject2(source) || !isObject2(comparison)) { return source; } const res = isArray(source) ? [...source] : { ...source }; if (isArray(res)) { for (let i = res.length - 1; i >= 0; i--) { if (res[i] === comparison[i]) { res.splice(i, 1); } else if (isObject2(res[i]) && isObject2(comparison[i])) { res[i] = removePropsIfExisting(res[i], comparison[i]); } } } else { Object.keys(comparison).forEach((key) => { if (key in res) { if (isObject2(res[key]) && isObject2(comparison[key])) { res[key] = removePropsIfExisting(res[key], comparison[key]); } else if (res[key] === comparison[key]) { delete res[key]; } } }); } return res; } const DEFAULT_ONE_OF_TYPE = "type"; const DEFAULT_ONE_OF_VALUE = "value"; function isReadOnly(property) { if (property.readOnly) return true; if (property.dataType === "date") { if (property.autoValue) return true; } if (property.dataType === "reference") { return !property.path && !property.Field; } return false; } function isHidden(property) { return typeof property.disabled === "object" && Boolean(property.disabled.hidden); } function isPropertyBuilder(propertyOrBuilder) { return typeof propertyOrBuilder === "function"; } function getDefaultValuesFor(properties) { if (!properties) return {}; return Object.entries(properties).map(([key, property]) => { if (!property) return {}; const value = getDefaultValueFor(property); return value === void 0 ? {} : { [key]: value }; }).reduce((a, b) => ({ ...a, ...b }), {}); } function getDefaultValueFor(property) { if (!property) return void 0; if (isPropertyBuilder(property)) return void 0; if (property.defaultValue || property.defaultValue === null) { return property.defaultValue; } else if (property.dataType === "map" && property.properties) { const defaultValuesFor = getDefaultValuesFor(property.properties); if (Object.keys(defaultValuesFor).length === 0) return void 0; return defaultValuesFor; } else { return getDefaultValueForDataType(property.dataType); } } function getDefaultValueForDataType(dataType) { if (dataType === "string") { return null; } else if (dataType === "number") { return null; } else if (dataType === "boolean") { return false; } else if (dataType === "date") { return null; } else if (dataType === "array") { return []; } else if (dataType === "map") { return {}; } else { return null; } } function updateDateAutoValues({ inputValues, properties, status, timestampNowValue }) { return traverseValuesProperties(inputValues, properties, (inputValue, property) => { if (property.dataType === "date") { if (status === "existing" && property.autoValue === "on_update") { return timestampNowValue; } else if ((status === "new" || status === "copy") && (property.autoValue === "on_update" || property.autoValue === "on_create")) { return timestampNowValue; } else { return inputValue; } } else { return inputValue; } }) ?? {}; } function sanitizeData(values, properties) { const result = values; Object.entries(properties).forEach(([key, property]) => { if (values && values[key] !== void 0) result[key] = values[key]; else if (property.validation?.required) result[key] = null; }); return result; } function getReferenceFrom(entity) { return new EntityReference(entity.id, entity.path, entity.databaseId); } function traverseValuesProperties(inputValues, properties, operation) { const safeInputValues = inputValues ?? {}; const updatedValues = Object.entries(properties).map(([key, property]) => { const inputValue = safeInputValues && safeInputValues[key]; const updatedValue = traverseValueProperty(inputValue, property, operation); if (updatedValue === null) return null; if (updatedValue === void 0) return void 0; return { [key]: updatedValue }; }).reduce((a, b) => ({ ...a, ...b }), {}); const result = mergeDeep(safeInputValues, updatedValues); if (!result || Object.keys(result).length === 0) return void 0; return result; } function traverseValueProperty(inputValue, property, operation) { let value; if (property.dataType === "map" && property.properties) { value = traverseValuesProperties(inputValue, property.properties, operation); } else if (property.dataType === "array") { if (property.of && Array.isArray(inputValue)) { value = inputValue.map((e) => traverseValueProperty(e, property.of, operation)); } else if (property.oneOf && Array.isArray(inputValue)) { const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE; const valueField = property.oneOf?.valueField ?? DEFAULT_ONE_OF_VALUE; value = inputValue.map((e) => { if (e === null) return null; if (typeof e !== "object") return e; const type = e[typeField]; const childProperty = property.oneOf?.properties[type]; if (!type || !childProperty) return e; return { [typeField]: type, [valueField]: traverseValueProperty(e[valueField], childProperty, operation) }; }); } else { value = inputValue; } } else { value = operation(inputValue, property); } return value; } function enumToObjectEntries(enumValues) { if (Array.isArray(enumValues)) { return enumValues; } else { return Object.entries(enumValues).map(([id, value]) => { if (typeof value === "string") { return { id, label: value }; } else { return { ...value, id }; } }); } } function getLabelOrConfigFrom(enumValues, key) { if (key === null || key === void 0) return void 0; return enumValues.find((entry) => String(entry.id) === String(key)); } function getColorScheme(enumValues, key) { const labelOrConfig = getLabelOrConfigFrom(enumValues, key); if (!labelOrConfig?.color) return getColorSchemeForSeed(key.toString()); if (typeof labelOrConfig === "object" && "color" in labelOrConfig) { if (typeof labelOrConfig.color === "string") return CHIP_COLORS[labelOrConfig.color]; if (typeof labelOrConfig.color === "object") return labelOrConfig.color; } return void 0; } function isEnumValueDisabled(labelOrConfig) { return typeof labelOrConfig === "object" && labelOrConfig.disabled; } function buildEnumLabel(labelOrConfig) { if (labelOrConfig === void 0) return void 0; if (typeof labelOrConfig === "object") { return labelOrConfig.label; } else { return labelOrConfig; } } const resolveCollection = ({ collection, path, entityId, values, previousValues, userConfigPersistence, propertyConfigs, ignoreMissingFields = false, authController }) => { const collectionOverride = userConfigPersistence?.getCollectionConfig(path); const storedProperties = getValueInPath(collectionOverride, "properties"); const defaultValues = getDefaultValuesFor(collection.properties); const usedValues = values ?? defaultValues; const usedPreviousValues = previousValues ?? values ?? defaultValues; const resolvedProperties = Object.entries(collection.properties).filter(([, propertyOrBuilder]) => propertyOrBuilder != null).map(([key, propertyOrBuilder]) => { const childResolvedProperty = resolveProperty({ propertyKey: key, propertyOrBuilder, values: usedValues, previousValues: usedPreviousValues, path, entityId, propertyConfigs, ignoreMissingFields, authController }); if (!childResolvedProperty) return {}; return { [key]: childResolvedProperty }; }).filter((a) => a !== null).reduce((a, b) => ({ ...a, ...b }), {}); const properties = mergeDeep(resolvedProperties, storedProperties); const cleanedProperties = Object.entries(properties).filter(([_, property]) => Boolean(property?.dataType)).map(([id, property]) => ({ [id]: property })).reduce((a, b) => ({ ...a, ...b }), {}); return { ...collection, properties: cleanedProperties, originalCollection: collection }; }; function resolveProperty({ propertyOrBuilder, fromBuilder = false, ignoreMissingFields = false, ...props }) { if (propertyOrBuilder !== null && typeof propertyOrBuilder === "object" && "resolved" in propertyOrBuilder) { return propertyOrBuilder; } let resolvedProperty = null; if (!propertyOrBuilder) { return null; } else if (isPropertyBuilder(propertyOrBuilder)) { const path = props.path; if (!path) throw Error("Trying to resolve a property builder without specifying the entity path"); const usedPropertyValue = props.propertyKey ? getIn(props.values, props.propertyKey) : void 0; const result = propertyOrBuilder({ ...props, path, propertyValue: usedPropertyValue, values: props.values ?? {}, previousValues: props.previousValues ?? props.values ?? {} }); if (!result) { return null; } resolvedProperty = resolveProperty({ ...props, propertyOrBuilder: result, fromBuilder: true, ignoreMissingFields }); } else { const property = propertyOrBuilder; if (property.dataType === "map" && property.properties) { const properties = resolveProperties({ ignoreMissingFields, ...props, properties: property.properties }); resolvedProperty = { ...property, resolved: true, fromBuilder, properties }; } else if (property.dataType === "array") { resolvedProperty = resolveArrayProperty({ property, fromBuilder, ignoreMissingFields, ...props }); } else if ((property.dataType === "string" || property.dataType === "number") && property.enumValues) { resolvedProperty = resolvePropertyEnum(property, fromBuilder); } } if (!resolvedProperty) { resolvedProperty = { ...propertyOrBuilder, resolved: true, fromBuilder }; } if (resolvedProperty.propertyConfig && !isDefaultFieldConfigId(resolvedProperty.propertyConfig)) { const cmsFields = props.propertyConfigs; if (!cmsFields && !ignoreMissingFields) { throw Error(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property configs were provided. Use the property 'propertyConfigs' in your app config to provide them`); } const customField = cmsFields?.[resolvedProperty.propertyConfig]; if (!customField) { console.warn(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property config with that key was found. Check the 'propertyConfigs' in your app config`); console.warn("Available property configs", cmsFields); return null; } if (customField.property) { const configPropertyOrBuilder = customField.property; if ("propertyConfig" in configPropertyOrBuilder) { delete configPropertyOrBuilder.propertyConfig; } const customFieldProperty = resolveProperty({ propertyOrBuilder: configPropertyOrBuilder, ignoreMissingFields, ...props }); if (customFieldProperty) { resolvedProperty = mergeDeep(customFieldProperty, resolvedProperty); } } } return resolvedProperty ? { ...resolvedProperty, resolved: true } : null; } function getArrayResolvedProperties({ propertyKey, propertyValue, property, ...props }) { const of = property.of; return Array.isArray(propertyValue) ? propertyValue.map((v, index) => { return resolveProperty({ propertyKey: `${propertyKey}.${index}`, propertyOrBuilder: of, ...props, index }); }).filter((e) => Boolean(e)) : []; } function resolveArrayProperty({ propertyKey, property, ignoreMissingFields = false, ...props }) { const propertyValue = propertyKey ? getIn(props.values, propertyKey) : void 0; if (property.of) { if (Array.isArray(property.of)) { return { ...property, resolved: true, fromBuilder: props.fromBuilder, resolvedProperties: property.of.map((p, index) => { return resolveProperty({ propertyKey: `${propertyKey}.${index}`, propertyOrBuilder: p, ignoreMissingFields, ...props, index }); }) }; } else { const of = property.of; const resolvedProperties = getArrayResolvedProperties({ propertyValue, propertyKey, property, ignoreMissingFields, ...props }); const ofProperty = resolveProperty({ propertyOrBuilder: of, ignoreMissingFields, ...props }); if (!ofProperty && !ignoreMissingFields) throw Error("When using a property builder as the 'of' prop of an ArrayProperty, you must return a valid child property"); return { ...property, resolved: true, fromBuilder: props.fromBuilder, of: ofProperty, resolvedProperties }; } } else if (property.oneOf) { const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE; const resolvedProperties = Array.isArray(propertyValue) ? propertyValue.map((v, index) => { const type = v && v[typeField]; const childProperty = property.oneOf?.properties[type]; if (!type || !childProperty) return null; return resolveProperty({ propertyKey: `${propertyKey}.${index}`, propertyOrBuilder: childProperty, ignoreMissingFields, ...props }); }).filter((e) => Boolean(e)) : []; const properties = resolveProperties({ propertyKey, properties: property.oneOf.properties, ignoreMissingFields, ...props }); return { ...property, resolved: true, oneOf: { ...property.oneOf, properties }, fromBuilder: props.fromBuilder, resolvedProperties }; } else if (!property.Field) { console.error("The array property needs to declare an 'of' or a 'oneOf' property, or provide a custom `Field`", property); return null; } else { return { ...property, resolved: true, fromBuilder: props.fromBuilder }; } } function resolveProperties({ propertyKey, properties, ignoreMissingFields, ...props }) { return Object.entries(properties).map(([key, property]) => { const childResolvedProperty = resolveProperty({ propertyKey: propertyKey ? `${propertyKey}.${key}` : void 0, propertyOrBuilder: property, ignoreMissingFields, ...props }); if (!childResolvedProperty) return {}; return { [key]: childResolvedProperty }; }).filter((a) => a !== null).reduce((a, b) => ({ ...a, ...b }), {}); } function resolvePropertyEnum(property, fromBuilder) { if (typeof property.enumValues === "object") { return { ...property, resolved: true, enumValues: enumToObjectEntries(property.enumValues)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? [], fromBuilder: fromBuilder ?? false }; } return property; } function resolveEnumValues(input) { if (Array.isArray(input)) { return input; } else if (typeof input === "object" && input !== null) { return Object.entries(input).map(([id, value]) => typeof value === "string" ? { id, label: value } : value); } else { return void 0; } } function resolveEntityView(entityView, contextEntityViews) { if (typeof entityView === "string") { return contextEntityViews?.find((entry) => entry.key === entityView); } else { return entityView; } } function resolveEntityAction(entityAction, contextEntityActions) { if (typeof entityAction === "string") { return contextEntityActions?.find((entry) => entry.key === entityAction); } else { return entityAction; } } function resolvedSelectedEntityView(customViews, customizationController, selectedTab, canEdit) { const resolvedEntityViews = customViews ? customViews.map((e) => resolveEntityView(e, customizationController.entityViews)).filter((e) => Boolean(e)) : []; const selectedEntityView = resolvedEntityViews.find((e) => e.key === selectedTab); const selectedSecondaryForm = customViews && resolvedEntityViews.filter((e) => e.includeActions).find((e) => e.key === selectedTab); return { resolvedEntityViews, selectedEntityView, selectedSecondaryForm }; } function getNavigationEntriesFromPath(props) { const { path, collections = [], currentFullPath, currentFullIdPath } = props; const subpaths = removeInitialAndTrailingSlashes(path).split("/"); const subpathCombinations = getCollectionPathsCombinations(subpaths); const result = []; for (let i = 0; i < subpathCombinations.length; i++) { const subpathCombination = subpathCombinations[i]; let collection; collection = collections && collections.find((entry) => entry.id === subpathCombination); if (!collection) { collection = collections && collections.find((entry) => entry.path === subpathCombination); } if (collection) { const collectionPath = currentFullPath && currentFullPath.length > 0 ? currentFullPath + "/" + collection.path : collection.path; const fullIdPath = currentFullIdPath && currentFullIdPath.length > 0 ? currentFullIdPath + "/" + collection.id : collection.id; result.push({ type: "collection", id: collection.id, path: collectionPath, fullPath: collectionPath, fullIdPath, collection }); const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, "")); const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : []; if (nextSegments.length > 0) { const entityId = nextSegments[0]; const fullPath = collectionPath + "/" + entityId; result.push({ type: "entity", entityId, path: collectionPath, fullIdPath, fullPath, parentCollection: collection }); if (nextSegments.length > 1) { const newPath = nextSegments.slice(1).join("/"); if (!collection) { throw Error("collection not found resolving path: " + collection); } const entityViews = collection.entityViews; const customView = entityViews && entityViews.map((entry) => resolveEntityView(entry, props.contextEntityViews)).filter(Boolean).find((entry) => entry.key === newPath); if (customView) { result.push({ type: "custom_view", path: collectionPath, entityId, fullIdPath, fullPath: fullPath + "/" + customView.key, view: customView }); } else if (collection.subcollections) { result.push(...getNavigationEntriesFromPath({ path: newPath, collections: collection.subcollections, currentFullPath: fullPath, currentFullIdPath: fullIdPath, contextEntityViews: props.contextEntityViews })); } } } break; } } return result; } function sortProperties(properties, propertiesOrder) { try { const propertiesKeys = Object.keys(properties); if (!propertiesOrder || propertiesOrder.length === 0) { return propertiesKeys.map((key) => { const property = properties[key]; if (!isPropertyBuilder(property) && property?.dataType === "map" && property.properties) { return { [key]: { ...property, properties: sortProperties(property.properties, property.propertiesOrder) } }; } else { return { [key]: property }; } }).reduce((a, b) => ({ ...a, ...b }), {}); } const validOrderKeys = propertiesOrder.filter((key) => { return !key.includes(".") && properties[key]; }); const processedKeys = new Set(validOrderKeys); const orderedResult = validOrderKeys.map((key) => { const property = properties[key]; if (!isPropertyBuilder(property) && property?.dataType === "map" && property.properties) { return { [key]: { ...property, properties: sortProperties(property.properties, property.propertiesOrder) } }; } else { return { [key]: property }; } }).reduce((a, b) => ({ ...a, ...b }), {}); const missingProperties = propertiesKeys.filter((key) => !processedKeys.has(key)).map((key) => { const property = properties[key]; if (!isPropertyBuilder(property) && property?.dataType === "map" && property.properties) { return { [key]: { ...property, properties: sortProperties(property.properties, property.propertiesOrder) } }; } else { return { [key]: property }; } }).reduce((a, b) => ({ ...a, ...b }), {}); return { ...orderedResult, ...missingProperties }; } catch (e) { console.error("Error sorting properties", e); return properties; } } function resolveDefaultSelectedView(defaultSelectedView, params) { if (!defaultSelectedView) { return void 0; } else if (typeof defaultSelectedView === "string") { return defaultSelectedView; } else { return defaultSelectedView(params); } } const applyPermissionsFunctionIfEmpty = (collections, permissionsBuilder) => { return collections.map((collection) => { if (collection.permissions) { return collection; } return { ...collection, permissions: permissionsBuilder }; }); }; function getLocalChangesBackup(collection) { if (!collection.localChangesBackup) { return "manual_apply"; } return collection.localChangesBackup; } const kebabCaseRegex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g; const toKebabCase = (str) => { const regExpMatchArray = str.match(kebabCaseRegex); if (!regExpMatchArray) return ""; return regExpMatchArray.map((x) => x.toLowerCase()).join("-"); }; const snakeCaseRegex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g; const toSnakeCase = (str) => { const regExpMatchArray = str.match(snakeCaseRegex); if (!regExpMatchArray) return ""; return regExpMatchArray.map((x) => x.toLowerCase()).join("_"); }; function randomString(strLength = 5) { return Math.random().toString(36).slice(2, 2 + strLength); } function randomColor() { return Math.floor(Math.random() * 16777215).toString(16); } function slugify(text, separator = "_", lowercase = true) { if (!text) return ""; const from = "ãàáäâẽèéëêìíïîõòóöôùúüûñç·/_,:;-"; const to = `aaaaaeeeeeiiiiooooouuuunc${separator}${separator}${separator}${separator}${separator}${separator}${separator}`; for (let i = 0, l = from.length; i < l; i++) { text = text.replace(new RegExp(from.charAt(i), "g"), to.charAt(i)); } text = text.toString().replace(/\s+/g, separator).replace(/&/g, separator).replace(/[^\w\\-]+/g, "").replace(new RegExp("\\" + separator + "\\" + separator + "+", "g"), separator).trim().replace(/^\s+|\s+$/g, ""); return lowercase ? text.toLowerCase() : text; } function unslugify(slug) { if (!slug) return ""; if (slug.includes("-") || slug.includes("_") || !slug.includes(" ")) { const result = slug.replace(/[-_]/g, " "); return result.replace(/\w\S*/g, function(txt) { return txt.charAt(0).toUpperCase() + txt.substr(1); }).trim(); } else { return slug.trim(); } } function prettifyIdentifier(input) { if (!input) return ""; let text = input; text = text.replace(/([a-z])([A-Z])|([A-Z])([A-Z][a-z])/g, "$1$3 $2$4"); text = text.replace(/[_-]+/g, " "); const s = text.trim().replace(/\b\w/g, (char) => char.toUpperCase()); console.log("Prettified identifier:", { input, s }); return s; } const defaultDateFormat = "MMMM dd, yyyy, HH:mm:ss"; const COLLECTION_PATH_SEPARATOR = "::"; function stripCollectionPath(path) { return segmentsToStrippedPath(fullPathToCollectionSegments(path)); } function segmentsToStrippedPath(paths) { if (paths.length === 1) return paths[0]; return paths.reduce((a, b) => `${a}${COLLECTION_PATH_SEPARATOR}${b}`); } function fullPathToCollectionSegments(path) { return path.split("/").filter((e, i) => i % 2 === 0); } function serializeRegExp(input) { if (!input) return ""; return input.toString(); } function hydrateRegExp(input) { if (!input) return void 0; const fragments = input.match(/\/(.*?)\/([a-z]*)?$/i); if (fragments) { return new RegExp(fragments[1], fragments[2] || ""); } else { return new RegExp(input, ""); } } function isValidRegExp(input) { const fullRegexp = input.match(/\/((?![*+?])(?:[^\r\n[/\\]|\\.|\[(?:[^\r\n\]\\]|\\.)*])+)\/((?:g(?:im?|mi?)?|i(?:gm?|mg?)?|m(?:gi?|ig?)?)?)/); if (fullRegexp) return true; const simpleRegexp = input.match(/((?![*+?])(?:[^\r\n[/\\]|\\.|\[(?:[^\r\n\]\\]|\\.)*])+)/); return !!simpleRegexp; } const reservedKeys = ["edit", "copy", "delete"]; function mergeEntityActions(currentActions, newActions) { const updatedActions = []; currentActions.forEach((action) => { const newAction = newActions.find((a) => a.key === action.key); if (newAction) { const mergedAction = { ...action, ...newAction }; updatedActions.push(mergedAction); } else { updatedActions.push(action); } }); newActions.forEach((action) => { if (!currentActions.find((a) => a.key === action.key) && (!action.key || !reservedKeys.includes(action.key))) { updatedActions.push(action); } }); return updatedActions; } function useDebouncedCallback(value, callback, immediate, t0) { const $ = c(9); const timeoutMs = t0 === void 0 ? 300 : t0; const pendingUpdate = React__default.useRef(false); let t1; if ($[0] !== callback) { t1 = () => { callback(); pendingUpdate.current = false; }; $[0] = callback; $[1] = t1; } else { t1 = $[1]; } const performUpdate = t1; const handlerRef = React__default.useRef(void 0); let t2; if ($[2] !== immediate || $[3] !== performUpdate || $[4] !== timeoutMs) { t2 = () => { pendingUpdate.current = true; clearTimeout(handlerRef.current); handlerRef.current = setTimeout(performUpdate, timeoutMs); return () => { if (immediate) { performUpdate(); } }; }; $[2] = immediate; $[3] = performUpdate; $[4] = timeoutMs; $[5] = t2; } else { t2 = $[5]; } let t3; if ($[6] !== immediate || $[7] !== value) { t3 = [immediate, value]; $[6] = immediate; $[7] = value; $[8] = t3; } else { t3 = $[8]; } React__default.useEffect(t2, t3); } function isReferenceProperty(authController, propertyOrBuilder, fields) { const resolvedProperty = resolveProperty({ propertyKey: "ignore", // TODO propertyOrBuilder, propertyConfigs: fields, authController }); if (!resolvedProperty) return null; if (resolvedProperty.dataType === "reference") { return true; } if (resolvedProperty.dataType === "array") { if (Array.isArray(resolvedProperty.of)) return false; else return resolvedProperty.of?.dataType === "reference"; } return false; } function getIdIcon(size) { return /* @__PURE__ */ jsx(CircleIcon, { size }); } function getIconForWidget(widget, size) { const Icon2 = widget?.Icon ?? CircleIcon; return /* @__PURE__ */ jsx(Icon2, { size }); } function getIconForProperty(property, size = "small", fields = {}) { if (isPropertyBuilder(property)) { return /* @