@bearmenu/ui 0.8.52 → 0.8.54
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/{chunk-4WW3HPIW.js → chunk-6Z6MBILC.js} +8 -4
- package/dist/chunk-GIJGVFGC.js +177 -0
- package/dist/chunk-RKNPPYRU.js +86 -0
- package/dist/{chunk-2ADCZXZJ.js → chunk-RXXD3UYP.js} +3 -1
- package/dist/components/alert.d.ts +2 -2
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +2 -2
- package/dist/components/combobox.js +1 -1
- package/dist/components/event-detail-panel.d.ts +17 -6
- package/dist/components/event-detail-panel.js +58 -8
- package/dist/components/facet-group.d.ts +9 -2
- package/dist/components/facet-group.js +108 -10
- package/dist/components/filter-option-chip.d.ts +15 -0
- package/dist/components/filter-option-chip.js +3 -0
- package/dist/components/filter-option-group.d.ts +37 -0
- package/dist/components/filter-option-group.js +10 -0
- package/dist/components/filter-panel-footer.d.ts +15 -0
- package/dist/components/filter-panel-footer.js +63 -0
- package/dist/components/menu-item.d.ts +24 -3
- package/dist/components/menu-item.js +1 -1
- package/dist/components/multi-select-combobox.js +1 -1
- package/dist/components/place-card.js +1 -1
- package/dist/components/popover.js +1 -1
- package/dist/components/searchable-select.js +1 -1
- package/dist/components/spinner.d.ts +2 -2
- package/dist/components/tag.d.ts +2 -2
- package/dist/components/text.d.ts +1 -1
- package/dist/components/toggle.d.ts +1 -1
- package/dist/components/top-ranking-entry.js +1 -1
- package/package.json +11 -8
- package/src/components/event-detail-panel.stories.tsx +16 -0
- package/src/components/event-detail-panel.test.tsx +36 -0
- package/src/components/event-detail-panel.tsx +86 -14
- package/src/components/facet-group.tsx +153 -54
- package/src/components/filter-option-chip.test.tsx +50 -0
- package/src/components/filter-option-chip.tsx +81 -0
- package/src/components/filter-option-group.stories.tsx +250 -0
- package/src/components/filter-option-group.test.tsx +100 -0
- package/src/components/filter-option-group.tsx +231 -0
- package/src/components/filter-panel-footer.tsx +63 -0
- package/src/components/menu-item.stories.tsx +82 -0
- package/src/components/menu-item.test.tsx +55 -1
- package/src/components/menu-item.tsx +31 -4
- package/src/components/popover.tsx +3 -1
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import { FilterOptionGroup } from '../chunk-GIJGVFGC.js';
|
|
2
|
+
import '../chunk-RKNPPYRU.js';
|
|
3
|
+
import '../chunk-NWZGSLPU.js';
|
|
1
4
|
import { Skeleton } from '../chunk-XBBEX4SF.js';
|
|
5
|
+
import { Button } from '../chunk-GHKTVHQG.js';
|
|
6
|
+
import '../chunk-RIH2IS7X.js';
|
|
7
|
+
import '../chunk-5YNIQ2BL.js';
|
|
8
|
+
import '../chunk-ZLMRVBD7.js';
|
|
9
|
+
import '../chunk-RYPPN2GZ.js';
|
|
2
10
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
3
11
|
import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
|
|
4
12
|
import { useId, useState } from 'react';
|
|
5
13
|
import { ChevronDown, Check } from 'lucide-react';
|
|
6
|
-
import {
|
|
14
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
15
|
|
|
8
16
|
var ROW_SIZE = {
|
|
9
17
|
comfortable: "-mx-2 min-h-11 rounded-lg px-2",
|
|
@@ -21,6 +29,10 @@ function FacetGroup(_a) {
|
|
|
21
29
|
selection = "multiple",
|
|
22
30
|
name,
|
|
23
31
|
size = "comfortable",
|
|
32
|
+
layout = "rows",
|
|
33
|
+
visibleLimit,
|
|
34
|
+
showAllLabel,
|
|
35
|
+
showLessLabel,
|
|
24
36
|
collapsible = false,
|
|
25
37
|
defaultOpen = true,
|
|
26
38
|
onToggle,
|
|
@@ -35,6 +47,10 @@ function FacetGroup(_a) {
|
|
|
35
47
|
"selection",
|
|
36
48
|
"name",
|
|
37
49
|
"size",
|
|
50
|
+
"layout",
|
|
51
|
+
"visibleLimit",
|
|
52
|
+
"showAllLabel",
|
|
53
|
+
"showLessLabel",
|
|
38
54
|
"collapsible",
|
|
39
55
|
"defaultOpen",
|
|
40
56
|
"onToggle",
|
|
@@ -50,6 +66,48 @@ function FacetGroup(_a) {
|
|
|
50
66
|
const setOpen = setIsOpen;
|
|
51
67
|
const fallbackName = useId();
|
|
52
68
|
const groupName = name != null ? name : fallbackName;
|
|
69
|
+
if (layout === "chips") {
|
|
70
|
+
const value = options.filter((option) => option.checked).map((option) => option.id);
|
|
71
|
+
return /* @__PURE__ */ jsx(
|
|
72
|
+
FilterOptionGroup,
|
|
73
|
+
{
|
|
74
|
+
title,
|
|
75
|
+
options: options.map((option) => __spreadProps(__spreadValues({}, option), { value: option.id })),
|
|
76
|
+
value,
|
|
77
|
+
selection,
|
|
78
|
+
name: groupName,
|
|
79
|
+
defaultOpen,
|
|
80
|
+
collapsible,
|
|
81
|
+
divider: false,
|
|
82
|
+
visibleLimit,
|
|
83
|
+
showAllLabel,
|
|
84
|
+
showLessLabel,
|
|
85
|
+
disabled: rest.disabled,
|
|
86
|
+
className,
|
|
87
|
+
onValueChange: (next) => {
|
|
88
|
+
if (selection === "single") {
|
|
89
|
+
if (next[0] != null) onToggle == null ? void 0 : onToggle(String(next[0]), true);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
for (const option of options) {
|
|
93
|
+
const checked = next.includes(option.id);
|
|
94
|
+
if (checked !== !!option.checked) onToggle == null ? void 0 : onToggle(option.id, checked);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
footer: moreLabel && /* @__PURE__ */ jsx(
|
|
98
|
+
Button,
|
|
99
|
+
{
|
|
100
|
+
type: "button",
|
|
101
|
+
variant: "link",
|
|
102
|
+
className: "min-h-11 px-0",
|
|
103
|
+
onClick: onMore,
|
|
104
|
+
"aria-expanded": moreExpanded,
|
|
105
|
+
children: moreLabel
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
}
|
|
53
111
|
return (
|
|
54
112
|
// NO SEPARATOR OF ITS OWN. This drew `<Separator className="absolute
|
|
55
113
|
// inset-x-0 top-0" />`, which looked like a rule above each group and was
|
|
@@ -98,7 +156,14 @@ function FacetGroup(_a) {
|
|
|
98
156
|
open ? "mb-[11px]" : "mb-0"
|
|
99
157
|
),
|
|
100
158
|
children: [
|
|
101
|
-
Icon && /* @__PURE__ */ jsx(
|
|
159
|
+
Icon && /* @__PURE__ */ jsx(
|
|
160
|
+
Icon,
|
|
161
|
+
{
|
|
162
|
+
"aria-hidden": true,
|
|
163
|
+
className: "size-3.5 shrink-0",
|
|
164
|
+
strokeWidth: 2.25
|
|
165
|
+
}
|
|
166
|
+
),
|
|
102
167
|
title,
|
|
103
168
|
/* @__PURE__ */ jsx(
|
|
104
169
|
ChevronDown,
|
|
@@ -114,7 +179,14 @@ function FacetGroup(_a) {
|
|
|
114
179
|
}
|
|
115
180
|
)
|
|
116
181
|
) : /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
117
|
-
Icon && /* @__PURE__ */ jsx(
|
|
182
|
+
Icon && /* @__PURE__ */ jsx(
|
|
183
|
+
Icon,
|
|
184
|
+
{
|
|
185
|
+
"aria-hidden": true,
|
|
186
|
+
className: "size-3.5 shrink-0",
|
|
187
|
+
strokeWidth: 2.25
|
|
188
|
+
}
|
|
189
|
+
),
|
|
118
190
|
title
|
|
119
191
|
] })
|
|
120
192
|
}
|
|
@@ -164,7 +236,13 @@ function FacetGroup(_a) {
|
|
|
164
236
|
"peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2",
|
|
165
237
|
checked ? "border-primary bg-primary" : "border-border bg-background"
|
|
166
238
|
),
|
|
167
|
-
children: checked && (isSingle ? /* @__PURE__ */ jsx("span", { className: "size-[7px] rounded-full bg-primary-foreground" }) : /* @__PURE__ */ jsx(
|
|
239
|
+
children: checked && (isSingle ? /* @__PURE__ */ jsx("span", { className: "size-[7px] rounded-full bg-primary-foreground" }) : /* @__PURE__ */ jsx(
|
|
240
|
+
Check,
|
|
241
|
+
{
|
|
242
|
+
className: "h-[11px] w-[11px] text-primary-foreground",
|
|
243
|
+
strokeWidth: 3
|
|
244
|
+
}
|
|
245
|
+
))
|
|
168
246
|
}
|
|
169
247
|
),
|
|
170
248
|
/* @__PURE__ */ jsx(
|
|
@@ -205,18 +283,38 @@ function FacetGroupSkeleton({
|
|
|
205
283
|
optionCount = 4,
|
|
206
284
|
showMore,
|
|
207
285
|
size = "comfortable",
|
|
208
|
-
className
|
|
286
|
+
className,
|
|
287
|
+
layout = "rows"
|
|
209
288
|
}) {
|
|
289
|
+
if (layout === "chips")
|
|
290
|
+
return /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: cn("py-3", className), children: [
|
|
291
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mb-4 h-4 w-24" }),
|
|
292
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: Array.from({ length: optionCount }, (_, index) => /* @__PURE__ */ jsx(
|
|
293
|
+
Skeleton,
|
|
294
|
+
{
|
|
295
|
+
className: cn("h-11 rounded-2xl", index % 2 ? "w-28" : "w-24")
|
|
296
|
+
},
|
|
297
|
+
index
|
|
298
|
+
)) }),
|
|
299
|
+
showMore && /* @__PURE__ */ jsx(Skeleton, { className: "mt-3 h-4 w-20" })
|
|
300
|
+
] });
|
|
210
301
|
return (
|
|
211
302
|
// Matches the live group: no rule of its own, `py-3`, and the row gap keyed
|
|
212
303
|
// to the same density — or the panel shifts on load by the difference.
|
|
213
304
|
/* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: cn("py-3", className), children: [
|
|
214
305
|
/* @__PURE__ */ jsx(Skeleton, { className: "mb-[11px] h-3 w-20" }),
|
|
215
|
-
/* @__PURE__ */ jsx("div", { className: cn("flex flex-col", ROW_GAP[size]), children: Array.from({ length: optionCount }).map((_, index) => /* @__PURE__ */ jsxs(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
306
|
+
/* @__PURE__ */ jsx("div", { className: cn("flex flex-col", ROW_GAP[size]), children: Array.from({ length: optionCount }).map((_, index) => /* @__PURE__ */ jsxs(
|
|
307
|
+
"div",
|
|
308
|
+
{
|
|
309
|
+
className: cn("flex items-center gap-[9px]", ROW_SIZE[size]),
|
|
310
|
+
children: [
|
|
311
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-4 rounded-[5px]" }),
|
|
312
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-24" }),
|
|
313
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "ml-auto h-3 w-5" })
|
|
314
|
+
]
|
|
315
|
+
},
|
|
316
|
+
index
|
|
317
|
+
)) }),
|
|
220
318
|
showMore && /* @__PURE__ */ jsx(Skeleton, { className: "mt-[11px] h-3 w-16" })
|
|
221
319
|
] })
|
|
222
320
|
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type FilterOptionChipProps = Omit<ComponentPropsWithoutRef<"input">, "type" | "children" | "size"> & {
|
|
5
|
+
label: string;
|
|
6
|
+
/** Radios in one group must share a name. */
|
|
7
|
+
type?: "checkbox" | "radio";
|
|
8
|
+
/** Omit while counts are unresolved. Zero is a valid, selectable count. */
|
|
9
|
+
count?: number | null;
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
/** A wrapping filter option with native selection and keyboard semantics. */
|
|
13
|
+
declare function FilterOptionChip({ label, type, count, icon, className, id, disabled, ...input }: FilterOptionChipProps): react_jsx_runtime.JSX.Element;
|
|
14
|
+
|
|
15
|
+
export { FilterOptionChip, type FilterOptionChipProps };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type FilterOptionValue = string | number;
|
|
5
|
+
type FilterOptionGroupProps = {
|
|
6
|
+
title: string;
|
|
7
|
+
options: {
|
|
8
|
+
value: FilterOptionValue;
|
|
9
|
+
label: string;
|
|
10
|
+
count?: number | null;
|
|
11
|
+
icon?: ReactNode;
|
|
12
|
+
section?: string;
|
|
13
|
+
}[];
|
|
14
|
+
value: FilterOptionValue[];
|
|
15
|
+
onValueChange: (value: FilterOptionValue[]) => void;
|
|
16
|
+
selection?: "multiple" | "single";
|
|
17
|
+
name?: string;
|
|
18
|
+
defaultOpen?: boolean;
|
|
19
|
+
collapsible?: boolean;
|
|
20
|
+
visibleLimit?: number;
|
|
21
|
+
showAllLabel?: string | ((count: number) => string);
|
|
22
|
+
showLessLabel?: string;
|
|
23
|
+
/** Extra controls or a selection explanation, before the options. */
|
|
24
|
+
children?: ReactNode;
|
|
25
|
+
footer?: ReactNode;
|
|
26
|
+
clearLabel?: string;
|
|
27
|
+
onClear?: () => void;
|
|
28
|
+
/** Omit neutral choices from the active summary without changing radio selection. */
|
|
29
|
+
summaryValues?: FilterOptionValue[];
|
|
30
|
+
className?: string;
|
|
31
|
+
divider?: boolean;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
};
|
|
34
|
+
/** Presentation only: callers own filter rules, URL state, counts and translations. */
|
|
35
|
+
declare function FilterOptionGroup({ title, options, value, onValueChange, selection, name, defaultOpen, collapsible, visibleLimit, showAllLabel, showLessLabel, children, footer, clearLabel, onClear, summaryValues, className, divider, disabled, }: FilterOptionGroupProps): react_jsx_runtime.JSX.Element;
|
|
36
|
+
|
|
37
|
+
export { FilterOptionGroup, type FilterOptionGroupProps, type FilterOptionValue };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { FilterOptionGroup } from '../chunk-GIJGVFGC.js';
|
|
2
|
+
import '../chunk-RKNPPYRU.js';
|
|
3
|
+
import '../chunk-NWZGSLPU.js';
|
|
4
|
+
import '../chunk-GHKTVHQG.js';
|
|
5
|
+
import '../chunk-RIH2IS7X.js';
|
|
6
|
+
import '../chunk-5YNIQ2BL.js';
|
|
7
|
+
import '../chunk-ZLMRVBD7.js';
|
|
8
|
+
import '../chunk-RYPPN2GZ.js';
|
|
9
|
+
import '../chunk-FEK5XGZW.js';
|
|
10
|
+
import '../chunk-MMUBH76A.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type FilterPanelFooterProps = {
|
|
4
|
+
resetLabel: string;
|
|
5
|
+
resultsLabel: string;
|
|
6
|
+
onReset: () => void;
|
|
7
|
+
onResults: () => void;
|
|
8
|
+
canReset: boolean;
|
|
9
|
+
pending?: boolean;
|
|
10
|
+
resetHint?: string;
|
|
11
|
+
};
|
|
12
|
+
/** Persistent actions belong outside the panel's scroll container. */
|
|
13
|
+
declare function FilterPanelFooter({ resetLabel, resultsLabel, onReset, onResults, canReset, pending, resetHint, }: FilterPanelFooterProps): react_jsx_runtime.JSX.Element;
|
|
14
|
+
|
|
15
|
+
export { FilterPanelFooter, type FilterPanelFooterProps };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Button } from '../chunk-GHKTVHQG.js';
|
|
2
|
+
import '../chunk-RIH2IS7X.js';
|
|
3
|
+
import '../chunk-5YNIQ2BL.js';
|
|
4
|
+
import { Text } from '../chunk-ZLMRVBD7.js';
|
|
5
|
+
import '../chunk-FEK5XGZW.js';
|
|
6
|
+
import '../chunk-MMUBH76A.js';
|
|
7
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
function FilterPanelFooter({
|
|
10
|
+
resetLabel,
|
|
11
|
+
resultsLabel,
|
|
12
|
+
onReset,
|
|
13
|
+
onResults,
|
|
14
|
+
canReset,
|
|
15
|
+
pending,
|
|
16
|
+
resetHint
|
|
17
|
+
}) {
|
|
18
|
+
return /* @__PURE__ */ jsxs(
|
|
19
|
+
"footer",
|
|
20
|
+
{
|
|
21
|
+
className: "shrink-0 border-t border-border bg-card px-4 pt-3 pb-[max(1rem,env(safe-area-inset-bottom))]",
|
|
22
|
+
"data-slot": "filter-panel-footer",
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
25
|
+
/* @__PURE__ */ jsx(
|
|
26
|
+
Button,
|
|
27
|
+
{
|
|
28
|
+
type: "button",
|
|
29
|
+
variant: "outline",
|
|
30
|
+
className: "min-h-11 h-auto flex-1 whitespace-normal py-2",
|
|
31
|
+
disabled: !canReset,
|
|
32
|
+
onClick: onReset,
|
|
33
|
+
children: resetLabel
|
|
34
|
+
}
|
|
35
|
+
),
|
|
36
|
+
/* @__PURE__ */ jsx(
|
|
37
|
+
Button,
|
|
38
|
+
{
|
|
39
|
+
type: "button",
|
|
40
|
+
className: "min-h-11 h-auto flex-[2] whitespace-normal py-2",
|
|
41
|
+
disabled: pending,
|
|
42
|
+
"aria-busy": pending || void 0,
|
|
43
|
+
onClick: onResults,
|
|
44
|
+
children: resultsLabel
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
] }),
|
|
48
|
+
resetHint && /* @__PURE__ */ jsx(
|
|
49
|
+
Text,
|
|
50
|
+
{
|
|
51
|
+
as: "p",
|
|
52
|
+
variant: "caption",
|
|
53
|
+
color: "muted",
|
|
54
|
+
className: "mt-2 leading-relaxed",
|
|
55
|
+
children: resetHint
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { FilterPanelFooter };
|
|
@@ -64,6 +64,18 @@ type MenuItemLabels = {
|
|
|
64
64
|
*/
|
|
65
65
|
externalRating?: (score: string) => string;
|
|
66
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* What the count beside the row counts.
|
|
69
|
+
*
|
|
70
|
+
* - `"mentions"`: every review that named the item, complaints included. Drawn
|
|
71
|
+
* with the quote glyph.
|
|
72
|
+
* - `"praise"`: only the reviews that praised it. Drawn with a thumbs-up.
|
|
73
|
+
*
|
|
74
|
+
* The caller decides, because only the caller knows which number it passed in
|
|
75
|
+
* `item.mentions`. A thumbs-up must never sit on a count that includes
|
|
76
|
+
* complaints, so `"mentions"` is the default.
|
|
77
|
+
*/
|
|
78
|
+
type MenuItemCountKind = "praise" | "mentions";
|
|
67
79
|
type MenuItemProps = {
|
|
68
80
|
item: MenuItemPreview;
|
|
69
81
|
labels: MenuItemLabels;
|
|
@@ -85,6 +97,12 @@ type MenuItemProps = {
|
|
|
85
97
|
onSignatureEvidence?: () => void;
|
|
86
98
|
/** Opens the reviews behind the mention count. */
|
|
87
99
|
onMentions?: () => void;
|
|
100
|
+
/**
|
|
101
|
+
* Whether the count is praise or every mention. Sets the glyph before the
|
|
102
|
+
* numeral: a thumbs-up for `"praise"`, the quote glyph for `"mentions"`.
|
|
103
|
+
* Defaults to `"mentions"`. Keep `labels.mentions` in step with it.
|
|
104
|
+
*/
|
|
105
|
+
countKind?: MenuItemCountKind;
|
|
88
106
|
/**
|
|
89
107
|
* An outside aggregate score for the item (a wine's Vivino rating, a beer's
|
|
90
108
|
* Untappd one), shown as a star and the score beside the mention count.
|
|
@@ -128,17 +146,20 @@ type MenuItemProps = {
|
|
|
128
146
|
className?: string;
|
|
129
147
|
};
|
|
130
148
|
/**
|
|
131
|
-
* The mention count: a
|
|
149
|
+
* The mention count: a glyph and the numeral, pinned right. The glyph is the
|
|
150
|
+
* quote glyph for every mention, or a thumbs-up when the count is praise only
|
|
151
|
+
* (see `MenuItemCountKind`).
|
|
132
152
|
*
|
|
133
153
|
* Deliberately NOT a badge — a chip would add a filled shape to a row that has
|
|
134
154
|
* none, and would read as a status label the venue was given rather than a
|
|
135
155
|
* count drawn from the same reviews.
|
|
136
156
|
*/
|
|
137
|
-
declare function MentionSignal({ label, count, onMentions, formatCount, }: {
|
|
157
|
+
declare function MentionSignal({ label, count, onMentions, formatCount, countKind, }: {
|
|
138
158
|
label: string;
|
|
139
159
|
count: number;
|
|
140
160
|
onMentions?: () => void;
|
|
141
161
|
formatCount?: (value: number) => string;
|
|
162
|
+
countKind?: MenuItemCountKind;
|
|
142
163
|
}): react_jsx_runtime.JSX.Element;
|
|
143
164
|
/**
|
|
144
165
|
* An external aggregate score: a star and the numeral, in the neutral muted
|
|
@@ -152,4 +173,4 @@ declare function ExternalRatingSignal({ label, value }: {
|
|
|
152
173
|
}): react_jsx_runtime.JSX.Element;
|
|
153
174
|
declare const MenuItem: React.NamedExoticComponent<MenuItemProps>;
|
|
154
175
|
|
|
155
|
-
export { ExternalRatingSignal, MentionSignal, MenuItem, type MenuItemLabels, type MenuItemLinkProps, type MenuItemProps };
|
|
176
|
+
export { ExternalRatingSignal, MentionSignal, MenuItem, type MenuItemCountKind, type MenuItemLabels, type MenuItemLinkProps, type MenuItemProps };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ExternalRatingSignal, MentionSignal, MenuItem } from '../chunk-
|
|
1
|
+
export { ExternalRatingSignal, MentionSignal, MenuItem } from '../chunk-6Z6MBILC.js';
|
|
2
2
|
import '../chunk-6PQINSAA.js';
|
|
3
3
|
import '../chunk-RYPPN2GZ.js';
|
|
4
4
|
import '../chunk-FEK5XGZW.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Checkbox } from '../chunk-L3CDXVEP.js';
|
|
2
|
-
import { Popover, PopoverTrigger, PopoverContent } from '../chunk-
|
|
2
|
+
import { Popover, PopoverTrigger, PopoverContent } from '../chunk-RXXD3UYP.js';
|
|
3
3
|
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from '../chunk-PGQXVJXW.js';
|
|
4
4
|
import '../chunk-IQIJCMG2.js';
|
|
5
5
|
import { Button } from '../chunk-GHKTVHQG.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { HOVER_IMAGE_MOTION, HOVER_IMAGE_WRAPPER, HOVER_MOTION, HOVER_LIFT_GRID_GROUP } from '../chunk-XCB3F6SF.js';
|
|
1
2
|
import { NEUTRAL_ICON_FILTER } from '../chunk-LQWGZIYA.js';
|
|
2
3
|
import { OVERLAY_PILL } from '../chunk-EZG2NELX.js';
|
|
3
|
-
import { HOVER_IMAGE_MOTION, HOVER_IMAGE_WRAPPER, HOVER_MOTION, HOVER_LIFT_GRID_GROUP } from '../chunk-XCB3F6SF.js';
|
|
4
4
|
import { GradeAtom } from '../chunk-YE4ZQUTI.js';
|
|
5
5
|
import { showsGradeAtom } from '../chunk-I5V446BX.js';
|
|
6
6
|
import { Separator } from '../chunk-NWZGSLPU.js';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from '../chunk-
|
|
1
|
+
export { Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from '../chunk-RXXD3UYP.js';
|
|
2
2
|
import '../chunk-FEK5XGZW.js';
|
|
3
3
|
import '../chunk-MMUBH76A.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Popover, PopoverTrigger, PopoverContent } from '../chunk-
|
|
1
|
+
import { Popover, PopoverTrigger, PopoverContent } from '../chunk-RXXD3UYP.js';
|
|
2
2
|
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from '../chunk-PGQXVJXW.js';
|
|
3
3
|
import '../chunk-IQIJCMG2.js';
|
|
4
4
|
import { Button } from '../chunk-GHKTVHQG.js';
|
|
@@ -4,11 +4,11 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
4
4
|
|
|
5
5
|
declare const spinnerVariants: (props?: ({
|
|
6
6
|
size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
|
|
7
|
-
tone?: "primary" | "
|
|
7
|
+
tone?: "primary" | "inherit" | "default" | "destructive" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
declare const Spinner: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
|
|
10
10
|
size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
|
|
11
|
-
tone?: "primary" | "
|
|
11
|
+
tone?: "primary" | "inherit" | "default" | "destructive" | null | undefined;
|
|
12
12
|
} & class_variance_authority_types.ClassProp) | undefined) => string> & {
|
|
13
13
|
/** Visually-hidden label for screen readers. Defaults to "Loading". */
|
|
14
14
|
label?: string;
|
package/dist/components/tag.d.ts
CHANGED
|
@@ -3,11 +3,11 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const tagVariants: (props?: ({
|
|
6
|
-
variant?: "primary" | "
|
|
6
|
+
variant?: "primary" | "success" | "warning" | "default" | "destructive" | "outline" | "quiet" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
declare const Tag: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
|
|
10
|
-
variant?: "primary" | "
|
|
10
|
+
variant?: "primary" | "success" | "warning" | "default" | "destructive" | "outline" | "quiet" | null | undefined;
|
|
11
11
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
12
12
|
} & class_variance_authority_types.ClassProp) | undefined) => string> & {
|
|
13
13
|
onRemove?: () => void;
|
|
@@ -23,7 +23,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
23
23
|
*/
|
|
24
24
|
declare const textVariants: (props?: ({
|
|
25
25
|
variant?: "small" | "body" | "display" | "h2" | "h3" | "label" | "caption" | "h1" | "h4" | "body-lg" | "body-sm" | "overline" | null | undefined;
|
|
26
|
-
color?: "primary" | "
|
|
26
|
+
color?: "primary" | "inherit" | "default" | "destructive" | "muted" | "primary-foreground" | null | undefined;
|
|
27
27
|
weight?: "bold" | "medium" | "normal" | "semibold" | null | undefined;
|
|
28
28
|
leading?: "none" | "normal" | "tight" | "snug" | "relaxed" | "loose" | null | undefined;
|
|
29
29
|
tracking?: "normal" | "tight" | "wide" | "wider" | null | undefined;
|
|
@@ -11,7 +11,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
11
11
|
* `data-[state=on]:` utilities sort after `hover:` and still win while on.
|
|
12
12
|
*/
|
|
13
13
|
declare const toggleVariants: (props?: ({
|
|
14
|
-
variant?: "
|
|
14
|
+
variant?: "tab" | "default" | "outline" | "chip" | "segmented" | null | undefined;
|
|
15
15
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
16
16
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
17
17
|
/**
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bearmenu/ui",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.54",
|
|
4
4
|
"packageManager": "pnpm@10.28.1",
|
|
5
|
-
"description": "BearMenu design system
|
|
5
|
+
"description": "BearMenu design system \u2014 shared UI tokens, primitives, and components",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -204,7 +204,10 @@
|
|
|
204
204
|
"./lib/icon-registry": "./src/lib/icon-registry.ts",
|
|
205
205
|
"./lib/motion": "./src/lib/motion.ts",
|
|
206
206
|
"./lib/overlay-pill": "./src/lib/overlay-pill.ts",
|
|
207
|
-
"./lib/utils": "./src/lib/utils.ts"
|
|
207
|
+
"./lib/utils": "./src/lib/utils.ts",
|
|
208
|
+
"./components/filter-option-chip": "./src/components/filter-option-chip.tsx",
|
|
209
|
+
"./components/filter-option-group": "./src/components/filter-option-group.tsx",
|
|
210
|
+
"./components/filter-panel-footer": "./src/components/filter-panel-footer.tsx"
|
|
208
211
|
},
|
|
209
212
|
"publishConfig": {
|
|
210
213
|
"exports": {
|
|
@@ -304,10 +307,10 @@
|
|
|
304
307
|
"@radix-ui/react-toggle": "^1.1.10",
|
|
305
308
|
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
306
309
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
307
|
-
"@storybook/addon-a11y": "^10.
|
|
308
|
-
"@storybook/addon-docs": "^10.
|
|
309
|
-
"@storybook/react": "^10.
|
|
310
|
-
"@storybook/react-vite": "^10.
|
|
310
|
+
"@storybook/addon-a11y": "^10.6.5",
|
|
311
|
+
"@storybook/addon-docs": "^10.6.5",
|
|
312
|
+
"@storybook/react": "^10.6.5",
|
|
313
|
+
"@storybook/react-vite": "^10.6.5",
|
|
311
314
|
"@tailwindcss/postcss": "^4.2.4",
|
|
312
315
|
"@tailwindcss/vite": "^4.2.4",
|
|
313
316
|
"@testing-library/jest-dom": "^6.9.1",
|
|
@@ -333,7 +336,7 @@
|
|
|
333
336
|
"react-markdown": "^10.1.0",
|
|
334
337
|
"remark-gfm": "^4.0.1",
|
|
335
338
|
"sonner": "^2.0.7",
|
|
336
|
-
"storybook": "^10.
|
|
339
|
+
"storybook": "^10.6.5",
|
|
337
340
|
"tailwind-merge": "^3.5.0",
|
|
338
341
|
"tailwindcss": "^4.2.4",
|
|
339
342
|
"tsup": "^8.5.0",
|
|
@@ -283,6 +283,22 @@ export const Loading: Story = {
|
|
|
283
283
|
decorators: SideSheet.decorators,
|
|
284
284
|
};
|
|
285
285
|
|
|
286
|
+
export const EntryAndBrowseActions: Story = {
|
|
287
|
+
args: {
|
|
288
|
+
tags: [{ label: "Party", href: "/events?tags=party" }],
|
|
289
|
+
entryDetails: {
|
|
290
|
+
label: "Entry and tickets",
|
|
291
|
+
description: "Check availability with the organizer.",
|
|
292
|
+
action: (
|
|
293
|
+
<Button asChild fullWidth>
|
|
294
|
+
<a href="https://tickets.example.com">Get tickets</a>
|
|
295
|
+
</Button>
|
|
296
|
+
),
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
decorators: SideSheet.decorators,
|
|
300
|
+
};
|
|
301
|
+
|
|
286
302
|
export const Error: Story = {
|
|
287
303
|
render: () => (
|
|
288
304
|
<EventDetailPanelError
|
|
@@ -37,6 +37,42 @@ const event: EventPreview = {
|
|
|
37
37
|
const base = { event, labels, formatters, dateLine: "Tuesday · 17:30" };
|
|
38
38
|
|
|
39
39
|
describe("EventDetailPanel", () => {
|
|
40
|
+
it("opens entry information and exposes a separate booking link", () => {
|
|
41
|
+
render(
|
|
42
|
+
<EventDetailPanel
|
|
43
|
+
{...base}
|
|
44
|
+
entryDetails={{
|
|
45
|
+
label: "Entry and tickets",
|
|
46
|
+
description: "Book with the organizer.",
|
|
47
|
+
action: <a href="https://tickets.example.com">Get tickets</a>,
|
|
48
|
+
}}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
expect(
|
|
52
|
+
screen.queryByRole("link", { name: "Get tickets" }),
|
|
53
|
+
).not.toBeInTheDocument();
|
|
54
|
+
fireEvent.click(screen.getByRole("button", { name: "Entry and tickets" }));
|
|
55
|
+
expect(screen.getByText("Book with the organizer.")).toBeVisible();
|
|
56
|
+
expect(screen.getByRole("link", { name: "Get tickets" })).toHaveAttribute(
|
|
57
|
+
"href",
|
|
58
|
+
"https://tickets.example.com",
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("gives browse tags destinations while keeping descriptive tags out of the tab order", () => {
|
|
63
|
+
render(
|
|
64
|
+
<EventDetailPanel
|
|
65
|
+
{...base}
|
|
66
|
+
tags={["Descriptive", { label: "Party", href: "/events?tags=party" }]}
|
|
67
|
+
/>,
|
|
68
|
+
);
|
|
69
|
+
expect(screen.getByRole("link", { name: "Party" })).toHaveAttribute(
|
|
70
|
+
"href",
|
|
71
|
+
"/events?tags=party",
|
|
72
|
+
);
|
|
73
|
+
expect(screen.getByText("Descriptive").tagName).toBe("SPAN");
|
|
74
|
+
});
|
|
75
|
+
|
|
40
76
|
it("shows the status chip for live, soon and ended, and none when upcoming", () => {
|
|
41
77
|
const { rerender } = render(<EventDetailPanel {...base} />);
|
|
42
78
|
expect(screen.queryByText("Now")).not.toBeInTheDocument();
|