@schukai/monster 4.142.1 → 4.142.2
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/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"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.142.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"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.142.2"}
|
|
@@ -204,6 +204,7 @@ const EVENT_LAYOUT_CHANGED = "monster-control-bar-layout-changed";
|
|
|
204
204
|
* @type {number}
|
|
205
205
|
*/
|
|
206
206
|
const LAYOUT_CHANGED_EVENT_DELAY = 20;
|
|
207
|
+
const LAYOUT_SWITCH_HYSTERESIS = 16;
|
|
207
208
|
|
|
208
209
|
/**
|
|
209
210
|
* A control bar control.
|
|
@@ -801,6 +802,7 @@ function rearrangeItems() {
|
|
|
801
802
|
availableSpace = 0;
|
|
802
803
|
}
|
|
803
804
|
let sum = 0;
|
|
805
|
+
let overflowStarted = false;
|
|
804
806
|
const visibleItemsInMainSlot = [];
|
|
805
807
|
const itemsToMoveToPopper = [];
|
|
806
808
|
|
|
@@ -810,7 +812,8 @@ function rearrangeItems() {
|
|
|
810
812
|
continue;
|
|
811
813
|
}
|
|
812
814
|
|
|
813
|
-
if (sum + entry.width > availableSpace) {
|
|
815
|
+
if (overflowStarted || sum + entry.width > availableSpace) {
|
|
816
|
+
overflowStarted = true;
|
|
814
817
|
itemsToMoveToPopper.push(entry.element);
|
|
815
818
|
} else {
|
|
816
819
|
sum += entry.width;
|
|
@@ -821,12 +824,41 @@ function rearrangeItems() {
|
|
|
821
824
|
return { visibleItemsInMainSlot, itemsToMoveToPopper };
|
|
822
825
|
};
|
|
823
826
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
+
const switchCurrentlyVisible =
|
|
828
|
+
this[switchElementSymbol] instanceof HTMLElement &&
|
|
829
|
+
!this[switchElementSymbol].hasAttribute("hidden");
|
|
830
|
+
let layout;
|
|
831
|
+
if (switchCurrentlyVisible) {
|
|
832
|
+
layout = layoutItems(space - switchWidth - LAYOUT_SWITCH_HYSTERESIS);
|
|
833
|
+
if (layout.itemsToMoveToPopper.length === 0) {
|
|
834
|
+
layout = layoutItems(space);
|
|
835
|
+
}
|
|
836
|
+
} else {
|
|
837
|
+
layout = layoutItems(space - LAYOUT_SWITCH_HYSTERESIS);
|
|
838
|
+
if (layout.itemsToMoveToPopper.length > 0) {
|
|
839
|
+
layout = layoutItems(space - switchWidth);
|
|
840
|
+
} else {
|
|
841
|
+
layout = layoutItems(space);
|
|
842
|
+
}
|
|
827
843
|
}
|
|
828
844
|
|
|
845
|
+
layout = normalizeSpacerOverflowLayout(
|
|
846
|
+
layout,
|
|
847
|
+
itemEntries.map((entry) => entry.element),
|
|
848
|
+
);
|
|
849
|
+
layout = stabilizeOverflowBoundary.call(
|
|
850
|
+
this,
|
|
851
|
+
layout,
|
|
852
|
+
itemEntries,
|
|
853
|
+
switchCurrentlyVisible,
|
|
854
|
+
);
|
|
855
|
+
|
|
829
856
|
const shouldShowSwitch = layout.itemsToMoveToPopper.length > 0 && hasItems;
|
|
857
|
+
updateLastOverflowBoundary.call(
|
|
858
|
+
this,
|
|
859
|
+
layout,
|
|
860
|
+
itemEntries.map((entry) => entry.element),
|
|
861
|
+
);
|
|
830
862
|
const shouldUseStackedLayout =
|
|
831
863
|
shouldShowSwitch || isStackedBreakpointMatched.call(this);
|
|
832
864
|
|
|
@@ -854,6 +886,147 @@ function rearrangeItems() {
|
|
|
854
886
|
}
|
|
855
887
|
}
|
|
856
888
|
|
|
889
|
+
/**
|
|
890
|
+
* @private
|
|
891
|
+
* @param {{visibleItemsInMainSlot: HTMLElement[], itemsToMoveToPopper: HTMLElement[]}} layout
|
|
892
|
+
* @param {{element: HTMLElement, hidden: boolean}[]} itemEntries
|
|
893
|
+
* @param {boolean} switchCurrentlyVisible
|
|
894
|
+
* @return {{visibleItemsInMainSlot: HTMLElement[], itemsToMoveToPopper: HTMLElement[]}}
|
|
895
|
+
*/
|
|
896
|
+
function stabilizeOverflowBoundary(layout, itemEntries, switchCurrentlyVisible) {
|
|
897
|
+
const state = this[layoutStateSymbol];
|
|
898
|
+
if (!switchCurrentlyVisible || !state) {
|
|
899
|
+
return layout;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
const orderedItems = itemEntries.map((entry) => entry.element);
|
|
903
|
+
const nextBoundary = getFirstOverflowIndex(layout, orderedItems);
|
|
904
|
+
const previousBoundary = state.lastOverflowStartIndex;
|
|
905
|
+
if (
|
|
906
|
+
typeof previousBoundary !== "number" ||
|
|
907
|
+
typeof nextBoundary !== "number" ||
|
|
908
|
+
nextBoundary <= previousBoundary ||
|
|
909
|
+
previousBoundary < 0 ||
|
|
910
|
+
previousBoundary >= itemEntries.length
|
|
911
|
+
) {
|
|
912
|
+
return layout;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
const popperItems = new Set();
|
|
916
|
+
const mainItems = new Set();
|
|
917
|
+
for (let index = 0; index < itemEntries.length; index++) {
|
|
918
|
+
const entry = itemEntries[index];
|
|
919
|
+
if (index >= previousBoundary && !entry.hidden) {
|
|
920
|
+
popperItems.add(entry.element);
|
|
921
|
+
} else {
|
|
922
|
+
mainItems.add(entry.element);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
return normalizeSpacerOverflowLayout(
|
|
927
|
+
{
|
|
928
|
+
visibleItemsInMainSlot: orderedItems.filter((item) => mainItems.has(item)),
|
|
929
|
+
itemsToMoveToPopper: orderedItems.filter((item) => popperItems.has(item)),
|
|
930
|
+
},
|
|
931
|
+
orderedItems,
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* @private
|
|
937
|
+
* @param {{itemsToMoveToPopper: HTMLElement[]}} layout
|
|
938
|
+
* @param {HTMLElement[]} orderedItems
|
|
939
|
+
* @return {void}
|
|
940
|
+
*/
|
|
941
|
+
function updateLastOverflowBoundary(layout, orderedItems) {
|
|
942
|
+
const state = this[layoutStateSymbol];
|
|
943
|
+
if (!state) {
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
const boundary = getFirstOverflowIndex(layout, orderedItems);
|
|
948
|
+
if (typeof boundary === "number") {
|
|
949
|
+
state.lastOverflowStartIndex = boundary;
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
delete state.lastOverflowStartIndex;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* @private
|
|
957
|
+
* @param {{itemsToMoveToPopper: HTMLElement[]}} layout
|
|
958
|
+
* @param {HTMLElement[]} orderedItems
|
|
959
|
+
* @return {number|undefined}
|
|
960
|
+
*/
|
|
961
|
+
function getFirstOverflowIndex(layout, orderedItems) {
|
|
962
|
+
const popperItems = new Set(layout.itemsToMoveToPopper);
|
|
963
|
+
for (let index = 0; index < orderedItems.length; index++) {
|
|
964
|
+
if (popperItems.has(orderedItems[index])) {
|
|
965
|
+
return index;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return undefined;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* @private
|
|
973
|
+
* @param {{visibleItemsInMainSlot: HTMLElement[], itemsToMoveToPopper: HTMLElement[]}} layout
|
|
974
|
+
* @param {HTMLElement[]} orderedItems
|
|
975
|
+
* @return {{visibleItemsInMainSlot: HTMLElement[], itemsToMoveToPopper: HTMLElement[]}}
|
|
976
|
+
*/
|
|
977
|
+
function normalizeSpacerOverflowLayout(layout, orderedItems) {
|
|
978
|
+
if (
|
|
979
|
+
!layout.itemsToMoveToPopper.some(
|
|
980
|
+
(item) => !isControlBarSpacerElement(item),
|
|
981
|
+
)
|
|
982
|
+
) {
|
|
983
|
+
return layout;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
const popperItems = new Set(layout.itemsToMoveToPopper);
|
|
987
|
+
const mainItems = new Set(layout.visibleItemsInMainSlot);
|
|
988
|
+
|
|
989
|
+
for (let index = 0; index < orderedItems.length; index++) {
|
|
990
|
+
const item = orderedItems[index];
|
|
991
|
+
if (!isControlBarSpacerElement(item) || !mainItems.has(item)) {
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
const previous = findAdjacentNonSpacerItem(orderedItems, index, -1);
|
|
996
|
+
const next = findAdjacentNonSpacerItem(orderedItems, index, 1);
|
|
997
|
+
if (popperItems.has(previous) || popperItems.has(next)) {
|
|
998
|
+
mainItems.delete(item);
|
|
999
|
+
popperItems.add(item);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
return {
|
|
1004
|
+
visibleItemsInMainSlot: orderedItems.filter((item) => mainItems.has(item)),
|
|
1005
|
+
itemsToMoveToPopper: orderedItems.filter((item) => popperItems.has(item)),
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* @private
|
|
1011
|
+
* @param {HTMLElement[]} orderedItems
|
|
1012
|
+
* @param {number} startIndex
|
|
1013
|
+
* @param {1|-1} direction
|
|
1014
|
+
* @return {HTMLElement|undefined}
|
|
1015
|
+
*/
|
|
1016
|
+
function findAdjacentNonSpacerItem(orderedItems, startIndex, direction) {
|
|
1017
|
+
for (
|
|
1018
|
+
let index = startIndex + direction;
|
|
1019
|
+
index >= 0 && index < orderedItems.length;
|
|
1020
|
+
index += direction
|
|
1021
|
+
) {
|
|
1022
|
+
const item = orderedItems[index];
|
|
1023
|
+
if (!isControlBarSpacerElement(item)) {
|
|
1024
|
+
return item;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return undefined;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
857
1030
|
/**
|
|
858
1031
|
* @private
|
|
859
1032
|
* @param {HTMLElement} node
|
|
@@ -756,6 +756,479 @@ describe("ControlBar", function () {
|
|
|
756
756
|
}
|
|
757
757
|
});
|
|
758
758
|
|
|
759
|
+
it("should keep the overflow switch stable across the switch-width threshold", async function () {
|
|
760
|
+
const OriginalResizeObserver = window.ResizeObserver;
|
|
761
|
+
const originalGlobalResizeObserver = globalThis.ResizeObserver;
|
|
762
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
763
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
764
|
+
|
|
765
|
+
class TrackingResizeObserver extends ResizeObserverMock {
|
|
766
|
+
static instances = [];
|
|
767
|
+
|
|
768
|
+
constructor(callback) {
|
|
769
|
+
super(callback);
|
|
770
|
+
TrackingResizeObserver.instances.push(this);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
const scheduledCallbacks = [];
|
|
775
|
+
const flushFrames = async () => {
|
|
776
|
+
while (scheduledCallbacks.length > 0) {
|
|
777
|
+
scheduledCallbacks.shift()();
|
|
778
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
try {
|
|
783
|
+
window.ResizeObserver = TrackingResizeObserver;
|
|
784
|
+
globalThis.ResizeObserver = TrackingResizeObserver;
|
|
785
|
+
window.requestAnimationFrame = (callback) => {
|
|
786
|
+
scheduledCallbacks.push(callback);
|
|
787
|
+
return scheduledCallbacks.length;
|
|
788
|
+
};
|
|
789
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
790
|
+
|
|
791
|
+
const mocks = document.getElementById("mocks");
|
|
792
|
+
mocks.innerHTML = `
|
|
793
|
+
<div id="threshold-bar-wrapper">
|
|
794
|
+
<monster-control-bar id="threshold-bar">
|
|
795
|
+
<button id="threshold-one">One</button>
|
|
796
|
+
<button id="threshold-two">Two</button>
|
|
797
|
+
<button id="threshold-three">Three</button>
|
|
798
|
+
</monster-control-bar>
|
|
799
|
+
</div>
|
|
800
|
+
`;
|
|
801
|
+
|
|
802
|
+
const wrapper = document.getElementById("threshold-bar-wrapper");
|
|
803
|
+
const bar = document.getElementById("threshold-bar");
|
|
804
|
+
const buttons = [
|
|
805
|
+
document.getElementById("threshold-one"),
|
|
806
|
+
document.getElementById("threshold-two"),
|
|
807
|
+
document.getElementById("threshold-three"),
|
|
808
|
+
];
|
|
809
|
+
let wrapperWidth = 90;
|
|
810
|
+
|
|
811
|
+
wrapper.style.boxSizing = "border-box";
|
|
812
|
+
Object.defineProperty(wrapper, "clientWidth", {
|
|
813
|
+
configurable: true,
|
|
814
|
+
get: () => wrapperWidth,
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
for (const button of buttons) {
|
|
818
|
+
Object.defineProperty(button, "offsetWidth", {
|
|
819
|
+
configurable: true,
|
|
820
|
+
value: 50,
|
|
821
|
+
});
|
|
822
|
+
Object.defineProperty(button, "offsetHeight", {
|
|
823
|
+
configurable: true,
|
|
824
|
+
value: 30,
|
|
825
|
+
});
|
|
826
|
+
button.getBoundingClientRect = () => ({
|
|
827
|
+
width: 50,
|
|
828
|
+
height: 30,
|
|
829
|
+
top: 0,
|
|
830
|
+
right: 50,
|
|
831
|
+
bottom: 30,
|
|
832
|
+
left: 0,
|
|
833
|
+
x: 0,
|
|
834
|
+
y: 0,
|
|
835
|
+
toJSON: () => {},
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const switchButton = bar.shadowRoot.querySelector(
|
|
840
|
+
'[data-monster-role="switch"]',
|
|
841
|
+
);
|
|
842
|
+
Object.defineProperty(switchButton, "offsetWidth", {
|
|
843
|
+
configurable: true,
|
|
844
|
+
value: 20,
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
await flushFrames();
|
|
848
|
+
expect(switchButton.hasAttribute("hidden")).to.be.false;
|
|
849
|
+
|
|
850
|
+
const resizeObserver = TrackingResizeObserver.instances.find((observer) =>
|
|
851
|
+
observer.elements.includes(wrapper),
|
|
852
|
+
);
|
|
853
|
+
expect(resizeObserver).to.exist;
|
|
854
|
+
|
|
855
|
+
wrapperWidth = 155;
|
|
856
|
+
resizeObserver.triggerResize([{ target: wrapper }]);
|
|
857
|
+
await flushFrames();
|
|
858
|
+
expect(switchButton.hasAttribute("hidden")).to.be.false;
|
|
859
|
+
expect(document.getElementById("threshold-three").getAttribute("slot")).to
|
|
860
|
+
.equal("popper");
|
|
861
|
+
|
|
862
|
+
wrapperWidth = 175;
|
|
863
|
+
resizeObserver.triggerResize([{ target: wrapper }]);
|
|
864
|
+
await flushFrames();
|
|
865
|
+
expect(switchButton.hasAttribute("hidden")).to.be.false;
|
|
866
|
+
expect(document.getElementById("threshold-three").getAttribute("slot")).to
|
|
867
|
+
.equal("popper");
|
|
868
|
+
|
|
869
|
+
wrapperWidth = 190;
|
|
870
|
+
resizeObserver.triggerResize([{ target: wrapper }]);
|
|
871
|
+
await flushFrames();
|
|
872
|
+
expect(switchButton.hasAttribute("hidden")).to.be.true;
|
|
873
|
+
expect(document.getElementById("threshold-three").hasAttribute("slot")).to
|
|
874
|
+
.be.false;
|
|
875
|
+
|
|
876
|
+
wrapperWidth = 160;
|
|
877
|
+
resizeObserver.triggerResize([{ target: wrapper }]);
|
|
878
|
+
await flushFrames();
|
|
879
|
+
expect(switchButton.hasAttribute("hidden")).to.be.false;
|
|
880
|
+
expect(document.getElementById("threshold-three").getAttribute("slot")).to
|
|
881
|
+
.equal("popper");
|
|
882
|
+
} finally {
|
|
883
|
+
window.ResizeObserver = OriginalResizeObserver;
|
|
884
|
+
globalThis.ResizeObserver = originalGlobalResizeObserver;
|
|
885
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
886
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
it("should move spacers with adjacent overflow controls", async function () {
|
|
891
|
+
const OriginalResizeObserver = window.ResizeObserver;
|
|
892
|
+
const originalGlobalResizeObserver = globalThis.ResizeObserver;
|
|
893
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
894
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
895
|
+
|
|
896
|
+
class TrackingResizeObserver extends ResizeObserverMock {
|
|
897
|
+
static instances = [];
|
|
898
|
+
|
|
899
|
+
constructor(callback) {
|
|
900
|
+
super(callback);
|
|
901
|
+
TrackingResizeObserver.instances.push(this);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const scheduledCallbacks = [];
|
|
906
|
+
const flushFrames = async () => {
|
|
907
|
+
while (scheduledCallbacks.length > 0) {
|
|
908
|
+
scheduledCallbacks.shift()();
|
|
909
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
try {
|
|
914
|
+
window.ResizeObserver = TrackingResizeObserver;
|
|
915
|
+
globalThis.ResizeObserver = TrackingResizeObserver;
|
|
916
|
+
window.requestAnimationFrame = (callback) => {
|
|
917
|
+
scheduledCallbacks.push(callback);
|
|
918
|
+
return scheduledCallbacks.length;
|
|
919
|
+
};
|
|
920
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
921
|
+
|
|
922
|
+
const mocks = document.getElementById("mocks");
|
|
923
|
+
mocks.innerHTML = `
|
|
924
|
+
<div id="spacer-overflow-wrapper">
|
|
925
|
+
<monster-control-bar id="spacer-overflow-bar">
|
|
926
|
+
<button id="spacer-overflow-one">One</button>
|
|
927
|
+
<monster-control-bar-spacer id="spacer-overflow-spacer-one"></monster-control-bar-spacer>
|
|
928
|
+
<button id="spacer-overflow-two">Two</button>
|
|
929
|
+
<monster-control-bar-spacer id="spacer-overflow-spacer-two"></monster-control-bar-spacer>
|
|
930
|
+
<button id="spacer-overflow-three">Three</button>
|
|
931
|
+
</monster-control-bar>
|
|
932
|
+
</div>
|
|
933
|
+
`;
|
|
934
|
+
|
|
935
|
+
const wrapper = document.getElementById("spacer-overflow-wrapper");
|
|
936
|
+
const bar = document.getElementById("spacer-overflow-bar");
|
|
937
|
+
const sizes = new Map([
|
|
938
|
+
["spacer-overflow-one", 50],
|
|
939
|
+
["spacer-overflow-spacer-one", 10],
|
|
940
|
+
["spacer-overflow-two", 100],
|
|
941
|
+
["spacer-overflow-spacer-two", 10],
|
|
942
|
+
["spacer-overflow-three", 50],
|
|
943
|
+
]);
|
|
944
|
+
|
|
945
|
+
wrapper.style.boxSizing = "border-box";
|
|
946
|
+
Object.defineProperty(wrapper, "clientWidth", {
|
|
947
|
+
configurable: true,
|
|
948
|
+
value: 100,
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
for (const [id, width] of sizes) {
|
|
952
|
+
const element = document.getElementById(id);
|
|
953
|
+
Object.defineProperty(element, "offsetWidth", {
|
|
954
|
+
configurable: true,
|
|
955
|
+
value: width,
|
|
956
|
+
});
|
|
957
|
+
Object.defineProperty(element, "offsetHeight", {
|
|
958
|
+
configurable: true,
|
|
959
|
+
value: 30,
|
|
960
|
+
});
|
|
961
|
+
element.getBoundingClientRect = () => ({
|
|
962
|
+
width,
|
|
963
|
+
height: 30,
|
|
964
|
+
top: 0,
|
|
965
|
+
right: width,
|
|
966
|
+
bottom: 30,
|
|
967
|
+
left: 0,
|
|
968
|
+
x: 0,
|
|
969
|
+
y: 0,
|
|
970
|
+
toJSON: () => {},
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
const switchButton = bar.shadowRoot.querySelector(
|
|
975
|
+
'[data-monster-role="switch"]',
|
|
976
|
+
);
|
|
977
|
+
Object.defineProperty(switchButton, "offsetWidth", {
|
|
978
|
+
configurable: true,
|
|
979
|
+
value: 20,
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
await flushFrames();
|
|
983
|
+
expect(document.getElementById("spacer-overflow-one").hasAttribute("slot"))
|
|
984
|
+
.to.be.false;
|
|
985
|
+
expect(
|
|
986
|
+
document.getElementById("spacer-overflow-spacer-one").getAttribute("slot"),
|
|
987
|
+
).to.equal("popper");
|
|
988
|
+
expect(document.getElementById("spacer-overflow-two").getAttribute("slot"))
|
|
989
|
+
.to.equal("popper");
|
|
990
|
+
expect(
|
|
991
|
+
document.getElementById("spacer-overflow-spacer-two").getAttribute("slot"),
|
|
992
|
+
).to.equal("popper");
|
|
993
|
+
expect(
|
|
994
|
+
document.getElementById("spacer-overflow-three").getAttribute("slot"),
|
|
995
|
+
).to.equal("popper");
|
|
996
|
+
|
|
997
|
+
const resizeObserver = TrackingResizeObserver.instances.find((observer) =>
|
|
998
|
+
observer.elements.includes(wrapper),
|
|
999
|
+
);
|
|
1000
|
+
expect(resizeObserver).to.exist;
|
|
1001
|
+
|
|
1002
|
+
resizeObserver.triggerResize([{ target: wrapper }]);
|
|
1003
|
+
await flushFrames();
|
|
1004
|
+
expect(
|
|
1005
|
+
document.getElementById("spacer-overflow-spacer-one").getAttribute("slot"),
|
|
1006
|
+
).to.equal("popper");
|
|
1007
|
+
expect(
|
|
1008
|
+
document.getElementById("spacer-overflow-spacer-two").getAttribute("slot"),
|
|
1009
|
+
).to.equal("popper");
|
|
1010
|
+
} finally {
|
|
1011
|
+
window.ResizeObserver = OriginalResizeObserver;
|
|
1012
|
+
globalThis.ResizeObserver = originalGlobalResizeObserver;
|
|
1013
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
1014
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
it("should keep overflow as an ordered suffix when later controls would fit", async function () {
|
|
1019
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
1020
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
1021
|
+
|
|
1022
|
+
const scheduledCallbacks = [];
|
|
1023
|
+
const flushFrames = async () => {
|
|
1024
|
+
while (scheduledCallbacks.length > 0) {
|
|
1025
|
+
scheduledCallbacks.shift()();
|
|
1026
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
try {
|
|
1031
|
+
window.requestAnimationFrame = (callback) => {
|
|
1032
|
+
scheduledCallbacks.push(callback);
|
|
1033
|
+
return scheduledCallbacks.length;
|
|
1034
|
+
};
|
|
1035
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
1036
|
+
|
|
1037
|
+
const mocks = document.getElementById("mocks");
|
|
1038
|
+
mocks.innerHTML = `
|
|
1039
|
+
<div id="ordered-overflow-wrapper">
|
|
1040
|
+
<monster-control-bar id="ordered-overflow-bar">
|
|
1041
|
+
<button id="ordered-overflow-one">One</button>
|
|
1042
|
+
<button id="ordered-overflow-wide">Wide</button>
|
|
1043
|
+
<button id="ordered-overflow-small">Small</button>
|
|
1044
|
+
</monster-control-bar>
|
|
1045
|
+
</div>
|
|
1046
|
+
`;
|
|
1047
|
+
|
|
1048
|
+
const wrapper = document.getElementById("ordered-overflow-wrapper");
|
|
1049
|
+
const bar = document.getElementById("ordered-overflow-bar");
|
|
1050
|
+
const sizes = new Map([
|
|
1051
|
+
["ordered-overflow-one", 50],
|
|
1052
|
+
["ordered-overflow-wide", 100],
|
|
1053
|
+
["ordered-overflow-small", 20],
|
|
1054
|
+
]);
|
|
1055
|
+
|
|
1056
|
+
wrapper.style.boxSizing = "border-box";
|
|
1057
|
+
Object.defineProperty(wrapper, "clientWidth", {
|
|
1058
|
+
configurable: true,
|
|
1059
|
+
value: 90,
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
for (const [id, width] of sizes) {
|
|
1063
|
+
const element = document.getElementById(id);
|
|
1064
|
+
Object.defineProperty(element, "offsetWidth", {
|
|
1065
|
+
configurable: true,
|
|
1066
|
+
value: width,
|
|
1067
|
+
});
|
|
1068
|
+
Object.defineProperty(element, "offsetHeight", {
|
|
1069
|
+
configurable: true,
|
|
1070
|
+
value: 30,
|
|
1071
|
+
});
|
|
1072
|
+
element.getBoundingClientRect = () => ({
|
|
1073
|
+
width,
|
|
1074
|
+
height: 30,
|
|
1075
|
+
top: 0,
|
|
1076
|
+
right: width,
|
|
1077
|
+
bottom: 30,
|
|
1078
|
+
left: 0,
|
|
1079
|
+
x: 0,
|
|
1080
|
+
y: 0,
|
|
1081
|
+
toJSON: () => {},
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
const switchButton = bar.shadowRoot.querySelector(
|
|
1086
|
+
'[data-monster-role="switch"]',
|
|
1087
|
+
);
|
|
1088
|
+
Object.defineProperty(switchButton, "offsetWidth", {
|
|
1089
|
+
configurable: true,
|
|
1090
|
+
value: 20,
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
await flushFrames();
|
|
1094
|
+
|
|
1095
|
+
expect(document.getElementById("ordered-overflow-one").hasAttribute("slot"))
|
|
1096
|
+
.to.be.false;
|
|
1097
|
+
expect(document.getElementById("ordered-overflow-wide").getAttribute("slot"))
|
|
1098
|
+
.to.equal("popper");
|
|
1099
|
+
expect(document.getElementById("ordered-overflow-small").getAttribute("slot"))
|
|
1100
|
+
.to.equal("popper");
|
|
1101
|
+
} finally {
|
|
1102
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
1103
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
it("should keep the previous overflow boundary while the switch remains visible", async function () {
|
|
1108
|
+
const OriginalResizeObserver = window.ResizeObserver;
|
|
1109
|
+
const originalGlobalResizeObserver = globalThis.ResizeObserver;
|
|
1110
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
1111
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
1112
|
+
|
|
1113
|
+
class TrackingResizeObserver extends ResizeObserverMock {
|
|
1114
|
+
static instances = [];
|
|
1115
|
+
|
|
1116
|
+
constructor(callback) {
|
|
1117
|
+
super(callback);
|
|
1118
|
+
TrackingResizeObserver.instances.push(this);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
const scheduledCallbacks = [];
|
|
1123
|
+
const flushFrames = async () => {
|
|
1124
|
+
while (scheduledCallbacks.length > 0) {
|
|
1125
|
+
scheduledCallbacks.shift()();
|
|
1126
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
try {
|
|
1131
|
+
window.ResizeObserver = TrackingResizeObserver;
|
|
1132
|
+
globalThis.ResizeObserver = TrackingResizeObserver;
|
|
1133
|
+
window.requestAnimationFrame = (callback) => {
|
|
1134
|
+
scheduledCallbacks.push(callback);
|
|
1135
|
+
return scheduledCallbacks.length;
|
|
1136
|
+
};
|
|
1137
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
1138
|
+
|
|
1139
|
+
const mocks = document.getElementById("mocks");
|
|
1140
|
+
mocks.innerHTML = `
|
|
1141
|
+
<div id="sticky-overflow-wrapper">
|
|
1142
|
+
<monster-control-bar id="sticky-overflow-bar">
|
|
1143
|
+
<button id="sticky-overflow-one">One</button>
|
|
1144
|
+
<button id="sticky-overflow-wide">Wide</button>
|
|
1145
|
+
<button id="sticky-overflow-small">Small</button>
|
|
1146
|
+
</monster-control-bar>
|
|
1147
|
+
</div>
|
|
1148
|
+
`;
|
|
1149
|
+
|
|
1150
|
+
const wrapper = document.getElementById("sticky-overflow-wrapper");
|
|
1151
|
+
const bar = document.getElementById("sticky-overflow-bar");
|
|
1152
|
+
let wrapperWidth = 90;
|
|
1153
|
+
const sizes = new Map([
|
|
1154
|
+
["sticky-overflow-one", 50],
|
|
1155
|
+
["sticky-overflow-wide", 100],
|
|
1156
|
+
["sticky-overflow-small", 20],
|
|
1157
|
+
]);
|
|
1158
|
+
|
|
1159
|
+
wrapper.style.boxSizing = "border-box";
|
|
1160
|
+
Object.defineProperty(wrapper, "clientWidth", {
|
|
1161
|
+
configurable: true,
|
|
1162
|
+
get: () => wrapperWidth,
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
for (const [id, width] of sizes) {
|
|
1166
|
+
const element = document.getElementById(id);
|
|
1167
|
+
Object.defineProperty(element, "offsetWidth", {
|
|
1168
|
+
configurable: true,
|
|
1169
|
+
value: width,
|
|
1170
|
+
});
|
|
1171
|
+
Object.defineProperty(element, "offsetHeight", {
|
|
1172
|
+
configurable: true,
|
|
1173
|
+
value: 30,
|
|
1174
|
+
});
|
|
1175
|
+
element.getBoundingClientRect = () => ({
|
|
1176
|
+
width,
|
|
1177
|
+
height: 30,
|
|
1178
|
+
top: 0,
|
|
1179
|
+
right: width,
|
|
1180
|
+
bottom: 30,
|
|
1181
|
+
left: 0,
|
|
1182
|
+
x: 0,
|
|
1183
|
+
y: 0,
|
|
1184
|
+
toJSON: () => {},
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
const switchButton = bar.shadowRoot.querySelector(
|
|
1189
|
+
'[data-monster-role="switch"]',
|
|
1190
|
+
);
|
|
1191
|
+
Object.defineProperty(switchButton, "offsetWidth", {
|
|
1192
|
+
configurable: true,
|
|
1193
|
+
value: 20,
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
await flushFrames();
|
|
1197
|
+
expect(document.getElementById("sticky-overflow-wide").getAttribute("slot"))
|
|
1198
|
+
.to.equal("popper");
|
|
1199
|
+
expect(document.getElementById("sticky-overflow-small").getAttribute("slot"))
|
|
1200
|
+
.to.equal("popper");
|
|
1201
|
+
|
|
1202
|
+
const resizeObserver = TrackingResizeObserver.instances.find((observer) =>
|
|
1203
|
+
observer.elements.includes(wrapper),
|
|
1204
|
+
);
|
|
1205
|
+
expect(resizeObserver).to.exist;
|
|
1206
|
+
|
|
1207
|
+
wrapperWidth = 190;
|
|
1208
|
+
resizeObserver.triggerResize([{ target: wrapper }]);
|
|
1209
|
+
await flushFrames();
|
|
1210
|
+
expect(switchButton.hasAttribute("hidden")).to.be.false;
|
|
1211
|
+
expect(document.getElementById("sticky-overflow-wide").getAttribute("slot"))
|
|
1212
|
+
.to.equal("popper");
|
|
1213
|
+
expect(document.getElementById("sticky-overflow-small").getAttribute("slot"))
|
|
1214
|
+
.to.equal("popper");
|
|
1215
|
+
|
|
1216
|
+
wrapperWidth = 220;
|
|
1217
|
+
resizeObserver.triggerResize([{ target: wrapper }]);
|
|
1218
|
+
await flushFrames();
|
|
1219
|
+
expect(switchButton.hasAttribute("hidden")).to.be.true;
|
|
1220
|
+
expect(document.getElementById("sticky-overflow-wide").hasAttribute("slot"))
|
|
1221
|
+
.to.be.false;
|
|
1222
|
+
expect(document.getElementById("sticky-overflow-small").hasAttribute("slot"))
|
|
1223
|
+
.to.be.false;
|
|
1224
|
+
} finally {
|
|
1225
|
+
window.ResizeObserver = OriginalResizeObserver;
|
|
1226
|
+
globalThis.ResizeObserver = originalGlobalResizeObserver;
|
|
1227
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
1228
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
|
|
759
1232
|
it("should match the stacked breakpoint when the content-box container width is a css string", async function () {
|
|
760
1233
|
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
761
1234
|
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|