@kolkrabbi/kol-component 0.118.2 → 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.
|
|
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",
|
|
@@ -92,14 +92,28 @@ const isImage = (o) => (o.contentType || '').startsWith('image/')
|
|
|
92
92
|
/* kol-icons has no `audio` glyph yet — audio rows wear `file` until it does */
|
|
93
93
|
const COL_ICON = { image: 'image', video: 'video', audio: 'file', playlist: 'video' }
|
|
94
94
|
|
|
95
|
+
/* ColumnBrowserChromeCorrections (kol-r2b2 2026-08-28, four user rulings measured live on admin.):
|
|
96
|
+
* every column keeps its right edge, the last included — without it the browser reads as an
|
|
97
|
+
* open-sided container when the columns do not fill the box (Finder closes it); a column's ONLY
|
|
98
|
+
* row keeps its bottom hairline (`only:`) — its last row is also its first, and a missing
|
|
99
|
+
* hairline beside neighbours that show one reads as a defect; and ONE ink for folders and files
|
|
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.
|
|
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. */
|
|
95
108
|
function Row({ icon, label, active, cursor = false, trailing, onClick, muted = false }) {
|
|
96
109
|
return (
|
|
97
110
|
<li
|
|
98
111
|
/* Row metrics are the DS Table's (kol-components-organisms.css .kol-table-cell-*):
|
|
99
112
|
* 12px 16px padding, mono 12, an oq-08 hairline between rows, none after the last. */
|
|
100
113
|
/* `cursor` = the keyboard row, drawn with the hover fill so ↑/↓ always shows where you are. */
|
|
101
|
-
className={`kol-column-browser-row flex items-center gap-2 px-4 py-3 border-b last:border-b-0 cursor-pointer transition-colors
|
|
102
|
-
active
|
|
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'
|
|
103
117
|
}`}
|
|
104
118
|
style={{ borderColor: 'var(--kol-oq-08)' }}
|
|
105
119
|
onClick={onClick}
|
|
@@ -216,6 +230,7 @@ export default function ColumnBrowser({
|
|
|
216
230
|
columnWidth = 260,
|
|
217
231
|
columnWidths,
|
|
218
232
|
onColumnResize,
|
|
233
|
+
autoFocus = false,
|
|
219
234
|
className = '',
|
|
220
235
|
}) {
|
|
221
236
|
/* height: controlled-or-uncontrolled like Slider; widths: organism-internal,
|
|
@@ -289,6 +304,10 @@ export default function ColumnBrowser({
|
|
|
289
304
|
* read as a second selection. Arrows arm it; a click seeds it. */
|
|
290
305
|
const [cursorActive, setCursorActive] = useState(false)
|
|
291
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])
|
|
292
311
|
|
|
293
312
|
const land = (level, item) => {
|
|
294
313
|
if (!item) return
|
|
@@ -383,7 +402,7 @@ export default function ColumnBrowser({
|
|
|
383
402
|
return (
|
|
384
403
|
<Fragment key={level || '/'}>
|
|
385
404
|
<ul
|
|
386
|
-
className=
|
|
405
|
+
className="kol-column-browser-column shrink-0 overflow-y-auto border-r"
|
|
387
406
|
style={{ borderColor: 'var(--kol-oq-08)', width: widthOf(k) }}
|
|
388
407
|
>
|
|
389
408
|
{folders.map((f, i) => (
|
|
@@ -402,7 +421,6 @@ export default function ColumnBrowser({
|
|
|
402
421
|
key={o.key}
|
|
403
422
|
icon={COL_ICON[kindOf(o)] || 'file'}
|
|
404
423
|
label={o.displayKey ?? o.key}
|
|
405
|
-
muted={shown?.key !== o.key}
|
|
406
424
|
active={shown?.key === o.key}
|
|
407
425
|
cursor={cursorActive && cursor.col === k && cursor.idx === folders.length + i}
|
|
408
426
|
onClick={() => { setCursor({ col: k, idx: folders.length + i }); setCursorActive(true); rootRef.current?.focus(); pickFile(level, o) }}
|
|
@@ -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
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
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}
|