@bearmenu/ui 0.8.48 → 0.8.49
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/components/alert.d.ts +2 -2
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/concept-compare.d.ts +1 -1
- package/dist/components/doorway-tiles.d.ts +1 -1
- package/dist/components/grade-atom.d.ts +1 -1
- package/dist/components/kitchen-dossier.d.ts +1 -1
- package/dist/components/menu-dead-end.d.ts +1 -1
- package/dist/components/menu-family-block.d.ts +1 -1
- package/dist/components/menu-family-card.d.ts +1 -1
- package/dist/components/menu-filter-chips.d.ts +1 -1
- package/dist/components/menu-group-rows.d.ts +1 -1
- package/dist/components/menu-index-header.d.ts +1 -1
- package/dist/components/menu-index-list.d.ts +1 -1
- package/dist/components/menu-intent-hero.d.ts +1 -1
- package/dist/components/menu-item.d.ts +1 -1
- package/dist/components/menu-kitchen-list.d.ts +1 -1
- package/dist/components/menu-mocks.d.ts +1 -1
- package/dist/components/menu-types.d.ts +1 -1
- package/dist/components/open-now-list.d.ts +1 -1
- package/dist/components/place-about-tab.d.ts +1 -1
- package/dist/components/place-card.d.ts +1 -1
- package/dist/components/place-card.js +13 -5
- package/dist/components/place-details-mobile.d.ts +1 -1
- package/dist/components/place-schedule-week.d.ts +1 -1
- package/dist/components/place-types.d.ts +1 -1
- package/dist/components/qa-block.d.ts +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-comparison.d.ts +1 -1
- package/dist/components/top-cross-link.d.ts +1 -1
- package/dist/components/top-ranking-entry.d.ts +1 -1
- package/dist/components/top-ranking-hero.d.ts +1 -1
- package/dist/components/top-types.d.ts +1 -1
- package/dist/components/venue-grade-dimensions.d.ts +1 -1
- package/dist/components/venue-quotes.d.ts +1 -1
- package/dist/components/venue-wall.d.ts +1 -1
- package/dist/{place-types-C4rR3LTs.d.ts → place-types-DCVqWmk_.d.ts} +11 -0
- package/package.json +217 -23
- package/src/components/place-card.stories.tsx +36 -0
- package/src/components/place-card.test.tsx +29 -0
- package/src/components/place-card.tsx +24 -5
- package/src/components/place-types.ts +11 -0
|
@@ -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?: "
|
|
6
|
+
variant?: "success" | "warning" | "default" | "destructive" | 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?: "
|
|
9
|
+
variant?: "success" | "warning" | "default" | "destructive" | 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>>;
|
|
@@ -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?: "
|
|
7
|
+
variant?: "secondary" | "success" | "warning" | "default" | "destructive" | "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;
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const buttonVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
6
|
+
variant?: "link" | "secondary" | "warning" | "default" | "destructive" | "outline" | "ghost" | "glass" | "overlay" | "ink" | "brown" | "cta" | null | undefined;
|
|
7
7
|
size?: "icon" | "default" | "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;
|
|
@@ -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-
|
|
4
|
+
import '../place-types-DCVqWmk_.js';
|
|
5
5
|
import '../lib/grade.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -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-
|
|
3
|
+
export { G as GradeAtom, h as GradeAtomLabels, i as GradeAtomProps, j as GradeAtomSize } from '../place-types-DCVqWmk_.js';
|
|
@@ -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-
|
|
4
|
+
import { P as PlacePreview, C as CitedText, a as PlaceGradeDimensions } from '../place-types-DCVqWmk_.js';
|
|
5
5
|
import { VenueGradeDimensionKey } from './venue-grade-dimensions.js';
|
|
6
6
|
import '../lib/grade.js';
|
|
7
7
|
|
|
@@ -2,7 +2,7 @@ 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
4
|
import 'react';
|
|
5
|
-
import '../place-types-
|
|
5
|
+
import '../place-types-DCVqWmk_.js';
|
|
6
6
|
import '../lib/grade.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -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-
|
|
4
|
+
import '../place-types-DCVqWmk_.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-
|
|
4
|
+
import '../place-types-DCVqWmk_.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-
|
|
4
|
+
import '../place-types-DCVqWmk_.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-
|
|
4
|
+
import '../place-types-DCVqWmk_.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 { MenuKitchenPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
|
|
3
3
|
import 'react';
|
|
4
|
-
import '../place-types-
|
|
4
|
+
import '../place-types-DCVqWmk_.js';
|
|
5
5
|
import '../lib/grade.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -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-
|
|
3
|
+
import { P as PlacePreview } from '../place-types-DCVqWmk_.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-
|
|
2
|
+
import { M as MenuSizeAndPrice, P as PlacePreview } from '../place-types-DCVqWmk_.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-
|
|
4
|
+
import '../place-types-DCVqWmk_.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-
|
|
2
|
+
import { P as PlacePreview, b as PlaceAboutTabLabels, c as PlaceLinkKind } from '../place-types-DCVqWmk_.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-
|
|
4
|
+
import { P as PlacePreview, d as PlaceCardLabels } from '../place-types-DCVqWmk_.js';
|
|
5
5
|
import '../lib/grade.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -56,6 +56,10 @@ function PriceLevel({ level, label }) {
|
|
|
56
56
|
/* @__PURE__ */ jsx("span", { className: "opacity-30", children: "$".repeat(4 - filled) })
|
|
57
57
|
] });
|
|
58
58
|
}
|
|
59
|
+
function launchYear(launchedAt) {
|
|
60
|
+
const match = launchedAt ? /^(\d{4})-\d{2}$/.exec(launchedAt) : null;
|
|
61
|
+
return match ? Number(match[1]) : void 0;
|
|
62
|
+
}
|
|
59
63
|
var STATIC_ROOT_CLASSES = `group relative flex w-full select-none flex-col ${HOVER_MOTION} ${HOVER_LIFT_GRID_GROUP}`;
|
|
60
64
|
var ROOT_CLASSES = `${STATIC_ROOT_CLASSES} cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:rounded-xl`;
|
|
61
65
|
var DEFAULT_SIZES = "(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw";
|
|
@@ -72,7 +76,7 @@ var PlaceCard = memo(function PlaceCard2({
|
|
|
72
76
|
photoOverlay,
|
|
73
77
|
className
|
|
74
78
|
}) {
|
|
75
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
79
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
76
80
|
const [imgError, setImgError] = useState(false);
|
|
77
81
|
const MotionLink = useMemo(() => motion.create(LinkComponent), [LinkComponent]);
|
|
78
82
|
const coverUrl = imgError ? void 0 : place.cover_url;
|
|
@@ -81,6 +85,8 @@ var PlaceCard = memo(function PlaceCard2({
|
|
|
81
85
|
const neighbourhood = (_e = (_d = place.areas) == null ? void 0 : _d[0]) == null ? void 0 : _e.name;
|
|
82
86
|
const priceLevel = (_h = (_f = place.price_dishes) == null ? void 0 : _f.price_score) != null ? _h : (_g = place.price_drinks) == null ? void 0 : _g.price_score;
|
|
83
87
|
const showPrice = priceLevel !== void 0 && priceLevel > 0;
|
|
88
|
+
const year = labels.established ? launchYear(place.launched_at) : void 0;
|
|
89
|
+
const established = year !== void 0 ? (_i = labels.established) == null ? void 0 : _i.call(labels, year) : void 0;
|
|
84
90
|
const ariaLabel = labels.viewDetails(place.name);
|
|
85
91
|
const handleKeyDown = (e) => {
|
|
86
92
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -145,10 +151,12 @@ var PlaceCard = memo(function PlaceCard2({
|
|
|
145
151
|
/* @__PURE__ */ jsx(HeadlineRating, { place, labels })
|
|
146
152
|
] }),
|
|
147
153
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
148
|
-
(cuisine || showPrice) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
149
|
-
cuisine && /* @__PURE__ */ jsx("span", { className: "capitalize", children: cuisine }),
|
|
150
|
-
cuisine && showPrice && /* @__PURE__ */ jsx("span", { className: "text-border", children: "\xB7" }),
|
|
151
|
-
showPrice && /* @__PURE__ */ jsx(PriceLevel, { level: priceLevel, label: labels.priceLevel(priceLevel) })
|
|
154
|
+
(cuisine || showPrice || established) && /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
155
|
+
cuisine && /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate capitalize", children: cuisine }),
|
|
156
|
+
cuisine && showPrice && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-border", children: "\xB7" }),
|
|
157
|
+
showPrice && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: /* @__PURE__ */ jsx(PriceLevel, { level: priceLevel, label: labels.priceLevel(priceLevel) }) }),
|
|
158
|
+
established && (cuisine || showPrice) && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-border", children: "\xB7" }),
|
|
159
|
+
established && /* @__PURE__ */ jsx("span", { className: "shrink-0 whitespace-nowrap", children: established })
|
|
152
160
|
] }),
|
|
153
161
|
distance !== void 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
154
162
|
/* @__PURE__ */ jsx(MapPin, { className: "h-3 w-3 shrink-0", "aria-hidden": true }),
|
|
@@ -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-
|
|
2
|
+
import { P as PlacePreview, e as PlaceDetailsMobileLabels } from '../place-types-DCVqWmk_.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-
|
|
2
|
+
import { f as PlaceSchedule, g as PlaceScheduleWeekLabels } from '../place-types-DCVqWmk_.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-
|
|
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';
|
|
2
2
|
import 'react/jsx-runtime';
|
|
3
3
|
import '../lib/grade.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?: "
|
|
7
|
+
tone?: "inherit" | "primary" | "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?: "
|
|
11
|
+
tone?: "inherit" | "primary" | "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?: "
|
|
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?: "
|
|
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?: "body" | "small" | "display" | "h2" | "h3" | "label" | "caption" | "h1" | "h4" | "body-lg" | "body-sm" | "overline" | null | undefined;
|
|
26
|
-
color?: "
|
|
26
|
+
color?: "inherit" | "primary" | "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
|
/**
|
|
@@ -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-
|
|
5
|
+
import '../place-types-DCVqWmk_.js';
|
|
6
6
|
import '../lib/grade.js';
|
|
7
7
|
|
|
8
8
|
type TopComparisonLabels = {
|
|
@@ -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-
|
|
6
|
+
import '../place-types-DCVqWmk_.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-
|
|
5
|
+
import '../place-types-DCVqWmk_.js';
|
|
6
6
|
import '../lib/grade.js';
|
|
7
7
|
|
|
8
8
|
type TopRankingHeroProps = {
|
|
@@ -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-
|
|
3
|
+
import { P as PlacePreview } from '../place-types-DCVqWmk_.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
import '../lib/grade.js';
|
|
6
6
|
|
|
@@ -123,6 +123,12 @@ type PlacePreview = {
|
|
|
123
123
|
* bare storage key straight to `next/image`'s loader).
|
|
124
124
|
*/
|
|
125
125
|
cover_url?: string;
|
|
126
|
+
/**
|
|
127
|
+
* Year and month the place launched (`YYYY-MM`), as the schema ships it.
|
|
128
|
+
* The backend approximates it from the earliest review it holds, so it is a
|
|
129
|
+
* floor rather than a founding date. `<PlaceCard>` shows the year only.
|
|
130
|
+
*/
|
|
131
|
+
launched_at?: string;
|
|
126
132
|
};
|
|
127
133
|
type PlaceHourBlock = {
|
|
128
134
|
opens_at: number;
|
|
@@ -248,6 +254,11 @@ type PlaceCardLabels = {
|
|
|
248
254
|
viewDetails: (name: string) => string;
|
|
249
255
|
/** Accessible name of the `$` price glyphs, e.g. "Price level 2 of 4". */
|
|
250
256
|
priceLevel: (level: number) => string;
|
|
257
|
+
/**
|
|
258
|
+
* The launch year on the meta line, e.g. "Est. 2020" / "Din 2020". Optional:
|
|
259
|
+
* without it the card shows no year even when `launched_at` is set.
|
|
260
|
+
*/
|
|
261
|
+
established?: (year: number) => string;
|
|
251
262
|
grade: GradeAtomLabels;
|
|
252
263
|
};
|
|
253
264
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bearmenu/ui",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.49",
|
|
4
|
+
"packageManager": "pnpm@10.28.1",
|
|
4
5
|
"description": "BearMenu design system — shared UI tokens, primitives, and components",
|
|
5
6
|
"license": "UNLICENSED",
|
|
6
7
|
"repository": {
|
|
@@ -16,20 +17,223 @@
|
|
|
16
17
|
"src"
|
|
17
18
|
],
|
|
18
19
|
"exports": {
|
|
20
|
+
"./components/accordion": "./src/components/accordion.tsx",
|
|
21
|
+
"./components/action-bar": "./src/components/action-bar.tsx",
|
|
22
|
+
"./components/activity-row": "./src/components/activity-row.tsx",
|
|
23
|
+
"./components/alert": "./src/components/alert.tsx",
|
|
24
|
+
"./components/alert-dialog": "./src/components/alert-dialog.tsx",
|
|
25
|
+
"./components/aspect-ratio": "./src/components/aspect-ratio.tsx",
|
|
26
|
+
"./components/avatar": "./src/components/avatar.tsx",
|
|
27
|
+
"./components/badge": "./src/components/badge.tsx",
|
|
28
|
+
"./components/banner": "./src/components/banner.tsx",
|
|
29
|
+
"./components/booking-row": "./src/components/booking-row.tsx",
|
|
30
|
+
"./components/booking-summary-card": "./src/components/booking-summary-card.tsx",
|
|
31
|
+
"./components/breadcrumb": "./src/components/breadcrumb.tsx",
|
|
32
|
+
"./components/button": "./src/components/button.tsx",
|
|
33
|
+
"./components/button-group": "./src/components/button-group.tsx",
|
|
34
|
+
"./components/calendar": "./src/components/calendar.tsx",
|
|
35
|
+
"./components/card": "./src/components/card.tsx",
|
|
36
|
+
"./components/carousel": "./src/components/carousel.tsx",
|
|
37
|
+
"./components/category-deck": "./src/components/category-deck.tsx",
|
|
38
|
+
"./components/checkbox": "./src/components/checkbox.tsx",
|
|
39
|
+
"./components/circular-progress": "./src/components/circular-progress.tsx",
|
|
40
|
+
"./components/coach-marks": "./src/components/coach-marks.tsx",
|
|
41
|
+
"./components/collapsible": "./src/components/collapsible.tsx",
|
|
42
|
+
"./components/combobox": "./src/components/combobox.tsx",
|
|
43
|
+
"./components/command": "./src/components/command.tsx",
|
|
44
|
+
"./components/concept-card": "./src/components/concept-card.tsx",
|
|
45
|
+
"./components/concept-compare": "./src/components/concept-compare.tsx",
|
|
46
|
+
"./components/concept-compare-rail": "./src/components/concept-compare-rail.tsx",
|
|
47
|
+
"./components/concept-convergence": "./src/components/concept-convergence.tsx",
|
|
48
|
+
"./components/convergence": "./src/components/convergence.tsx",
|
|
49
|
+
"./components/coverflow": "./src/components/coverflow.tsx",
|
|
50
|
+
"./components/currency-input": "./src/components/currency-input.tsx",
|
|
51
|
+
"./components/date-input": "./src/components/date-input.tsx",
|
|
52
|
+
"./components/day-rail-chip": "./src/components/day-rail-chip.tsx",
|
|
53
|
+
"./components/description-list": "./src/components/description-list.tsx",
|
|
54
|
+
"./components/dialog": "./src/components/dialog.tsx",
|
|
55
|
+
"./components/doorway-tiles": "./src/components/doorway-tiles.tsx",
|
|
56
|
+
"./components/drawer": "./src/components/drawer.tsx",
|
|
57
|
+
"./components/drift-rail": "./src/components/drift-rail.tsx",
|
|
58
|
+
"./components/dropdown-menu": "./src/components/dropdown-menu.tsx",
|
|
59
|
+
"./components/empty-state": "./src/components/empty-state.tsx",
|
|
60
|
+
"./components/event-card": "./src/components/event-card.tsx",
|
|
61
|
+
"./components/event-compact-row": "./src/components/event-compact-row.tsx",
|
|
62
|
+
"./components/event-day-header": "./src/components/event-day-header.tsx",
|
|
63
|
+
"./components/event-day-separator": "./src/components/event-day-separator.tsx",
|
|
64
|
+
"./components/event-detail-panel": "./src/components/event-detail-panel.tsx",
|
|
65
|
+
"./components/event-hub-hero": "./src/components/event-hub-hero.tsx",
|
|
66
|
+
"./components/event-index-row": "./src/components/event-index-row.tsx",
|
|
67
|
+
"./components/event-list-row": "./src/components/event-list-row.tsx",
|
|
68
|
+
"./components/event-list-row-wide": "./src/components/event-list-row-wide.tsx",
|
|
69
|
+
"./components/event-poster-fallback": "./src/components/event-poster-fallback.tsx",
|
|
70
|
+
"./components/event-tonight-strip": "./src/components/event-tonight-strip.tsx",
|
|
71
|
+
"./components/event-types": "./src/components/event-types.ts",
|
|
72
|
+
"./components/events-empty-state": "./src/components/events-empty-state.tsx",
|
|
73
|
+
"./components/explainer-steps": "./src/components/explainer-steps.tsx",
|
|
74
|
+
"./components/facet-group": "./src/components/facet-group.tsx",
|
|
75
|
+
"./components/featured-rail-item": "./src/components/featured-rail-item.tsx",
|
|
76
|
+
"./components/field": "./src/components/field.tsx",
|
|
77
|
+
"./components/filter-category-row": "./src/components/filter-category-row.tsx",
|
|
78
|
+
"./components/form": "./src/components/form.tsx",
|
|
79
|
+
"./components/glass-ground": "./src/components/glass-ground.tsx",
|
|
80
|
+
"./components/grade-atom": "./src/components/grade-atom.tsx",
|
|
81
|
+
"./components/haptic-switch": "./src/components/haptic-switch.tsx",
|
|
82
|
+
"./components/hold-countdown": "./src/components/hold-countdown.tsx",
|
|
83
|
+
"./components/hub-surface": "./src/components/hub-surface.tsx",
|
|
84
|
+
"./components/hue-chip": "./src/components/hue-chip.tsx",
|
|
85
|
+
"./components/icon": "./src/components/icon.tsx",
|
|
86
|
+
"./components/input": "./src/components/input.tsx",
|
|
87
|
+
"./components/input-group": "./src/components/input-group.tsx",
|
|
88
|
+
"./components/input-otp": "./src/components/input-otp.tsx",
|
|
89
|
+
"./components/item": "./src/components/item.tsx",
|
|
90
|
+
"./components/kbd": "./src/components/kbd.tsx",
|
|
91
|
+
"./components/label": "./src/components/label.tsx",
|
|
92
|
+
"./components/kitchen-dossier": "./src/components/kitchen-dossier.tsx",
|
|
93
|
+
"./components/lazy-markdown": "./src/components/lazy-markdown.tsx",
|
|
94
|
+
"./components/link-button": "./src/components/link-button.tsx",
|
|
95
|
+
"./components/markdown-renderer": "./src/components/markdown-renderer.tsx",
|
|
96
|
+
"./components/menu-dead-end": "./src/components/menu-dead-end.tsx",
|
|
97
|
+
"./components/menu-family-block": "./src/components/menu-family-block.tsx",
|
|
98
|
+
"./components/menu-family-card": "./src/components/menu-family-card.tsx",
|
|
99
|
+
"./components/menu-field": "./src/components/menu-field.tsx",
|
|
100
|
+
"./components/menu-filter-chips": "./src/components/menu-filter-chips.tsx",
|
|
101
|
+
"./components/menu-group-rows": "./src/components/menu-group-rows.tsx",
|
|
102
|
+
"./components/menu-hub-hero": "./src/components/menu-hub-hero.tsx",
|
|
103
|
+
"./components/menu-index-header": "./src/components/menu-index-header.tsx",
|
|
104
|
+
"./components/menu-index-list": "./src/components/menu-index-list.tsx",
|
|
105
|
+
"./components/menu-intent-hero": "./src/components/menu-intent-hero.tsx",
|
|
106
|
+
"./components/menu-item": "./src/components/menu-item.tsx",
|
|
107
|
+
"./components/menu-pick-row": "./src/components/menu-pick-row.tsx",
|
|
108
|
+
"./components/menu-kitchen-list": "./src/components/menu-kitchen-list.tsx",
|
|
109
|
+
"./components/menu-mocks": "./src/components/menu-mocks.ts",
|
|
110
|
+
"./components/menu-types": "./src/components/menu-types.ts",
|
|
111
|
+
"./components/motion-icon": "./src/components/motion-icon.tsx",
|
|
112
|
+
"./components/motion-icon-actions": "./src/components/motion-icon-actions.tsx",
|
|
113
|
+
"./components/motion-icon-controls": "./src/components/motion-icon-controls.tsx",
|
|
114
|
+
"./components/motion-icon-set": "./src/components/motion-icon-set.tsx",
|
|
115
|
+
"./components/multi-select-combobox": "./src/components/multi-select-combobox.tsx",
|
|
116
|
+
"./components/multi-select-menu": "./src/components/multi-select-menu.tsx",
|
|
117
|
+
"./components/native-select": "./src/components/native-select.tsx",
|
|
118
|
+
"./components/open-now-list": "./src/components/open-now-list.tsx",
|
|
119
|
+
"./components/pagination": "./src/components/pagination.tsx",
|
|
120
|
+
"./components/pan-zoom-viewport": "./src/components/pan-zoom-viewport.tsx",
|
|
121
|
+
"./components/party-counter": "./src/components/party-counter.tsx",
|
|
122
|
+
"./components/phone-frame": "./src/components/phone-frame.tsx",
|
|
123
|
+
"./components/photo-ground": "./src/components/photo-ground.tsx",
|
|
124
|
+
"./components/place-about-tab": "./src/components/place-about-tab.tsx",
|
|
125
|
+
"./components/place-card": "./src/components/place-card.tsx",
|
|
126
|
+
"./components/place-details-mobile": "./src/components/place-details-mobile.tsx",
|
|
127
|
+
"./components/place-schedule-week": "./src/components/place-schedule-week.tsx",
|
|
128
|
+
"./components/place-types": "./src/components/place-types.ts",
|
|
129
|
+
"./components/popover": "./src/components/popover.tsx",
|
|
130
|
+
"./components/poster-stack": "./src/components/poster-stack.tsx",
|
|
131
|
+
"./components/poster-tape": "./src/components/poster-tape.tsx",
|
|
132
|
+
"./components/poster-wall": "./src/components/poster-wall.tsx",
|
|
133
|
+
"./components/progress": "./src/components/progress.tsx",
|
|
134
|
+
"./components/qa-block": "./src/components/qa-block.tsx",
|
|
135
|
+
"./components/qr-panel": "./src/components/qr-panel.tsx",
|
|
136
|
+
"./components/quiet-stretch": "./src/components/quiet-stretch.tsx",
|
|
137
|
+
"./components/radio-group": "./src/components/radio-group.tsx",
|
|
138
|
+
"./components/reconcile-bar": "./src/components/reconcile-bar.tsx",
|
|
139
|
+
"./components/responsive-overlay": "./src/components/responsive-overlay.tsx",
|
|
140
|
+
"./components/rating": "./src/components/rating.tsx",
|
|
141
|
+
"./components/scroll-area": "./src/components/scroll-area.tsx",
|
|
142
|
+
"./components/scroll-fade": "./src/components/scroll-fade.tsx",
|
|
143
|
+
"./components/search-bar": "./src/components/search-bar.tsx",
|
|
144
|
+
"./components/searchable-select": "./src/components/searchable-select.tsx",
|
|
145
|
+
"./components/section-header": "./src/components/section-header.tsx",
|
|
146
|
+
"./components/segmented-nav": "./src/components/segmented-nav.tsx",
|
|
147
|
+
"./components/select": "./src/components/select.tsx",
|
|
148
|
+
"./components/selection-bar": "./src/components/selection-bar.tsx",
|
|
149
|
+
"./components/separator": "./src/components/separator.tsx",
|
|
150
|
+
"./components/sheet": "./src/components/sheet.tsx",
|
|
151
|
+
"./components/signature-chip": "./src/components/signature-chip.tsx",
|
|
152
|
+
"./components/skeleton": "./src/components/skeleton.tsx",
|
|
153
|
+
"./components/skeleton-card": "./src/components/skeleton-card.tsx",
|
|
154
|
+
"./components/slider": "./src/components/slider.tsx",
|
|
155
|
+
"./components/sliding-panels": "./src/components/sliding-panels.tsx",
|
|
156
|
+
"./components/sonner": "./src/components/sonner.tsx",
|
|
157
|
+
"./components/sort-option-list": "./src/components/sort-option-list.tsx",
|
|
158
|
+
"./components/spinner": "./src/components/spinner.tsx",
|
|
159
|
+
"./components/split-share-row": "./src/components/split-share-row.tsx",
|
|
160
|
+
"./components/stat-banner": "./src/components/stat-banner.tsx",
|
|
161
|
+
"./components/stepper": "./src/components/stepper.tsx",
|
|
162
|
+
"./components/sticky-container": "./src/components/sticky-container.tsx",
|
|
163
|
+
"./components/story": "./src/components/story.tsx",
|
|
164
|
+
"./components/switch": "./src/components/switch.tsx",
|
|
165
|
+
"./components/table": "./src/components/table.tsx",
|
|
166
|
+
"./components/table-ledger-card": "./src/components/table-ledger-card.tsx",
|
|
167
|
+
"./components/table-trigger-band": "./src/components/table-trigger-band.tsx",
|
|
168
|
+
"./components/tabs": "./src/components/tabs.tsx",
|
|
169
|
+
"./components/tag": "./src/components/tag.tsx",
|
|
170
|
+
"./components/tag-group": "./src/components/tag-group.tsx",
|
|
171
|
+
"./components/text": "./src/components/text.tsx",
|
|
172
|
+
"./components/textarea": "./src/components/textarea.tsx",
|
|
173
|
+
"./components/time-slot": "./src/components/time-slot.tsx",
|
|
174
|
+
"./components/timeline-day": "./src/components/timeline-day.tsx",
|
|
175
|
+
"./components/timeline-hour": "./src/components/timeline-hour.tsx",
|
|
176
|
+
"./components/timeline-hour-puck": "./src/components/timeline-hour-puck.tsx",
|
|
177
|
+
"./components/toggle": "./src/components/toggle.tsx",
|
|
178
|
+
"./components/toggle-group": "./src/components/toggle-group.tsx",
|
|
179
|
+
"./components/tooltip": "./src/components/tooltip.tsx",
|
|
180
|
+
"./components/top-comparison": "./src/components/top-comparison.tsx",
|
|
181
|
+
"./components/top-cross-link": "./src/components/top-cross-link.tsx",
|
|
182
|
+
"./components/top-ranking-entry": "./src/components/top-ranking-entry.tsx",
|
|
183
|
+
"./components/top-ranking-hero": "./src/components/top-ranking-hero.tsx",
|
|
184
|
+
"./components/top-ranking-legend": "./src/components/top-ranking-legend.tsx",
|
|
185
|
+
"./components/top-types": "./src/components/top-types.ts",
|
|
186
|
+
"./components/use-mobile": "./src/components/use-mobile.tsx",
|
|
187
|
+
"./components/venue-events-block": "./src/components/venue-events-block.tsx",
|
|
188
|
+
"./components/venue-grade-dimensions": "./src/components/venue-grade-dimensions.tsx",
|
|
189
|
+
"./components/venue-quotes": "./src/components/venue-quotes.tsx",
|
|
190
|
+
"./components/venue-wall": "./src/components/venue-wall.tsx",
|
|
191
|
+
"./components/weekday-chip": "./src/components/weekday-chip.tsx",
|
|
192
|
+
"./components/weekday-theme-banner": "./src/components/weekday-theme-banner.tsx",
|
|
193
|
+
"./components/weekend-panel": "./src/components/weekend-panel.tsx",
|
|
19
194
|
"./globals.css": "./src/globals.css",
|
|
20
|
-
"./
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"./hooks
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"./lib
|
|
29
|
-
|
|
30
|
-
|
|
195
|
+
"./hooks/use-drag-swipe": "./src/hooks/use-drag-swipe.ts",
|
|
196
|
+
"./hooks/use-haptic": "./src/hooks/use-haptic.ts",
|
|
197
|
+
"./hooks/use-media-query": "./src/hooks/use-media-query.ts",
|
|
198
|
+
"./hooks/use-mobile": "./src/hooks/use-mobile.ts",
|
|
199
|
+
"./hooks/use-mounted-theme": "./src/hooks/use-mounted-theme.ts",
|
|
200
|
+
"./hooks/use-sticky": "./src/hooks/use-sticky.ts",
|
|
201
|
+
"./lib/card-hover": "./src/lib/card-hover.ts",
|
|
202
|
+
"./lib/grade": "./src/lib/grade.ts",
|
|
203
|
+
"./lib/icon-depth": "./src/lib/icon-depth.ts",
|
|
204
|
+
"./lib/icon-registry": "./src/lib/icon-registry.ts",
|
|
205
|
+
"./lib/motion": "./src/lib/motion.ts",
|
|
206
|
+
"./lib/overlay-pill": "./src/lib/overlay-pill.ts",
|
|
207
|
+
"./lib/utils": "./src/lib/utils.ts"
|
|
208
|
+
},
|
|
209
|
+
"publishConfig": {
|
|
210
|
+
"exports": {
|
|
211
|
+
"./globals.css": "./src/globals.css",
|
|
212
|
+
"./components/*": {
|
|
213
|
+
"types": "./dist/components/*.d.ts",
|
|
214
|
+
"import": "./dist/components/*.js"
|
|
215
|
+
},
|
|
216
|
+
"./hooks/*": {
|
|
217
|
+
"types": "./dist/hooks/*.d.ts",
|
|
218
|
+
"import": "./dist/hooks/*.js"
|
|
219
|
+
},
|
|
220
|
+
"./lib/*": {
|
|
221
|
+
"types": "./dist/lib/*.d.ts",
|
|
222
|
+
"import": "./dist/lib/*.js"
|
|
223
|
+
}
|
|
31
224
|
}
|
|
32
225
|
},
|
|
226
|
+
"scripts": {
|
|
227
|
+
"storybook": "storybook dev -p 6006",
|
|
228
|
+
"build-storybook": "storybook build",
|
|
229
|
+
"build": "tsup",
|
|
230
|
+
"dev": "tsup --watch",
|
|
231
|
+
"typecheck": "tsc --noEmit",
|
|
232
|
+
"test": "vitest run",
|
|
233
|
+
"test:watch": "vitest",
|
|
234
|
+
"clean": "rm -rf dist",
|
|
235
|
+
"prepublishOnly": "pnpm run build"
|
|
236
|
+
},
|
|
33
237
|
"peerDependencies": {
|
|
34
238
|
"@hookform/resolvers": "^3.0.0 || ^5.0.0",
|
|
35
239
|
"@radix-ui/react-accordion": "*",
|
|
@@ -138,15 +342,5 @@
|
|
|
138
342
|
"vaul": "^1.1.2",
|
|
139
343
|
"vitest": "^4.1.5",
|
|
140
344
|
"zod": "^3.25.67"
|
|
141
|
-
},
|
|
142
|
-
"scripts": {
|
|
143
|
-
"storybook": "storybook dev -p 6006",
|
|
144
|
-
"build-storybook": "storybook build",
|
|
145
|
-
"build": "tsup",
|
|
146
|
-
"dev": "tsup --watch",
|
|
147
|
-
"typecheck": "tsc --noEmit",
|
|
148
|
-
"test": "vitest run",
|
|
149
|
-
"test:watch": "vitest",
|
|
150
|
-
"clean": "rm -rf dist"
|
|
151
345
|
}
|
|
152
|
-
}
|
|
346
|
+
}
|
|
@@ -10,6 +10,7 @@ const labels: PlaceCardLabels = {
|
|
|
10
10
|
closed: "Closed",
|
|
11
11
|
viewDetails: (name) => `View details for ${name}`,
|
|
12
12
|
priceLevel: (level) => `Price level ${level} of 4`,
|
|
13
|
+
established: (year) => String(year),
|
|
13
14
|
grade: {
|
|
14
15
|
soft: "Worth exploring",
|
|
15
16
|
bands: {
|
|
@@ -35,6 +36,7 @@ const basePlace: PlacePreview = {
|
|
|
35
36
|
price_dishes: { price_range_low: 35, price_range_high: 90, price_score: 2 },
|
|
36
37
|
reviews_digest: { grade: { overall: 8.4 } },
|
|
37
38
|
cover_url: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=800&q=80",
|
|
39
|
+
launched_at: "2020-02",
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
const meta = {
|
|
@@ -101,6 +103,40 @@ export const WithFooter: Story = {
|
|
|
101
103
|
},
|
|
102
104
|
};
|
|
103
105
|
|
|
106
|
+
/**
|
|
107
|
+
* The launch year closes the meta line, bare — fe's copy; `labels.established`
|
|
108
|
+
* may spell it out ("Est. 2020") where there is room. It is the backend's
|
|
109
|
+
* earliest-review estimate (`launched_at`, `YYYY-MM`). No year without the
|
|
110
|
+
* label or without a well-formed `launched_at`.
|
|
111
|
+
*/
|
|
112
|
+
export const Established: Story = {
|
|
113
|
+
args: { place: { ...basePlace, launched_at: "2012-09" } },
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** 220px rail with a long cuisine and full price: the cuisine gives way, price and year never do. */
|
|
117
|
+
export const EstablishedNarrowRail: Story = {
|
|
118
|
+
args: {
|
|
119
|
+
place: { ...basePlace, cuisines: ["mediterranean"], price_dishes: { price_score: 4 } },
|
|
120
|
+
},
|
|
121
|
+
decorators: [
|
|
122
|
+
(Story) => (
|
|
123
|
+
<div className="w-[220px]">
|
|
124
|
+
<Story />
|
|
125
|
+
</div>
|
|
126
|
+
),
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/** Spelled out by the label, for a consumer with room for the word. */
|
|
131
|
+
export const EstablishedWithLabel: Story = {
|
|
132
|
+
args: { labels: { ...labels, established: (year) => `Est. ${year}` } },
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/** The year alone still gets a meta line: no cuisine, no price signal. */
|
|
136
|
+
export const EstablishedOnly: Story = {
|
|
137
|
+
args: { place: { ...basePlace, cuisines: undefined, price_dishes: undefined } },
|
|
138
|
+
};
|
|
139
|
+
|
|
104
140
|
export const Minimal: Story = {
|
|
105
141
|
args: { place: { id: "2", slug: "tiny", name: "The Tiny Spot" } },
|
|
106
142
|
};
|
|
@@ -9,6 +9,7 @@ const LABELS: PlaceCardLabels = {
|
|
|
9
9
|
closed: "Closed",
|
|
10
10
|
viewDetails: (name) => `View details for ${name}`,
|
|
11
11
|
priceLevel: (level) => `Price level ${level} of 4`,
|
|
12
|
+
established: (year) => `Est. ${year}`,
|
|
12
13
|
grade: {
|
|
13
14
|
soft: "Worth exploring",
|
|
14
15
|
bands: {
|
|
@@ -104,6 +105,34 @@ describe("PlaceCard", () => {
|
|
|
104
105
|
expect(screen.getByLabelText("Price level 2 of 4")).toBeInTheDocument();
|
|
105
106
|
});
|
|
106
107
|
|
|
108
|
+
it("closes the meta line with the launch year", () => {
|
|
109
|
+
render(
|
|
110
|
+
<PlaceCard
|
|
111
|
+
place={makePlace({ cuisines: ["italian"], launched_at: "2020-02" })}
|
|
112
|
+
labels={LABELS}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
expect(screen.getByText("Est. 2020")).toBeInTheDocument();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("shows the launch year on its own when there is no cuisine or price", () => {
|
|
119
|
+
const { container } = render(
|
|
120
|
+
<PlaceCard place={makePlace({ launched_at: "2012-09" })} labels={LABELS} />
|
|
121
|
+
);
|
|
122
|
+
expect(screen.getByText("Est. 2012")).toBeInTheDocument();
|
|
123
|
+
expect(container).not.toHaveTextContent("·");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("shows no year without the label or a well-formed launched_at", () => {
|
|
127
|
+
const { established: _omit, ...withoutEstablished } = LABELS;
|
|
128
|
+
const { rerender } = render(
|
|
129
|
+
<PlaceCard place={makePlace({ launched_at: "2020-02" })} labels={withoutEstablished} />
|
|
130
|
+
);
|
|
131
|
+
expect(screen.queryByText(/2020/)).not.toBeInTheDocument();
|
|
132
|
+
rerender(<PlaceCard place={makePlace({ launched_at: "2020" })} labels={LABELS} />);
|
|
133
|
+
expect(screen.queryByText(/Est\./)).not.toBeInTheDocument();
|
|
134
|
+
});
|
|
135
|
+
|
|
107
136
|
it("clamps an out-of-range price level instead of throwing", () => {
|
|
108
137
|
render(<PlaceCard place={makePlace({ price_drinks: { price_score: 7 } })} labels={LABELS} />);
|
|
109
138
|
expect(screen.getByLabelText("Price level 7 of 4")).toHaveTextContent("$$$$");
|
|
@@ -169,6 +169,12 @@ function PriceLevel({ level, label }: { level: number; label: string }) {
|
|
|
169
169
|
);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/** The year out of the schema's `YYYY-MM`; anything else shows no year. */
|
|
173
|
+
function launchYear(launchedAt: string | undefined): number | undefined {
|
|
174
|
+
const match = launchedAt ? /^(\d{4})-\d{2}$/.exec(launchedAt) : null;
|
|
175
|
+
return match ? Number(match[1]) : undefined;
|
|
176
|
+
}
|
|
177
|
+
|
|
172
178
|
const STATIC_ROOT_CLASSES = `group relative flex w-full select-none flex-col ${HOVER_MOTION} ${HOVER_LIFT_GRID_GROUP}`;
|
|
173
179
|
const ROOT_CLASSES = `${STATIC_ROOT_CLASSES} cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:rounded-xl`;
|
|
174
180
|
|
|
@@ -202,6 +208,8 @@ export const PlaceCard = memo(function PlaceCard({
|
|
|
202
208
|
const neighbourhood = place.areas?.[0]?.name;
|
|
203
209
|
const priceLevel = place.price_dishes?.price_score ?? place.price_drinks?.price_score;
|
|
204
210
|
const showPrice = priceLevel !== undefined && priceLevel > 0;
|
|
211
|
+
const year = labels.established ? launchYear(place.launched_at) : undefined;
|
|
212
|
+
const established = year !== undefined ? labels.established?.(year) : undefined;
|
|
205
213
|
const ariaLabel = labels.viewDetails(place.name);
|
|
206
214
|
|
|
207
215
|
const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
|
|
@@ -277,11 +285,22 @@ export const PlaceCard = memo(function PlaceCard({
|
|
|
277
285
|
</div>
|
|
278
286
|
|
|
279
287
|
<div className="flex flex-col gap-0.5">
|
|
280
|
-
{
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
288
|
+
{/* Cuisine · price · launch year. At rail width the three can
|
|
289
|
+
outrun the card, so the cuisine is the part that gives way: the
|
|
290
|
+
price glyphs and the year are short and fixed. */}
|
|
291
|
+
{(cuisine || showPrice || established) && (
|
|
292
|
+
<div className="flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground">
|
|
293
|
+
{cuisine && <span className="min-w-0 truncate capitalize">{cuisine}</span>}
|
|
294
|
+
{cuisine && showPrice && <span className="shrink-0 text-border">·</span>}
|
|
295
|
+
{showPrice && (
|
|
296
|
+
<span className="shrink-0">
|
|
297
|
+
<PriceLevel level={priceLevel} label={labels.priceLevel(priceLevel)} />
|
|
298
|
+
</span>
|
|
299
|
+
)}
|
|
300
|
+
{established && (cuisine || showPrice) && (
|
|
301
|
+
<span className="shrink-0 text-border">·</span>
|
|
302
|
+
)}
|
|
303
|
+
{established && <span className="shrink-0 whitespace-nowrap">{established}</span>}
|
|
285
304
|
</div>
|
|
286
305
|
)}
|
|
287
306
|
|
|
@@ -58,6 +58,12 @@ export type PlacePreview = {
|
|
|
58
58
|
* bare storage key straight to `next/image`'s loader).
|
|
59
59
|
*/
|
|
60
60
|
cover_url?: string
|
|
61
|
+
/**
|
|
62
|
+
* Year and month the place launched (`YYYY-MM`), as the schema ships it.
|
|
63
|
+
* The backend approximates it from the earliest review it holds, so it is a
|
|
64
|
+
* floor rather than a founding date. `<PlaceCard>` shows the year only.
|
|
65
|
+
*/
|
|
66
|
+
launched_at?: string
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
export type PlaceHourBlock = { opens_at: number; closes_at: number }
|
|
@@ -193,6 +199,11 @@ export type PlaceCardLabels = {
|
|
|
193
199
|
viewDetails: (name: string) => string
|
|
194
200
|
/** Accessible name of the `$` price glyphs, e.g. "Price level 2 of 4". */
|
|
195
201
|
priceLevel: (level: number) => string
|
|
202
|
+
/**
|
|
203
|
+
* The launch year on the meta line, e.g. "Est. 2020" / "Din 2020". Optional:
|
|
204
|
+
* without it the card shows no year even when `launched_at` is set.
|
|
205
|
+
*/
|
|
206
|
+
established?: (year: number) => string
|
|
196
207
|
grade: GradeAtomLabels
|
|
197
208
|
}
|
|
198
209
|
|