From c403632f46f3c6c2f9f207f29fb9c8bfc0252ab7 Mon Sep 17 00:00:00 2001 From: Sebastian Pietras Date: Sat, 8 Aug 2026 22:16:56 +0200 Subject: [PATCH] Cleaned up code --- .../common/generic/lib/create-url/types.ts | 2 +- .../generic/components/click/index.ts | 2 ++ .../generic/components/click/main.tsx | 24 +++++++++++++++ .../generic/components/click/types.ts | 9 ++++++ .../generic/components/client-link/index.ts | 2 ++ .../generic/components/client-link/main.tsx | 9 ++++++ .../generic/components/client-link/types.ts | 4 +++ .../generic/components/history-link/index.ts | 8 +++++ .../generic/components/history-link/main.tsx | 30 +++++++++++++++++++ .../generic/components/history-link/types.ts | 20 +++++++++++++ .../components/history-synchronizer/main.tsx | 4 ++- .../generic/components/route-modal/main.tsx | 29 +++++++----------- .../generic/components/route-modal/types.ts | 8 ++--- .../components/localized/index.ts | 2 ++ .../components/localized/main.tsx | 11 +++++++ .../components/localized/types.ts | 5 ++++ src/src/isomorphic/state/types.ts | 2 +- 17 files changed, 144 insertions(+), 27 deletions(-) create mode 100644 src/src/isomorphic/generic/components/click/index.ts create mode 100644 src/src/isomorphic/generic/components/click/main.tsx create mode 100644 src/src/isomorphic/generic/components/click/types.ts create mode 100644 src/src/isomorphic/generic/components/client-link/index.ts create mode 100644 src/src/isomorphic/generic/components/client-link/main.tsx create mode 100644 src/src/isomorphic/generic/components/client-link/types.ts create mode 100644 src/src/isomorphic/generic/components/history-link/index.ts create mode 100644 src/src/isomorphic/generic/components/history-link/main.tsx create mode 100644 src/src/isomorphic/generic/components/history-link/types.ts create mode 100644 src/src/isomorphic/localization/components/localized/index.ts create mode 100644 src/src/isomorphic/localization/components/localized/main.tsx create mode 100644 src/src/isomorphic/localization/components/localized/types.ts diff --git a/src/src/common/generic/lib/create-url/types.ts b/src/src/common/generic/lib/create-url/types.ts index 4f465e5..351baab 100644 --- a/src/src/common/generic/lib/create-url/types.ts +++ b/src/src/common/generic/lib/create-url/types.ts @@ -2,7 +2,7 @@ type QueryValue = boolean | number | string; type AnyUrlInput = { fragment?: null | string; - query?: null | { [key: string]: QueryValue | QueryValue[] }; + query?: null | { [key: string]: QueryValue | readonly QueryValue[] }; }; type AbsoluteUrlInput = AnyUrlInput & { diff --git a/src/src/isomorphic/generic/components/click/index.ts b/src/src/isomorphic/generic/components/click/index.ts new file mode 100644 index 0000000..df0632d --- /dev/null +++ b/src/src/isomorphic/generic/components/click/index.ts @@ -0,0 +1,2 @@ +export { Click } from "./main"; +export type { BaseClickInput, ClickInput } from "./types"; diff --git a/src/src/isomorphic/generic/components/click/main.tsx b/src/src/isomorphic/generic/components/click/main.tsx new file mode 100644 index 0000000..43a08f2 --- /dev/null +++ b/src/src/isomorphic/generic/components/click/main.tsx @@ -0,0 +1,24 @@ +"use client"; + +import { createPolymorphicComponent } from "@mantine/core"; +import { useMergedRef } from "@mantine/hooks"; +import { useEffect, useRef } from "react"; + +import type { BaseClickInput, ClickInput } from "./types"; + +export const Click = createPolymorphicComponent<"a", BaseClickInput>( + function Click({ component: Component = "a", ref, ...input }: ClickInput) { + const clicked = useRef(false); + const innerRef = useRef(null); + const mergedRef = useMergedRef(ref, innerRef); + + useEffect(() => { + if (clicked.current || !innerRef.current) return; + + innerRef.current.click(); + clicked.current = true; + }, []); + + return ; + }, +); diff --git a/src/src/isomorphic/generic/components/click/types.ts b/src/src/isomorphic/generic/components/click/types.ts new file mode 100644 index 0000000..cfa0d13 --- /dev/null +++ b/src/src/isomorphic/generic/components/click/types.ts @@ -0,0 +1,9 @@ +import type { PolymorphicComponentProps } from "@mantine/core"; +import type { ElementType } from "react"; + +export type BaseClickInput = object; + +export type ClickInput = PolymorphicComponentProps< + C, + BaseClickInput +>; diff --git a/src/src/isomorphic/generic/components/client-link/index.ts b/src/src/isomorphic/generic/components/client-link/index.ts new file mode 100644 index 0000000..428396f --- /dev/null +++ b/src/src/isomorphic/generic/components/client-link/index.ts @@ -0,0 +1,2 @@ +export { ClientLink } from "./main"; +export type { ClientLinkInput } from "./types"; diff --git a/src/src/isomorphic/generic/components/client-link/main.tsx b/src/src/isomorphic/generic/components/client-link/main.tsx new file mode 100644 index 0000000..eddcf34 --- /dev/null +++ b/src/src/isomorphic/generic/components/client-link/main.tsx @@ -0,0 +1,9 @@ +"use client"; + +import Link from "next/link"; + +import type { ClientLinkInput } from "./types"; + +export function ClientLink({ ...input }: ClientLinkInput) { + return ; +} diff --git a/src/src/isomorphic/generic/components/client-link/types.ts b/src/src/isomorphic/generic/components/client-link/types.ts new file mode 100644 index 0000000..89b7c7f --- /dev/null +++ b/src/src/isomorphic/generic/components/client-link/types.ts @@ -0,0 +1,4 @@ +import type Link from "next/link"; +import type { ComponentProps } from "react"; + +export type ClientLinkInput = ComponentProps; diff --git a/src/src/isomorphic/generic/components/history-link/index.ts b/src/src/isomorphic/generic/components/history-link/index.ts new file mode 100644 index 0000000..ae51c4a --- /dev/null +++ b/src/src/isomorphic/generic/components/history-link/index.ts @@ -0,0 +1,8 @@ +export { HistoryLink } from "./main"; +export type { + HistoryLinkAction, + HistoryLinkActions, + HistoryLinkInput, + HistoryLinkPopAction, + HistoryLinkPushAction, +} from "./types"; diff --git a/src/src/isomorphic/generic/components/history-link/main.tsx b/src/src/isomorphic/generic/components/history-link/main.tsx new file mode 100644 index 0000000..c1e5f9c --- /dev/null +++ b/src/src/isomorphic/generic/components/history-link/main.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { range } from "es-toolkit/math"; +import { useCallback } from "react"; +import { useDeepCompareMemo } from "use-deep-compare"; + +import type { HistoryLinkInput } from "./types"; + +import { useGlobalState } from "../../../state/hooks/use-global-state"; +import { ClientLink } from "../client-link"; + +export function HistoryLink({ actions, ...input }: HistoryLinkInput) { + const { state } = useGlobalState(); + + const cachedActions = useDeepCompareMemo(() => actions, [actions]); + + const handleNavigate = useCallback(() => { + const entries = state.current.history.entries; + + for (const action of cachedActions) { + if (action.type === "pop") { + range(action.count ?? 1).forEach(() => entries.pop()); + } else if (action.type === "push") { + entries.push(...action.entries); + } + } + }, [cachedActions, state.current.history.entries]); + + return ; +} diff --git a/src/src/isomorphic/generic/components/history-link/types.ts b/src/src/isomorphic/generic/components/history-link/types.ts new file mode 100644 index 0000000..d2855dd --- /dev/null +++ b/src/src/isomorphic/generic/components/history-link/types.ts @@ -0,0 +1,20 @@ +import type { HistoryEntry } from "../../../state/types"; +import type { ClientLinkInput } from "../client-link"; + +export type HistoryLinkPopAction = { + count?: number; + type: "pop"; +}; + +export type HistoryLinkPushAction = { + entries: HistoryEntry[]; + type: "push"; +}; + +export type HistoryLinkAction = HistoryLinkPopAction | HistoryLinkPushAction; + +export type HistoryLinkActions = HistoryLinkAction[]; + +export type HistoryLinkInput = ClientLinkInput & { + actions: HistoryLinkActions; +}; diff --git a/src/src/isomorphic/generic/components/history-provider/components/history-synchronizer/main.tsx b/src/src/isomorphic/generic/components/history-provider/components/history-synchronizer/main.tsx index 749d419..7bd5ee2 100644 --- a/src/src/isomorphic/generic/components/history-provider/components/history-synchronizer/main.tsx +++ b/src/src/isomorphic/generic/components/history-provider/components/history-synchronizer/main.tsx @@ -18,13 +18,15 @@ export function HistorySynchronizer({}: HistorySynchronizerInput) { path: pathname, query: searchParams.entries().reduce( (acc, [key, value]) => { + acc ??= {}; + if (acc[key] === undefined) acc[key] = value; else if (Array.isArray(acc[key])) acc[key].push(value); else acc[key] = [acc[key], value]; return acc; }, - {} as { [key: string]: string | string[] }, + undefined as undefined | { [key: string]: string | string[] }, ), }; diff --git a/src/src/isomorphic/generic/components/route-modal/main.tsx b/src/src/isomorphic/generic/components/route-modal/main.tsx index 8ebb86c..a5835bb 100644 --- a/src/src/isomorphic/generic/components/route-modal/main.tsx +++ b/src/src/isomorphic/generic/components/route-modal/main.tsx @@ -6,30 +6,23 @@ import { useCallback } from "react"; import type { RouteModalInput } from "./types"; -import { useLocalization } from "../../../localization/hooks/use-localization"; +import { createUrl } from "../../../../common/generic/lib/create-url"; import { useHistory } from "../../hooks/use-history"; -export function RouteModal({ fallback, title, ...props }: RouteModalInput) { +export function RouteModal({ fallback, force, ...input }: RouteModalInput) { const router = useRouter(); const { history } = useHistory(); - const { localization } = useLocalization(); const handleClose = useCallback(() => { - if (history.entries.length > 1) router.back(); - else router.push(fallback); - }, [fallback, history.entries.length, router]); + if (!force && history.entries.length > 1) { + const target = history.entries[history.entries.length - 2]!; + const { url } = createUrl({ path: target.path, query: target.query }); + router.push(url); + } else { + router.push(fallback); + } + }, [fallback, force, history.entries, history.entries.length, router]); - return ( - - ); + return ; } diff --git a/src/src/isomorphic/generic/components/route-modal/types.ts b/src/src/isomorphic/generic/components/route-modal/types.ts index 33fb7ee..ef20604 100644 --- a/src/src/isomorphic/generic/components/route-modal/types.ts +++ b/src/src/isomorphic/generic/components/route-modal/types.ts @@ -1,10 +1,6 @@ -import type { MessageDescriptor } from "@lingui/core"; import type { ModalProps } from "@mantine/core"; -export type RouteModalInput = Omit< - ModalProps, - "onClose" | "opened" | "title" -> & { +export type RouteModalInput = Omit & { fallback: string; - title?: MessageDescriptor | string; + force?: boolean; }; diff --git a/src/src/isomorphic/localization/components/localized/index.ts b/src/src/isomorphic/localization/components/localized/index.ts new file mode 100644 index 0000000..793c4a3 --- /dev/null +++ b/src/src/isomorphic/localization/components/localized/index.ts @@ -0,0 +1,2 @@ +export { Localized } from "./main"; +export type { LocalizedInput } from "./types"; diff --git a/src/src/isomorphic/localization/components/localized/main.tsx b/src/src/isomorphic/localization/components/localized/main.tsx new file mode 100644 index 0000000..5014bfc --- /dev/null +++ b/src/src/isomorphic/localization/components/localized/main.tsx @@ -0,0 +1,11 @@ +"use client"; + +import type { LocalizedInput } from "./types"; + +import { useLocalization } from "../../hooks/use-localization"; + +export function Localized({ message }: LocalizedInput) { + const { localization } = useLocalization(); + + return localization.localize(message); +} diff --git a/src/src/isomorphic/localization/components/localized/types.ts b/src/src/isomorphic/localization/components/localized/types.ts new file mode 100644 index 0000000..41e8e37 --- /dev/null +++ b/src/src/isomorphic/localization/components/localized/types.ts @@ -0,0 +1,5 @@ +import type { MessageDescriptor } from "@lingui/core"; + +export type LocalizedInput = { + message: MessageDescriptor; +}; diff --git a/src/src/isomorphic/state/types.ts b/src/src/isomorphic/state/types.ts index ddf5e7a..e46e38a 100644 --- a/src/src/isomorphic/state/types.ts +++ b/src/src/isomorphic/state/types.ts @@ -4,7 +4,7 @@ import type { ReadonlyDeep } from "type-fest"; export type HistoryEntry = { path: string; - query: { [key: string]: string | string[] }; + query?: { [key: string]: string | string[] }; }; export type HistoryState = {