@firecms/core
Version:
Awesome Firebase/Firestore-based headless open-source CMS
1,339 lines • 975 kB
JavaScript
(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