@kolkrabbi/kol-component 0.97.2 → 0.97.3

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.2",
3
+ "version": "0.97.3",
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",
@@ -156,14 +156,6 @@ export default function ColumnBrowser({
156
156
  * outside prefix change fires null */
157
157
  const pick = (o) => { setPicked(o); onPick?.(o) }
158
158
  useEffect(() => { if (picked) pick(null) }, [prefix]) // eslint-disable-line react-hooks/exhaustive-deps
159
- // Keyboard cursor: which column, which row. Clicks keep it in sync.
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)
165
- const rootRef = useRef(null)
166
-
167
159
  const levelsOf = (pfx) => {
168
160
  const out = ['']
169
161
  if (pfx) {
@@ -176,6 +168,27 @@ export default function ColumnBrowser({
176
168
  const { folders, files } = partition(objects.filter((o) => o.key.startsWith(level)), level)
177
169
  return [...folders.map((f) => ({ type: 'folder', name: f })), ...files.map((o) => ({ type: 'file', o }))]
178
170
  }
171
+ // Keyboard cursor: which column, which row. Clicks keep it in sync.
172
+ /* SEEDED on the open folder of the deepest column that has one (Finder's
173
+ * start — ColumnBrowserCursorSeed, kol-r2b2 2026-08-27): with a deep `prefix`
174
+ * the first ↓ used to act in column 0. Re-seeded when `prefix` changes; the
175
+ * internal moves land on the same spot, so nothing jumps. */
176
+ const seedCursor = (pfx) => {
177
+ const lv = levelsOf(pfx)
178
+ if (lv.length < 2) return { col: 0, idx: 0 }
179
+ const col = lv.length - 2
180
+ const opened = lv[col + 1].slice(lv[col].length)
181
+ const idx = itemsAt(lv[col]).findIndex((it) => it.type === 'folder' && it.name === opened)
182
+ return { col, idx: Math.max(0, idx) }
183
+ }
184
+ const [cursor, setCursor] = useState(() => seedCursor(prefix))
185
+ useEffect(() => { setCursor(seedCursor(prefix)) }, [prefix, objects.length]) // eslint-disable-line react-hooks/exhaustive-deps
186
+ /* the cursor is NOT drawn until the keyboard is used (ColumnBrowserCursorStart,
187
+ * kol-r2b2 2026-08-27): at rest it sat on row 0 beside the open folder and
188
+ * read as a second selection. Arrows arm it; a click seeds it. */
189
+ const [cursorActive, setCursorActive] = useState(false)
190
+ const rootRef = useRef(null)
191
+
179
192
  const land = (level, item) => {
180
193
  if (!item) return
181
194
  if (item.type === 'folder') { pick(null); onPrefix(level + item.name) }
@@ -198,6 +211,9 @@ export default function ColumnBrowser({
198
211
  const items = itemsAt(lv[col])
199
212
  if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
200
213
  const idx = Math.max(0, Math.min(items.length - 1, cursor.idx + (e.key === 'ArrowDown' ? 1 : -1)))
214
+ /* an arrow that did not move the cursor does nothing — re-landing on an
215
+ * open folder re-fired onPrefix (ColumnBrowserCursorSeed) */
216
+ if (idx === cursor.idx && col === cursor.col) return
201
217
  setCursor({ col, idx })
202
218
  land(lv[col], items[idx])
203
219
  } else if (e.key === 'ArrowRight') {