@limetech/lime-elements
Version:
564 lines (563 loc) • 18.8 kB
JavaScript
import { Host, h } from '@stencil/core';
import { createRandomString } from '../../util/random-string';
/**
* A rich text editor that offers a rich text editing experience with markdown support,
* in the sense that you can easily type markdown syntax and see the rendered
* result as rich text in real-time. For instance, you can type `# Hello, world!`
* and see it directly turning to a heading 1 (an `<h1>` HTML element).
*
* Naturally, you can use standard keyboard hotkeys such as <kbd>Ctrl</kbd> + <kbd>B</kbd>
* to toggle bold text, <kbd>Ctrl</kbd> + <kbd>I</kbd> to toggle italic text, and so on.
*
* @exampleComponent limel-example-text-editor-basic
* @exampleComponent limel-example-text-editor-as-form-component
* @exampleComponent limel-example-text-editor-with-markdown
* @exampleComponent limel-example-text-editor-with-html
* @exampleComponent limel-example-text-editor-with-tables
* @exampleComponent limel-example-text-editor-with-inline-images-file-storage
* @exampleComponent limel-example-text-editor-with-inline-images-base64
* @exampleComponent limel-example-text-editor-allow-resize
* @exampleComponent limel-example-text-editor-size
* @exampleComponent limel-example-text-editor-ui
* @exampleComponent limel-example-text-editor-custom-element
* @exampleComponent limel-example-text-editor-triggers
* @exampleComponent limel-example-text-editor-composite
* @beta
*/
export class TextEditor {
constructor() {
this.renderHelperLine = () => {
if (!this.helperText) {
return;
}
return (h("limel-helper-line", { helperText: this.helperText, helperTextId: this.helperTextId, invalid: this.isInvalid() }));
};
this.isInvalid = () => {
if (this.readonly) {
// A readonly field can never be invalid.
return false;
}
if (this.invalid) {
return true;
}
};
this.handleChange = (event) => {
event.stopPropagation();
this.change.emit(event.detail);
};
this.handleImagePasted = (event) => {
event.stopPropagation();
this.imagePasted.emit(event.detail);
};
this.handleMetadataChange = (event) => {
event.stopPropagation();
this.metadataChange.emit(event.detail);
};
this.handleImageRemoved = (event) => {
event.stopPropagation();
this.imageRemoved.emit(event.detail);
};
this.contentType = 'markdown';
this.language = 'en';
this.disabled = false;
this.readonly = false;
this.helperText = undefined;
this.placeholder = undefined;
this.label = undefined;
this.invalid = false;
this.value = undefined;
this.customElements = [];
this.triggers = [];
this.required = false;
this.allowResize = true;
this.ui = 'standard';
this.helperTextId = createRandomString();
this.editorId = createRandomString();
}
render() {
return (h(Host, null, h("limel-notched-outline", { labelId: this.editorId, label: this.label, required: this.required, invalid: this.invalid, disabled: this.disabled, readonly: this.readonly, hasValue: !!this.value, hasFloatingLabel: true }, this.renderEditor(), this.renderPlaceholder()), this.renderHelperLine()));
}
renderEditor() {
if (this.readonly) {
return (h("limel-markdown", { slot: "content", value: this.value, "aria-controls": this.helperText ? this.helperTextId : undefined, id: this.editorId }));
}
return (h("limel-prosemirror-adapter", { slot: "content", "aria-placeholder": this.placeholder, contentType: this.contentType, onChange: this.handleChange, onImagePasted: this.handleImagePasted, onImageRemoved: this.handleImageRemoved, onMetadataChange: this.handleMetadataChange, customElements: this.customElements, value: this.value, "aria-controls": this.helperText ? this.helperTextId : undefined, id: this.editorId, "aria-disabled": this.disabled, "aria-invalid": this.invalid, "aria-required": this.required, language: this.language, triggerCharacters: this.triggers, disabled: this.disabled, ui: this.ui }));
}
renderPlaceholder() {
if (!this.placeholder || this.value) {
return;
}
return (h("span", { class: "placeholder", "aria-hidden": "true", slot: "content" }, this.placeholder));
}
static get is() { return "limel-text-editor"; }
static get encapsulation() { return "shadow"; }
static get delegatesFocus() { return true; }
static get originalStyleUrls() {
return {
"$": ["text-editor.scss"]
};
}
static get styleUrls() {
return {
"$": ["text-editor.css"]
};
}
static get properties() {
return {
"contentType": {
"type": "string",
"mutable": false,
"complexType": {
"original": "'markdown' | 'html'",
"resolved": "\"html\" | \"markdown\"",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "The type of content that the editor should handle and emit, defaults to `markdown`\n\nAssumed to be set only once, so not reactive to changes"
},
"attribute": "content-type",
"reflect": false,
"defaultValue": "'markdown'"
},
"language": {
"type": "string",
"mutable": false,
"complexType": {
"original": "Languages",
"resolved": "\"da\" | \"de\" | \"en\" | \"fi\" | \"fr\" | \"nb\" | \"nl\" | \"no\" | \"sv\"",
"references": {
"Languages": {
"location": "import",
"path": "../date-picker/date.types"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Defines the language for translations."
},
"attribute": "language",
"reflect": true,
"defaultValue": "'en'"
},
"disabled": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Set to `true` to disable the field.\nUse `disabled` to indicate that the field can normally be interacted\nwith, but is currently disabled. This tells the user that if certain\nrequirements are met, the field may become enabled again."
},
"attribute": "disabled",
"reflect": true,
"defaultValue": "false"
},
"readonly": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Set to `true` to make the component read-only.\nUse `readonly` when the field is only there to present the data it holds,\nand will not become possible for the current user to edit.\n:::note\nConsider that it might be better to use `limel-markdown`\ninstead of `limel-text-editor` when the goal is visualizing data.\n:::"
},
"attribute": "readonly",
"reflect": true,
"defaultValue": "false"
},
"helperText": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Optional helper text to display below the input field when it has focus"
},
"attribute": "helper-text",
"reflect": true
},
"placeholder": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The placeholder text shown inside the input field,\nwhen the field is empty."
},
"attribute": "placeholder",
"reflect": true
},
"label": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The label of the editor"
},
"attribute": "label",
"reflect": true
},
"invalid": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Set to `true` to indicate that the current value of the editor is\ninvalid."
},
"attribute": "invalid",
"reflect": true,
"defaultValue": "false"
},
"value": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Description of the text inside the editor as markdown"
},
"attribute": "value",
"reflect": true
},
"customElements": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "CustomElementDefinition[]",
"resolved": "CustomElementDefinition[]",
"references": {
"CustomElementDefinition": {
"location": "import",
"path": "../../global/shared-types/custom-element.types"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "private",
"text": undefined
}, {
"name": "alpha",
"text": undefined
}],
"text": "A list of custom elements\n\nAny `CustomElement` that should be used inside the text editor needs\nto be defined here."
},
"defaultValue": "[]"
},
"triggers": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "TriggerCharacter[]",
"resolved": "TriggerCharacter[]",
"references": {
"TriggerCharacter": {
"location": "import",
"path": "./text-editor.types"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "private",
"text": undefined
}, {
"name": "alpha",
"text": undefined
}],
"text": "A set of trigger characters\n\nDefining a character here will enable trigger events to be sent if the\ncharacter is detected in the editor."
},
"defaultValue": "[]"
},
"required": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Set to `true` to indicate that the field is required.\n\n:::important\nAn empty but required field is not automatically considered invalid.\nYou must make sure to check the validity of the field on your own,\nand properly handle the `invalid` state.\n:::"
},
"attribute": "required",
"reflect": true,
"defaultValue": "false"
},
"allowResize": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Set to `true` to allow the user to vertically resize the editor.\nSet to `false` to disable the resize functionality."
},
"attribute": "allow-resize",
"reflect": true,
"defaultValue": "true"
},
"ui": {
"type": "string",
"mutable": false,
"complexType": {
"original": "EditorUiType",
"resolved": "\"minimal\" | \"no-toolbar\" | \"standard\"",
"references": {
"EditorUiType": {
"location": "import",
"path": "./types"
}
}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Specifies the visual appearance of the editor.\n\n- `standard`: The default editor appearance with a full toolbar and\n standard layout.\n- `minimal`: A compact editor appearance, ideal for limited space\n scenarios such as mobile devices. In this mode, the toolbar is hidden\n until the editor is focused.\n- `no-toolbar`: A basic textarea appearance without any text styling toolbar.\n This mode is suitable for scenarios where you want to provide a simple\n text input without any visible formatting options; but still provide\n support for markdown syntax and rich text, using hotkeys or when pasting."
},
"attribute": "ui",
"reflect": true,
"defaultValue": "'standard'"
}
};
}
static get events() {
return [{
"method": "change",
"name": "change",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "Dispatched when a change is made to the editor"
},
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
}
}, {
"method": "imagePasted",
"name": "imagePasted",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [{
"name": "private",
"text": undefined
}, {
"name": "alpha",
"text": undefined
}],
"text": "Dispatched when a image is pasted into the editor"
},
"complexType": {
"original": "ImageInserter",
"resolved": "ImageInserter",
"references": {
"ImageInserter": {
"location": "import",
"path": "./text-editor.types"
}
}
}
}, {
"method": "imageRemoved",
"name": "imageRemoved",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [{
"name": "private",
"text": undefined
}, {
"name": "alpha",
"text": undefined
}, {
"name": "deprecated",
"text": "- This event is deprecated and will be removed in a future version.\nUse the `metadataChange` event instead to track image removals."
}],
"text": "Dispatched when a image is removed from the editor"
},
"complexType": {
"original": "EditorImage",
"resolved": "EditorImage",
"references": {
"EditorImage": {
"location": "import",
"path": "./text-editor.types"
}
}
}
}, {
"method": "metadataChange",
"name": "metadataChange",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [{
"name": "private",
"text": undefined
}, {
"name": "alpha",
"text": undefined
}],
"text": "Dispatched when the metadata of the editor changes"
},
"complexType": {
"original": "EditorMetadata",
"resolved": "EditorMetadata",
"references": {
"EditorMetadata": {
"location": "import",
"path": "./text-editor.types"
}
}
}
}, {
"method": "triggerStart",
"name": "triggerStart",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [{
"name": "private",
"text": undefined
}, {
"name": "alpha",
"text": undefined
}],
"text": "Dispatched if a trigger character is detected."
},
"complexType": {
"original": "TriggerEventDetail",
"resolved": "TriggerEventDetail",
"references": {
"TriggerEventDetail": {
"location": "import",
"path": "./text-editor.types"
}
}
}
}, {
"method": "triggerStop",
"name": "triggerStop",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [{
"name": "private",
"text": undefined
}, {
"name": "alpha",
"text": undefined
}],
"text": "Dispatched if a trigger session is ended. That is if the selection\ngoes outside the trigger input or if something is inserted using the\nsupplied `TextEditor` insert function."
},
"complexType": {
"original": "TriggerEventDetail",
"resolved": "TriggerEventDetail",
"references": {
"TriggerEventDetail": {
"location": "import",
"path": "./text-editor.types"
}
}
}
}, {
"method": "triggerChange",
"name": "triggerChange",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [{
"name": "private",
"text": undefined
}, {
"name": "alpha",
"text": undefined
}],
"text": "Dispatched if a input is changed during an active trigger."
},
"complexType": {
"original": "TriggerEventDetail",
"resolved": "TriggerEventDetail",
"references": {
"TriggerEventDetail": {
"location": "import",
"path": "./text-editor.types"
}
}
}
}];
}
}
//# sourceMappingURL=text-editor.js.map