@kolkrabbi/kol-component 0.97.0 → 0.97.2

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.97.0",
3
+ "version": "0.97.2",
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",
@@ -35,6 +35,7 @@ import { Icon } from '@kolkrabbi/kol-icons'
35
35
  * @param {Function} onPrefix (prefix) => void
36
36
  * @param {{files: object[], index: number}|null} quickLook the open Quick Look, or null
37
37
  * @param {Function} onQuickLook (quickLook|null) => void — space opens / closes, ↑/↓ step while open
38
+ * @param {Function} onPick (file|null) => void — the picked file whenever it changes (a folder pick or an outside prefix change → null)
38
39
  * @param {Function} urlOf (o) => string — the object's public URL for the preview image
39
40
  * @param {Function} kindOf (o) => 'image' | 'video' | 'audio' | string
40
41
  * @param {Object} kindLabel kind → label shown when there is no visual preview
@@ -141,6 +142,7 @@ export default function ColumnBrowser({
141
142
  onPrefix = () => {},
142
143
  quickLook = null,
143
144
  onQuickLook,
145
+ onPick,
144
146
  urlOf,
145
147
  kindOf = defaultKindOf,
146
148
  kindLabel = DEFAULT_KIND_LABEL,
@@ -149,8 +151,17 @@ export default function ColumnBrowser({
149
151
  className = '',
150
152
  }) {
151
153
  const [picked, setPicked] = useState(null)
154
+ /* `onPick` (ColumnBrowserOnPick, kol-r2b2 2026-08-27): the picked file, for
155
+ * a Finder-style breadcrumb — fired whenever it changes; a folder pick or an
156
+ * outside prefix change fires null */
157
+ const pick = (o) => { setPicked(o); onPick?.(o) }
158
+ useEffect(() => { if (picked) pick(null) }, [prefix]) // eslint-disable-line react-hooks/exhaustive-deps
152
159
  // Keyboard cursor: which column, which row. Clicks keep it in sync.
153
160
  const [cursor, setCursor] = useState({ col: 0, idx: 0 })
161
+ /* the cursor is NOT drawn until the keyboard is used (ColumnBrowserCursorStart,
162
+ * kol-r2b2 2026-08-27): at rest it sat on row 0 beside the open folder and
163
+ * read as a second selection. Arrows arm it; a click seeds it. */
164
+ const [cursorActive, setCursorActive] = useState(false)
154
165
  const rootRef = useRef(null)
155
166
 
156
167
  const levelsOf = (pfx) => {
@@ -167,8 +178,8 @@ export default function ColumnBrowser({
167
178
  }
168
179
  const land = (level, item) => {
169
180
  if (!item) return
170
- if (item.type === 'folder') { setPicked(null); onPrefix(level + item.name) }
171
- else setPicked(item.o)
181
+ if (item.type === 'folder') { pick(null); onPrefix(level + item.name) }
182
+ else pick(item.o)
172
183
  }
173
184
 
174
185
  const onKeyDown = (e) => {
@@ -181,6 +192,7 @@ export default function ColumnBrowser({
181
192
  }
182
193
  if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) return
183
194
  e.preventDefault()
195
+ setCursorActive(true)
184
196
  const lv = levelsOf(prefix)
185
197
  const col = Math.min(cursor.col, lv.length - 1)
186
198
  const items = itemsAt(lv[col])
@@ -201,7 +213,7 @@ export default function ColumnBrowser({
201
213
  const opened = lv[col].slice(lv[col - 1].length)
202
214
  const idx = Math.max(0, parentItems.findIndex((it) => it.type === 'folder' && it.name === opened))
203
215
  setCursor({ col: col - 1, idx })
204
- setPicked(null)
216
+ pick(null)
205
217
  onPrefix(lv[col])
206
218
  }
207
219
  }
@@ -223,7 +235,7 @@ export default function ColumnBrowser({
223
235
  do { idx += step } while (items[idx] && items[idx].type !== 'file')
224
236
  if (!items[idx]) return
225
237
  setCursor({ col, idx })
226
- setPicked(items[idx].o)
238
+ pick(items[idx].o)
227
239
  const siblings = items.filter((it) => it.type === 'file').map((it) => it.o)
228
240
  onQuickLook?.({ files: siblings, index: siblings.findIndex((o) => o.key === items[idx].o.key) })
229
241
  }
@@ -260,9 +272,9 @@ export default function ColumnBrowser({
260
272
  icon="folder"
261
273
  label={f.replace(/\/$/, '')}
262
274
  active={f === activeFolder}
263
- cursor={cursor.col === k && cursor.idx === i}
275
+ cursor={cursorActive && cursor.col === k && cursor.idx === i}
264
276
  trailing={<Icon name="chevron-right" size={12} className="text-fg-32" />}
265
- onClick={() => { setCursor({ col: k, idx: i }); rootRef.current?.focus(); setPicked(null); onPrefix(level + f) }}
277
+ onClick={() => { setCursor({ col: k, idx: i }); setCursorActive(true); rootRef.current?.focus(); pick(null); onPrefix(level + f) }}
266
278
  />
267
279
  ))}
268
280
  {files.map((o, i) => (
@@ -272,8 +284,8 @@ export default function ColumnBrowser({
272
284
  label={o.displayKey ?? o.key}
273
285
  muted={shown?.key !== o.key}
274
286
  active={shown?.key === o.key}
275
- cursor={cursor.col === k && cursor.idx === folders.length + i}
276
- onClick={() => { setCursor({ col: k, idx: folders.length + i }); rootRef.current?.focus(); setPicked(o) }}
287
+ cursor={cursorActive && cursor.col === k && cursor.idx === folders.length + i}
288
+ onClick={() => { setCursor({ col: k, idx: folders.length + i }); setCursorActive(true); rootRef.current?.focus(); pick(o) }}
277
289
  />
278
290
  ))}
279
291
  {folders.length === 0 && files.length === 0 && (