@blinkk/editor
Version:
Structured content editor with live previews.
172 lines • 7.15 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LiveEditor = void 0;
const dataStorage_1 = require("../utility/dataStorage");
const events_1 = require("./events");
const selective_edit_1 = require("@blinkk/selective-edit");
const api_1 = require("./api");
const amagakiProjectType_1 = require("../projectType/amagaki/amagakiProjectType");
const content_1 = require("./parts/content");
const empty_1 = require("./parts/empty");
const growProjectType_1 = require("../projectType/grow/growProjectType");
const menu_1 = require("./parts/menu");
const modals_1 = require("./parts/modals");
const notifications_1 = require("./parts/notifications");
const overview_1 = require("./parts/overview");
const preview_1 = require("./parts/preview");
const events_2 = require("@blinkk/selective-edit/dist/src/selective/events");
const javascript_time_ago_1 = __importDefault(require("javascript-time-ago"));
const toasts_1 = require("./parts/toasts");
const en_1 = __importDefault(require("javascript-time-ago/locale/en"));
// Set the default locale for the time ago globally.
javascript_time_ago_1.default.addDefaultLocale(en_1.default);
class LiveEditor {
constructor(config, container) {
this.config = config;
this.container = container;
this.isRendering = false;
this.isPendingRender = false;
this.storage = new dataStorage_1.LocalDataStorage();
this.state = this.config.state;
this.parts = {
content: new content_1.ContentPart({
selectiveConfig: this.config.selectiveConfig,
state: this.state,
storage: this.storage,
}),
empty: new empty_1.EmptyPart({
state: this.state,
}),
menu: new menu_1.MenuPart({
state: this.state,
storage: this.storage,
}),
modals: new modals_1.ModalsPart(),
notifications: new notifications_1.NotificationsPart(),
overview: new overview_1.OverviewPart({
state: this.state,
}),
preview: new preview_1.PreviewPart({
state: this.state,
storage: this.storage,
}),
toasts: new toasts_1.ToastsPart(),
};
// Bind the editor to the global selective config.
if (this.config.selectiveConfig.global) {
this.config.selectiveConfig.global.editor = this;
}
// Update the project type when the project changes.
this.state.addListener('getProject', () => {
if (this.state.project?.type === api_1.ProjectTypes.Grow) {
this.updateProjectType(new growProjectType_1.GrowProjectType());
}
else if (this.state.project?.type === api_1.ProjectTypes.Amagaki) {
this.updateProjectType(new amagakiProjectType_1.AmagakiProjectType());
}
// Pull in the UI labels.
if (this.state.project?.ui?.labels) {
this.config.labels = Object.assign({}, this.config.labels || {}, this.state.project?.ui?.labels);
}
});
// Automatically re-render after the window resizes.
window.addEventListener('resize', () => {
this.render();
});
// Handle the global key bindings.
container.addEventListener('keydown', (evt) => {
if (evt.ctrlKey || evt.metaKey) {
switch (evt.key) {
case 's':
evt.preventDefault();
document.dispatchEvent(new CustomEvent(events_1.EVENT_SAVE));
break;
}
}
});
// Bind to the custom event to re-render the editor.
document.addEventListener(events_1.EVENT_RENDER, () => {
this.render();
});
// Bind to the selective event for rendering as well.
document.addEventListener(events_2.EVENT_RENDER, () => {
this.render();
});
}
classesForEditor() {
return {
le: true,
'le--docked-menu': this.parts.menu.isDocked,
};
}
render() {
if (this.isRendering) {
this.isPendingRender = true;
return;
}
this.isPendingRender = false;
this.isRendering = true;
selective_edit_1.render(this.template(this), this.container);
this.isRendering = false;
document.dispatchEvent(new CustomEvent(events_1.EVENT_RENDER_COMPLETE));
if (this.isPendingRender) {
this.render();
}
}
template(editor) {
return selective_edit_1.html `<div class=${selective_edit_1.classMap(this.classesForEditor())}>
${this.parts.menu.template(editor)}
<div class="le__structure__content">
<div class="le__structure__content_header">
${this.parts.overview.template(editor)}
</div>
${this.templateContentStructure(editor)}
</div>
${this.parts.modals.template(editor)}
${this.parts.toasts.template(editor)}
</div>`;
}
templateContentStructure(editor) {
if (!this.state.file) {
return this.parts.empty.template(editor);
}
// Not having a url also qualifies to not show the preview.
if (this.parts.content.isExpanded || !this.state.file.url) {
return selective_edit_1.html `<div class="le__structure__content_only">
${this.parts.content.template(editor)}
</div>`;
}
if (this.parts.preview.isExpanded) {
return selective_edit_1.html `<div class="le__structure__preview_only">
${this.parts.preview.template(editor)}
</div>`;
}
return selective_edit_1.html `<div class="le__structure__content_panes">
${this.parts.content.template(editor)}
${this.parts.preview.template(editor)}
</div>`;
}
updateProjectType(projectType) {
this.projectType = projectType;
this.config.selectiveConfig = this.config.selectiveConfig || {};
// Update selective fields available.
this.config.selectiveConfig.fieldTypes =
this.config.selectiveConfig.fieldTypes || {};
for (const [key, value] of Object.entries(this.projectType.fieldTypes || {})) {
this.config.selectiveConfig.fieldTypes[key] = value;
}
// Update selective validation rules available.
this.config.selectiveConfig.ruleTypes =
this.config.selectiveConfig.ruleTypes || {};
for (const [key, value] of Object.entries(this.projectType.ruleTypes || {})) {
this.config.selectiveConfig.ruleTypes[key] = value;
}
// Event to allow existing selective editors to reset.
document.dispatchEvent(new CustomEvent(events_1.EVENT_PROJECT_TYPE_UPDATE));
}
}
exports.LiveEditor = LiveEditor;
//# sourceMappingURL=editor.js.map