@kolkrabbi/kol-component 0.213.0 → 0.214.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-component",
3
- "version": "0.213.0",
3
+ "version": "0.214.0",
4
4
  "description": "KOL design-system components \u2014 atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -601,17 +601,6 @@ export function MediaLibraryBrowse({
601
601
  <MediaSettings bucketMeta={bucketMeta} settings={settings} profile={profile} onChange={setSettings} onReset={() => setSettings(null)} onClose={() => setSettingsOpen(false)} settingsFooter={settingsFooter} />
602
602
  )}
603
603
 
604
- {/* THE TAB PILL (item 16) — floats over the list, so the list owes it
605
- room or its last row sits under the bar forever. Below `md` only;
606
- above it the 2026-08-26 one-view ruling stands and nothing here
607
- renders. */}
608
- {tabs?.length > 0 && (
609
- <>
610
- <div className="md:hidden" aria-hidden="true" style={{ height: TABBAR_H }} />
611
- <MobileTabBar tabs={tabs} value={activeTab} onChange={onTabChange} />
612
- </>
613
- )}
614
-
615
604
  {folderView === 'columns' ? (
616
605
  <div ref={columnsRef} className="relative">
617
606
  <ColumnBrowser
@@ -680,6 +669,24 @@ export function MediaLibraryBrowse({
680
669
  </p>
681
670
  )}
682
671
  {void onOpen}
672
+
673
+ {/* THE TAB PILL (item 16) — floats over the list, so the list owes it
674
+ room or its last row sits under the bar forever.
675
+ THE SPACER GOES LAST, AND THAT IS THE WHOLE POINT
676
+ (TabBarSpacerAboveTheList, kol-r2b2 2026-09-04). `MobileTabBar` is
677
+ `fixed`, so where it sits in the tree is irrelevant — the SPACER is
678
+ in NORMAL FLOW, so its position is everything. Rendered before the
679
+ list it failed twice at once: a 56px hole punched into the gap under
680
+ the pinned search, and the last row still running 99px under the
681
+ bar. The comment above named the failure it was meant to prevent and
682
+ the block sat in the wrong place anyway. Below `md` only; above it
683
+ the 2026-08-26 one-view ruling stands. */}
684
+ {tabs?.length > 0 && (
685
+ <>
686
+ <div className="md:hidden" aria-hidden="true" style={{ height: TABBAR_H }} />
687
+ <MobileTabBar tabs={tabs} value={activeTab} onChange={onTabChange} />
688
+ </>
689
+ )}
683
690
  </div>
684
691
  </div>
685
692
  )
@@ -18,7 +18,26 @@ import CloseButton from './CloseButton.jsx'
18
18
  const FOCUSABLE =
19
19
  'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
20
20
 
21
- export default function FullscreenOverlay({ open, onClose, closeButton = true, children }) {
21
+ export default function FullscreenOverlay({
22
+ open, onClose, closeButton = true,
23
+ /* WHERE FOCUS LANDS ON OPEN. The sheet takes it by default, which is right
24
+ * for a browser and wrong for a sheet opened to be TYPED IN: a child's
25
+ * `autoFocus` cannot win, because child effects run BEFORE the parent's and
26
+ * this one moves focus afterwards — so the field focuses and is immediately
27
+ * robbed, with nothing in either file looking wrong (kol-fxr measured it on
28
+ * design-editor 0.10.0: Save As routed into the dialog correctly and focus
29
+ * sat on `.kol-overlay-sheet`). Pass a ref to the node that should hold it.
30
+ * A ref that is empty on mount falls back to the sheet, so a conditional
31
+ * field cannot leave the overlay unfocused.
32
+ *
33
+ * The ref may point at the control ITSELF or at a WRAPPER around it — the
34
+ * first focusable descendant is taken. That is deliberate: a DS input is a
35
+ * component, not a DOM node, and whether it forwards a ref is a detail no
36
+ * caller should have to know to put focus in it. A plain `<div ref>` always
37
+ * works. */
38
+ initialFocus,
39
+ children,
40
+ }) {
22
41
  const sheetRef = useRef(null)
23
42
 
24
43
  /* Escape closes; Tab is TRAPPED in the sheet (SettingsPanel, 2026-08-26 —
@@ -45,13 +64,19 @@ export default function FullscreenOverlay({ open, onClose, closeButton = true, c
45
64
  const prev = document.body.style.overflow
46
65
  document.body.style.overflow = 'hidden'
47
66
  const prevFocus = document.activeElement
48
- sheetRef.current?.focus()
67
+ const wanted = initialFocus?.current
68
+ const target = wanted
69
+ ? (typeof wanted.focus === 'function' && wanted.matches?.(FOCUSABLE)
70
+ ? wanted
71
+ : wanted.querySelector?.(FOCUSABLE) ?? wanted)
72
+ : sheetRef.current
73
+ target?.focus?.()
49
74
  return () => {
50
75
  document.removeEventListener('keydown', onKey)
51
76
  document.body.style.overflow = prev
52
77
  if (prevFocus instanceof HTMLElement) prevFocus.focus()
53
78
  }
54
- }, [open, onClose])
79
+ }, [open, onClose, initialFocus])
55
80
 
56
81
  if (!open) return null
57
82