@bearmenu/ui 0.8.15 → 0.8.17
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/README.md +1 -0
- package/dist/{chunk-ZLPWEHZO.js → chunk-54FQQZZJ.js} +1 -1
- package/dist/{chunk-5FCC5L5F.js → chunk-5LTILG3L.js} +29 -6
- package/dist/{chunk-P43B3ONY.js → chunk-5XREWOBB.js} +3 -1
- package/dist/{chunk-V4E333QS.js → chunk-5YNIQ2BL.js} +7 -29
- package/dist/{chunk-CLLY6XB3.js → chunk-6VOWT4YR.js} +6 -3
- package/dist/{chunk-S33K4RC5.js → chunk-BKKQ7GCZ.js} +1 -1
- package/dist/chunk-QY66ME5B.js +207 -0
- package/dist/chunk-RIH2IS7X.js +29 -0
- package/dist/{chunk-T6S2ZBV6.js → chunk-SDWN6L4O.js} +1 -1
- package/dist/{chunk-CNJQUHCU.js → chunk-SWD6CJ35.js} +2 -1
- package/dist/{chunk-HDR7V6IZ.js → chunk-TIQNM4XD.js} +8 -1
- package/dist/components/alert-dialog.js +3 -2
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/button.d.ts +5 -1
- package/dist/components/button.js +3 -2
- package/dist/components/calendar.js +3 -2
- package/dist/components/carousel.js +3 -2
- package/dist/components/coach-marks.d.ts +63 -0
- package/dist/components/coach-marks.js +162 -0
- package/dist/components/combobox.js +3 -2
- package/dist/components/concept-compare-rail.js +2 -2
- package/dist/components/concept-compare.js +2 -2
- package/dist/components/concept-convergence.js +2 -2
- package/dist/components/date-input.d.ts +1 -1
- package/dist/components/doorway-tiles.d.ts +11 -0
- package/dist/components/doorway-tiles.js +5 -3
- package/dist/components/empty-state.js +3 -2
- package/dist/components/event-detail-panel.js +4 -3
- package/dist/components/event-index-row.js +1 -1
- package/dist/components/event-list-row-wide.js +4 -3
- package/dist/components/event-list-row.js +1 -1
- package/dist/components/facet-group.d.ts +8 -1
- package/dist/components/facet-group.js +3 -0
- package/dist/components/filter-category-row.js +1 -1
- package/dist/components/haptic-switch.d.ts +35 -0
- package/dist/components/haptic-switch.js +3 -0
- package/dist/components/hue-chip.js +1 -1
- package/dist/components/item.js +4 -1
- package/dist/components/kitchen-dossier.js +2 -2
- package/dist/components/link-button.js +3 -2
- package/dist/components/menu-family-block.d.ts +8 -1
- package/dist/components/menu-family-block.js +103 -69
- package/dist/components/menu-family-card.js +2 -2
- package/dist/components/menu-filter-chips.d.ts +12 -3
- package/dist/components/menu-filter-chips.js +22 -4
- package/dist/components/menu-index-list.js +3 -2
- package/dist/components/menu-item.js +3 -3
- package/dist/components/menu-kitchen-list.d.ts +14 -2
- package/dist/components/menu-kitchen-list.js +1 -1
- package/dist/components/multi-select-combobox.js +4 -3
- package/dist/components/pagination.js +3 -2
- package/dist/components/photo-ground.d.ts +7 -1
- package/dist/components/photo-ground.js +1 -1
- package/dist/components/place-card.js +2 -2
- package/dist/components/place-details-mobile.js +1 -1
- package/dist/components/searchable-select.js +3 -2
- package/dist/components/section-header.d.ts +7 -1
- package/dist/components/section-header.js +11 -7
- package/dist/components/signature-chip.js +2 -2
- package/dist/components/sliding-panels.js +3 -2
- package/dist/components/switch.d.ts +7 -1
- package/dist/components/switch.js +33 -24
- package/dist/components/toggle-group.js +9 -4
- package/dist/components/toggle.d.ts +1 -1
- package/dist/components/toggle.js +1 -1
- package/dist/components/top-ranking-entry.js +3 -3
- package/dist/hooks/use-haptic.d.ts +9 -1
- package/dist/hooks/use-haptic.js +1 -1
- package/package.json +1 -1
- package/src/components/badge.stories.tsx +31 -1
- package/src/components/badge.tsx +2 -0
- package/src/components/button.stories.tsx +5 -4
- package/src/components/button.test.tsx +11 -1
- package/src/components/button.tsx +41 -6
- package/src/components/coach-marks.stories.tsx +79 -0
- package/src/components/coach-marks.test.tsx +140 -0
- package/src/components/coach-marks.tsx +252 -0
- package/src/components/doorway-tiles.stories.tsx +34 -0
- package/src/components/doorway-tiles.test.tsx +27 -0
- package/src/components/doorway-tiles.tsx +30 -2
- package/src/components/facet-group.tsx +9 -0
- package/src/components/haptic-switch.test.tsx +134 -0
- package/src/components/haptic-switch.tsx +62 -0
- package/src/components/hue-chip.test.tsx +6 -3
- package/src/components/hue-chip.tsx +5 -2
- package/src/components/item.tsx +4 -1
- package/src/components/menu-family-block.stories.tsx +12 -0
- package/src/components/menu-family-block.tsx +53 -20
- package/src/components/menu-filter-chips.tsx +32 -4
- package/src/components/menu-kitchen-list.tsx +45 -6
- package/src/components/photo-ground.tsx +7 -0
- package/src/components/section-header.test.tsx +15 -0
- package/src/components/section-header.tsx +26 -12
- package/src/components/switch.tsx +29 -18
- package/src/components/toggle-group.stories.tsx +37 -1
- package/src/components/toggle-group.tsx +11 -6
- package/src/components/toggle.tsx +8 -0
- package/src/hooks/use-haptic.test.tsx +54 -59
- package/src/hooks/use-haptic.ts +18 -44
- package/dist/chunk-SXQZ7DLH.js +0 -167
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@ import { Input } from "@bearmenu/ui/components/input";
|
|
|
48
48
|
| Dropdown Menu | `@bearmenu/ui/components/dropdown-menu` |
|
|
49
49
|
| Empty State | `@bearmenu/ui/components/empty-state` |
|
|
50
50
|
| Form | `@bearmenu/ui/components/form` |
|
|
51
|
+
| Haptic Switch | `@bearmenu/ui/components/haptic-switch` |
|
|
51
52
|
| Input | `@bearmenu/ui/components/input` |
|
|
52
53
|
| Input OTP | `@bearmenu/ui/components/input-otp` |
|
|
53
54
|
| Label | `@bearmenu/ui/components/label` |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SnapRail, SnapRailDots } from './chunk-WYTJR2YU.js';
|
|
2
|
-
import { PhotoGround } from './chunk-
|
|
2
|
+
import { PhotoGround } from './chunk-SWD6CJ35.js';
|
|
3
3
|
import { cn } from './chunk-FEK5XGZW.js';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HapticSwitch, isHapticSwitchTarget } from './chunk-RIH2IS7X.js';
|
|
2
|
+
import { useHaptic } from './chunk-5YNIQ2BL.js';
|
|
2
3
|
import { cn } from './chunk-FEK5XGZW.js';
|
|
3
4
|
import { __objRest, __spreadProps, __spreadValues } from './chunk-MMUBH76A.js';
|
|
4
5
|
import * as React from 'react';
|
|
@@ -14,9 +15,19 @@ var buttonVariants = cva(
|
|
|
14
15
|
variant: {
|
|
15
16
|
default: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 hover:shadow",
|
|
16
17
|
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/85 hover:shadow",
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* The quiet variants tint on hover, they do not invert. `--accent`
|
|
20
|
+
* here is the brand brown, so the shadcn default (`hover:bg-accent`,
|
|
21
|
+
* written for an accent that is a pale grey) turned every outline,
|
|
22
|
+
* ghost and secondary button into a filled brown block the moment the
|
|
23
|
+
* pointer touched it — a second primary on every row. The standard
|
|
24
|
+
* (shadcn on its own tokens, Radix, Polaris, Material) is a step of the
|
|
25
|
+
* surface: the fill darkens a notch, the border firms up, the type
|
|
26
|
+
* stays put. `--accent-subtle` is that step; `active:` goes one more.
|
|
27
|
+
*/
|
|
28
|
+
outline: "border border-border bg-background hover:bg-accent-subtle hover:border-accent/40 active:bg-muted",
|
|
29
|
+
secondary: "bg-secondary text-secondary-foreground shadow-sm border border-border hover:bg-accent-subtle hover:border-accent/40 active:bg-muted",
|
|
30
|
+
ghost: "hover:bg-accent-subtle active:bg-muted",
|
|
20
31
|
link: "text-primary underline-offset-4 hover:underline",
|
|
21
32
|
glass: "glass glass-hover text-foreground",
|
|
22
33
|
/**
|
|
@@ -44,7 +55,8 @@ var buttonVariants = cva(
|
|
|
44
55
|
*/
|
|
45
56
|
ink: "border border-white/20 bg-white/[0.16] text-white hover:bg-white/25 focus-visible:ring-white/70 focus-visible:ring-offset-0",
|
|
46
57
|
brown: "bg-brown text-brown-foreground shadow-sm hover:bg-brown/85 hover:shadow",
|
|
47
|
-
|
|
58
|
+
/** An ON state that is not the primary — a toggled mode, a guide running. */
|
|
59
|
+
warning: "bg-warning text-warning-foreground shadow-sm hover:bg-warning/90 hover:shadow"
|
|
48
60
|
},
|
|
49
61
|
size: {
|
|
50
62
|
xs: "h-8 rounded-lg px-3 text-xs [&_svg]:size-3.5",
|
|
@@ -82,6 +94,7 @@ var Button = React.forwardRef(
|
|
|
82
94
|
hapticStyle = "light",
|
|
83
95
|
isLoading = false,
|
|
84
96
|
disabled,
|
|
97
|
+
type,
|
|
85
98
|
onClick,
|
|
86
99
|
children
|
|
87
100
|
} = _b, props = __objRest(_b, [
|
|
@@ -94,27 +107,34 @@ var Button = React.forwardRef(
|
|
|
94
107
|
"hapticStyle",
|
|
95
108
|
"isLoading",
|
|
96
109
|
"disabled",
|
|
110
|
+
"type",
|
|
97
111
|
"onClick",
|
|
98
112
|
"children"
|
|
99
113
|
]);
|
|
100
114
|
const Comp = asChild ? Slot : "button";
|
|
101
115
|
const { trigger } = useHaptic();
|
|
102
116
|
const [isMounted, setIsMounted] = React.useState(false);
|
|
117
|
+
const withSwitch = haptic && !asChild;
|
|
103
118
|
React.useEffect(() => {
|
|
104
119
|
setIsMounted(true);
|
|
105
120
|
}, []);
|
|
106
121
|
const handleClick = (event) => {
|
|
122
|
+
var _a2;
|
|
107
123
|
if (haptic && isMounted) {
|
|
108
124
|
trigger(hapticStyle);
|
|
109
125
|
}
|
|
110
126
|
onClick == null ? void 0 : onClick(event);
|
|
127
|
+
if (withSwitch && type === "submit" && !event.defaultPrevented && isHapticSwitchTarget(event.target)) {
|
|
128
|
+
(_a2 = event.currentTarget.form) == null ? void 0 : _a2.requestSubmit(event.currentTarget);
|
|
129
|
+
}
|
|
111
130
|
};
|
|
112
131
|
return /* @__PURE__ */ jsx(
|
|
113
132
|
Comp,
|
|
114
133
|
__spreadProps(__spreadValues({
|
|
115
134
|
"data-slot": "button",
|
|
116
|
-
className: cn(buttonVariants({ variant, size, fullWidth, className })),
|
|
135
|
+
className: cn(buttonVariants({ variant, size, fullWidth, className }), withSwitch && "relative"),
|
|
117
136
|
ref,
|
|
137
|
+
type,
|
|
118
138
|
onClick: handleClick,
|
|
119
139
|
disabled: disabled || isLoading,
|
|
120
140
|
"aria-busy": isLoading || void 0
|
|
@@ -122,6 +142,9 @@ var Button = React.forwardRef(
|
|
|
122
142
|
children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
123
143
|
/* @__PURE__ */ jsx(Loader2, { className: "animate-spin", "aria-hidden": "true" }),
|
|
124
144
|
!asChild && children
|
|
145
|
+
] }) : withSwitch ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
146
|
+
children,
|
|
147
|
+
/* @__PURE__ */ jsx(HapticSwitch, { disabled })
|
|
125
148
|
] }) : children
|
|
126
149
|
})
|
|
127
150
|
);
|
|
@@ -16,7 +16,9 @@ var badgeVariants = cva(
|
|
|
16
16
|
glass: "glass text-foreground border-border/50",
|
|
17
17
|
success: "border-transparent bg-success text-success-foreground",
|
|
18
18
|
warning: "border-transparent bg-warning text-warning-foreground",
|
|
19
|
-
brown: "border-transparent bg-brown text-brown-foreground"
|
|
19
|
+
brown: "border-transparent bg-brown text-brown-foreground",
|
|
20
|
+
/** A count sitting INSIDE a primary-filled control: its own ink, lifted a notch. */
|
|
21
|
+
onPrimary: "border-transparent bg-primary-foreground/20 text-primary-foreground"
|
|
20
22
|
},
|
|
21
23
|
// v1.0 motion: "live" surface signal — applies `live-pulse` archetype.
|
|
22
24
|
// Pair with a colored variant (e.g. variant="destructive" live).
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
|
|
3
|
+
function isIos() {
|
|
4
|
+
if (typeof navigator === "undefined") return false;
|
|
5
|
+
return /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
|
|
6
|
+
}
|
|
3
7
|
var useHaptic = () => {
|
|
4
8
|
const [platform, setPlatform] = useState("unknown");
|
|
5
9
|
const [isSupported, setIsSupported] = useState(false);
|
|
6
10
|
useEffect(() => {
|
|
7
11
|
var _a, _b;
|
|
8
12
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
9
|
-
const isIOS = /iphone|ipad|ipod/.test(userAgent);
|
|
10
13
|
const isAndroid = /android/.test(userAgent);
|
|
11
|
-
if (
|
|
14
|
+
if (isIos()) {
|
|
12
15
|
setPlatform("ios");
|
|
13
16
|
} else if (isAndroid) {
|
|
14
17
|
setPlatform("android");
|
|
@@ -17,7 +20,7 @@ var useHaptic = () => {
|
|
|
17
20
|
}
|
|
18
21
|
const hasVibration = "vibrate" in navigator;
|
|
19
22
|
const hasTelegram = typeof window !== "undefined" && ((_b = (_a = window.Telegram) == null ? void 0 : _a.WebApp) == null ? void 0 : _b.HapticFeedback);
|
|
20
|
-
setIsSupported(hasVibration || !!hasTelegram ||
|
|
23
|
+
setIsSupported(hasVibration || !!hasTelegram || isIos());
|
|
21
24
|
}, []);
|
|
22
25
|
const trigger = useCallback((style) => {
|
|
23
26
|
var _a, _b;
|
|
@@ -29,10 +32,6 @@ var useHaptic = () => {
|
|
|
29
32
|
triggerVibrationAPI(style);
|
|
30
33
|
return;
|
|
31
34
|
}
|
|
32
|
-
if (supportsSwitchHaptic()) {
|
|
33
|
-
triggerSwitchHaptic();
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
35
|
console.debug("Haptic feedback not supported on this device");
|
|
37
36
|
}, []);
|
|
38
37
|
return { isSupported, platform, trigger };
|
|
@@ -78,26 +77,5 @@ function triggerVibrationAPI(style) {
|
|
|
78
77
|
};
|
|
79
78
|
navigator.vibrate(patterns[style]);
|
|
80
79
|
}
|
|
81
|
-
var hapticSwitch = null;
|
|
82
|
-
function supportsSwitchHaptic() {
|
|
83
|
-
return typeof HTMLInputElement !== "undefined" && "switch" in HTMLInputElement.prototype;
|
|
84
|
-
}
|
|
85
|
-
function getHapticSwitch() {
|
|
86
|
-
if (hapticSwitch == null ? void 0 : hapticSwitch.isConnected) return hapticSwitch;
|
|
87
|
-
const label = document.createElement("label");
|
|
88
|
-
const input = document.createElement("input");
|
|
89
|
-
input.type = "checkbox";
|
|
90
|
-
input.setAttribute("switch", "");
|
|
91
|
-
input.tabIndex = -1;
|
|
92
|
-
label.setAttribute("aria-hidden", "true");
|
|
93
|
-
label.style.cssText = "position:fixed;top:0;left:0;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none";
|
|
94
|
-
label.appendChild(input);
|
|
95
|
-
document.body.appendChild(label);
|
|
96
|
-
hapticSwitch = label;
|
|
97
|
-
return label;
|
|
98
|
-
}
|
|
99
|
-
function triggerSwitchHaptic() {
|
|
100
|
-
getHapticSwitch().click();
|
|
101
|
-
}
|
|
102
80
|
|
|
103
|
-
export { useHaptic };
|
|
81
|
+
export { isIos, useHaptic };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cn } from './chunk-FEK5XGZW.js';
|
|
2
|
-
import { jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
var STRENGTH = {
|
|
5
5
|
/** A labelled facet: readable at 11.5px, still clearly the family's colour. */
|
|
@@ -25,14 +25,17 @@ function HueChip({
|
|
|
25
25
|
{
|
|
26
26
|
"aria-label": ariaLabel,
|
|
27
27
|
className: cn(
|
|
28
|
-
|
|
28
|
+
// One line, and never wider than its row: a chip that wraps grows past
|
|
29
|
+
// its 28px and paints over its neighbours; a chip that overflows pushes
|
|
30
|
+
// the row. The label ellipsises instead.
|
|
31
|
+
"inline-flex h-7 min-w-0 max-w-full items-center gap-1.5 whitespace-nowrap rounded-full px-2.5 text-[11.5px] text-foreground",
|
|
29
32
|
!hue && "bg-muted",
|
|
30
33
|
className
|
|
31
34
|
),
|
|
32
35
|
style: hue ? { backgroundColor: hueTint(hue, tone, base) } : void 0,
|
|
33
36
|
children: [
|
|
34
37
|
icon,
|
|
35
|
-
children
|
|
38
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children })
|
|
36
39
|
]
|
|
37
40
|
}
|
|
38
41
|
);
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { Separator } from './chunk-NWZGSLPU.js';
|
|
2
|
+
import { Skeleton } from './chunk-XBBEX4SF.js';
|
|
3
|
+
import { cn } from './chunk-FEK5XGZW.js';
|
|
4
|
+
import { __objRest, __spreadValues } from './chunk-MMUBH76A.js';
|
|
5
|
+
import { Star, Clock, Check, MessageSquare, ChevronRight } from 'lucide-react';
|
|
6
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function DefaultLink(_a) {
|
|
9
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
10
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
11
|
+
}
|
|
12
|
+
function MenuKitchenList({
|
|
13
|
+
kitchens,
|
|
14
|
+
labels,
|
|
15
|
+
onShowMore,
|
|
16
|
+
layout = "wide",
|
|
17
|
+
linkComponent = DefaultLink,
|
|
18
|
+
className
|
|
19
|
+
}) {
|
|
20
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
|
|
21
|
+
/* @__PURE__ */ jsx("ol", { children: kitchens.map((kitchen, index) => /* @__PURE__ */ jsxs("li", { className: "[&:first-child>div]:pt-0", children: [
|
|
22
|
+
index > 0 && /* @__PURE__ */ jsx(Separator, {}),
|
|
23
|
+
/* @__PURE__ */ jsx(
|
|
24
|
+
KitchenRow,
|
|
25
|
+
{
|
|
26
|
+
kitchen,
|
|
27
|
+
labels,
|
|
28
|
+
layout,
|
|
29
|
+
linkComponent
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
] }, kitchen.place.slug)) }),
|
|
33
|
+
onShowMore && labels.showMore && /* @__PURE__ */ jsx(
|
|
34
|
+
"button",
|
|
35
|
+
{
|
|
36
|
+
type: "button",
|
|
37
|
+
onClick: onShowMore,
|
|
38
|
+
className: "mt-2 self-start rounded-md py-2 text-[13px] font-bold text-accent transition-colors hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
|
|
39
|
+
children: labels.showMore
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
] });
|
|
43
|
+
}
|
|
44
|
+
function KitchenRow({
|
|
45
|
+
kitchen,
|
|
46
|
+
labels,
|
|
47
|
+
layout,
|
|
48
|
+
linkComponent: LinkComponent
|
|
49
|
+
}) {
|
|
50
|
+
const { place, dish } = kitchen;
|
|
51
|
+
const openNow = place.open_now === true;
|
|
52
|
+
const wide = layout === "wide";
|
|
53
|
+
return /* @__PURE__ */ jsxs(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
className: cn(
|
|
57
|
+
"relative flex flex-wrap items-center gap-[11px] py-3 transition-colors hover:bg-muted/30 motion-reduce:transition-none lg:gap-4 lg:py-3.5",
|
|
58
|
+
wide && "lg:flex-nowrap"
|
|
59
|
+
),
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsx(
|
|
62
|
+
LinkComponent,
|
|
63
|
+
{
|
|
64
|
+
href: kitchen.href,
|
|
65
|
+
"aria-label": labels.openVenue(place.name),
|
|
66
|
+
className: "absolute inset-0 z-[1] rounded-xl focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
/* @__PURE__ */ jsxs("span", { className: "relative h-[46px] w-[46px] shrink-0 overflow-hidden rounded-[11px] bg-[repeating-linear-gradient(135deg,oklch(0.78_0.018_55)_0_8px,oklch(0.84_0.012_58)_8px_16px)] lg:h-[58px] lg:w-[58px] lg:rounded-[13px]", children: [
|
|
70
|
+
place.thumbnail_url && // eslint-disable-next-line @next/next/no-img-element
|
|
71
|
+
/* @__PURE__ */ jsx(
|
|
72
|
+
"img",
|
|
73
|
+
{
|
|
74
|
+
src: place.thumbnail_url,
|
|
75
|
+
alt: "",
|
|
76
|
+
loading: "lazy",
|
|
77
|
+
decoding: "async",
|
|
78
|
+
className: "h-full w-full object-cover"
|
|
79
|
+
}
|
|
80
|
+
),
|
|
81
|
+
/* @__PURE__ */ jsx(
|
|
82
|
+
"span",
|
|
83
|
+
{
|
|
84
|
+
title: openNow ? labels.openNow : void 0,
|
|
85
|
+
className: cn(
|
|
86
|
+
"absolute right-[5px] top-[5px] h-[7px] w-[7px] rounded-full ring-2 ring-white/85",
|
|
87
|
+
openNow ? "bg-[var(--free,oklch(0.55_0.13_155))]" : "bg-[oklch(0.8_0.01_60)]"
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
] }),
|
|
92
|
+
/* @__PURE__ */ jsxs(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
className: cn(
|
|
96
|
+
"flex min-w-0 flex-1 flex-col gap-0.5",
|
|
97
|
+
wide && "lg:w-[226px] lg:flex-none"
|
|
98
|
+
),
|
|
99
|
+
children: [
|
|
100
|
+
/* @__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
|
+
/* @__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
|
+
] }),
|
|
107
|
+
kitchen.metaLabel
|
|
108
|
+
] }),
|
|
109
|
+
kitchen.hoursLabel && /* @__PURE__ */ jsxs(
|
|
110
|
+
"span",
|
|
111
|
+
{
|
|
112
|
+
className: cn(
|
|
113
|
+
"mt-[3px] inline-flex h-[21px] w-max items-center gap-[5px] rounded-md px-2 text-[10.5px] font-bold",
|
|
114
|
+
openNow ? "bg-[oklch(0.96_0.02_155)] text-[var(--free,oklch(0.45_0.13_155))]" : "bg-muted text-muted-foreground"
|
|
115
|
+
),
|
|
116
|
+
children: [
|
|
117
|
+
/* @__PURE__ */ jsx(Clock, { className: "h-2.5 w-2.5 shrink-0", "aria-hidden": true }),
|
|
118
|
+
kitchen.hoursLabel
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
dish && /* DESKTOP ONLY, where it is a column of its own beside the venue.
|
|
126
|
+
On the phone it wrapped to a third line under the row — kicker, dish,
|
|
127
|
+
mention count — which turned a four-line venue row into a seven-line
|
|
128
|
+
block and, ten rows down, buried the venue names the list exists to
|
|
129
|
+
be scanned for. The dish is not what a reader is choosing here; it is
|
|
130
|
+
one tap away on the venue's own page, with room to state it.
|
|
131
|
+
|
|
132
|
+
`order-5` puts it last in the visual order without moving it in the
|
|
133
|
+
DOM, where it belongs to the venue. In the `stacked` layout it is a
|
|
134
|
+
second LINE under the venue, indented past the thumbnail. */
|
|
135
|
+
/* @__PURE__ */ jsxs(
|
|
136
|
+
"div",
|
|
137
|
+
{
|
|
138
|
+
className: cn(
|
|
139
|
+
"hidden min-w-0 basis-full pl-[57px]",
|
|
140
|
+
layout !== "phone" && "lg:block",
|
|
141
|
+
wide ? "lg:order-none lg:flex-1 lg:basis-auto lg:pl-0" : "lg:order-5 lg:pl-[74px]"
|
|
142
|
+
),
|
|
143
|
+
children: [
|
|
144
|
+
/* @__PURE__ */ jsxs(
|
|
145
|
+
"span",
|
|
146
|
+
{
|
|
147
|
+
className: cn(
|
|
148
|
+
"flex items-center gap-1.5 text-[9px] font-extrabold uppercase tracking-[0.1em]",
|
|
149
|
+
dish.matched ? "text-accent" : "text-[oklch(0.72_0.01_60)]"
|
|
150
|
+
),
|
|
151
|
+
children: [
|
|
152
|
+
dish.kicker,
|
|
153
|
+
dish.matched && dish.matchLabel && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-[5px] bg-accent-subtle px-1.5 py-px text-[10.5px] font-bold normal-case tracking-normal text-accent", children: [
|
|
154
|
+
/* @__PURE__ */ jsx(Check, { className: "h-2.5 w-2.5 shrink-0", "aria-hidden": true }),
|
|
155
|
+
dish.matchLabel
|
|
156
|
+
] })
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
),
|
|
160
|
+
dish.href ? /* @__PURE__ */ jsx(
|
|
161
|
+
LinkComponent,
|
|
162
|
+
{
|
|
163
|
+
href: dish.href,
|
|
164
|
+
"aria-label": labels.openDish(dish.name),
|
|
165
|
+
className: "relative z-[2] mt-0.5 block truncate font-serif text-[13.5px] italic underline decoration-[oklch(0.84_0.01_60)] decoration-1 underline-offset-[3px] transition-colors hover:text-accent hover:decoration-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none lg:text-[15px]",
|
|
166
|
+
children: dish.name
|
|
167
|
+
}
|
|
168
|
+
) : /* @__PURE__ */ jsx("span", { className: "mt-0.5 block truncate font-serif text-[13.5px] italic lg:text-[15px]", children: dish.name }),
|
|
169
|
+
dish.meta && /* @__PURE__ */ jsxs("span", { className: "mt-[3px] flex items-center gap-1 text-[11.5px] text-muted-foreground", children: [
|
|
170
|
+
!dish.matched && /* @__PURE__ */ jsx(MessageSquare, { className: "h-2.5 w-2.5 shrink-0", "aria-hidden": true }),
|
|
171
|
+
dish.meta
|
|
172
|
+
] })
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
),
|
|
176
|
+
/* @__PURE__ */ jsxs("div", { className: cn("ml-auto flex shrink-0 items-center gap-[11px]", wide && "lg:ml-0"), children: [
|
|
177
|
+
kitchen.priceLabel && /* @__PURE__ */ jsx("span", { className: "text-base font-extrabold tabular-nums text-accent", children: kitchen.priceLabel }),
|
|
178
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "h-[15px] w-[15px] shrink-0 text-muted-foreground", "aria-hidden": true })
|
|
179
|
+
] })
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
function MenuKitchenListSkeleton({
|
|
185
|
+
rows = 10,
|
|
186
|
+
className
|
|
187
|
+
}) {
|
|
188
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col", className), "aria-hidden": true, children: Array.from({ length: rows }, (_, index) => /* @__PURE__ */ jsxs("div", { children: [
|
|
189
|
+
index > 0 && /* @__PURE__ */ jsx(Separator, {}),
|
|
190
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-[11px] py-3 lg:flex-nowrap lg:gap-4 lg:py-3.5", children: [
|
|
191
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-[46px] w-[46px] rounded-[11px] lg:h-[58px] lg:w-[58px] lg:rounded-[13px]" }),
|
|
192
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5 lg:w-[226px] lg:flex-none", children: [
|
|
193
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-[60%]" }),
|
|
194
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-[45%]" }),
|
|
195
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-[21px] w-[120px] rounded-md" })
|
|
196
|
+
] }),
|
|
197
|
+
/* @__PURE__ */ jsxs("div", { className: "hidden min-w-0 basis-full pl-[57px] lg:order-none lg:block lg:flex-1 lg:basis-auto lg:pl-0", children: [
|
|
198
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-2.5 w-[90px]" }),
|
|
199
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-4 w-[70%]" }),
|
|
200
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-3 w-[40%]" })
|
|
201
|
+
] }),
|
|
202
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "ml-auto h-5 w-[56px] lg:ml-0" })
|
|
203
|
+
] })
|
|
204
|
+
] }, index)) });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export { MenuKitchenList, MenuKitchenListSkeleton };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { isIos } from './chunk-5YNIQ2BL.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var HAPTIC_SWITCH_SLOT = "haptic-switch";
|
|
6
|
+
function isHapticSwitchTarget(target) {
|
|
7
|
+
return target instanceof HTMLElement && target.dataset.slot === HAPTIC_SWITCH_SLOT;
|
|
8
|
+
}
|
|
9
|
+
function HapticSwitch({ disabled = false }) {
|
|
10
|
+
const [enabled, setEnabled] = React.useState(false);
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
setEnabled(isIos());
|
|
13
|
+
}, []);
|
|
14
|
+
if (!enabled) return null;
|
|
15
|
+
return /* @__PURE__ */ jsx(
|
|
16
|
+
"input",
|
|
17
|
+
{
|
|
18
|
+
type: "checkbox",
|
|
19
|
+
ref: (el) => el == null ? void 0 : el.setAttribute("switch", ""),
|
|
20
|
+
"data-slot": HAPTIC_SWITCH_SLOT,
|
|
21
|
+
"aria-hidden": "true",
|
|
22
|
+
tabIndex: -1,
|
|
23
|
+
disabled,
|
|
24
|
+
className: "absolute inset-0 m-0 h-full w-full cursor-[inherit] rounded-[inherit] opacity-0 touch-manipulation [-webkit-tap-highlight-color:transparent]"
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { HAPTIC_SWITCH_SLOT, HapticSwitch, isHapticSwitchTarget };
|
|
@@ -2,6 +2,7 @@ import { cn } from './chunk-FEK5XGZW.js';
|
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
var PHOTO_GROUND = "bg-[oklch(0.21_0.012_55)]";
|
|
5
|
+
var PHOTO_GLYPH_SHADOW = "drop-shadow-[0_2px_6px_oklch(0.16_0.012_55_/_0.6)]";
|
|
5
6
|
var OVERLAY = "absolute inset-0 bg-[oklch(0.16_0.012_55_/_0.5)]";
|
|
6
7
|
function PhotoGround({
|
|
7
8
|
photos,
|
|
@@ -79,4 +80,4 @@ function PhotoGround({
|
|
|
79
80
|
] });
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
export { PHOTO_GROUND, PhotoGround };
|
|
83
|
+
export { PHOTO_GLYPH_SHADOW, PHOTO_GROUND, PhotoGround };
|
|
@@ -10,7 +10,14 @@ var toggleVariants = cva(
|
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
12
12
|
default: "bg-transparent",
|
|
13
|
-
outline: "border border-border bg-transparent hover:bg-accent/50 hover:text-accent-foreground data-[state=on]:border-primary"
|
|
13
|
+
outline: "border border-border bg-transparent hover:bg-accent/50 hover:text-accent-foreground data-[state=on]:border-primary",
|
|
14
|
+
/**
|
|
15
|
+
* One cell of a segmented control: a raised card on the group's
|
|
16
|
+
* muted capsule when on, quiet type when off. The group
|
|
17
|
+
* (`ToggleGroup variant="segmented"`) supplies the capsule; the cell
|
|
18
|
+
* never fills with the accent, which would read as a primary button.
|
|
19
|
+
*/
|
|
20
|
+
segmented: "h-10 w-11 rounded-full text-muted-foreground hover:bg-transparent hover:text-foreground data-[state=on]:bg-card data-[state=on]:text-foreground data-[state=on]:shadow-[var(--shadow-card)]"
|
|
14
21
|
},
|
|
15
22
|
size: {
|
|
16
23
|
default: "h-9 px-2 min-w-9",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { buttonVariants } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
import { buttonVariants } from '../chunk-5LTILG3L.js';
|
|
2
|
+
import '../chunk-RIH2IS7X.js';
|
|
3
|
+
import '../chunk-5YNIQ2BL.js';
|
|
3
4
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
4
5
|
import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
|
|
5
6
|
import * as React from 'react';
|
|
@@ -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?: "default" | "destructive" | "warning" | "success" | "outline" | "secondary" | "glass" | "overlay" | "brown" | null | undefined;
|
|
7
|
+
variant?: "default" | "destructive" | "warning" | "success" | "outline" | "secondary" | "glass" | "overlay" | "brown" | "onPrimary" | null | undefined;
|
|
8
8
|
live?: boolean | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
package/dist/components/badge.js
CHANGED
|
@@ -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?: "default" | "destructive" | "link" | "outline" | "secondary" | "ghost" | "glass" | "overlay" | "ink" | "brown" |
|
|
6
|
+
variant?: "default" | "destructive" | "warning" | "link" | "outline" | "secondary" | "ghost" | "glass" | "overlay" | "ink" | "brown" | null | undefined;
|
|
7
7
|
size?: "default" | "xs" | "sm" | "lg" | "pill" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
|
|
8
8
|
fullWidth?: boolean | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
@@ -12,6 +12,10 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, Var
|
|
|
12
12
|
/**
|
|
13
13
|
* Off by default. Haptics are for actions that change state — a save, a
|
|
14
14
|
* toggle, a destructive confirm — not for every button, so each site opts in.
|
|
15
|
+
*
|
|
16
|
+
* Android vibrates through `useHaptic`; iOS ticks through a `HapticSwitch`
|
|
17
|
+
* laid over the button, which needs a real element, so `asChild` gets the
|
|
18
|
+
* vibration only.
|
|
15
19
|
*/
|
|
16
20
|
haptic?: boolean;
|
|
17
21
|
hapticStyle?: "light" | "medium" | "heavy";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { Button, buttonVariants } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { Button, buttonVariants } from '../chunk-5LTILG3L.js';
|
|
2
|
+
import '../chunk-RIH2IS7X.js';
|
|
3
|
+
import '../chunk-5YNIQ2BL.js';
|
|
3
4
|
import '../chunk-FEK5XGZW.js';
|
|
4
5
|
import '../chunk-MMUBH76A.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { buttonVariants, Button } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
import { buttonVariants, Button } from '../chunk-5LTILG3L.js';
|
|
2
|
+
import '../chunk-RIH2IS7X.js';
|
|
3
|
+
import '../chunk-5YNIQ2BL.js';
|
|
3
4
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
4
5
|
import { __objRest, __spreadValues, __spreadProps } from '../chunk-MMUBH76A.js';
|
|
5
6
|
import * as React from 'react';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Button } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
import { Button } from '../chunk-5LTILG3L.js';
|
|
2
|
+
import '../chunk-RIH2IS7X.js';
|
|
3
|
+
import '../chunk-5YNIQ2BL.js';
|
|
3
4
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
4
5
|
import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
|
|
5
6
|
import * as React from 'react';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CoachMarks — a guided tour drawn OVER the real page.
|
|
5
|
+
*
|
|
6
|
+
* Four things, one at a time: the page dims, the current stop's target is cut
|
|
7
|
+
* out of the dimming in a spotlight, its step number sits on the spotlight's
|
|
8
|
+
* corner, and one callout beside the spotlight says what the target is. Only
|
|
9
|
+
* the current stop is marked — a map of every stop's number over the page
|
|
10
|
+
* competes with the page's own numerals and with the callout's "Step N of
|
|
11
|
+
* M"; the way through is Next, Back and the arrow keys. The reader learns
|
|
12
|
+
* the surface by looking at the surface, not at a picture of it; nothing on
|
|
13
|
+
* the page moves or re-renders to be taught.
|
|
14
|
+
*
|
|
15
|
+
* Targets are found by `data-coach-mark="<stop id>"` on the page's own
|
|
16
|
+
* elements, so the page keeps its markup and the tour attaches from outside.
|
|
17
|
+
* The tour is built from the stops whose target is ON THE PAGE when it opens
|
|
18
|
+
* (or when the itinerary changes): a stop with nothing to point at is not
|
|
19
|
+
* counted, so "Step 3 of 5" is always a step the reader will reach. Where
|
|
20
|
+
* the same id is on two elements — a desktop and a phone rendering of one
|
|
21
|
+
* control — the visible one is taken.
|
|
22
|
+
*
|
|
23
|
+
* The consumer owns the words (`labels`, `stops`) and the open state; the
|
|
24
|
+
* tour owns its position, the geometry (8px breathing room, the callout's
|
|
25
|
+
* side, re-measure on scroll and resize) and the keys (Escape closes, the
|
|
26
|
+
* arrows step). `onProgress` reports the current stop for a toolbar label.
|
|
27
|
+
*
|
|
28
|
+
* Rendered into `document.body`: a page wrapper with an opacity or transform
|
|
29
|
+
* transition is a stacking context, and the tour must sit above everything
|
|
30
|
+
* the page and its chrome draw, whatever wraps the consumer.
|
|
31
|
+
*/
|
|
32
|
+
type CoachMarkStop = {
|
|
33
|
+
/** Matches the target's `data-coach-mark` attribute. */
|
|
34
|
+
id: string;
|
|
35
|
+
title: string;
|
|
36
|
+
body: string;
|
|
37
|
+
};
|
|
38
|
+
type CoachMarksLabels = {
|
|
39
|
+
/** "Step 2 of 4". */
|
|
40
|
+
progress: (step: number, total: number) => string;
|
|
41
|
+
back: string;
|
|
42
|
+
next: string;
|
|
43
|
+
/** The last stop's forward action, e.g. "Done". */
|
|
44
|
+
done: string;
|
|
45
|
+
skip: string;
|
|
46
|
+
};
|
|
47
|
+
type CoachMarksProps = {
|
|
48
|
+
open: boolean;
|
|
49
|
+
stops: CoachMarkStop[];
|
|
50
|
+
/** Skip, Done, Escape and a tap on the dimmed page all end here. */
|
|
51
|
+
onClose: () => void;
|
|
52
|
+
/**
|
|
53
|
+
* 1-based position, the count of stops the page actually has, and the
|
|
54
|
+
* stop being shown — its id is what a consumer remembers, since the
|
|
55
|
+
* position shifts when a stop's target is missing.
|
|
56
|
+
*/
|
|
57
|
+
onProgress?: (step: number, total: number, stop: CoachMarkStop) => void;
|
|
58
|
+
labels: CoachMarksLabels;
|
|
59
|
+
className?: string;
|
|
60
|
+
};
|
|
61
|
+
declare function CoachMarks({ open, stops, onClose, onProgress, labels, className }: CoachMarksProps): React.ReactPortal | null;
|
|
62
|
+
|
|
63
|
+
export { type CoachMarkStop, CoachMarks, type CoachMarksLabels, type CoachMarksProps };
|