@bearmenu/ui 0.8.49 → 0.8.50

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.
Files changed (55) hide show
  1. package/dist/{chunk-QY66ME5B.js → chunk-CZ2HC7WY.js} +24 -6
  2. package/dist/components/alert.d.ts +2 -2
  3. package/dist/components/avatar.d.ts +2 -2
  4. package/dist/components/badge.d.ts +1 -1
  5. package/dist/components/banner.d.ts +1 -1
  6. package/dist/components/button.d.ts +2 -2
  7. package/dist/components/card.d.ts +2 -2
  8. package/dist/components/command.d.ts +3 -3
  9. package/dist/components/concept-compare.d.ts +1 -1
  10. package/dist/components/doorway-tiles.d.ts +1 -1
  11. package/dist/components/empty-state.d.ts +2 -2
  12. package/dist/components/event-detail-panel.d.ts +12 -2
  13. package/dist/components/event-detail-panel.js +15 -3
  14. package/dist/components/grade-atom.d.ts +1 -1
  15. package/dist/components/item.d.ts +2 -2
  16. package/dist/components/kitchen-dossier.d.ts +1 -1
  17. package/dist/components/menu-dead-end.d.ts +1 -1
  18. package/dist/components/menu-family-block.d.ts +2 -2
  19. package/dist/components/menu-family-block.js +3 -1
  20. package/dist/components/menu-family-card.d.ts +1 -1
  21. package/dist/components/menu-filter-chips.d.ts +1 -1
  22. package/dist/components/menu-group-rows.d.ts +1 -1
  23. package/dist/components/menu-index-header.d.ts +1 -1
  24. package/dist/components/menu-index-list.d.ts +1 -1
  25. package/dist/components/menu-intent-hero.d.ts +1 -1
  26. package/dist/components/menu-item.d.ts +1 -1
  27. package/dist/components/menu-kitchen-list.d.ts +9 -2
  28. package/dist/components/menu-kitchen-list.js +3 -1
  29. package/dist/components/menu-mocks.d.ts +1 -1
  30. package/dist/components/menu-types.d.ts +1 -1
  31. package/dist/components/open-now-list.d.ts +1 -1
  32. package/dist/components/place-about-tab.d.ts +1 -1
  33. package/dist/components/place-card.d.ts +1 -1
  34. package/dist/components/place-details-mobile.d.ts +1 -1
  35. package/dist/components/place-schedule-week.d.ts +1 -1
  36. package/dist/components/place-types.d.ts +1 -1
  37. package/dist/components/qa-block.d.ts +1 -1
  38. package/dist/components/spinner.d.ts +2 -2
  39. package/dist/components/tag.d.ts +2 -2
  40. package/dist/components/text.d.ts +3 -3
  41. package/dist/components/toggle.d.ts +1 -1
  42. package/dist/components/top-comparison.d.ts +1 -1
  43. package/dist/components/top-cross-link.d.ts +1 -1
  44. package/dist/components/top-ranking-entry.d.ts +1 -1
  45. package/dist/components/top-ranking-hero.d.ts +1 -1
  46. package/dist/components/top-types.d.ts +1 -1
  47. package/dist/components/venue-grade-dimensions.d.ts +1 -1
  48. package/dist/components/venue-quotes.d.ts +1 -1
  49. package/dist/components/venue-wall.d.ts +1 -1
  50. package/dist/{place-types-DCVqWmk_.d.ts → place-types-Dzy6FH67.d.ts} +1 -1
  51. package/package.json +1 -1
  52. package/src/components/event-detail-panel.test.tsx +35 -0
  53. package/src/components/event-detail-panel.tsx +32 -10
  54. package/src/components/menu-kitchen-list.test.tsx +39 -0
  55. package/src/components/menu-kitchen-list.tsx +36 -7
@@ -1,8 +1,10 @@
1
+ import { GradeAtom } from './chunk-YE4ZQUTI.js';
2
+ import { showsGradeAtom } from './chunk-I5V446BX.js';
1
3
  import { Separator } from './chunk-NWZGSLPU.js';
2
4
  import { Skeleton } from './chunk-XBBEX4SF.js';
3
5
  import { cn } from './chunk-FEK5XGZW.js';
4
6
  import { __objRest, __spreadValues } from './chunk-MMUBH76A.js';
5
- import { Star, Clock, Check, MessageSquare, ChevronRight } from 'lucide-react';
7
+ import { Clock, Check, MessageSquare, ChevronRight, Star } from 'lucide-react';
6
8
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
9
 
8
10
  function DefaultLink(_a) {
@@ -41,6 +43,25 @@ function MenuKitchenList({
41
43
  )
42
44
  ] });
43
45
  }
46
+ function gradeShown(place, labels) {
47
+ var _a;
48
+ return labels.grade !== void 0 && showsGradeAtom((_a = place.reviews_digest) == null ? void 0 : _a.grade, "xs");
49
+ }
50
+ function KitchenRating({
51
+ place,
52
+ labels
53
+ }) {
54
+ var _a;
55
+ const grade = (_a = place.reviews_digest) == null ? void 0 : _a.grade;
56
+ if (labels.grade && showsGradeAtom(grade, "xs")) {
57
+ return /* @__PURE__ */ jsx(GradeAtom, { grade, labels: labels.grade, size: "xs", className: "shrink-0" });
58
+ }
59
+ if (!place.rating) return null;
60
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
61
+ /* @__PURE__ */ jsx(Star, { className: "h-[11px] w-[11px] shrink-0", "aria-hidden": true }),
62
+ place.rating.rating
63
+ ] });
64
+ }
44
65
  function KitchenRow({
45
66
  kitchen,
46
67
  labels,
@@ -99,11 +120,8 @@ function KitchenRow({
99
120
  children: [
100
121
  /* @__PURE__ */ jsx("span", { className: "truncate text-[14.5px] font-bold tracking-[-0.01em] lg:line-clamp-2 lg:whitespace-normal lg:text-[15.5px] lg:leading-tight", children: place.name }),
101
122
  /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 truncate text-xs text-muted-foreground", children: [
102
- place.rating && /* @__PURE__ */ jsxs(Fragment, { children: [
103
- /* @__PURE__ */ jsx(Star, { className: "h-[11px] w-[11px] shrink-0", "aria-hidden": true }),
104
- place.rating.rating,
105
- kitchen.metaLabel && " \xB7 "
106
- ] }),
123
+ /* @__PURE__ */ jsx(KitchenRating, { place, labels }),
124
+ kitchen.metaLabel && (gradeShown(place, labels) || place.rating) && " \xB7 ",
107
125
  kitchen.metaLabel
108
126
  ] }),
109
127
  kitchen.hoursLabel && /* @__PURE__ */ jsxs(
@@ -3,10 +3,10 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const alertVariants: (props?: ({
6
- variant?: "success" | "warning" | "default" | "destructive" | null | undefined;
6
+ variant?: "default" | "destructive" | "warning" | "success" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  declare const Alert: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
9
- variant?: "success" | "warning" | "default" | "destructive" | null | undefined;
9
+ variant?: "default" | "destructive" | "warning" | "success" | null | undefined;
10
10
  } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
11
11
  declare const AlertTitle: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLHeadingElement> & React.RefAttributes<HTMLParagraphElement>>;
12
12
  declare const AlertDescription: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>>;
@@ -5,13 +5,13 @@ import { VariantProps } from 'class-variance-authority';
5
5
 
6
6
  declare const avatarVariants: (props?: ({
7
7
  size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
8
- tone?: "brown" | "muted" | "teal" | "green" | "blue" | "violet" | "rose" | null | undefined;
8
+ tone?: "muted" | "brown" | "teal" | "green" | "blue" | "violet" | "rose" | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  type AvatarSize = NonNullable<VariantProps<typeof avatarVariants>["size"]>;
11
11
  type AvatarTone = NonNullable<VariantProps<typeof avatarVariants>["tone"]>;
12
12
  declare const Avatar: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarProps & React.RefAttributes<HTMLSpanElement>, "ref"> & VariantProps<(props?: ({
13
13
  size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
14
- tone?: "brown" | "muted" | "teal" | "green" | "blue" | "violet" | "rose" | null | undefined;
14
+ tone?: "muted" | "brown" | "teal" | "green" | "blue" | "violet" | "rose" | null | undefined;
15
15
  } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLSpanElement>>;
16
16
  declare const AvatarImage: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarImageProps & React.RefAttributes<HTMLImageElement>, "ref"> & React.RefAttributes<HTMLImageElement>>;
17
17
  declare const AvatarFallback: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarFallbackProps & React.RefAttributes<HTMLSpanElement>, "ref"> & React.RefAttributes<HTMLSpanElement>>;
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { VariantProps } from 'class-variance-authority';
5
5
 
6
6
  declare const badgeVariants: (props?: ({
7
- variant?: "secondary" | "success" | "warning" | "default" | "destructive" | "outline" | "glass" | "overlay" | "brown" | "code" | "onPrimary" | "confirmed" | "pending" | "completed" | "assignee" | "softSuccess" | "softWarning" | "softDestructive" | "softNeutral" | null | undefined;
7
+ variant?: "default" | "destructive" | "warning" | "success" | "secondary" | "outline" | "glass" | "overlay" | "brown" | "code" | "onPrimary" | "confirmed" | "pending" | "completed" | "assignee" | "softSuccess" | "softWarning" | "softDestructive" | "softNeutral" | null | undefined;
8
8
  size?: "status" | "chip" | null | undefined;
9
9
  live?: boolean | null | undefined;
10
10
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -31,7 +31,7 @@ import { VariantProps } from 'class-variance-authority';
31
31
  * right edge, centred on the text, and never wraps under it.
32
32
  */
33
33
  declare const bannerVariants: (props?: ({
34
- tone?: "warning" | "destructive" | "info" | null | undefined;
34
+ tone?: "destructive" | "warning" | "info" | null | undefined;
35
35
  } & class_variance_authority_types.ClassProp) | undefined) => string;
36
36
  type BannerProps = Omit<React.HTMLAttributes<HTMLDivElement>, "title"> & VariantProps<typeof bannerVariants> & {
37
37
  /**
@@ -3,8 +3,8 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "link" | "secondary" | "warning" | "default" | "destructive" | "outline" | "ghost" | "glass" | "overlay" | "ink" | "brown" | "cta" | null | undefined;
7
- size?: "icon" | "default" | "cta" | "xs" | "sm" | "lg" | "pill" | "cta-sm" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
6
+ variant?: "default" | "destructive" | "warning" | "link" | "secondary" | "outline" | "ghost" | "glass" | "overlay" | "ink" | "brown" | "cta" | null | undefined;
7
+ size?: "default" | "icon" | "cta" | "xs" | "sm" | "lg" | "pill" | "cta-sm" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
8
8
  fullWidth?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
@@ -3,12 +3,12 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const cardVariants: (props?: ({
6
- variant?: "outline" | "glass" | "section" | "muted" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
6
+ variant?: "muted" | "outline" | "glass" | "section" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | null | undefined;
8
8
  interactive?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
11
- variant?: "outline" | "glass" | "section" | "muted" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
11
+ variant?: "muted" | "outline" | "glass" | "section" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
12
12
  size?: "default" | "sm" | "lg" | null | undefined;
13
13
  interactive?: boolean | null | undefined;
14
14
  } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
@@ -28,7 +28,7 @@ declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<
28
28
  ref?: React.Ref<HTMLInputElement>;
29
29
  } & {
30
30
  asChild?: boolean;
31
- }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "value" | "onChange" | "type"> & {
31
+ }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "value" | "type"> & {
32
32
  value?: string;
33
33
  onValueChange?: (search: string) => void;
34
34
  } & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
@@ -64,7 +64,7 @@ declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
64
64
  ref?: React.Ref<HTMLDivElement>;
65
65
  } & {
66
66
  asChild?: boolean;
67
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "value" | "heading"> & {
67
+ }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "heading" | "value"> & {
68
68
  heading?: React.ReactNode;
69
69
  value?: string;
70
70
  forceMount?: boolean;
@@ -82,7 +82,7 @@ declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
82
82
  ref?: React.Ref<HTMLDivElement>;
83
83
  } & {
84
84
  asChild?: boolean;
85
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "value" | "onSelect" | "disabled"> & {
85
+ }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "onSelect" | "value" | "disabled"> & {
86
86
  disabled?: boolean;
87
87
  onSelect?: (value: string) => void;
88
88
  value?: string;
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuConceptPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuLinkComponent } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -53,7 +53,7 @@ interface EmptyStateProps {
53
53
  * louder than a load failure the user can retry.
54
54
  */
55
55
  declare const EmptyState: React.ForwardRefExoticComponent<EmptyStateProps & React.RefAttributes<HTMLDivElement>>;
56
- declare const SearchEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "icon" | "variant"> & React.RefAttributes<HTMLDivElement>>;
57
- declare const ErrorEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "icon" | "variant"> & React.RefAttributes<HTMLDivElement>>;
56
+ declare const SearchEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "variant" | "icon"> & React.RefAttributes<HTMLDivElement>>;
57
+ declare const ErrorEmptyState: React.ForwardRefExoticComponent<Omit<EmptyStateProps, "variant" | "icon"> & React.RefAttributes<HTMLDivElement>>;
58
58
 
59
59
  export { EmptyState, ErrorEmptyState, SearchEmptyState };
@@ -1,6 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
+ import { G as GradeAtomLabels, a as PlaceGrade } from '../place-types-Dzy6FH67.js';
3
4
  import { EventPreview, EventFormatters } from './event-types.js';
5
+ import '../lib/grade.js';
4
6
 
5
7
  /**
6
8
  * EventDetailPanel — the BODY of the event detail sheet, and the body the
@@ -51,7 +53,13 @@ type EventDetailVenue = {
51
53
  name: string;
52
54
  /** Address line, e.g. "Str. Iuliu Maniu 5". */
53
55
  address?: string;
54
- /** e.g. "4.7" — rendered with a star. */
56
+ /**
57
+ * The venue's BearMenu grade. Shown as the grade pill in the rating's place
58
+ * when `labels.grade` is given; the star `ratingLabel` then stays only for a
59
+ * venue without a grade.
60
+ */
61
+ grade?: PlaceGrade;
62
+ /** e.g. "4.7" — Google's rating, rendered with a star. The fallback for an ungraded venue. */
55
63
  ratingLabel?: string;
56
64
  /** e.g. "open until 20:00" — appended to the meta row when given. */
57
65
  openLabel?: string;
@@ -66,6 +74,8 @@ type EventDetailVenue = {
66
74
  href?: string;
67
75
  };
68
76
  type EventDetailPanelLabels = {
77
+ /** The venue's grade pill. Optional: without it the venue shows the star, as before. */
78
+ grade?: GradeAtomLabels;
69
79
  free: string;
70
80
  liveNow: string;
71
81
  /** The ended chip, e.g. "Ended". */
@@ -208,7 +218,7 @@ type EventDetailChipRowProps = Pick<EventDetailPanelProps, "event" | "formatters
208
218
  declare function EventDetailChipRow({ event, labels, formatters, status, soonLabel, hype, tags, className, }: EventDetailChipRowProps): react_jsx_runtime.JSX.Element;
209
219
  type EventDetailVenueCardProps = {
210
220
  venue: EventDetailVenue;
211
- labels: Pick<EventDetailPanelLabels, "venueNoPage">;
221
+ labels: Pick<EventDetailPanelLabels, "venueNoPage" | "grade">;
212
222
  linkComponent?: React.ComponentType<EventDetailPanelLinkProps>;
213
223
  /** Thumbnail size in px: 52 in the sheet, 64 on the page. */
214
224
  thumbnailSize?: 52 | 64;
@@ -1,3 +1,5 @@
1
+ import { GradeAtom } from '../chunk-YE4ZQUTI.js';
2
+ import { showsGradeAtom } from '../chunk-I5V446BX.js';
1
3
  import { resolveEventEntryFee } from '../chunk-DNSXMHYX.js';
2
4
  import { Separator } from '../chunk-NWZGSLPU.js';
3
5
  import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
@@ -463,7 +465,9 @@ function EventDetailVenueCard({
463
465
  action,
464
466
  className
465
467
  }) {
466
- const meta = venue.href && (venue.address || venue.ratingLabel || venue.openLabel);
468
+ const showGrade = labels.grade !== void 0 && showsGradeAtom(venue.grade, "xs");
469
+ const hasRating = showGrade || Boolean(venue.ratingLabel);
470
+ const meta = venue.href && (venue.address || hasRating || venue.openLabel);
467
471
  const main = /* @__PURE__ */ jsxs(Fragment, { children: [
468
472
  /* @__PURE__ */ jsxs(
469
473
  "div",
@@ -501,8 +505,16 @@ function EventDetailVenueCard({
501
505
  /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-bold", children: venue.name }),
502
506
  venue.href ? meta && /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted-foreground", children: [
503
507
  venue.address && /* @__PURE__ */ jsx("span", { className: "truncate", children: venue.address }),
504
- venue.address && venue.ratingLabel && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-border", children: "\xB7" }),
505
- venue.ratingLabel && /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-center gap-1", children: [
508
+ venue.address && hasRating && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-border", children: "\xB7" }),
509
+ showGrade && labels.grade && venue.grade ? /* @__PURE__ */ jsx(
510
+ GradeAtom,
511
+ {
512
+ grade: venue.grade,
513
+ labels: labels.grade,
514
+ size: "xs",
515
+ className: "shrink-0"
516
+ }
517
+ ) : venue.ratingLabel && /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-center gap-1", children: [
506
518
  /* @__PURE__ */ jsx(Star, { className: "h-[11px] w-[11px]", "aria-hidden": true }),
507
519
  venue.ratingLabel
508
520
  ] }),
@@ -1,3 +1,3 @@
1
1
  import 'react/jsx-runtime';
2
2
  import '../lib/grade.js';
3
- export { G as GradeAtom, h as GradeAtomLabels, i as GradeAtomProps, j as GradeAtomSize } from '../place-types-DCVqWmk_.js';
3
+ export { i as GradeAtom, G as GradeAtomLabels, j as GradeAtomProps, k as GradeAtomSize } from '../place-types-Dzy6FH67.js';
@@ -3,12 +3,12 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const itemVariants: (props?: ({
6
- variant?: "default" | "outline" | "muted" | null | undefined;
6
+ variant?: "default" | "muted" | "outline" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | null | undefined;
8
8
  interactive?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  declare const Item: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
11
- variant?: "default" | "outline" | "muted" | null | undefined;
11
+ variant?: "default" | "muted" | "outline" | null | undefined;
12
12
  size?: "default" | "sm" | "lg" | null | undefined;
13
13
  interactive?: boolean | null | undefined;
14
14
  } & class_variance_authority_types.ClassProp) | undefined) => string> & {
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
3
  import { MenuVenueSignature, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
4
- import { P as PlacePreview, C as CitedText, a as PlaceGradeDimensions } from '../place-types-DCVqWmk_.js';
4
+ import { P as PlacePreview, C as CitedText, b as PlaceGradeDimensions } from '../place-types-Dzy6FH67.js';
5
5
  import { VenueGradeDimensionKey } from './venue-grade-dimensions.js';
6
6
  import '../lib/grade.js';
7
7
 
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuLinkComponent, MenuLoosenOption } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,9 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuKitchenListLabels } from './menu-kitchen-list.js';
3
3
  import { MenuFamilyBlockPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
4
- import 'react';
5
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
6
5
  import '../lib/grade.js';
6
+ import 'react';
7
7
 
8
8
  /**
9
9
  * MenuFamilyBlock — the cuisines index's family card. One photograph is the ground.
@@ -1,5 +1,7 @@
1
- import { MenuKitchenList } from '../chunk-QY66ME5B.js';
1
+ import { MenuKitchenList } from '../chunk-CZ2HC7WY.js';
2
2
  import { HueChip } from '../chunk-6VOWT4YR.js';
3
+ import '../chunk-YE4ZQUTI.js';
4
+ import '../chunk-I5V446BX.js';
3
5
  import { Separator } from '../chunk-NWZGSLPU.js';
4
6
  import { PhotoGround, PHOTO_GROUND } from '../chunk-SWD6CJ35.js';
5
7
  import { Skeleton } from '../chunk-XBBEX4SF.js';
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuFamilyPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuFilterChip } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuLinkComponent, MenuLinkProps } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuIndexEntry, MenuLinkComponent } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  type MenuIndexHeaderProps = {
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuIndexEntry, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuIntentFigure, MenuLinkComponent } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { MenuItemPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { G as GradeAtomLabels } from '../place-types-Dzy6FH67.js';
2
3
  import { MenuKitchenPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
3
- import 'react';
4
- import '../place-types-DCVqWmk_.js';
5
4
  import '../lib/grade.js';
5
+ import 'react';
6
6
 
7
7
  /**
8
8
  * MenuKitchenList — the intent page's spine.
@@ -53,6 +53,13 @@ type MenuKitchenListLabels = {
53
53
  openNow: string;
54
54
  /** Reveals the next page of rows, e.g. "Show 10 more". */
55
55
  showMore?: string;
56
+ /**
57
+ * The grade pill's labels. With them, a graded venue shows its BearMenu
58
+ * grade where the Google star was; the star stays only for a venue with no
59
+ * grade. Optional so existing callers keep compiling — without it every row
60
+ * shows the star, as before.
61
+ */
62
+ grade?: GradeAtomLabels;
56
63
  };
57
64
  /**
58
65
  * `wide` (default): the intent page's full-width list — from `lg` the venue
@@ -1,4 +1,6 @@
1
- export { MenuKitchenList, MenuKitchenListSkeleton } from '../chunk-QY66ME5B.js';
1
+ export { MenuKitchenList, MenuKitchenListSkeleton } from '../chunk-CZ2HC7WY.js';
2
+ import '../chunk-YE4ZQUTI.js';
3
+ import '../chunk-I5V446BX.js';
2
4
  import '../chunk-NWZGSLPU.js';
3
5
  import '../chunk-XBBEX4SF.js';
4
6
  import '../chunk-FEK5XGZW.js';
@@ -1,6 +1,6 @@
1
1
  import { MenuFamilyBlockPreview, MenuConceptPreview, MenuFamilyPreview, MenuFilterChip, MenuIndexEntry, MenuIntentFigure, MenuItemPreview, MenuKitchenPreview, MenuLoosenOption, QaItem, MenuVenuePreview } from './menu-types.js';
2
2
  import { DoorwayTile } from './doorway-tiles.js';
3
- import { P as PlacePreview } from '../place-types-DCVqWmk_.js';
3
+ import { P as PlacePreview } from '../place-types-Dzy6FH67.js';
4
4
  import 'react';
5
5
  import 'react/jsx-runtime';
6
6
  import '../lib/grade.js';
@@ -1,5 +1,5 @@
1
1
  import { ComponentType, CSSProperties, ReactNode } from 'react';
2
- import { M as MenuSizeAndPrice, P as PlacePreview } from '../place-types-DCVqWmk_.js';
2
+ import { M as MenuSizeAndPrice, P as PlacePreview } from '../place-types-Dzy6FH67.js';
3
3
  import 'react/jsx-runtime';
4
4
  import '../lib/grade.js';
5
5
 
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuVenuePreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { P as PlacePreview, b as PlaceAboutTabLabels, c as PlaceLinkKind } from '../place-types-DCVqWmk_.js';
2
+ import { P as PlacePreview, c as PlaceAboutTabLabels, d as PlaceLinkKind } from '../place-types-Dzy6FH67.js';
3
3
  import '../lib/grade.js';
4
4
 
5
5
  interface PlaceAboutTabProps {
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React$1 from 'react';
3
3
  import { ReactNode, MouseEvent } from 'react';
4
- import { P as PlacePreview, d as PlaceCardLabels } from '../place-types-DCVqWmk_.js';
4
+ import { P as PlacePreview, e as PlaceCardLabels } from '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { P as PlacePreview, e as PlaceDetailsMobileLabels } from '../place-types-DCVqWmk_.js';
2
+ import { P as PlacePreview, f as PlaceDetailsMobileLabels } from '../place-types-Dzy6FH67.js';
3
3
  import '../lib/grade.js';
4
4
 
5
5
  interface PlaceDetailsMobileProps {
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { f as PlaceSchedule, g as PlaceScheduleWeekLabels } from '../place-types-DCVqWmk_.js';
2
+ import { g as PlaceSchedule, h as PlaceScheduleWeekLabels } from '../place-types-Dzy6FH67.js';
3
3
  import '../lib/grade.js';
4
4
 
5
5
  interface PlaceScheduleWeekProps {
@@ -1,3 +1,3 @@
1
- export { C as CitedText, k as CitedValue, M as MenuSizeAndPrice, b as PlaceAboutTabLabels, d as PlaceCardLabels, e as PlaceDetailsMobileLabels, l as PlaceGrade, a as PlaceGradeDimensions, m as PlaceHourBlock, c as PlaceLinkKind, n as PlaceMustTry, P as PlacePreview, o as PlacePriceRange, p as PlaceReviewsDigest, f as PlaceSchedule, g as PlaceScheduleWeekLabels, W as WeekdayKey } from '../place-types-DCVqWmk_.js';
1
+ export { C as CitedText, l as CitedValue, M as MenuSizeAndPrice, c as PlaceAboutTabLabels, e as PlaceCardLabels, f as PlaceDetailsMobileLabels, a as PlaceGrade, b as PlaceGradeDimensions, m as PlaceHourBlock, d as PlaceLinkKind, n as PlaceMustTry, P as PlacePreview, o as PlacePriceRange, p as PlaceReviewsDigest, g as PlaceSchedule, h as PlaceScheduleWeekLabels, W as WeekdayKey } from '../place-types-Dzy6FH67.js';
2
2
  import 'react/jsx-runtime';
3
3
  import '../lib/grade.js';
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { QaItem } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  /**
@@ -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?: "inherit" | "primary" | "default" | "destructive" | null | undefined;
7
+ tone?: "default" | "destructive" | "inherit" | "primary" | 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?: "inherit" | "primary" | "default" | "destructive" | null | undefined;
11
+ tone?: "default" | "destructive" | "inherit" | "primary" | 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;
@@ -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" | "success" | "warning" | "default" | "destructive" | "outline" | "quiet" | null | undefined;
6
+ variant?: "default" | "destructive" | "warning" | "success" | "primary" | "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" | "success" | "warning" | "default" | "destructive" | "outline" | "quiet" | null | undefined;
10
+ variant?: "default" | "destructive" | "warning" | "success" | "primary" | "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;
@@ -22,9 +22,9 @@ import { VariantProps } from 'class-variance-authority';
22
22
  * Weights are 400 / 600 / 700. 500 is not in the system.
23
23
  */
24
24
  declare const textVariants: (props?: ({
25
- variant?: "body" | "small" | "display" | "h2" | "h3" | "label" | "caption" | "h1" | "h4" | "body-lg" | "body-sm" | "overline" | null | undefined;
26
- color?: "inherit" | "primary" | "default" | "destructive" | "muted" | "primary-foreground" | null | undefined;
27
- weight?: "bold" | "medium" | "normal" | "semibold" | null | undefined;
25
+ variant?: "small" | "display" | "h1" | "h2" | "h3" | "h4" | "body-lg" | "body" | "body-sm" | "caption" | "label" | "overline" | null | undefined;
26
+ color?: "default" | "destructive" | "inherit" | "muted" | "primary" | "primary-foreground" | null | undefined;
27
+ weight?: "bold" | "normal" | "medium" | "semibold" | null | undefined;
28
28
  leading?: "none" | "normal" | "tight" | "snug" | "relaxed" | "loose" | null | undefined;
29
29
  tracking?: "normal" | "tight" | "wide" | "wider" | null | undefined;
30
30
  wrap?: "balance" | "pretty" | "nowrap" | 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?: "tab" | "default" | "outline" | "chip" | "segmented" | null | undefined;
14
+ variant?: "default" | "tab" | "outline" | "chip" | "segmented" | null | undefined;
15
15
  size?: "default" | "sm" | "lg" | null | undefined;
16
16
  } & class_variance_authority_types.ClassProp) | undefined) => string;
17
17
  /**
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuLinkComponent } from './menu-types.js';
3
3
  import { TopComparisonRow } from './top-types.js';
4
4
  import 'react';
5
- import '../place-types-DCVqWmk_.js';
5
+ import '../place-types-Dzy6FH67.js';
6
6
  import '../lib/grade.js';
7
7
 
8
8
  type TopComparisonLabels = {
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuLinkComponent } from './menu-types.js';
3
3
  import 'react';
4
- import '../place-types-DCVqWmk_.js';
4
+ import '../place-types-Dzy6FH67.js';
5
5
  import '../lib/grade.js';
6
6
 
7
7
  type TopCrossLinkProps = {
@@ -3,7 +3,7 @@ import { MenuItemLabels } from './menu-item.js';
3
3
  import { MenuItemPreview, MenuLinkComponent } from './menu-types.js';
4
4
  import { TopRankEntry } from './top-types.js';
5
5
  import 'react';
6
- import '../place-types-DCVqWmk_.js';
6
+ import '../place-types-Dzy6FH67.js';
7
7
  import '../lib/grade.js';
8
8
 
9
9
  type TopRankingEntryLabels = {
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuLinkComponent } from './menu-types.js';
3
3
  import { TopExit } from './top-types.js';
4
4
  import 'react';
5
- import '../place-types-DCVqWmk_.js';
5
+ import '../place-types-Dzy6FH67.js';
6
6
  import '../lib/grade.js';
7
7
 
8
8
  type TopRankingHeroProps = {
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { MenuItemPreview } from './menu-types.js';
3
- import '../place-types-DCVqWmk_.js';
3
+ import '../place-types-Dzy6FH67.js';
4
4
  import 'react/jsx-runtime';
5
5
  import '../lib/grade.js';
6
6
 
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { a as PlaceGradeDimensions } from '../place-types-DCVqWmk_.js';
2
+ import { b as PlaceGradeDimensions } from '../place-types-Dzy6FH67.js';
3
3
  import '../lib/grade.js';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { C as CitedText } from '../place-types-DCVqWmk_.js';
2
+ import { C as CitedText } from '../place-types-Dzy6FH67.js';
3
3
  import '../lib/grade.js';
4
4
 
5
5
  /**
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MenuLinkComponent, MenuLinkProps } from './menu-types.js';
3
- import { P as PlacePreview } from '../place-types-DCVqWmk_.js';
3
+ import { P as PlacePreview } from '../place-types-Dzy6FH67.js';
4
4
  import 'react';
5
5
  import '../lib/grade.js';
6
6
 
@@ -332,4 +332,4 @@ type PlaceDetailsMobileLabels = PlaceAboutTabLabels & {
332
332
  noImageAvailable: string;
333
333
  };
334
334
 
335
- export { type CitedText as C, GradeAtom as G, type MenuSizeAndPrice as M, type PlacePreview as P, type WeekdayKey as W, type PlaceGradeDimensions as a, type PlaceAboutTabLabels as b, type PlaceLinkKind as c, type PlaceCardLabels as d, type PlaceDetailsMobileLabels as e, type PlaceSchedule as f, type PlaceScheduleWeekLabels as g, type GradeAtomLabels as h, type GradeAtomProps as i, type GradeAtomSize as j, type CitedValue as k, type PlaceGrade as l, type PlaceHourBlock as m, type PlaceMustTry as n, type PlacePriceRange as o, type PlaceReviewsDigest as p };
335
+ export { type CitedText as C, type GradeAtomLabels as G, type MenuSizeAndPrice as M, type PlacePreview as P, type WeekdayKey as W, type PlaceGrade as a, type PlaceGradeDimensions as b, type PlaceAboutTabLabels as c, type PlaceLinkKind as d, type PlaceCardLabels as e, type PlaceDetailsMobileLabels as f, type PlaceSchedule as g, type PlaceScheduleWeekLabels as h, GradeAtom as i, type GradeAtomProps as j, type GradeAtomSize as k, type CitedValue as l, type PlaceHourBlock as m, type PlaceMustTry as n, type PlacePriceRange as o, type PlaceReviewsDigest as p };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bearmenu/ui",
3
- "version": "0.8.49",
3
+ "version": "0.8.50",
4
4
  "packageManager": "pnpm@10.28.1",
5
5
  "description": "BearMenu design system — shared UI tokens, primitives, and components",
6
6
  "license": "UNLICENSED",
@@ -84,6 +84,41 @@ describe("EventDetailPanel", () => {
84
84
  expect(screen.getByText(labels.venueNoPage)).toBeInTheDocument();
85
85
  });
86
86
 
87
+ it("shows the venue's BearMenu grade instead of the Google star when given", () => {
88
+ const venue = {
89
+ name: "Sophie",
90
+ address: "Str. 5",
91
+ ratingLabel: "4.7",
92
+ href: "/places/sophie",
93
+ grade: { overall: 8.7, show_number: true },
94
+ };
95
+ const gradeLabels = {
96
+ soft: "Few reviews",
97
+ bands: {
98
+ exceptional: "Exceptional",
99
+ great: "Great",
100
+ good: "Good",
101
+ decent: "Decent",
102
+ weak: "Weak",
103
+ },
104
+ aria: (value: string) => `Grade ${value}`,
105
+ capped: "Capped",
106
+ };
107
+ const { container, rerender } = render(
108
+ <EventDetailPanel
109
+ {...base}
110
+ labels={{ ...labels, grade: gradeLabels }}
111
+ venue={venue}
112
+ />,
113
+ );
114
+ expect(container.textContent).toMatch(/8[.,]7/);
115
+ expect(container.textContent).not.toContain("4.7");
116
+
117
+ // No grade labels: exactly the old card.
118
+ rerender(<EventDetailPanel {...base} venue={venue} />);
119
+ expect(container.textContent).toContain("4.7");
120
+ });
121
+
87
122
  it("previews five performers and reveals the rest on request", () => {
88
123
  const performers = ["A", "B", "C", "D", "E", "F", "G"];
89
124
  render(
@@ -14,7 +14,10 @@ import {
14
14
  Unplug,
15
15
  User,
16
16
  } from "lucide-react";
17
+ import { showsGradeAtom } from "../lib/grade";
17
18
  import { cn } from "../lib/utils";
19
+ import { GradeAtom, type GradeAtomLabels } from "./grade-atom";
20
+ import type { PlaceGrade } from "./place-types";
18
21
  import { Button } from "./button";
19
22
  import { EventPosterFallback } from "./event-poster-fallback";
20
23
  import { Separator } from "./separator";
@@ -81,7 +84,13 @@ export type EventDetailVenue = {
81
84
  name: string;
82
85
  /** Address line, e.g. "Str. Iuliu Maniu 5". */
83
86
  address?: string;
84
- /** e.g. "4.7" — rendered with a star. */
87
+ /**
88
+ * The venue's BearMenu grade. Shown as the grade pill in the rating's place
89
+ * when `labels.grade` is given; the star `ratingLabel` then stays only for a
90
+ * venue without a grade.
91
+ */
92
+ grade?: PlaceGrade;
93
+ /** e.g. "4.7" — Google's rating, rendered with a star. The fallback for an ungraded venue. */
85
94
  ratingLabel?: string;
86
95
  /** e.g. "open until 20:00" — appended to the meta row when given. */
87
96
  openLabel?: string;
@@ -97,6 +106,8 @@ export type EventDetailVenue = {
97
106
  };
98
107
 
99
108
  export type EventDetailPanelLabels = {
109
+ /** The venue's grade pill. Optional: without it the venue shows the star, as before. */
110
+ grade?: GradeAtomLabels;
100
111
  free: string;
101
112
  liveNow: string;
102
113
  /** The ended chip, e.g. "Ended". */
@@ -752,7 +763,7 @@ export function EventDetailChipRow({
752
763
 
753
764
  export type EventDetailVenueCardProps = {
754
765
  venue: EventDetailVenue;
755
- labels: Pick<EventDetailPanelLabels, "venueNoPage">;
766
+ labels: Pick<EventDetailPanelLabels, "venueNoPage" | "grade">;
756
767
  linkComponent?: React.ComponentType<EventDetailPanelLinkProps>;
757
768
  /** Thumbnail size in px: 52 in the sheet, 64 on the page. */
758
769
  thumbnailSize?: 52 | 64;
@@ -774,8 +785,10 @@ export function EventDetailVenueCard({
774
785
  action,
775
786
  className,
776
787
  }: EventDetailVenueCardProps) {
777
- const meta =
778
- venue.href && (venue.address || venue.ratingLabel || venue.openLabel);
788
+ const showGrade =
789
+ labels.grade !== undefined && showsGradeAtom(venue.grade, "xs");
790
+ const hasRating = showGrade || Boolean(venue.ratingLabel);
791
+ const meta = venue.href && (venue.address || hasRating || venue.openLabel);
779
792
  // The main area (thumb, name, meta, chevron) is the link; `action` sits
780
793
  // BESIDE it, never inside — an anchor cannot contain another control, and a
781
794
  // page's "Open in Maps" must not navigate to the venue instead.
@@ -816,16 +829,25 @@ export function EventDetailVenueCard({
816
829
  {venue.address && (
817
830
  <span className="truncate">{venue.address}</span>
818
831
  )}
819
- {venue.address && venue.ratingLabel && (
832
+ {venue.address && hasRating && (
820
833
  <span aria-hidden className="text-border">
821
834
  ·
822
835
  </span>
823
836
  )}
824
- {venue.ratingLabel && (
825
- <span className="inline-flex shrink-0 items-center gap-1">
826
- <Star className="h-[11px] w-[11px]" aria-hidden />
827
- {venue.ratingLabel}
828
- </span>
837
+ {showGrade && labels.grade && venue.grade ? (
838
+ <GradeAtom
839
+ grade={venue.grade}
840
+ labels={labels.grade}
841
+ size="xs"
842
+ className="shrink-0"
843
+ />
844
+ ) : (
845
+ venue.ratingLabel && (
846
+ <span className="inline-flex shrink-0 items-center gap-1">
847
+ <Star className="h-[11px] w-[11px]" aria-hidden />
848
+ {venue.ratingLabel}
849
+ </span>
850
+ )
829
851
  )}
830
852
  {venue.openLabel && (
831
853
  <>
@@ -24,6 +24,45 @@ const kitchen: MenuKitchenPreview = {
24
24
  },
25
25
  };
26
26
 
27
+ const gradeLabels = {
28
+ soft: "Few reviews",
29
+ bands: { exceptional: "Exceptional", great: "Great", good: "Good", decent: "Decent", weak: "Weak" },
30
+ aria: (value: string) => `Grade ${value}`,
31
+ capped: "Capped",
32
+ };
33
+
34
+ describe("MenuKitchenList rating", () => {
35
+ const graded = {
36
+ ...kitchen,
37
+ place: {
38
+ ...kitchen.place,
39
+ rating: { rating: 4.8, count: 900 },
40
+ reviews_digest: { grade: { overall: 8.7, show_number: true } },
41
+ },
42
+ } as MenuKitchenPreview;
43
+
44
+ it("shows the BearMenu grade instead of the Google star when the venue has one", () => {
45
+ const { container } = render(
46
+ <MenuKitchenList kitchens={[graded]} labels={{ ...labels, grade: gradeLabels }} />
47
+ );
48
+ expect(container.textContent).toMatch(/8[.,]7/);
49
+ expect(container.textContent).not.toContain("4.8");
50
+ });
51
+
52
+ it("keeps the star for a venue with no grade", () => {
53
+ const ungraded = { ...graded, place: { ...graded.place, reviews_digest: undefined } };
54
+ const { container } = render(
55
+ <MenuKitchenList kitchens={[ungraded]} labels={{ ...labels, grade: gradeLabels }} />
56
+ );
57
+ expect(container.textContent).toContain("4.8");
58
+ });
59
+
60
+ it("keeps the star for callers that pass no grade labels", () => {
61
+ const { container } = render(<MenuKitchenList kitchens={[graded]} labels={labels} />);
62
+ expect(container.textContent).toContain("4.8");
63
+ });
64
+ });
65
+
27
66
  describe("MenuKitchenList", () => {
28
67
  /**
29
68
  * The reason this component exists rather than a flag on `RankedVenueList`.
@@ -1,5 +1,7 @@
1
1
  import { Check, ChevronRight, Clock, MessageSquare, Star } from "lucide-react";
2
+ import { showsGradeAtom } from "../lib/grade";
2
3
  import { cn } from "../lib/utils";
4
+ import { GradeAtom, type GradeAtomLabels } from "./grade-atom";
3
5
  import { Separator } from "./separator";
4
6
  import { Skeleton } from "./skeleton";
5
7
  import type { MenuKitchenPreview, MenuLinkComponent, MenuLinkProps } from "./menu-types";
@@ -59,6 +61,13 @@ export type MenuKitchenListLabels = {
59
61
  openNow: string;
60
62
  /** Reveals the next page of rows, e.g. "Show 10 more". */
61
63
  showMore?: string;
64
+ /**
65
+ * The grade pill's labels. With them, a graded venue shows its BearMenu
66
+ * grade where the Google star was; the star stays only for a venue with no
67
+ * grade. Optional so existing callers keep compiling — without it every row
68
+ * shows the star, as before.
69
+ */
70
+ grade?: GradeAtomLabels;
62
71
  };
63
72
 
64
73
  /**
@@ -127,6 +136,31 @@ export function MenuKitchenList({
127
136
  );
128
137
  }
129
138
 
139
+ function gradeShown(place: MenuKitchenPreview["place"], labels: MenuKitchenListLabels) {
140
+ return labels.grade !== undefined && showsGradeAtom(place.reviews_digest?.grade, "xs");
141
+ }
142
+
143
+ /** BearMenu's grade when the venue has one; Google's star only without it. */
144
+ function KitchenRating({
145
+ place,
146
+ labels,
147
+ }: {
148
+ place: MenuKitchenPreview["place"];
149
+ labels: MenuKitchenListLabels;
150
+ }) {
151
+ const grade = place.reviews_digest?.grade;
152
+ if (labels.grade && showsGradeAtom(grade, "xs")) {
153
+ return <GradeAtom grade={grade} labels={labels.grade} size="xs" className="shrink-0" />;
154
+ }
155
+ if (!place.rating) return null;
156
+ return (
157
+ <>
158
+ <Star className="h-[11px] w-[11px] shrink-0" aria-hidden />
159
+ {place.rating.rating}
160
+ </>
161
+ );
162
+ }
163
+
130
164
  function KitchenRow({
131
165
  kitchen,
132
166
  labels,
@@ -189,13 +223,8 @@ function KitchenRow({
189
223
  {place.name}
190
224
  </span>
191
225
  <span className="flex items-center gap-1 truncate text-xs text-muted-foreground">
192
- {place.rating && (
193
- <>
194
- <Star className="h-[11px] w-[11px] shrink-0" aria-hidden />
195
- {place.rating.rating}
196
- {kitchen.metaLabel && " · "}
197
- </>
198
- )}
226
+ <KitchenRating place={place} labels={labels} />
227
+ {kitchen.metaLabel && (gradeShown(place, labels) || place.rating) && " · "}
199
228
  {kitchen.metaLabel}
200
229
  </span>
201
230
  {kitchen.hoursLabel && (