@johly/bits-ui 2.18.2 → 2.18.3

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.
@@ -21,6 +21,8 @@
21
21
  let scrollable = $state(false);
22
22
  let atStart = $state(true);
23
23
  let atEnd = $state(true);
24
+ let rowWidthFrame: number | null = null;
25
+ let appliedRowWidth: string | null = null;
24
26
  const listState = $derived<DataListChildrenSnippetProps<TMeta>>({
25
27
  search: surface.search,
26
28
  nodes: surface.displayNodes,
@@ -45,19 +47,111 @@
45
47
  atEnd = ref.scrollTop >= maxScrollTop - 1;
46
48
  }
47
49
 
50
+ function getRowWidthTarget(node: HTMLElement) {
51
+ return node.closest<HTMLElement>(
52
+ [
53
+ "[data-menu-content]",
54
+ "[data-menu-sub-content]",
55
+ "[data-dropdown-menu-content]",
56
+ "[data-dropdown-menu-sub-content]",
57
+ "[data-context-menu-content]",
58
+ "[data-context-menu-sub-content]",
59
+ "[data-menubar-content]",
60
+ "[data-menubar-sub-content]",
61
+ ].join(",")
62
+ );
63
+ }
64
+
65
+ function getRowCandidates(node: HTMLElement) {
66
+ const selector = [
67
+ "[data-menu-item]",
68
+ "[data-menu-checkbox-item]",
69
+ "[data-menu-radio-item]",
70
+ "[data-menu-sub-trigger]",
71
+ "[data-dropdown-menu-item]",
72
+ "[data-dropdown-menu-checkbox-item]",
73
+ "[data-dropdown-menu-radio-item]",
74
+ "[data-dropdown-menu-sub-trigger]",
75
+ "[data-context-menu-item]",
76
+ "[data-context-menu-checkbox-item]",
77
+ "[data-context-menu-radio-item]",
78
+ "[data-context-menu-sub-trigger]",
79
+ "[data-menubar-item]",
80
+ "[data-menubar-checkbox-item]",
81
+ "[data-menubar-radio-item]",
82
+ "[data-menubar-sub-trigger]",
83
+ ].join(",");
84
+ const rows = Array.from(node.querySelectorAll<HTMLElement>(selector));
85
+ return rows.length
86
+ ? rows
87
+ : Array.from(node.children).filter(
88
+ (child): child is HTMLElement => child instanceof HTMLElement
89
+ );
90
+ }
91
+
92
+ function updateRowWidth() {
93
+ rowWidthFrame = null;
94
+ if (!ref) return;
95
+ const rows = getRowCandidates(ref);
96
+ if (!rows.length) {
97
+ if (appliedRowWidth !== null) {
98
+ ref.style.removeProperty("--row-width");
99
+ getRowWidthTarget(ref)?.style.removeProperty("--row-width");
100
+ appliedRowWidth = null;
101
+ }
102
+ return;
103
+ }
104
+
105
+ let maxWidth = 0;
106
+ const restore: Array<() => void> = [];
107
+ for (const row of rows) {
108
+ const previousWidth = row.style.width;
109
+ row.style.width = "max-content";
110
+ restore.push(() => (row.style.width = previousWidth));
111
+ maxWidth = Math.max(maxWidth, row.scrollWidth, row.offsetWidth);
112
+ }
113
+ for (const restoreRow of restore) restoreRow();
114
+
115
+ if (maxWidth <= 0) return;
116
+ const width = `${Math.ceil(Math.min(maxWidth + 1, 500))}px`;
117
+ if (appliedRowWidth === width) return;
118
+ appliedRowWidth = width;
119
+ ref.style.setProperty("--row-width", width);
120
+ getRowWidthTarget(ref)?.style.setProperty("--row-width", width);
121
+ }
122
+
123
+ function queueUpdateRowWidth() {
124
+ if (!ref) return;
125
+ const win = ref.ownerDocument.defaultView;
126
+ if (!win) return;
127
+ if (rowWidthFrame !== null) win.cancelAnimationFrame(rowWidthFrame);
128
+ rowWidthFrame = win.requestAnimationFrame(updateRowWidth);
129
+ }
130
+
131
+ function clearRowWidthFrame() {
132
+ if (rowWidthFrame === null || !ref) return;
133
+ ref.ownerDocument.defaultView?.cancelAnimationFrame(rowWidthFrame);
134
+ rowWidthFrame = null;
135
+ }
136
+
48
137
  $effect(() => {
49
138
  const node = ref;
50
139
  if (!node) return;
51
140
  updateScrollState();
141
+ queueUpdateRowWidth();
52
142
  const win = node.ownerDocument.defaultView;
53
143
  if (!win) return;
54
- const resizeObserver = new win.ResizeObserver(updateScrollState);
144
+ const updateMeasurements = () => {
145
+ updateScrollState();
146
+ queueUpdateRowWidth();
147
+ };
148
+ const resizeObserver = new win.ResizeObserver(updateMeasurements);
55
149
  resizeObserver.observe(node);
56
150
  for (const child of Array.from(node.children)) {
57
151
  resizeObserver.observe(child);
58
152
  }
59
153
  const mutationObserver = new win.MutationObserver(() => {
60
- updateScrollState();
154
+ updateMeasurements();
61
155
  for (const child of Array.from(node.children)) {
62
156
  resizeObserver.observe(child);
63
157
  }
@@ -68,12 +162,16 @@
68
162
  node.removeEventListener("scroll", updateScrollState);
69
163
  mutationObserver.disconnect();
70
164
  resizeObserver.disconnect();
165
+ clearRowWidthFrame();
71
166
  };
72
167
  });
73
168
 
74
169
  $effect(() => {
75
170
  surface.displayNodes;
76
- afterTick(updateScrollState);
171
+ afterTick(() => {
172
+ updateScrollState();
173
+ queueUpdateRowWidth();
174
+ });
77
175
  });
78
176
 
79
177
  function stopNavigationEvent(event: KeyboardEvent) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johly/bits-ui",
3
- "version": "2.18.2",
3
+ "version": "2.18.3",
4
4
  "license": "MIT",
5
5
  "repository": "github:johanohly/bits-ui",
6
6
  "funding": "https://github.com/sponsors/huntabyte",