minka-ds 0.1.5 → 0.1.7
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
|
@@ -52,7 +52,7 @@ function FilterChip(props: FilterChipProps) {
|
|
|
52
52
|
{values.map((value, i) => (
|
|
53
53
|
<span
|
|
54
54
|
key={i}
|
|
55
|
-
className="inline-flex items-center gap-1 rounded-full border border-[var(--color-border-default)] px-2 py-0.5 text-caption text-[var(--color-text-default)]"
|
|
55
|
+
className="inline-flex items-center gap-1 rounded-full border border-[var(--color-border-default)] bg-[var(--color-bg-base)] px-2 py-0.5 text-caption text-[var(--color-text-default)]"
|
|
56
56
|
>
|
|
57
57
|
{value.label}
|
|
58
58
|
<button
|
|
@@ -62,6 +62,7 @@ function FilterCombobox({
|
|
|
62
62
|
|
|
63
63
|
const containerRef = React.useRef<HTMLDivElement>(null)
|
|
64
64
|
const searchRef = React.useRef<HTMLInputElement>(null)
|
|
65
|
+
const isSingle = categories.length === 1
|
|
65
66
|
|
|
66
67
|
// Close on outside click
|
|
67
68
|
React.useEffect(() => {
|
|
@@ -91,6 +92,18 @@ function FilterCombobox({
|
|
|
91
92
|
setAmountMax("")
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
function handleToggle() {
|
|
96
|
+
if (open) {
|
|
97
|
+
handleClose()
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
// Single-category: skip step 1 and jump straight to options
|
|
101
|
+
if (isSingle) {
|
|
102
|
+
openCategory(categories[0])
|
|
103
|
+
}
|
|
104
|
+
setOpen(true)
|
|
105
|
+
}
|
|
106
|
+
|
|
94
107
|
function openCategory(cat: FilterCategory) {
|
|
95
108
|
const existing = activeFilters[cat.id] ?? []
|
|
96
109
|
|
|
@@ -193,8 +206,8 @@ function FilterCombobox({
|
|
|
193
206
|
|
|
194
207
|
return (
|
|
195
208
|
<div ref={containerRef} className={cn("relative inline-block", className)}>
|
|
196
|
-
{trigger ? trigger({ open, onClick:
|
|
197
|
-
<Button variant="default" size="sm" className="h-7 text-caption gap-1.5 px-2.5" onClick={
|
|
209
|
+
{trigger ? trigger({ open, onClick: handleToggle }) : (
|
|
210
|
+
<Button variant="default" size="sm" className="h-7 text-caption gap-1.5 px-2.5" onClick={handleToggle}>
|
|
198
211
|
<PlusIcon className="size-3.5" />
|
|
199
212
|
Add filter
|
|
200
213
|
</Button>
|
|
@@ -204,11 +217,11 @@ function FilterCombobox({
|
|
|
204
217
|
<div className={cn(
|
|
205
218
|
dropdownAlign === "right" ? "absolute right-0 top-full mt-1.5 overflow-hidden [border-radius:var(--radius-popover)]" : "absolute left-0 top-full mt-1.5 overflow-hidden [border-radius:var(--radius-popover)]",
|
|
206
219
|
"bg-[var(--color-bg-overlay)] shadow-[var(--shadow-popover)] ring-1 ring-[var(--color-border-subtle)]",
|
|
207
|
-
"[z-index:var(--z-
|
|
220
|
+
"[z-index:var(--z-floating)]",
|
|
208
221
|
step === 3 && isDate ? "w-auto" : "w-56"
|
|
209
222
|
)}>
|
|
210
223
|
|
|
211
|
-
{/* Step 1 — category list */}
|
|
224
|
+
{/* Step 1 — category list (multi-category mode only) */}
|
|
212
225
|
{step === 1 && (
|
|
213
226
|
<ul className="p-1">
|
|
214
227
|
{categories.map(cat => (
|
|
@@ -222,10 +235,12 @@ function FilterCombobox({
|
|
|
222
235
|
{/* Step 2 — value list */}
|
|
223
236
|
{step === 2 && selectedCategory && (
|
|
224
237
|
<>
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
238
|
+
{!isSingle && (
|
|
239
|
+
<StepHeader
|
|
240
|
+
title={selectedCategory.label}
|
|
241
|
+
onBack={() => { setStep(1); setSearch("") }}
|
|
242
|
+
/>
|
|
243
|
+
)}
|
|
229
244
|
{showSearch && (
|
|
230
245
|
<div className="px-1 pt-1">
|
|
231
246
|
<SearchInput ref={searchRef} value={search} onChange={setSearch} />
|
|
@@ -259,10 +274,12 @@ function FilterCombobox({
|
|
|
259
274
|
{/* Step 3 — custom date range */}
|
|
260
275
|
{step === 3 && isDate && (
|
|
261
276
|
<>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
277
|
+
{!isSingle && (
|
|
278
|
+
<StepHeader
|
|
279
|
+
title={selectedCategory?.label ?? "Date"}
|
|
280
|
+
onBack={() => setStep(1)}
|
|
281
|
+
/>
|
|
282
|
+
)}
|
|
266
283
|
<div className="p-1">
|
|
267
284
|
<Calendar mode="range" selected={dateRange} onSelect={setDateRange} numberOfMonths={1} />
|
|
268
285
|
</div>
|
|
@@ -276,14 +293,16 @@ function FilterCombobox({
|
|
|
276
293
|
</>
|
|
277
294
|
)}
|
|
278
295
|
|
|
279
|
-
{/* Step 3 — custom amount
|
|
296
|
+
{/* Step 3 — custom amount */}
|
|
280
297
|
{step === 3 && isAmount && (
|
|
281
298
|
<>
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
299
|
+
{!isSingle && (
|
|
300
|
+
<StepHeader
|
|
301
|
+
title="Amount"
|
|
302
|
+
onBack={() => setStep(1)}
|
|
303
|
+
/>
|
|
304
|
+
)}
|
|
305
|
+
<div className="p-2 flex flex-col gap-2">
|
|
287
306
|
<Tabs
|
|
288
307
|
value={amountMode}
|
|
289
308
|
onValueChange={v => setAmountMode(v as "exact" | "range")}
|
|
@@ -52,6 +52,7 @@ interface SearchBarProps {
|
|
|
52
52
|
onRemoveFilter?: (categoryId: string, value: CategoryValue) => void
|
|
53
53
|
onClearFilters?: () => void
|
|
54
54
|
filterValueLabel?: (categoryId: string, value: CategoryValue) => string
|
|
55
|
+
alwaysShowFilterBar?: boolean
|
|
55
56
|
|
|
56
57
|
children?: React.ReactNode
|
|
57
58
|
className?: string
|
|
@@ -73,11 +74,13 @@ function SearchBar({
|
|
|
73
74
|
onRemoveFilter,
|
|
74
75
|
onClearFilters,
|
|
75
76
|
filterValueLabel = defaultFilterValueLabel,
|
|
77
|
+
alwaysShowFilterBar = false,
|
|
76
78
|
children,
|
|
77
79
|
className,
|
|
78
80
|
}: SearchBarProps) {
|
|
79
81
|
const hasActiveFilters = Object.values(activeFilters).some(v => v.length > 0)
|
|
80
82
|
const hasFilterCategories = filterCategories.length > 0
|
|
83
|
+
const showFilterBar = hasActiveFilters || alwaysShowFilterBar
|
|
81
84
|
|
|
82
85
|
return (
|
|
83
86
|
<div data-search-bar className={cn("relative flex flex-col", className)}>
|
|
@@ -86,7 +89,7 @@ function SearchBar({
|
|
|
86
89
|
<InputGroup
|
|
87
90
|
className={cn(
|
|
88
91
|
"h-12",
|
|
89
|
-
|
|
92
|
+
showFilterBar && "[border-bottom-left-radius:0] [border-bottom-right-radius:0]"
|
|
90
93
|
)}
|
|
91
94
|
>
|
|
92
95
|
<InputGroupAddon align="inline-start">
|
|
@@ -101,15 +104,15 @@ function SearchBar({
|
|
|
101
104
|
onFocus={onFocus}
|
|
102
105
|
autoComplete="off"
|
|
103
106
|
/>
|
|
104
|
-
{(!!value || (hasFilterCategories && !hasActiveFilters) || (!value && !!kbdHint)) && (
|
|
107
|
+
{(!!value || (hasFilterCategories && !hasActiveFilters && !alwaysShowFilterBar) || (!value && !!kbdHint)) && (
|
|
105
108
|
<InputGroupAddon align="inline-end">
|
|
106
109
|
{value && (
|
|
107
110
|
<InputGroupButton size="sm" variant="ghost" onClick={() => onChange("")} className="text-[var(--color-text-muted)] hover:text-[var(--color-text-default)]">
|
|
108
111
|
<XIcon className="size-4" />
|
|
109
112
|
</InputGroupButton>
|
|
110
113
|
)}
|
|
111
|
-
{!value && kbdHint && !hasFilterCategories && <Kbd>{kbdHint}</Kbd>}
|
|
112
|
-
{hasFilterCategories && !hasActiveFilters && (
|
|
114
|
+
{!value && kbdHint && (!hasFilterCategories || alwaysShowFilterBar) && <Kbd>{kbdHint}</Kbd>}
|
|
115
|
+
{hasFilterCategories && !hasActiveFilters && !alwaysShowFilterBar && (
|
|
113
116
|
<>
|
|
114
117
|
{!value && kbdHint && <Kbd>{kbdHint}</Kbd>}
|
|
115
118
|
<span className="h-4 w-px bg-[var(--color-border-default)]" />
|
|
@@ -130,41 +133,102 @@ function SearchBar({
|
|
|
130
133
|
)}
|
|
131
134
|
</InputGroup>
|
|
132
135
|
|
|
133
|
-
{/* Filter bar
|
|
134
|
-
{
|
|
136
|
+
{/* Filter bar */}
|
|
137
|
+
{showFilterBar && (
|
|
135
138
|
<div className="flex flex-wrap items-center gap-3 [border-bottom-left-radius:var(--radius-card)] [border-bottom-right-radius:var(--radius-card)] border border-t-0 border-[var(--color-border-default)] bg-[var(--color-bg-raised)] px-3 py-2.5">
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
{alwaysShowFilterBar ? (
|
|
140
|
+
// Always-open mode: one trigger per category, active ones show chips
|
|
141
|
+
<>
|
|
142
|
+
<span className="text-caption text-[var(--color-text-default)] shrink-0">Filters:</span>
|
|
143
|
+
{filterCategories.map(cat => {
|
|
144
|
+
const activeVals = activeFilters[cat.id] ?? []
|
|
145
|
+
const hasActive = activeVals.length > 0
|
|
146
|
+
|
|
147
|
+
if (hasActive) {
|
|
148
|
+
return (
|
|
149
|
+
<FilterCombobox
|
|
150
|
+
key={cat.id}
|
|
151
|
+
categories={[cat]}
|
|
152
|
+
onApply={onApplyFilter ?? (() => {})}
|
|
153
|
+
activeFilters={activeFilters}
|
|
154
|
+
trigger={({ onClick }) => (
|
|
155
|
+
<FilterChip
|
|
156
|
+
label={cat.label}
|
|
157
|
+
values={activeVals.map(v => ({
|
|
158
|
+
label: filterValueLabel(cat.id, v),
|
|
159
|
+
onRemove: () => onRemoveFilter?.(cat.id, v),
|
|
160
|
+
}))}
|
|
161
|
+
onLabelClick={onClick}
|
|
162
|
+
/>
|
|
163
|
+
)}
|
|
164
|
+
/>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<FilterCombobox
|
|
170
|
+
key={cat.id}
|
|
171
|
+
categories={[cat]}
|
|
172
|
+
onApply={onApplyFilter ?? (() => {})}
|
|
173
|
+
activeFilters={activeFilters}
|
|
174
|
+
trigger={({ onClick }) => (
|
|
175
|
+
<button
|
|
176
|
+
type="button"
|
|
177
|
+
onClick={onClick}
|
|
178
|
+
className="inline-flex items-center gap-1 rounded-full border border-[var(--color-border-default)] px-2 py-0.5 text-caption text-[var(--color-text-muted)] hover:text-[var(--color-text-default)] hover:border-[var(--color-border-hover,var(--color-border-default))] transition-colors"
|
|
179
|
+
>
|
|
180
|
+
{cat.label}
|
|
181
|
+
<PlusIcon className="size-3 shrink-0" />
|
|
182
|
+
</button>
|
|
183
|
+
)}
|
|
184
|
+
/>
|
|
185
|
+
)
|
|
186
|
+
})}
|
|
187
|
+
{hasActiveFilters && (
|
|
141
188
|
<FilterChip
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
label: filterValueLabel(categoryId, v),
|
|
146
|
-
onRemove: () => onRemoveFilter?.(categoryId, v),
|
|
147
|
-
}))}
|
|
148
|
-
onLabelClick={() => {}}
|
|
189
|
+
variant="clear-all"
|
|
190
|
+
className="ml-auto"
|
|
191
|
+
onClear={onClearFilters ?? (() => {})}
|
|
149
192
|
/>
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
193
|
+
)}
|
|
194
|
+
</>
|
|
195
|
+
) : (
|
|
196
|
+
// Legacy mode: active chips + generic Add button
|
|
197
|
+
<>
|
|
198
|
+
{Object.entries(activeFilters)
|
|
199
|
+
.filter(([, vals]) => vals.length > 0)
|
|
200
|
+
.map(([categoryId, values]) => {
|
|
201
|
+
const cat = filterCategories.find(c => c.id === categoryId)
|
|
202
|
+
return (
|
|
203
|
+
<FilterChip
|
|
204
|
+
key={categoryId}
|
|
205
|
+
label={cat?.label ?? categoryId}
|
|
206
|
+
values={values.map(v => ({
|
|
207
|
+
label: filterValueLabel(categoryId, v),
|
|
208
|
+
onRemove: () => onRemoveFilter?.(categoryId, v),
|
|
209
|
+
}))}
|
|
210
|
+
onLabelClick={() => {}}
|
|
211
|
+
/>
|
|
212
|
+
)
|
|
213
|
+
})}
|
|
214
|
+
<FilterCombobox
|
|
215
|
+
categories={filterCategories}
|
|
216
|
+
onApply={onApplyFilter ?? (() => {})}
|
|
217
|
+
activeFilters={activeFilters}
|
|
218
|
+
trigger={({ onClick }) => (
|
|
219
|
+
<Button variant="default" size="sm" className="h-7 text-caption gap-1.5 px-2.5" onClick={onClick}>
|
|
220
|
+
<PlusIcon className="size-3.5" />
|
|
221
|
+
Add
|
|
222
|
+
</Button>
|
|
223
|
+
)}
|
|
224
|
+
/>
|
|
225
|
+
<FilterChip
|
|
226
|
+
variant="clear-all"
|
|
227
|
+
className="ml-auto"
|
|
228
|
+
onClear={onClearFilters ?? (() => {})}
|
|
229
|
+
/>
|
|
230
|
+
</>
|
|
231
|
+
)}
|
|
168
232
|
</div>
|
|
169
233
|
)}
|
|
170
234
|
|
package/tokens/primitives.css
CHANGED
|
@@ -22,6 +22,18 @@
|
|
|
22
22
|
|
|
23
23
|
/* --- Color scales --- */
|
|
24
24
|
|
|
25
|
+
--primitive-beige-50: oklch(0.990 0.008 98);
|
|
26
|
+
--primitive-beige-100: oklch(0.982 0.030 98);
|
|
27
|
+
--primitive-beige-200: oklch(0.968 0.052 98);
|
|
28
|
+
--primitive-beige-300: oklch(0.948 0.072 98);
|
|
29
|
+
--primitive-beige-400: oklch(0.918 0.083 98);
|
|
30
|
+
--primitive-beige-500: oklch(0.876 0.085 98);
|
|
31
|
+
--primitive-beige-600: oklch(0.792 0.076 98);
|
|
32
|
+
--primitive-beige-700: oklch(0.680 0.062 98);
|
|
33
|
+
--primitive-beige-800: oklch(0.480 0.044 98);
|
|
34
|
+
--primitive-beige-900: oklch(0.310 0.028 98);
|
|
35
|
+
--primitive-beige-950: oklch(0.230 0.020 98);
|
|
36
|
+
|
|
25
37
|
--primitive-red-50: oklch(0.95 0.03 28);
|
|
26
38
|
--primitive-red-100: oklch(0.93 0.05 28);
|
|
27
39
|
--primitive-red-200: oklch(0.89 0.07 28);
|
|
@@ -58,9 +70,9 @@
|
|
|
58
70
|
--primitive-yellow-900: oklch(0.3 0.07 98);
|
|
59
71
|
--primitive-yellow-950: oklch(0.25 0.06 98);
|
|
60
72
|
|
|
61
|
-
--primitive-lulo-50: oklch(0.
|
|
62
|
-
--primitive-lulo-100: oklch(0.
|
|
63
|
-
--primitive-lulo-200: oklch(0.
|
|
73
|
+
--primitive-lulo-50: oklch(0.967 0.099 118);
|
|
74
|
+
--primitive-lulo-100: oklch(0.918 0.136 116);
|
|
75
|
+
--primitive-lulo-200: oklch(0.869 0.173 114);
|
|
64
76
|
--primitive-lulo-300: oklch(0.82 0.21 112);
|
|
65
77
|
--primitive-lulo-400: oklch(0.76 0.2 112);
|
|
66
78
|
--primitive-lulo-500: oklch(0.7 0.18 112);
|
|
@@ -106,6 +118,18 @@
|
|
|
106
118
|
--primitive-sea-900: oklch(0.29 0.07 220);
|
|
107
119
|
--primitive-sea-950: oklch(0.24 0.06 220);
|
|
108
120
|
|
|
121
|
+
--primitive-slate-50: oklch(0.950 0.012 218);
|
|
122
|
+
--primitive-slate-100: oklch(0.932 0.020 218);
|
|
123
|
+
--primitive-slate-200: oklch(0.898 0.032 218);
|
|
124
|
+
--primitive-slate-300: oklch(0.848 0.042 218);
|
|
125
|
+
--primitive-slate-400: oklch(0.762 0.054 218);
|
|
126
|
+
--primitive-slate-500: oklch(0.656 0.063 218);
|
|
127
|
+
--primitive-slate-600: oklch(0.560 0.054 218);
|
|
128
|
+
--primitive-slate-700: oklch(0.472 0.046 218);
|
|
129
|
+
--primitive-slate-800: oklch(0.352 0.034 218);
|
|
130
|
+
--primitive-slate-900: oklch(0.248 0.023 218);
|
|
131
|
+
--primitive-slate-950: oklch(0.198 0.017 218);
|
|
132
|
+
|
|
109
133
|
--primitive-blue-50: oklch(0.95 0.03 257);
|
|
110
134
|
--primitive-blue-100: oklch(0.93 0.04 257);
|
|
111
135
|
--primitive-blue-200: oklch(0.88 0.06 257);
|