@kolkrabbi/kol-component 0.118.3 → 0.119.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.118.3",
3
+ "version": "0.119.0",
4
4
  "description": "KOL design-system components — atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -98,15 +98,22 @@ const COL_ICON = { image: 'image', video: 'video', audio: 'file', playlist: 'vid
98
98
  * row keeps its bottom hairline (`only:`) — its last row is also its first, and a missing
99
99
  * hairline beside neighbours that show one reads as a defect; and ONE ink for folders and files
100
100
  * — the `bg-fg-04` fill alone marks selection ("why is folder full opacity but not files?").
101
- * `muted` stays a prop for other callers; the file rows no longer pass it. */
101
+ * `muted` stays a prop for other callers; the file rows no longer pass it.
102
+ * STATE CLASSES (ColumnBrowserSeams, kol-r2b2 2026-08-28): `is-selected` / `is-cursor` on the row,
103
+ * and the fill LIVES IN THE THEME now (kol-components-molecules.css) instead of a `bg-fg-04`
104
+ * utility — the only hook a consumer had for "selected" was that utility, and a restyle that
105
+ * changes it breaks every rule hanging off it silently (twice already in kol-r2b2). It also lets the
106
+ * theme say the user's ruling — "ONLY one selected state can exist, not TWO": the selected row in
107
+ * the deepest column that holds one is full strength, every column on the way there is the trail. */
102
108
  function Row({ icon, label, active, cursor = false, trailing, onClick, muted = false }) {
103
109
  return (
104
110
  <li
105
111
  /* Row metrics are the DS Table's (kol-components-organisms.css .kol-table-cell-*):
106
112
  * 12px 16px padding, mono 12, an oq-08 hairline between rows, none after the last. */
107
113
  /* `cursor` = the keyboard row, drawn with the hover fill so ↑/↓ always shows where you are. */
108
- className={`kol-column-browser-row flex items-center gap-2 px-4 py-3 border-b last:border-b-0 only:border-b cursor-pointer transition-colors ${
109
- active || cursor ? 'bg-fg-04 text-fg-default' : muted ? 'text-fg-48 hover:bg-fg-04' : 'text-fg-default hover:bg-fg-04'
114
+ className={`kol-column-browser-row flex items-center gap-2 px-4 py-3 border-b last:border-b-0 only:border-b cursor-pointer transition-colors${
115
+ active ? ' is-selected' : ''}${cursor ? ' is-cursor' : ''} ${
116
+ active || cursor || !muted ? 'text-fg-default' : 'text-fg-48'
110
117
  }`}
111
118
  style={{ borderColor: 'var(--kol-oq-08)' }}
112
119
  onClick={onClick}
@@ -223,6 +230,7 @@ export default function ColumnBrowser({
223
230
  columnWidth = 260,
224
231
  columnWidths,
225
232
  onColumnResize,
233
+ autoFocus = false,
226
234
  className = '',
227
235
  }) {
228
236
  /* height: controlled-or-uncontrolled like Slider; widths: organism-internal,
@@ -296,6 +304,10 @@ export default function ColumnBrowser({
296
304
  * read as a second selection. Arrows arm it; a click seeds it. */
297
305
  const [cursorActive, setCursorActive] = useState(false)
298
306
  const rootRef = useRef(null)
307
+ /* `autoFocus` (ColumnBrowserSeams, kol-r2b2 2026-08-28): the arrow keys were dead until a row was
308
+ * clicked — nothing focused the root on mount, and the consumer reached into the DOM for it. Re-run
309
+ * on `prefix`, so a bucket switch from the header (which takes focus) hands the keyboard back. */
310
+ useEffect(() => { if (autoFocus) rootRef.current?.focus() }, [autoFocus, prefix])
299
311
 
300
312
  const land = (level, item) => {
301
313
  if (!item) return
@@ -299,7 +299,7 @@ const profileOf = (objects, rawFiles, systemCount) => ({
299
299
  /* ══ BROWSE — folder / files ═══════════════════════════════════════════════ */
300
300
  export function MediaLibraryBrowse({
301
301
  client, title = 'MEDIA', bucket, onBucketChange, prefix: prefixProp, onPrefix, defaults, settings: settingsProp, onSettingsChange,
302
- folderTree, headerActions, refreshKey, onOpen, className = '',
302
+ folderTree, headerActions, refreshKey, onOpen, autoFocus = false, className = '',
303
303
  }) {
304
304
  const [ownPrefix, setOwnPrefix] = useState('')
305
305
  const prefix = prefixProp ?? ownPrefix
@@ -405,6 +405,7 @@ export function MediaLibraryBrowse({
405
405
  {folderView === 'columns' ? (
406
406
  <div ref={columnsRef} className="relative">
407
407
  <ColumnBrowser
408
+ autoFocus={autoFocus}
408
409
  className={appRoot ? 'is-root' : ''}
409
410
  height={settings.columnHeight}
410
411
  onHeightChange={(px) => setSettings({ ...settings, columnHeight: px })}
@@ -467,7 +468,7 @@ export function MediaLibraryBrowse({
467
468
  /* ══ LIBRARY — the content-filters wall ═══════════════════════════════════ */
468
469
  export function MediaLibraryLibrary({
469
470
  client, title = 'MEDIA', bucket, onBucketChange, prefix = '', defaults, settings: settingsProp, onSettingsChange,
470
- headerActions, refreshKey, header = true, className = '',
471
+ headerActions, refreshKey, header = true, stats = true, className = '',
471
472
  }) {
472
473
  const [ownBucket, setOwnBucket] = useState(bucket)
473
474
  const bucketId = bucket ?? ownBucket
@@ -635,10 +636,14 @@ export function MediaLibraryLibrary({
635
636
  <MediaSettings bucketMeta={bucketMeta} settings={settings} profile={profile} onChange={setSettings} onReset={() => setSettings(null)} onClose={() => setSettingsOpen(false)} />
636
637
  )}
637
638
 
638
- <p className="kol-mono-12 text-fg-48">
639
- {files.length !== rawFiles.length ? `${files.length} of ${rawFiles.length}` : rawFiles.length}{' '}{rawFiles.length === 1 ? 'file' : 'files'} · {formatSize(totalBytes)}
640
- {systemCount > 0 && <span className="text-fg-32">{' · '}{systemCount} system files hidden</span>}
641
- </p>
639
+ {/* `stats={false}` (ColumnBrowserSeams, kol-r2b2 2026-08-28) — beside `header={false}`: a browse
640
+ * page stacked above this one already prints the folder-aware count ("it used to be a row") */}
641
+ {stats && (
642
+ <p className="kol-mono-12 text-fg-48">
643
+ {files.length !== rawFiles.length ? `${files.length} of ${rawFiles.length}` : rawFiles.length}{' '}{rawFiles.length === 1 ? 'file' : 'files'} · {formatSize(totalBytes)}
644
+ {systemCount > 0 && <span className="text-fg-32">{' · '}{systemCount} system files hidden</span>}
645
+ </p>
646
+ )}
642
647
 
643
648
  <ContentFilters
644
649
  items={files}