UNPKG

@dotcms/uve

Version:

Official JavaScript library for interacting with Universal Visual Editor (UVE)

410 lines (404 loc) 13.5 kB
'use strict'; var _public = require('./public.cjs.js'); var types = require('@dotcms/types'); require('@dotcms/types/internal'); /** * TinyMCE default config across all versions * * @internal */ const __DEFAULT_TINYMCE_CONFIG__ = { menubar: false, inline: true, valid_styles: { '*': 'font-size,font-family,color,text-decoration,text-align' }, powerpaste_word_import: 'clean', powerpaste_html_import: 'clean', suffix: '.min' // Suffix to use when loading resources }; /** * TinyMCE config to use per mode * * @internal */ const __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__ = { full: { plugins: 'link lists autolink charmap', toolbar: ['styleselect undo redo | bold italic underline | forecolor backcolor | alignleft aligncenter alignright alignfull | numlist bullist outdent indent | hr charmap removeformat | link'], style_formats: [{ title: 'Paragraph', format: 'p' }, { title: 'Header 1', format: 'h1' }, { title: 'Header 2', format: 'h2' }, { title: 'Header 3', format: 'h3' }, { title: 'Header 4', format: 'h4' }, { title: 'Header 5', format: 'h5' }, { title: 'Header 6', format: 'h6' }, { title: 'Pre', format: 'pre' }, { title: 'Code', format: 'code' }] }, plain: { plugins: '', toolbar: '' }, minimal: { plugins: 'link autolink', toolbar: 'bold italic underline | link', valid_elements: 'strong,em,span[style],a[href]' } }; /** * TinyMCE path * * @internal */ const __TINYMCE_PATH_ON_DOTCMS__ = '/ext/tinymcev7/tinymce.min.js'; /** * Normalizes a field definition into the schema format expected by UVE. * * Converts developer-friendly field definitions into a normalized structure where * all type-specific configuration properties are moved into a `config` object. * This transformation ensures consistency in the schema format sent to UVE. * * **Field Type Handling:** * - **Input fields**: Extracts `inputType` and `placeholder` into config * - **Dropdown fields**: Normalizes options (strings become `{ label, value }` objects) * - **Radio fields**: Normalizes options (preserves image properties like `imageURL`, `width`, `height`), * extracts `columns` into config * - **Checkbox group fields**: Normalizes options (converts to format expected by UVE) * * @experimental This method is experimental and may be subject to change. * * @param field - The field definition to normalize. Must be one of: input, dropdown, radio, or checkboxGroup * @returns The normalized field schema with type, label, and config properties ready to be sent to UVE * * @example * ```typescript * // Input field normalization * normalizeField({ * type: 'input', * id: 'font-size', * label: 'Font Size', * inputType: 'number', * placeholder: 'Enter size' * }) * // Returns: { * // type: 'input', * // id: 'font-size', * // label: 'Font Size', * // config: { inputType: 'number', placeholder: 'Enter size' } * // } * * // Dropdown field with string options normalization * normalizeField({ * type: 'dropdown', * label: 'Font Family', * options: ['Arial', 'Helvetica'] * }) * // Returns: { * // type: 'dropdown', * // label: 'Font Family', * // config: { options: [{ label: 'Arial', value: 'Arial' }, { label: 'Helvetica', value: 'Helvetica' }] } * // } * ``` */ function normalizeField(field) { const base = { type: field.type, label: field.label, id: field.id }; const config = {}; // Handle type-specific properties if (field.type === 'input') { config.inputType = field.inputType; config.placeholder = field.placeholder; } if (field.type === 'dropdown' || field.type === 'radio') { // Normalize options to consistent format config.options = field.options.map(opt => typeof opt === 'string' ? { label: opt, value: opt } : opt); // Handle radio-specific properties if (field.type === 'radio') { config.columns = field.columns; } } if (field.type === 'checkboxGroup') { // Normalize checkbox options - convert to format expected by UVE // Options have label and key - convert key to 'value' for UVE format config.options = field.options.map(opt => ({ label: opt.label, value: opt.key // UVE expects 'value' to be the key identifier })); } return { ...base, config }; } /** * Normalizes a section definition into the schema format expected by UVE. * * Converts a section with a flat array of fields into the normalized schema format * where fields are organized as a multi-dimensional array (array of column arrays). * Currently, all sections are normalized to a single-column layout structure. * * **Normalization Process:** * 1. Normalizes each field in the section using `normalizeField` * 2. Wraps the normalized fields array in an outer array to create the column structure * 3. Preserves the section title * * The output format always uses a multi-dimensional array structure (`fields: StyleEditorFieldSchema[][]`), * even for single-column layouts, ensuring consistency in the UVE schema format. * * @experimental This method is experimental and may be subject to change. * * @param section - The section definition to normalize, containing a title and array of fields * @param section.title - The section title displayed to users * @param section.fields - Array of field definitions to normalize * @returns The normalized section schema with fields organized as a single-column array structure * * @example * ```typescript * normalizeSection({ * title: 'Typography', * fields: [ * { type: 'input', label: 'Font Size', inputType: 'number' }, * { type: 'dropdown', label: 'Font Family', options: ['Arial'] } * ] * }) * // Returns: { * // title: 'Typography', * // fields: [ * // [ * // { type: 'input', label: 'Font Size', config: { inputType: 'number' } }, * // { type: 'dropdown', label: 'Font Family', config: { options: [...] } } * // ] * // ] * // } * ``` */ function normalizeSection(section) { // Determine if fields is multi-column or single column const normalizedFields = section.fields.map(normalizeField); return { title: section.title, fields: normalizedFields }; } /** * Normalizes a complete form definition into the schema format expected by UVE. * * This is the main entry point for converting a developer-friendly form definition * into the normalized schema structure that UVE (Universal Visual Editor) can consume. * The normalization process transforms the entire form hierarchy: * * **Normalization Process:** * 1. Preserves the `contentType` identifier * 2. Processes each section using `normalizeSection`, which: * - Normalizes all fields in the section using `normalizeField` * - Organizes fields into the required multi-dimensional array structure * 3. Returns a fully normalized schema with consistent structure across all sections * * The resulting schema has all field-specific properties moved into `config` objects * and all sections using the consistent single-column array structure, regardless * of the input format. * * @experimental This method is experimental and may be subject to change. * * @param form - The complete form definition to normalize * @param form.contentType - The content type identifier this form is associated with * @param form.sections - Array of section definitions, each containing a title and fields * @returns The normalized form schema ready to be sent to UVE, with all fields and sections normalized * * @example * ```typescript * const schema = normalizeForm({ * contentType: 'my-content-type', * sections: [ * { * title: 'Typography', * fields: [ * { type: 'input', id: 'font-size', label: 'Font Size', inputType: 'number' }, * { type: 'dropdown', id: 'font-family', label: 'Font Family', options: ['Arial', 'Helvetica'] } * ] * }, * { * title: 'Colors', * fields: [ * { type: 'input', id: 'primary-color', label: 'Primary Color', inputType: 'text' } * ] * } * ] * }); * // Returns: { * // contentType: 'my-content-type', * // sections: [ * // { * // title: 'Typography', * // fields: [ * // [ * // { type: 'input', id: 'font-size', label: 'Font Size', config: { inputType: 'number' } }, * // { type: 'dropdown', id: 'font-family', label: 'Font Family', config: { options: [...] } } * // ] * // ] * // }, * // { * // title: 'Colors', * // fields: [ * // [ * // { type: 'input', id: 'primary-color', label: 'Primary Color', config: { inputType: 'text' } } * // ] * // ] * // } * // ] * // } * ``` */ function normalizeForm(form) { return { contentType: form.contentType, sections: form.sections.map(normalizeSection) }; } /** * Normalizes and validates a style editor form definition into the schema format * expected by UVE. Used internally by the schema builder. * * @internal */ function defineStyleEditorSchema(form) { return normalizeForm(form); } /** * Registers style editor form schemas with the UVE editor. * * Sends normalized style editor schemas to UVE for registration. * Only registers schemas when UVE is in EDIT mode. * * @internal — called automatically by useEditableDotCMSPage and DotCMSEditablePageService */ function registerStyleEditorSchemas(schemas) { const { mode } = _public.getUVEState() || {}; if (!mode || mode !== types.UVE_MODE.EDIT) { return; } const validatedSchemas = schemas.filter((schema, index) => { if (!schema.contentType) { console.warn(`[registerStyleEditorSchemas] Skipping schema with index [${index}] for not having a contentType`); return false; } return true; }); _public.sendMessageToUVE({ action: types.DotCMSUVEAction.REGISTER_STYLE_SCHEMAS, payload: { schemas: validatedSchemas } }); } /** * Helper functions for creating style editor field definitions. * Used by the dotCMS schema builder UI. * * @experimental This API is experimental and may be subject to change. */ const styleEditorField = { /** * Creates an input field definition. * * @experimental This method is experimental and may be subject to change. */ input: config => ({ type: 'input', ...config }), /** * Creates a dropdown field definition. * * @experimental This method is experimental and may be subject to change. */ dropdown: config => ({ type: 'dropdown', ...config, options: config.options }), /** * Creates a radio button field definition. * * @experimental This method is experimental and may be subject to change. */ radio: config => ({ type: 'radio', ...config, options: config.options }), /** * Creates a checkbox group field definition. * * @experimental This method is experimental and may be subject to change. */ checkboxGroup: config => ({ type: 'checkboxGroup', ...config }) }; exports.CUSTOM_NO_COMPONENT = _public.CUSTOM_NO_COMPONENT; exports.DEVELOPMENT_MODE = _public.DEVELOPMENT_MODE; exports.DOT_SECTION_ID_PREFIX = _public.DOT_SECTION_ID_PREFIX; exports.EMPTY_CONTAINER_STYLE_ANGULAR = _public.EMPTY_CONTAINER_STYLE_ANGULAR; exports.EMPTY_CONTAINER_STYLE_REACT = _public.EMPTY_CONTAINER_STYLE_REACT; exports.END_CLASS = _public.END_CLASS; exports.PRODUCTION_MODE = _public.PRODUCTION_MODE; exports.START_CLASS = _public.START_CLASS; exports.TEMP_EMPTY_CONTENTLET = _public.TEMP_EMPTY_CONTENTLET; exports.TEMP_EMPTY_CONTENTLET_TYPE = _public.TEMP_EMPTY_CONTENTLET_TYPE; exports.__UVE_EVENTS__ = _public.__UVE_EVENTS__; exports.__UVE_EVENT_ERROR_FALLBACK__ = _public.__UVE_EVENT_ERROR_FALLBACK__; exports.combineClasses = _public.combineClasses; exports.computeScrollIsInBottom = _public.computeScrollIsInBottom; exports.createUVESubscription = _public.createUVESubscription; exports.findDotCMSElement = _public.findDotCMSElement; exports.findDotCMSVTLData = _public.findDotCMSVTLData; exports.getClosestDotCMSContainerData = _public.getClosestDotCMSContainerData; exports.getColumnPositionClasses = _public.getColumnPositionClasses; exports.getContainersData = _public.getContainersData; exports.getContentletsInContainer = _public.getContentletsInContainer; exports.getDotCMSContainerData = _public.getDotCMSContainerData; exports.getDotCMSContentletsBound = _public.getDotCMSContentletsBound; exports.getDotCMSPageBounds = _public.getDotCMSPageBounds; exports.getDotContainerAttributes = _public.getDotContainerAttributes; exports.getDotContentletAttributes = _public.getDotContentletAttributes; exports.getUVEState = _public.getUVEState; exports.isValidBlocks = _public.isValidBlocks; exports.readContentletDataset = _public.readContentletDataset; exports.setBounds = _public.setBounds; exports.__BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__ = __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__; exports.__DEFAULT_TINYMCE_CONFIG__ = __DEFAULT_TINYMCE_CONFIG__; exports.__TINYMCE_PATH_ON_DOTCMS__ = __TINYMCE_PATH_ON_DOTCMS__; exports.defineStyleEditorSchema = defineStyleEditorSchema; exports.normalizeForm = normalizeForm; exports.registerStyleEditorSchemas = registerStyleEditorSchemas; exports.styleEditorField = styleEditorField;