diff --git a/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/SpacingCss.java b/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/SpacingCss.java index d214ccd00..fdb271160 100644 --- a/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/SpacingCss.java +++ b/domino-ui-shared/src/main/java/org/dominokit/domino/ui/style/SpacingCss.java @@ -548,6 +548,8 @@ public interface SpacingCss { CssClass dui_h_full = () -> "dui-h-full"; + CssClass dui_h_inherit = () -> "dui-h-inherit"; + CssClass dui_h_px = () -> "dui-h-px"; CssClass dui_inset_0_5 = () -> "dui-inset-0_5"; @@ -3034,6 +3036,8 @@ public interface SpacingCss { CssClass dui_w_full = () -> "dui-w-full"; + CssClass dui_w_inherit = () -> "dui-w-inherit"; + CssClass dui_w_px = () -> "dui-w-px"; CssClass dui_z_0 = () -> "dui-z-0"; @@ -3774,6 +3778,10 @@ public interface SpacingCss { CssClass dui_whitespace_pre_wrap = () -> "dui-whitespace-pre-wrap"; + CssClass dui_whitespace_unset = () -> "dui-whitespace-unset"; + + CssClass dui_whitespace_break_spaces = () -> "dui-whitespace-break-spaces"; + CssClass dui_break_normal = () -> "dui-break-normal"; CssClass dui_break_words = () -> "dui-break-words"; diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/forms/TextAreaBox.java b/domino-ui/src/main/java/org/dominokit/domino/ui/forms/TextAreaBox.java index 74314647e..cf1b913bc 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/forms/TextAreaBox.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/forms/TextAreaBox.java @@ -25,6 +25,9 @@ import org.dominokit.domino.ui.elements.SpanElement; import org.dominokit.domino.ui.utils.DominoElement; import org.dominokit.domino.ui.utils.FillerElement; +import org.dominokit.domino.ui.utils.IntersectionObserver; +import org.dominokit.domino.ui.utils.IntersectionObserverEntry; +import org.dominokit.domino.ui.utils.IntersectionObserverOptions; import org.dominokit.domino.ui.utils.LazyChild; import org.dominokit.domino.ui.utils.PostfixAddOn; import org.dominokit.domino.ui.utils.PrefixAddOn; @@ -55,6 +58,7 @@ public class TextAreaBox extends CountableInputFormField headerFiller; + private IntersectionObserver intersectionObserver; /** * Factory method to create a new instance of {@link TextAreaBox}. @@ -79,19 +83,23 @@ public static TextAreaBox create(String label) { public TextAreaBox() { setRows(4); addCss(dui_form_text_area); - wrapperElement.appendChild( - header = - div() - .addCss( - dui_form_text_area_header, - dui_hide_empty, - dui_flex, - dui_items_center, - dui_order_first)); + wrapperElement + .appendChild( + header = + div() + .addCss( + dui_form_text_area_header, + dui_hide_empty, + dui_flex, + dui_items_center, + dui_order_first)) + .addCss(dui_h_inherit); + bodyElement.addCss(dui_h_inherit); + headerFiller = LazyChild.of(FillerElement.create().addCss(dui_order_30), header); onAttached(mutationRecord -> adjustHeight()); setDefaultValue(""); - getInputElement().setAttribute("data-scroll", "0"); + getInputElement().addCss(dui_h_inherit).setAttribute("data-scroll", "0"); getInputElement() .addEventListener( "scroll", @@ -99,6 +107,19 @@ public TextAreaBox() { getInputElement() .element() .setAttribute("data-scroll", getInputElement().element().scrollTop)); + + intersectionObserver = + new IntersectionObserver( + entries -> { + IntersectionObserverEntry entry = entries.asList().get(0); + if (entry.getIsIntersecting()) { + adjustHeight(); + intersectionObserver.unobserve(this.element()); + intersectionObserver.disconnect(); + } + }, + IntersectionObserverOptions.create()); + intersectionObserver.observe(this.element()); } @Override @@ -197,12 +218,10 @@ protected DominoElement createInputElement(String type) { protected void doSetValue(String value) { if (nonNull(value)) { getInputElement().element().value = value; - if (isAttached()) { - adjustHeight(); - } } else { getInputElement().element().value = ""; } + nowOrWhenAttached(this::adjustHeight); updateCounter(getLength(), getMaxCount()); } @@ -235,13 +254,11 @@ public TextAreaBox fixedSize() { /** Adjusts the height of the text area based on its content. */ private void adjustHeight() { - getInputElement().style().setHeight("auto"); - int scrollHeight = getInputElement().element().scrollHeight; - if (scrollHeight < 30) { - scrollHeight = 28; - } + if (autoSize) { - getInputElement().style().setHeight(scrollHeight + "px"); + getInputElement().style().setHeight("auto"); + int scrollHeight = getInputElement().element().scrollHeight; + getInputElement().style().setHeight(Math.max(scrollHeight, 28) + "px"); } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/IntersectionObserver.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/IntersectionObserver.java new file mode 100644 index 000000000..31a4934b4 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/IntersectionObserver.java @@ -0,0 +1,81 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.domino.ui.utils; + +import elemental2.core.JsArray; +import elemental2.dom.Element; +import jsinterop.annotations.JsFunction; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsType; + +/** + * provides a way to asynchronously observe changes in the intersection of a target element with an + * ancestor element or with a top-level document's viewport. + * + * @see MDN Web Docs + * (IntersectionObserver) + */ +@JsType(isNative = true, namespace = JsPackage.GLOBAL) +public class IntersectionObserver { + + /** + * Disconnects the {@code IntersectionObserver} instance, stopping it from tracking changes in + * element intersection. + */ + public native void disconnect(); + + /** + * Begins observing the specified {@link Element} for intersection changes. + * + * @param target The DOM element to observe for intersection changes. + */ + public native void observe(Element target); + + /** + * Stops observing the specified {@link Element} for intersection changes. + * + * @param target The DOM element to stop observing. + */ + public native void unobserve(Element target); + + public native JsArray takeRecords(); + + /** + * A functional interface representing a callback function to be invoked when intersection changes + * are observed. + */ + @JsFunction + public interface IntersectionObserverCallbackFn { + /** + * Invoked when element intersection with viewport is observed. + * + * @param entries An array of {@link IntersectionObserverEntry} objects describing the observed + * intersection changes. + */ + void onInvoke(JsArray entries); + } + + /** + * Constructs a {@code IntersectionObserver} instance with the specified callback function. + * + * @param callback The callback function to be invoked when size changes are observed. + * @param options The {@link IntersectionObserverOptions} to configure the IntersectionObserver + */ + public IntersectionObserver( + IntersectionObserver.IntersectionObserverCallbackFn callback, + IntersectionObserverOptions options) {} +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/IntersectionObserverEntry.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/IntersectionObserverEntry.java new file mode 100644 index 000000000..cbdfe3ca3 --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/IntersectionObserverEntry.java @@ -0,0 +1,58 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.domino.ui.utils; + +import elemental2.dom.DOMRect; +import elemental2.dom.Element; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsProperty; +import jsinterop.annotations.JsType; + +/** + * The IntersectionObserverEntry interface of the Intersection Observer API describes the + * intersection between the target element and its root container at a specific moment of + * transition. + * + * @see MDN Web + * Docs (IntersectionObserverEntry) + */ +@JsType(isNative = true, namespace = JsPackage.GLOBAL) +public interface IntersectionObserverEntry { + + @JsProperty + DOMRect getBoundingClientRect(); + + @JsProperty + double getIntersectionRatio(); + + @JsProperty + DOMRect getIntersectionRect(); + + @JsProperty + boolean getIsIntersecting(); + + @JsProperty + boolean getIsVisible(); + + @JsProperty + DOMRect getRootBounds(); + + @JsProperty + Element getTarget(); + + @JsProperty + double getTime(); +} diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/utils/IntersectionObserverOptions.java b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/IntersectionObserverOptions.java new file mode 100644 index 000000000..b29a90d4d --- /dev/null +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/utils/IntersectionObserverOptions.java @@ -0,0 +1,53 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.domino.ui.utils; + +import elemental2.core.JsArray; +import elemental2.dom.Element; +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsType; +import jsinterop.base.Js; +import jsinterop.base.JsPropertyMap; + +/** + * An optional object which customizes the observer. If options isn't specified, the observer uses + * the document's viewport as the root, with no margin, and a 0% threshold (meaning that even a + * one-pixel change is enough to trigger a callback). You can provide any combination of the + * following options: + * + * @see MDN + * Web Docs (IntersectionObserver) + */ +@JsType(isNative = true, namespace = JsPackage.GLOBAL) +public class IntersectionObserverOptions { + + /** + * Creates a new instance of {@code ResizeObserverOptions} with default settings. + * + * @return A {@code ResizeObserverOptions} instance with default settings. + */ + @JsOverlay + public static IntersectionObserverOptions create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + public Element root; + public String rootMargin; + public JsArray threshold; +} diff --git a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/domino-ui/dui-components/domino-ui-forms.css b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/domino-ui/dui-components/domino-ui-forms.css index a7cd59269..78b038ff4 100644 --- a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/domino-ui/dui-components/domino-ui-forms.css +++ b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/domino-ui/dui-components/domino-ui-forms.css @@ -13,6 +13,7 @@ fieldset.dui { margin-inline: 0; max-inline-size: 100%; min-inline-size:0; + --dui-form-field-input-whitespace: nowrap; } .dui-grid-col > .dui-form-field { @@ -202,7 +203,7 @@ fieldset.dui { font: var(--dui-form-field-input-line-font); line-height: var(--dui-form-field-input-line-hieght); overflow: var(--dui-form-input-overflow, hidden); - white-space: nowrap; + white-space: var(--dui-form-field-input-whitespace); text-overflow: ellipsis; align-items: center; background-color: inherit; @@ -211,7 +212,8 @@ fieldset.dui { } .dui-form-text-area { - --dui-form-input-overflow: auto + --dui-form-input-overflow: auto; + --dui-form-field-input-whitespace:unset; } .dui-field-input:focus { diff --git a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/domino-ui/dui-components/domino-ui-spacing.css b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/domino-ui/dui-components/domino-ui-spacing.css index a1eeafbd5..5317e9675 100644 --- a/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/domino-ui/dui-components/domino-ui-spacing.css +++ b/domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/domino-ui/dui-components/domino-ui-spacing.css @@ -6214,6 +6214,10 @@ width: var(--dui-spc-full); } +.dui.dui-w-inherit { + width: inherit; +} + .dui.dui-w-auto { width: auto; } @@ -6230,6 +6234,10 @@ height: var(--dui-spc-full); } +.dui.dui-h-inherit { + height: inherit; +} + .dui.dui-max-h-full { max-height: var(--dui-spc-full); } @@ -8094,6 +8102,14 @@ white-space: pre-wrap; } +.dui.dui-whitespace-unset { + white-space: unset; +} + +.dui.dui-whitespace-break-spaces { + white-space: break-spaces; +} + .dui.dui-break-normal { overflow-wrap: normal; word-break: normal;