diff --git a/src/main/java/com/flowingcode/vaadin/addons/markdown/BaseMarkdownComponent.java b/src/main/java/com/flowingcode/vaadin/addons/markdown/BaseMarkdownComponent.java index 2049209..89cda87 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/markdown/BaseMarkdownComponent.java +++ b/src/main/java/com/flowingcode/vaadin/addons/markdown/BaseMarkdownComponent.java @@ -19,8 +19,12 @@ */ package com.flowingcode.vaadin.addons.markdown; +import com.vaadin.flow.component.AttachEvent; +import com.vaadin.flow.component.DetachEvent; import com.vaadin.flow.component.HasSize; +import com.vaadin.flow.component.UI; import com.vaadin.flow.component.dependency.CssImport; +import com.vaadin.flow.component.dependency.JavaScript; import com.vaadin.flow.component.dependency.NpmPackage; import com.vaadin.flow.component.react.ReactAdapterComponent; import com.vaadin.flow.function.SerializableConsumer; @@ -33,29 +37,9 @@ @NpmPackage(value = "mermaid", version = "11.2.1") @NpmPackage(value = "@uiw/react-md-editor", version = "4.0.4") @NpmPackage(value = "dompurify", version = "3.1.6") +@JavaScript("./connector.js") public class BaseMarkdownComponent extends ReactAdapterComponent implements HasSize { - /** - * Defines the color schemes for the Markdown component. - * - * The color mode can be set using the {@link #setDataColorMode(DataColorMode)} method. - * - * - */ - public enum DataColorMode { - DARK, - LIGHT, - /** - * @deprecated Use LIGHT instead - */ - @Deprecated - LIGTH, - AUTO}; - private String content; /** @@ -96,24 +80,27 @@ public void addContentChangeListener(SerializableConsumer listener) { addStateChangeListener("content", String.class, listener); } - /** - * Sets the color mode of the Markdown component. - * - * @param mode the color mode of the component - */ - public void setDataColorMode(DataColorMode mode) { - switch (mode) { - case DARK: - getElement().setAttribute("data-color-mode", "dark"); - break; - case LIGTH: - case LIGHT: - getElement().setAttribute("data-color-mode", "light"); - break; - case AUTO: - getElement().removeAttribute("data-color-mode"); - break; - } + private void runBeforeClientResponse(SerializableConsumer command) { + getElement().getNode().runWhenAttached(ui -> ui + .beforeClientResponse(this, context -> command.accept(ui))); } - + + @Override + protected void onAttach(AttachEvent attachEvent) { + super.onAttach(attachEvent); + + runBeforeClientResponse(ui -> ui.getPage().executeJs( + "window.Vaadin.Flow.fcMarkdownEditorConnector.observeThemeChange($0)", + getElement())); + } + + @Override + protected void onDetach(DetachEvent detachEvent) { + super.onDetach(detachEvent); + + getUI().ifPresent(ui -> ui.getPage().executeJs( + "window.Vaadin.Flow.fcMarkdownEditorConnector.unobserveThemeChange($0)", + this.getElement())); + } + } diff --git a/src/main/java/com/flowingcode/vaadin/addons/markdown/MarkdownEditor.java b/src/main/java/com/flowingcode/vaadin/addons/markdown/MarkdownEditor.java index 81f2dc1..e18b65e 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/markdown/MarkdownEditor.java +++ b/src/main/java/com/flowingcode/vaadin/addons/markdown/MarkdownEditor.java @@ -20,7 +20,6 @@ package com.flowingcode.vaadin.addons.markdown; -import com.flowingcode.vaadin.addons.markdown.BaseMarkdownComponent.DataColorMode; import com.vaadin.flow.component.AbstractCompositeField; import com.vaadin.flow.component.HasSize; @@ -101,15 +100,6 @@ public void setMaxLength(int maxLength) { getEditor().setMaxLength(maxLength); } - /** - * Sets the color mode of the Markdown component. - * - * @param mode the color mode of the component - */ - public void setDataColorMode(DataColorMode mode) { - getEditor().setDataColorMode(mode); - } - @Override protected void setPresentationValue(String newPresentationValue) { getEditor().setContent(newPresentationValue); diff --git a/src/main/java/com/flowingcode/vaadin/addons/markdown/MarkdownViewer.java b/src/main/java/com/flowingcode/vaadin/addons/markdown/MarkdownViewer.java index 2904335..7011083 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/markdown/MarkdownViewer.java +++ b/src/main/java/com/flowingcode/vaadin/addons/markdown/MarkdownViewer.java @@ -22,8 +22,6 @@ import com.vaadin.flow.component.Tag; import com.vaadin.flow.component.dependency.JsModule; -import com.vaadin.flow.component.dependency.NpmPackage; -import com.vaadin.flow.component.react.ReactAdapterComponent; /** * Component for displaying Markdown text diff --git a/src/main/resources/META-INF/resources/frontend/connector.js b/src/main/resources/META-INF/resources/frontend/connector.js new file mode 100644 index 0000000..3b572a6 --- /dev/null +++ b/src/main/resources/META-INF/resources/frontend/connector.js @@ -0,0 +1,82 @@ +/*- + * #%L + * Markdown Editor Add-on + * %% + * Copyright (C) 2025 Flowing Code + * %% + * 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. + * #L% + */ +(function() { + + window.Vaadin.Flow.fcMarkdownEditorConnector = { + observeThemeChange: markDownEditor => { + // Check whether the connector was already initialized for markDownEditor + if (markDownEditor.$connector) { + return; + } + + markDownEditor.$connector = {}; + + const supportedTheme = theme => ['light', 'dark'].includes(theme); + + const setDataColorMode = theme => { + if (supportedTheme(theme)) { + markDownEditor.setAttribute('data-color-mode', theme); + } else { + // force light theme which is Vaadin's default theme + markDownEditor.setAttribute('data-color-mode', 'light'); + } + }; + + // Get theme from html element when using Vaadin's @Theme annotation + const mainTheme = document.documentElement.getAttribute('theme'); + if (supportedTheme(mainTheme)) { + setDataColorMode(mainTheme); + } else { + // set light theme by default + markDownEditor.setAttribute('data-color-mode', 'light'); + } + + // options for the observer (which mutations to observe) + const config = { attributes: true }; + + // callback function to execute when mutations are observed + const callback = (mutationList, observer) => { + for (const mutation of mutationList) { + if (mutation.type === 'attributes' && mutation.attributeName === 'theme') { + const themeName = mutation.target.getAttribute(mutation.attributeName); + console.log("theme", themeName); + setDataColorMode(themeName); + } + } + }; + + // create an observer instance linked to the callback function + markDownEditor.$connector.themeChangeObserver = new MutationObserver(callback); + + // observe html tag for configured mutations + markDownEditor.$connector.themeChangeObserver.observe(document.documentElement, config); + + // observe body tag for configured mutations + markDownEditor.$connector.themeChangeObserver.observe(document.body, config); + }, + unobserveThemeChange: markDownEditor => { + // stop observing the target node for configured mutations + if (markDownEditor.$connector.themeChangeObserver) { + markDownEditor.$connector.themeChangeObserver.disconnect(); + markDownEditor.$connector.themeChangeObserver = null; + } + } + } +})(); diff --git a/src/test/java/com/flowingcode/vaadin/addons/markdown/MarkdownEditorDemo.java b/src/test/java/com/flowingcode/vaadin/addons/markdown/MarkdownEditorDemo.java index 3b4827f..86daa5b 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/markdown/MarkdownEditorDemo.java +++ b/src/test/java/com/flowingcode/vaadin/addons/markdown/MarkdownEditorDemo.java @@ -20,7 +20,7 @@ package com.flowingcode.vaadin.addons.markdown; import com.flowingcode.vaadin.addons.demo.DemoSource; -import com.flowingcode.vaadin.addons.markdown.BaseMarkdownComponent.DataColorMode; +import com.vaadin.flow.component.UI; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.notification.Notification; @@ -28,6 +28,8 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; +import com.vaadin.flow.theme.lumo.Lumo; +import java.util.List; @DemoSource @PageTitle("Markdown Editor Demo") @@ -41,26 +43,6 @@ public MarkdownEditorDemo() { mde.setSizeFull(); mde.setPlaceholder("Enter Markdown here"); mde.setMaxLength(500); - mde.setDataColorMode(DataColorMode.LIGTH); - ComboBox cb = new ComboBox(); - cb.setItems("Dark","Light","Automatic"); - cb.setLabel("Color mode"); - cb.setValue("Light"); - cb.addValueChangeListener(ev->{ - switch(ev.getValue()) { - case "Dark": - mde.setDataColorMode(DataColorMode.DARK); - break; - case "Light": - mde.setDataColorMode(DataColorMode.LIGHT); - break; - case "Automatic": - mde.setDataColorMode(DataColorMode.AUTO); - break; - default: - break; - } - }); Button getContentButton = new Button("Show content",ev->Notification.show(mde.getValue())); Button setSampleContent = new Button("Set sample content",ev->{ mde.setValue(""" @@ -90,6 +72,6 @@ public static void main(String[] args) { ~~This is strikethrough text~~ """); }); - add(mde,cb,new HorizontalLayout(getContentButton,setSampleContent)); + add(mde,new HorizontalLayout(getContentButton,setSampleContent)); } } diff --git a/src/test/java/com/flowingcode/vaadin/addons/markdown/MarkdownViewerDemo.java b/src/test/java/com/flowingcode/vaadin/addons/markdown/MarkdownViewerDemo.java index f60ec19..130a3db 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/markdown/MarkdownViewerDemo.java +++ b/src/test/java/com/flowingcode/vaadin/addons/markdown/MarkdownViewerDemo.java @@ -20,8 +20,6 @@ package com.flowingcode.vaadin.addons.markdown; import com.flowingcode.vaadin.addons.demo.DemoSource; -import com.flowingcode.vaadin.addons.markdown.BaseMarkdownComponent.DataColorMode; -import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; @@ -36,7 +34,6 @@ public MarkdownViewerDemo() { setSizeFull(); MarkdownViewer mdv = new MarkdownViewer(); mdv.setSizeFull(); - mdv.setDataColorMode(DataColorMode.LIGTH); mdv.setContent(""" # h1 Heading @@ -202,25 +199,6 @@ public MarkdownViewerDemo() { [^second]: Footnote text. """); - ComboBox cb = new ComboBox(); - cb.setItems("Dark","Light","Automatic"); - cb.setLabel("Color mode"); - cb.setValue("Light"); - cb.addValueChangeListener(ev->{ - switch(ev.getValue()) { - case "Dark": - mdv.setDataColorMode(DataColorMode.DARK); - break; - case "Light": - mdv.setDataColorMode(DataColorMode.LIGHT); - break; - case "Automatic": - mdv.setDataColorMode(DataColorMode.AUTO); - break; - default: - break; - } - }); - add(mdv,cb); + add(mdv); } }