@adecore/ui 0.28.0 → 0.29.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.
@@ -98,10 +98,21 @@ export function TabsList({ className, children, start, end, after, variant = 'li
98
98
  }
99
99
  return () => observer.disconnect();
100
100
  });
101
- // A picked tab that scrolled out of sight comes back into it, wherever it was picked from.
101
+ // A picked tab that scrolled out of sight comes back into it, wherever it was picked from. Only the strip
102
+ // moves: `scrollIntoView` would scroll every ancestor too, such as a thread that holds a block of tabs.
102
103
  useEffect(() => {
103
- if (scrolls) {
104
- list.current?.querySelector('[aria-selected="true"]')?.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });
104
+ const row = list.current;
105
+ const tab = row?.querySelector('[aria-selected="true"]');
106
+ if (!scrolls || !row || !tab) {
107
+ return;
108
+ }
109
+ const strip = row.getBoundingClientRect();
110
+ const picked = tab.getBoundingClientRect();
111
+ if (picked.left < strip.left) {
112
+ row.scrollLeft -= strip.left - picked.left;
113
+ }
114
+ else if (picked.right > strip.right) {
115
+ row.scrollLeft += picked.right - strip.right;
105
116
  }
106
117
  }, [scrolls, value]);
107
118
  useEffect(() => {
package/dist/theme.css CHANGED
@@ -320,6 +320,9 @@
320
320
  same outline for something that takes focus without being a button, such as a row in a list. */
321
321
  .icon-btn:focus-visible, .menu-item:focus-visible, button:focus-visible, .focus-ring:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
322
322
  .menu-item:focus-visible { outline-offset: -2px; }
323
+ /* A container that only holds focus, a pane or a block a jump lands on, wears no ring: only what a person
324
+ operates does. A heading keeps it, since a prompt moves focus there to be read out. */
325
+ :where([tabindex='-1']:not(h1, h2, h3, h4, h5, h6)):focus-visible, .ade-split-pane:focus-visible, .ade-split-content:focus-visible { outline: none; }
323
326
  /* A card that holds a field, the composer's or a prompt's, wears the field's outline on its own edge. */
324
327
  .focus-ring-within:has(:focus-visible) { outline: 2px solid var(--accent); outline-offset: -1px; }
325
328
  /* Base UI moves DOM focus to the item the pointer hovers, and a browser keeps `:focus-visible`
@@ -796,7 +799,6 @@
796
799
  min-width: 0;
797
800
  border-bottom: 1px solid var(--separator);
798
801
  }
799
- .ade-split-content { outline-offset: -2px; }
800
802
  .ade-split-view[data-dragging] > .ade-split-content { pointer-events: none; }
801
803
  .ade-split-divider {
802
804
  position: absolute;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/ui",
3
- "version": "0.28.0",
3
+ "version": "0.29.1",
4
4
  "description": "React components, a theme, formatters and a settings dialog for desktop-grade apps on React 19, Base UI and Tailwind 4.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "author": "Bas Milius",
@@ -163,10 +163,20 @@ export function TabsList({ className, children, start, end, after, variant = 'li
163
163
  return () => observer.disconnect();
164
164
  });
165
165
 
166
- // A picked tab that scrolled out of sight comes back into it, wherever it was picked from.
166
+ // A picked tab that scrolled out of sight comes back into it, wherever it was picked from. Only the strip
167
+ // moves: `scrollIntoView` would scroll every ancestor too, such as a thread that holds a block of tabs.
167
168
  useEffect(() => {
168
- if (scrolls) {
169
- list.current?.querySelector('[aria-selected="true"]')?.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });
169
+ const row = list.current;
170
+ const tab = row?.querySelector<HTMLElement>('[aria-selected="true"]');
171
+ if (!scrolls || !row || !tab) {
172
+ return;
173
+ }
174
+ const strip = row.getBoundingClientRect();
175
+ const picked = tab.getBoundingClientRect();
176
+ if (picked.left < strip.left) {
177
+ row.scrollLeft -= strip.left - picked.left;
178
+ } else if (picked.right > strip.right) {
179
+ row.scrollLeft += picked.right - strip.right;
170
180
  }
171
181
  }, [scrolls, value]);
172
182
 
package/src/theme.css CHANGED
@@ -320,6 +320,9 @@
320
320
  same outline for something that takes focus without being a button, such as a row in a list. */
321
321
  .icon-btn:focus-visible, .menu-item:focus-visible, button:focus-visible, .focus-ring:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
322
322
  .menu-item:focus-visible { outline-offset: -2px; }
323
+ /* A container that only holds focus, a pane or a block a jump lands on, wears no ring: only what a person
324
+ operates does. A heading keeps it, since a prompt moves focus there to be read out. */
325
+ :where([tabindex='-1']:not(h1, h2, h3, h4, h5, h6)):focus-visible, .ade-split-pane:focus-visible, .ade-split-content:focus-visible { outline: none; }
323
326
  /* A card that holds a field, the composer's or a prompt's, wears the field's outline on its own edge. */
324
327
  .focus-ring-within:has(:focus-visible) { outline: 2px solid var(--accent); outline-offset: -1px; }
325
328
  /* Base UI moves DOM focus to the item the pointer hovers, and a browser keeps `:focus-visible`
@@ -796,7 +799,6 @@
796
799
  min-width: 0;
797
800
  border-bottom: 1px solid var(--separator);
798
801
  }
799
- .ade-split-content { outline-offset: -2px; }
800
802
  .ade-split-view[data-dragging] > .ade-split-content { pointer-events: none; }
801
803
  .ade-split-divider {
802
804
  position: absolute;