UNPKG

@browser.style/data-entry

Version:

Dynamic data entry form component with JSON schema validation and internationalization support

215 lines (184 loc) 5.7 kB
function isLikelyUrl(value) { const urlPattern = /^(http|https):\/\/[^\s$.?#].[^\s]*$/; return urlPattern.test(value); } function isLikelySkuOrId(value) { const noSpaces = !/\s/.test(value); const alphanumeric = /^[a-zA-Z0-9]+$/.test(value); return noSpaces && alphanumeric; } function isLikelyImageUrl(value) { const imagePattern = /\.(jpeg|jpg|gif|png|svg)$/; return imagePattern.test(value); } export function generateRenderMethod(type, key, value) { const baseAttributes = [{ name: key }]; const iso8601DateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[\+\-]\d{2}:\d{2})?$/; if (type === 'string') { if (iso8601DateRegex.test(value)) { return { method: 'input', attributes: baseAttributes.concat([{ type: 'datetime-local' }, { placeholder: 'Enter date and time' }]) }; } if (isLikelyUrl(value)) { if (isLikelyImageUrl(value)) { return { method: 'img', attributes: baseAttributes.concat([{ alt: 'none' }]) }; } else { return { method: 'input', attributes: baseAttributes.concat([{ type: 'url' }, { placeholder: 'Enter URL' }]) }; } } if (isLikelySkuOrId(value)) { return { method: 'input', attributes: baseAttributes.concat([{ type: 'text' }, { placeholder: `Enter ${key}` }]) }; } if (value.length >= 100) { return { method: 'richtext', attributes: [{ toolbar: 'h1,h2,h3|b,i,u,s|sub,sup|ol,ul,hr|img|link,unlink' }] }; } return { method: 'input', attributes: baseAttributes.concat([{ type: 'text' }, { placeholder: `Enter ${key}` }]) }; } if (type === 'number') { return { method: 'input', attributes: baseAttributes.concat([{ type: 'number' }, { placeholder: `Enter ${key}` }]) }; } if (type === 'boolean') { return { method: 'input', attributes: baseAttributes.concat([{ type: 'checkbox' }]) }; } return { method: 'input', attributes: baseAttributes.concat([{ type: 'text' }, { placeholder: `Enter ${key}` }]) }; } export function generateSchemaFromData(data, disabledKeys = [], toolbar = null, schemaId = 'https://json-schema.org/draft/2020-12/output/schema', schemaUri = 'https://json-schema.org/draft/2020-12/schema') { const schema = { $schema: schemaUri, $id: schemaId, type: 'object', default: {}, properties: {}, required: [], }; if (toolbar) { schema.render = { toolbar }; } for (const [key, value] of Object.entries(data)) { let type = typeof value; const render = generateRenderMethod(type, key, value); if (value === null) { // If the value is null, set type to ["object", "null"] type = ["object", "null"]; } else if (type === 'object' && !Array.isArray(value)) { schema.properties[key] = { type: 'object', title: key, properties: generateSchemaFromData(value, disabledKeys).properties, required: Object.keys(value), }; continue; } else if (Array.isArray(value)) { const itemSchema = generateSchemaFromData(value[0] || {}, disabledKeys); const isMediaArray = value.some(item => isLikelyImageUrl(item.url || item)); render.summary = 'LABEL'; render.label = 'VALUE'; // Add property attributes to each item under properties for (const [itemKey, itemValue] of Object.entries(itemSchema.properties)) { if (!itemValue.property) { itemValue.property = itemKey.toLowerCase(); // Example logic to set property } } schema.properties[key] = { type: 'array', title: toTitleCase(key), items: { type: 'object', properties: itemSchema.properties, required: Object.keys(itemSchema.properties), }, render: { method: isMediaArray ? 'media' : 'array', attributes: [{ part: isMediaArray ? 'media' : 'array' }], summary: render.summary, label: render.label, }, }; continue; } const propertyObject = { type: type, // Use the updated type, which can now be ["object", "null"] title: toTitleCase(key), render, }; if (isLikelyImageUrl(value)) { propertyObject.property = 'src'; } // Check if the key is in the disabledKeys array if (disabledKeys.includes(key)) { propertyObject.render.attributes = propertyObject.render.attributes || []; propertyObject.render.attributes.push({ disabled: "disabled" }); } schema.properties[key] = propertyObject; schema.required.push(key); } return schema; } // export function addEntryToSchema(schema, key, value, disabledKeys = []) { // if (!schema.properties[key] || !Array.isArray(value)) { // return; // Ensure the key exists in the schema and the value is an array // } // // const isMediaArray = value.some(item => isLikelyImageUrl(item.url || item)); // let entryProperties = {}; // try { // entryProperties = generateEntryProperties(value[0]); // } catch (error) { // console.error(`Error generating entry properties for key "${key}":`, error); // } // schema.properties[key].render.entry = { // id: `add_${key}`, // label: 'Add row', // name: '', // schema: { // type: 'object', // properties: entryProperties // } // }; // } // function generateEntryProperties(data) { // const properties = {}; // for (const [key, value] of Object.entries(data)) { // const type = typeof value; // const render = generateRenderMethod(type, key, value); // properties[key] = { // title: toTitleCase(key), // type: type === 'number' ? 'number' : 'string', // render: render // }; // if (isLikelyImageUrl(value)) { // properties[key].property = 'src'; // } // } // return properties; // } function toTitleCase(str) { return str.split('_') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); }