mirror of
https://github.com/Vikeo/LifeTrinket.git
synced 2025-11-11 21:56:25 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
354c0dbbb2 | ||
|
|
3770d13beb | ||
|
|
13733242a2 | ||
|
|
81f3891b20 | ||
|
|
e153de9093 | ||
|
|
07775f85d2 | ||
|
|
10039175a1 | ||
|
|
bcf2a0a840 | ||
|
|
d25da5d97b | ||
|
|
f5a80e573e | ||
|
|
1f36264e39 | ||
|
|
d615cfd3ba | ||
|
|
4453b12ce6 | ||
|
|
d601a820f8 | ||
|
|
0455f43794 | ||
|
|
f94103fe51 | ||
|
|
c36668b933 | ||
|
|
f9d0346300 | ||
|
|
2f3ee74c74 | ||
|
|
f8f0788b97 | ||
|
|
bfe25eacb7 |
@@ -1,8 +1,8 @@
|
|||||||
robots.txt,1693082171694,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
|
index.html,1705225256081,6ef0d7e2de82bf64addbb9294fb28845fd06daaa544b010a47422c12ae3ad97f
|
||||||
manifest.json,1693082171694,91ce94afb71f33a477f5d8d48c3f98bd7de422279c74f17b6500eec72003ac1a
|
robots.txt,1705225255906,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
|
||||||
assets/index-5265c558.css,1693082171837,08c4451946bbdf520fe337edb365417a8bbf91914c018b83866723ef52d57b43
|
manifest.json,1705225255906,91ce94afb71f33a477f5d8d48c3f98bd7de422279c74f17b6500eec72003ac1a
|
||||||
index.html,1693082171837,09e1919fbaaa3a0bf08f43eb46c29136d62a7747b41f8b5d0f4a7ed23337c344
|
assets/index-08359bdb.css,1705225256081,d2766260d28230d960d75362810713efaddf40687205e697432b52869f162af7
|
||||||
logo192.png,1693082171693,4309255bccbdbb341b5ab88708677e3d43b9e171d2666528ff932295a8257e4e
|
logo192.png,1705225255905,3b0fcf91fe2128f493de0bce2f6e2d35520a4260a04e05b8d855181359b3d3fe
|
||||||
favicon.ico,1693082171692,48d8c1b9714dbc9bcb012d9c9f04112d229f20e6c889bda588ac159f973e6a8d
|
favicon.ico,1705225255905,75661e6187b524767554b4f28ec09a64bc72b0bb102a0b453aaead88519d9ed3
|
||||||
logo512.png,1693082171694,92c7c05dc98170596d04f48e5e60eaae9535f409bcaeff129fd98fef8aba9f4e
|
logo512.png,1705225255906,cf49739c9e6890bbfcd4157f299dde425df60759b7320ae9188d7ab9dc51e8ca
|
||||||
assets/index-5023e89e.js,1693082171838,8a6177168e95e1ca90e5ad8774252a8a02a9a78765bd329b7deae729c01aedf3
|
assets/index-20658f4b.js,1705225256081,742f2c10740beea3a23f269aa6266b3c288d1fd9c7e20b6829034e8a898bf1e1
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "life-trinket",
|
"name": "life-trinket",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.5.41",
|
"version": "0.6.0",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18",
|
"node": ">=18",
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"generate-icons": "npx @svgr/cli src/Icons/svgs",
|
"generate-icons": "npx @svgr/cli src/Icons/svgs",
|
||||||
"deploy": "bun build && firebase deploy --only hosting"
|
"deploy": "bun run build && firebase deploy --only hosting"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mui/material": "^5.13.6",
|
"@mui/material": "^5.13.6",
|
||||||
@@ -43,8 +43,9 @@
|
|||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"prettier": "2.8.8",
|
"prettier": "2.8.8",
|
||||||
"tailwindcss": "^3.4.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^4.4.5"
|
"vite": "^5.0.12",
|
||||||
|
"vite-plugin-pwa": "^0.17.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const PlayerLostWrapper = twc.div<RotationDivProps>((props) => [
|
|||||||
: '',
|
: '',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const DynamicText = twc.div`text-[8vmin]`;
|
const DynamicText = twc.div`text-[8vmin] whitespace-nowrap`;
|
||||||
|
|
||||||
const hasCommanderDamageReached21 = (player: Player) => {
|
const hasCommanderDamageReached21 = (player: Player) => {
|
||||||
const commanderDamageTotals = player.commanderDamage.map(
|
const commanderDamageTotals = player.commanderDamage.map(
|
||||||
@@ -131,7 +131,11 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
|
|||||||
<StartingPlayerNoticeWrapper
|
<StartingPlayerNoticeWrapper
|
||||||
style={{ rotate: `${calcRotation}deg` }}
|
style={{ rotate: `${calcRotation}deg` }}
|
||||||
>
|
>
|
||||||
<DynamicText style={{ rotate: `${calcTextRotation}deg` }}>
|
<DynamicText
|
||||||
|
style={{
|
||||||
|
rotate: `${calcTextRotation}deg`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
You start!
|
You start!
|
||||||
</DynamicText>
|
</DynamicText>
|
||||||
</StartingPlayerNoticeWrapper>
|
</StartingPlayerNoticeWrapper>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Modal } from '@mui/material';
|
import { Modal } from '@mui/material';
|
||||||
import { theme } from '../../Data/theme';
|
|
||||||
import { twc } from 'react-twc';
|
import { twc } from 'react-twc';
|
||||||
|
import { Separator } from './Separator';
|
||||||
|
import { Paragraph } from './TextComponents';
|
||||||
|
|
||||||
export const ModalWrapper = twc.div`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[80vw] h-[85vh] bg-background-default p-4 overflow-scroll rounded-2xl border-none text-text-primary`;
|
export const ModalWrapper = twc.div`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 h-[85vh] bg-background-default p-4 overflow-scroll rounded-2xl border-none text-text-primary w-[95vw] max-w-[548px]`;
|
||||||
|
|
||||||
type InfoModalProps = {
|
type InfoModalProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -11,73 +12,77 @@ type InfoModalProps = {
|
|||||||
|
|
||||||
export const InfoModal = ({ isOpen, closeModal }: InfoModalProps) => {
|
export const InfoModal = ({ isOpen, closeModal }: InfoModalProps) => {
|
||||||
return (
|
return (
|
||||||
<Modal open={isOpen} onClose={closeModal}>
|
<Modal
|
||||||
<ModalWrapper>
|
open={isOpen}
|
||||||
<div>
|
onClose={closeModal}
|
||||||
<h2 style={{ textAlign: 'center' }}>📋 Usage Guide</h2>
|
style={{ display: 'flex', justifyContent: 'center' }}
|
||||||
<p>
|
>
|
||||||
There are some controls that you might not know about, so here's a
|
<>
|
||||||
short list of them.
|
<div className="flex relative w-full max-w-[548px]">
|
||||||
</p>
|
<button
|
||||||
|
onClick={closeModal}
|
||||||
<h3>Life counter</h3>
|
className="flex absolute top-10 right-0 z-10 w-10 h-10 text-common-white bg-primary-main items-center justify-center rounded-full border-solid border-primary-dark border-2"
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Tap</strong> on a player's + or - button to add or
|
|
||||||
subtract <strong>1 life</strong>.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Long press</strong> on a player's + or - button to add or
|
|
||||||
subtract <strong>10 life</strong>.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3>Commander damage and other counters</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Tap</strong> on the counter to add{' '}
|
|
||||||
<strong>1 counter</strong>.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Long press</strong> on the counter to subtract{' '}
|
|
||||||
<strong>1 counter</strong>.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3>Other</h3>
|
|
||||||
<p>
|
|
||||||
When a player is <strong>at or below 0 life</strong>, has taken{' '}
|
|
||||||
<strong>21 or more Commander Damage</strong> or has{' '}
|
|
||||||
<strong>10 or more poison counters</strong>, a button with a skull
|
|
||||||
will appear on that player's card.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Tap on the button to mark that player as lost, dimming their player
|
|
||||||
card.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
textAlign: 'center',
|
|
||||||
marginTop: '1rem',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Visit my
|
|
||||||
<a
|
|
||||||
href="https://github.com/Vikeo/LifeTrinket"
|
|
||||||
target="_blank"
|
|
||||||
style={{
|
|
||||||
textDecoration: 'none',
|
|
||||||
color: theme.palette.primary.light,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{' '}
|
X
|
||||||
GitHub{' '}
|
</button>
|
||||||
</a>
|
|
||||||
for more info about this web app.
|
|
||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
<ModalWrapper>
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl text-center mb-4">📋 Usage Guide</h2>
|
||||||
|
<Separator height="1px" />
|
||||||
|
<Paragraph className="my-4">
|
||||||
|
There are some controls that you might not know about, so here's a
|
||||||
|
short list of them.
|
||||||
|
</Paragraph>
|
||||||
|
<h3 className="text-lg font-bold mb-2">Life counter</h3>
|
||||||
|
<ul className="list-disc ml-6 mb-4">
|
||||||
|
<li>
|
||||||
|
<strong>Tap</strong> on a player's + or - button to add or
|
||||||
|
subtract <strong>1 life</strong>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Long press</strong> on a player's + or - button to add
|
||||||
|
or subtract <strong>10 life</strong>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-bold mb-2">
|
||||||
|
Commander damage and other counters
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc ml-6 mb-4">
|
||||||
|
<li>
|
||||||
|
<strong>Tap</strong> on the counter to add{' '}
|
||||||
|
<strong>1 counter</strong>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Long press</strong> on the counter to subtract{' '}
|
||||||
|
<strong>1 counter</strong>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-bold mb-2">Other</h3>
|
||||||
|
<Paragraph className="mb-4">
|
||||||
|
When a player is <strong>at or below 0 life</strong>, has taken{' '}
|
||||||
|
<strong>21 or more Commander Damage</strong> or has{' '}
|
||||||
|
<strong>10 or more poison counters</strong>, a button with a skull
|
||||||
|
will appear on that player's card. Tapping it will dim the
|
||||||
|
player's card.
|
||||||
|
</Paragraph>
|
||||||
|
</div>
|
||||||
|
<div className="text-center mt-4">
|
||||||
|
Visit my
|
||||||
|
<a
|
||||||
|
href="https://github.com/Vikeo/LifeTrinket"
|
||||||
|
target="_blank"
|
||||||
|
className="text-text-secondary underline"
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
GitHub{' '}
|
||||||
|
</a>
|
||||||
|
for more info about this web app.
|
||||||
|
</div>
|
||||||
|
</ModalWrapper>
|
||||||
|
</>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const Separator = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`bg-common-black bg-opacity-30 rounded-full mt-2 mb-2`}
|
className={`bg-common-white bg-opacity-30 rounded-full mt-2 mb-2`}
|
||||||
style={{ width, height }}
|
style={{ width, height }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
|||||||
import { ModalWrapper } from './InfoModal';
|
import { ModalWrapper } from './InfoModal';
|
||||||
import { Separator } from './Separator';
|
import { Separator } from './Separator';
|
||||||
import { Paragraph } from './TextComponents';
|
import { Paragraph } from './TextComponents';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
const SettingContainer = twc.div`w-full flex flex-col`;
|
const SettingContainer = twc.div`w-full flex flex-col`;
|
||||||
|
|
||||||
@@ -44,17 +44,19 @@ export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => {
|
|||||||
const data = await result.json();
|
const data = await result.json();
|
||||||
|
|
||||||
if (!data.name) {
|
if (!data.name) {
|
||||||
setIsLatestVersion(false);
|
|
||||||
setNewVersion(undefined);
|
setNewVersion(undefined);
|
||||||
|
setIsLatestVersion(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setNewVersion(data.name);
|
||||||
|
|
||||||
/* @ts-expect-error is defined in vite.config.ts*/
|
/* @ts-expect-error is defined in vite.config.ts*/
|
||||||
if (data.name === APP_VERSION) {
|
if (data.name === APP_VERSION) {
|
||||||
setNewVersion(data.name);
|
|
||||||
setIsLatestVersion(true);
|
setIsLatestVersion(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLatestVersion(false);
|
setIsLatestVersion(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('error getting latest version string', error);
|
console.error('error getting latest version string', error);
|
||||||
@@ -65,115 +67,129 @@ export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={isOpen} onClose={closeModal}>
|
<Modal open={isOpen} onClose={closeModal}>
|
||||||
<ModalWrapper>
|
<>
|
||||||
<Container>
|
<div className="flex relative w-full max-w-[548px]">
|
||||||
<h2 style={{ textAlign: 'center' }}>⚙️ Settings ⚙️</h2>
|
<button
|
||||||
<SettingContainer>
|
onClick={closeModal}
|
||||||
<ToggleContainer>
|
className="flex absolute top-10 right-0 z-10 w-10 h-10 text-common-white bg-primary-main items-center justify-center rounded-full border-solid border-primary-dark border-2"
|
||||||
<FormLabel>Show Start Player</FormLabel>
|
>
|
||||||
<Switch
|
X
|
||||||
checked={settings.showStartingPlayer}
|
</button>
|
||||||
onChange={() => {
|
</div>
|
||||||
setSettings({
|
<ModalWrapper>
|
||||||
...settings,
|
<Container>
|
||||||
showStartingPlayer: !settings.showStartingPlayer,
|
<h2 className="text-center text-2xl mb-2">⚙️ Settings ⚙️</h2>
|
||||||
});
|
<Separator height="1px" />
|
||||||
}}
|
<SettingContainer>
|
||||||
/>
|
<ToggleContainer>
|
||||||
</ToggleContainer>
|
<FormLabel>Show Start Player</FormLabel>
|
||||||
<Description>
|
<Switch
|
||||||
On start or reset of game, will pick a random player who will
|
checked={settings.showStartingPlayer}
|
||||||
start first if this is enabled.
|
onChange={() => {
|
||||||
</Description>
|
setSettings({
|
||||||
</SettingContainer>
|
...settings,
|
||||||
<SettingContainer>
|
showStartingPlayer: !settings.showStartingPlayer,
|
||||||
<ToggleContainer>
|
});
|
||||||
<FormLabel>Keep Awake</FormLabel>
|
}}
|
||||||
<Switch
|
/>
|
||||||
checked={settings.keepAwake}
|
</ToggleContainer>
|
||||||
onChange={() => {
|
<Description>
|
||||||
setSettings({ ...settings, keepAwake: !settings.keepAwake });
|
On start or reset of game, will pick a random player who will
|
||||||
}}
|
start first if this is enabled.
|
||||||
/>
|
</Description>
|
||||||
</ToggleContainer>
|
</SettingContainer>
|
||||||
<Description>
|
<SettingContainer>
|
||||||
Will prevent device from going to sleep while this app is open if
|
<ToggleContainer>
|
||||||
this is enabled.
|
<FormLabel>Keep Awake</FormLabel>
|
||||||
</Description>
|
<Switch
|
||||||
</SettingContainer>
|
checked={settings.keepAwake}
|
||||||
<SettingContainer>
|
onChange={() => {
|
||||||
<ToggleContainer>
|
setSettings({
|
||||||
<FormLabel>Go fullscreen on start (Android only)</FormLabel>
|
...settings,
|
||||||
<Switch
|
keepAwake: !settings.keepAwake,
|
||||||
checked={settings.goFullscreenOnStart}
|
});
|
||||||
onChange={() => {
|
}}
|
||||||
setSettings({
|
/>
|
||||||
...settings,
|
</ToggleContainer>
|
||||||
goFullscreenOnStart: !settings.goFullscreenOnStart,
|
<Description>
|
||||||
});
|
Will prevent device from going to sleep while this app is open
|
||||||
}}
|
if this is enabled.
|
||||||
/>
|
</Description>
|
||||||
</ToggleContainer>
|
</SettingContainer>
|
||||||
<Description>
|
<SettingContainer>
|
||||||
Will enter fullscreen mode when starting a game if this is
|
<ToggleContainer>
|
||||||
enabled.
|
<FormLabel>Go fullscreen on start (Android only)</FormLabel>
|
||||||
</Description>
|
<Switch
|
||||||
</SettingContainer>
|
checked={settings.goFullscreenOnStart}
|
||||||
{!isPWA && (
|
onChange={() => {
|
||||||
<>
|
setSettings({
|
||||||
<Separator height="1px" />
|
...settings,
|
||||||
<SettingContainer>
|
goFullscreenOnStart: !settings.goFullscreenOnStart,
|
||||||
<ToggleContainer>
|
});
|
||||||
<Paragraph>
|
}}
|
||||||
<b>Tip:</b> You can{' '}
|
/>
|
||||||
<b>add this webapp to your home page on iOS</b> or{' '}
|
</ToggleContainer>
|
||||||
<b>install it on Android</b> to have it act just like a
|
<Description>
|
||||||
normal app!
|
Will enter fullscreen mode when starting a game if this is
|
||||||
</Paragraph>
|
enabled.
|
||||||
</ToggleContainer>
|
</Description>
|
||||||
<Description className="mt-1">
|
</SettingContainer>
|
||||||
If you do, this app will work offline and the toolbar will be
|
{!isPWA && (
|
||||||
automatically hidden.
|
<>
|
||||||
</Description>
|
<Separator height="1px" />
|
||||||
</SettingContainer>
|
<SettingContainer>
|
||||||
</>
|
<ToggleContainer>
|
||||||
)}
|
<Paragraph>
|
||||||
<Separator height="1px" />
|
<b>Tip:</b> You can{' '}
|
||||||
<SettingContainer>
|
<b>add this webapp to your home page on iOS</b> or{' '}
|
||||||
<Paragraph>
|
<b>install it on Android</b> to have it act just like a
|
||||||
{/* @ts-expect-error is defined in vite.config.ts*/}
|
normal app!
|
||||||
Current version: {APP_VERSION}{' '}
|
</Paragraph>
|
||||||
{isLatestVersion && (
|
</ToggleContainer>
|
||||||
<span className="text-sm text-text-secondary">(latest)</span>
|
<Description className="mt-1">
|
||||||
)}
|
If you do, this app will work offline and the toolbar will
|
||||||
</Paragraph>
|
be automatically hidden.
|
||||||
{!isLatestVersion && newVersion && (
|
</Description>
|
||||||
<Paragraph className="text-text-secondary text-lg text-center">
|
</SettingContainer>
|
||||||
New version ({newVersion}) is available!{' '}
|
</>
|
||||||
</Paragraph>
|
|
||||||
)}
|
)}
|
||||||
</SettingContainer>
|
<Separator height="1px" />
|
||||||
{!isLatestVersion && newVersion && (
|
<SettingContainer>
|
||||||
|
<Paragraph>
|
||||||
|
{/* @ts-expect-error is defined in vite.config.ts*/}
|
||||||
|
Current version: {APP_VERSION}{' '}
|
||||||
|
{isLatestVersion && (
|
||||||
|
<span className="text-sm text-text-secondary">(latest)</span>
|
||||||
|
)}
|
||||||
|
</Paragraph>
|
||||||
|
{!isLatestVersion && newVersion && (
|
||||||
|
<Paragraph className="text-text-secondary text-lg text-center">
|
||||||
|
New version ({newVersion}) is available!{' '}
|
||||||
|
</Paragraph>
|
||||||
|
)}
|
||||||
|
</SettingContainer>
|
||||||
|
{!isLatestVersion && newVersion && (
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
style={{ marginTop: '0.25rem', marginBottom: '0.25rem' }}
|
||||||
|
onClick={() => window?.location?.reload()}
|
||||||
|
>
|
||||||
|
<span>Update</span>
|
||||||
|
<span className="text-xs"> (reload app)</span>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Separator height="1px" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
style={{ marginTop: '0.25rem', marginBottom: '0.25rem' }}
|
onClick={closeModal}
|
||||||
onClick={() => window?.location?.reload()}
|
style={{ marginTop: '0.25rem' }}
|
||||||
>
|
>
|
||||||
<span>Update</span>
|
Save and Close
|
||||||
<span className="text-xs"> (reload app)</span>
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</Container>
|
||||||
<Separator height="1px" />
|
</ModalWrapper>
|
||||||
|
</>
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={closeModal}
|
|
||||||
style={{ marginTop: '0.25rem' }}
|
|
||||||
>
|
|
||||||
Save and Close
|
|
||||||
</Button>
|
|
||||||
</Container>
|
|
||||||
</ModalWrapper>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -373,25 +373,27 @@ const PlayerMenu = ({ player, setShowPlayerMenu }: PlayerMenuProps) => {
|
|||||||
</BetterRowContainer>
|
</BetterRowContainer>
|
||||||
<dialog
|
<dialog
|
||||||
ref={dialogRef}
|
ref={dialogRef}
|
||||||
className="z-[9999] bg-background-default text-text-primary rounded-2xl border-none absolute top-[10%]"
|
className="z-[9999] min-h-2/4 bg-background-default text-text-primary rounded-2xl border-none absolute top-[10%]"
|
||||||
>
|
>
|
||||||
<h1>Reset Game?</h1>
|
<div className="h-full flex flex-col p-4 gap-2">
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-evenly' }}>
|
<h1 className="text-center">Reset Game?</h1>
|
||||||
<Button
|
<div className="flex justify-evenly gap-4">
|
||||||
variant="contained"
|
<Button
|
||||||
onClick={() => dialogRef.current?.close()}
|
variant="contained"
|
||||||
>
|
onClick={() => dialogRef.current?.close()}
|
||||||
No
|
>
|
||||||
</Button>
|
No
|
||||||
<Button
|
</Button>
|
||||||
variant="contained"
|
<Button
|
||||||
onClick={() => {
|
variant="contained"
|
||||||
handleResetGame();
|
onClick={() => {
|
||||||
dialogRef.current?.close();
|
handleResetGame();
|
||||||
}}
|
dialogRef.current?.close();
|
||||||
>
|
}}
|
||||||
Yes
|
>
|
||||||
</Button>
|
Yes
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
</SettingsContainer>
|
</SettingsContainer>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { twc } from 'react-twc';
|
|||||||
import OnePlayerLandscape from '../../../Icons/generated/Layouts/OnePlayerLandscape';
|
import OnePlayerLandscape from '../../../Icons/generated/Layouts/OnePlayerLandscape';
|
||||||
import { Orientation } from '../../../Types/Settings';
|
import { Orientation } from '../../../Types/Settings';
|
||||||
|
|
||||||
const LayoutWrapper = twc.div`flex flex-row justify-between self-center`;
|
const LayoutWrapper = twc.div`flex flex-row justify-center items-center self-center w-full`;
|
||||||
|
|
||||||
type LayoutOptionsProps = {
|
type LayoutOptionsProps = {
|
||||||
numberOfPlayers: number;
|
numberOfPlayers: number;
|
||||||
@@ -31,14 +31,16 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
selectedOrientation,
|
selectedOrientation,
|
||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const iconHeight = '30vmin';
|
const iconWidth = '21vmin';
|
||||||
const iconWidth = '20vmin';
|
const iconHeight = '40vmin';
|
||||||
|
const iconMaxWidth = '124px';
|
||||||
|
const iconMaxHeight = '196px';
|
||||||
|
|
||||||
const renderLayoutOptions = () => {
|
const renderLayoutOptions = () => {
|
||||||
switch (numberOfPlayers) {
|
switch (numberOfPlayers) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<>
|
<div>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
control={
|
||||||
@@ -58,6 +60,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
TouchRippleProps={{ style: { display: 'none' } }}
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label=""
|
label=""
|
||||||
@@ -81,11 +84,12 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
TouchRippleProps={{ style: { display: 'none' } }}
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label=""
|
label=""
|
||||||
/>
|
/>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
@@ -94,6 +98,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
icon={
|
icon={
|
||||||
<TwoPlayersSameSide
|
<TwoPlayersSameSide
|
||||||
height={iconHeight}
|
height={iconHeight}
|
||||||
@@ -117,6 +122,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
value={Orientation.Portrait}
|
value={Orientation.Portrait}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
icon={
|
icon={
|
||||||
<TwoPlayersOppositePortrait
|
<TwoPlayersOppositePortrait
|
||||||
height={iconHeight}
|
height={iconHeight}
|
||||||
@@ -140,6 +146,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
value={Orientation.OppositeLandscape}
|
value={Orientation.OppositeLandscape}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
icon={
|
icon={
|
||||||
<TwoPlayersOppositeLandscape
|
<TwoPlayersOppositeLandscape
|
||||||
height={iconHeight}
|
height={iconHeight}
|
||||||
@@ -168,6 +175,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
icon={
|
icon={
|
||||||
<ThreePlayers
|
<ThreePlayers
|
||||||
height={iconHeight}
|
height={iconHeight}
|
||||||
@@ -191,6 +199,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
value={Orientation.Portrait}
|
value={Orientation.Portrait}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
icon={
|
icon={
|
||||||
<ThreePlayersSide
|
<ThreePlayersSide
|
||||||
height={iconHeight}
|
height={iconHeight}
|
||||||
@@ -220,6 +229,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
icon={
|
icon={
|
||||||
<FourPlayers
|
<FourPlayers
|
||||||
height={iconHeight}
|
height={iconHeight}
|
||||||
@@ -243,6 +253,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
value={Orientation.Portrait}
|
value={Orientation.Portrait}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
icon={
|
icon={
|
||||||
<FourPlayersSide
|
<FourPlayersSide
|
||||||
height={iconHeight}
|
height={iconHeight}
|
||||||
@@ -272,6 +283,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
icon={
|
icon={
|
||||||
<FivePlayers
|
<FivePlayers
|
||||||
height={iconHeight}
|
height={iconHeight}
|
||||||
@@ -324,6 +336,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
|||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
|
style={{ maxWidth: iconMaxWidth, maxHeight: iconMaxHeight }}
|
||||||
icon={
|
icon={
|
||||||
<SixPlayers
|
<SixPlayers
|
||||||
height={iconHeight}
|
height={iconHeight}
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ import { LayoutOptions } from './LayoutOptions';
|
|||||||
|
|
||||||
const MainWrapper = twc.div`w-[100dvw] h-fit pb-14 overflow-hidden items-center flex flex-col`;
|
const MainWrapper = twc.div`w-[100dvw] h-fit pb-14 overflow-hidden items-center flex flex-col`;
|
||||||
|
|
||||||
const StartButtonFooter = twc.div`fixed bottom-4 z-1`;
|
const StartButtonFooter = twc.div`w-full max-w-[548px] fixed bottom-4 z-1 items-center flex flex-col px-4`;
|
||||||
|
|
||||||
|
const SliderWrapper = twc.div`mx-8`;
|
||||||
|
|
||||||
const ToggleButtonsWrapper = twc.div`flex flex-row justify-between items-center`;
|
const ToggleButtonsWrapper = twc.div`flex flex-row justify-between items-center`;
|
||||||
|
|
||||||
@@ -174,87 +176,93 @@ const Start = () => {
|
|||||||
Life Trinket
|
Life Trinket
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<FormControl focused={false} style={{ width: '80vw' }}>
|
<div className="overflow-hidden items-center flex flex-col max-w-[548px] w-full mb-8 px-4">
|
||||||
<FormLabel>Number of Players</FormLabel>
|
<FormControl focused={false} style={{ width: '100%' }}>
|
||||||
<Slider
|
<FormLabel>Number of Players</FormLabel>
|
||||||
title="Number of Players"
|
<SliderWrapper>
|
||||||
max={6}
|
<Slider
|
||||||
min={1}
|
title="Number of Players"
|
||||||
aria-label="Custom marks"
|
max={6}
|
||||||
value={playerOptions?.numberOfPlayers ?? 4}
|
min={1}
|
||||||
getAriaValueText={valuetext}
|
aria-label="Custom marks"
|
||||||
step={null}
|
value={playerOptions?.numberOfPlayers ?? 4}
|
||||||
marks={playerMarks}
|
getAriaValueText={valuetext}
|
||||||
onChange={(_e, value) => {
|
step={null}
|
||||||
setPlayerOptions({
|
marks={playerMarks}
|
||||||
...playerOptions,
|
|
||||||
numberOfPlayers: value as number,
|
|
||||||
orientation: Orientation.Landscape,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FormLabel className="mt-[0.7rem]">Starting Health</FormLabel>
|
|
||||||
<Slider
|
|
||||||
title="Starting Health"
|
|
||||||
max={60}
|
|
||||||
min={20}
|
|
||||||
aria-label="Custom marks"
|
|
||||||
value={playerOptions?.startingLifeTotal ?? 40}
|
|
||||||
getAriaValueText={valuetext}
|
|
||||||
step={10}
|
|
||||||
marks={healthMarks}
|
|
||||||
onChange={(_e, value) =>
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
startingLifeTotal: value as number,
|
|
||||||
orientation: Orientation.Landscape,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButtonsWrapper className="mt-4">
|
|
||||||
<ToggleContainer>
|
|
||||||
<FormLabel>Commander</FormLabel>
|
|
||||||
<Switch
|
|
||||||
checked={
|
|
||||||
playerOptions.useCommanderDamage ??
|
|
||||||
initialGameSettings?.useCommanderDamage ??
|
|
||||||
true
|
|
||||||
}
|
|
||||||
onChange={(_e, value) => {
|
onChange={(_e, value) => {
|
||||||
if (value) {
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
useCommanderDamage: value,
|
|
||||||
numberOfPlayers: 4,
|
|
||||||
startingLifeTotal: 40,
|
|
||||||
orientation: Orientation.Landscape,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setPlayerOptions({
|
setPlayerOptions({
|
||||||
...playerOptions,
|
...playerOptions,
|
||||||
useCommanderDamage: value,
|
numberOfPlayers: value as number,
|
||||||
numberOfPlayers: 2,
|
|
||||||
startingLifeTotal: 20,
|
|
||||||
orientation: Orientation.Landscape,
|
orientation: Orientation.Landscape,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ToggleContainer>
|
</SliderWrapper>
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
style={{ height: '2rem' }}
|
|
||||||
onClick={() => {
|
|
||||||
setOpenSettingsModal(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Cog /> Other settings
|
|
||||||
</Button>
|
|
||||||
</ToggleButtonsWrapper>
|
|
||||||
|
|
||||||
<FormLabel>Layout</FormLabel>
|
<FormLabel className="mt-[0.7rem]">Starting Health</FormLabel>
|
||||||
{/* <LayoutOptions
|
<SliderWrapper>
|
||||||
|
<Slider
|
||||||
|
title="Starting Health"
|
||||||
|
max={60}
|
||||||
|
min={20}
|
||||||
|
aria-label="Custom marks"
|
||||||
|
value={playerOptions?.startingLifeTotal ?? 40}
|
||||||
|
getAriaValueText={valuetext}
|
||||||
|
step={10}
|
||||||
|
marks={healthMarks}
|
||||||
|
onChange={(_e, value) =>
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
startingLifeTotal: value as number,
|
||||||
|
orientation: Orientation.Landscape,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SliderWrapper>
|
||||||
|
|
||||||
|
<ToggleButtonsWrapper className="mt-4">
|
||||||
|
<ToggleContainer>
|
||||||
|
<FormLabel>Commander</FormLabel>
|
||||||
|
<Switch
|
||||||
|
checked={
|
||||||
|
playerOptions.useCommanderDamage ??
|
||||||
|
initialGameSettings?.useCommanderDamage ??
|
||||||
|
true
|
||||||
|
}
|
||||||
|
onChange={(_e, value) => {
|
||||||
|
if (value) {
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
useCommanderDamage: value,
|
||||||
|
numberOfPlayers: 4,
|
||||||
|
startingLifeTotal: 40,
|
||||||
|
orientation: Orientation.Landscape,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
useCommanderDamage: value,
|
||||||
|
numberOfPlayers: 2,
|
||||||
|
startingLifeTotal: 20,
|
||||||
|
orientation: Orientation.Landscape,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ToggleContainer>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
style={{ height: '2rem' }}
|
||||||
|
onClick={() => {
|
||||||
|
setOpenSettingsModal(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Cog /> Other settings
|
||||||
|
</Button>
|
||||||
|
</ToggleButtonsWrapper>
|
||||||
|
|
||||||
|
<FormLabel>Layout</FormLabel>
|
||||||
|
{/* <LayoutOptions
|
||||||
numberOfPlayers={playerOptions.numberOfPlayers}
|
numberOfPlayers={playerOptions.numberOfPlayers}
|
||||||
gridAreas={playerOptions.gridAreas}
|
gridAreas={playerOptions.gridAreas}
|
||||||
onChange={(gridAreas) =>
|
onChange={(gridAreas) =>
|
||||||
@@ -266,32 +274,32 @@ const Start = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/> */}
|
/> */}
|
||||||
<LayoutOptions
|
<LayoutOptions
|
||||||
numberOfPlayers={playerOptions.numberOfPlayers}
|
numberOfPlayers={playerOptions.numberOfPlayers}
|
||||||
selectedOrientation={playerOptions.orientation}
|
selectedOrientation={playerOptions.orientation}
|
||||||
onChange={(orientation) => {
|
onChange={(orientation) => {
|
||||||
setPlayerOptions({
|
setPlayerOptions({
|
||||||
...playerOptions,
|
...playerOptions,
|
||||||
orientation,
|
orientation,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
{!isPWA && (
|
||||||
{!isPWA && (
|
<p className="text-center text-xs text-text-primary w-11/12 mt-4">
|
||||||
<p className="text-center, max-w-[75%] text-xs text-text-primary">
|
If you're on iOS, this page works better if you{' '}
|
||||||
If you're on iOS, this page works better if you{' '}
|
<strong>hide the toolbar</strong> or{' '}
|
||||||
<strong>hide the toolbar</strong> or{' '}
|
<strong>add the app to your home screen</strong>.
|
||||||
<strong>add the app to your home screen</strong>.
|
</p>
|
||||||
</p>
|
)}
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
<StartButtonFooter>
|
<StartButtonFooter>
|
||||||
<Button
|
<Button
|
||||||
size="large"
|
size="large"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={doStartGame}
|
onClick={doStartGame}
|
||||||
style={{ width: '90dvw' }}
|
fullWidth
|
||||||
>
|
>
|
||||||
START GAME
|
START GAME
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import type { Config } from 'tailwindcss';
|
|||||||
export default {
|
export default {
|
||||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||||
theme: {
|
theme: {
|
||||||
|
screens: {
|
||||||
|
modalSm: '548px',
|
||||||
|
},
|
||||||
extend: {
|
extend: {
|
||||||
gridTemplateAreas: {
|
gridTemplateAreas: {
|
||||||
onePlayerLandscape: ['player0 player0'],
|
onePlayerLandscape: ['player0 player0'],
|
||||||
@@ -39,23 +42,24 @@ export default {
|
|||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#7F9172',
|
main: '#3E7D78',
|
||||||
dark: '#57654F',
|
dark: '#2D5F5B',
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: '#5E714C',
|
main: '#284F4C',
|
||||||
|
dark: '#1B3B38',
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
default: '#495E35',
|
default: '#08253B',
|
||||||
backdrop: 'rgba(0, 0, 0, 0.3)',
|
backdrop: 'rgba(0, 0, 0, 0.3)',
|
||||||
settings: 'rgba(20, 20, 0, 0.9)',
|
settings: 'rgba(20, 20, 0, 0.9)',
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
primary: '#F5F5F5',
|
primary: '#F5F5F5',
|
||||||
secondary: '#b3b39b',
|
secondary: '#76A6A5',
|
||||||
},
|
},
|
||||||
action: {
|
action: {
|
||||||
disabled: '#5E714C',
|
disabled: '#234A47',
|
||||||
},
|
},
|
||||||
common: {
|
common: {
|
||||||
white: '#F9FFE3',
|
white: '#F9FFE3',
|
||||||
@@ -95,15 +99,4 @@ export default {
|
|||||||
},
|
},
|
||||||
plugins: [tailwindcssGridAreas],
|
plugins: [tailwindcssGridAreas],
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
// #98FF98
|
||||||
// const fadeOut = keyframes`
|
|
||||||
// 0% {
|
|
||||||
// opacity: 1;
|
|
||||||
// }
|
|
||||||
// 33% {
|
|
||||||
// opacity: 0.6;
|
|
||||||
// }
|
|
||||||
// 100% {
|
|
||||||
// opacity: 0;
|
|
||||||
// }
|
|
||||||
// `;
|
|
||||||
|
|||||||
@@ -1,9 +1,19 @@
|
|||||||
import { defineConfig } from 'vite';
|
|
||||||
import react from '@vitejs/plugin-react-swc';
|
import react from '@vitejs/plugin-react-swc';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import { VitePWA } from 'vite-plugin-pwa';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [
|
||||||
|
react(),
|
||||||
|
VitePWA({
|
||||||
|
registerType: 'autoUpdate',
|
||||||
|
workbox: {
|
||||||
|
clientsClaim: true,
|
||||||
|
skipWaiting: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
build: {
|
build: {
|
||||||
minify: 'esbuild',
|
minify: 'esbuild',
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
|||||||
Reference in New Issue
Block a user