Skip to content

Commit

Permalink
feat: add initial implementation for the initiative tracker
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Stevens committed Apr 27, 2024
1 parent 38adb46 commit 2aef13d
Show file tree
Hide file tree
Showing 24 changed files with 1,170 additions and 1,017 deletions.
1,545 changes: 697 additions & 848 deletions package-lock.json

Large diffs are not rendered by default.

37 changes: 19 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@
"type": "module",
"dependencies": {
"@ark-ui/react": "^2.2.3",
"@serwist/next": "9.0.0-preview.21",
"@serwist/precaching": "9.0.0-preview.21",
"@serwist/sw": "9.0.0-preview.21",
"@serwist/next": "9.0.1",
"@serwist/precaching": "9.0.1",
"@serwist/sw": "9.0.1",
"@vercel/analytics": "^1.2.2",
"@vercel/speed-insights": "^1.0.10",
"auto-text-size": "^0.2.3",
"immer": "^10.0.4",
"lucide-react": "^0.370.0",
"lucide-react": "^0.373.0",
"million": "^3.0.6",
"next": "14.2.1",
"next": "14.2.3",
"npm": "^10.5.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -41,24 +41,24 @@
"@park-ui/panda-preset": "0.37.0",
"@release-it/bumper": "^6.0.1",
"@release-it/conventional-changelog": "^8.0.1",
"@storybook/addon-essentials": "^8.0.8",
"@storybook/addon-interactions": "^8.0.8",
"@storybook/addon-links": "^8.0.8",
"@storybook/addon-onboarding": "^8.0.8",
"@storybook/blocks": "^8.0.8",
"@storybook/nextjs": "^8.0.8",
"@storybook/react": "^8.0.8",
"@storybook/test": "^8.0.8",
"@storybook/addon-essentials": "^8.0.9",
"@storybook/addon-interactions": "^8.0.9",
"@storybook/addon-links": "^8.0.9",
"@storybook/addon-onboarding": "^8.0.9",
"@storybook/blocks": "^8.0.9",
"@storybook/nextjs": "^8.0.9",
"@storybook/react": "^8.0.9",
"@storybook/test": "^8.0.9",
"@types/glob": "^8.1.0",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.7",
"@types/react": "^18.2.79",
"@types/react-dom": "^18.2.25",
"@types/svg-sprite": "^0.0.39",
"@typescript-eslint/eslint-plugin": "7.7.0",
"@typescript-eslint/parser": "^7.7.0",
"@typescript-eslint/eslint-plugin": "7.7.1",
"@typescript-eslint/parser": "^7.7.1",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.1",
"eslint-config-next": "^14.2.3",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-deprecation": "^2.0.0",
"eslint-plugin-no-relative-import-paths": "^1.5.4",
Expand All @@ -71,12 +71,13 @@
"husky": "^9.0.11",
"prettier": "^3.2.5",
"release-it": "^17.2.0",
"storybook": "^8.0.8",
"serwist": "^9.0.2",
"storybook": "^8.0.9",
"svg-sprite": "^2.0.4",
"tsconfig-paths-webpack-plugin": "^4.1.0",
"tsx": "^4.7.2",
"typescript": "5.4.5",
"typescript-eslint": "^7.7.0"
"typescript-eslint": "^7.7.1"
},
"overrides": {
"svgo": "3.2.0"
Expand Down
15 changes: 15 additions & 0 deletions panda.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,28 @@ export default defineConfig({
pirataOne: { value: 'var(--font-pirata-one)' },
},
},
slotRecipes: {
pinInput: {
variants: {
size: {
xl: { input: { height: '12' } },
'2xl': { input: { height: '16' } },
},
},
},
},
recipes: {
heading: {
base: {
letterSpacing: '0 !important',
fontWeight: 'normal',
},
},
text: {
base: {
fontWeight: 'normal',
},
},
button: {
base: {
fontWeight: 'normal',
Expand Down
16 changes: 10 additions & 6 deletions src/app/scenarios/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Box } from '@style/jsx';
import { SCENARIO_DEFINITIONS } from 'data/scenarios';
import { notFound } from 'next/navigation';

import { Main } from 'components/@navigation';
import { DeckList, Navbar } from 'components/@scenario';
import { UseWakeLock } from 'components/@utils';
import { DeckList, InitiativeList, Navbar } from 'components/@scenario';

const ScenarioPage = ({ params }: { params: { id: string } }) => {
const scenario = SCENARIO_DEFINITIONS.find(
Expand All @@ -15,10 +15,14 @@ const ScenarioPage = ({ params }: { params: { id: string } }) => {
return (
<>
<Navbar scenarioName={scenario.name} />
<Main justify="start">
<DeckList scenario={scenario} />
</Main>
<UseWakeLock />
<div>
<Main justify="start" flexDir="row">
<Box flexDir="column" flex={1}>
<DeckList scenario={scenario} />
</Box>
<InitiativeList />
</Main>
</div>
</>
);
};
Expand Down
8 changes: 4 additions & 4 deletions src/app/sw.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { SerwistGlobalConfig } from '@serwist/core';
import { defaultCache } from '@serwist/next/worker';
import type { PrecacheEntry } from '@serwist/precaching';
import { installSerwist } from '@serwist/sw';
import { type PrecacheEntry, Serwist, type SerwistGlobalConfig } from 'serwist';

declare global {
interface WorkerGlobalScope extends SerwistGlobalConfig {
Expand All @@ -14,11 +12,13 @@ declare global {

declare const self: ServiceWorkerGlobalScope;

installSerwist({
const serwist = new Serwist({
precacheEntries: self.__MM_MANIFEST,
skipWaiting: true,
clientsClaim: true,
navigationPreload: true,
runtimeCaching: defaultCache,
disableDevLogs: true,
});

serwist.addEventListeners();
59 changes: 59 additions & 0 deletions src/components/@common/PinInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
PinInput as ArkPinInput,
type PinInputRootProps,
} from '@ark-ui/react/pin-input';
import { css, cx } from '@style/css';
import { splitCssProps } from '@style/jsx';
import { pinInput, type PinInputVariantProps } from '@style/recipes';
import type { Assign, JsxStyleProps } from '@style/types';
import { forwardRef, type ReactNode } from 'react';

import { Input } from './Input';

export interface PinInputProps
extends Assign<JsxStyleProps, PinInputRootProps>,
PinInputVariantProps {
children?: ReactNode;
/**
* The number of inputs to render.
* @default 4
*/
length?: number;
}

export const PinInput = forwardRef<HTMLDivElement, PinInputProps>(
(props, ref) => {
const [variantProps, pinInputProps] = pinInput.splitVariantProps(props);
const [cssProps, localProps] = splitCssProps(pinInputProps);
const { children, className, length = 4, ...rootProps } = localProps;
const styles = pinInput(variantProps);

return (
<ArkPinInput.Root
className={cx(styles.root, css(cssProps), className)}
ref={ref}
{...rootProps}
>
{children && (
<ArkPinInput.Label className={styles.label}>
{children}
</ArkPinInput.Label>
)}
<ArkPinInput.Control className={styles.control}>
{Array.from({ length }, (_, index) => index).map((id, index) => (
<ArkPinInput.Input
className={styles.input}
key={id}
index={index}
asChild
>
<Input size={variantProps.size} />
</ArkPinInput.Input>
))}
</ArkPinInput.Control>
</ArkPinInput.Root>
);
},
);

PinInput.displayName = 'PinInput';
1 change: 1 addition & 0 deletions src/components/@common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './Heading';
export * from './IconButton';
export * from './Input';
export * as Menu from './Menu';
export * from './PinInput';
export * as Popover from './Popover';
export * as RadioButtonGroup from './RadioButtonGroup';
export * as Select from './Select';
Expand Down
4 changes: 2 additions & 2 deletions src/components/@scenario/BossCardTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ const BossCardTitle = ({ deck }: Props) => {
style={{
gridTemplateColumns: `repeat(${Math.round(
immunities.length / 2,
)}, 1.25em)`,
)}, 1em)`,
}}
fontSize="1.4em"
fontSize="1.2em"
dangerouslySetInnerHTML={{ __html: immunityIcons }}
mr="8"
/>
Expand Down
31 changes: 8 additions & 23 deletions src/components/@scenario/ChangePartySize.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,28 @@
import { cva } from '@style/css';
import { Box, Stack } from '@style/jsx';
import { CHARACTERS } from 'data/characters';
import { Icon } from 'icons';
import Image from 'next/image';
import { useState } from 'react';
import useDeepCompareEffect from 'use-deep-compare-effect';

import { CharacterNames } from 'types/character.types';

import { Button, Dialog, IconButton } from 'components/@common';

import { characterInactive } from './styles';

interface Props {
open: boolean;
currentParty: CharacterNames[];
onSubmit: (party: CharacterNames[]) => void;
onClose: () => void;
}

const hoverIcon = cva({
base: { filter: 'none' },
variants: {
state: {
disabled: {
filter:
'brightness(0) invert(24%) sepia(2%) saturate(17%) hue-rotate(324deg) brightness(98%) contrast(82%)',
},
active: {
filter: 'none',
},
},
},
});

const ChangePartySize = ({ open, currentParty, onSubmit, onClose }: Props) => {
const [party, selectParty] = useState(currentParty);
const handleClose = (details: { open: boolean }) => {
if (!details.open) onClose();
};

useDeepCompareEffect(() => {
selectParty(currentParty);
}, [currentParty]);

const onChange = (character: CharacterNames) => {
selectParty((party) => {
if (party.includes(character)) {
Expand Down Expand Up @@ -80,13 +61,17 @@ const ChangePartySize = ({ open, currentParty, onSubmit, onClose }: Props) => {
{Object.values(CHARACTERS).map((item) => {
const isSelected = party.includes(item.name);
return (
<Box key={item.name} onClick={() => onChange(item.name)}>
<Box
key={item.name}
onClick={() => onChange(item.name)}
cursor="pointer"
>
<Image
src={`/images/characters/${item.icon}`}
width={42}
height={42}
alt={item.name}
className={hoverIcon({
className={characterInactive({
state: isSelected ? 'active' : 'disabled',
})}
/>
Expand Down
71 changes: 71 additions & 0 deletions src/components/@scenario/InitiativeList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use client';

import { Box, VStack } from '@style/jsx';
import { CHARACTERS } from 'data/characters';
import Image from 'next/image';

import { getEnemyArtwork } from 'utils/deck.utils';

import { useStore } from 'services/stores';
import { CharacterNames } from 'types/character.types';

import { inactiveImage } from './styles';

const isCharacterName = (name: string): name is CharacterNames =>
Object.values(CharacterNames).includes(name as CharacterNames);

const InitiativeList = () => {
const initiatives = useStore((state) => state.initiatives);
console.log(initiatives);

Check warning on line 19 in src/components/@scenario/InitiativeList.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement
const { toggleInitiativePlayed } = useStore((state) => state.actions);

const sortedInitiatives = Object.values(initiatives).sort(
(initiativeA, initiativeB) =>
initiativeA.initiative - initiativeB.initiative,
);

console.log({ sortedInitiatives });

Check warning on line 27 in src/components/@scenario/InitiativeList.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected console statement

return (
<Box
width={75}
borderLeft="1px"
borderColor="border.subtle"
borderStyle="solid"
my="4"
px="4"
>
<VStack gap={1}>
{sortedInitiatives.map((initiative) => {
const isCharacter = isCharacterName(initiative.name);
const icon = isCharacterName(initiative.name)
? `/images/characters/${CHARACTERS[initiative.name].icon}`
: `/images/thumbnails/${getEnemyArtwork(initiative.name)}`;

return (
<Box
key={initiative.name}
my="2"
cursor="pointer"
onClick={() => toggleInitiativePlayed(initiative.name)}
>
<Image
src={icon}
width={37}
height={37}
alt={initiative.name}
className={inactiveImage({
state: initiative.played ? 'disabled' : 'active',
type: isCharacter ? 'character' : 'enemy',
})}
style={{ filter: 'grey-scale(1)' }}
/>
</Box>
);
})}
</VStack>
</Box>
);
};

export default InitiativeList;
Loading

0 comments on commit 2aef13d

Please sign in to comment.