@bearmenu/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2CUFS5LF.js +80 -0
- package/dist/chunk-3GWWZKX7.js +33 -0
- package/dist/chunk-3VMDCCCX.js +10 -0
- package/dist/chunk-4AVS6CV4.js +89 -0
- package/dist/chunk-4JOV7FXW.js +17 -0
- package/dist/chunk-4KOMHDZD.js +89 -0
- package/dist/chunk-4UO73SKC.js +26 -0
- package/dist/chunk-7H54PTVH.js +116 -0
- package/dist/chunk-CW24PLDW.js +24 -0
- package/dist/chunk-EBAMCZDW.js +112 -0
- package/dist/chunk-ERMIA7NP.js +106 -0
- package/dist/chunk-ESO2EM3L.js +46 -0
- package/dist/chunk-EUHEIWHZ.js +32 -0
- package/dist/chunk-FEK5XGZW.js +8 -0
- package/dist/chunk-NKHH23YK.js +31 -0
- package/dist/chunk-R47NEOSI.js +78 -0
- package/dist/chunk-UXAYXRFN.js +69 -0
- package/dist/chunk-X6GATUI2.js +19 -0
- package/dist/components/accordion.d.ts +9 -0
- package/dist/components/accordion.js +53 -0
- package/dist/components/alert-dialog.d.ts +23 -0
- package/dist/components/alert-dialog.js +98 -0
- package/dist/components/alert.d.ts +11 -0
- package/dist/components/alert.js +52 -0
- package/dist/components/aspect-ratio.d.ts +6 -0
- package/dist/components/aspect-ratio.js +2 -0
- package/dist/components/avatar.d.ts +8 -0
- package/dist/components/avatar.js +47 -0
- package/dist/components/badge.d.ts +13 -0
- package/dist/components/badge.js +3 -0
- package/dist/components/breadcrumb.d.ts +14 -0
- package/dist/components/breadcrumb.js +96 -0
- package/dist/components/button.d.ts +16 -0
- package/dist/components/button.js +4 -0
- package/dist/components/calendar.d.ts +11 -0
- package/dist/components/calendar.js +56 -0
- package/dist/components/card.d.ts +13 -0
- package/dist/components/card.js +4 -0
- package/dist/components/carousel.d.ts +24 -0
- package/dist/components/carousel.js +198 -0
- package/dist/components/checkbox.d.ts +6 -0
- package/dist/components/checkbox.js +25 -0
- package/dist/components/collapsible.d.ts +8 -0
- package/dist/components/collapsible.js +26 -0
- package/dist/components/command.d.ts +84 -0
- package/dist/components/command.js +4 -0
- package/dist/components/currency-input.d.ts +17 -0
- package/dist/components/currency-input.js +121 -0
- package/dist/components/dialog.d.ts +22 -0
- package/dist/components/dialog.js +3 -0
- package/dist/components/drawer.d.ts +27 -0
- package/dist/components/drawer.js +85 -0
- package/dist/components/dropdown-menu.d.ts +28 -0
- package/dist/components/dropdown-menu.js +211 -0
- package/dist/components/empty-state.d.ts +22 -0
- package/dist/components/empty-state.js +81 -0
- package/dist/components/form.d.ts +28 -0
- package/dist/components/form.js +97 -0
- package/dist/components/haptic-button.d.ts +12 -0
- package/dist/components/haptic-button.js +17 -0
- package/dist/components/input-otp.d.ts +14 -0
- package/dist/components/input-otp.js +62 -0
- package/dist/components/input.d.ts +6 -0
- package/dist/components/input.js +3 -0
- package/dist/components/label.d.ts +8 -0
- package/dist/components/label.js +3 -0
- package/dist/components/lazy-markdown.d.ts +11 -0
- package/dist/components/lazy-markdown.js +13 -0
- package/dist/components/link-button.d.ts +30 -0
- package/dist/components/link-button.js +17 -0
- package/dist/components/markdown-renderer.d.ts +11 -0
- package/dist/components/markdown-renderer.js +10 -0
- package/dist/components/multi-select-combobox.d.ts +23 -0
- package/dist/components/multi-select-combobox.js +114 -0
- package/dist/components/pagination.d.ts +18 -0
- package/dist/components/pagination.js +101 -0
- package/dist/components/popover.d.ts +8 -0
- package/dist/components/popover.js +3 -0
- package/dist/components/progress.d.ts +6 -0
- package/dist/components/progress.js +30 -0
- package/dist/components/radio-group.d.ts +7 -0
- package/dist/components/radio-group.js +30 -0
- package/dist/components/rating.d.ts +15 -0
- package/dist/components/rating.js +120 -0
- package/dist/components/scroll-area.d.ts +7 -0
- package/dist/components/scroll-area.js +44 -0
- package/dist/components/search-bar.d.ts +17 -0
- package/dist/components/search-bar.js +92 -0
- package/dist/components/searchable-select.d.ts +22 -0
- package/dist/components/searchable-select.js +104 -0
- package/dist/components/select.d.ts +15 -0
- package/dist/components/select.js +129 -0
- package/dist/components/separator.d.ts +6 -0
- package/dist/components/separator.js +25 -0
- package/dist/components/sheet.d.ts +29 -0
- package/dist/components/sheet.js +96 -0
- package/dist/components/skeleton-card.d.ts +13 -0
- package/dist/components/skeleton-card.js +154 -0
- package/dist/components/skeleton.d.ts +6 -0
- package/dist/components/skeleton.js +3 -0
- package/dist/components/slider.d.ts +6 -0
- package/dist/components/slider.js +24 -0
- package/dist/components/sliding-panels.d.ts +19 -0
- package/dist/components/sliding-panels.js +75 -0
- package/dist/components/sonner.d.ts +6 -0
- package/dist/components/sonner.js +23 -0
- package/dist/components/sticky-container.d.ts +44 -0
- package/dist/components/sticky-container.js +39 -0
- package/dist/components/switch.d.ts +6 -0
- package/dist/components/switch.js +31 -0
- package/dist/components/table.d.ts +12 -0
- package/dist/components/table.js +81 -0
- package/dist/components/tabs.d.ts +12 -0
- package/dist/components/tabs.js +55 -0
- package/dist/components/text.d.ts +31 -0
- package/dist/components/text.js +3 -0
- package/dist/components/textarea.d.ts +6 -0
- package/dist/components/textarea.js +23 -0
- package/dist/components/toast.d.ts +18 -0
- package/dist/components/toast.js +3 -0
- package/dist/components/toaster.d.ts +5 -0
- package/dist/components/toaster.js +25 -0
- package/dist/components/toggle-group.d.ts +12 -0
- package/dist/components/toggle-group.js +72 -0
- package/dist/components/toggle.d.ts +13 -0
- package/dist/components/toggle.js +3 -0
- package/dist/components/tooltip.d.ts +9 -0
- package/dist/components/tooltip.js +26 -0
- package/dist/components/use-mobile.d.ts +3 -0
- package/dist/components/use-mobile.js +19 -0
- package/dist/components/use-toast.d.ts +42 -0
- package/dist/components/use-toast.js +2 -0
- package/dist/hooks/use-haptic.d.ts +22 -0
- package/dist/hooks/use-haptic.js +2 -0
- package/dist/hooks/use-mobile.d.ts +3 -0
- package/dist/hooks/use-mobile.js +19 -0
- package/dist/hooks/use-mounted-theme.d.ts +6 -0
- package/dist/hooks/use-mounted-theme.js +18 -0
- package/dist/hooks/use-sticky.d.ts +18 -0
- package/dist/hooks/use-sticky.js +2 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +2 -0
- package/package.json +203 -0
- package/src/components/accordion.tsx +57 -0
- package/src/components/alert-dialog.tsx +116 -0
- package/src/components/alert.tsx +57 -0
- package/src/components/aspect-ratio.tsx +9 -0
- package/src/components/avatar.tsx +49 -0
- package/src/components/badge.tsx +38 -0
- package/src/components/breadcrumb.tsx +102 -0
- package/src/components/button.tsx +94 -0
- package/src/components/calendar.tsx +60 -0
- package/src/components/card.tsx +67 -0
- package/src/components/carousel.tsx +230 -0
- package/src/components/checkbox.tsx +27 -0
- package/src/components/collapsible.tsx +28 -0
- package/src/components/command.tsx +148 -0
- package/src/components/currency-input.tsx +158 -0
- package/src/components/dialog.tsx +103 -0
- package/src/components/drawer.tsx +99 -0
- package/src/components/dropdown-menu.tsx +228 -0
- package/src/components/empty-state.tsx +118 -0
- package/src/components/form.tsx +152 -0
- package/src/components/haptic-button.tsx +19 -0
- package/src/components/input-otp.tsx +70 -0
- package/src/components/input.tsx +23 -0
- package/src/components/label.tsx +20 -0
- package/src/components/lazy-markdown.tsx +20 -0
- package/src/components/link-button.tsx +40 -0
- package/src/components/markdown-renderer.tsx +20 -0
- package/src/components/multi-select-combobox.tsx +148 -0
- package/src/components/pagination.tsx +106 -0
- package/src/components/popover.tsx +30 -0
- package/src/components/progress.tsx +27 -0
- package/src/components/radio-group.tsx +37 -0
- package/src/components/rating.tsx +136 -0
- package/src/components/scroll-area.tsx +45 -0
- package/src/components/search-bar.tsx +113 -0
- package/src/components/searchable-select.tsx +134 -0
- package/src/components/select.tsx +152 -0
- package/src/components/separator.tsx +25 -0
- package/src/components/sheet.tsx +121 -0
- package/src/components/skeleton-card.tsx +282 -0
- package/src/components/skeleton.tsx +17 -0
- package/src/components/slider.tsx +24 -0
- package/src/components/sliding-panels.tsx +99 -0
- package/src/components/sonner.tsx +25 -0
- package/src/components/sticky-container.tsx +88 -0
- package/src/components/switch.tsx +28 -0
- package/src/components/table.tsx +90 -0
- package/src/components/tabs.tsx +72 -0
- package/src/components/text.test.tsx +192 -0
- package/src/components/text.tsx +114 -0
- package/src/components/textarea.tsx +22 -0
- package/src/components/toast.tsx +129 -0
- package/src/components/toaster.tsx +31 -0
- package/src/components/toggle-group.tsx +69 -0
- package/src/components/toggle.tsx +46 -0
- package/src/components/tooltip.tsx +29 -0
- package/src/components/use-mobile.tsx +19 -0
- package/src/components/use-toast.ts +180 -0
- package/src/globals.css +409 -0
- package/src/hooks/use-haptic.ts +118 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/hooks/use-mounted-theme.ts +20 -0
- package/src/hooks/use-sticky.ts +47 -0
- package/src/lib/utils.ts +6 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
type HapticStyle = "light" | "medium" | "heavy" | "success" | "warning" | "error" | "selection";
|
|
6
|
+
|
|
7
|
+
interface HapticAPI {
|
|
8
|
+
isSupported: boolean;
|
|
9
|
+
platform: "ios" | "android" | "desktop" | "unknown";
|
|
10
|
+
trigger: (style: HapticStyle) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const useHaptic = (): HapticAPI => {
|
|
14
|
+
const [platform, setPlatform] = useState<"ios" | "android" | "desktop" | "unknown">("unknown");
|
|
15
|
+
const [isSupported, setIsSupported] = useState(false);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// Detect platform
|
|
19
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
20
|
+
const isIOS = /iphone|ipad|ipod/.test(userAgent);
|
|
21
|
+
const isAndroid = /android/.test(userAgent);
|
|
22
|
+
|
|
23
|
+
if (isIOS) {
|
|
24
|
+
setPlatform("ios");
|
|
25
|
+
} else if (isAndroid) {
|
|
26
|
+
setPlatform("android");
|
|
27
|
+
} else if (/windows|mac|linux/.test(userAgent)) {
|
|
28
|
+
setPlatform("desktop");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check if any haptic API is available
|
|
32
|
+
const hasVibration = "vibrate" in navigator;
|
|
33
|
+
const hasTelegram = typeof window !== "undefined" && window.Telegram?.WebApp?.HapticFeedback;
|
|
34
|
+
|
|
35
|
+
setIsSupported(hasVibration || !!hasTelegram);
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
38
|
+
const trigger = (style: HapticStyle) => {
|
|
39
|
+
// Try Telegram API first (works on both iOS and Android in Telegram)
|
|
40
|
+
if (typeof window !== "undefined" && window.Telegram?.WebApp?.HapticFeedback) {
|
|
41
|
+
triggerTelegramHaptic(style);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fallback to Vibration API (Android, some browsers)
|
|
46
|
+
if ("vibrate" in navigator) {
|
|
47
|
+
triggerVibrationAPI(style);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// No haptic support - silently fail
|
|
52
|
+
console.debug("Haptic feedback not supported on this device");
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return { isSupported, platform, trigger };
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Telegram Haptic API implementation
|
|
59
|
+
function triggerTelegramHaptic(style: HapticStyle) {
|
|
60
|
+
const telegram = window.Telegram?.WebApp?.HapticFeedback;
|
|
61
|
+
if (!telegram) return;
|
|
62
|
+
|
|
63
|
+
switch (style) {
|
|
64
|
+
case "light":
|
|
65
|
+
telegram.impactOccurred("light");
|
|
66
|
+
break;
|
|
67
|
+
case "medium":
|
|
68
|
+
telegram.impactOccurred("medium");
|
|
69
|
+
break;
|
|
70
|
+
case "heavy":
|
|
71
|
+
telegram.impactOccurred("heavy");
|
|
72
|
+
break;
|
|
73
|
+
case "success":
|
|
74
|
+
telegram.notificationOccurred("success");
|
|
75
|
+
break;
|
|
76
|
+
case "warning":
|
|
77
|
+
telegram.notificationOccurred("warning");
|
|
78
|
+
break;
|
|
79
|
+
case "error":
|
|
80
|
+
telegram.notificationOccurred("error");
|
|
81
|
+
break;
|
|
82
|
+
case "selection":
|
|
83
|
+
telegram.selectionChanged();
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Vibration API implementation (Android)
|
|
89
|
+
function triggerVibrationAPI(style: HapticStyle) {
|
|
90
|
+
if (!("vibrate" in navigator)) return;
|
|
91
|
+
|
|
92
|
+
const patterns: Record<HapticStyle, number | number[]> = {
|
|
93
|
+
light: 10,
|
|
94
|
+
medium: 20,
|
|
95
|
+
heavy: 50,
|
|
96
|
+
success: [10, 50, 10],
|
|
97
|
+
warning: [20, 100, 20],
|
|
98
|
+
error: [50, 100, 50],
|
|
99
|
+
selection: 5,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
navigator.vibrate(patterns[style]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Extend Window interface for TypeScript
|
|
106
|
+
declare global {
|
|
107
|
+
interface Window {
|
|
108
|
+
Telegram?: {
|
|
109
|
+
WebApp?: {
|
|
110
|
+
HapticFeedback?: {
|
|
111
|
+
impactOccurred: (style: "light" | "medium" | "heavy") => void;
|
|
112
|
+
notificationOccurred: (type: "success" | "warning" | "error") => void;
|
|
113
|
+
selectionChanged: () => void;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768;
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
12
|
+
};
|
|
13
|
+
mql.addEventListener("change", onChange);
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
15
|
+
return () => mql.removeEventListener("change", onChange);
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
return !!isMobile;
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
import { useTheme } from "next-themes";
|
|
3
|
+
|
|
4
|
+
// A stable no-op subscribe that never notifies — we only care about the snapshot
|
|
5
|
+
const subscribe = () => () => {};
|
|
6
|
+
|
|
7
|
+
// Client: always mounted
|
|
8
|
+
const getClientSnapshot = () => true;
|
|
9
|
+
|
|
10
|
+
// Server: never mounted
|
|
11
|
+
const getServerSnapshot = () => false;
|
|
12
|
+
|
|
13
|
+
export function useMountedTheme() {
|
|
14
|
+
const mounted = useSyncExternalStore(subscribe, getClientSnapshot, getServerSnapshot);
|
|
15
|
+
const { resolvedTheme } = useTheme();
|
|
16
|
+
return {
|
|
17
|
+
mounted,
|
|
18
|
+
isDark: mounted && resolvedTheme === "dark",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook to detect when a sticky element becomes "stuck"
|
|
7
|
+
* Uses IntersectionObserver with a sentinel element to detect sticky state
|
|
8
|
+
*
|
|
9
|
+
* @returns An object with:
|
|
10
|
+
* - isSticky: boolean indicating if element is currently stuck
|
|
11
|
+
* - ref: ref to attach to the sticky element
|
|
12
|
+
* - sentinelRef: ref for the sentinel element (place 1px above sticky element)
|
|
13
|
+
*/
|
|
14
|
+
export function useSticky() {
|
|
15
|
+
const [isSticky, setIsSticky] = useState(false);
|
|
16
|
+
const sentinelRef = useRef<HTMLDivElement>(null);
|
|
17
|
+
const stickyRef = useRef<HTMLDivElement>(null);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const sentinel = sentinelRef.current;
|
|
21
|
+
if (!sentinel) return;
|
|
22
|
+
|
|
23
|
+
const observer = new IntersectionObserver(
|
|
24
|
+
([entry]) => {
|
|
25
|
+
// When sentinel is not visible (scrolled past), element is stuck
|
|
26
|
+
setIsSticky(!entry.isIntersecting);
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
threshold: [0],
|
|
30
|
+
// Negative top margin to detect when element reaches sticky position
|
|
31
|
+
rootMargin: "-1px 0px 0px 0px",
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
observer.observe(sentinel);
|
|
36
|
+
|
|
37
|
+
return () => {
|
|
38
|
+
observer.disconnect();
|
|
39
|
+
};
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
isSticky,
|
|
44
|
+
ref: stickyRef,
|
|
45
|
+
sentinelRef,
|
|
46
|
+
};
|
|
47
|
+
}
|