@kolkrabbi/kol-component 0.96.0 → 0.97.1
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.97.1",
|
|
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",
|
|
@@ -25,10 +25,10 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
25
25
|
* author strings in their final case at the call site.
|
|
26
26
|
*
|
|
27
27
|
* @param {ReactNode} title header heading (kol-helper-16)
|
|
28
|
-
* @param {string} icon Icon name for the header (16px)
|
|
29
|
-
* @param {string|ReactNode} visual image URL, `.svg` mask URL, or inline node
|
|
28
|
+
* @param {string} icon Icon name for the header (16px)
|
|
29
|
+
* @param {string|ReactNode} visual image URL, `.svg` mask URL, or inline node; NONE = a text-only tile (title + description, min-h 180, the .feature-card frame/hover)
|
|
30
30
|
* @param {ReactNode} description footer line (kol-mono-12, muted)
|
|
31
|
-
* @param {string} backgroundColor card background utility class
|
|
31
|
+
* @param {string} backgroundColor card background utility class (default: bg-surface-primary with a visual, transparent text-only)
|
|
32
32
|
* @param {string} href link target; `http*`/`mailto` → new tab, else plain same-tab anchor
|
|
33
33
|
* @param {Function} onNavigate (event) => void — click seam on the same-tab anchor (SPA intercept)
|
|
34
34
|
* @param {'auto'|'9/6'|'10/6'|'16/9'|'1/1'} imageAspectRatio aspect class on the visual middle
|
|
@@ -39,7 +39,7 @@ export default function SectionCardItem({
|
|
|
39
39
|
icon,
|
|
40
40
|
visual,
|
|
41
41
|
description,
|
|
42
|
-
backgroundColor
|
|
42
|
+
backgroundColor,
|
|
43
43
|
href,
|
|
44
44
|
onNavigate,
|
|
45
45
|
imageAspectRatio = 'auto',
|
|
@@ -48,6 +48,11 @@ export default function SectionCardItem({
|
|
|
48
48
|
style,
|
|
49
49
|
}) {
|
|
50
50
|
const isSvgUrl = typeof visual === 'string' && visual.endsWith('.svg')
|
|
51
|
+
/* TEXT-ONLY (FoundrySpecimenSections, 2026-08-27): no `visual` = a title +
|
|
52
|
+
* description tile — the frame and hover of kol-website's .feature-card, no
|
|
53
|
+
* 96px icon fallback. Hover only, never a persistent selected state. */
|
|
54
|
+
const textOnly = visual == null
|
|
55
|
+
const bg = backgroundColor ?? (textOnly ? 'bg-transparent' : 'bg-surface-primary')
|
|
51
56
|
|
|
52
57
|
const aspectClasses = {
|
|
53
58
|
'auto': '',
|
|
@@ -58,7 +63,15 @@ export default function SectionCardItem({
|
|
|
58
63
|
}
|
|
59
64
|
const aspectClass = aspectClasses[imageAspectRatio] || ''
|
|
60
65
|
|
|
61
|
-
const content = (
|
|
66
|
+
const content = textOnly ? (
|
|
67
|
+
<>
|
|
68
|
+
<div className="w-full flex items-start justify-between gap-2">
|
|
69
|
+
<h3 className="kol-helper-16">{title}</h3>
|
|
70
|
+
{icon && <Icon name={icon} size={16} className="shrink-0" />}
|
|
71
|
+
</div>
|
|
72
|
+
<p className="kol-mono-12 text-fg-48">{description}</p>
|
|
73
|
+
</>
|
|
74
|
+
) : (
|
|
62
75
|
<>
|
|
63
76
|
<div className="w-full flex items-center justify-between gap-2">
|
|
64
77
|
<h3 className="kol-helper-16">{title}</h3>
|
|
@@ -106,7 +119,9 @@ export default function SectionCardItem({
|
|
|
106
119
|
</>
|
|
107
120
|
)
|
|
108
121
|
|
|
109
|
-
const baseClasses =
|
|
122
|
+
const baseClasses = textOnly
|
|
123
|
+
? `kol-card-feature kol-card-feature--text w-full flex-1 min-h-[180px] p-4 md:p-5 lg:p-6 gap-2 ${bg} rounded border border-fg-08 flex flex-col justify-between items-start overflow-hidden ${className}`
|
|
124
|
+
: `kol-card-feature w-full flex-1 h-[304px] md:h-72 p-4 md:p-6 gap-4 ${bg} rounded border border-fg-08 flex flex-col justify-between items-start overflow-hidden ${className}`.trim()
|
|
110
125
|
|
|
111
126
|
if (href) {
|
|
112
127
|
const isExternal = href.startsWith('http') || href.startsWith('mailto')
|
|
@@ -151,6 +151,10 @@ export default function ColumnBrowser({
|
|
|
151
151
|
const [picked, setPicked] = useState(null)
|
|
152
152
|
// Keyboard cursor: which column, which row. Clicks keep it in sync.
|
|
153
153
|
const [cursor, setCursor] = useState({ col: 0, idx: 0 })
|
|
154
|
+
/* the cursor is NOT drawn until the keyboard is used (ColumnBrowserCursorStart,
|
|
155
|
+
* kol-r2b2 2026-08-27): at rest it sat on row 0 beside the open folder and
|
|
156
|
+
* read as a second selection. Arrows arm it; a click seeds it. */
|
|
157
|
+
const [cursorActive, setCursorActive] = useState(false)
|
|
154
158
|
const rootRef = useRef(null)
|
|
155
159
|
|
|
156
160
|
const levelsOf = (pfx) => {
|
|
@@ -181,6 +185,7 @@ export default function ColumnBrowser({
|
|
|
181
185
|
}
|
|
182
186
|
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) return
|
|
183
187
|
e.preventDefault()
|
|
188
|
+
setCursorActive(true)
|
|
184
189
|
const lv = levelsOf(prefix)
|
|
185
190
|
const col = Math.min(cursor.col, lv.length - 1)
|
|
186
191
|
const items = itemsAt(lv[col])
|
|
@@ -260,9 +265,9 @@ export default function ColumnBrowser({
|
|
|
260
265
|
icon="folder"
|
|
261
266
|
label={f.replace(/\/$/, '')}
|
|
262
267
|
active={f === activeFolder}
|
|
263
|
-
cursor={cursor.col === k && cursor.idx === i}
|
|
268
|
+
cursor={cursorActive && cursor.col === k && cursor.idx === i}
|
|
264
269
|
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) }}
|
|
270
|
+
onClick={() => { setCursor({ col: k, idx: i }); setCursorActive(true); rootRef.current?.focus(); setPicked(null); onPrefix(level + f) }}
|
|
266
271
|
/>
|
|
267
272
|
))}
|
|
268
273
|
{files.map((o, i) => (
|
|
@@ -272,8 +277,8 @@ export default function ColumnBrowser({
|
|
|
272
277
|
label={o.displayKey ?? o.key}
|
|
273
278
|
muted={shown?.key !== o.key}
|
|
274
279
|
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) }}
|
|
280
|
+
cursor={cursorActive && cursor.col === k && cursor.idx === folders.length + i}
|
|
281
|
+
onClick={() => { setCursor({ col: k, idx: folders.length + i }); setCursorActive(true); rootRef.current?.focus(); setPicked(o) }}
|
|
277
282
|
/>
|
|
278
283
|
))}
|
|
279
284
|
{folders.length === 0 && files.length === 0 && (
|
|
@@ -295,7 +295,8 @@ const ContentFilters = ({
|
|
|
295
295
|
* nothing, so it must not wear a button's chrome. The atom exists
|
|
296
296
|
* for exactly this (lobby ruling 2026-07-30 — "icons only, NO
|
|
297
297
|
* states"); the span here was the same defect that promoted it. */}
|
|
298
|
-
|
|
298
|
+
{/* the icon gap matches the specimen header — 16 from md (FoundrySpecimenSections, 2026-08-27) */}
|
|
299
|
+
<h2 className="flex items-center gap-2 md:gap-4">
|
|
299
300
|
{titleIcon && <IconFrame name={titleIcon} variant="secondary" size="md" />}
|
|
300
301
|
{/* `pr-4` by rule (ContentFiltersTitleGap, kol-website 2026-08-27): the
|
|
301
302
|
* divider sat 24px from the title's text edge but 32 from the
|