Skip to content

Commit 084fc5d

Browse files
committed
add character data observer, add primary addons to card, minor tabs fix
1 parent 64d1d90 commit 084fc5d

File tree

9 files changed

+189
-25
lines changed

9 files changed

+189
-25
lines changed

domino-ui/src/main/java/org/dominokit/domino/ui/cards/BaseCard.java

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
import org.dominokit.domino.ui.utils.NullLazyChild;
4444
import org.dominokit.domino.ui.utils.PostfixAddOn;
4545
import org.dominokit.domino.ui.utils.PostfixElement;
46-
import org.dominokit.domino.ui.utils.PrefixAddOn;
4746
import org.dominokit.domino.ui.utils.PrefixElement;
47+
import org.dominokit.domino.ui.utils.PrimaryAddOnElement;
4848

4949
public abstract class BaseCard<C extends BaseCard<C>> extends BaseDominoElement<HTMLDivElement, C>
5050
implements CardStyles, CollapsibleElement<C>, HasComponentConfig<CardConfig> {
@@ -468,28 +468,6 @@ public C withIcon(ChildHandler<CardHeader, Icon<?>> handler) {
468468
return (C) this;
469469
}
470470

471-
/**
472-
* Appends an element to the right side of the card header.
473-
*
474-
* @param postfix A {@link org.dominokit.domino.ui.utils.PostfixAddOn} wrapped element
475-
* @return same card instance
476-
*/
477-
public C appendChild(PostfixAddOn<?> postfix) {
478-
getPostfixElement().appendChild(postfix);
479-
return (C) this;
480-
}
481-
482-
/**
483-
* Appends an element to the left side of the card header, between the logo and the title.
484-
*
485-
* @param prefix A {@link org.dominokit.domino.ui.utils.PrefixAddOn} wrapped element
486-
* @return same card instance
487-
*/
488-
public C appendChild(PrefixAddOn<?> prefix) {
489-
getPrefixElement().appendChild(prefix);
490-
return (C) this;
491-
}
492-
493471
@Override
494472
public PostfixElement getPostfixElement() {
495473
return header.get().getPostfixElement();
@@ -500,6 +478,11 @@ public PrefixElement getPrefixElement() {
500478
return header.get().getPrefixElement();
501479
}
502480

481+
@Override
482+
public PrimaryAddOnElement getPrimaryAddonsElement() {
483+
return header.get().getPrimaryAddonsElement();
484+
}
485+
503486
/**
504487
* Appends an element to the right side of the card header.
505488
*

domino-ui/src/main/java/org/dominokit/domino/ui/cards/CardHeader.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,11 @@ public PrefixElement getPrefixElement() {
459459
return PrefixElement.of(mainHeader.get().element());
460460
}
461461

462+
@Override
463+
public PrimaryAddOnElement getPrimaryAddonsElement() {
464+
return PrimaryAddOnElement.of(mainHeader.get().element());
465+
}
466+
462467
/** @dominokit-site-ignore {@inheritDoc} */
463468
@Override
464469
public HTMLDivElement element() {

domino-ui/src/main/java/org/dominokit/domino/ui/tabs/Tab.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ public Tab activate() {
352352
* @return The current {@link Tab} instance.
353353
*/
354354
public Tab activate(boolean silent) {
355-
if (nonNull(parent)) {
355+
if (nonNull(parent) && nonNull(parent.getActiveTab())) {
356356
parent.deActivateTab(parent.getActiveTab(), silent);
357357
}
358358
dui_active.apply(tab, tabPanel);

domino-ui/src/main/java/org/dominokit/domino/ui/tabs/TabsPanel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ public TabsPanel insertAt(int index, Tab tab) {
112112
if (nonNull(tab)) {
113113
tabs.add(index, tab);
114114
if (isNull(activeTab) && autoActivate) {
115+
activateTab(tab);
115116
this.activeTab = tab;
116-
activateTab(this.activeTab);
117117
} else {
118118
if (tab.isActive()) {
119119
activateTab(tab);

domino-ui/src/main/java/org/dominokit/domino/ui/utils/BaseDominoElement.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public abstract class BaseDominoElement<E extends Element, T extends IsElement<E
125125
public static String ATTACH_UID_KEY = "dui-on-attach-uid";
126126
public static String DETACH_UID_KEY = "dui-on-detach-uid";
127127
public static String ATTRIBUTE_CHANGE_UID_KEY = "dui-on-attribute-change-uid";
128+
public static String CHARACTER_DATA_CHANGE_UID_KEY = "dui-on-character-data-change-uid";
128129

129130
@Editor.Ignore protected T element;
130131
/** A unique identifier for this DOM element. */
@@ -176,6 +177,7 @@ public abstract class BaseDominoElement<E extends Element, T extends IsElement<E
176177
private EventListener attachEventListener;
177178
private EventListener detachEventListener;
178179
private EventListener attributeChangeEventListener;
180+
private EventListener textContentChangeEventListener;
179181
private List<Consumer<T>> onBeforeRemoveHandlers;
180182
private List<Consumer<T>> onRemoveHandlers;
181183
private Map<String, ComponentMeta> metaObjects;
@@ -1129,6 +1131,50 @@ private Map<String, List<MutationObserverCallback>> getAttributesObservers() {
11291131
return Js.uncheckedCast(asPropertyMap.get("dui-attribute-observers"));
11301132
}
11311133

1134+
/**
1135+
* Registers an observer to be notified when this element is attached to the DOM.
1136+
*
1137+
* @param observerCallback The observer to be registered.
1138+
* @return The modified DOM element.
1139+
*/
1140+
@Editor.Ignore
1141+
public T onTextContentChange(MutationObserverCallback observerCallback) {
1142+
Set<MutationObserverCallback> original = getTextContentObservers();
1143+
Set<MutationObserverCallback> textContentObservers = new HashSet<>(original);
1144+
if (isNull(this.textContentChangeEventListener)) {
1145+
if (!hasAttribute(CHARACTER_DATA_CHANGE_UID_KEY)) {
1146+
setAttribute(CHARACTER_DATA_CHANGE_UID_KEY, DominoId.unique());
1147+
}
1148+
this.textContentChangeEventListener =
1149+
evt -> {
1150+
CustomEvent cevent = Js.uncheckedCast(evt);
1151+
MutationRecord record = Js.uncheckedCast(cevent.detail);
1152+
1153+
textContentObservers.forEach(
1154+
callback -> {
1155+
callback.onObserved(Js.uncheckedCast(cevent.detail));
1156+
if (callback.isAutoRemove()) {
1157+
original.remove(callback);
1158+
}
1159+
});
1160+
};
1161+
String type = ObserverEventType.characterDataType(this);
1162+
this.element.element().addEventListener(type, this.textContentChangeEventListener);
1163+
}
1164+
1165+
textContentObservers.add(observerCallback);
1166+
ElementUtil.startObservingTextContent();
1167+
return element;
1168+
}
1169+
1170+
private Set<MutationObserverCallback> getTextContentObservers() {
1171+
JsPropertyMap<Object> asPropertyMap = Js.asPropertyMap(element());
1172+
if (!asPropertyMap.has("dui-text-content-observers")) {
1173+
asPropertyMap.set("dui-text-content-observers", new HashSet<>());
1174+
}
1175+
return Js.uncheckedCast(asPropertyMap.get("dui-text-content-observers"));
1176+
}
1177+
11321178
/**
11331179
* Registers a resize handler to be notified when the size of this element changes.
11341180
*
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright © 2019 Dominokit
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.dominokit.domino.ui.utils;
17+
18+
import static elemental2.dom.DomGlobal.document;
19+
20+
import elemental2.core.JsArray;
21+
import elemental2.dom.CustomEvent;
22+
import elemental2.dom.CustomEventInit;
23+
import elemental2.dom.DomGlobal;
24+
import elemental2.dom.Element;
25+
import elemental2.dom.MutationObserver;
26+
import elemental2.dom.MutationObserverInit;
27+
import elemental2.dom.MutationRecord;
28+
import elemental2.dom.Node;
29+
import jsinterop.base.Js;
30+
31+
/**
32+
* The {@code BodyObserver} class is responsible for observing mutations in the document's body. It
33+
* tracks the addition and removal of elements with specific attributes and dispatches events
34+
* accordingly.
35+
*/
36+
final class CharacterDataObserver {
37+
38+
private static boolean ready = false;
39+
private static boolean paused = false;
40+
private static MutationObserver mutationObserver;
41+
42+
private CharacterDataObserver() {}
43+
44+
/**
45+
* Pauses the observer for a specified action and resumes it afterward.
46+
*
47+
* @param handler The action to perform while the observer is paused.
48+
*/
49+
static void pauseFor(Runnable handler) {
50+
paused = true;
51+
try {
52+
handler.run();
53+
} finally {
54+
DomGlobal.setTimeout(p0 -> paused = false, 0);
55+
}
56+
}
57+
58+
/** Starts observing mutations in the document's body. */
59+
static void startObserving() {
60+
if (!ready) {
61+
mutationObserver =
62+
new MutationObserver(
63+
(JsArray<MutationRecord> records, MutationObserver observer) -> {
64+
if (!paused) {
65+
MutationRecord[] recordsArray =
66+
Js.uncheckedCast(records.asArray(new MutationRecord[records.length]));
67+
for (MutationRecord record : recordsArray) {
68+
if ("characterData".equalsIgnoreCase(record.type)) {
69+
onElementTextChanged(record);
70+
}
71+
}
72+
}
73+
return null;
74+
});
75+
76+
observe();
77+
ready = true;
78+
}
79+
}
80+
81+
private static void onElementTextChanged(MutationRecord record) {
82+
CustomEventInit<MutationRecord> ceinit = CustomEventInit.create();
83+
ceinit.setDetail(record);
84+
Node target = Js.uncheckedCast(record.target);
85+
Element parentElement = target.parentElement;
86+
String type = ObserverEventType.characterDataType(parentElement);
87+
88+
CustomEvent<MutationRecord> event = new CustomEvent<>(type, ceinit);
89+
parentElement.dispatchEvent(event);
90+
}
91+
92+
private static void observe() {
93+
MutationObserverInit mutationObserverInit = MutationObserverInit.create();
94+
mutationObserverInit.setSubtree(true);
95+
mutationObserverInit.setCharacterData(true);
96+
mutationObserverInit.setCharacterDataOldValue(true);
97+
mutationObserver.observe(document.body, mutationObserverInit);
98+
}
99+
}

domino-ui/src/main/java/org/dominokit/domino/ui/utils/ElementUtil.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ public static void startObservingAttributes() {
188188
AttributesObserver.startObserving();
189189
}
190190

191+
/** Starts observing the body for elements attributes changes events. */
192+
public static void startObservingTextContent() {
193+
CharacterDataObserver.startObserving();
194+
}
195+
191196
/**
192197
* Registers an observer to be notified when an HTMLElement is detached from the DOM.
193198
*

domino-ui/src/main/java/org/dominokit/domino/ui/utils/ObserverEventType.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,26 @@ public static String attributeType(HasAttributes<?> element) {
6161
return "dui-attribute-change-"
6262
+ element.getAttribute(BaseDominoElement.ATTRIBUTE_CHANGE_UID_KEY);
6363
}
64+
65+
/**
66+
* Generates an event type for a detached element.
67+
*
68+
* @param element The element that has been detached.
69+
* @return A string representing the event type for detached elements.
70+
*/
71+
public static String characterDataType(Element element) {
72+
return "dui-character-data-change-"
73+
+ element.getAttribute(BaseDominoElement.CHARACTER_DATA_CHANGE_UID_KEY);
74+
}
75+
76+
/**
77+
* Generates an event type for a detached element.
78+
*
79+
* @param element The element that has been detached.
80+
* @return A string representing the event type for detached elements.
81+
*/
82+
public static String characterDataType(HasAttributes<?> element) {
83+
return "dui-character-data-change-"
84+
+ element.getAttribute(BaseDominoElement.CHARACTER_DATA_CHANGE_UID_KEY);
85+
}
6486
}

domino-ui/src/main/resources/org/dominokit/domino/ui/public/css/domino-ui/dui-components/domino-ui-cards.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@
101101
order: 60;
102102
}
103103

104+
.dui-card-header .dui-primary-addon:not(.dui-order-last) {
105+
order: 80;
106+
}
107+
104108
.dui-card-subheader {
105109
padding: var(--dui-cardsubheader-padding);
106110
display: flex;

0 commit comments

Comments
 (0)