UNPKG

@firecms/core

Version:

Awesome Firebase/Firestore-based headless open-source CMS

1,339 lines 975 kB
(function(global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("react-compiler-runtime"), require("react"), require("@firecms/ui"), require("notistack"), require("object-hash"), require("@firecms/formex"), require("react-router-dom"), require("fuse.js"), require("react-fast-compare"), require("date-fns"), require("date-fns/locale"), require("react-use-measure"), require("yup"), require("react-window"), require("@hello-pangea/dnd"), require("react-dropzone"), require("react-image-file-resizer"), require("@firecms/editor"), require("prism-react-renderer"), require("react-router"), require("@radix-ui/react-portal")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "react-compiler-runtime", "react", "@firecms/ui", "notistack", "object-hash", "@firecms/formex", "react-router-dom", "fuse.js", "react-fast-compare", "date-fns", "date-fns/locale", "react-use-measure", "yup", "react-window", "@hello-pangea/dnd", "react-dropzone", "react-image-file-resizer", "@firecms/editor", "prism-react-renderer", "react-router", "@radix-ui/react-portal"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["FireCMS Core"] = {}, global.jsxRuntime, global.reactCompilerRuntime, global.React, global.ui, global.notistack, global.hash, global.formex, global.reactRouterDom, global.Fuse, global.equal, global.dateFns, global.locales, global.useMeasure, global.yup, global.reactWindow, global.dnd, global.reactDropzone, global.Resizer, global.editor, global.prismReactRenderer, global.reactRouter, global.Portal)); })(this, function(exports2, jsxRuntime, reactCompilerRuntime, React, ui, notistack, hash, formex, reactRouterDom, Fuse, equal, dateFns, locales, useMeasure, yup, reactWindow, dnd, reactDropzone, Resizer, editor, prismReactRenderer, reactRouter, Portal) { "use strict"; function _interopNamespaceDefault(e) { const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); if (e) { for (const k in e) { if (k !== "default") { const d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React); const locales__namespace = /* @__PURE__ */ _interopNamespaceDefault(locales); const yup__namespace = /* @__PURE__ */ _interopNamespaceDefault(yup); const Portal__namespace = /* @__PURE__ */ _interopNamespaceDefault(Portal); const SnackbarProvider = (t0) => { const $ = reactCompilerRuntime.c(2); const { children } = t0; let t1; if ($[0] !== children) { t1 = /* @__PURE__ */ jsxRuntime.jsx(notistack.SnackbarProvider, { 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.createContext(DEFAULT_MODE_STATE); const ModeControllerProvider = ModeControllerContext.Provider; const AuthControllerContext = React.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) { const cleanPath = removeInitialAndTrailingSlashes(path); const subpaths = cleanPath.split("/"); if (subpaths.length % 2 === 0) { throw Error(`resolveCollectionPathIds: Collection paths must have an odd number of segments: ${path}`); } const exactMatch = allCollections.find((col) => col.path === cleanPath); if (exactMatch) { return exactMatch.path; } if (subpaths.length === 1) { const aliasedCollection = allCollections.find((col) => col.id === subpaths[0]); return aliasedCollection?.path ?? subpaths[0]; } let matchingCollection; let entityIndex = 1; for (const collection of allCollections) { const pathSegments = collection.path.split("/"); if (pathSegments.length > 1 && subpaths.slice(0, pathSegments.length).join("/") === collection.path) { matchingCollection = collection; entityIndex = pathSegments.length; break; } } if (!matchingCollection) { const matchingCollections = allCollections.filter((col) => col.id === subpaths[0] || col.path === subpaths[0]); if (!matchingCollections.length) { return cleanPath; } matchingCollection = matchingCollections[0]; } const entityId = subpaths[entityIndex]; const remainingPath = subpaths.slice(entityIndex + 1); if (remainingPath.length > 0) { const subcollectionId = remainingPath[0]; const subcollection = matchingCollection.subcollections?.find((subcol) => subcol.id === subcollectionId); if (subcollection) { return `${matchingCollection.path}/${entityId}/${subcollection.path}`; } } if (remainingPath.length === 0) { return `${matchingCollection.path}/${entityId}`; } return `${matchingCollection.path}/${entityId}/${remainingPath.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 ?? 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; constructor(id, path) { this.id = id; this.path = path; } get pathWithId() { return `${this.path}/${this.id}`; } 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 mergeDeep(target, source, ignoreUndefined = false) { const targetIsObject = isObject(target); const output = targetIsObject ? { ...target } : target; if (targetIsObject && isObject(source)) { Object.keys(source).forEach((key) => { const sourceElement = source[key]; if (ignoreUndefined && sourceElement === void 0) { return; } if (sourceElement instanceof Date) { Object.assign(output, { [key]: new Date(sourceElement.getTime()) }); } else if (isObject(sourceElement)) { if (!(key in target)) Object.assign(output, { [key]: sourceElement }); else output[key] = mergeDeep(target[key], sourceElement); } else { Object.assign(output, { [key]: sourceElement }); } }); } 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") { 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") { const res = {}; if (value === null) return value; 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") { const res = {}; if (value === null) return value; 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; } 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, setDateToMidnight }) { return traverseValuesProperties(inputValues, properties, (inputValue, property) => { if (property.dataType === "date") { let resultDate; if (status === "existing" && property.autoValue === "on_update") { resultDate = timestampNowValue; } else if ((status === "new" || status === "copy") && (property.autoValue === "on_update" || property.autoValue === "on_create")) { resultDate = timestampNowValue; } else { resultDate = inputValue; } if (property.mode === "date") resultDate = setDateToMidnight(resultDate); return resultDate; } 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); } function traverseValuesProperties(inputValues, properties, operation) { const updatedValues = Object.entries(properties).map(([key, property]) => { const inputValue = inputValues && inputValues[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 = { ...inputValues, ...updatedValues }; if (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 ui.getColorSchemeForSeed(key.toString()); if (typeof labelOrConfig === "object" && "color" in labelOrConfig) { if (typeof labelOrConfig.color === "string") return ui.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).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 (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 ? formex.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 ? formex.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) { throw Error("The array property needs to declare an 'of' or a 'oneOf' property, or provide a custom `Field`"); } 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 (typeof input === "object") { return Object.entries(input).map(([id, value]) => typeof value === "string" ? { id, label: value } : value); } else if (Array.isArray(input)) { return input; } else { return void 0; } } function resolveEntityView(entityView, contextEntityViews) { if (typeof entityView === "string") { return contextEntityViews?.find((entry) => entry.key === entityView); } else { return entityView; } } 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 } = 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 pathOrAlias = collection.id ?? collection.path; const collectionPath = currentFullPath && currentFullPath.length > 0 ? currentFullPath + "/" + pathOrAlias : pathOrAlias; result.push({ type: "collection", id: collection.id, path: collectionPath, fullPath: collectionPath, 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, 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, fullPath: fullPath + "/" + customView.key, view: customView }); } else if (collection.subcollections) { result.push(...getNavigationEntriesFromPath({ path: newPath, collections: collection.subcollections, currentFullPath: fullPath, contextEntityViews: props.contextEntityViews })); } } } break; } } return result; } function sortProperties(properties, propertiesOrder) { try { const propertiesKeys = Object.keys(properties); const allPropertiesOrder = propertiesOrder ?? propertiesKeys; return allPropertiesOrder.map((key) => { if (properties[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 }; } } else { return void 0; } }).filter((a) => a !== void 0).reduce((a, b) => ({ ...a, ...b }), {}); } 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 }; }); }; 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(); } } 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 $ = reactCompilerRuntime.c(9); const timeoutMs = t0 === void 0 ? 300 : t0; const pendingUpdate = React.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.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.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__ */ jsxRuntime.jsx(ui.CircleIcon, { size }); } function getIconForWidget(widget, size) { const Icon = widget?.Icon ?? ui.CircleIcon; return /* @__PURE__ */ jsxRuntime.jsx(Icon, { size }); } function getIconForProperty(property, size = "small", fields = {}) { if (isPropertyBuilder(property)) { return /* @__PURE__ */ jsxRuntime.jsx(ui.FunctionsIcon, { size }); } else { const widget = getFieldConfig(property, fields); return getIconForWidget(widget, size); } } function getColorForProperty(property, fields) { if (isPropertyBuilder(property)) { return "#888"; } else { const widget = getFieldConfig(property, fields); return widget?.color ?? "#666"; } } function getPropertyInPath(properties, path) { if (typeof properties === "object") { if (path in properties) { return properties[path]; } if (path.includes(".")) { const pathSegments = path.split("."); const childProperty = properties[pathSegments[0]]; if (typeof childProperty === "object" && childProperty.dataType === "map" && childProperty.properties) { return getPropertyInPath(childProperty.properties, pathSegments.slice(1).join(".")); } } } return void 0; } function getResolvedPropertyInPath(properties, path) { if (typeof properties === "object") { if (path in properties) { return properties[path]; } if (path.includes(".")) { const pathSegments = path.split("."); const childProperty = properties[pathSegments[0]]; if (childProperty.dataType === "map" && childProperty.properties) { return getResolvedPropertyInPath(childProperty.properties, pathSegments.slice(1).join(".")); } } } return void 0; } function getBracketNotation(path) { return path.replace(/\.([^.]*)/g, "[$1]"); } function getPropertiesWithPropertiesOrder(properties, propertiesOrder) { if (!propertiesOrder) return properties; const result = {}; propertiesOrder.filter(Boolean).forEach((path) => { const property = getPropertyInPath(properties, path); if (typeof property === "object" && property.dataType === "map" && property.properties) { result[path] = { ...property, properties: getPropertiesWithPropertiesOrder(property.properties, property.propertiesOrder ?? []) }; } if (property) { result[path] = property; } }); return result; } function getDefaultPropertiesOrder(collection) { if (collection.propertiesOrder) return collection.propertiesOrder; return [...Object.keys(collection.properties), ...(collection.additionalFields ?? []).map((field) => field.key)]; } const DEFAULT_PERMISSIONS = { read: true, edit: true, create: true, delete: true }; function resolvePermissions(collection, authController, path, entity) { const permission = collection.permissions; if (permission === void 0) { return DEFAULT_PERMISSIONS; } else if (typeof permission === "object") { return permission; } else if (typeof permission === "function") { const pathSegments = fullPathToCollectionSegments(path); return permission({ entity, path, user: authController.user, authController, collection, pathSegments }); } console.error("Permissions:", permission); throw Error("New type of permission added and not mapped"); } function canEditEntity(collection, authController, path, entity) { return resolvePermissions(collection, authController, path, entity)?.edit ?? DEFAULT_PERMISSIONS.edit; } function canCreateEntity(collection, authController, path, entity) { if (collection.collectionGroup) return false; return resolvePermissions(collection, authController, path, entity)?.create ?? DEFAULT_PERMISSIONS.create; } function canDeleteEntity(collection, authController, path, entity) { return resolvePermissions(collection, authController, path, entity)?.delete ?? DEFAULT_PERMISSIONS.delete; } const iconSynonyms = { abc: "alphabet character font letter symbol text type", access_alarm: "clock time", access_alarms: "clock time", accessibility: "accessible body handicap help human people person user", accessibility_new: "accessible arms body handicap help human people person user", accessible: "accessibility body handicap help human people person user wheelchair", accessible_forward: "accessibility body handicap help human people person wheelchair", access_time: "clock time", account_balance: "bank bill building card cash coin commerce court credit currency dollars finance money online payment structure temple transaction", account_balance_wallet: "bank bill card cash coin commerce credit currency dollars finance money online payment transaction", account_box: "avatar face human people person profile square thumbnail user", account_circle: "avatar face human people person profile thumbnail user", account_tree: "analytics chart connect data diagram flow infographic measure metrics process project sitemap square statistics structure tracking", ac_unit: "air cold conditioner freeze snowflake temperature weather winter", adb: "android bridge debug", add: "+ create item new plus symbol", add_alarm: "clock plus time", add_alert: "+ active alarm announcement bell callout chime information new notifications notify plus reminder ring sound symbol", add_a_photo: "+ camera lens new photography picture plus symbol", add_box: "create new plus square symbol", add_business: "+ bill building card cash coin commerce company credit currency dollars market money new online payment plus retail shopping storefront symbol", add_card: "+ bill cash coin commerce cost credit currency dollars finance money new online payment plus price shopping symbol", add_chart: "+ analytics bars data diagram infographic measure metrics new plus statistics symbol tracking", add_circle: "+ create new plus", add_circle_outline: "+ create new plus", add_comment: "+ bubble chat communicate feedback message new plus speech symbol", add_ic_call: "+ cell contact device hardware mobile new plus symbol telephone", add_link: "attach clip new plus symbol", add_location: "+ destination direction gps maps new pin place plus stop symbol", add_location_alt: "+ destination direction maps new pin place plus stop symbol", add_moderator: "+ certified new plus privacy private protection security shield symbol verified", add_photo_alternate: "+ image landscape mountains new photography picture plus symbol", add_road: "+ destination direction highway maps new plus stop street symbol traffic", add_shopping_cart: "card cash checkout coin commerce credit currency dollars money online payment plus", add_task: "+ approve check circle completed increase mark ok plus select tick yes", add_to_drive: "+ app backup cloud data files folders gdrive google plus recovery storage", add_to_home_screen: "Android add arrow cell device hardware iOS mobile phone tablet to up", add_to_photos: "collection image landscape mountains photography picture plus", add_to_queue: "+ Android backlog chrome desktop device display hardware iOS lineup mac monitor new plus screen symbol television watch web window", adf_scanner: "document feeder machine office", adjust: "alter center circles control dot edit filter fix image mix move setting slider sort switch target tune", admin_panel_settings: "account avatar certified face human people person privacy private profile protection security shield user verified", ad_units: "Android banner cell device hardware iOS mobile notifications phone tablet top", agriculture: "automobile cars cultivation farm harvest maps tractor transport travel truck vehicle", air: "blowing breeze flow wave weather wind", airlines: "airplane airport flight transportation travel trip", airline_seat_flat: "bed body business class first human people person rest sleep travel", airline_seat_flat_angled: "bed body business class first human people person rest sleep travel", airline_seat_individual_suite: "bed body business class first human people person rest sleep travel", airline_seat_legroom_extra: "body feet human people person sitting space travel", airline_seat_legroom_normal: "body feet human people person sitting space travel", airline_seat_legroom_reduced: "body feet human people person sitting space travel", airline_se