@silexlabs/grapesjs-data-source
Version:
Grapesjs Data Source
380 lines • 14.3 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NOTIFICATION_GROUP = void 0;
exports.cleanStateName = cleanStateName;
exports.getComponentDebug = getComponentDebug;
exports.concatWithLength = concatWithLength;
exports.getTokenDisplayName = getTokenDisplayName;
exports.groupByType = groupByType;
exports.getFixedToken = getFixedToken;
exports.toValue = toValue;
exports.toId = toId;
exports.fromString = fromString;
exports.isExpression = isExpression;
exports.toExpression = toExpression;
exports.convertKind = convertKind;
exports.getFieldType = getFieldType;
exports.optionsFormKeySelector = optionsFormKeySelector;
exports.getElementFromOption = getElementFromOption;
exports.getDefaultOptions = getDefaultOptions;
exports.createDataSource = createDataSource;
const state_1 = require("./model/state");
const lit_1 = require("lit");
const token_1 = require("./model/token");
const GraphQL_1 = __importDefault(require("./datasources/GraphQL"));
const dataSourceRegistry_1 = require("./model/dataSourceRegistry");
const types_1 = require("./types");
exports.NOTIFICATION_GROUP = 'Data source';
/**
* Get the display name of a field
*/
function cleanStateName(name) {
var _a, _b;
return (_b = (_a = name === null || name === void 0 ? void 0 : name.toLowerCase()) === null || _a === void 0 ? void 0 : _a.replace(/[^a-z0-9:._-]/g, '-')) === null || _b === void 0 ? void 0 : _b.replace(/^[0-9]+/, '-'); // HTML attributes cannot start with digits
}
/**
* Get the display type of a field
* For the dropdown in expressions
* @example "String", "String [ ]", "String { }"
*/
function getTypeDisplayName(typeIds, kind) {
const typeIdsStr = typeIds.join(', ').toLowerCase();
return kind === 'list' ? ` (${typeIdsStr}[])` : kind === 'object' ? ` (${typeIdsStr}{})` : ` (${typeIdsStr})`;
}
function getComponentDebug(component) {
const parent = component.parent();
const parentName = parent === null || parent === void 0 ? void 0 : parent.getName();
const parentTagName = parent === null || parent === void 0 ? void 0 : parent.get('tagName');
const parentDebug = parentName ? `${parentName} (${parentTagName})` : parentTagName;
const id = component.cid;
const tagName = component.get('tagName');
const classes = component.getClasses();
const classesStr = classes.length ? `.${classes.join('.')}` : '';
const name = component.getName();
return `${parentDebug} > ${name} (${tagName}#${id}${classesStr})`;
}
/**
* Concatenate strings to get a desired length string as result
* Exported for tests
*/
function concatWithLength(desiredNumChars, ...strings) {
// const diff = desiredNumChars - `${token.label} ${type}`.length
// return `${token.label}${'\xA0'.repeat(diff * 2)} ${type} ${desiredNumChars}`
// Get current string length
const len = strings.reduce((acc, str) => acc + str.length, 0);
const diff = Math.max(desiredNumChars - len, 0);
// Give the fist string the desired length
const [first, ...rest] = strings;
const newFirst = first + '\xA0'.repeat(diff);
// Return the concatenated string
return [newFirst, ...rest].join('');
}
/**
* Get the label for a token
* This is mostly about formatting a string for the dropdowns
*/
function getTokenDisplayName(component, token) {
switch (token.type) {
case 'property': {
const type = getTypeDisplayName(token.typeIds, token.kind);
return `${token.label} ${type}`;
}
case 'filter': return token.label;
case 'state':
return (0, state_1.getStateDisplayName)(component, token);
default:
console.error('Unknown token type (reading type)', token);
throw new Error('Unknown token type');
}
}
/**
* Group tokens by type
* This is used to create the groups in dropdowns
*/
function groupByType(editor, component, completion, expression) {
return completion
.reduce((acc, token) => {
var _a, _b, _c, _d;
let label;
switch (token.type) {
case 'filter':
label = 'Filters';
break;
case 'property': {
if (token.dataSourceId) {
if (expression.length > 0) {
try {
const type = (0, token_1.getExpressionResultType)(expression, component);
label = (_b = (_a = type === null || type === void 0 ? void 0 : type.label) !== null && _a !== void 0 ? _a : type === null || type === void 0 ? void 0 : type.id) !== null && _b !== void 0 ? _b : 'Unknown';
}
catch (e) {
// FIXME: notify user
console.error('Error while getting expression result type in groupByType', { expression, component });
label = 'Unknown';
}
}
else {
const dataSource = (0, dataSourceRegistry_1.getDataSource)(token.dataSourceId);
if (dataSource) {
label = dataSource.label || ((_d = (_c = dataSource).get) === null || _d === void 0 ? void 0 : _d.call(_c, 'label')) || token.dataSourceId;
}
else {
console.error('Data source not found', token.dataSourceId);
editor.runCommand('notifications:add', {
type: 'error',
group: exports.NOTIFICATION_GROUP,
message: `Data source not found: ${token.dataSourceId}`,
});
throw new Error(`Data source not found: ${token.dataSourceId}`);
}
}
}
else {
label = 'Fields';
}
break;
}
case 'state': {
const parent = (0, state_1.getParentByPersistentId)(token.componentId, component);
const name = (parent === null || parent === void 0 ? void 0 : parent.get('tagName')) === 'body' ? 'Website' : parent === null || parent === void 0 ? void 0 : parent.getName();
label = name ? `${name}'s states` : 'States';
break;
}
default:
console.error('Unknown token type (reading type)', token);
throw new Error('Unknown token type');
}
if (!acc[label])
acc[label] = [];
acc[label].push(token);
return acc;
}, {});
}
/**
* Create a "fixed" token
* It is a hard coded content with which you can start an expression
*/
function getFixedToken(value) {
return {
type: 'property',
propType: 'field',
fieldId: types_1.FIXED_TOKEN_ID,
label: 'Fixed value',
kind: 'scalar',
typeIds: ['String'],
options: {
value,
},
optionsForm: () => (0, lit_1.html) `
<label>Value
<input type="text" name="value" .value=${value}>
</label>
`,
};
}
/**
* Convert a token to a string
* This is used to store the token in the component
*/
function toValue(token) {
return JSON.stringify(Object.assign({}, token));
}
/**
* Convert a token to an option's tag value (json string)
*/
function toId(token) {
switch (token.type) {
case 'property': return `property__${token.dataSourceId || ''}__${token.fieldId}__${token.kind}__${token.typeIds.join(',')}`;
case 'filter': return `filter____${token.id}`;
case 'state': return `state__${token.componentId}__${token.storedStateId}`;
default:
console.error('Unknown token type (reading type)', token);
throw new Error('Unknown token type');
}
}
/**
* Revert an option's tag value to a token
* @throws Error if the token type is not found
*/
function fromString(editor, id, componentId) {
return (0, token_1.fromStored)(JSON.parse(id), componentId);
}
/**
* Check if a json is an expression, i.e. an array of tokens
*/
function isExpression(json) {
if (typeof json === 'string')
throw new Error('json must be parsed');
if (!Array.isArray(json))
return false;
return json.every(token => {
var _a;
if (typeof token !== 'object')
return false;
if (!token.type)
return false;
switch (token.type) {
case 'property': {
if (!token.fieldId)
return false;
if (token.fieldId === types_1.FIXED_TOKEN_ID) {
if (!((_a = token.options) === null || _a === void 0 ? void 0 : _a.value))
return false;
}
break;
}
case 'state': {
if (!token.componentId)
return false;
if (!token.storedStateId)
return false;
break;
}
case 'filter': {
if (!token.id)
return false;
break;
}
}
return true;
});
}
/**
* Convert a json to an expression
*/
function toExpression(json) {
try {
if (typeof json === 'string')
json = JSON.parse(json);
if (isExpression(json))
return json;
return null;
}
catch (e) {
return null;
}
}
/**
* Apply a kind to a field
*/
function convertKind(field, from, to) {
if (!field) {
return null;
}
if (field.kind !== from) {
console.error(`Field is not a ${from}`, field);
throw new Error(`Field ${field.label} is not a ${from}`);
}
return Object.assign(Object.assign({}, field), { kind: to });
}
/**
* Get the type of a field, as provided by the data source
* @throws Error if the field has a token with an unknown type
*/
function getFieldType(editor, field, key, componentId) {
if (!field || !key)
return null;
const allDataSources = (0, dataSourceRegistry_1.getAllDataSources)();
const dataSource = allDataSources.find((ds) => ds.id === field.dataSourceId);
if (!(dataSource === null || dataSource === void 0 ? void 0 : dataSource.isConnected()))
return null;
const types = field.typeIds.map(typeId => {
const dsTypes = dataSource.getTypes();
return dsTypes.find((type) => type.id === typeId);
}).filter(Boolean);
const fields = types.map((type) => type === null || type === void 0 ? void 0 : type.fields.find((f) => f.label === key)).filter(Boolean);
switch (fields.length) {
case 0: return null;
case 1: return fields[0];
default: return {
id: `${field.id}.${key}`,
label: `${field.label}.${key}`,
typeIds: fields.reduce((typeIds, field) => typeIds
.concat(field.typeIds.filter((t) => !typeIds.includes(t))), []),
kind: 'object',
dataSourceId: field.dataSourceId
};
}
}
/**
* Generate a form to edit the options of a token
* @throws Error if the field has a token with an unknown type
*/
function optionsFormKeySelector(editor, field, options, name) {
if (!field)
return (0, lit_1.html) `
<label>${name}
<input type="text" name=${name} />
</label>
`;
const allDataSources = (0, dataSourceRegistry_1.getAllDataSources)();
const dataSource = allDataSources.find((ds) => ds.id === field.dataSourceId);
if (!(dataSource === null || dataSource === void 0 ? void 0 : dataSource.isConnected())) {
return (0, lit_1.html) `
<select name=${name}>
<option value="">Data source not connected</option>
</select>
`;
}
const dsTypes = dataSource.getTypes();
const fieldOptions = field.typeIds
.flatMap(typeId => { var _a; return ((_a = dsTypes.find((type) => type.id === typeId)) === null || _a === void 0 ? void 0 : _a.fields) || []; });
return (0, lit_1.html) `
<select name=${name}>
<option value="">Select a ${name}</option>
${fieldOptions.map(f => (0, lit_1.html) `<option value=${f.label} .selected=${f.label === options.key}>${f.label}</option>`)}
</select>
`;
}
/**
* Get a container element from an option
* @throws Error if the option is not a string or an HTMLElement or a function
* @throws Error if the element is not found
*/
function getElementFromOption(option, optionNameForError) {
// Get the container element for the UI
if (typeof option === 'undefined') {
// This should never happen as we set a default value in /index.ts
throw new Error('el option must be set');
}
else if (typeof option === 'string') {
const el = document.querySelector(option);
if (!el)
throw new Error(`Element ${option} not found`);
return el;
}
else if (typeof option === 'function') {
const el = option();
if (!el)
throw new Error('el option must be a returned by the provided function');
return el;
}
else if (option instanceof HTMLElement) {
return option;
}
throw new Error(`${optionNameForError} must be a string or an HTMLElement or a function`);
}
function getDefaultOptions(postFix = Math.random().toString(36).slice(2, 8)) {
return {
id: `ds-${postFix}`,
label: 'New data source',
type: 'graphql',
url: '',
method: 'POST',
headers: {},
readonly: false,
};
}
function createDataSource(opts = {}, postFix) {
const options = Object.assign(Object.assign({}, getDefaultOptions(postFix)), opts);
switch (options.type) {
case 'graphql':
return new GraphQL_1.default(options);
default:
throw new Error(`Unknown data source type: ${options.type}`);
}
}
//# sourceMappingURL=utils.js.map