UNPKG

q42-cms-components

Version:

Front-end package that provides a UI on top of the QMS back-end

194 lines (174 loc) 6.41 kB
import moment from 'moment'; import { crudStores } from '../tools/crud-stores.js'; export function initialValue(schema, isProp) { if (schema.default) { return schema.default; } if (schema.enum) { return schema.enum[0]; } switch (schema.type) { case 'string': if (!schema.required) return null; switch (schema.format) { case 'datetime-picker': return new Date(); default: return ''; } case 'integer': case 'number': if (!schema.required) { return null; } return 0; case 'boolean': return false; case 'null': return null; case 'array': return schema.minLength && !schema.items.oneOf && !schema.items.$ref ? Array(schema.minLength).fill(0).map(() => initialValue(schema.items)) : []; case 'object': default: { if (!schema.required && isProp) { return null; } if (schema.oneOf && isProp) { return null; } if (schema.$ref && isProp) { return null; } if (!schema.properties) { console.error(`Missing initializer for type ${schema.type}`, schema); throw `Missing initialiser for type ${schema.type}`; } const value = {$type: null}; // $type needs to be the first prop in the object, to be compatible with the backend parser for (const p in schema.properties) { value[p] = initialValue(schema.properties[p], true); } // Objects that are created need to be expanded by default. The validation only kicks in when their schema-page-component // is expanded. Objects with isNew will expand when they're just created. // This works for nested objects as well as array items that are automatically created. // The isNew property is not saved to the backend. value.isNew = true; return value; } } } export function initialWithQuery(schema, query) { const initial = initialValue(schema); if (query instanceof Object) { for (const propKey in query) { // We support propName.nestedProp syntax in query string, so we need to get a reference to the containing object. const result = getContainingObj(schema, initial, propKey); if (result) { const { containingObj, propName, propSchema } = result; // alters some property initial containingObj[propName] = convertQueryValue(propSchema, query[propKey]); } } } return initial; } function convertQueryValue(propSchema, queryValue) { switch (propSchema.type) { case 'string': if (propSchema.format === 'datetime-picker') { return moment(queryValue).toDate(); } return queryValue; case 'boolean': return queryValue.toLowerCase() === 'true'; case 'number': return Number.parseFloat(queryValue); case 'array': console.warn('Ignoring query string value for array', queryValue); return []; default: console.warn(`Ignoring query string value for ${propSchema.type}`, queryValue); return null; } } function getContainingObj(schema, obj, propKey) { if (schema.type !== 'object' || !schema.properties) { return null; } const propPath = propKey.split('.'); let containingObj = obj; let propName = propPath.shift(); if (!(propName in schema.properties)) { return null; } let propSchema = schema.properties[propName]; // check if we can find the propKey in the schema // if not, we'll skip the property while (propPath.length) { const nextPropName = propPath.shift(); switch (propSchema.type) { case 'object': if (!(nextPropName in propSchema.properties)) { return null; } if (containingObj[propName] === null || containingObj[propName] === undefined) { containingObj[propName] = containingObj[propName] || initialValue(propSchema); } else if (!(containingObj[propName] instanceof Object)) { return null; } containingObj = containingObj[propName]; propSchema = propSchema.properties[nextPropName]; break; default: console.warn(`unsupported schema type ${schema.type} at ${propName} in ${propKey}`); return null; } propName = nextPropName; } return { containingObj, propName, propSchema, }; } export function isSimpleProp(schema) { // null check for schema.items is for the case List<Guid> with [MappedTo(typeof([CmsItem]))] which has schema.type - array, but doesn't have items. return !(schema.type === 'object' || (schema.type === 'array' && schema.items && schema.items.type === 'object')); } export function getNameForRef($ref, guid) { const store = crudStores.getStoreBySchemaRef($ref); const item = store.indexedItems[guid]; return item ? item[store.displayField] : guid; } // gets the first usable string to display as the title of the component export function getComponentTitle(component, schemaProperties) { for (const propName in schemaProperties) { const propSchema = schemaProperties[propName]; if (!propSchema.isHidden && propSchema.type === 'string') { const titleValue = component[propName]; // the value of the current string property // if cms item, where titleValue is a single Guid. We like to show a friendly name if (propSchema.$ref) { return getNameForRef(propSchema.$ref, titleValue); } // if the property has options it's an enum, we don't want to show the raw enum value. We'll look for displayName if it's there. else if (propSchema.options) { // look for the currently selected option const activeOption = propSchema.options.find(option => option.key === titleValue); // if the option is found use it's displayName, or just the raw value if displayName isn't used. return (activeOption && activeOption.displayName) || titleValue; } else { return titleValue; } } } } export function getCmsPageDisplayTitle(version) { return version.page && version.page.url || version.metaTitle; } export function getCmsItemDisplayTitle(item) { return item.title || item.name; }