@gridkitjs/dom 0.1.0 → 0.2.0

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/dist/index.js CHANGED
@@ -66,6 +66,111 @@ function bodyRowFrom(target) {
66
66
  };
67
67
  }
68
68
 
69
+ // src/contextMenu/attachContextMenuTrigger.ts
70
+ var LONG_PRESS_DELAY = 500;
71
+ var LONG_PRESS_SLOP = 10;
72
+ function isContextMenuKey(event) {
73
+ if (event.ctrlKey || event.altKey || event.metaKey) {
74
+ return false;
75
+ }
76
+ return event.key === "ContextMenu" || event.key === "F10" && event.shiftKey;
77
+ }
78
+ function attachContextMenuTrigger(root, options) {
79
+ let swallowContextMenu = false;
80
+ let swallowClick = false;
81
+ let press = null;
82
+ function ask(event, point, source) {
83
+ const { target } = event;
84
+ return !event.defaultPrevented && target instanceof Element && root.contains(target) && options.onRequest({ target, point, source });
85
+ }
86
+ function request(event, point, source) {
87
+ if (!ask(event, point, source)) {
88
+ return false;
89
+ }
90
+ event.preventDefault();
91
+ return true;
92
+ }
93
+ function cancelPress() {
94
+ if (press !== null) {
95
+ window.clearTimeout(press.timer);
96
+ press = null;
97
+ }
98
+ }
99
+ function onKeyDown2(event) {
100
+ swallowContextMenu = false;
101
+ swallowClick = false;
102
+ if (isContextMenuKey(event) && request(event, null, "keyboard")) {
103
+ swallowContextMenu = true;
104
+ }
105
+ }
106
+ function onPointerDown(event) {
107
+ swallowContextMenu = false;
108
+ swallowClick = false;
109
+ cancelPress();
110
+ if (event.pointerType !== "touch" || !event.isPrimary) {
111
+ return;
112
+ }
113
+ const point = { x: event.clientX, y: event.clientY };
114
+ press = {
115
+ pointerId: event.pointerId,
116
+ ...point,
117
+ timer: window.setTimeout(() => {
118
+ press = null;
119
+ if (ask(event, point, "pointer")) {
120
+ swallowContextMenu = true;
121
+ swallowClick = true;
122
+ }
123
+ }, LONG_PRESS_DELAY)
124
+ };
125
+ }
126
+ function onPointerMove(event) {
127
+ if (press?.pointerId === event.pointerId && Math.hypot(event.clientX - press.x, event.clientY - press.y) > LONG_PRESS_SLOP) {
128
+ cancelPress();
129
+ }
130
+ }
131
+ function onPointerEnd(event) {
132
+ if (press?.pointerId === event.pointerId) {
133
+ cancelPress();
134
+ }
135
+ }
136
+ function onClick(event) {
137
+ if (swallowClick) {
138
+ swallowClick = false;
139
+ event.preventDefault();
140
+ event.stopPropagation();
141
+ }
142
+ }
143
+ function onContextMenu(event) {
144
+ if (swallowContextMenu) {
145
+ swallowContextMenu = false;
146
+ event.preventDefault();
147
+ return;
148
+ }
149
+ const touching = press !== null;
150
+ cancelPress();
151
+ if (request(event, { x: event.clientX, y: event.clientY }, "pointer") && touching) {
152
+ swallowClick = true;
153
+ }
154
+ }
155
+ document.addEventListener("keydown", onKeyDown2);
156
+ document.addEventListener("pointerdown", onPointerDown, true);
157
+ document.addEventListener("pointermove", onPointerMove, true);
158
+ document.addEventListener("pointerup", onPointerEnd, true);
159
+ document.addEventListener("pointercancel", onPointerEnd, true);
160
+ document.addEventListener("click", onClick, true);
161
+ document.addEventListener("contextmenu", onContextMenu);
162
+ return () => {
163
+ cancelPress();
164
+ document.removeEventListener("keydown", onKeyDown2);
165
+ document.removeEventListener("pointerdown", onPointerDown, true);
166
+ document.removeEventListener("pointermove", onPointerMove, true);
167
+ document.removeEventListener("pointerup", onPointerEnd, true);
168
+ document.removeEventListener("pointercancel", onPointerEnd, true);
169
+ document.removeEventListener("click", onClick, true);
170
+ document.removeEventListener("contextmenu", onContextMenu);
171
+ };
172
+ }
173
+
69
174
  // src/columnResize.ts
70
175
  import { applyColumnResize } from "@gridkitjs/core";
71
176
 
@@ -79,7 +184,7 @@ function startPointerGesture(element, pointerId, handlers) {
79
184
  element.removeEventListener("pointermove", move);
80
185
  element.removeEventListener("pointerup", end);
81
186
  element.removeEventListener("pointercancel", cancel);
82
- window.removeEventListener("keydown", onKeyDown);
187
+ window.removeEventListener("keydown", onKeyDown2);
83
188
  if (element.hasPointerCapture(pointerId)) {
84
189
  element.releasePointerCapture(pointerId);
85
190
  }
@@ -95,7 +200,7 @@ function startPointerGesture(element, pointerId, handlers) {
95
200
  stop();
96
201
  handlers.onCancel();
97
202
  }
98
- function onKeyDown(event) {
203
+ function onKeyDown2(event) {
99
204
  if (event.key === "Escape") {
100
205
  cancel();
101
206
  }
@@ -103,7 +208,7 @@ function startPointerGesture(element, pointerId, handlers) {
103
208
  element.addEventListener("pointermove", move);
104
209
  element.addEventListener("pointerup", end);
105
210
  element.addEventListener("pointercancel", cancel);
106
- window.addEventListener("keydown", onKeyDown);
211
+ window.addEventListener("keydown", onKeyDown2);
107
212
  }
108
213
 
109
214
  // src/columnResize.ts
@@ -425,6 +530,219 @@ function attachInfiniteScroll(root, options) {
425
530
  };
426
531
  }
427
532
 
533
+ // src/menu/attachMenu.ts
534
+ import { nextMenuIndex, typeaheadIndex } from "@gridkitjs/core";
535
+ var ITEM = [
536
+ '[role="menuitem"][data-gridkit-menu-item]',
537
+ '[role="menuitemcheckbox"][data-gridkit-menu-item]',
538
+ '[role="menuitemradio"][data-gridkit-menu-item]'
539
+ ].join(", ");
540
+ var HOVER_DELAY = 150;
541
+ var TYPEAHEAD_TIMEOUT = 500;
542
+ var AS_ENTRY = { type: "item" };
543
+ var MOVES = {
544
+ ArrowDown: "next",
545
+ ArrowUp: "previous",
546
+ Home: "first",
547
+ End: "last"
548
+ };
549
+ function idOf(item) {
550
+ return item.getAttribute("data-gridkit-menu-item") ?? "";
551
+ }
552
+ function isDisabled(item) {
553
+ return item.getAttribute("aria-disabled") === "true";
554
+ }
555
+ function opensSubmenu(item) {
556
+ const popup = item.getAttribute("aria-haspopup");
557
+ return popup === "menu" || popup === "true";
558
+ }
559
+ function labelOf(item) {
560
+ return (item.getAttribute("aria-label") ?? item.textContent).trim();
561
+ }
562
+ function isTypeahead(event) {
563
+ return /^\S$/u.test(event.key) && !event.ctrlKey && !event.metaKey && !event.altKey;
564
+ }
565
+ function attachMenu(panel, options) {
566
+ let typed = "";
567
+ let typeaheadTimer;
568
+ let hovered = null;
569
+ let hoverTimer;
570
+ function items() {
571
+ return Array.from(panel.querySelectorAll(ITEM));
572
+ }
573
+ function itemOf(target) {
574
+ const item = target instanceof Element ? target.closest(ITEM) : null;
575
+ return item !== null && panel.contains(item) ? item : null;
576
+ }
577
+ function setTabStop(stop) {
578
+ for (const item of items()) {
579
+ item.tabIndex = item === stop ? 0 : -1;
580
+ }
581
+ }
582
+ function focus(item) {
583
+ setTabStop(item);
584
+ item.focus();
585
+ }
586
+ function openSubmenu(item, focusTo) {
587
+ if (!isDisabled(item)) {
588
+ options.onOpenSubmenu?.(idOf(item), { focus: focusTo });
589
+ }
590
+ }
591
+ function choose(item, focusTo) {
592
+ if (opensSubmenu(item)) {
593
+ openSubmenu(item, focusTo);
594
+ } else if (!isDisabled(item)) {
595
+ options.onActivate(idOf(item));
596
+ }
597
+ }
598
+ function typeahead(key, all, from) {
599
+ typed += key;
600
+ window.clearTimeout(typeaheadTimer);
601
+ typeaheadTimer = window.setTimeout(() => {
602
+ typed = "";
603
+ }, TYPEAHEAD_TIMEOUT);
604
+ const match = all[typeaheadIndex(all.map(labelOf), from, typed)];
605
+ if (match !== void 0) {
606
+ focus(match);
607
+ }
608
+ }
609
+ function onKeyDown2(event) {
610
+ if (event.isComposing) {
611
+ return;
612
+ }
613
+ const all = items();
614
+ const item = itemOf(event.target);
615
+ const from = item === null ? -1 : all.indexOf(item);
616
+ const move = MOVES[event.key];
617
+ if (move !== void 0) {
618
+ const next = all[nextMenuIndex(
619
+ all.map(() => AS_ENTRY),
620
+ from,
621
+ move
622
+ )];
623
+ if (next !== void 0) {
624
+ focus(next);
625
+ }
626
+ } else if (event.key === "Enter" || event.key === " ") {
627
+ if (item !== null) {
628
+ choose(item, "first");
629
+ }
630
+ } else if (event.key === "ArrowRight") {
631
+ if (item !== null && opensSubmenu(item)) {
632
+ openSubmenu(item, "first");
633
+ }
634
+ } else if (event.key === "ArrowLeft") {
635
+ options.onCloseSubmenu?.();
636
+ } else if (event.key === "Tab") {
637
+ options.onClose("tab");
638
+ } else if (isTypeahead(event)) {
639
+ typeahead(event.key, all, from);
640
+ } else {
641
+ return;
642
+ }
643
+ event.preventDefault();
644
+ }
645
+ function onFocusIn(event) {
646
+ const item = itemOf(event.target);
647
+ if (item !== null) {
648
+ setTabStop(item);
649
+ }
650
+ }
651
+ function onClick(event) {
652
+ const item = itemOf(event.target);
653
+ if (item !== null) {
654
+ choose(item, "none");
655
+ }
656
+ }
657
+ function onPointerMove(event) {
658
+ if (event.pointerType === "touch") {
659
+ return;
660
+ }
661
+ const item = itemOf(event.target);
662
+ if (item !== null && document.activeElement !== item) {
663
+ focus(item);
664
+ }
665
+ if (item === hovered) {
666
+ return;
667
+ }
668
+ hovered = item;
669
+ window.clearTimeout(hoverTimer);
670
+ if (item !== null) {
671
+ hoverTimer = window.setTimeout(() => {
672
+ if (opensSubmenu(item) && !isDisabled(item)) {
673
+ openSubmenu(item, "none");
674
+ } else {
675
+ options.onCollapseSubmenu?.();
676
+ }
677
+ }, HOVER_DELAY);
678
+ }
679
+ }
680
+ function onPointerLeave() {
681
+ hovered = null;
682
+ window.clearTimeout(hoverTimer);
683
+ }
684
+ const focused = itemOf(document.activeElement);
685
+ setTabStop(focused ?? items()[0]);
686
+ panel.addEventListener("keydown", onKeyDown2);
687
+ panel.addEventListener("focusin", onFocusIn);
688
+ panel.addEventListener("click", onClick);
689
+ panel.addEventListener("pointermove", onPointerMove);
690
+ panel.addEventListener("pointerleave", onPointerLeave);
691
+ return {
692
+ focusItem(which) {
693
+ const all = items();
694
+ const item = typeof which === "number" ? all[which] : all.find((candidate) => idOf(candidate) === which);
695
+ if (item !== void 0) {
696
+ focus(item);
697
+ }
698
+ },
699
+ detach() {
700
+ window.clearTimeout(typeaheadTimer);
701
+ window.clearTimeout(hoverTimer);
702
+ panel.removeEventListener("keydown", onKeyDown2);
703
+ panel.removeEventListener("focusin", onFocusIn);
704
+ panel.removeEventListener("click", onClick);
705
+ panel.removeEventListener("pointermove", onPointerMove);
706
+ panel.removeEventListener("pointerleave", onPointerLeave);
707
+ }
708
+ };
709
+ }
710
+
711
+ // src/overlay/overlayStack.ts
712
+ var stack = [];
713
+ function onKeyDown(event) {
714
+ const topmost = stack.at(-1);
715
+ if (event.key !== "Escape" || event.isComposing || topmost === void 0) {
716
+ return;
717
+ }
718
+ event.preventDefault();
719
+ event.stopPropagation();
720
+ topmost.onEscape();
721
+ }
722
+ function pushOverlay(overlay) {
723
+ if (stack.length === 0) {
724
+ window.addEventListener("keydown", onKeyDown, true);
725
+ }
726
+ stack.push(overlay);
727
+ }
728
+ function removeOverlay(overlay) {
729
+ const index = stack.indexOf(overlay);
730
+ if (index === -1) {
731
+ return;
732
+ }
733
+ const above = stack.splice(index).slice(1).reverse();
734
+ for (const child of above) {
735
+ child.onParentDetached();
736
+ }
737
+ if (stack.length === 0) {
738
+ window.removeEventListener("keydown", onKeyDown, true);
739
+ }
740
+ }
741
+ function isInsideOverlayAbove(overlay, target) {
742
+ const index = stack.indexOf(overlay);
743
+ return index !== -1 && stack.slice(index + 1).some((above) => above.panel.contains(target));
744
+ }
745
+
428
746
  // src/overlay/placeOverlay.ts
429
747
  var OPPOSITE_SIDE = {
430
748
  top: "bottom",
@@ -480,6 +798,7 @@ function clampIntoView(value, limit) {
480
798
  function placeOverlay(anchor, panel, viewport, options = {}) {
481
799
  const [preferredSide, preferredAlign] = (options.placement ?? "bottom-start").split("-");
482
800
  const gap = options.gap ?? 0;
801
+ const offset = options.offset ?? 0;
483
802
  const vertical = isVertical(preferredSide);
484
803
  const side = chooseSide(
485
804
  preferredSide,
@@ -487,7 +806,7 @@ function placeOverlay(anchor, panel, viewport, options = {}) {
487
806
  anchor,
488
807
  viewport
489
808
  );
490
- const anchorStart = vertical ? anchor.left : anchor.top;
809
+ const anchorStart = (vertical ? anchor.left : anchor.top) + offset;
491
810
  const anchorEnd = anchorStart + (vertical ? anchor.width : anchor.height);
492
811
  const extent = vertical ? panel.width : panel.height;
493
812
  const room = vertical ? viewport.width : viewport.height;
@@ -514,24 +833,59 @@ function anchorRect(anchor) {
514
833
  }
515
834
  return { top: anchor.y, left: anchor.x, width: 0, height: 0 };
516
835
  }
836
+ function initialFocusTarget(panel, initialFocus) {
837
+ if (initialFocus === "none") {
838
+ return null;
839
+ }
840
+ if (initialFocus instanceof HTMLElement) {
841
+ return initialFocus;
842
+ }
843
+ const first = initialFocus === "first" ? panel.querySelector(FOCUSABLE) : null;
844
+ if (first !== null) {
845
+ return first;
846
+ }
847
+ if (!panel.hasAttribute("tabindex")) {
848
+ panel.tabIndex = -1;
849
+ }
850
+ return panel;
851
+ }
517
852
  function attachOverlay(anchor, panel, options) {
518
853
  const { onDismiss } = options;
519
854
  const returnFocusTo = options.returnFocusTo === void 0 ? document.activeElement instanceof HTMLElement ? document.activeElement : null : options.returnFocusTo;
520
- function belongs(target) {
521
- return target instanceof Node && (panel.contains(target) || anchor instanceof Element && anchor.contains(target));
522
- }
523
- function onKeyDown(event) {
524
- if (event.key === "Escape") {
855
+ const stacked = {
856
+ panel,
857
+ onEscape: () => {
525
858
  onDismiss("escape");
859
+ },
860
+ onParentDetached: () => {
861
+ onDismiss("parent");
862
+ detach();
526
863
  }
864
+ };
865
+ function inside(target) {
866
+ return target instanceof Node && (panel.contains(target) || isInsideOverlayAbove(stacked, target));
867
+ }
868
+ function belongs(target) {
869
+ return inside(target) || anchor instanceof Element && target instanceof Node && anchor.contains(target);
527
870
  }
528
871
  function onPointerDown(event) {
529
872
  if (!belongs(event.target)) {
530
873
  onDismiss("outside");
531
874
  }
532
875
  }
876
+ let settlingFrame = null;
877
+ function onInput() {
878
+ if (settlingFrame !== null) {
879
+ cancelAnimationFrame(settlingFrame);
880
+ }
881
+ settlingFrame = requestAnimationFrame(() => {
882
+ settlingFrame = requestAnimationFrame(() => {
883
+ settlingFrame = null;
884
+ });
885
+ });
886
+ }
533
887
  function onScroll(event) {
534
- if (!(event.target instanceof Node && panel.contains(event.target))) {
888
+ if (!inside(event.target) && settlingFrame === null) {
535
889
  onDismiss("scroll");
536
890
  }
537
891
  }
@@ -546,50 +900,72 @@ function attachOverlay(anchor, panel, options) {
546
900
  function onWindowBlur() {
547
901
  onDismiss("blur");
548
902
  }
549
- panel.style.position = "fixed";
550
- panel.style.top = "0px";
551
- panel.style.left = "0px";
552
- const { width, height } = panel.getBoundingClientRect();
553
- const position = placeOverlay(
554
- anchorRect(anchor),
555
- { width, height },
556
- {
557
- width: document.documentElement.clientWidth,
558
- height: document.documentElement.clientHeight
559
- },
560
- options
561
- );
562
- panel.style.top = `${String(position.top)}px`;
563
- panel.style.left = `${String(position.left)}px`;
564
- panel.setAttribute("data-gridkit-placement", position.placement);
565
- const first = panel.querySelector(FOCUSABLE);
566
- if (first === null && !panel.hasAttribute("tabindex")) {
567
- panel.tabIndex = -1;
903
+ let lastAnchorBox = null;
904
+ function anchorBox() {
905
+ if (lastAnchorBox === null || !(anchor instanceof Element) || anchor.isConnected) {
906
+ lastAnchorBox = anchorRect(anchor);
907
+ }
908
+ return lastAnchorBox;
568
909
  }
569
- (first ?? panel).focus({ preventScroll: true });
570
- document.addEventListener("keydown", onKeyDown, true);
910
+ function place() {
911
+ panel.style.top = "0px";
912
+ panel.style.left = "0px";
913
+ const { width, height } = panel.getBoundingClientRect();
914
+ const position = placeOverlay(
915
+ anchorBox(),
916
+ { width, height },
917
+ {
918
+ width: document.documentElement.clientWidth,
919
+ height: document.documentElement.clientHeight
920
+ },
921
+ options
922
+ );
923
+ panel.style.top = `${String(position.top)}px`;
924
+ panel.style.left = `${String(position.left)}px`;
925
+ panel.setAttribute("data-gridkit-placement", position.placement);
926
+ }
927
+ panel.style.position = "fixed";
928
+ place();
929
+ const resizeObserver = new ResizeObserver(place);
930
+ resizeObserver.observe(panel);
931
+ pushOverlay(stacked);
932
+ initialFocusTarget(panel, options.initialFocus ?? "first")?.focus({
933
+ preventScroll: true
934
+ });
571
935
  document.addEventListener("pointerdown", onPointerDown, true);
572
936
  window.addEventListener("scroll", onScroll, { capture: true, passive: true });
573
937
  window.addEventListener("resize", onResize);
574
938
  window.addEventListener("blur", onWindowBlur);
575
939
  panel.addEventListener("focusout", onFocusOut);
940
+ if (options.ignoreScrollAfterInput === true) {
941
+ panel.addEventListener("input", onInput);
942
+ panel.addEventListener("change", onInput);
943
+ }
576
944
  let attached = true;
577
- return () => {
945
+ function detach() {
578
946
  if (!attached) {
579
947
  return;
580
948
  }
581
949
  attached = false;
582
- document.removeEventListener("keydown", onKeyDown, true);
950
+ const active = document.activeElement;
951
+ const focusInside = active === null || active === document.body || inside(active);
952
+ removeOverlay(stacked);
953
+ resizeObserver.disconnect();
583
954
  document.removeEventListener("pointerdown", onPointerDown, true);
584
955
  window.removeEventListener("scroll", onScroll, { capture: true });
585
956
  window.removeEventListener("resize", onResize);
586
957
  window.removeEventListener("blur", onWindowBlur);
587
958
  panel.removeEventListener("focusout", onFocusOut);
588
- const active = document.activeElement;
589
- if (returnFocusTo !== null && (active === null || active === document.body || panel.contains(active))) {
959
+ panel.removeEventListener("input", onInput);
960
+ panel.removeEventListener("change", onInput);
961
+ if (settlingFrame !== null) {
962
+ cancelAnimationFrame(settlingFrame);
963
+ }
964
+ if (returnFocusTo !== null && focusInside) {
590
965
  returnFocusTo.focus({ preventScroll: true });
591
966
  }
592
- };
967
+ }
968
+ return detach;
593
969
  }
594
970
 
595
971
  // src/rowVirtualizer.ts
@@ -716,10 +1092,214 @@ function createRowVirtualizer(scrollElement, options) {
716
1092
  }
717
1093
  };
718
1094
  }
1095
+
1096
+ // src/toolbar/attachToolbar.ts
1097
+ import { nextMenuIndex as nextMenuIndex2 } from "@gridkitjs/core";
1098
+ var ENTRY = "[data-gridkit-toolbar-item]";
1099
+ var FOCUSABLE2 = [
1100
+ "button:not(:disabled)",
1101
+ "input:not(:disabled)",
1102
+ "select:not(:disabled)",
1103
+ "textarea:not(:disabled)",
1104
+ "a[href]",
1105
+ "[tabindex]"
1106
+ ].join(", ");
1107
+ var MOVES2 = {
1108
+ ArrowRight: "next",
1109
+ ArrowLeft: "previous",
1110
+ Home: "first",
1111
+ End: "last"
1112
+ };
1113
+ var AS_ENTRY2 = { type: "item" };
1114
+ var CARETLESS_INPUTS = /* @__PURE__ */ new Set([
1115
+ "button",
1116
+ "checkbox",
1117
+ "color",
1118
+ "file",
1119
+ "image",
1120
+ "radio",
1121
+ "range",
1122
+ "reset",
1123
+ "submit"
1124
+ ]);
1125
+ function targetOf(entry) {
1126
+ return entry.matches(FOCUSABLE2) ? entry : entry.querySelector(FOCUSABLE2);
1127
+ }
1128
+ function textFieldOf(target) {
1129
+ return target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement && !CARETLESS_INPUTS.has(target.type) ? target : null;
1130
+ }
1131
+ function belongsToField(target, move) {
1132
+ if (target instanceof HTMLElement && target.isContentEditable) {
1133
+ return true;
1134
+ }
1135
+ const field = textFieldOf(target);
1136
+ if (field === null) {
1137
+ return false;
1138
+ }
1139
+ const { selectionStart, selectionEnd, value } = field;
1140
+ if (move === "first" || move === "last" || selectionStart === null || selectionStart !== selectionEnd) {
1141
+ return true;
1142
+ }
1143
+ return move === "previous" ? selectionStart > 0 : selectionStart < value.length;
1144
+ }
1145
+ function attachToolbar(toolbar) {
1146
+ let stop;
1147
+ function controls() {
1148
+ return Array.from(toolbar.querySelectorAll(ENTRY)).flatMap(
1149
+ (entry) => {
1150
+ const target = targetOf(entry);
1151
+ return target === null ? [] : [{ entry, target }];
1152
+ }
1153
+ );
1154
+ }
1155
+ function indexOf(all2, node) {
1156
+ return node instanceof Node ? all2.findIndex((control) => control.entry.contains(node)) : -1;
1157
+ }
1158
+ function setTabStop(next) {
1159
+ stop = next;
1160
+ for (const { target } of controls()) {
1161
+ target.tabIndex = target === next ? 0 : -1;
1162
+ }
1163
+ }
1164
+ function onKeyDown2(event) {
1165
+ const move = MOVES2[event.key];
1166
+ if (move === void 0 || event.isComposing || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || belongsToField(event.target, move)) {
1167
+ return;
1168
+ }
1169
+ const all2 = controls();
1170
+ const from = indexOf(all2, event.target);
1171
+ if (from === -1) {
1172
+ return;
1173
+ }
1174
+ const next = all2[nextMenuIndex2(
1175
+ all2.map(() => AS_ENTRY2),
1176
+ from,
1177
+ move
1178
+ )];
1179
+ if (next !== void 0) {
1180
+ event.preventDefault();
1181
+ setTabStop(next.target);
1182
+ next.target.focus();
1183
+ }
1184
+ }
1185
+ function onFocusIn(event) {
1186
+ const all2 = controls();
1187
+ const control = all2[indexOf(all2, event.target)];
1188
+ if (control !== void 0) {
1189
+ setTabStop(control.target);
1190
+ }
1191
+ }
1192
+ const observer = new MutationObserver(() => {
1193
+ const all2 = controls();
1194
+ setTabStop(
1195
+ all2.find(({ target }) => target === stop)?.target ?? all2[0]?.target
1196
+ );
1197
+ });
1198
+ const all = controls();
1199
+ setTabStop(
1200
+ all[indexOf(all, document.activeElement)]?.target ?? all[0]?.target
1201
+ );
1202
+ toolbar.addEventListener("keydown", onKeyDown2);
1203
+ toolbar.addEventListener("focusin", onFocusIn);
1204
+ observer.observe(toolbar, { childList: true, subtree: true });
1205
+ return () => {
1206
+ observer.disconnect();
1207
+ toolbar.removeEventListener("keydown", onKeyDown2);
1208
+ toolbar.removeEventListener("focusin", onFocusIn);
1209
+ };
1210
+ }
1211
+
1212
+ // src/toolbar/observeToolbarOverflow.ts
1213
+ import {
1214
+ fitToolbarItems,
1215
+ isPinnedToolbarItem
1216
+ } from "@gridkitjs/core";
1217
+ var ENTRY_ATTRIBUTE = "data-gridkit-toolbar-item";
1218
+ var MORE_ATTRIBUTE = "data-gridkit-toolbar-more";
1219
+ function pixels(value) {
1220
+ return Number.parseFloat(value) || 0;
1221
+ }
1222
+ function outerWidth(element) {
1223
+ const style = getComputedStyle(element);
1224
+ return element.getBoundingClientRect().width + pixels(style.marginLeft) + pixels(style.marginRight);
1225
+ }
1226
+ function observeToolbarOverflow(toolbar, options) {
1227
+ let current = options;
1228
+ const widths = /* @__PURE__ */ new Map();
1229
+ let moreWidth = 0;
1230
+ let reported;
1231
+ function drawnEntries() {
1232
+ return Array.from(
1233
+ toolbar.querySelectorAll(`[${ENTRY_ATTRIBUTE}]`)
1234
+ );
1235
+ }
1236
+ function measure() {
1237
+ for (const element of drawnEntries()) {
1238
+ if (element.hasAttribute(MORE_ATTRIBUTE)) {
1239
+ moreWidth = outerWidth(element);
1240
+ } else {
1241
+ widths.set(
1242
+ element.getAttribute(ENTRY_ATTRIBUTE) ?? "",
1243
+ outerWidth(element)
1244
+ );
1245
+ }
1246
+ }
1247
+ const style = getComputedStyle(toolbar);
1248
+ const gap = pixels(style.columnGap);
1249
+ const fit = fitToolbarItems({
1250
+ widths: current.items.map(
1251
+ (item) => (item.type === "spacer" ? 0 : widths.get(item.key) ?? 0) + gap
1252
+ ),
1253
+ available: toolbar.clientWidth - pixels(style.paddingLeft) - pixels(style.paddingRight) + gap,
1254
+ moreWidth: moreWidth + gap,
1255
+ pinned: current.items.map(isPinnedToolbarItem)
1256
+ });
1257
+ if (fit !== reported) {
1258
+ reported = fit;
1259
+ current.onChange(fit);
1260
+ }
1261
+ }
1262
+ const resizes = new ResizeObserver(measure);
1263
+ function observeSizes() {
1264
+ resizes.disconnect();
1265
+ resizes.observe(toolbar);
1266
+ for (const element of drawnEntries()) {
1267
+ resizes.observe(element);
1268
+ }
1269
+ }
1270
+ const draws = new MutationObserver(() => {
1271
+ observeSizes();
1272
+ measure();
1273
+ });
1274
+ observeSizes();
1275
+ draws.observe(toolbar, { childList: true, subtree: true });
1276
+ measure();
1277
+ return {
1278
+ update(next) {
1279
+ const labels = new Map(
1280
+ current.items.map((item) => [item.key, item.label])
1281
+ );
1282
+ for (const item of next.items) {
1283
+ if (labels.has(item.key) && labels.get(item.key) !== item.label) {
1284
+ widths.delete(item.key);
1285
+ }
1286
+ }
1287
+ current = next;
1288
+ measure();
1289
+ },
1290
+ detach() {
1291
+ resizes.disconnect();
1292
+ draws.disconnect();
1293
+ }
1294
+ };
1295
+ }
719
1296
  export {
720
1297
  NO_DRAG,
1298
+ attachContextMenuTrigger,
721
1299
  attachInfiniteScroll,
1300
+ attachMenu,
722
1301
  attachOverlay,
1302
+ attachToolbar,
723
1303
  bodyCellFrom,
724
1304
  bodyRowFrom,
725
1305
  createGridFocus,
@@ -730,6 +1310,7 @@ export {
730
1310
  measureColumnContent,
731
1311
  measurePageSize,
732
1312
  observeElementWidth,
1313
+ observeToolbarOverflow,
733
1314
  placeOverlay,
734
1315
  resolveColumnDropTarget,
735
1316
  resolveGroupByDropTarget,