@schukai/monster 4.128.0 → 4.128.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","@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.128.0"}
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.128.2"}
@@ -117,6 +117,12 @@ const columnBarElementSymbol = Symbol("columnBarElement");
117
117
  */
118
118
  const copyAllElementSymbol = Symbol("copyAllElement");
119
119
 
120
+ /**
121
+ * @private
122
+ * @type {symbol}
123
+ */
124
+ const tableScrollElementSymbol = Symbol("tableScrollElement");
125
+
120
126
  /**
121
127
  * @private
122
128
  * @type {symbol}
@@ -794,6 +800,7 @@ function getCellValueForCopy(cell) {
794
800
  */
795
801
  function initEventHandler() {
796
802
  const self = this;
803
+ const tableScrollElement = this[tableScrollElementSymbol];
797
804
 
798
805
  // --- Column resizing state ---
799
806
  let isResizing = false;
@@ -801,6 +808,166 @@ function initEventHandler() {
801
808
  let startX = 0;
802
809
  let startWidth = 0;
803
810
 
811
+ // --- Table drag scrolling state ---
812
+ let isTableDragging = false;
813
+ let tableDragMoved = false;
814
+ let tableDragPointerId = null;
815
+ let tableDragStartX = 0;
816
+ let tableDragStartY = 0;
817
+ let tableDragStartScrollLeft = 0;
818
+ let suppressClickUntil = 0;
819
+
820
+ const isInteractiveDragTarget = (event) => {
821
+ if (
822
+ findTargetElementFromEvent(event, "data-monster-role", "resize-handle")
823
+ ) {
824
+ return true;
825
+ }
826
+
827
+ const path = event.composedPath?.();
828
+ if (!isArray(path)) {
829
+ return false;
830
+ }
831
+
832
+ for (const node of path) {
833
+ if (!(node instanceof HTMLElement)) {
834
+ continue;
835
+ }
836
+
837
+ if (node === tableScrollElement) {
838
+ return false;
839
+ }
840
+
841
+ if (
842
+ node.matches(
843
+ [
844
+ "button",
845
+ "a",
846
+ "input",
847
+ "select",
848
+ "textarea",
849
+ "label",
850
+ "summary",
851
+ "[contenteditable]",
852
+ "[contenteditable='true']",
853
+ "[draggable='true']",
854
+ "[role='button']",
855
+ "[role='link']",
856
+ "[role='textbox']",
857
+ "[role='checkbox']",
858
+ "[role='switch']",
859
+ "[role='menuitem']",
860
+ ].join(","),
861
+ )
862
+ ) {
863
+ return true;
864
+ }
865
+ }
866
+
867
+ return false;
868
+ };
869
+
870
+ const stopTableDragging = () => {
871
+ if (tableScrollElement instanceof HTMLElement) {
872
+ tableScrollElement.classList.remove("is-dragging");
873
+ }
874
+
875
+ getWindow().removeEventListener("pointermove", onTablePointerMove);
876
+ getWindow().removeEventListener("pointerup", onTablePointerUp);
877
+ getWindow().removeEventListener("pointercancel", onTablePointerUp);
878
+
879
+ if (tableDragPointerId !== null) {
880
+ tableScrollElement?.releasePointerCapture?.(tableDragPointerId);
881
+ }
882
+
883
+ isTableDragging = false;
884
+ tableDragMoved = false;
885
+ tableDragPointerId = null;
886
+ };
887
+
888
+ const onTablePointerDown = (event) => {
889
+ if (!(tableScrollElement instanceof HTMLElement)) {
890
+ return;
891
+ }
892
+
893
+ if (
894
+ (event.pointerType === "mouse" && event.button !== 0) ||
895
+ event.isPrimary === false
896
+ ) {
897
+ return;
898
+ }
899
+
900
+ if (tableScrollElement.scrollWidth <= tableScrollElement.clientWidth) {
901
+ return;
902
+ }
903
+
904
+ if (isInteractiveDragTarget(event)) {
905
+ return;
906
+ }
907
+
908
+ isTableDragging = true;
909
+ tableDragMoved = false;
910
+ tableDragPointerId = event.pointerId;
911
+ tableDragStartX = event.clientX;
912
+ tableDragStartY = event.clientY;
913
+ tableDragStartScrollLeft = tableScrollElement.scrollLeft;
914
+
915
+ tableScrollElement.setPointerCapture?.(event.pointerId);
916
+ getWindow().addEventListener("pointermove", onTablePointerMove);
917
+ getWindow().addEventListener("pointerup", onTablePointerUp);
918
+ getWindow().addEventListener("pointercancel", onTablePointerUp);
919
+ };
920
+
921
+ const onTablePointerMove = (event) => {
922
+ if (
923
+ !isTableDragging ||
924
+ tableDragPointerId === null ||
925
+ event.pointerId !== tableDragPointerId ||
926
+ !(tableScrollElement instanceof HTMLElement)
927
+ ) {
928
+ return;
929
+ }
930
+
931
+ const deltaX = event.clientX - tableDragStartX;
932
+ const deltaY = event.clientY - tableDragStartY;
933
+
934
+ if (!tableDragMoved) {
935
+ if (Math.abs(deltaX) < 8 || Math.abs(deltaX) <= Math.abs(deltaY)) {
936
+ return;
937
+ }
938
+
939
+ tableDragMoved = true;
940
+ tableScrollElement.classList.add("is-dragging");
941
+ }
942
+
943
+ event.preventDefault();
944
+ tableScrollElement.scrollLeft = tableDragStartScrollLeft - deltaX;
945
+ };
946
+
947
+ const onTablePointerUp = (event) => {
948
+ if (tableDragPointerId === null || event.pointerId !== tableDragPointerId) {
949
+ return;
950
+ }
951
+
952
+ if (tableDragMoved) {
953
+ suppressClickUntil = Date.now() + 250;
954
+ }
955
+
956
+ stopTableDragging();
957
+ };
958
+
959
+ const onTableClickCapture = (event) => {
960
+ if (suppressClickUntil < Date.now()) {
961
+ return;
962
+ }
963
+
964
+ event.preventDefault();
965
+ event.stopPropagation();
966
+ };
967
+
968
+ tableScrollElement?.addEventListener("pointerdown", onTablePointerDown);
969
+ tableScrollElement?.addEventListener("click", onTableClickCapture, true);
970
+
804
971
  // --- Pointer-based resize handlers (robust for mouse/touch/stylus) ---
805
972
  const onPointerDown = (event) => {
806
973
  // Important: Call helper with (attrName, value) – not with CSS selector
@@ -879,6 +1046,58 @@ function initEventHandler() {
879
1046
  const delimiterChar = this.getOption("copy.delimiter");
880
1047
  const rowBreak = this.getOption("copy.rowBreak");
881
1048
 
1049
+ const writeTextToClipboard = (text) => {
1050
+ if (getWindow().navigator.clipboard && text) {
1051
+ getWindow()
1052
+ .navigator.clipboard.writeText(text)
1053
+ .then(
1054
+ () => {},
1055
+ () => {},
1056
+ );
1057
+ }
1058
+ };
1059
+
1060
+ const getCopyTextForRow = (headCell) => {
1061
+ const index = headCell.getAttribute("data-monster-insert-reference");
1062
+ if (!index) {
1063
+ return "";
1064
+ }
1065
+
1066
+ const cols = self.getGridElements(
1067
+ `[data-monster-insert-reference="${index}"]`,
1068
+ );
1069
+ const colTexts = [];
1070
+
1071
+ for (let i = 0; i < cols.length; i++) {
1072
+ const col = cols[i];
1073
+
1074
+ if (
1075
+ col.querySelector("monster-button-bar") ||
1076
+ col.querySelector("monster-button")
1077
+ ) {
1078
+ continue;
1079
+ }
1080
+
1081
+ const cellValue = getCellValueForCopy(col);
1082
+ if (cellValue) {
1083
+ colTexts.push(quoteOpenChar + cellValue + quoteCloseChar);
1084
+ }
1085
+ }
1086
+
1087
+ return colTexts.join(delimiterChar);
1088
+ };
1089
+
1090
+ const getCopyTextForCell = (headCell) => {
1091
+ if (
1092
+ headCell.querySelector("monster-button-bar") ||
1093
+ headCell.querySelector("monster-button")
1094
+ ) {
1095
+ return "";
1096
+ }
1097
+
1098
+ return getCellValueForCopy(headCell);
1099
+ };
1100
+
882
1101
  // --- Column-Bar -> Header visibility & persistence ---
883
1102
  if (self[columnBarElementSymbol]) {
884
1103
  self[columnBarElementSymbol].attachObserver(
@@ -917,60 +1136,30 @@ function initEventHandler() {
917
1136
  }
918
1137
  });
919
1138
 
920
- // --- Double-Click Copy (column or entire row with Shift) ---
921
- const eventHandlerDoubleClickCopyToClipboard = (event) => {
1139
+ // --- Copy interactions ---
1140
+ const eventHandlerShiftClickCopyRowToClipboard = (event) => {
922
1141
  const headCell = findTargetElementFromEvent(event, "data-monster-head");
923
- if (!headCell) return;
924
-
925
- let text = "";
926
-
927
- if (event.shiftKey) {
928
- const index = headCell.getAttribute("data-monster-insert-reference");
929
- if (index) {
930
- const cols = self.getGridElements(
931
- `[data-monster-insert-reference="${index}"]`,
932
- );
933
-
934
- const colTexts = [];
935
- for (let i = 0; i < cols.length; i++) {
936
- const col = cols[i];
937
-
938
- if (
939
- col.querySelector("monster-button-bar") ||
940
- col.querySelector("monster-button")
941
- ) {
942
- continue;
943
- }
1142
+ if (!headCell || !event.shiftKey) {
1143
+ return;
1144
+ }
944
1145
 
945
- const cellValue = getCellValueForCopy(col);
946
- if (cellValue) {
947
- colTexts.push(quoteOpenChar + cellValue + quoteCloseChar);
948
- }
949
- }
1146
+ writeTextToClipboard(getCopyTextForRow(headCell));
1147
+ };
950
1148
 
951
- text = colTexts.join(delimiterChar);
952
- }
953
- } else {
954
- if (
955
- headCell.querySelector("monster-button-bar") ||
956
- headCell.querySelector("monster-button")
957
- ) {
958
- return;
959
- }
960
- text = getCellValueForCopy(headCell);
1149
+ const eventHandlerDoubleClickCopyToClipboard = (event) => {
1150
+ const headCell = findTargetElementFromEvent(event, "data-monster-head");
1151
+ if (!headCell || event.shiftKey) {
1152
+ return;
961
1153
  }
962
1154
 
963
- if (getWindow().navigator.clipboard && text) {
964
- getWindow()
965
- .navigator.clipboard.writeText(text)
966
- .then(
967
- () => {},
968
- () => {},
969
- );
970
- }
1155
+ writeTextToClipboard(getCopyTextForCell(headCell));
971
1156
  };
972
1157
 
973
1158
  if (self.getOption("features.doubleClickCopyToClipboard")) {
1159
+ self[gridElementSymbol].addEventListener(
1160
+ "click",
1161
+ eventHandlerShiftClickCopyRowToClipboard,
1162
+ );
974
1163
  self[gridElementSymbol].addEventListener(
975
1164
  "dblclick",
976
1165
  eventHandlerDoubleClickCopyToClipboard,
@@ -1537,6 +1726,10 @@ function initControlReferences() {
1537
1726
  "[data-monster-role=copy-all]",
1538
1727
  );
1539
1728
 
1729
+ this[tableScrollElementSymbol] = this.shadowRoot.querySelector(
1730
+ "[data-monster-role=table-scroll]",
1731
+ );
1732
+
1540
1733
  return this;
1541
1734
  }
1542
1735
 
@@ -56,6 +56,11 @@
56
56
  overflow-y: visible;
57
57
  width: 100%;
58
58
  padding-bottom: 3rem;
59
+ touch-action: pan-y;
60
+
61
+ &.is-dragging {
62
+ user-select: none;
63
+ }
59
64
  }
60
65
 
61
66
  :host {