@schukai/monster 4.128.3 → 4.129.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/package.json +1 -1
- package/source/components/datatable/datatable.mjs +4 -1
- package/source/components/datatable/pagination.mjs +39 -1
- package/source/components/form/login.mjs +197 -0
- package/source/components/form/message-state-button.mjs +233 -18
- package/source/components/form/select.mjs +4 -4
- package/source/components/form/style/message-state-button.pcss +46 -3
- package/source/components/form/stylesheet/message-state-button.mjs +7 -14
- package/source/components/form/stylesheet/popper-button.mjs +7 -14
- package/source/components/form/tree-select.mjs +2 -2
- package/source/components/layout/popper.mjs +72 -1
- package/source/components/layout/stylesheet/popper.mjs +7 -14
- package/source/components/style/floating-ui.css +1 -49
- package/source/components/style/floating-ui.pcss +10 -0
- package/source/components/stylesheet/floating-ui.mjs +7 -14
- package/source/dom/customelement.mjs +182 -2
- package/source/dom/util/extract-keys.mjs +46 -10
- package/source/dom/util/init-options-from-attributes.mjs +4 -2
- package/source/dom/util/set-option-from-attribute.mjs +4 -2
- package/source/types/version.mjs +1 -1
- package/test/cases/components/form/login.mjs +168 -0
- package/test/cases/components/form/message-state-button.mjs +272 -0
- package/test/cases/components/form/popper-button.mjs +89 -0
- package/test/cases/components/form/select.mjs +24 -0
- package/test/cases/components/form/tree-select.mjs +22 -1
- package/test/cases/dom/util/extract-keys.mjs +34 -23
- package/test/cases/dom/util/init-options-from-attributes.mjs +21 -0
- package/test/cases/monster.mjs +1 -1
- package/test/web/import.js +1 -0
- package/test/web/test.html +2 -2
- package/test/web/tests.js +9300 -6050
package/CHANGELOG.md
CHANGED
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
- **customelement:** skip mutation-observer updater reruns for disconnected or disposed instances
|
|
16
16
|
- **message-state-button:** clear auto-hide timers on disconnect and ignore delayed hides after removal
|
|
17
17
|
- **popper:** guard show/hide/update flows against disconnected hosts and missing internal elements
|
|
18
|
+
- **message-state-button:** distinguish overlay, prose, and wide message layouts for smart popper sizing and overflow ([#401](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/401))
|
|
19
|
+
- **popper:** support kebab-case camelCase option attributes such as `data-monster-option-popper-content-overflow` ([#401](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/401))
|
|
18
20
|
|
|
19
21
|
### Changes
|
|
20
22
|
|
|
21
23
|
- document lifecycle ownership rules for Updater-driven and stateful custom element implementations
|
|
24
|
+
- document smart message popper layout behavior for forms and feedback flows ([#401](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/401))
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.129.1"}
|
|
@@ -1692,7 +1692,10 @@ function updateGrid() {
|
|
|
1692
1692
|
* @return {void}
|
|
1693
1693
|
*/
|
|
1694
1694
|
function scheduleGridUpdate() {
|
|
1695
|
-
if (
|
|
1695
|
+
if (
|
|
1696
|
+
this[resizeFrameRequestSymbol] !== null &&
|
|
1697
|
+
this[resizeFrameRequestSymbol] !== undefined
|
|
1698
|
+
) {
|
|
1696
1699
|
return;
|
|
1697
1700
|
}
|
|
1698
1701
|
|
|
@@ -238,6 +238,8 @@ class Pagination extends CustomElement {
|
|
|
238
238
|
list.setAttribute("data-monster-adaptive-ready", "true");
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
|
+
|
|
242
|
+
syncPaginationStateToDom.call(this, pagination);
|
|
241
243
|
}
|
|
242
244
|
/**
|
|
243
245
|
*
|
|
@@ -803,6 +805,42 @@ function buildPagination(current, max) {
|
|
|
803
805
|
};
|
|
804
806
|
}
|
|
805
807
|
|
|
808
|
+
/**
|
|
809
|
+
* @private
|
|
810
|
+
* @param {object} pagination
|
|
811
|
+
* @returns {void}
|
|
812
|
+
*/
|
|
813
|
+
function syncPaginationStateToDom(pagination) {
|
|
814
|
+
if (!this.shadowRoot || !pagination) {
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
const prevLink = this.shadowRoot.querySelector(
|
|
819
|
+
"a[data-monster-role=pagination-prev]",
|
|
820
|
+
);
|
|
821
|
+
if (prevLink instanceof HTMLElement) {
|
|
822
|
+
prevLink.setAttribute("href", pagination.prevHref || "#");
|
|
823
|
+
prevLink.setAttribute("data-page-no", `${pagination.prevNo ?? ""}`);
|
|
824
|
+
prevLink.className = `previous${pagination.prevClass || ""}`.trim();
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const nextLink = this.shadowRoot.querySelector(
|
|
828
|
+
"a[data-monster-role=pagination-next]",
|
|
829
|
+
);
|
|
830
|
+
if (nextLink instanceof HTMLElement) {
|
|
831
|
+
nextLink.setAttribute("href", pagination.nextHref || "#");
|
|
832
|
+
nextLink.setAttribute("data-page-no", `${pagination.nextNo ?? ""}`);
|
|
833
|
+
nextLink.className = `next${pagination.nextClass || ""}`.trim();
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const summary = this.shadowRoot.querySelector(
|
|
837
|
+
"[data-monster-role=pagination-summary] span",
|
|
838
|
+
);
|
|
839
|
+
if (summary instanceof HTMLElement) {
|
|
840
|
+
summary.textContent = pagination.summary || "";
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
806
844
|
/**
|
|
807
845
|
* @private
|
|
808
846
|
*/
|
|
@@ -1226,7 +1264,7 @@ function getTemplate() {
|
|
|
1226
1264
|
data-monster-replace="path:items.label"></a></li>
|
|
1227
1265
|
</template>
|
|
1228
1266
|
|
|
1229
|
-
<div data-monster-role="control" part="control">
|
|
1267
|
+
<div data-monster-role="control" part="control" data-monster-select-this="true">
|
|
1230
1268
|
<nav data-monster-role="pagination" role="navigation" aria-label="pagination" part="nav">
|
|
1231
1269
|
<ul class="pagination-list" data-monster-insert="items path:pagination.items"
|
|
1232
1270
|
data-monster-adaptive-ready="false"
|
|
@@ -89,6 +89,30 @@ const resetLoginProcessLinksSymbol = Symbol("resetLoginProcessLink");
|
|
|
89
89
|
*/
|
|
90
90
|
const loggedInCollapseSymbol = Symbol("loggedInCollapse");
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* @private
|
|
94
|
+
* @type {symbol}
|
|
95
|
+
*/
|
|
96
|
+
const customCollapseSymbol = Symbol("customCollapse");
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @private
|
|
100
|
+
* @type {symbol}
|
|
101
|
+
*/
|
|
102
|
+
const customCollapseHeaderSymbol = Symbol("customCollapseHeader");
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @private
|
|
106
|
+
* @type {symbol}
|
|
107
|
+
*/
|
|
108
|
+
const customCollapseContentSymbol = Symbol("customCollapseContent");
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @private
|
|
112
|
+
* @type {symbol}
|
|
113
|
+
*/
|
|
114
|
+
const customCollapseFooterSymbol = Symbol("customCollapseFooter");
|
|
115
|
+
|
|
92
116
|
/**
|
|
93
117
|
* @private
|
|
94
118
|
* @type {symbol}
|
|
@@ -473,6 +497,98 @@ class Login extends CustomElement {
|
|
|
473
497
|
return this;
|
|
474
498
|
}
|
|
475
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Opens the custom collapse and focuses the first focusable element if available.
|
|
502
|
+
*
|
|
503
|
+
* @returns {Login}
|
|
504
|
+
*/
|
|
505
|
+
openCustomCollapse() {
|
|
506
|
+
this[customCollapseSymbol].open();
|
|
507
|
+
focusFirstCustomCollapseElement.call(this);
|
|
508
|
+
return this;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Replaces the header content of the custom collapse.
|
|
513
|
+
*
|
|
514
|
+
* @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
|
|
515
|
+
* @returns {Login}
|
|
516
|
+
*/
|
|
517
|
+
setCustomCollapseHeader(content) {
|
|
518
|
+
setCustomCollapseSectionContent.call(
|
|
519
|
+
this,
|
|
520
|
+
this[customCollapseHeaderSymbol],
|
|
521
|
+
content,
|
|
522
|
+
);
|
|
523
|
+
return this;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Replaces the main content of the custom collapse.
|
|
528
|
+
*
|
|
529
|
+
* @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
|
|
530
|
+
* @returns {Login}
|
|
531
|
+
*/
|
|
532
|
+
setCustomCollapseContent(content) {
|
|
533
|
+
setCustomCollapseSectionContent.call(
|
|
534
|
+
this,
|
|
535
|
+
this[customCollapseContentSymbol],
|
|
536
|
+
content,
|
|
537
|
+
);
|
|
538
|
+
return this;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Replaces the footer content of the custom collapse.
|
|
543
|
+
*
|
|
544
|
+
* @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
|
|
545
|
+
* @returns {Login}
|
|
546
|
+
*/
|
|
547
|
+
setCustomCollapseFooter(content) {
|
|
548
|
+
setCustomCollapseSectionContent.call(
|
|
549
|
+
this,
|
|
550
|
+
this[customCollapseFooterSymbol],
|
|
551
|
+
content,
|
|
552
|
+
);
|
|
553
|
+
return this;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Clears all custom collapse sections.
|
|
558
|
+
*
|
|
559
|
+
* @returns {Login}
|
|
560
|
+
*/
|
|
561
|
+
clearCustomCollapse() {
|
|
562
|
+
this[customCollapseHeaderSymbol].replaceChildren();
|
|
563
|
+
this[customCollapseContentSymbol].replaceChildren();
|
|
564
|
+
this[customCollapseFooterSymbol].replaceChildren();
|
|
565
|
+
return this;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Replaces the custom collapse content and opens it.
|
|
570
|
+
*
|
|
571
|
+
* @param {string|number|boolean|Node|Array<string|number|boolean|Node>|Object|null|undefined} content
|
|
572
|
+
* @returns {Login}
|
|
573
|
+
*/
|
|
574
|
+
showCustomCollapse(content) {
|
|
575
|
+
if (isObject(content) && !(content instanceof Node) && !isArray(content)) {
|
|
576
|
+
if ("header" in content) {
|
|
577
|
+
this.setCustomCollapseHeader(content.header);
|
|
578
|
+
}
|
|
579
|
+
if ("content" in content) {
|
|
580
|
+
this.setCustomCollapseContent(content.content);
|
|
581
|
+
}
|
|
582
|
+
if ("footer" in content) {
|
|
583
|
+
this.setCustomCollapseFooter(content.footer);
|
|
584
|
+
}
|
|
585
|
+
} else if (content !== undefined) {
|
|
586
|
+
this.setCustomCollapseContent(content);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return this.openCustomCollapse();
|
|
590
|
+
}
|
|
591
|
+
|
|
476
592
|
/**
|
|
477
593
|
* @return {string}
|
|
478
594
|
*/
|
|
@@ -1745,6 +1861,20 @@ function initControlReferences() {
|
|
|
1745
1861
|
`[data-monster-role="logged-in-collapse"]`,
|
|
1746
1862
|
);
|
|
1747
1863
|
|
|
1864
|
+
this[customCollapseSymbol] = this.shadowRoot.querySelector(
|
|
1865
|
+
`[data-monster-role="custom-collapse"]`,
|
|
1866
|
+
);
|
|
1867
|
+
|
|
1868
|
+
this[customCollapseHeaderSymbol] = this.shadowRoot.getElementById(
|
|
1869
|
+
"customCollapseHeader",
|
|
1870
|
+
);
|
|
1871
|
+
this[customCollapseContentSymbol] = this.shadowRoot.getElementById(
|
|
1872
|
+
"customCollapseContent",
|
|
1873
|
+
);
|
|
1874
|
+
this[customCollapseFooterSymbol] = this.shadowRoot.getElementById(
|
|
1875
|
+
"customCollapseFooter",
|
|
1876
|
+
);
|
|
1877
|
+
|
|
1748
1878
|
this[digitsCollapseSymbol] = this.shadowRoot.querySelector(
|
|
1749
1879
|
`[data-monster-role="digits-collapse"]`,
|
|
1750
1880
|
);
|
|
@@ -1957,8 +2087,75 @@ function getTemplate() {
|
|
|
1957
2087
|
<slot name="logged-in-footer"></slot>
|
|
1958
2088
|
</div>
|
|
1959
2089
|
</monster-collapse>
|
|
2090
|
+
<monster-collapse data-monster-role="custom-collapse"
|
|
2091
|
+
exportparts="
|
|
2092
|
+
control:collapse-custom-control,
|
|
2093
|
+
container:collapse-custom-container,
|
|
2094
|
+
deco:collapse-custom-deco"
|
|
2095
|
+
part="custom-collapse">
|
|
2096
|
+
<div part="custom-collapse-wrapper">
|
|
2097
|
+
<div id="customCollapseHeader" part="custom-collapse-header"></div>
|
|
2098
|
+
<div id="customCollapseContent" part="custom-collapse-content"></div>
|
|
2099
|
+
<div id="customCollapseFooter" part="custom-collapse-footer"></div>
|
|
2100
|
+
</div>
|
|
2101
|
+
</monster-collapse>
|
|
1960
2102
|
|
|
1961
2103
|
</div>`;
|
|
1962
2104
|
}
|
|
1963
2105
|
|
|
2106
|
+
/**
|
|
2107
|
+
* @private
|
|
2108
|
+
* @param {HTMLElement} target
|
|
2109
|
+
* @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
|
|
2110
|
+
* @returns {void}
|
|
2111
|
+
*/
|
|
2112
|
+
function setCustomCollapseSectionContent(target, content) {
|
|
2113
|
+
if (!(target instanceof HTMLElement)) {
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
target.replaceChildren(...normalizeCustomCollapseContent.call(this, content));
|
|
2118
|
+
|
|
2119
|
+
if (this[customCollapseSymbol]?.isOpen()) {
|
|
2120
|
+
this[customCollapseSymbol].adjustHeight();
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
/**
|
|
2125
|
+
* @private
|
|
2126
|
+
* @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
|
|
2127
|
+
* @returns {Node[]}
|
|
2128
|
+
*/
|
|
2129
|
+
function normalizeCustomCollapseContent(content) {
|
|
2130
|
+
if (content === null || content === undefined) {
|
|
2131
|
+
return [];
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
if (content instanceof Node) {
|
|
2135
|
+
return [content];
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
if (isArray(content)) {
|
|
2139
|
+
return content.flatMap((entry) =>
|
|
2140
|
+
normalizeCustomCollapseContent.call(this, entry),
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
return [this.ownerDocument.createTextNode(String(content))];
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
/**
|
|
2148
|
+
* @private
|
|
2149
|
+
* @returns {void}
|
|
2150
|
+
*/
|
|
2151
|
+
function focusFirstCustomCollapseElement() {
|
|
2152
|
+
setTimeout(() => {
|
|
2153
|
+
const focusable = this[customCollapseSymbol]?.querySelector(
|
|
2154
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
|
|
2155
|
+
);
|
|
2156
|
+
|
|
2157
|
+
focusable?.focus?.();
|
|
2158
|
+
}, 0);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
1964
2161
|
registerCustomElement(Login);
|
|
@@ -37,9 +37,13 @@ export { MessageStateButton };
|
|
|
37
37
|
const buttonElementSymbol = Symbol("buttonElement");
|
|
38
38
|
const innerDisabledObserverSymbol = Symbol("innerDisabledObserver");
|
|
39
39
|
const popperElementSymbol = Symbol("popperElement");
|
|
40
|
+
const contentElementSymbol = Symbol("contentElement");
|
|
40
41
|
const messageElementSymbol = Symbol("messageElement");
|
|
41
42
|
const measurementPopperSymbol = Symbol("measurementPopper");
|
|
42
43
|
const autoHideTimerSymbol = Symbol("autoHideTimer");
|
|
44
|
+
const MESSAGE_LAYOUT_OVERLAY = "overlay";
|
|
45
|
+
const MESSAGE_LAYOUT_PROSE = "prose";
|
|
46
|
+
const MESSAGE_LAYOUT_WIDE = "wide";
|
|
43
47
|
|
|
44
48
|
/**
|
|
45
49
|
* A specialized button component that combines state management with message display capabilities.
|
|
@@ -111,6 +115,8 @@ class MessageStateButton extends Popper {
|
|
|
111
115
|
* @property {string|number} message.width.min Minimum width (px, rem, em, vw)
|
|
112
116
|
* @property {string|number} message.width.max Maximum width (px, rem, em, vw)
|
|
113
117
|
* @property {number} message.width.viewportRatio Max width as ratio of viewport width (0-1)
|
|
118
|
+
* @property {Object} popper Popper options inherited from the base popper
|
|
119
|
+
* @property {string} popper.contentOverflow Content clipping mode: both|horizontal|smart
|
|
114
120
|
* @property {string} mode The mode of the button, can be `manual` or `submit`
|
|
115
121
|
* @property {string} labels.button Button label
|
|
116
122
|
* @property {Object} classes Classes for internal elements
|
|
@@ -122,20 +128,25 @@ class MessageStateButton extends Popper {
|
|
|
122
128
|
* @property {string} aria.label Aria label for the button
|
|
123
129
|
*/
|
|
124
130
|
get defaults() {
|
|
125
|
-
|
|
131
|
+
const defaults = super.defaults;
|
|
132
|
+
|
|
133
|
+
return Object.assign({}, defaults, {
|
|
126
134
|
message: {
|
|
127
135
|
title: undefined,
|
|
128
136
|
content: undefined,
|
|
129
137
|
icon: undefined,
|
|
130
138
|
width: {
|
|
131
139
|
min: "12rem",
|
|
132
|
-
max:
|
|
133
|
-
viewportRatio:
|
|
140
|
+
max: null,
|
|
141
|
+
viewportRatio: null,
|
|
134
142
|
},
|
|
135
143
|
},
|
|
136
144
|
templates: {
|
|
137
145
|
main: getTemplate(),
|
|
138
146
|
},
|
|
147
|
+
popper: Object.assign({}, defaults.popper, {
|
|
148
|
+
contentOverflow: "smart",
|
|
149
|
+
}),
|
|
139
150
|
mode: "manual",
|
|
140
151
|
labels: {
|
|
141
152
|
button: "<slot></slot>",
|
|
@@ -234,6 +245,8 @@ class MessageStateButton extends Popper {
|
|
|
234
245
|
);
|
|
235
246
|
}
|
|
236
247
|
|
|
248
|
+
applyResolvedMessagePresentation.call(this);
|
|
249
|
+
|
|
237
250
|
return this;
|
|
238
251
|
}
|
|
239
252
|
|
|
@@ -247,6 +260,7 @@ class MessageStateButton extends Popper {
|
|
|
247
260
|
this.setOption("message.content", undefined);
|
|
248
261
|
this.setOption("message.icon", undefined);
|
|
249
262
|
clearAutoHideTimer.call(this);
|
|
263
|
+
applyResolvedMessagePresentation.call(this);
|
|
250
264
|
return this;
|
|
251
265
|
}
|
|
252
266
|
|
|
@@ -258,6 +272,7 @@ class MessageStateButton extends Popper {
|
|
|
258
272
|
*/
|
|
259
273
|
showMessage(timeout) {
|
|
260
274
|
clearAutoHideTimer.call(this);
|
|
275
|
+
applyResolvedMessagePresentation.call(this);
|
|
261
276
|
applyMeasuredMessageWidth.call(this);
|
|
262
277
|
this.showDialog.call(this);
|
|
263
278
|
|
|
@@ -287,6 +302,13 @@ class MessageStateButton extends Popper {
|
|
|
287
302
|
return this;
|
|
288
303
|
}
|
|
289
304
|
|
|
305
|
+
/**
|
|
306
|
+
* @return {string}
|
|
307
|
+
*/
|
|
308
|
+
resolveContentOverflowMode() {
|
|
309
|
+
return resolveContentOverflowMode.call(this);
|
|
310
|
+
}
|
|
311
|
+
|
|
290
312
|
/**
|
|
291
313
|
*
|
|
292
314
|
* @return {MessageStateButton}
|
|
@@ -438,9 +460,11 @@ function initControlReferences() {
|
|
|
438
460
|
this[popperElementSymbol] = this.shadowRoot.querySelector(
|
|
439
461
|
`[${ATTRIBUTE_ROLE}=popper]`,
|
|
440
462
|
);
|
|
463
|
+
this[contentElementSymbol] = this.shadowRoot.querySelector(`[part="content"]`);
|
|
441
464
|
this[messageElementSymbol] = this.shadowRoot.querySelector(
|
|
442
465
|
`[${ATTRIBUTE_ROLE}=message]`,
|
|
443
466
|
);
|
|
467
|
+
applyResolvedMessagePresentation.call(this);
|
|
444
468
|
}
|
|
445
469
|
|
|
446
470
|
/**
|
|
@@ -534,8 +558,11 @@ function getTemplate() {
|
|
|
534
558
|
|
|
535
559
|
<div data-monster-role="popper" part="popper" tabindex="-1" class="monster-color-primary-1">
|
|
536
560
|
<div data-monster-role="arrow"></div>
|
|
537
|
-
<div
|
|
538
|
-
|
|
561
|
+
<div part="content"
|
|
562
|
+
class="flex">
|
|
563
|
+
<div data-monster-role="message" part="message" class="flex"
|
|
564
|
+
data-monster-patch="path:message.content"></div>
|
|
565
|
+
</div>
|
|
539
566
|
</div>
|
|
540
567
|
</div>
|
|
541
568
|
</div>
|
|
@@ -561,7 +588,166 @@ function getMeasurementContent(content) {
|
|
|
561
588
|
|
|
562
589
|
/**
|
|
563
590
|
* @private
|
|
564
|
-
* @return {
|
|
591
|
+
* @return {void}
|
|
592
|
+
*/
|
|
593
|
+
function applyResolvedMessagePresentation() {
|
|
594
|
+
const contentElement = this[contentElementSymbol];
|
|
595
|
+
const messageElement = this[messageElementSymbol];
|
|
596
|
+
const layoutMode = resolveMessageLayoutMode.call(this);
|
|
597
|
+
const overflowMode = this.resolveContentOverflowMode();
|
|
598
|
+
|
|
599
|
+
if (contentElement instanceof HTMLElement) {
|
|
600
|
+
contentElement.setAttribute("data-monster-overflow-mode", overflowMode);
|
|
601
|
+
contentElement.setAttribute("data-monster-message-layout", layoutMode);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (messageElement instanceof HTMLElement) {
|
|
605
|
+
messageElement.setAttribute("data-monster-message-layout", layoutMode);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (this[measurementPopperSymbol]?.content instanceof HTMLElement) {
|
|
609
|
+
this[measurementPopperSymbol].content.setAttribute(
|
|
610
|
+
"data-monster-overflow-mode",
|
|
611
|
+
overflowMode,
|
|
612
|
+
);
|
|
613
|
+
this[measurementPopperSymbol].content.setAttribute(
|
|
614
|
+
"data-monster-message-layout",
|
|
615
|
+
layoutMode,
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (this[measurementPopperSymbol]?.message instanceof HTMLElement) {
|
|
620
|
+
this[measurementPopperSymbol].message.setAttribute(
|
|
621
|
+
"data-monster-message-layout",
|
|
622
|
+
layoutMode,
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* @private
|
|
629
|
+
* @return {string}
|
|
630
|
+
*/
|
|
631
|
+
function resolveContentOverflowMode() {
|
|
632
|
+
const configuredMode = this.getOption("popper.contentOverflow", "smart");
|
|
633
|
+
|
|
634
|
+
if (configuredMode !== "smart") {
|
|
635
|
+
return configuredMode;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (resolveMessageLayoutMode.call(this) === MESSAGE_LAYOUT_OVERLAY) {
|
|
639
|
+
return "horizontal";
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return "both";
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* @private
|
|
647
|
+
* @return {string}
|
|
648
|
+
*/
|
|
649
|
+
function resolveMessageLayoutMode() {
|
|
650
|
+
const content = this.getOption("message.content");
|
|
651
|
+
|
|
652
|
+
if (containsNestedOverlayContent(content)) {
|
|
653
|
+
return MESSAGE_LAYOUT_OVERLAY;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (containsWideContent(content)) {
|
|
657
|
+
return MESSAGE_LAYOUT_WIDE;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return MESSAGE_LAYOUT_PROSE;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* @private
|
|
665
|
+
* @param {unknown} content
|
|
666
|
+
* @return {boolean}
|
|
667
|
+
*/
|
|
668
|
+
function containsNestedOverlayContent(content) {
|
|
669
|
+
const selector = [
|
|
670
|
+
"monster-details",
|
|
671
|
+
"monster-message-state-button",
|
|
672
|
+
"monster-popper",
|
|
673
|
+
"monster-popper-button",
|
|
674
|
+
"monster-select",
|
|
675
|
+
"details",
|
|
676
|
+
].join(",");
|
|
677
|
+
|
|
678
|
+
if (isString(content)) {
|
|
679
|
+
const container = document.createElement("div");
|
|
680
|
+
container.innerHTML = content;
|
|
681
|
+
return container.querySelector(selector) instanceof HTMLElement;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (!(content instanceof HTMLElement)) {
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (content.matches(selector)) {
|
|
689
|
+
return true;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
return content.querySelector(selector) instanceof HTMLElement;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* @private
|
|
697
|
+
* @param {unknown} content
|
|
698
|
+
* @return {boolean}
|
|
699
|
+
*/
|
|
700
|
+
function containsWideContent(content) {
|
|
701
|
+
const root = getContentSearchRoot(content);
|
|
702
|
+
if (!(root instanceof HTMLElement)) {
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const selector = [
|
|
707
|
+
'[data-monster-message-layout="wide"]',
|
|
708
|
+
"pre",
|
|
709
|
+
"table",
|
|
710
|
+
'[style*="white-space: nowrap"]',
|
|
711
|
+
'[style*="white-space:nowrap"]',
|
|
712
|
+
'[style*="overflow-x: auto"]',
|
|
713
|
+
'[style*="overflow-x:auto"]',
|
|
714
|
+
'[style*="overflow-x: scroll"]',
|
|
715
|
+
'[style*="overflow-x:scroll"]',
|
|
716
|
+
'[style*="overflow: auto"]',
|
|
717
|
+
'[style*="overflow:auto"]',
|
|
718
|
+
'[style*="overflow: scroll"]',
|
|
719
|
+
'[style*="overflow:scroll"]',
|
|
720
|
+
].join(",");
|
|
721
|
+
|
|
722
|
+
if (root.matches(selector)) {
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return root.querySelector(selector) instanceof HTMLElement;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* @private
|
|
731
|
+
* @param {unknown} content
|
|
732
|
+
* @return {HTMLElement|null}
|
|
733
|
+
*/
|
|
734
|
+
function getContentSearchRoot(content) {
|
|
735
|
+
if (isString(content)) {
|
|
736
|
+
const container = document.createElement("div");
|
|
737
|
+
container.innerHTML = content;
|
|
738
|
+
return container;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (content instanceof HTMLElement) {
|
|
742
|
+
return content;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* @private
|
|
750
|
+
* @return {{popper: HTMLElement, content: HTMLElement, message: HTMLElement}|null}
|
|
565
751
|
*/
|
|
566
752
|
function ensureMeasurementPopper() {
|
|
567
753
|
if (this[measurementPopperSymbol]) {
|
|
@@ -588,14 +774,19 @@ function ensureMeasurementPopper() {
|
|
|
588
774
|
popper.className = this[popperElementSymbol].className;
|
|
589
775
|
}
|
|
590
776
|
|
|
777
|
+
const content = document.createElement("div");
|
|
778
|
+
content.setAttribute("part", "content");
|
|
779
|
+
|
|
591
780
|
const message = document.createElement("div");
|
|
592
781
|
message.setAttribute(ATTRIBUTE_ROLE, "message");
|
|
593
782
|
message.className = "flex";
|
|
594
783
|
|
|
595
|
-
|
|
784
|
+
content.appendChild(message);
|
|
785
|
+
popper.appendChild(content);
|
|
596
786
|
this.shadowRoot.appendChild(popper);
|
|
597
787
|
|
|
598
|
-
this[measurementPopperSymbol] = { popper, message };
|
|
788
|
+
this[measurementPopperSymbol] = { popper, content, message };
|
|
789
|
+
applyResolvedMessagePresentation.call(this);
|
|
599
790
|
return this[measurementPopperSymbol];
|
|
600
791
|
}
|
|
601
792
|
|
|
@@ -623,6 +814,8 @@ function applyMeasuredMessageWidth() {
|
|
|
623
814
|
measurement.popper.className = popper.className;
|
|
624
815
|
}
|
|
625
816
|
|
|
817
|
+
applyResolvedMessagePresentation.call(this);
|
|
818
|
+
|
|
626
819
|
measurement.message.innerHTML = "";
|
|
627
820
|
if (isString(measureContent)) {
|
|
628
821
|
measurement.message.innerHTML = measureContent;
|
|
@@ -637,6 +830,7 @@ function applyMeasuredMessageWidth() {
|
|
|
637
830
|
const rootFontSize =
|
|
638
831
|
parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
|
639
832
|
const widthOptions = this.getOption("message.width", {});
|
|
833
|
+
const layoutMode = resolveMessageLayoutMode.call(this);
|
|
640
834
|
const minWidthOption = resolveLength(
|
|
641
835
|
widthOptions?.min,
|
|
642
836
|
fontSize,
|
|
@@ -649,21 +843,23 @@ function applyMeasuredMessageWidth() {
|
|
|
649
843
|
rootFontSize,
|
|
650
844
|
window.innerWidth,
|
|
651
845
|
);
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
widthOptions
|
|
656
|
-
|
|
657
|
-
|
|
846
|
+
const viewportPadding = rootFontSize * 2;
|
|
847
|
+
const viewportWidthLimit = Math.max(0, window.innerWidth - viewportPadding);
|
|
848
|
+
const viewportRatio = resolveViewportRatio(
|
|
849
|
+
widthOptions?.viewportRatio,
|
|
850
|
+
layoutMode,
|
|
851
|
+
);
|
|
852
|
+
const fallbackMaxWidth =
|
|
853
|
+
layoutMode === MESSAGE_LAYOUT_WIDE ? viewportWidthLimit : fontSize * 32;
|
|
658
854
|
|
|
659
855
|
const minWidth = Math.max(0, minWidthOption ?? Math.round(fontSize * 12));
|
|
660
856
|
const maxViewportWidth = Math.max(
|
|
661
857
|
minWidth,
|
|
662
|
-
window.innerWidth * viewportRatio,
|
|
858
|
+
Math.min(window.innerWidth * viewportRatio, viewportWidthLimit),
|
|
663
859
|
);
|
|
664
860
|
const maxWidth = Math.max(
|
|
665
861
|
minWidth,
|
|
666
|
-
Math.min(maxWidthOption ??
|
|
862
|
+
Math.min(maxWidthOption ?? fallbackMaxWidth, maxViewportWidth),
|
|
667
863
|
);
|
|
668
864
|
const targetWidth = Math.max(
|
|
669
865
|
minWidth,
|
|
@@ -673,8 +869,27 @@ function applyMeasuredMessageWidth() {
|
|
|
673
869
|
popper.style.width = `${targetWidth}px`;
|
|
674
870
|
popper.style.minWidth = `${minWidth}px`;
|
|
675
871
|
popper.style.maxWidth = `${maxWidth}px`;
|
|
676
|
-
popper.style.
|
|
677
|
-
popper.style.
|
|
872
|
+
popper.style.removeProperty("white-space");
|
|
873
|
+
popper.style.removeProperty("overflow-wrap");
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* @private
|
|
878
|
+
* @param {unknown} value
|
|
879
|
+
* @param {string} layoutMode
|
|
880
|
+
* @return {number}
|
|
881
|
+
*/
|
|
882
|
+
function resolveViewportRatio(value, layoutMode) {
|
|
883
|
+
if (
|
|
884
|
+
typeof value === "number" &&
|
|
885
|
+
Number.isFinite(value) &&
|
|
886
|
+
value > 0 &&
|
|
887
|
+
value <= 1
|
|
888
|
+
) {
|
|
889
|
+
return value;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
return layoutMode === MESSAGE_LAYOUT_WIDE ? 1 : 0.7;
|
|
678
893
|
}
|
|
679
894
|
|
|
680
895
|
/**
|