@streamplace/components 0.0.1 → 0.7.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/LICENSE +18 -0
- package/README.md +35 -0
- package/dist/components/chat/chat-box.js +109 -0
- package/dist/components/chat/chat-message.js +76 -0
- package/dist/components/chat/chat.js +56 -0
- package/dist/components/chat/mention-suggestions.js +39 -0
- package/dist/components/chat/mod-view.js +33 -0
- package/dist/components/mobile-player/fullscreen.js +69 -0
- package/dist/components/mobile-player/fullscreen.native.js +151 -0
- package/dist/components/mobile-player/player.js +103 -0
- package/dist/components/mobile-player/props.js +1 -0
- package/dist/components/mobile-player/shared.js +51 -0
- package/dist/components/mobile-player/ui/countdown.js +79 -0
- package/dist/components/mobile-player/ui/index.js +5 -0
- package/dist/components/mobile-player/ui/input.js +38 -0
- package/dist/components/mobile-player/ui/metrics.js +40 -0
- package/dist/components/mobile-player/ui/streamer-context-menu.js +4 -0
- package/dist/components/mobile-player/ui/viewer-context-menu.js +20 -0
- package/dist/components/mobile-player/use-webrtc.js +232 -0
- package/dist/components/mobile-player/video.js +375 -0
- package/dist/components/mobile-player/video.native.js +238 -0
- package/dist/components/mobile-player/webrtc-diagnostics.js +106 -0
- package/dist/components/mobile-player/webrtc-primitives.js +25 -0
- package/dist/components/mobile-player/webrtc-primitives.native.js +1 -0
- package/dist/components/ui/button.js +220 -0
- package/dist/components/ui/dialog.js +203 -0
- package/dist/components/ui/dropdown.js +148 -0
- package/dist/components/ui/icons.js +22 -0
- package/dist/components/ui/index.js +22 -0
- package/dist/components/ui/input.js +202 -0
- package/dist/components/ui/loader.js +7 -0
- package/dist/components/ui/primitives/button.js +121 -0
- package/dist/components/ui/primitives/input.js +202 -0
- package/dist/components/ui/primitives/modal.js +203 -0
- package/dist/components/ui/primitives/text.js +286 -0
- package/dist/components/ui/resizeable.js +101 -0
- package/dist/components/ui/text.js +175 -0
- package/dist/components/ui/textarea.js +17 -0
- package/dist/components/ui/toast.js +129 -0
- package/dist/components/ui/view.js +250 -0
- package/dist/hooks/index.js +9 -0
- package/dist/hooks/useAvatars.js +32 -0
- package/dist/hooks/useCameraToggle.js +9 -0
- package/dist/hooks/useKeyboard.js +33 -0
- package/dist/hooks/useKeyboardSlide.js +11 -0
- package/dist/hooks/useLivestreamInfo.js +62 -0
- package/dist/hooks/useOuterAndInnerDimensions.js +27 -0
- package/dist/hooks/usePlayerDimensions.js +19 -0
- package/dist/hooks/useSegmentTiming.js +62 -0
- package/dist/index.js +16 -0
- package/dist/lib/facet.js +88 -0
- package/dist/lib/theme/atoms.js +620 -0
- package/dist/lib/theme/atoms.types.js +5 -0
- package/dist/lib/theme/index.js +9 -0
- package/dist/lib/theme/theme.js +248 -0
- package/dist/lib/theme/tokens.js +383 -0
- package/dist/lib/utils.js +94 -0
- package/dist/livestream-provider/index.js +25 -0
- package/dist/livestream-provider/websocket.js +41 -0
- package/dist/livestream-store/chat.js +186 -0
- package/dist/livestream-store/context.js +2 -0
- package/dist/livestream-store/index.js +4 -0
- package/dist/livestream-store/livestream-state.js +1 -0
- package/dist/livestream-store/livestream-store.js +42 -0
- package/dist/livestream-store/stream-key.js +115 -0
- package/dist/livestream-store/websocket-consumer.js +55 -0
- package/dist/player-store/context.js +2 -0
- package/dist/player-store/index.js +6 -0
- package/dist/player-store/player-provider.js +52 -0
- package/dist/player-store/player-state.js +22 -0
- package/dist/player-store/player-store.js +159 -0
- package/dist/player-store/single-player-provider.js +109 -0
- package/dist/streamplace-provider/context.js +2 -0
- package/dist/streamplace-provider/index.js +16 -0
- package/dist/streamplace-provider/poller.js +46 -0
- package/dist/streamplace-provider/xrpc.js +0 -0
- package/dist/streamplace-store/block.js +23 -0
- package/dist/streamplace-store/index.js +3 -0
- package/dist/streamplace-store/stream.js +193 -0
- package/dist/streamplace-store/streamplace-store.js +37 -0
- package/dist/streamplace-store/user.js +47 -0
- package/dist/streamplace-store/xrpc.js +12 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/56540125 +0 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/67b1eb60 +0 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/7c275f90 +0 -0
- package/package.json +50 -8
- package/src/components/chat/chat-box.tsx +195 -0
- package/src/components/chat/chat-message.tsx +192 -0
- package/src/components/chat/chat.tsx +128 -0
- package/src/components/chat/mention-suggestions.tsx +71 -0
- package/src/components/chat/mod-view.tsx +118 -0
- package/src/components/mobile-player/fullscreen.native.tsx +193 -0
- package/src/components/mobile-player/fullscreen.tsx +79 -0
- package/src/components/mobile-player/player.tsx +134 -0
- package/src/components/mobile-player/props.tsx +11 -0
- package/src/components/mobile-player/shared.tsx +56 -0
- package/src/components/mobile-player/ui/countdown.tsx +119 -0
- package/src/components/mobile-player/ui/index.ts +5 -0
- package/src/components/mobile-player/ui/input.tsx +85 -0
- package/src/components/mobile-player/ui/metrics.tsx +69 -0
- package/src/components/mobile-player/ui/streamer-context-menu.tsx +3 -0
- package/src/components/mobile-player/ui/viewer-context-menu.tsx +70 -0
- package/src/components/mobile-player/use-webrtc.tsx +282 -0
- package/src/components/mobile-player/video.native.tsx +360 -0
- package/src/components/mobile-player/video.tsx +557 -0
- package/src/components/mobile-player/webrtc-diagnostics.tsx +149 -0
- package/src/components/mobile-player/webrtc-primitives.native.tsx +6 -0
- package/src/components/mobile-player/webrtc-primitives.tsx +33 -0
- package/src/components/ui/button.tsx +309 -0
- package/src/components/ui/dialog.tsx +376 -0
- package/src/components/ui/dropdown.tsx +399 -0
- package/src/components/ui/icons.tsx +50 -0
- package/src/components/ui/index.ts +33 -0
- package/src/components/ui/input.tsx +350 -0
- package/src/components/ui/loader.tsx +9 -0
- package/src/components/ui/primitives/button.tsx +292 -0
- package/src/components/ui/primitives/input.tsx +422 -0
- package/src/components/ui/primitives/modal.tsx +421 -0
- package/src/components/ui/primitives/text.tsx +499 -0
- package/src/components/ui/resizeable.tsx +169 -0
- package/src/components/ui/text.tsx +330 -0
- package/src/components/ui/textarea.tsx +34 -0
- package/src/components/ui/toast.tsx +203 -0
- package/src/components/ui/view.tsx +344 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/useAvatars.tsx +44 -0
- package/src/hooks/useCameraToggle.ts +12 -0
- package/src/hooks/useKeyboard.tsx +41 -0
- package/src/hooks/useKeyboardSlide.ts +12 -0
- package/src/hooks/useLivestreamInfo.ts +67 -0
- package/src/hooks/useOuterAndInnerDimensions.tsx +32 -0
- package/src/hooks/usePlayerDimensions.ts +23 -0
- package/src/hooks/useSegmentTiming.tsx +88 -0
- package/src/index.tsx +27 -0
- package/src/lib/facet.ts +131 -0
- package/src/lib/theme/atoms.ts +760 -0
- package/src/lib/theme/atoms.types.ts +258 -0
- package/src/lib/theme/index.ts +48 -0
- package/src/lib/theme/theme.tsx +436 -0
- package/src/lib/theme/tokens.ts +409 -0
- package/src/lib/utils.ts +132 -0
- package/src/livestream-provider/index.tsx +48 -0
- package/src/livestream-provider/websocket.tsx +47 -0
- package/src/livestream-store/chat.tsx +261 -0
- package/src/livestream-store/context.tsx +10 -0
- package/src/livestream-store/index.tsx +4 -0
- package/src/livestream-store/livestream-state.tsx +21 -0
- package/src/livestream-store/livestream-store.tsx +59 -0
- package/src/livestream-store/stream-key.tsx +124 -0
- package/src/livestream-store/websocket-consumer.tsx +62 -0
- package/src/player-store/context.tsx +11 -0
- package/src/player-store/index.tsx +6 -0
- package/src/player-store/player-provider.tsx +89 -0
- package/src/player-store/player-state.tsx +187 -0
- package/src/player-store/player-store.tsx +239 -0
- package/src/player-store/single-player-provider.tsx +181 -0
- package/src/streamplace-provider/context.tsx +10 -0
- package/src/streamplace-provider/index.tsx +32 -0
- package/src/streamplace-provider/poller.tsx +55 -0
- package/src/streamplace-provider/xrpc.tsx +0 -0
- package/src/streamplace-store/block.tsx +29 -0
- package/src/streamplace-store/index.tsx +3 -0
- package/src/streamplace-store/stream.tsx +262 -0
- package/src/streamplace-store/streamplace-store.tsx +89 -0
- package/src/streamplace-store/user.tsx +57 -0
- package/src/streamplace-store/xrpc.tsx +15 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
export function useWebRTCDiagnostics() {
|
|
3
|
+
const [diagnostics, setDiagnostics] = useState({
|
|
4
|
+
done: false,
|
|
5
|
+
browserSupport: false,
|
|
6
|
+
rtcPeerConnection: false,
|
|
7
|
+
rtcSessionDescription: false,
|
|
8
|
+
getUserMedia: false,
|
|
9
|
+
getDisplayMedia: false,
|
|
10
|
+
errors: [],
|
|
11
|
+
warnings: [],
|
|
12
|
+
});
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const errors = [];
|
|
15
|
+
const warnings = [];
|
|
16
|
+
// Check if we're in a browser environment
|
|
17
|
+
if (typeof window === "undefined") {
|
|
18
|
+
errors.push("Running in non-browser environment");
|
|
19
|
+
setDiagnostics({
|
|
20
|
+
done: false,
|
|
21
|
+
browserSupport: false,
|
|
22
|
+
rtcPeerConnection: false,
|
|
23
|
+
rtcSessionDescription: false,
|
|
24
|
+
getUserMedia: false,
|
|
25
|
+
getDisplayMedia: false,
|
|
26
|
+
errors,
|
|
27
|
+
warnings,
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// Check RTCPeerConnection support
|
|
32
|
+
const rtcPeerConnection = !!(window.RTCPeerConnection ||
|
|
33
|
+
window.webkitRTCPeerConnection ||
|
|
34
|
+
window.mozRTCPeerConnection);
|
|
35
|
+
if (!rtcPeerConnection) {
|
|
36
|
+
errors.push("RTCPeerConnection is not supported");
|
|
37
|
+
}
|
|
38
|
+
// Check RTCSessionDescription support
|
|
39
|
+
const rtcSessionDescription = !!(window.RTCSessionDescription ||
|
|
40
|
+
window.webkitRTCSessionDescription ||
|
|
41
|
+
window.mozRTCSessionDescription);
|
|
42
|
+
if (!rtcSessionDescription) {
|
|
43
|
+
errors.push("RTCSessionDescription is not supported");
|
|
44
|
+
}
|
|
45
|
+
// Check getUserMedia support
|
|
46
|
+
const getUserMedia = !!(navigator.mediaDevices?.getUserMedia ||
|
|
47
|
+
navigator.getUserMedia ||
|
|
48
|
+
navigator.webkitGetUserMedia ||
|
|
49
|
+
navigator.mozGetUserMedia);
|
|
50
|
+
if (!getUserMedia) {
|
|
51
|
+
warnings.push("getUserMedia is not supported - webcam features unavailable");
|
|
52
|
+
}
|
|
53
|
+
// Check getDisplayMedia support
|
|
54
|
+
const getDisplayMedia = !!navigator.mediaDevices?.getDisplayMedia;
|
|
55
|
+
if (!getDisplayMedia) {
|
|
56
|
+
warnings.push("getDisplayMedia is not supported - screen sharing unavailable");
|
|
57
|
+
}
|
|
58
|
+
// Check if running over HTTPS (required for some WebRTC features)
|
|
59
|
+
if (location.protocol !== "https:" &&
|
|
60
|
+
location.hostname !== "localhost" &&
|
|
61
|
+
location.hostname !== "127.0.0.1") {
|
|
62
|
+
warnings.push("WebRTC features may be limited over HTTP connections");
|
|
63
|
+
}
|
|
64
|
+
// Check browser-specific issues
|
|
65
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
66
|
+
if (userAgent.includes("safari") && !userAgent.includes("chrome")) {
|
|
67
|
+
warnings.push("Safari may have limited WebRTC codec support");
|
|
68
|
+
}
|
|
69
|
+
const browserSupport = rtcPeerConnection && rtcSessionDescription;
|
|
70
|
+
setDiagnostics({
|
|
71
|
+
done: true,
|
|
72
|
+
browserSupport,
|
|
73
|
+
rtcPeerConnection,
|
|
74
|
+
rtcSessionDescription,
|
|
75
|
+
getUserMedia,
|
|
76
|
+
getDisplayMedia,
|
|
77
|
+
errors,
|
|
78
|
+
warnings,
|
|
79
|
+
});
|
|
80
|
+
}, []);
|
|
81
|
+
return diagnostics;
|
|
82
|
+
}
|
|
83
|
+
export function logWebRTCDiagnostics() {
|
|
84
|
+
console.group("WebRTC Diagnostics");
|
|
85
|
+
// Log browser support
|
|
86
|
+
console.log("RTCPeerConnection:", !!window.RTCPeerConnection);
|
|
87
|
+
console.log("RTCSessionDescription:", !!window.RTCSessionDescription);
|
|
88
|
+
console.log("getUserMedia:", !!navigator.mediaDevices?.getUserMedia);
|
|
89
|
+
console.log("getDisplayMedia:", !!navigator.mediaDevices?.getDisplayMedia);
|
|
90
|
+
// Log browser info
|
|
91
|
+
console.log("User Agent:", navigator.userAgent);
|
|
92
|
+
console.log("Protocol:", location.protocol);
|
|
93
|
+
console.log("Host:", location.hostname);
|
|
94
|
+
// Test basic WebRTC functionality
|
|
95
|
+
if (window.RTCPeerConnection) {
|
|
96
|
+
try {
|
|
97
|
+
const pc = new RTCPeerConnection();
|
|
98
|
+
console.log("RTCPeerConnection creation: ✓ Success");
|
|
99
|
+
pc.close();
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error("RTCPeerConnection creation: ✗ Failed", error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
console.groupEnd();
|
|
106
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Browser compatibility checks for WebRTC
|
|
2
|
+
const checkWebRTCSupport = () => {
|
|
3
|
+
if (typeof window === "undefined") {
|
|
4
|
+
throw new Error("WebRTC is not available in non-browser environments");
|
|
5
|
+
}
|
|
6
|
+
if (!window.RTCPeerConnection) {
|
|
7
|
+
throw new Error("RTCPeerConnection is not supported in this browser. Please use a modern browser that supports WebRTC.");
|
|
8
|
+
}
|
|
9
|
+
if (!window.RTCSessionDescription) {
|
|
10
|
+
throw new Error("RTCSessionDescription is not supported in this browser. Please use a modern browser that supports WebRTC.");
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
// Check support immediately
|
|
14
|
+
try {
|
|
15
|
+
checkWebRTCSupport();
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
console.error("WebRTC Compatibility Error:", error.message);
|
|
19
|
+
}
|
|
20
|
+
export const RTCPeerConnection = window.RTCPeerConnection;
|
|
21
|
+
export const RTCSessionDescription = window.RTCSessionDescription;
|
|
22
|
+
export const WebRTCMediaStream = window.MediaStream;
|
|
23
|
+
export const mediaDevices = navigator.mediaDevices;
|
|
24
|
+
// Export the compatibility checker for use in other components
|
|
25
|
+
export { checkWebRTCSupport };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RTCPeerConnection, RTCSessionDescription, MediaStream as WebRTCMediaStream, mediaDevices, } from "react-native-webrtc";
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import React, { forwardRef, useMemo } from "react";
|
|
4
|
+
import { ActivityIndicator, StyleSheet } from "react-native";
|
|
5
|
+
import { useTheme } from "../../lib/theme/theme";
|
|
6
|
+
import * as tokens from "../../lib/theme/tokens";
|
|
7
|
+
import { ButtonPrimitive } from "./primitives/button";
|
|
8
|
+
import { TextPrimitive } from "./primitives/text";
|
|
9
|
+
// Button variants using class-variance-authority pattern
|
|
10
|
+
const buttonVariants = cva("", {
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
primary: "primary",
|
|
14
|
+
secondary: "secondary",
|
|
15
|
+
outline: "outline",
|
|
16
|
+
ghost: "ghost",
|
|
17
|
+
destructive: "destructive",
|
|
18
|
+
success: "success",
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
sm: "sm",
|
|
22
|
+
md: "md",
|
|
23
|
+
lg: "lg",
|
|
24
|
+
xl: "xl",
|
|
25
|
+
pill: "pill",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
defaultVariants: {
|
|
29
|
+
variant: "primary",
|
|
30
|
+
size: "md",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
export const Button = forwardRef(({ variant = "primary", size = "md", children, leftIcon, rightIcon, loading = false, loadingText, disabled, style, ...props }, ref) => {
|
|
34
|
+
const { theme } = useTheme();
|
|
35
|
+
// Create dynamic styles based on theme
|
|
36
|
+
const styles = useMemo(() => createStyles(theme), [theme]);
|
|
37
|
+
// Get variant styles
|
|
38
|
+
const buttonStyle = useMemo(() => {
|
|
39
|
+
const variantStyle = styles[`${variant}Button`];
|
|
40
|
+
const sizeStyle = styles[`${size}Button`];
|
|
41
|
+
return [variantStyle, sizeStyle];
|
|
42
|
+
}, [variant, size, styles]);
|
|
43
|
+
// Get inner styles for button content
|
|
44
|
+
const buttonInnerStyle = useMemo(() => {
|
|
45
|
+
const sizeInnerStyle = styles[`${size}ButtonInner`];
|
|
46
|
+
return sizeInnerStyle;
|
|
47
|
+
}, [size, styles]);
|
|
48
|
+
const textStyle = React.useMemo(() => {
|
|
49
|
+
const variantTextStyle = styles[`${variant}Text`];
|
|
50
|
+
const sizeTextStyle = styles[`${size}Text`];
|
|
51
|
+
return [variantTextStyle, sizeTextStyle];
|
|
52
|
+
}, [variant, size, styles]);
|
|
53
|
+
const iconSize = React.useMemo(() => {
|
|
54
|
+
switch (size) {
|
|
55
|
+
case "sm":
|
|
56
|
+
return 16;
|
|
57
|
+
case "lg":
|
|
58
|
+
return 20;
|
|
59
|
+
case "xl":
|
|
60
|
+
return 24;
|
|
61
|
+
case "md":
|
|
62
|
+
default:
|
|
63
|
+
return 18;
|
|
64
|
+
}
|
|
65
|
+
}, [size]);
|
|
66
|
+
const spinnerSize = useMemo(() => {
|
|
67
|
+
switch (size) {
|
|
68
|
+
case "sm":
|
|
69
|
+
return "small";
|
|
70
|
+
case "lg":
|
|
71
|
+
case "xl":
|
|
72
|
+
return "large";
|
|
73
|
+
case "md":
|
|
74
|
+
default:
|
|
75
|
+
return "small";
|
|
76
|
+
}
|
|
77
|
+
}, [size]);
|
|
78
|
+
const spinnerColor = useMemo(() => {
|
|
79
|
+
switch (variant) {
|
|
80
|
+
case "outline":
|
|
81
|
+
case "ghost":
|
|
82
|
+
return theme.colors.primary;
|
|
83
|
+
case "secondary":
|
|
84
|
+
return theme.colors.secondaryForeground;
|
|
85
|
+
case "destructive":
|
|
86
|
+
return theme.colors.destructiveForeground;
|
|
87
|
+
default:
|
|
88
|
+
return theme.colors.primaryForeground;
|
|
89
|
+
}
|
|
90
|
+
}, [variant, theme.colors]);
|
|
91
|
+
return (_jsx(ButtonPrimitive.Root, { ref: ref, disabled: disabled || loading, style: [buttonStyle, style], ...props, children: _jsxs(ButtonPrimitive.Content, { style: buttonInnerStyle, children: [loading && !leftIcon ? (_jsx(ButtonPrimitive.Icon, { position: "left", children: _jsx(ActivityIndicator, { size: spinnerSize, color: spinnerColor }) })) : leftIcon ? (_jsx(ButtonPrimitive.Icon, { position: "left", style: { width: iconSize, height: iconSize }, children: leftIcon })) : null, _jsx(TextPrimitive.Root, { style: textStyle, children: loading && loadingText ? loadingText : children }), loading && rightIcon ? (_jsx(ButtonPrimitive.Icon, { position: "right", children: _jsx(ActivityIndicator, { size: spinnerSize, color: spinnerColor }) })) : rightIcon ? (_jsx(ButtonPrimitive.Icon, { position: "right", style: { width: iconSize, height: iconSize }, children: rightIcon })) : null] }) }));
|
|
92
|
+
});
|
|
93
|
+
Button.displayName = "Button";
|
|
94
|
+
// Create theme-based styles
|
|
95
|
+
function createStyles(theme) {
|
|
96
|
+
return StyleSheet.create({
|
|
97
|
+
// Variant styles
|
|
98
|
+
primaryButton: {
|
|
99
|
+
backgroundColor: theme.colors.primary,
|
|
100
|
+
borderWidth: 0,
|
|
101
|
+
...theme.shadows.sm,
|
|
102
|
+
},
|
|
103
|
+
primaryText: {
|
|
104
|
+
color: theme.colors.primaryForeground,
|
|
105
|
+
fontWeight: "600",
|
|
106
|
+
},
|
|
107
|
+
secondaryButton: {
|
|
108
|
+
backgroundColor: theme.colors.secondary,
|
|
109
|
+
borderWidth: 0,
|
|
110
|
+
},
|
|
111
|
+
secondaryText: {
|
|
112
|
+
color: theme.colors.secondaryForeground,
|
|
113
|
+
fontWeight: "500",
|
|
114
|
+
},
|
|
115
|
+
outlineButton: {
|
|
116
|
+
backgroundColor: "transparent",
|
|
117
|
+
borderWidth: 1,
|
|
118
|
+
borderColor: theme.colors.border,
|
|
119
|
+
},
|
|
120
|
+
outlineText: {
|
|
121
|
+
color: theme.colors.foreground,
|
|
122
|
+
fontWeight: "500",
|
|
123
|
+
},
|
|
124
|
+
ghostButton: {
|
|
125
|
+
backgroundColor: "transparent",
|
|
126
|
+
borderWidth: 0,
|
|
127
|
+
},
|
|
128
|
+
ghostText: {
|
|
129
|
+
color: theme.colors.foreground,
|
|
130
|
+
fontWeight: "500",
|
|
131
|
+
},
|
|
132
|
+
destructiveButton: {
|
|
133
|
+
backgroundColor: theme.colors.destructive,
|
|
134
|
+
borderWidth: 0,
|
|
135
|
+
...theme.shadows.sm,
|
|
136
|
+
},
|
|
137
|
+
destructiveText: {
|
|
138
|
+
color: theme.colors.destructiveForeground,
|
|
139
|
+
fontWeight: "600",
|
|
140
|
+
},
|
|
141
|
+
successButton: {
|
|
142
|
+
backgroundColor: theme.colors.success,
|
|
143
|
+
borderWidth: 0,
|
|
144
|
+
...theme.shadows.sm,
|
|
145
|
+
},
|
|
146
|
+
successText: {
|
|
147
|
+
color: theme.colors.successForeground,
|
|
148
|
+
fontWeight: "600",
|
|
149
|
+
},
|
|
150
|
+
pillButton: {
|
|
151
|
+
paddingHorizontal: theme.spacing[3],
|
|
152
|
+
paddingVertical: theme.spacing[2],
|
|
153
|
+
borderRadius: tokens.borderRadius.full,
|
|
154
|
+
minHeight: tokens.touchTargets.minimum / 2,
|
|
155
|
+
},
|
|
156
|
+
pillText: {
|
|
157
|
+
color: theme.colors.primaryForeground,
|
|
158
|
+
fontWeight: "400",
|
|
159
|
+
},
|
|
160
|
+
// Size styles
|
|
161
|
+
smButton: {
|
|
162
|
+
paddingHorizontal: theme.spacing[3],
|
|
163
|
+
paddingVertical: theme.spacing[2],
|
|
164
|
+
borderRadius: tokens.borderRadius.md,
|
|
165
|
+
minHeight: tokens.touchTargets.minimum,
|
|
166
|
+
gap: theme.spacing[1],
|
|
167
|
+
},
|
|
168
|
+
smButtonInner: {
|
|
169
|
+
gap: theme.spacing[1],
|
|
170
|
+
},
|
|
171
|
+
smText: {
|
|
172
|
+
fontSize: 14,
|
|
173
|
+
lineHeight: 16,
|
|
174
|
+
},
|
|
175
|
+
mdButton: {
|
|
176
|
+
paddingHorizontal: theme.spacing[4],
|
|
177
|
+
paddingVertical: theme.spacing[3],
|
|
178
|
+
borderRadius: tokens.borderRadius.md,
|
|
179
|
+
minHeight: tokens.touchTargets.minimum,
|
|
180
|
+
gap: theme.spacing[2],
|
|
181
|
+
},
|
|
182
|
+
mdButtonInner: {
|
|
183
|
+
gap: theme.spacing[2],
|
|
184
|
+
},
|
|
185
|
+
mdText: {
|
|
186
|
+
fontSize: 16,
|
|
187
|
+
lineHeight: 18,
|
|
188
|
+
},
|
|
189
|
+
lgButton: {
|
|
190
|
+
paddingHorizontal: theme.spacing[6],
|
|
191
|
+
paddingVertical: theme.spacing[4],
|
|
192
|
+
borderRadius: tokens.borderRadius.md,
|
|
193
|
+
minHeight: tokens.touchTargets.comfortable,
|
|
194
|
+
gap: theme.spacing[3],
|
|
195
|
+
},
|
|
196
|
+
lgButtonInner: {
|
|
197
|
+
gap: theme.spacing[3],
|
|
198
|
+
},
|
|
199
|
+
lgText: {
|
|
200
|
+
fontSize: 18,
|
|
201
|
+
lineHeight: 20,
|
|
202
|
+
},
|
|
203
|
+
xlButton: {
|
|
204
|
+
paddingHorizontal: theme.spacing[8],
|
|
205
|
+
paddingVertical: theme.spacing[5],
|
|
206
|
+
borderRadius: tokens.borderRadius.lg,
|
|
207
|
+
minHeight: tokens.touchTargets.large,
|
|
208
|
+
gap: theme.spacing[4],
|
|
209
|
+
},
|
|
210
|
+
xlButtonInner: {
|
|
211
|
+
gap: theme.spacing[4],
|
|
212
|
+
},
|
|
213
|
+
xlText: {
|
|
214
|
+
fontSize: 20,
|
|
215
|
+
lineHeight: 24,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// Export button variants for external use
|
|
220
|
+
export { buttonVariants };
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { X } from "lucide-react-native";
|
|
4
|
+
import React, { forwardRef } from "react";
|
|
5
|
+
import { Platform, StyleSheet, Text } from "react-native";
|
|
6
|
+
import { useTheme } from "../../lib/theme/theme";
|
|
7
|
+
import { createThemedIcon } from "./icons";
|
|
8
|
+
import { ModalPrimitive } from "./primitives/modal";
|
|
9
|
+
const ThemedX = createThemedIcon(X);
|
|
10
|
+
// Dialog variants using class-variance-authority pattern
|
|
11
|
+
const dialogVariants = cva("", {
|
|
12
|
+
variants: {
|
|
13
|
+
variant: {
|
|
14
|
+
default: "default",
|
|
15
|
+
sheet: "sheet",
|
|
16
|
+
fullscreen: "fullscreen",
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
sm: "sm",
|
|
20
|
+
md: "md",
|
|
21
|
+
lg: "lg",
|
|
22
|
+
xl: "xl",
|
|
23
|
+
full: "full",
|
|
24
|
+
},
|
|
25
|
+
position: {
|
|
26
|
+
center: "center",
|
|
27
|
+
top: "top",
|
|
28
|
+
bottom: "bottom",
|
|
29
|
+
left: "left",
|
|
30
|
+
right: "right",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
defaultVariants: {
|
|
34
|
+
variant: "default",
|
|
35
|
+
size: "md",
|
|
36
|
+
position: "center",
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
export const Dialog = forwardRef(({ variant = "left", size = "md", position = "center", children, title, description, dismissible = true, showCloseButton = true, onClose, open = false, onOpenChange, ...props }, ref) => {
|
|
40
|
+
const { theme } = useTheme();
|
|
41
|
+
// Create dynamic styles based on theme
|
|
42
|
+
const styles = React.useMemo(() => createStyles(theme), [theme]);
|
|
43
|
+
const handleClose = React.useCallback(() => {
|
|
44
|
+
if (onClose) {
|
|
45
|
+
onClose();
|
|
46
|
+
}
|
|
47
|
+
if (onOpenChange) {
|
|
48
|
+
onOpenChange(false);
|
|
49
|
+
}
|
|
50
|
+
}, [onClose, onOpenChange]);
|
|
51
|
+
const presentationStyle = React.useMemo(() => {
|
|
52
|
+
if (variant === "sheet" && Platform.OS === "ios") {
|
|
53
|
+
return "pageSheet";
|
|
54
|
+
}
|
|
55
|
+
if (variant === "fullscreen") {
|
|
56
|
+
return "fullScreen";
|
|
57
|
+
}
|
|
58
|
+
return Platform.OS === "ios"
|
|
59
|
+
? "pageSheet"
|
|
60
|
+
: "fullScreen";
|
|
61
|
+
}, [variant]);
|
|
62
|
+
const animationType = React.useMemo(() => {
|
|
63
|
+
if (variant === "sheet") {
|
|
64
|
+
return "slide";
|
|
65
|
+
}
|
|
66
|
+
return "fade";
|
|
67
|
+
}, [variant]);
|
|
68
|
+
return (_jsx(ModalPrimitive.Root, { ref: ref, open: open, onOpenChange: onOpenChange, presentationStyle: presentationStyle, animationType: animationType, ...props, children: _jsx(ModalPrimitive.Overlay, { dismissible: dismissible, onDismiss: handleClose, style: styles.overlay, children: _jsxs(ModalPrimitive.Content, { position: position || "left", size: size || "md", style: [
|
|
69
|
+
styles.content,
|
|
70
|
+
styles[`${variant}Content`],
|
|
71
|
+
styles[`${size}Content`],
|
|
72
|
+
], children: [(title || showCloseButton) && (_jsxs(ModalPrimitive.Header, { withBorder: variant !== "sheet", style: styles.header, children: [_jsx(DialogTitle, { children: title }), showCloseButton && (_jsx(ModalPrimitive.Close, { onClose: handleClose, style: styles.closeButton, children: _jsx(DialogCloseIcon, {}) }))] })), _jsxs(ModalPrimitive.Body, { scrollable: variant !== "fullscreen", style: styles.body, children: [description && (_jsx(DialogDescription, { children: description })), children] })] }) }) }));
|
|
73
|
+
});
|
|
74
|
+
Dialog.displayName = "Dialog";
|
|
75
|
+
export const DialogTitle = forwardRef(({ children, style, ...props }, ref) => {
|
|
76
|
+
const { theme } = useTheme();
|
|
77
|
+
const styles = React.useMemo(() => createStyles(theme), [theme]);
|
|
78
|
+
if (!children)
|
|
79
|
+
return null;
|
|
80
|
+
return (_jsx(Text, { ref: ref, style: [styles.title, style], ...props, children: children }));
|
|
81
|
+
});
|
|
82
|
+
DialogTitle.displayName = "DialogTitle";
|
|
83
|
+
export const DialogDescription = forwardRef(({ children, style, ...props }, ref) => {
|
|
84
|
+
const { theme } = useTheme();
|
|
85
|
+
const styles = React.useMemo(() => createStyles(theme), [theme]);
|
|
86
|
+
if (!children)
|
|
87
|
+
return null;
|
|
88
|
+
return (_jsx(Text, { ref: ref, style: [styles.description, style], ...props, children: children }));
|
|
89
|
+
});
|
|
90
|
+
DialogDescription.displayName = "DialogDescription";
|
|
91
|
+
export const DialogFooter = forwardRef(({ children, direction = "row", justify = "flex-end", withBorder = true, style, ...props }, ref) => {
|
|
92
|
+
const { theme } = useTheme();
|
|
93
|
+
const styles = React.useMemo(() => createStyles(theme), [theme]);
|
|
94
|
+
if (!children)
|
|
95
|
+
return null;
|
|
96
|
+
return (_jsx(ModalPrimitive.Footer, { ref: ref, withBorder: withBorder, direction: direction, justify: justify, style: [styles.footer, style], ...props, children: children }));
|
|
97
|
+
});
|
|
98
|
+
DialogFooter.displayName = "DialogFooter";
|
|
99
|
+
// Dialog Close Icon component (Lucide X)
|
|
100
|
+
const DialogCloseIcon = () => {
|
|
101
|
+
return _jsx(ThemedX, { size: "md", variant: "muted" });
|
|
102
|
+
};
|
|
103
|
+
// Create theme-aware styles
|
|
104
|
+
function createStyles(theme) {
|
|
105
|
+
return StyleSheet.create({
|
|
106
|
+
overlay: {
|
|
107
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
108
|
+
},
|
|
109
|
+
content: {
|
|
110
|
+
backgroundColor: theme.colors.card,
|
|
111
|
+
borderRadius: theme.borderRadius.lg,
|
|
112
|
+
...theme.shadows.lg,
|
|
113
|
+
maxHeight: "90%",
|
|
114
|
+
maxWidth: "90%",
|
|
115
|
+
},
|
|
116
|
+
// Variant styles
|
|
117
|
+
defaultContent: {
|
|
118
|
+
// Default styles already applied in content
|
|
119
|
+
},
|
|
120
|
+
sheetContent: {
|
|
121
|
+
borderTopLeftRadius: theme.borderRadius.xl,
|
|
122
|
+
borderTopRightRadius: theme.borderRadius.xl,
|
|
123
|
+
borderBottomLeftRadius: 0,
|
|
124
|
+
borderBottomRightRadius: 0,
|
|
125
|
+
marginTop: "auto",
|
|
126
|
+
marginBottom: 0,
|
|
127
|
+
maxHeight: "80%",
|
|
128
|
+
width: "100%",
|
|
129
|
+
maxWidth: "100%",
|
|
130
|
+
},
|
|
131
|
+
fullscreenContent: {
|
|
132
|
+
width: "100%",
|
|
133
|
+
height: "100%",
|
|
134
|
+
maxWidth: "100%",
|
|
135
|
+
maxHeight: "100%",
|
|
136
|
+
borderRadius: 0,
|
|
137
|
+
margin: 0,
|
|
138
|
+
},
|
|
139
|
+
// Size styles
|
|
140
|
+
smContent: {
|
|
141
|
+
minWidth: 300,
|
|
142
|
+
minHeight: 200,
|
|
143
|
+
},
|
|
144
|
+
mdContent: {
|
|
145
|
+
minWidth: 400,
|
|
146
|
+
minHeight: 300,
|
|
147
|
+
},
|
|
148
|
+
lgContent: {
|
|
149
|
+
minWidth: 500,
|
|
150
|
+
minHeight: 400,
|
|
151
|
+
},
|
|
152
|
+
xlContent: {
|
|
153
|
+
minWidth: 600,
|
|
154
|
+
minHeight: 500,
|
|
155
|
+
},
|
|
156
|
+
fullContent: {
|
|
157
|
+
width: "95%",
|
|
158
|
+
height: "95%",
|
|
159
|
+
maxWidth: "95%",
|
|
160
|
+
maxHeight: "95%",
|
|
161
|
+
},
|
|
162
|
+
header: {
|
|
163
|
+
paddingHorizontal: theme.spacing[6],
|
|
164
|
+
paddingVertical: theme.spacing[4],
|
|
165
|
+
flexDirection: "row",
|
|
166
|
+
alignItems: "center",
|
|
167
|
+
justifyContent: "space-between",
|
|
168
|
+
},
|
|
169
|
+
body: {
|
|
170
|
+
paddingHorizontal: theme.spacing[6],
|
|
171
|
+
paddingBottom: theme.spacing[6],
|
|
172
|
+
flex: 1,
|
|
173
|
+
},
|
|
174
|
+
footer: {
|
|
175
|
+
paddingHorizontal: theme.spacing[6],
|
|
176
|
+
paddingVertical: theme.spacing[4],
|
|
177
|
+
gap: theme.spacing[2],
|
|
178
|
+
},
|
|
179
|
+
title: {
|
|
180
|
+
fontSize: 20,
|
|
181
|
+
fontWeight: "600",
|
|
182
|
+
color: theme.colors.text,
|
|
183
|
+
flex: 1,
|
|
184
|
+
lineHeight: 24,
|
|
185
|
+
},
|
|
186
|
+
description: {
|
|
187
|
+
fontSize: 16,
|
|
188
|
+
color: theme.colors.textMuted,
|
|
189
|
+
lineHeight: 22,
|
|
190
|
+
marginBottom: theme.spacing[4],
|
|
191
|
+
},
|
|
192
|
+
closeButton: {
|
|
193
|
+
width: theme.touchTargets.minimum,
|
|
194
|
+
height: theme.touchTargets.minimum,
|
|
195
|
+
alignItems: "center",
|
|
196
|
+
justifyContent: "center",
|
|
197
|
+
borderRadius: theme.borderRadius.sm,
|
|
198
|
+
marginLeft: theme.spacing[2],
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// Export dialog variants for external use
|
|
203
|
+
export { dialogVariants };
|