update sound / add random feature
Browse files- .DS_Store +0 -0
- client/public/sounds/dice-1.mp3 +0 -0
- client/public/sounds/dice-2.mp3 +0 -0
- client/public/sounds/dice-3.mp3 +0 -0
- client/public/sounds/lock-1.mp3 +0 -0
- client/public/sounds/tick-1.mp3 +0 -0
- client/public/sounds/tick-2.mp3 +0 -0
- client/public/sounds/tick-3.mp3 +0 -0
- client/public/sounds/tick-4.mp3 +0 -0
- client/src/components/StoryChoices.jsx +77 -5
- client/src/components/UniverseSlotMachine.jsx +33 -2
- client/src/contexts/SoundContext.jsx +31 -2
- client/src/pages/Tutorial.jsx +45 -21
.DS_Store
CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
|
|
client/public/sounds/dice-1.mp3
ADDED
Binary file (31.8 kB). View file
|
|
client/public/sounds/dice-2.mp3
ADDED
Binary file (31.8 kB). View file
|
|
client/public/sounds/dice-3.mp3
ADDED
Binary file (31.8 kB). View file
|
|
client/public/sounds/lock-1.mp3
ADDED
Binary file (5.42 kB). View file
|
|
client/public/sounds/tick-1.mp3
ADDED
Binary file (2.93 kB). View file
|
|
client/public/sounds/tick-2.mp3
ADDED
Binary file (3.41 kB). View file
|
|
client/public/sounds/tick-3.mp3
ADDED
Binary file (4.37 kB). View file
|
|
client/public/sounds/tick-4.mp3
ADDED
Binary file (4.37 kB). View file
|
|
client/src/components/StoryChoices.jsx
CHANGED
@@ -13,6 +13,7 @@ import {
|
|
13 |
useMediaQuery,
|
14 |
useTheme,
|
15 |
IconButton,
|
|
|
16 |
} from "@mui/material";
|
17 |
import { useNavigate } from "react-router-dom";
|
18 |
import { TalkWithSarah } from "./TalkWithSarah";
|
@@ -21,9 +22,24 @@ import { useGame } from "../contexts/GameContext";
|
|
21 |
import { storyApi } from "../utils/api";
|
22 |
import { useSoundEffect } from "../hooks/useSoundEffect";
|
23 |
import CloseIcon from "@mui/icons-material/Close";
|
|
|
24 |
|
25 |
const { initAudioContext } = storyApi;
|
26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
// Function to convert text with ** to Chip elements
|
28 |
const formatTextWithBold = (text) => {
|
29 |
if (!text) return "";
|
@@ -54,6 +70,12 @@ export function StoryChoices() {
|
|
54 |
const [sarahRecommendation, setSarahRecommendation] = useState(null);
|
55 |
const [showCustomDialog, setShowCustomDialog] = useState(false);
|
56 |
const [customChoice, setCustomChoice] = useState("");
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
const {
|
58 |
choices,
|
59 |
onChoice,
|
@@ -76,12 +98,26 @@ export function StoryChoices() {
|
|
76 |
volume: 0.5,
|
77 |
});
|
78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
const lastSegment = getLastSegment();
|
80 |
const isLastStep = lastSegment?.is_last_step;
|
81 |
const isDeath = lastSegment?.isDeath;
|
82 |
const isVictory = lastSegment?.isVictory;
|
83 |
const storyText = lastSegment?.rawText || "";
|
84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
if (isGameOver()) {
|
86 |
return (
|
87 |
<Box
|
@@ -193,13 +229,26 @@ export function StoryChoices() {
|
|
193 |
</Button>
|
194 |
</Box>
|
195 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
<Box
|
197 |
sx={{
|
198 |
display: "flex",
|
199 |
flexDirection: "column",
|
200 |
alignItems: "center",
|
201 |
gap: 1,
|
202 |
-
ml: isMobile ? 0 : 4,
|
203 |
minWidth: "fit-content",
|
204 |
maxWidth: "30%",
|
205 |
}}
|
@@ -216,7 +265,7 @@ export function StoryChoices() {
|
|
216 |
textTransform: "none",
|
217 |
}}
|
218 |
>
|
219 |
-
Write your own
|
220 |
</Button>
|
221 |
</Box>
|
222 |
</>
|
@@ -279,7 +328,7 @@ export function StoryChoices() {
|
|
279 |
rows={isMobile ? 5 : 4}
|
280 |
fullWidth
|
281 |
variant="outlined"
|
282 |
-
placeholder=
|
283 |
value={customChoice}
|
284 |
onChange={(e) => setCustomChoice(e.target.value)}
|
285 |
sx={{
|
@@ -302,7 +351,31 @@ export function StoryChoices() {
|
|
302 |
},
|
303 |
}}
|
304 |
/>
|
305 |
-
<Box
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
306 |
<Button
|
307 |
onClick={() => {
|
308 |
if (customChoice.trim()) {
|
@@ -317,7 +390,6 @@ export function StoryChoices() {
|
|
317 |
disabled={!customChoice.trim()}
|
318 |
variant="contained"
|
319 |
sx={{
|
320 |
-
mt: 1,
|
321 |
py: 1.5,
|
322 |
px: 4,
|
323 |
fontWeight: "bold",
|
|
|
13 |
useMediaQuery,
|
14 |
useTheme,
|
15 |
IconButton,
|
16 |
+
Tooltip,
|
17 |
} from "@mui/material";
|
18 |
import { useNavigate } from "react-router-dom";
|
19 |
import { TalkWithSarah } from "./TalkWithSarah";
|
|
|
22 |
import { storyApi } from "../utils/api";
|
23 |
import { useSoundEffect } from "../hooks/useSoundEffect";
|
24 |
import CloseIcon from "@mui/icons-material/Close";
|
25 |
+
import CasinoOutlinedIcon from "@mui/icons-material/CasinoOutlined";
|
26 |
|
27 |
const { initAudioContext } = storyApi;
|
28 |
|
29 |
+
// Phrases aléatoires WTF pour le placeholder
|
30 |
+
const RANDOM_PLACEHOLDERS = [
|
31 |
+
"A dragon appears right above the hero...",
|
32 |
+
"Suddenly, all the trees start dancing the macarena...",
|
33 |
+
"A time-traveling pizza delivery guy shows up with a mysterious package...",
|
34 |
+
"The ground turns into jello and starts wobbling menacingly...",
|
35 |
+
"A choir of singing cats descends from the sky...",
|
36 |
+
"The hero's shadow detaches itself and starts doing stand-up comedy...",
|
37 |
+
"All the nearby rocks transform into vintage toasters...",
|
38 |
+
"A portal opens, and out steps the hero's evil twin made entirely of cheese...",
|
39 |
+
"The moon starts beatboxing an ominous rhythm...",
|
40 |
+
"Every nearby plant suddenly develops a British accent and starts having tea...",
|
41 |
+
];
|
42 |
+
|
43 |
// Function to convert text with ** to Chip elements
|
44 |
const formatTextWithBold = (text) => {
|
45 |
if (!text) return "";
|
|
|
70 |
const [sarahRecommendation, setSarahRecommendation] = useState(null);
|
71 |
const [showCustomDialog, setShowCustomDialog] = useState(false);
|
72 |
const [customChoice, setCustomChoice] = useState("");
|
73 |
+
const [currentPlaceholder] = useState(
|
74 |
+
() =>
|
75 |
+
RANDOM_PLACEHOLDERS[
|
76 |
+
Math.floor(Math.random() * RANDOM_PLACEHOLDERS.length)
|
77 |
+
]
|
78 |
+
);
|
79 |
const {
|
80 |
choices,
|
81 |
onChoice,
|
|
|
98 |
volume: 0.5,
|
99 |
});
|
100 |
|
101 |
+
// Son de dé
|
102 |
+
const playDiceSound = useSoundEffect({
|
103 |
+
basePath: "/sounds/dice-",
|
104 |
+
numSounds: 3,
|
105 |
+
volume: 0.1,
|
106 |
+
enabled: true,
|
107 |
+
});
|
108 |
+
|
109 |
const lastSegment = getLastSegment();
|
110 |
const isLastStep = lastSegment?.is_last_step;
|
111 |
const isDeath = lastSegment?.isDeath;
|
112 |
const isVictory = lastSegment?.isVictory;
|
113 |
const storyText = lastSegment?.rawText || "";
|
114 |
|
115 |
+
const getRandomPlaceholder = () => {
|
116 |
+
return RANDOM_PLACEHOLDERS[
|
117 |
+
Math.floor(Math.random() * RANDOM_PLACEHOLDERS.length)
|
118 |
+
];
|
119 |
+
};
|
120 |
+
|
121 |
if (isGameOver()) {
|
122 |
return (
|
123 |
<Box
|
|
|
229 |
</Button>
|
230 |
</Box>
|
231 |
|
232 |
+
<Typography
|
233 |
+
variant="h6"
|
234 |
+
sx={{
|
235 |
+
display: { xs: "none", sm: "block" },
|
236 |
+
color: "rgba(255,255,255,0.5)",
|
237 |
+
fontWeight: "bold",
|
238 |
+
fontSize: "1.2rem",
|
239 |
+
mx: 2,
|
240 |
+
}}
|
241 |
+
>
|
242 |
+
OR
|
243 |
+
</Typography>
|
244 |
+
|
245 |
<Box
|
246 |
sx={{
|
247 |
display: "flex",
|
248 |
flexDirection: "column",
|
249 |
alignItems: "center",
|
250 |
gap: 1,
|
251 |
+
// ml: isMobile ? 0 : 4,
|
252 |
minWidth: "fit-content",
|
253 |
maxWidth: "30%",
|
254 |
}}
|
|
|
265 |
textTransform: "none",
|
266 |
}}
|
267 |
>
|
268 |
+
Write your own choice...
|
269 |
</Button>
|
270 |
</Box>
|
271 |
</>
|
|
|
328 |
rows={isMobile ? 5 : 4}
|
329 |
fullWidth
|
330 |
variant="outlined"
|
331 |
+
placeholder={currentPlaceholder}
|
332 |
value={customChoice}
|
333 |
onChange={(e) => setCustomChoice(e.target.value)}
|
334 |
sx={{
|
|
|
351 |
},
|
352 |
}}
|
353 |
/>
|
354 |
+
<Box
|
355 |
+
sx={{ display: "flex", justifyContent: "flex-end", gap: 1, mt: 1 }}
|
356 |
+
>
|
357 |
+
<Button
|
358 |
+
onClick={() => {
|
359 |
+
const randomChoice = getRandomPlaceholder();
|
360 |
+
setCustomChoice(randomChoice.slice(0, -3));
|
361 |
+
playDiceSound();
|
362 |
+
}}
|
363 |
+
variant="outlined"
|
364 |
+
sx={{
|
365 |
+
minWidth: "48px",
|
366 |
+
width: "48px",
|
367 |
+
height: "48px",
|
368 |
+
p: 0,
|
369 |
+
borderColor: "rgba(255, 255, 255, 0.23)",
|
370 |
+
color: "white",
|
371 |
+
"&:hover": {
|
372 |
+
borderColor: "white",
|
373 |
+
backgroundColor: "rgba(255, 255, 255, 0.08)",
|
374 |
+
},
|
375 |
+
}}
|
376 |
+
>
|
377 |
+
<CasinoOutlinedIcon />
|
378 |
+
</Button>
|
379 |
<Button
|
380 |
onClick={() => {
|
381 |
if (customChoice.trim()) {
|
|
|
390 |
disabled={!customChoice.trim()}
|
391 |
variant="contained"
|
392 |
sx={{
|
|
|
393 |
py: 1.5,
|
394 |
px: 4,
|
395 |
fontWeight: "bold",
|
client/src/components/UniverseSlotMachine.jsx
CHANGED
@@ -1,10 +1,11 @@
|
|
1 |
import React, { useEffect, useRef, useState } from "react";
|
2 |
import { Box, Typography, useTheme, useMediaQuery } from "@mui/material";
|
3 |
import { motion, useAnimation } from "framer-motion";
|
|
|
4 |
|
5 |
// Animation timing configuration
|
6 |
const SLOT_ANIMATION_DURATION = 2; // Duration of each slot animation
|
7 |
-
const SLOT_SPEED =
|
8 |
const TOTAL_ANIMATION_DURATION = 1; // Total duration for each slot reel in seconds
|
9 |
const SLOT_START_DELAY = 2; // Delay between each slot start in seconds
|
10 |
|
@@ -26,6 +27,9 @@ const SlotReel = ({ words, isActive, finalValue, onComplete, delay = 0 }) => {
|
|
26 |
const [isVisible, setIsVisible] = useState(false);
|
27 |
const theme = useTheme();
|
28 |
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
|
|
|
|
|
|
29 |
|
30 |
useEffect(() => {
|
31 |
if (isActive) {
|
@@ -38,6 +42,7 @@ const SlotReel = ({ words, isActive, finalValue, onComplete, delay = 0 }) => {
|
|
38 |
setReelItems(repeatedWords);
|
39 |
|
40 |
const itemHeight = isMobile ? 60 : 80;
|
|
|
41 |
const totalHeight = repeatedWords.length * itemHeight;
|
42 |
|
43 |
setTimeout(() => {
|
@@ -49,6 +54,28 @@ const SlotReel = ({ words, isActive, finalValue, onComplete, delay = 0 }) => {
|
|
49 |
duration: TOTAL_ANIMATION_DURATION / SLOT_SPEED,
|
50 |
ease: [0.25, 0.1, 0.25, 1.0],
|
51 |
times: [0, 1],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
},
|
53 |
})
|
54 |
.then(() => {
|
@@ -56,7 +83,11 @@ const SlotReel = ({ words, isActive, finalValue, onComplete, delay = 0 }) => {
|
|
56 |
});
|
57 |
}, delay * SLOT_START_DELAY * 1000);
|
58 |
}
|
59 |
-
|
|
|
|
|
|
|
|
|
60 |
|
61 |
return (
|
62 |
<Box
|
|
|
1 |
import React, { useEffect, useRef, useState } from "react";
|
2 |
import { Box, Typography, useTheme, useMediaQuery } from "@mui/material";
|
3 |
import { motion, useAnimation } from "framer-motion";
|
4 |
+
import { useSoundSystem } from "../contexts/SoundContext";
|
5 |
|
6 |
// Animation timing configuration
|
7 |
const SLOT_ANIMATION_DURATION = 2; // Duration of each slot animation
|
8 |
+
const SLOT_SPEED = 1; // Base speed of the slot animation (higher = faster)
|
9 |
const TOTAL_ANIMATION_DURATION = 1; // Total duration for each slot reel in seconds
|
10 |
const SLOT_START_DELAY = 2; // Delay between each slot start in seconds
|
11 |
|
|
|
27 |
const [isVisible, setIsVisible] = useState(false);
|
28 |
const theme = useTheme();
|
29 |
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
30 |
+
const { playSound } = useSoundSystem();
|
31 |
+
const lastPositionRef = useRef(0);
|
32 |
+
const itemHeightRef = useRef(0);
|
33 |
|
34 |
useEffect(() => {
|
35 |
if (isActive) {
|
|
|
42 |
setReelItems(repeatedWords);
|
43 |
|
44 |
const itemHeight = isMobile ? 60 : 80;
|
45 |
+
itemHeightRef.current = itemHeight;
|
46 |
const totalHeight = repeatedWords.length * itemHeight;
|
47 |
|
48 |
setTimeout(() => {
|
|
|
54 |
duration: TOTAL_ANIMATION_DURATION / SLOT_SPEED,
|
55 |
ease: [0.25, 0.1, 0.25, 1.0],
|
56 |
times: [0, 1],
|
57 |
+
onUpdate: (latest) => {
|
58 |
+
// Calculer l'index du mot actuel basé sur la position
|
59 |
+
const currentPosition = Math.abs(latest);
|
60 |
+
const currentIndex = Math.floor(currentPosition / itemHeight);
|
61 |
+
|
62 |
+
// Si on a changé de mot, jouer le son
|
63 |
+
if (
|
64 |
+
Math.floor(lastPositionRef.current / itemHeight) !==
|
65 |
+
currentIndex
|
66 |
+
) {
|
67 |
+
// Vérifier si c'est le dernier mot (final)
|
68 |
+
const isFinalWord = currentIndex === repeatedWords.length - 1;
|
69 |
+
// Jouer le son approprié
|
70 |
+
if (isFinalWord) {
|
71 |
+
playSound("lock");
|
72 |
+
} else {
|
73 |
+
playSound("tick", "normal");
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
lastPositionRef.current = currentPosition;
|
78 |
+
},
|
79 |
},
|
80 |
})
|
81 |
.then(() => {
|
|
|
83 |
});
|
84 |
}, delay * SLOT_START_DELAY * 1000);
|
85 |
}
|
86 |
+
|
87 |
+
return () => {
|
88 |
+
lastPositionRef.current = 0;
|
89 |
+
};
|
90 |
+
}, [isActive, finalValue, words, delay, isMobile, playSound]);
|
91 |
|
92 |
return (
|
93 |
<Box
|
client/src/contexts/SoundContext.jsx
CHANGED
@@ -35,6 +35,17 @@ const SOUNDS = {
|
|
35 |
off: "/sounds/talky-walky-off.mp3",
|
36 |
volume: 0.5,
|
37 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
};
|
39 |
|
40 |
const SoundContext = createContext(null);
|
@@ -69,7 +80,19 @@ export function SoundProvider({ children }) {
|
|
69 |
// Initialiser tous les sons
|
70 |
const soundInstances = {};
|
71 |
Object.entries(SOUNDS).forEach(([category, config]) => {
|
72 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
// Pour les sons avec plusieurs variations
|
74 |
soundInstances[category] = config.files.map((file) => {
|
75 |
const [play] = useSound(file, { volume: config.volume });
|
@@ -102,7 +125,13 @@ export function SoundProvider({ children }) {
|
|
102 |
if (!isSoundEnabled) return;
|
103 |
|
104 |
try {
|
105 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
// Pour les sons avec sous-catégories (comme talkySarah.on)
|
107 |
soundInstances[category][subCategory]?.();
|
108 |
} else if (Array.isArray(soundInstances[category])) {
|
|
|
35 |
off: "/sounds/talky-walky-off.mp3",
|
36 |
volume: 0.5,
|
37 |
},
|
38 |
+
tick: {
|
39 |
+
files: Array.from({ length: 4 }, (_, i) => `/sounds/tick-${i + 1}.mp3`),
|
40 |
+
volume: {
|
41 |
+
normal: 0.05, // Volume normal à 50% du volume final
|
42 |
+
final: 0.4, // Volume final (comme avant)
|
43 |
+
},
|
44 |
+
},
|
45 |
+
lock: {
|
46 |
+
files: ["/sounds/lock-1.mp3"],
|
47 |
+
volume: 0.025,
|
48 |
+
},
|
49 |
};
|
50 |
|
51 |
const SoundContext = createContext(null);
|
|
|
80 |
// Initialiser tous les sons
|
81 |
const soundInstances = {};
|
82 |
Object.entries(SOUNDS).forEach(([category, config]) => {
|
83 |
+
if (category === "tick") {
|
84 |
+
// Initialisation spéciale pour les ticks avec volumes différents
|
85 |
+
soundInstances[category] = {
|
86 |
+
normal: config.files.map((file) => {
|
87 |
+
const [play] = useSound(file, { volume: config.volume.normal });
|
88 |
+
return play;
|
89 |
+
}),
|
90 |
+
final: config.files.map((file) => {
|
91 |
+
const [play] = useSound(file, { volume: config.volume.final });
|
92 |
+
return play;
|
93 |
+
}),
|
94 |
+
};
|
95 |
+
} else if (Array.isArray(config.files)) {
|
96 |
// Pour les sons avec plusieurs variations
|
97 |
soundInstances[category] = config.files.map((file) => {
|
98 |
const [play] = useSound(file, { volume: config.volume });
|
|
|
125 |
if (!isSoundEnabled) return;
|
126 |
|
127 |
try {
|
128 |
+
if (category === "tick") {
|
129 |
+
// Pour les ticks avec volumes différents
|
130 |
+
const type = subCategory || "normal";
|
131 |
+
const sounds = soundInstances[category][type];
|
132 |
+
const randomIndex = Math.floor(Math.random() * sounds.length);
|
133 |
+
sounds[randomIndex]?.();
|
134 |
+
} else if (subCategory) {
|
135 |
// Pour les sons avec sous-catégories (comme talkySarah.on)
|
136 |
soundInstances[category][subCategory]?.();
|
137 |
} else if (Array.isArray(soundInstances[category])) {
|
client/src/pages/Tutorial.jsx
CHANGED
@@ -93,17 +93,20 @@ export function Tutorial() {
|
|
93 |
<Box
|
94 |
sx={{
|
95 |
display: "flex",
|
96 |
-
gap: 4,
|
97 |
justifyContent: "center",
|
98 |
-
mb: 2,
|
99 |
alignItems: "center",
|
|
|
|
|
100 |
}}
|
101 |
>
|
102 |
<Box
|
103 |
sx={{
|
104 |
position: "relative",
|
105 |
-
flex: 1,
|
106 |
-
|
|
|
107 |
"&::before": {
|
108 |
content: '""',
|
109 |
position: "absolute",
|
@@ -113,32 +116,39 @@ export function Tutorial() {
|
|
113 |
bottom: 0,
|
114 |
background: "rgba(255, 255, 255, 0.05)",
|
115 |
backdropFilter: "blur(10px)",
|
116 |
-
WebkitBackdropFilter: "blur(10px)",
|
117 |
borderRadius: "8px",
|
118 |
-
border: "1px solid
|
|
|
119 |
},
|
120 |
}}
|
121 |
>
|
122 |
<Box
|
123 |
sx={{
|
124 |
position: "relative",
|
125 |
-
p: 2,
|
126 |
display: "flex",
|
127 |
flexDirection: "column",
|
128 |
alignItems: "center",
|
129 |
-
gap:
|
130 |
zIndex: 1,
|
131 |
}}
|
132 |
>
|
133 |
<CallSplitOutlinedIcon
|
134 |
sx={{
|
135 |
-
fontSize: 40,
|
136 |
-
color: "primary.
|
137 |
-
mb:
|
138 |
-
transform: "rotate(90deg)",
|
139 |
}}
|
140 |
/>
|
141 |
-
<Typography
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
Make a choice
|
143 |
</Typography>
|
144 |
</Box>
|
@@ -149,6 +159,8 @@ export function Tutorial() {
|
|
149 |
sx={{
|
150 |
color: "rgba(255,255,255,0.5)",
|
151 |
fontWeight: "bold",
|
|
|
|
|
152 |
}}
|
153 |
>
|
154 |
OR
|
@@ -157,8 +169,9 @@ export function Tutorial() {
|
|
157 |
<Box
|
158 |
sx={{
|
159 |
position: "relative",
|
160 |
-
flex: 1,
|
161 |
-
|
|
|
162 |
"&::before": {
|
163 |
content: '""',
|
164 |
position: "absolute",
|
@@ -168,27 +181,38 @@ export function Tutorial() {
|
|
168 |
bottom: 0,
|
169 |
background: "rgba(255, 255, 255, 0.05)",
|
170 |
backdropFilter: "blur(10px)",
|
171 |
-
WebkitBackdropFilter: "blur(10px)",
|
172 |
borderRadius: "8px",
|
173 |
-
border: "1px solid
|
|
|
174 |
},
|
175 |
}}
|
176 |
>
|
177 |
<Box
|
178 |
sx={{
|
179 |
position: "relative",
|
180 |
-
p: 2,
|
181 |
display: "flex",
|
182 |
flexDirection: "column",
|
183 |
alignItems: "center",
|
184 |
-
gap:
|
185 |
zIndex: 1,
|
186 |
}}
|
187 |
>
|
188 |
<CreateOutlinedIcon
|
189 |
-
sx={{
|
|
|
|
|
|
|
|
|
190 |
/>
|
191 |
-
<Typography
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
Write your own
|
193 |
</Typography>
|
194 |
</Box>
|
|
|
93 |
<Box
|
94 |
sx={{
|
95 |
display: "flex",
|
96 |
+
gap: { xs: 1, sm: 4 },
|
97 |
justifyContent: "center",
|
98 |
+
mb: { xs: 1, sm: 2 },
|
99 |
alignItems: "center",
|
100 |
+
flexDirection: { xs: "column", sm: "row" },
|
101 |
+
width: "100%",
|
102 |
}}
|
103 |
>
|
104 |
<Box
|
105 |
sx={{
|
106 |
position: "relative",
|
107 |
+
flex: { xs: "none", sm: 1 },
|
108 |
+
width: { xs: "50%", sm: "auto" },
|
109 |
+
maxWidth: { xs: "160px", sm: "200px" },
|
110 |
"&::before": {
|
111 |
content: '""',
|
112 |
position: "absolute",
|
|
|
116 |
bottom: 0,
|
117 |
background: "rgba(255, 255, 255, 0.05)",
|
118 |
backdropFilter: "blur(10px)",
|
119 |
+
WebkitBackdropFilter: "blur(10px)",
|
120 |
borderRadius: "8px",
|
121 |
+
border: "1px solid",
|
122 |
+
borderColor: "primary.main",
|
123 |
},
|
124 |
}}
|
125 |
>
|
126 |
<Box
|
127 |
sx={{
|
128 |
position: "relative",
|
129 |
+
p: { xs: 1.5, sm: 2 },
|
130 |
display: "flex",
|
131 |
flexDirection: "column",
|
132 |
alignItems: "center",
|
133 |
+
gap: 0.5,
|
134 |
zIndex: 1,
|
135 |
}}
|
136 |
>
|
137 |
<CallSplitOutlinedIcon
|
138 |
sx={{
|
139 |
+
fontSize: { xs: 28, sm: 40 },
|
140 |
+
color: "primary.main",
|
141 |
+
mb: 0.5,
|
142 |
+
transform: "rotate(90deg)",
|
143 |
}}
|
144 |
/>
|
145 |
+
<Typography
|
146 |
+
variant="subtitle1"
|
147 |
+
sx={{
|
148 |
+
color: "primary.main",
|
149 |
+
fontSize: { xs: "0.875rem", sm: "1rem" },
|
150 |
+
}}
|
151 |
+
>
|
152 |
Make a choice
|
153 |
</Typography>
|
154 |
</Box>
|
|
|
159 |
sx={{
|
160 |
color: "rgba(255,255,255,0.5)",
|
161 |
fontWeight: "bold",
|
162 |
+
fontSize: { xs: "1rem", sm: "1.2rem" },
|
163 |
+
my: { xs: 0.25, sm: 0 },
|
164 |
}}
|
165 |
>
|
166 |
OR
|
|
|
169 |
<Box
|
170 |
sx={{
|
171 |
position: "relative",
|
172 |
+
flex: { xs: "none", sm: 1 },
|
173 |
+
width: { xs: "50%", sm: "auto" },
|
174 |
+
maxWidth: { xs: "160px", sm: "200px" },
|
175 |
"&::before": {
|
176 |
content: '""',
|
177 |
position: "absolute",
|
|
|
181 |
bottom: 0,
|
182 |
background: "rgba(255, 255, 255, 0.05)",
|
183 |
backdropFilter: "blur(10px)",
|
184 |
+
WebkitBackdropFilter: "blur(10px)",
|
185 |
borderRadius: "8px",
|
186 |
+
border: "1px solid",
|
187 |
+
borderColor: "secondary.main",
|
188 |
},
|
189 |
}}
|
190 |
>
|
191 |
<Box
|
192 |
sx={{
|
193 |
position: "relative",
|
194 |
+
p: { xs: 1.5, sm: 2 },
|
195 |
display: "flex",
|
196 |
flexDirection: "column",
|
197 |
alignItems: "center",
|
198 |
+
gap: 0.5,
|
199 |
zIndex: 1,
|
200 |
}}
|
201 |
>
|
202 |
<CreateOutlinedIcon
|
203 |
+
sx={{
|
204 |
+
fontSize: { xs: 28, sm: 40 },
|
205 |
+
color: "secondary.main",
|
206 |
+
mb: 0.5,
|
207 |
+
}}
|
208 |
/>
|
209 |
+
<Typography
|
210 |
+
variant="subtitle1"
|
211 |
+
sx={{
|
212 |
+
color: "secondary.main",
|
213 |
+
fontSize: { xs: "0.875rem", sm: "1rem" },
|
214 |
+
}}
|
215 |
+
>
|
216 |
Write your own
|
217 |
</Typography>
|
218 |
</Box>
|