@kolkrabbi/kol-component 0.161.0 → 0.163.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.163.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",
|
|
@@ -199,6 +199,13 @@ export default function ContentCard({
|
|
|
199
199
|
) : null
|
|
200
200
|
const r = ratio ?? RATIOS[variant]
|
|
201
201
|
const padding = pad ? `var(--kol-pad-card-${pad})` : box.pad
|
|
202
|
+
/* the inset takes SINGLE values (ContentCardActionsInsetShorthand, kol-monitor
|
|
203
|
+
* 2026-09-02): `catalog`'s pad is the two-value shorthand `sm md`, and a
|
|
204
|
+
* two-value string is invalid for top / right / bottom — all three dropped
|
|
205
|
+
* and the actions landed at their static position, under the copy at the
|
|
206
|
+
* left, instead of bottom-right (the 2026-08-15 ruling). Block from the
|
|
207
|
+
* first value, inline from the second (or the same one). */
|
|
208
|
+
const [padBlock, padInline = padBlock] = String(padding ?? '0').trim().split(/\s+/)
|
|
202
209
|
/* image-only cards (print) pass no text slots — the empty plate must not render */
|
|
203
210
|
const hasText = ['title', 'body', 'kicker', 'detail', 'date', 'size', 'meta', 'tags'].some((k) => textSlots[k] != null)
|
|
204
211
|
/* `actions` sit IN THE TEXT PLATE, bottom-right (user ruling 2026-08-15:
|
|
@@ -233,7 +240,7 @@ export default function ContentCard({
|
|
|
233
240
|
{actions && (
|
|
234
241
|
<div
|
|
235
242
|
className="absolute flex"
|
|
236
|
-
style={{ top:
|
|
243
|
+
style={{ top: padBlock, bottom: padBlock, right: padInline }}
|
|
237
244
|
>
|
|
238
245
|
{actions}
|
|
239
246
|
</div>
|
|
@@ -21,6 +21,10 @@ import IconFrame from '../atoms/IconFrame.jsx'
|
|
|
21
21
|
* @param {Array} props.filterGroups — [{label, key, values}, ...]
|
|
22
22
|
* @param {Function} props.renderItem — (filteredItems, viewMode, layout) => ReactNode
|
|
23
23
|
* @param {Array} props.viewModeOptions — optional view mode options for the view strip
|
|
24
|
+
* @param {'auto'|'header'|'below'} props.viewPlacement — WHERE the RECENT/SAVED strip sits (ContentFiltersViewStripOverflow,
|
|
25
|
+
* kol-mirror 2026-09-02): `auto` (default) rides the header row from `md` and takes its OWN line under the divider
|
|
26
|
+
* below it, full width and wrapping — five views at 390 were 440px in a 390px page, two cut off and unreachable;
|
|
27
|
+
* `header` always the header row; `below` always its own line. Same family, same rung as LIST/GRID's drop.
|
|
24
28
|
* @param {string} props.defaultViewMode — default view mode (falls back to the FIRST option)
|
|
25
29
|
* @param {string} props.layout — controlled LIST/GRID value (kol-r2b2 2026-08-27: a consumer that persists
|
|
26
30
|
* layout per bucket needs the strip's value back; `defaultLayout` alone kept it internal)
|
|
@@ -90,6 +94,7 @@ const ContentFilters = ({
|
|
|
90
94
|
* "N of N" is below the divider in BOTH — it is a count of what the filters
|
|
91
95
|
* did, so it belongs with them, and it only renders while they are open. */
|
|
92
96
|
layoutPlacement = 'below',
|
|
97
|
+
viewPlacement = 'auto',
|
|
93
98
|
onFilterChange,
|
|
94
99
|
mutuallyExclusiveFilters = [],
|
|
95
100
|
customFilterKeys = [],
|
|
@@ -302,6 +307,22 @@ const ContentFilters = ({
|
|
|
302
307
|
)
|
|
303
308
|
}
|
|
304
309
|
|
|
310
|
+
/* RECENT / SAVED — one node, two homes (header from md, its own line below) */
|
|
311
|
+
const viewStrip = (wrapCls) => (
|
|
312
|
+
<div className={`${wrapCls} items-center gap-4`}>
|
|
313
|
+
{viewModeOptions.map((opt) => (
|
|
314
|
+
<span
|
|
315
|
+
key={opt.value}
|
|
316
|
+
onClick={() => handleViewModeChange(opt.value)}
|
|
317
|
+
className={`${viewClassName} cursor-pointer select-none ${viewMode === opt.value ? stripActiveClassName : stripRestClassName}`}
|
|
318
|
+
style={{ textTransform: 'uppercase', letterSpacing: 1 }}
|
|
319
|
+
>
|
|
320
|
+
{opt.label}
|
|
321
|
+
</span>
|
|
322
|
+
))}
|
|
323
|
+
</div>
|
|
324
|
+
)
|
|
325
|
+
|
|
305
326
|
return (
|
|
306
327
|
/* minHeight 0 on both this root and the body below: without it a flex
|
|
307
328
|
* child refuses to shrink past its content, so a scrollable body pushed
|
|
@@ -426,20 +447,11 @@ const ContentFilters = ({
|
|
|
426
447
|
* This REPLACES the filled-chip reading of the 2026-07-28 ruling on
|
|
427
448
|
* this surface — user ruling 2026-08-15: the fork's look, everywhere.
|
|
428
449
|
* One family across the whole row. */}
|
|
429
|
-
{
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
onClick={() => handleViewModeChange(opt.value)}
|
|
435
|
-
className={`${viewClassName} cursor-pointer select-none ${viewMode === opt.value ? stripActiveClassName : stripRestClassName}`}
|
|
436
|
-
style={{ textTransform: 'uppercase', letterSpacing: 1 }}
|
|
437
|
-
>
|
|
438
|
-
{opt.label}
|
|
439
|
-
</span>
|
|
440
|
-
))}
|
|
441
|
-
</div>
|
|
442
|
-
)}
|
|
450
|
+
{/* THE STRIP HAS A NARROW RUNG (ContentFiltersViewStripOverflow,
|
|
451
|
+
* kol-mirror 2026-09-02): in the header it is `hidden md:flex` under
|
|
452
|
+
* `auto`, and the same options render on their own line under the
|
|
453
|
+
* divider below `md` — full width, wrapping. */}
|
|
454
|
+
{viewModeOptions && viewPlacement !== 'below' && viewStrip(viewPlacement === 'auto' ? 'hidden md:flex' : 'flex')}
|
|
443
455
|
</div>
|
|
444
456
|
</div>
|
|
445
457
|
|
|
@@ -447,6 +459,14 @@ const ContentFilters = ({
|
|
|
447
459
|
* dropped, which is how this margin nearly went missing. */}
|
|
448
460
|
<Divider className="mb-3" />
|
|
449
461
|
|
|
462
|
+
{/* the view family's own line below md (`auto`) or always (`below`) —
|
|
463
|
+
* full width, wrapping, so no view is ever off the page */}
|
|
464
|
+
{viewModeOptions && viewPlacement !== 'header' && (
|
|
465
|
+
<div className={`${viewPlacement === 'auto' ? 'md:hidden ' : ''}mb-3`}>
|
|
466
|
+
{viewStrip('flex flex-wrap gap-y-2')}
|
|
467
|
+
</div>
|
|
468
|
+
)}
|
|
469
|
+
|
|
450
470
|
{/* BELOW the divider: the filter GROUPS only. Left-aligned columns —
|
|
451
471
|
* label above values — visible only while the filter toggle is open.
|
|
452
472
|
*
|