@kolkrabbi/kol-shell 0.1.3 → 0.2.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 +1 -1
- package/src/ContentFilters.jsx +45 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "KOL application shell — fixed 48px NavRail + AppShell layout root, PageShell/PageHeader scaffolds, ContentFilters catalog organism, GridCard, SettingsScaffold, WalkthroughPanel, ShortcutsOverlay. App chrome (kol-framework owns site chrome). Nav items, content, shortcuts and settings are consumer-injected. Sits above @kolkrabbi/kol-{theme,component,framework}.",
|
|
6
6
|
"license": "MIT",
|
package/src/ContentFilters.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useMemo, useRef, useEffect } from 'react'
|
|
1
|
+
import { Fragment, useState, useMemo, useRef, useEffect } from 'react'
|
|
2
2
|
import { Divider } from '@kolkrabbi/kol-component'
|
|
3
3
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
4
4
|
import TabStrip from './TabStrip.jsx'
|
|
@@ -27,6 +27,22 @@ import TabStrip from './TabStrip.jsx'
|
|
|
27
27
|
* @param {Array} props.mutuallyExclusiveFilters - filter keys that self-clear
|
|
28
28
|
* @param {Array} props.customFilterKeys - keys renderItem handles, not this organism
|
|
29
29
|
* @param {ElementType} props.iconComponent - icon seam (defaults to DS Icon; needs `filter` + `search`)
|
|
30
|
+
* @param {Function} props.renderFilterValue - `(value, isActive, toggle) => node`, one filter value
|
|
31
|
+
* @param {string} props.labelClassName - REPLACES the group label's class (never stacks)
|
|
32
|
+
*
|
|
33
|
+
* **The two rendering seams (0.2.0).** How a filter value and a group label look
|
|
34
|
+
* is the consumer's call, not this organism's. Three publishes in one day went
|
|
35
|
+
* into re-ruling that here — outlined pills vs bare strip items vs chips — none
|
|
36
|
+
* of which is a design-system question: it is what a given app's filter bar
|
|
37
|
+
* should look like. `renderFilterValue` and `labelClassName` end that class of
|
|
38
|
+
* change without a publish.
|
|
39
|
+
*
|
|
40
|
+
* `labelClassName` REPLACES rather than stacks, per the 2026-07-30 law: two
|
|
41
|
+
* equal-specificity type classes on one element are decided by sheet order, so
|
|
42
|
+
* a stacked seam produces different renders in different consumers with no
|
|
43
|
+
* version difference. Same contract as ListingCard's `titleClassName`.
|
|
44
|
+
*
|
|
45
|
+
* Defaults reproduce 0.1.3 exactly — a bump moves nothing.
|
|
30
46
|
*/
|
|
31
47
|
const ContentFilters = ({
|
|
32
48
|
items,
|
|
@@ -47,6 +63,8 @@ const ContentFilters = ({
|
|
|
47
63
|
headerActions,
|
|
48
64
|
showCountOnlyWhenFiltering = false,
|
|
49
65
|
iconComponent,
|
|
66
|
+
renderFilterValue,
|
|
67
|
+
labelClassName,
|
|
50
68
|
}) => {
|
|
51
69
|
const [activeFilters, setActiveFilters] = useState(new Set())
|
|
52
70
|
const [isExpanded, setIsExpanded] = useState(false)
|
|
@@ -132,12 +150,10 @@ const ContentFilters = ({
|
|
|
132
150
|
})
|
|
133
151
|
}, [items, activeFilters, customFilterKeys, searchText, searchKeys])
|
|
134
152
|
|
|
135
|
-
// A group is a COLUMN: label on top, values beneath it
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
// family ("LIST GRID text has style, that style also on TAGS and any other
|
|
140
|
-
// filter category"). Rest ink, not active: the label is not interactive.
|
|
153
|
+
// A group is a COLUMN: label on top, values beneath it (user ruling
|
|
154
|
+
// 2026-08-15). How a value and a label RENDER is the consumer's — three
|
|
155
|
+
// publishes in one day went into re-deciding that here, which is the defect
|
|
156
|
+
// the seams below end. Defaults are 0.1.3 exactly, so nothing moves on bump.
|
|
141
157
|
const renderFilterGroup = (group) => {
|
|
142
158
|
const prefix = `${group.key}:`
|
|
143
159
|
const selected = new Set(
|
|
@@ -148,17 +164,30 @@ const ContentFilters = ({
|
|
|
148
164
|
|
|
149
165
|
return (
|
|
150
166
|
<div key={group.key} className="flex flex-col gap-3">
|
|
151
|
-
<h4
|
|
167
|
+
<h4
|
|
168
|
+
className={labelClassName || 'kol-helper-12 text-fg-32'}
|
|
169
|
+
style={labelClassName ? undefined : { letterSpacing: 1 }}
|
|
170
|
+
>
|
|
152
171
|
{group.label}
|
|
153
172
|
</h4>
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
173
|
+
{renderFilterValue ? (
|
|
174
|
+
<div className="flex flex-wrap items-center gap-4">
|
|
175
|
+
{group.values.map((v) => (
|
|
176
|
+
<Fragment key={v}>
|
|
177
|
+
{renderFilterValue(v, selected.has(v), () => toggleFilter(group.key, v))}
|
|
178
|
+
</Fragment>
|
|
179
|
+
))}
|
|
180
|
+
</div>
|
|
181
|
+
) : (
|
|
182
|
+
<TabStrip
|
|
183
|
+
options={group.values.map((v) => ({ value: v, label: v }))}
|
|
184
|
+
value={selected}
|
|
185
|
+
onChange={(v) => toggleFilter(group.key, v)}
|
|
186
|
+
size={12}
|
|
187
|
+
tracked
|
|
188
|
+
className="gap-4 flex-wrap"
|
|
189
|
+
/>
|
|
190
|
+
)}
|
|
162
191
|
</div>
|
|
163
192
|
)
|
|
164
193
|
}
|