All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.devbench.uibuilder.components.richtext.UIBuilderRichTextEditor Maven / Gradle / Ivy

The newest version!
/*
 *
 * Copyright © 2018 Webvalto Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.devbench.uibuilder.components.richtext;

import com.vaadin.flow.component.*;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.dom.DisabledUpdateMode;
import com.vaadin.flow.dom.DomListenerRegistration;
import elemental.json.Json;
import elemental.json.JsonObject;
import elemental.json.JsonType;
import elemental.json.JsonValue;
import io.devbench.quilldelta.Delta;
import io.devbench.quilldelta.FormatterManager;
import io.devbench.quilldelta.Ops;
import io.devbench.quilldelta.formatter.DeltaFormatter;
import io.devbench.quilldelta.formatter.HtmlFormatter;
import io.devbench.uibuilder.api.listeners.BackendAttachListener;
import io.devbench.uibuilder.components.richtext.exception.UIBuilderRichTextEditorException;
import org.jetbrains.annotations.Nullable;

import java.util.Optional;

@Tag("uibuilder-rich-text-editor")
@JsModule("./uibuilder-rich-text-editor/src/uibuilder-rich-text-editor.js")
@NpmPackage(value = "quill", version = "1.3.7")
@CssImport(value = "quill/dist/quill.snow.css")
@CssImport(value = "quill/dist/quill.bubble.css")
@CssImport(value = "quill/dist/quill.snow.css", themeFor = "uibuilder-rich-text-editor")
@CssImport(value = "quill/dist/quill.bubble.css", themeFor = "uibuilder-rich-text-editor")
public class UIBuilderRichTextEditor extends AbstractSinglePropertyField implements HasComponents, BackendAttachListener {

    static final String ATTR_HTML_RENDER_MODE = "html-render-mode";
    static final String ATTR_VALUE_MODE = "value-mode";
    static final String ATTR_FORMATTER = "formatter";

    static final String EVENT_DETAIL_OPS = "event.detail.ops";
    static final String EVENT_DETAIL_HTML = "event.detail.html";
    static final String EVENT_DETAIL_RESET = "event.detail.reset";

    private static final String HTML_RENDER_MODE = "htmlRenderMode";
    private static final String VALUE_MODE = "valueMode";
    private static final String FORMATTER = ATTR_FORMATTER;
    private static final String DEFAULT_FORMATTER = "HTML";

    private static final String VALUE = "value";
    private static final String CHANGED_SUFFIX = "-changed";
    private static final String VALUE_CHANGED = VALUE + CHANGED_SUFFIX;

    private static final PropertyDescriptor PROP_HTML_RENDER_MODE =
        PropertyDescriptors.propertyWithDefault(HTML_RENDER_MODE, HtmlRenderMode.FRONTEND.name().toLowerCase());

    private static final PropertyDescriptor PROP_VALUE_MODE =
        PropertyDescriptors.propertyWithDefault(VALUE_MODE, ValueMode.HTML.name().toLowerCase());

    private static final PropertyDescriptor PROP_FORMATTER =
        PropertyDescriptors.propertyWithDefault(FORMATTER, DEFAULT_FORMATTER);

    private final Delta delta;
    private final HtmlFormatter htmlFormatter;
    private String htmlText;

    public UIBuilderRichTextEditor() {
        super(VALUE, "", true);
        delta = new Delta();
        htmlFormatter = new HtmlFormatter();
    }

    @Synchronize(property = HTML_RENDER_MODE, value = {ATTR_HTML_RENDER_MODE + CHANGED_SUFFIX, VALUE_CHANGED}, allowUpdates = DisabledUpdateMode.ALWAYS)
    public HtmlRenderMode getHtmlRenderMode() {
        return HtmlRenderMode.valueOf(get(PROP_HTML_RENDER_MODE).toUpperCase());
    }

    public void setHtmlRenderMode(HtmlRenderMode htmlRenderMode) {
        set(PROP_HTML_RENDER_MODE, htmlRenderMode.name().toLowerCase());
    }

    @Synchronize(property = VALUE_MODE, value = {ATTR_VALUE_MODE + CHANGED_SUFFIX, VALUE_CHANGED}, allowUpdates = DisabledUpdateMode.ALWAYS)
    public ValueMode getValueMode() {
        return ValueMode.valueOf(get(PROP_VALUE_MODE).toUpperCase());
    }

    public void setValueMode(ValueMode valueMode) {
        set(PROP_VALUE_MODE, valueMode.name().toLowerCase());
    }

    @Synchronize(property = FORMATTER, value = {ATTR_FORMATTER + CHANGED_SUFFIX, VALUE_CHANGED}, allowUpdates = DisabledUpdateMode.ALWAYS)
    public String getFormatterName() {
        return get(PROP_FORMATTER);
    }

    public void setFormatterName(String formatterName) {
        set(PROP_FORMATTER, FormatterManager.getInstance()
            .findFormatter(formatterName)
            .map(DeltaFormatter::getFormatterName)
            .orElseThrow(() -> new UIBuilderRichTextEditorException("Formatter not found: " + formatterName)));
    }

    public String getValueAsPlainText() {
        return delta.getText();
    }

    public void setValueAsPlainText(String plainText) {
        getElement().executeJs("$0.setValueAsPlainText($1)", this, plainText);
    }

    public String getValueAsHtmlText() {
        return getHtmlRenderMode() == HtmlRenderMode.BACKEND ? htmlFormatter.render(delta) : htmlText;
    }

    public void setValueAsHtmlText(String htmlAsText) {
        getElement().executeJs("$0.setValueAsHtmlText($1)", this, htmlAsText);
    }

    public Ops getValueAsOps() {
        return delta.getOps();
    }

    public void setValueAsOps(Ops ops) {
        getElement().executeJs("$0.setValueAsOps($1)", this, ops.toJson());
    }

    public String getFormattedText() {
        return getFormatterIfRendererPresent()
            .map(deltaFormatter -> deltaFormatter.render(delta))
            .orElseThrow(() -> new UIBuilderRichTextEditorException("Formatter or format renderer not found"));
    }

    public void setFormattedText(String renderedValue) {
        setValueAsOps(
            getFormatterIfParserPresent()
                .orElseThrow(() -> new UIBuilderRichTextEditorException("Formatter or format parser not found"))
                .parse(renderedValue).getOps());
    }

    private Optional getFormatterIfRendererPresent() {
        DeltaFormatter formatter = getFormatter();
        return formatter != null && formatter.getRenderer() != null ? Optional.of(formatter) : Optional.empty();
    }

    private Optional getFormatterIfParserPresent() {
        DeltaFormatter formatter = getFormatter();
        return formatter != null && formatter.getParser() != null ? Optional.of(formatter) : Optional.empty();
    }

    @Nullable
    private DeltaFormatter getFormatter() {
        return FormatterManager.getInstance().findFormatter(getFormatterName()).orElse(null);
    }

    @Override
    public void onAttached() {
        DomListenerRegistration deltaEventRegistration = getElement().addEventListener("delta", event -> {
            JsonObject jsonOps = event.getEventData().getObject(EVENT_DETAIL_OPS);
            JsonValue jsonHtml = event.getEventData().get(EVENT_DETAIL_HTML);

            String oldValue = getValue();

            if (event.getEventData().getBoolean(EVENT_DETAIL_RESET)) {
                delta.clear();
            }

            htmlText = jsonHtml.getType() == JsonType.STRING ? jsonHtml.asString() : null;
            Ops ops = Ops.fromJson(jsonOps);
            delta.apply(ops);

            ComponentUtil.fireEvent(this, new ComponentValueChangeEvent<>(this, this, oldValue, true));
        });

        deltaEventRegistration
            .addEventData(EVENT_DETAIL_OPS)
            .addEventData(EVENT_DETAIL_HTML)
            .addEventData(EVENT_DETAIL_RESET);
    }

    @Override
    public String getValue() {
        ValueMode valueMode = getValueMode();
        switch (valueMode) {
            case HTML:
                return getValueAsHtmlText();
            case PLAIN:
                return getValueAsPlainText();
            case DELTA:
                return getValueAsOps().toJson().toJson();
            case FORMATTED:
                return getFormattedText();
        }
        return super.getValue();
    }

    @Override
    public void setValue(String value) {
        ValueMode valueMode = getValueMode();
        switch (valueMode) {
            case HTML:
                setValueAsHtmlText(value);
                return;
            case PLAIN:
                setValueAsPlainText(value);
                return;
            case DELTA:
                setValueAsOps(Ops.fromJson(Json.parse(value)));
                return;
            case FORMATTED:
                setFormattedText(value);
                return;
        }
        delta.clear();
        super.setValue(value);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy