@lateralus-ai/shipping-ui 2.0.0-dev.22 → 2.0.0-dev.23
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/dist/domain/Filters/FilterDropdown.d.ts +10 -2
- package/dist/domain/Filters/FilterPill.d.ts +13 -3
- package/dist/domain/Filters/index.d.ts +1 -1
- package/dist/index.cjs +17 -17
- package/dist/index.esm.js +1474 -1450
- package/package.json +1 -1
- package/src/domain/Filters/FilterDropdown.tsx +202 -166
- package/src/domain/Filters/FilterPill.tsx +19 -6
- package/src/domain/Filters/index.ts +1 -0
- package/src/stories/canvases/FiltersCanvas.tsx +4 -0
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import * as Popover from "@radix-ui/react-popover";
|
|
8
8
|
import { cn } from "../../utils/cn";
|
|
9
9
|
import { ChevronIcon } from "../../icons/ChevronIcon";
|
|
10
|
-
import { FilterPill } from "./FilterPill";
|
|
10
|
+
import { FilterPill, type FilterPillAppearance } from "./FilterPill";
|
|
11
11
|
|
|
12
12
|
/** One selectable row at a leaf list. */
|
|
13
13
|
export type FilterOption = {
|
|
@@ -45,7 +45,7 @@ export type FilterNestedItem = {
|
|
|
45
45
|
content: FilterSubmenuContent;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
/** Left
|
|
48
|
+
/** Left-column row that opens a (possibly nested) submenu panel. */
|
|
49
49
|
export type FilterCategorySubmenuRow = {
|
|
50
50
|
kind?: "submenu";
|
|
51
51
|
id: string;
|
|
@@ -192,14 +192,18 @@ export type FilterDropdownProps = {
|
|
|
192
192
|
zIndexClass?: string;
|
|
193
193
|
/** Replace the default FilterPill trigger. */
|
|
194
194
|
trigger?: ReactNode;
|
|
195
|
+
/** Passed to the default FilterPill when `trigger` is omitted. */
|
|
196
|
+
triggerAppearance?: FilterPillAppearance;
|
|
197
|
+
/**
|
|
198
|
+
* Which side the options panel attaches to the category column.
|
|
199
|
+
* Carets stay on the right of category rows either way.
|
|
200
|
+
*/
|
|
201
|
+
submenuSide?: "left" | "right";
|
|
195
202
|
open?: boolean;
|
|
196
203
|
onOpenChange?: (open: boolean) => void;
|
|
197
204
|
className?: string;
|
|
198
205
|
};
|
|
199
206
|
|
|
200
|
-
const submenuPanelClass =
|
|
201
|
-
"bg-background-primary rounded-lg shadow-raise1 border border-divider-primary border-l-0 rounded-l-none min-w-[200px] max-w-[min(28rem,calc(100vw-2rem))] max-h-[min(28rem,calc(100dvh-8rem))] flex flex-col overflow-hidden";
|
|
202
|
-
|
|
203
207
|
const scrollClass = "py-2 overflow-y-auto overscroll-y-contain flex-1 min-h-0";
|
|
204
208
|
|
|
205
209
|
const rowClass =
|
|
@@ -221,6 +225,8 @@ export function FilterDropdown({
|
|
|
221
225
|
getOptionLabelStyle,
|
|
222
226
|
zIndexClass = "z-50",
|
|
223
227
|
trigger,
|
|
228
|
+
triggerAppearance = "filled",
|
|
229
|
+
submenuSide = "right",
|
|
224
230
|
open: openControlled,
|
|
225
231
|
onOpenChange,
|
|
226
232
|
className,
|
|
@@ -240,10 +246,185 @@ export function FilterDropdown({
|
|
|
240
246
|
? resolveSubmenuView(activeSubmenuRow.content, navPath)
|
|
241
247
|
: null;
|
|
242
248
|
|
|
243
|
-
const
|
|
249
|
+
const showSubmenuPanel = Boolean(activeSubmenuRow && view);
|
|
250
|
+
const submenuOpensLeft = submenuSide === "left";
|
|
244
251
|
|
|
245
252
|
const resetNav = () => setNavPath([]);
|
|
246
253
|
|
|
254
|
+
const submenuPanelClass = cn(
|
|
255
|
+
"bg-background-primary max-h-[min(28rem,calc(100dvh-8rem))] min-w-[200px] max-w-[min(28rem,calc(100vw-2rem))] flex flex-col overflow-hidden border border-divider-primary shadow-raise1",
|
|
256
|
+
submenuOpensLeft
|
|
257
|
+
? "rounded-l-lg rounded-r-none border-r-0"
|
|
258
|
+
: "rounded-r-lg rounded-l-none border-l-0",
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
const categoryPanelClass = cn(
|
|
262
|
+
"min-w-[180px] border border-divider-primary bg-background-primary shadow-raise1",
|
|
263
|
+
showSubmenuPanel
|
|
264
|
+
? submenuOpensLeft
|
|
265
|
+
? "rounded-r-lg rounded-l-none"
|
|
266
|
+
: "rounded-l-lg rounded-r-none"
|
|
267
|
+
: "rounded-lg",
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const categoryPanel = (
|
|
271
|
+
<div className={categoryPanelClass}>
|
|
272
|
+
<div className="py-2">
|
|
273
|
+
{categoryRows.map((row) =>
|
|
274
|
+
isToggleRow(row) ? (
|
|
275
|
+
<button
|
|
276
|
+
key={row.id}
|
|
277
|
+
type="button"
|
|
278
|
+
onClick={() => row.onCheckedChange(!row.checked)}
|
|
279
|
+
className={cn(rowClass, row.checked && "bg-grey-50")}
|
|
280
|
+
>
|
|
281
|
+
<span className="text-body text-display-on-light-primary">
|
|
282
|
+
{row.label}
|
|
283
|
+
</span>
|
|
284
|
+
<CheckSlot selected={row.checked} />
|
|
285
|
+
</button>
|
|
286
|
+
) : (
|
|
287
|
+
<button
|
|
288
|
+
key={row.id}
|
|
289
|
+
type="button"
|
|
290
|
+
onClick={() => {
|
|
291
|
+
if (activeSubmenuId === row.id) {
|
|
292
|
+
setActiveSubmenuId(null);
|
|
293
|
+
resetNav();
|
|
294
|
+
} else {
|
|
295
|
+
setActiveSubmenuId(row.id);
|
|
296
|
+
resetNav();
|
|
297
|
+
}
|
|
298
|
+
}}
|
|
299
|
+
className={cn(
|
|
300
|
+
rowClass,
|
|
301
|
+
activeSubmenuId === row.id && "bg-grey-50",
|
|
302
|
+
)}
|
|
303
|
+
>
|
|
304
|
+
<span className="text-body text-display-on-light-primary">
|
|
305
|
+
{row.label}
|
|
306
|
+
</span>
|
|
307
|
+
<ChevronIcon
|
|
308
|
+
direction="right"
|
|
309
|
+
size="small"
|
|
310
|
+
className={cn(
|
|
311
|
+
"text-grey-400 transition-transform",
|
|
312
|
+
activeSubmenuId === row.id && "rotate-90",
|
|
313
|
+
)}
|
|
314
|
+
/>
|
|
315
|
+
</button>
|
|
316
|
+
),
|
|
317
|
+
)}
|
|
318
|
+
|
|
319
|
+
<div className="mt-2 border-t border-divider-primary pt-2">
|
|
320
|
+
<button
|
|
321
|
+
type="button"
|
|
322
|
+
onClick={onResetAll}
|
|
323
|
+
className={cn(rowClass)}
|
|
324
|
+
>
|
|
325
|
+
<span className="text-body text-display-on-light-primary">
|
|
326
|
+
{resetAllLabel}
|
|
327
|
+
</span>
|
|
328
|
+
</button>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
const submenuPanel =
|
|
335
|
+
showSubmenuPanel && view ? (
|
|
336
|
+
<div className={cn(submenuPanelClass, "flex flex-col")}>
|
|
337
|
+
{navPath.length > 0 && (
|
|
338
|
+
<div className="flex-shrink-0 border-b border-grey-100 px-2 py-1.5">
|
|
339
|
+
<button
|
|
340
|
+
type="button"
|
|
341
|
+
onClick={() => setNavPath((p) => p.slice(0, -1))}
|
|
342
|
+
className="flex w-full items-center gap-1 rounded-md px-2 py-1 text-left text-caption-2-em text-display-on-light-secondary hover:bg-grey-50"
|
|
343
|
+
>
|
|
344
|
+
<ChevronIcon
|
|
345
|
+
direction="left"
|
|
346
|
+
size="small"
|
|
347
|
+
className="shrink-0"
|
|
348
|
+
/>
|
|
349
|
+
Back
|
|
350
|
+
</button>
|
|
351
|
+
</div>
|
|
352
|
+
)}
|
|
353
|
+
{view.trail.length > 0 && (
|
|
354
|
+
<div className="flex-shrink-0 truncate px-4 pb-1 pt-2 text-footnote text-grey-500">
|
|
355
|
+
{view.trail.join(" › ")}
|
|
356
|
+
</div>
|
|
357
|
+
)}
|
|
358
|
+
<div className={scrollClass}>
|
|
359
|
+
{view.kind === "folders" ? (
|
|
360
|
+
view.items.length === 0 ? (
|
|
361
|
+
<p className="px-4 py-2 text-body text-grey-500">No options</p>
|
|
362
|
+
) : (
|
|
363
|
+
view.items.map((item) => (
|
|
364
|
+
<button
|
|
365
|
+
key={item.id}
|
|
366
|
+
type="button"
|
|
367
|
+
onClick={() => setNavPath((p) => [...p, item.id])}
|
|
368
|
+
className={rowClass}
|
|
369
|
+
>
|
|
370
|
+
<span className="text-body text-display-on-light-primary">
|
|
371
|
+
{item.label}
|
|
372
|
+
</span>
|
|
373
|
+
<ChevronIcon
|
|
374
|
+
direction="right"
|
|
375
|
+
size="small"
|
|
376
|
+
className="shrink-0 text-grey-400"
|
|
377
|
+
/>
|
|
378
|
+
</button>
|
|
379
|
+
))
|
|
380
|
+
)
|
|
381
|
+
) : view.kind === "custom" ? (
|
|
382
|
+
<div className="p-4">{view.render()}</div>
|
|
383
|
+
) : view.options.length === 0 ? (
|
|
384
|
+
<p className="px-4 py-2 text-body text-grey-500">No options</p>
|
|
385
|
+
) : (
|
|
386
|
+
view.options.map((option) => {
|
|
387
|
+
const selected =
|
|
388
|
+
selectedValuesBySelectionKey[view.selectionKey]?.includes(
|
|
389
|
+
option.value,
|
|
390
|
+
) ?? false;
|
|
391
|
+
const Icon = option.icon;
|
|
392
|
+
const labelStyle = getOptionLabelStyle?.(
|
|
393
|
+
view.selectionKey,
|
|
394
|
+
option.value,
|
|
395
|
+
);
|
|
396
|
+
return (
|
|
397
|
+
<button
|
|
398
|
+
key={option.value}
|
|
399
|
+
type="button"
|
|
400
|
+
onClick={() =>
|
|
401
|
+
onSelectOption(view.selectionKey, option.value)
|
|
402
|
+
}
|
|
403
|
+
className={cn(rowClass, selected && "bg-grey-100")}
|
|
404
|
+
>
|
|
405
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
406
|
+
{Icon && <Icon className="h-4 w-4 shrink-0" />}
|
|
407
|
+
<span
|
|
408
|
+
className={cn(
|
|
409
|
+
"text-body",
|
|
410
|
+
labelStyle
|
|
411
|
+
? undefined
|
|
412
|
+
: "text-display-on-light-primary",
|
|
413
|
+
)}
|
|
414
|
+
style={labelStyle}
|
|
415
|
+
>
|
|
416
|
+
{option.label}
|
|
417
|
+
</span>
|
|
418
|
+
</div>
|
|
419
|
+
<CheckSlot selected={selected} />
|
|
420
|
+
</button>
|
|
421
|
+
);
|
|
422
|
+
})
|
|
423
|
+
)}
|
|
424
|
+
</div>
|
|
425
|
+
</div>
|
|
426
|
+
) : null;
|
|
427
|
+
|
|
247
428
|
return (
|
|
248
429
|
<Popover.Root
|
|
249
430
|
open={open}
|
|
@@ -257,7 +438,11 @@ export function FilterDropdown({
|
|
|
257
438
|
>
|
|
258
439
|
<Popover.Trigger asChild>
|
|
259
440
|
{trigger ?? (
|
|
260
|
-
<FilterPill
|
|
441
|
+
<FilterPill
|
|
442
|
+
activeFilterCount={activeFilterCount}
|
|
443
|
+
appearance={triggerAppearance}
|
|
444
|
+
className={className}
|
|
445
|
+
/>
|
|
261
446
|
)}
|
|
262
447
|
</Popover.Trigger>
|
|
263
448
|
<Popover.Portal>
|
|
@@ -270,165 +455,16 @@ export function FilterDropdown({
|
|
|
270
455
|
)}
|
|
271
456
|
>
|
|
272
457
|
<div className="flex items-start gap-0">
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
{
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
<span className="text-body text-display-on-light-primary">
|
|
284
|
-
{row.label}
|
|
285
|
-
</span>
|
|
286
|
-
<CheckSlot selected={row.checked} />
|
|
287
|
-
</button>
|
|
288
|
-
) : (
|
|
289
|
-
<button
|
|
290
|
-
key={row.id}
|
|
291
|
-
type="button"
|
|
292
|
-
onClick={() => {
|
|
293
|
-
if (activeSubmenuId === row.id) {
|
|
294
|
-
setActiveSubmenuId(null);
|
|
295
|
-
resetNav();
|
|
296
|
-
} else {
|
|
297
|
-
setActiveSubmenuId(row.id);
|
|
298
|
-
resetNav();
|
|
299
|
-
}
|
|
300
|
-
}}
|
|
301
|
-
className={cn(
|
|
302
|
-
rowClass,
|
|
303
|
-
activeSubmenuId === row.id && "bg-grey-50",
|
|
304
|
-
)}
|
|
305
|
-
>
|
|
306
|
-
<span className="text-body text-display-on-light-primary">
|
|
307
|
-
{row.label}
|
|
308
|
-
</span>
|
|
309
|
-
<ChevronIcon
|
|
310
|
-
direction="right"
|
|
311
|
-
size="small"
|
|
312
|
-
className={cn(
|
|
313
|
-
"text-grey-400 transition-transform",
|
|
314
|
-
activeSubmenuId === row.id && "rotate-90",
|
|
315
|
-
)}
|
|
316
|
-
/>
|
|
317
|
-
</button>
|
|
318
|
-
),
|
|
319
|
-
)}
|
|
320
|
-
|
|
321
|
-
<div className="mt-2 border-t border-divider-primary pt-2">
|
|
322
|
-
<button
|
|
323
|
-
type="button"
|
|
324
|
-
onClick={onResetAll}
|
|
325
|
-
className={cn(rowClass)}
|
|
326
|
-
>
|
|
327
|
-
<span className="text-body text-display-on-light-primary">
|
|
328
|
-
{resetAllLabel}
|
|
329
|
-
</span>
|
|
330
|
-
</button>
|
|
331
|
-
</div>
|
|
332
|
-
</div>
|
|
333
|
-
</div>
|
|
334
|
-
|
|
335
|
-
{showRightPanel && view && (
|
|
336
|
-
<div className={cn(submenuPanelClass, "flex flex-col")}>
|
|
337
|
-
{navPath.length > 0 && (
|
|
338
|
-
<div className="flex-shrink-0 border-b border-grey-100 px-2 py-1.5">
|
|
339
|
-
<button
|
|
340
|
-
type="button"
|
|
341
|
-
onClick={() => setNavPath((p) => p.slice(0, -1))}
|
|
342
|
-
className="flex w-full items-center gap-1 rounded-md px-2 py-1 text-left text-caption-2-em text-display-on-light-secondary hover:bg-grey-50"
|
|
343
|
-
>
|
|
344
|
-
<ChevronIcon
|
|
345
|
-
direction="left"
|
|
346
|
-
size="small"
|
|
347
|
-
className="shrink-0"
|
|
348
|
-
/>
|
|
349
|
-
Back
|
|
350
|
-
</button>
|
|
351
|
-
</div>
|
|
352
|
-
)}
|
|
353
|
-
{view.trail.length > 0 && (
|
|
354
|
-
<div className="flex-shrink-0 truncate px-4 pb-1 pt-2 text-footnote text-grey-500">
|
|
355
|
-
{view.trail.join(" › ")}
|
|
356
|
-
</div>
|
|
357
|
-
)}
|
|
358
|
-
<div className={scrollClass}>
|
|
359
|
-
{view.kind === "folders" ? (
|
|
360
|
-
view.items.length === 0 ? (
|
|
361
|
-
<p className="px-4 py-2 text-body text-grey-500">
|
|
362
|
-
No options
|
|
363
|
-
</p>
|
|
364
|
-
) : (
|
|
365
|
-
view.items.map((item) => (
|
|
366
|
-
<button
|
|
367
|
-
key={item.id}
|
|
368
|
-
type="button"
|
|
369
|
-
onClick={() => setNavPath((p) => [...p, item.id])}
|
|
370
|
-
className={rowClass}
|
|
371
|
-
>
|
|
372
|
-
<span className="text-body text-display-on-light-primary">
|
|
373
|
-
{item.label}
|
|
374
|
-
</span>
|
|
375
|
-
<ChevronIcon
|
|
376
|
-
direction="right"
|
|
377
|
-
size="small"
|
|
378
|
-
className="shrink-0 text-grey-400"
|
|
379
|
-
/>
|
|
380
|
-
</button>
|
|
381
|
-
))
|
|
382
|
-
)
|
|
383
|
-
) : view.kind === "custom" ? (
|
|
384
|
-
<div className="p-4">{view.render()}</div>
|
|
385
|
-
) : view.options.length === 0 ? (
|
|
386
|
-
<p className="px-4 py-2 text-body text-grey-500">
|
|
387
|
-
No options
|
|
388
|
-
</p>
|
|
389
|
-
) : (
|
|
390
|
-
view.options.map((option) => {
|
|
391
|
-
const selected =
|
|
392
|
-
selectedValuesBySelectionKey[
|
|
393
|
-
view.selectionKey
|
|
394
|
-
]?.includes(option.value) ?? false;
|
|
395
|
-
const Icon = option.icon;
|
|
396
|
-
const labelStyle = getOptionLabelStyle?.(
|
|
397
|
-
view.selectionKey,
|
|
398
|
-
option.value,
|
|
399
|
-
);
|
|
400
|
-
return (
|
|
401
|
-
<button
|
|
402
|
-
key={option.value}
|
|
403
|
-
type="button"
|
|
404
|
-
onClick={() =>
|
|
405
|
-
onSelectOption(view.selectionKey, option.value)
|
|
406
|
-
}
|
|
407
|
-
className={cn(rowClass, selected && "bg-grey-100")}
|
|
408
|
-
>
|
|
409
|
-
<div className="flex min-w-0 items-center gap-2">
|
|
410
|
-
{Icon && (
|
|
411
|
-
<Icon className="h-4 w-4 shrink-0" />
|
|
412
|
-
)}
|
|
413
|
-
<span
|
|
414
|
-
className={cn(
|
|
415
|
-
"text-body",
|
|
416
|
-
labelStyle
|
|
417
|
-
? undefined
|
|
418
|
-
: "text-display-on-light-primary",
|
|
419
|
-
)}
|
|
420
|
-
style={labelStyle}
|
|
421
|
-
>
|
|
422
|
-
{option.label}
|
|
423
|
-
</span>
|
|
424
|
-
</div>
|
|
425
|
-
<CheckSlot selected={selected} />
|
|
426
|
-
</button>
|
|
427
|
-
);
|
|
428
|
-
})
|
|
429
|
-
)}
|
|
430
|
-
</div>
|
|
431
|
-
</div>
|
|
458
|
+
{submenuOpensLeft ? (
|
|
459
|
+
<>
|
|
460
|
+
{submenuPanel}
|
|
461
|
+
{categoryPanel}
|
|
462
|
+
</>
|
|
463
|
+
) : (
|
|
464
|
+
<>
|
|
465
|
+
{categoryPanel}
|
|
466
|
+
{submenuPanel}
|
|
467
|
+
</>
|
|
432
468
|
)}
|
|
433
469
|
</div>
|
|
434
470
|
</Popover.Content>
|
|
@@ -3,22 +3,29 @@ import { cn } from "../../utils/cn";
|
|
|
3
3
|
import { FiltersIcon } from "../../icons/generated";
|
|
4
4
|
import { Badge } from "../../primitives/Badge";
|
|
5
5
|
|
|
6
|
+
export type FilterPillAppearance = "filled" | "ghost";
|
|
7
|
+
|
|
6
8
|
export type FilterPillProps = Omit<
|
|
7
9
|
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
8
10
|
"children"
|
|
9
11
|
> & {
|
|
10
|
-
/** When > 0,
|
|
12
|
+
/** When > 0, shows the orange count badge. */
|
|
11
13
|
activeFilterCount?: number;
|
|
14
|
+
/**
|
|
15
|
+
* `filled` — grey idle / blue when active (Figma default).
|
|
16
|
+
* `ghost` — white idle and active; only hover changes surface color.
|
|
17
|
+
*/
|
|
18
|
+
appearance?: FilterPillAppearance;
|
|
12
19
|
};
|
|
13
20
|
|
|
14
21
|
/**
|
|
15
22
|
* Filter trigger — Figma Filter Pill (6154:149608 / 6154:149599).
|
|
16
|
-
* Idle: grey-100. Active: accent blue-100 + orange count badge.
|
|
17
23
|
*/
|
|
18
24
|
export const FilterPill = forwardRef<HTMLButtonElement, FilterPillProps>(
|
|
19
25
|
(
|
|
20
26
|
{
|
|
21
27
|
activeFilterCount = 0,
|
|
28
|
+
appearance = "filled",
|
|
22
29
|
className,
|
|
23
30
|
type = "button",
|
|
24
31
|
"aria-label": ariaLabel = "Filters",
|
|
@@ -27,6 +34,7 @@ export const FilterPill = forwardRef<HTMLButtonElement, FilterPillProps>(
|
|
|
27
34
|
ref,
|
|
28
35
|
) => {
|
|
29
36
|
const active = activeFilterCount > 0;
|
|
37
|
+
const ghost = appearance === "ghost";
|
|
30
38
|
|
|
31
39
|
return (
|
|
32
40
|
<button
|
|
@@ -36,9 +44,11 @@ export const FilterPill = forwardRef<HTMLButtonElement, FilterPillProps>(
|
|
|
36
44
|
aria-pressed={active}
|
|
37
45
|
className={cn(
|
|
38
46
|
"relative flex size-9 shrink-0 items-center justify-center rounded-full transition-colors",
|
|
39
|
-
|
|
40
|
-
? "bg-
|
|
41
|
-
:
|
|
47
|
+
ghost
|
|
48
|
+
? "bg-white text-display-on-light-secondary hover:bg-grey-50 hover:text-display-on-light-primary"
|
|
49
|
+
: active
|
|
50
|
+
? "bg-accent-bg-light text-display-on-light-secondary hover:text-display-on-light-primary"
|
|
51
|
+
: "bg-background-secondary text-display-on-light-secondary hover:text-display-on-light-primary",
|
|
42
52
|
className,
|
|
43
53
|
)}
|
|
44
54
|
{...props}
|
|
@@ -48,7 +58,10 @@ export const FilterPill = forwardRef<HTMLButtonElement, FilterPillProps>(
|
|
|
48
58
|
<Badge
|
|
49
59
|
color="orange"
|
|
50
60
|
type="icon"
|
|
51
|
-
className=
|
|
61
|
+
className={cn(
|
|
62
|
+
"absolute -right-1 -top-2 border-2 text-display-on-light-primary",
|
|
63
|
+
ghost ? "border-white" : "border-background-primary",
|
|
64
|
+
)}
|
|
52
65
|
>
|
|
53
66
|
{activeFilterCount > 99 ? "99+" : activeFilterCount}
|
|
54
67
|
</Badge>
|
|
@@ -109,6 +109,8 @@ export const FiltersCanvas = () => {
|
|
|
109
109
|
<FigmaGrid gap={16}>
|
|
110
110
|
<FilterPill />
|
|
111
111
|
<FilterPill activeFilterCount={2} />
|
|
112
|
+
<FilterPill appearance="ghost" />
|
|
113
|
+
<FilterPill appearance="ghost" activeFilterCount={2} />
|
|
112
114
|
</FigmaGrid>
|
|
113
115
|
</FigmaSection>
|
|
114
116
|
|
|
@@ -125,6 +127,8 @@ export const FiltersCanvas = () => {
|
|
|
125
127
|
<div className="flex shrink-0 items-center gap-4">
|
|
126
128
|
<FilterDropdown
|
|
127
129
|
align="end"
|
|
130
|
+
submenuSide="left"
|
|
131
|
+
triggerAppearance="ghost"
|
|
128
132
|
categoryRows={categoryRows}
|
|
129
133
|
selectedValuesBySelectionKey={selectedValuesBySelectionKey}
|
|
130
134
|
activeFilterCount={activeFilterCount}
|