@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
176 lines (174 loc) • 6.63 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { createLookupTable } from './object';
import Jabber from 'jabber';
import { generatePlaceholderImageDataUrl } from './content';
import { toColor } from './string';
// TODO: Not used.
export function getRelatedContentTypeIds(contentType) {
return Object.values(contentType.fields).reduce((accumulator, field) => {
if (
field.type === 'array' &&
field.validations != null &&
'validations' in field &&
'allowedContentTypes' in field.validations
) {
Object.keys(field.validations.allowedContentTypes.value).forEach(
(ctid) => !accumulator.includes(ctid) && accumulator.push(ctid)
);
}
return accumulator;
}, []);
}
export function isGroupItem(contentType, fieldId) {
return fieldId.includes('.');
}
export function isComponentHolder(contentType, fieldId) {
return getField(contentType, fieldId).type === 'node-selector';
}
export function isGroup(contentType, fieldId) {
return getField(contentType, fieldId).type === 'repeat';
}
export function doesFieldAccept(contentType, fieldId) {
// const field = ContentType.getField(contentType, fieldId);
throw new Error('[doesFieldAccept] Not implemented');
}
export function getField(type, fieldId, contentTypes) {
const fields = fieldId.split('.');
let accumulator = Array.isArray(type.fields) ? createLookupTable(type.fields) : type.fields;
let parsedFieldId = [];
fields.forEach((field) => {
parsedFieldId.push(field);
if (accumulator.type === 'node-selector') {
if (!contentTypes) {
throw new Error(
`Content types not provided to content type helper \`getField\` method. ` +
`Unable to retrieve the field \`${fieldId}\` without full list of content types.`
);
}
const contentTypeWithTargetFieldId = Object.keys(accumulator.validations.allowedContentTypes.value).find((ct) =>
Boolean(contentTypes[ct].fields[field])
);
accumulator = contentTypes[contentTypeWithTargetFieldId].fields[field];
} else {
if (accumulator.type === 'repeat') {
// For repeat groups, the field inside the repeat group field will be
// under {repeatName}.fields.{fieldName}. To abstract this complexity from devs
// we parse it here.
accumulator = accumulator.fields[field];
} else {
accumulator = accumulator[field];
}
}
});
return accumulator;
}
export function getFields(type, ...ids) {
return ids.map((id) => getField(type, id));
}
export function getFieldsByType(contentType, fieldType) {
return Object.values(contentType.fields).filter((field) => field.type === fieldType);
}
// TODO: See variable.js for additional case items to possibly include here
export function getDefaultValue(field) {
if (field.defaultValue) {
return field.defaultValue;
} else if (field.validations.required?.value) {
switch (field.type) {
case 'image': {
const width = field.validations.width?.value ?? field.validations.minWidth?.value ?? 150;
const height = field.validations.height?.value ?? field.validations.minHeight?.value ?? width;
return generatePlaceholderImageDataUrl({
width,
height,
font: `24px Arial`,
text: width > 100 ? `${width} x ${height}` : ':)',
textPositionY: height / 2,
textPositionX: width / 2
});
}
case 'text':
case 'textarea': {
const maxLength = parseInt(field.validations.maxLength?.value);
const textGen = new Jabber();
return maxLength
? `${textGen.createParagraph(50).substring(0, maxLength)}.`.replace(/\.+/, '.')
: textGen.createParagraph(10);
}
case 'html': {
const textGen = new Jabber();
return textGen.createParagraph(10);
}
case 'numeric-input': {
return field.validations.minValue?.value ?? 1;
}
case 'boolean': {
return 'false';
}
case 'date-time': {
return new Date().toISOString();
}
case 'repeat': {
const repeat = [];
if (field.validations.minCount && field.fields) {
new Array(field.validations.minCount).fill(null).forEach(() => {
const item = {};
for (const subFieldId in field.fields) {
item[subFieldId] = getDefaultValue(field.fields[subFieldId]);
}
repeat.push(item);
});
}
return repeat;
}
case 'node-selector':
// TODO: CHECK MIN/MAX COUNT
return [];
default: {
return null;
}
}
}
}
// TODO: This could be used to more generic location. What it does is not specific to content types.
/**
* It takes a string and generates a colour and contrast colour for it.
* Applies a darkening effect when the theme is dark.
*/
export function getAvatarWithIconColors(colourBaseString, theme, darkenFn) {
if (!(colourBaseString && theme && darkenFn)) {
return { backgroundColor: '', textColor: '' };
}
const base = toColor(colourBaseString);
const backgroundColor = theme.palette.mode === 'dark' ? darkenFn(base, 0.2) : base;
const textColor = theme.palette.getContrastText(base);
return { backgroundColor, textColor };
}