@silexlabs/grapesjs-data-source
Version:
Grapesjs Data Source
384 lines • 14.8 kB
JavaScript
"use strict";
/*
* Silex website builder, free/libre no-code tool for makers.
* Copyright (c) 2023 lexoyo and Silex Labs foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomStatesEditor = void 0;
const lit_1 = require("lit");
const ref_js_1 = require("lit/directives/ref.js");
const decorators_js_1 = require("lit/decorators.js");
const state_1 = require("../model/state");
require("./state-editor");
const defaultStyles_1 = require("./defaultStyles");
const token_1 = require("../model/token");
const utils_1 = require("../utils");
/**
* Editor for selected element's states
*
*/
class CustomStatesEditor extends lit_1.LitElement {
constructor() {
super(...arguments);
this.disabled = false;
this.privateState = false;
this.title = 'Custom states';
this.defaultFixed = false;
this.createPrompt = 'Name this state';
this.renamePrompt = 'Rename this state';
this.defaultName = 'New state';
this.hideLoopData = false;
this.helpText = '';
this.helpLink = '';
this._reservedNames = [];
this.editor = null;
this.redrawing = false;
}
// This is a comma separated list of reserved names
// Or an array of reserved names
get reservedNames() { return this._reservedNames; }
set reservedNames(value) {
if (typeof value === 'string')
this._reservedNames = value.split(',').map(s => s.trim());
else
this._reservedNames = value;
}
setEditor(editor) {
if (this.editor) {
console.warn('property-editor setEditor already set');
return;
}
this.editor = editor;
// Update the UI when a page is added/renamed/removed
this.editor.on('page', () => this.requestUpdate());
// Update the UI on component selection change
this.editor.on('component:selected', () => this.requestUpdate());
// Update the UI on component change
this.editor.on('component:update', () => this.requestUpdate());
}
getHead(selected) {
return (0, lit_1.html) `
<style>
${defaultStyles_1.PROPERTY_STYLES}
</style>
<slot></slot>
<section class="ds-section">
<div>
<div class="gjs-traits-label">
<span>${this.title}</span>
<span>
${selected ? (0, lit_1.html) `
<button
title="Add a new state"
class="ds-states__add-button ds-states__button"
=${() => {
const item = this.createCustomState(selected);
if (!item)
return;
this.setState(selected, item.name, item.state);
}}
>+</button>
` : ''}
${this.helpText ? (0, lit_1.html) `
<details class="ds-states__help">
<summary>Help</summary>
<div class="ds-states__help--tooltip">
<span>${this.helpText}</span>
${this.helpLink ? (0, lit_1.html) `
<a
class="ds-states__help-link"
href="${this.helpLink}"
target="_blank"
>\u{1F517} Read more...</a>
` : ''}
</div>
</details>
` : ''}
</span>
</div>
</div>
</section>
`;
}
render() {
var _a;
super.render();
this.redrawing = true;
const selected = (_a = this.editor) === null || _a === void 0 ? void 0 : _a.getSelected();
const empty = (0, lit_1.html) `
${this.getHead(null)}
<p class="ds-empty">Select an element to edit its states</p>
`;
if (!this.editor || this.disabled) {
this.redrawing = false;
return (0, lit_1.html) ``;
}
if (!selected) {
this.redrawing = false;
return empty;
}
const items = this.getStateIds(selected)
.map(stateId => ({
name: stateId,
publicState: !this.privateState,
state: this.getState(selected, stateId),
}))
.filter(item => item.state && !item.state.hidden);
const result = (0, lit_1.html) `
${this.getHead(selected)}
<div class="ds-states">
<div class="ds-states__items">
${items.length === 0 ? (0, lit_1.html) `
<p>Use the "+" button to add elements to this list</p>
` : ''}
${items
.map((item, index) => (0, lit_1.html) `
<div class="ds-states__item">
${this.getStateEditor(selected, item.state.label || '', item.name)}
<div class="ds-states__buttons">
<button
title="Remove this state"
class="ds-states__remove-button ds-states__button"
=${() => {
this.removeState(selected, item.name);
this.requestUpdate();
}}
>x</button>
<button
title="Rename this state"
class="ds-states__rename-button ds-states__button"
=${() => {
const newItem = this.renameCustomState(item);
if (!newItem || newItem === item)
return;
this.removeState(selected, item.name);
this.setState(selected, newItem.name, newItem.state);
this.requestUpdate();
}}
>\u270F</button>
<button
title="Move this state up"
class="ds-states__item-move-up ds-states__button${index === 0 ? ' ds-states__button--disabled' : ''}"
=${() => {
items.splice(index - 1, 0, items.splice(index, 1)[0]);
this.updateOrderCustomStates(selected, items);
}}
>\u2191</button>
<button
title="Move this state down"
class="ds-states__item-move-down ds-states__button${index === items.length - 1 ? ' ds-states__button--disabled' : ''}"
=${() => {
items.splice(index + 1, 0, items.splice(index, 1)[0]);
this.updateOrderCustomStates(selected, items);
}}
>\u2193</button>
</div>
</div>
<hr class="ds-states__sep" />
`)}
</div>
</div>
`;
this.redrawing = false;
return result;
}
/**
* Get the states for this type of editor
*/
getStateIds(component) {
return (0, state_1.getStateIds)(component, !this.privateState)
// Filter out the states which are properties
.filter(stateId => !this.reservedNames.includes(stateId));
}
/**
* Get the states for this type of editor
*/
getState(component, name) {
return (0, state_1.getState)(component, name, !this.privateState);
}
/**
* Set the states for this type of editor
*/
setState(component, name, state) {
(0, state_1.setState)(component, name, state, !this.privateState);
}
/**
* Remove the states for this type of editor
*/
removeState(component, name) {
(0, state_1.removeState)(component, name, !this.privateState);
}
getStateEditor(selected, label, name) {
return (0, lit_1.html) `
<state-editor
.selected=${selected}
.editor=${this.editor}
id="${name}"
name=${name}
?hide-loop-data=${this.hideLoopData}
default-fixed=${this.defaultFixed}
${(0, ref_js_1.ref)(el => {
if (el) {
const stateEditor = el;
stateEditor.data = this.getTokens(selected, name);
}
})}
=${() => this.onChange(selected, name, label)}
.disabled=${this.disabled}
>
<label slot="label">${label || name}</label>
</state-editor>
`;
}
onChange(component, name, label) {
if (this.redrawing)
return;
const stateEditor = this.shadowRoot.querySelector(`#${name}`);
this.setState(component, name, {
expression: stateEditor.data,
label,
});
}
getTokens(component, name) {
const state = this.getState(component, name);
if (!state || !state.expression)
return [];
return state.expression.map(token => {
try {
return (0, token_1.fromStored)(token, component.getId());
}
catch (_a) {
// FIXME: notify user
console.error('Error while getting expression result type in getTokens', { expression: state.expression, component, name });
return {
type: 'property',
propType: 'field',
fieldId: 'unknown',
label: 'unknown',
kind: 'scalar',
typeIds: [],
};
}
});
}
/**
* Rename a custom state
*/
renameCustomState(item) {
var _a, _b, _c;
const label = (_c = (_b = (_a = prompt(this.renamePrompt, item.state.label)) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === null || _b === void 0 ? void 0 : _b.replace(/[^a-z0-9]/g, '-')) === null || _c === void 0 ? void 0 : _c.replace(/^-+|-+$/g, '');
if (!label || label === item.state.label)
return item;
return Object.assign(Object.assign({}, item), { state: Object.assign(Object.assign({}, item.state), { label: label }) });
}
/**
* Update the custom states, in the order of the list
*/
updateOrderCustomStates(component, items) {
const stateIds = this.getStateIds(component);
// Remove all states
stateIds.forEach(stateId => {
if (items.map(item => item.name).includes(stateId)) {
this.removeState(component, stateId);
}
});
// Add states in the order of the list
items.forEach(item => {
this.setState(component, item.name, item.state);
});
}
/**
* Create a new custom state
*/
createCustomState(component) {
const label = (0, utils_1.cleanStateName)(prompt(this.createPrompt, this.defaultName));
if (!label)
return null;
if (this.reservedNames.includes(label)) {
alert(`The name ${label} is reserved, please choose another name`);
return null;
}
const stateId = `${component.getId()}-${Math.random().toString(36).slice(2)}`;
const state = {
label,
expression: [],
};
this.setState(component, stateId, state);
return {
name: stateId,
state,
publicState: !this.privateState,
};
}
}
exports.CustomStatesEditor = CustomStatesEditor;
__decorate([
(0, decorators_js_1.property)({ type: Boolean }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "disabled", void 0);
__decorate([
(0, decorators_js_1.property)({ type: Boolean, attribute: 'private-state' }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "privateState", void 0);
__decorate([
(0, decorators_js_1.property)({ type: String }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "title", void 0);
__decorate([
(0, decorators_js_1.property)({ type: Boolean, attribute: 'default-fixed' }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "defaultFixed", void 0);
__decorate([
(0, decorators_js_1.property)({ type: String, attribute: 'create-prompt' }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "createPrompt", void 0);
__decorate([
(0, decorators_js_1.property)({ type: String, attribute: 'rename-prompt' }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "renamePrompt", void 0);
__decorate([
(0, decorators_js_1.property)({ type: String, attribute: 'default-name' }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "defaultName", void 0);
__decorate([
(0, decorators_js_1.property)({ type: String, attribute: 'reserved-names' }),
__metadata("design:type", Object),
__metadata("design:paramtypes", [Object])
], CustomStatesEditor.prototype, "reservedNames", null);
__decorate([
(0, decorators_js_1.property)({ type: Boolean, attribute: 'hide-loop-data' }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "hideLoopData", void 0);
__decorate([
(0, decorators_js_1.property)({ type: String, attribute: 'help-text' }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "helpText", void 0);
__decorate([
(0, decorators_js_1.property)({ type: String, attribute: 'help-link' }),
__metadata("design:type", Object)
], CustomStatesEditor.prototype, "helpLink", void 0);
if (!customElements.get('custom-states-editor')) {
customElements.define('custom-states-editor', CustomStatesEditor);
}
//# sourceMappingURL=custom-states-editor.js.map