@silexlabs/grapesjs-data-source
Version:
Grapesjs Data Source
246 lines • 11.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CMD_DS_REMOVE_STATE = exports.CMD_DS_SET_STATE = exports.CMD_DS_GET_STATES = exports.CMD_DS_LIST = void 0;
exports.getPreviewActive = getPreviewActive;
const types_1 = require("./types");
const dataSourceManager_1 = require("./model/dataSourceManager");
const canvas_1 = require("./view/canvas");
const api_1 = require("./api");
// Command IDs for data source management
exports.CMD_DS_LIST = 'data-source:list';
exports.CMD_DS_GET_STATES = 'data-source:get-states';
exports.CMD_DS_SET_STATE = 'data-source:set-state';
exports.CMD_DS_REMOVE_STATE = 'data-source:remove-state';
/**
* Validate an expression with explicit error messages.
* Checks both token structure and data source schema references.
* Throws with a detailed message on the first error found.
*/
function validateExpression(tokens) {
if (!Array.isArray(tokens) || tokens.length === 0) {
throw new Error('Expression must be a non-empty array of tokens.');
}
const dataSources = (0, api_1.getAllDataSources)();
const dsIds = dataSources.map(ds => ds.id);
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
const prefix = `Token[${i}]`;
if (!token || typeof token !== 'object') {
throw new Error(`${prefix}: must be an object. Got ${typeof token}.`);
}
if (!token.type) {
throw new Error(`${prefix}: missing "type". Must be "property", "filter", or "state".`);
}
switch (token.type) {
case 'property': {
if (!token.fieldId) {
throw new Error(`${prefix}: property token missing "fieldId". Use "fixed" for a fixed value, or a field name from your data source.`);
}
// Fixed values just need options.value
if (token.fieldId === types_1.FIXED_TOKEN_ID) {
const opts = token.options;
if (!opts || opts.value === undefined || opts.value === null) {
throw new Error(`${prefix}: fixed value token requires options.value. Example: {"type":"property","fieldId":"fixed","options":{"value":"Hello"}}`);
}
break;
}
// Validate data source reference
if (token.dataSourceId) {
const dsId = token.dataSourceId;
if (!dsIds.includes(dsId)) {
throw new Error(`${prefix}: data source "${dsId}" not found. Available: ${dsIds.join(', ') || '(none configured)'}. Use data-source:list to see connected sources.`);
}
// Validate that the field exists in the data source schema
const ds = (0, api_1.getDataSource)(dsId);
if (ds) {
const allTypes = ds.getTypes();
const queryableFieldIds = ds.getQueryables().map(f => f.id);
const allFieldIds = allTypes.flatMap(t => t.fields.map(f => f.id));
const allTypeIds = allTypes.map(t => t.id);
const fieldId = token.fieldId;
// Check if fieldId matches a queryable, a field, or a type
if (!queryableFieldIds.includes(fieldId) && !allFieldIds.includes(fieldId) && !allTypeIds.includes(fieldId)) {
throw new Error(`${prefix}: field "${fieldId}" not found in data source "${dsId}". Available queryables: ${queryableFieldIds.slice(0, 15).join(', ') || '(none)'}. Use data-source:list for full schema.`);
}
}
}
break;
}
case 'filter': {
if (!token.id) {
throw new Error(`${prefix}: filter token missing "id" (the filter name, e.g. "date", "upcase", "split").`);
}
break;
}
case 'state': {
if (!token.componentId) {
throw new Error(`${prefix}: state token missing "componentId".`);
}
if (!token.storedStateId) {
throw new Error(`${prefix}: state token missing "storedStateId".`);
}
break;
}
default:
throw new Error(`${prefix}: unknown token type "${token.type}". Must be "property", "filter", or "state".`);
}
}
}
// Global state for preview activation
let isPreviewActive = true;
function getPreviewActive() {
return isPreviewActive;
}
// Function to force GrapesJS to re-render all components
function forceRender(editor) {
// Force a complete re-render by refreshing the canvas
(0, canvas_1.doRender)(editor);
}
// GrapesJS plugin to add commands to the editor
exports.default = (editor, opts) => {
// Set initial preview state
isPreviewActive = opts.previewActive;
// Refresh all data sources
editor.Commands.add(types_1.COMMAND_REFRESH, {
run() {
(0, dataSourceManager_1.refreshDataSources)();
},
});
// Activate preview mode
editor.Commands.add(types_1.COMMAND_PREVIEW_ACTIVATE, {
run() {
if (!isPreviewActive) {
isPreviewActive = true;
// Force GrapesJS to re-render to show preview data
forceRender(editor);
// Emit event
editor.trigger(types_1.PREVIEW_ACTIVATED);
}
},
});
// Deactivate preview mode
editor.Commands.add(types_1.COMMAND_PREVIEW_DEACTIVATE, {
run() {
var _a;
if (isPreviewActive) {
isPreviewActive = false;
// Force GrapesJS to re-render to show original content
const main = (_a = editor.Pages.getSelected()) === null || _a === void 0 ? void 0 : _a.getMainComponent();
if (main)
(0, canvas_1.restoreOriginalRender)(main);
// Emit event
editor.trigger(types_1.PREVIEW_DEACTIVATED);
}
},
});
// Toggle preview mode
editor.Commands.add(types_1.COMMAND_PREVIEW_TOGGLE, {
run() {
var _a;
isPreviewActive = !isPreviewActive;
// Emit event
if (isPreviewActive) {
// Force GrapesJS to re-render to reflect the toggled state
forceRender(editor);
// Trigger event
editor.trigger(types_1.PREVIEW_ACTIVATED);
}
else {
// Force GrapesJS to re-render to show original content
const main = (_a = editor.Pages.getSelected()) === null || _a === void 0 ? void 0 : _a.getMainComponent();
if (main)
(0, canvas_1.restoreOriginalRender)(main);
// Trigger event
editor.trigger(types_1.PREVIEW_DEACTIVATED);
}
},
});
// Refresh preview data
editor.Commands.add(types_1.COMMAND_PREVIEW_REFRESH, {
run() {
if (isPreviewActive) {
forceRender(editor);
}
},
});
// List all data sources with their IDs and types
editor.Commands.add(exports.CMD_DS_LIST, {
run() {
return (0, api_1.getAllDataSources)().map(ds => ({
id: ds.id,
label: ds.label,
type: ds.type,
}));
},
});
// Get all states (expressions) on the selected component
editor.Commands.add(exports.CMD_DS_GET_STATES, {
run(editor, sender, options = {}) {
const component = options.component || editor.getSelected();
if (!component)
throw new Error('No component selected. Use components:select first.');
const exported = options.exported !== false;
const ids = (0, api_1.getStateIds)(component, exported);
return ids.map(id => {
const state = (0, api_1.getState)(component, id, exported);
return {
id,
label: state === null || state === void 0 ? void 0 : state.label,
expression: state === null || state === void 0 ? void 0 : state.expression,
hidden: state === null || state === void 0 ? void 0 : state.hidden,
};
});
},
});
// Set a state (expression) on the selected component
editor.Commands.add(exports.CMD_DS_SET_STATE, {
run(editor, sender, options = {}) {
const component = options.component || editor.getSelected();
if (!component)
throw new Error('No component selected. Use components:select first.');
const { stateId, expression, label, exported } = options;
if (!stateId)
throw new Error('Required: stateId (e.g. "innerHTML", "src", "href")');
if (!expression)
throw new Error('Required: expression — a JSON array of tokens, e.g. [{"type":"property","fieldId":"fixed","options":{"value":"Hello"}}]');
// Parse string expressions
let rawTokens = expression;
if (typeof rawTokens === 'string') {
try {
rawTokens = JSON.parse(rawTokens);
}
catch (_a) {
throw new Error(`Expression is not valid JSON: ${expression}`);
}
}
// Validate structure and data source references (throws with detailed message)
validateExpression(rawTokens);
const parsed = (0, api_1.toExpression)(rawTokens);
if (!parsed)
throw new Error(`Invalid expression: token validation passed but toExpression() rejected it. Got: ${JSON.stringify(rawTokens)}`);
const isExported = exported !== false;
(0, api_1.setState)(component, stateId, {
label: label || stateId,
expression: parsed,
}, isExported);
if (isPreviewActive)
forceRender(editor);
},
});
// Remove a state from the selected component
editor.Commands.add(exports.CMD_DS_REMOVE_STATE, {
run(editor, sender, options = {}) {
const component = options.component || editor.getSelected();
if (!component)
throw new Error('No component selected. Use components:select first.');
const { stateId, exported } = options;
if (!stateId)
throw new Error('Required: stateId (e.g. "innerHTML", "src", "href"). Use data-source:get-states to list existing states.');
const isExported = exported !== false;
(0, api_1.removeState)(component, stateId, isExported);
if (isPreviewActive)
forceRender(editor);
},
});
};
//# sourceMappingURL=commands.js.map