Skip to content

Commit

Permalink
chore: upgrade dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
lozinsky committed Feb 22, 2025
1 parent 88256f0 commit 72d2aed
Show file tree
Hide file tree
Showing 22 changed files with 462 additions and 468 deletions.
4 changes: 2 additions & 2 deletions app/components/base/animation-sync.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { ElementRef, ReactNode } from 'react';
import type { ComponentRef, ReactNode } from 'react';

import { Slot } from '~/components/base/slot';
import { expectToBeDefined } from '~/shared/expect';

export function AnimationSync({ children }: { children: ReactNode }) {
function setSlot(slot: ElementRef<typeof Slot> | null) {
function setSlot(slot: ComponentRef<typeof Slot> | null) {
if (slot === null) {
return;
}
Expand Down
4 changes: 2 additions & 2 deletions app/components/base/aria-label.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { type ReactNode, useContext } from 'react';
import { type ReactNode, use } from 'react';

import { AriaLabelledByContext } from '~/components/base/aria-labelled';
import { expectToBeDefined } from '~/shared/expect';

export function AriaLabel({ children }: { children: ReactNode }) {
const ariaLabelledBy = expectToBeDefined(useContext(AriaLabelledByContext));
const ariaLabelledBy = expectToBeDefined(use(AriaLabelledByContext));

return (
<span className='hidden' id={ariaLabelledBy}>
Expand Down
2 changes: 1 addition & 1 deletion app/components/base/aria-labelled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export const AriaLabelledByContext = createContext<string | undefined>(undefined
export function AriaLabelled({ children }: { children: ReactNode }) {
const id = useId();

return <AriaLabelledByContext.Provider value={id}>{children}</AriaLabelledByContext.Provider>;
return <AriaLabelledByContext value={id}>{children}</AriaLabelledByContext>;
}
4 changes: 2 additions & 2 deletions app/components/base/slot-outlet.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createContext, type ReactNode, useContext } from 'react';
import { createContext, type ReactNode, use } from 'react';

export const SlotOutletContext = createContext<ReactNode>(null);

export function SlotOutlet() {
return useContext(SlotOutletContext);
return use(SlotOutletContext);
}
2 changes: 1 addition & 1 deletion app/components/base/slot-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import type { ReactNode } from 'react';
import { SlotOutletContext } from '~/components/base/slot-outlet';

export function SlotRoot({ children, target }: { children: ReactNode; target: ReactNode }) {
return <SlotOutletContext.Provider value={target}>{children}</SlotOutletContext.Provider>;
return <SlotOutletContext value={target}>{children}</SlotOutletContext>;
}
30 changes: 15 additions & 15 deletions app/components/base/slot.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* @vitest-environment jsdom */

import { render } from '@testing-library/react';
import { createRef, type ElementRef, forwardRef, useImperativeHandle, useRef } from 'react';
import { type ComponentRef, createRef, type Ref, useImperativeHandle, useRef } from 'react';
import { expect, test } from 'vitest';

import { Slot } from './slot';
Expand Down Expand Up @@ -83,8 +83,8 @@ test.each([
});

test.each([
forwardRef<unknown>(function Component(props, ref) {
const slotRef = useRef<ElementRef<typeof Slot>>(null);
function Component({ ref }: { ref: Ref<unknown> }) {
const slotRef = useRef<ComponentRef<typeof Slot>>(null);

useImperativeHandle(ref, () => ({
slotRef,
Expand All @@ -98,9 +98,9 @@ test.each([
</span>
</Slot>
);
}),
forwardRef<unknown>(function Component(props, ref) {
const slotRef = useRef<ElementRef<typeof Slot>>(null);
},
function Component({ ref }: { ref: Ref<unknown> }) {
const slotRef = useRef<ComponentRef<typeof Slot>>(null);

useImperativeHandle(ref, () => ({
slotRef,
Expand All @@ -114,10 +114,10 @@ test.each([
</SlotRoot>
</Slot>
);
}),
forwardRef<unknown>(function Component(props, ref) {
const slotRef = useRef<ElementRef<typeof Slot>>(null);
const spanRef = useRef<ElementRef<'span'>>(null);
},
function Component({ ref }: { ref: Ref<unknown> }) {
const slotRef = useRef<ComponentRef<typeof Slot>>(null);
const spanRef = useRef<ComponentRef<'span'>>(null);

useImperativeHandle(ref, () => ({
slotRef,
Expand All @@ -132,10 +132,10 @@ test.each([
</span>
</Slot>
);
}),
forwardRef<unknown>(function Component(props, ref) {
const slotRef = useRef<ElementRef<typeof Slot>>(null);
const spanRef = useRef<ElementRef<'span'>>(null);
},
function Component({ ref }: { ref: Ref<unknown> }) {
const slotRef = useRef<ComponentRef<typeof Slot>>(null);
const spanRef = useRef<ComponentRef<'span'>>(null);

useImperativeHandle(ref, () => ({
slotRef,
Expand All @@ -150,7 +150,7 @@ test.each([
</SlotRoot>
</Slot>
);
}),
},
])('renders slot with ref', (Component) => {
const ref = createRef();

Expand Down
26 changes: 12 additions & 14 deletions app/components/base/slot.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
cloneElement,
type ComponentProps,
forwardRef,
type HTMLAttributes,
isValidElement,
type ReactElement,
Expand All @@ -17,7 +16,17 @@ import { expectToSatisfy } from '~/shared/expect';

type RootElement = ReactElement<ComponentProps<typeof SlotRoot>>;

type TargetElement = ReactElement<HTMLAttributes<HTMLElement>> & { ref?: Ref<HTMLElement> };
type TargetElement = ReactElement<HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>>;

export function Slot({ children, ref, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>) {
const target = getTarget(children);
const targetRef = useMergedRef<HTMLElement>([ref, target.props.ref]);

assignClassName(props, target.props.className);
assignRef(props, targetRef);

return cloneElement(target, props);
}

function assignClassName(target: HTMLAttributes<HTMLElement>, source: string | undefined) {
if (target.className !== undefined && source !== undefined) {
Expand All @@ -36,7 +45,7 @@ function getTarget(node: ReactNode): TargetElement {
return cloneElement(
target,
undefined,
<SlotOutletContext.Provider value={target.props.children}>{node.props.children}</SlotOutletContext.Provider>,
<SlotOutletContext value={target.props.children}>{node.props.children}</SlotOutletContext>,
);
}

Expand All @@ -50,14 +59,3 @@ function isRoot(node: unknown): node is RootElement {
function isTarget(node: unknown): node is TargetElement {
return isValidElement(node);
}

export const Slot = forwardRef<HTMLElement, HTMLAttributes<HTMLElement>>(function Slot({ children, ...props }, ref) {
const target = getTarget(children);
const targetRef = useMergedRef<HTMLElement>([ref, target.ref]);
const targetProps = props as HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>;

assignClassName(targetProps, target.props.className);
assignRef(targetProps, targetRef);

return cloneElement(target, targetProps);
});
10 changes: 8 additions & 2 deletions app/components/game-praise-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FormattedMessage } from 'react-intl';
import { href } from 'react-router';

import type { Messages } from '~/services/intl';

Expand Down Expand Up @@ -43,11 +44,16 @@ export function GamePraiseModal({ size }: { size?: number }) {
</GameModalHeader>
<GameModalFooter>
{size !== undefined && (
<ButtonLink prefetch='render' replace to={`/game/new/${size}`} variant='primary'>
<ButtonLink
prefetch='render'
replace
to={href('/game/new/:size', { size: size.toString() })}
variant='primary'
>
<FormattedMessage id='gameAgainLink' />
</ButtonLink>
)}
<ButtonLink history={false} replace to='/' variant='secondary'>
<ButtonLink history={false} replace to={href('/')} variant='secondary'>
<FormattedMessage id='gameMenuLink' />
</ButtonLink>
</GameModalFooter>
Expand Down
4 changes: 2 additions & 2 deletions app/components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Loader2 } from 'lucide-react';
import { type ReactNode, useContext } from 'react';
import { type ReactNode, use } from 'react';

import { AriaLabelledByContext } from '~/components/base/aria-labelled';
import { Slot } from '~/components/base/slot';
Expand All @@ -24,7 +24,7 @@ export function Button({
variant: ButtonVariant;
}) {
const Component = asChild ? Slot : 'button';
const ariaLabelledBy = useContext(AriaLabelledByContext);
const ariaLabelledBy = use(AriaLabelledByContext);

return (
<Component
Expand Down
4 changes: 2 additions & 2 deletions app/components/ui/game-board-cell.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ReactNode, useContext } from 'react';
import { type ReactNode, use } from 'react';

import { AriaLabelledByContext } from '~/components/base/aria-labelled';
import { Slot } from '~/components/base/slot';
Expand All @@ -24,7 +24,7 @@ export function GameBoardCell({
state: BoardCellState;
}) {
const Component = asChild ? Slot : 'span';
const ariaLabelledBy = useContext(AriaLabelledByContext);
const ariaLabelledBy = use(AriaLabelledByContext);

return (
<Component
Expand Down
6 changes: 3 additions & 3 deletions app/components/ui/root-layout-header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChevronLeft, Code, Settings2 } from 'lucide-react';
import { FormattedMessage } from 'react-intl';
import { useLocation, useResolvedPath } from 'react-router';
import { href, useLocation, useResolvedPath } from 'react-router';

import { AriaLabel } from '~/components/base/aria-label';
import { AriaLabelled } from '~/components/base/aria-labelled';
Expand Down Expand Up @@ -39,9 +39,9 @@ export function RootLayoutHeader() {
)}
</div>
<div className='flex items-center justify-end'>
{location.pathname !== '/settings' && (
{location.pathname !== href('/settings') && (
<AriaLabelled>
<ButtonLink size='icon' to='/settings' variant='ghost'>
<ButtonLink size='icon' to={href('/settings')} variant='ghost'>
<Settings2 aria-hidden />
<AriaLabel>
<FormattedMessage id='headerSettingsLabel' />
Expand Down
4 changes: 2 additions & 2 deletions app/hooks/use-merged-ref.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type MutableRefObject, type Ref, useMemo } from 'react';
import { type Ref, useMemo } from 'react';

export function useMergedRef<T>(refs: ReadonlyArray<Ref<T> | undefined>) {
const mergedRef = useMemo<Ref<T>>(() => {
Expand All @@ -11,7 +11,7 @@ export function useMergedRef<T>(refs: ReadonlyArray<Ref<T> | undefined>) {
if (typeof ref === 'function') {
ref(value);
} else if (ref != null) {
(ref as MutableRefObject<null | T>).current = value;
ref.current = value;
}
}
};
Expand Down
4 changes: 2 additions & 2 deletions app/hooks/use-random.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createContext, useContext } from 'react';
import { createContext, use } from 'react';

import { Random } from '~/shared/random';

export const RandomSeedContext = createContext(0);

export function useRandom() {
const seed = useContext(RandomSeedContext);
const seed = use(RandomSeedContext);
const random = new Random(seed);

return random;
Expand Down
9 changes: 5 additions & 4 deletions app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import '~/globals';

import type { ReactNode } from 'react';

import { IntlProvider } from 'react-intl';
Expand All @@ -11,7 +13,6 @@ import {
useRouteLoaderData,
} from 'react-router';

import '~/globals';
import { RootLayout } from '~/components/ui/root-layout';
import { RootLayoutContent } from '~/components/ui/root-layout-content';
import { RootLayoutHeader } from '~/components/ui/root-layout-header';
Expand Down Expand Up @@ -48,13 +49,13 @@ export function HydrateFallback() {
}

export function Layout({ children }: { children: ReactNode }) {
const { appearance, intl } = useRouteLoaderData<typeof clientLoader>('root') ?? {
const loaderData = useRouteLoaderData<typeof clientLoader>('root') ?? {
appearance: DEFAULT_APPEARANCE,
intl: { locale: DEFAULT_LOCALE, messages: {} },
};

return (
<html data-appearance={appearance} lang={intl.locale}>
<html data-appearance={loaderData.appearance} lang={loaderData.intl.locale}>
<head>
<meta charSet='utf-8' />
<meta content='width=device-width, initial-scale=1' name='viewport' />
Expand All @@ -81,7 +82,7 @@ export function links(): Route.LinkDescriptors {

export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (data === undefined) {
if (data === undefined || data === null) {
return [];
}

Expand Down
11 changes: 8 additions & 3 deletions app/routes/_index/route.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FormattedMessage } from 'react-intl';
import { href } from 'react-router';

import { BrandLogo } from '~/components/ui/brand-logo';
import { ButtonLink } from '~/components/ui/button-link';
Expand Down Expand Up @@ -26,18 +27,22 @@ export default function Route({ loaderData }: Route.ComponentProps) {
<MenuGroup>
{loaderData.board !== null && (
<MenuItem>
<ButtonLink prefetch='render' to={`/game/${loaderData.board}`} variant='primary'>
<ButtonLink prefetch='render' to={href('/game/:board', { board: loaderData.board })} variant='primary'>
<FormattedMessage id='menuGameContinueLink' />
</ButtonLink>
</MenuItem>
)}
<MenuItem>
<ButtonLink prefetch='render' to='/game' variant={loaderData.board === null ? 'primary' : 'secondary'}>
<ButtonLink
prefetch='render'
to={href('/game')}
variant={loaderData.board === null ? 'primary' : 'secondary'}
>
<FormattedMessage id='menuGameLink' />
</ButtonLink>
</MenuItem>
<MenuItem>
<ButtonLink to='/game/tutorial' variant='secondary'>
<ButtonLink to={href('/game/tutorial/:step?/:action?')} variant='secondary'>
<FormattedMessage id='menuGameTutorialLink' />
</ButtonLink>
</MenuItem>
Expand Down
3 changes: 2 additions & 1 deletion app/routes/game.$board/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ReactNode } from 'react';

import { HelpCircle } from 'lucide-react';
import { FormattedMessage, FormattedNumber } from 'react-intl';
import { href } from 'react-router';

import type { Board } from '~/lib/board';
import type { BoardAnalyzerReview } from '~/lib/board-analyzer';
Expand Down Expand Up @@ -48,7 +49,7 @@ export function GameBoardContent({
key={key}
locked={locked}
state={cell.state}
to={`/game/${getNextBoard(board, { x, y }).toString()}`}
to={href('/game/:board', { board: getNextBoard(board, { x, y }).toString() })}
/>,
);
}
Expand Down
4 changes: 3 additions & 1 deletion app/routes/game._index/route.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { href } from 'react-router';

import { ButtonLink } from '~/components/ui/button-link';
import { Menu } from '~/components/ui/menu';
import { MenuGroup } from '~/components/ui/menu-group';
Expand All @@ -15,7 +17,7 @@ export default function Route({ loaderData }: Route.ComponentProps) {
<MenuGroup>
{loaderData.sizes.map((size) => (
<MenuItem key={size}>
<ButtonLink prefetch='render' to={`/game/new/${size}`} variant='secondary'>
<ButtonLink prefetch='render' to={href('/game/new/:size', { size: size.toString() })} variant='secondary'>
{size}
</ButtonLink>
</MenuItem>
Expand Down
4 changes: 2 additions & 2 deletions app/routes/game.new.$size/route.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { redirect } from 'react-router';
import { href, redirect } from 'react-router';

import { generateBoard } from '~/services/game-worker.client';
import { expectNotToBeNaN } from '~/shared/expect';
Expand All @@ -9,7 +9,7 @@ export async function clientLoader({ params, request }: Route.ClientLoaderArgs)
const url = new URL(request.url);
const board = await generateBoard(expectNotToBeNaN(Number(params.size)));

url.pathname = `${import.meta.env.BASE_URL}game/${board.toString()}`;
url.pathname = href('/game/:board', { board: board.toString() }).replace('/', import.meta.env.BASE_URL);

return redirect(url.toString());
}
Loading

0 comments on commit 72d2aed

Please sign in to comment.