diff --git a/packages/root-cms/package.json b/packages/root-cms/package.json index 224deb68b..10f7cc3fb 100644 --- a/packages/root-cms/package.json +++ b/packages/root-cms/package.json @@ -154,7 +154,7 @@ "playwright": "1.56.1", "preact": "10.27.1", "preact-render-to-string": "6.6.1", - "preact-router": "4.1.2", + "preact-iso": "2.6.0", "react": "npm:@preact/compat@18.3.1", "react-dom": "npm:@preact/compat@18.3.1", "react-json-view-compare": "2.0.2", diff --git a/packages/root-cms/ui/components/CopyDocModal/CopyDocModal.tsx b/packages/root-cms/ui/components/CopyDocModal/CopyDocModal.tsx index 5dab0bc57..005910ba2 100644 --- a/packages/root-cms/ui/components/CopyDocModal/CopyDocModal.tsx +++ b/packages/root-cms/ui/components/CopyDocModal/CopyDocModal.tsx @@ -1,8 +1,8 @@ import {Button} from '@mantine/core'; import {ContextModalProps, useModals} from '@mantine/modals'; import {showNotification} from '@mantine/notifications'; +import {useLocation} from 'preact-iso'; import {useState} from 'preact/hooks'; -import {route} from 'preact-router'; import {isSlugValid, normalizeSlug} from '../../../shared/slug.js'; import {useModalTheme} from '../../hooks/useModalTheme.js'; import {cmsCopyDoc, cmsCreateDoc} from '../../utils/doc.js'; @@ -35,6 +35,7 @@ export function useCopyDocModal(props: CopyDocModalProps) { export function CopyDocModal(modalProps: ContextModalProps) { const {innerProps: props, context, id} = modalProps; + const {route} = useLocation(); const [toCollectionId, setToCollectionId] = useState(''); const [toSlug, setToSlug] = useState(''); const [error, setError] = useState(''); diff --git a/packages/root-cms/ui/components/DataSourceForm/DataSourceForm.tsx b/packages/root-cms/ui/components/DataSourceForm/DataSourceForm.tsx index 7a3c5e8b8..5c5c0e275 100644 --- a/packages/root-cms/ui/components/DataSourceForm/DataSourceForm.tsx +++ b/packages/root-cms/ui/components/DataSourceForm/DataSourceForm.tsx @@ -6,8 +6,8 @@ import { Textarea, } from '@mantine/core'; import {showNotification} from '@mantine/notifications'; +import {useLocation} from 'preact-iso'; import {useEffect, useRef, useState} from 'preact/hooks'; -import {route} from 'preact-router'; import {isSlugValid} from '../../../shared/slug.js'; import {useGapiClient} from '../../hooks/useGapiClient.js'; import { @@ -34,6 +34,7 @@ export interface DataSourceFormProps { } export function DataSourceForm(props: DataSourceFormProps) { + const {route} = useLocation(); const formRef = useRef(null); const gapiClient = useGapiClient(); const [submitting, setSubmitting] = useState(false); diff --git a/packages/root-cms/ui/components/DocEditor/DocEditor.tsx b/packages/root-cms/ui/components/DocEditor/DocEditor.tsx index 486338276..c326556a5 100644 --- a/packages/root-cms/ui/components/DocEditor/DocEditor.tsx +++ b/packages/root-cms/ui/components/DocEditor/DocEditor.tsx @@ -38,6 +38,7 @@ import { IconSparkles, } from '@tabler/icons-preact'; import {createContext} from 'preact'; +import {useLocation} from 'preact-iso'; import { useContext, useEffect, @@ -47,7 +48,6 @@ import { useState, useCallback, } from 'preact/hooks'; -import {route} from 'preact-router'; import * as schema from '../../../core/schema.js'; import {useCollectionSchema} from '../../hooks/useCollectionSchema.js'; import { @@ -172,6 +172,7 @@ type StatusBarProps = DocEditorProps & { }; DocEditor.StatusBar = (props: StatusBarProps) => { + const {route} = useLocation(); const {roles} = useProjectRoles(); const currentUserEmail = window.firebase.user.email || ''; const canPublish = testCanPublish(roles, currentUserEmail); @@ -200,7 +201,7 @@ DocEditor.StatusBar = (props: StatusBarProps) => { draft.controller.removePublishingLock(); } }, - [props.docId] + [props.docId, route] ); const publishDocModal = usePublishDocModal({docId: props.docId}); diff --git a/packages/root-cms/ui/components/NewDocModal/NewDocModal.tsx b/packages/root-cms/ui/components/NewDocModal/NewDocModal.tsx index 3a0bafb5f..2c0532a67 100644 --- a/packages/root-cms/ui/components/NewDocModal/NewDocModal.tsx +++ b/packages/root-cms/ui/components/NewDocModal/NewDocModal.tsx @@ -1,6 +1,6 @@ import {Button, Modal, useMantineTheme} from '@mantine/core'; +import {useLocation} from 'preact-iso'; import {useState} from 'preact/hooks'; -import {route} from 'preact-router'; import {isSlugValid, normalizeSlug} from '../../../shared/slug.js'; import {useCollectionSchema} from '../../hooks/useCollectionSchema.js'; import {cmsCreateDoc} from '../../utils/doc.js'; @@ -17,6 +17,7 @@ interface NewDocModalProps { } export function NewDocModal(props: NewDocModalProps) { + const {route} = useLocation(); const [slug, setSlug] = useState(''); const [rpcLoading, setRpcLoading] = useState(false); const [slugError, setSlugError] = useState(''); diff --git a/packages/root-cms/ui/components/ReleaseForm/ReleaseForm.tsx b/packages/root-cms/ui/components/ReleaseForm/ReleaseForm.tsx index 392199596..af340e490 100644 --- a/packages/root-cms/ui/components/ReleaseForm/ReleaseForm.tsx +++ b/packages/root-cms/ui/components/ReleaseForm/ReleaseForm.tsx @@ -9,8 +9,8 @@ import { } from '@mantine/core'; import {showNotification} from '@mantine/notifications'; import {IconArrowUpRight, IconTrash} from '@tabler/icons-preact'; +import {useLocation} from 'preact-iso'; import {useEffect, useRef, useState} from 'preact/hooks'; -import {route} from 'preact-router'; import {isSlugValid} from '../../../shared/slug.js'; import {ConditionalTooltip} from '../../components/ConditionalTooltip/ConditionalTooltip.js'; import {useProjectRoles} from '../../hooks/useProjectRoles.js'; @@ -34,6 +34,7 @@ export interface ReleaseFormProps { } export function ReleaseForm(props: ReleaseFormProps) { + const {route} = useLocation(); const formRef = useRef(null); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(''); diff --git a/packages/root-cms/ui/layout/Layout.tsx b/packages/root-cms/ui/layout/Layout.tsx index 4d78ed28b..bfaa36d97 100644 --- a/packages/root-cms/ui/layout/Layout.tsx +++ b/packages/root-cms/ui/layout/Layout.tsx @@ -19,7 +19,7 @@ import { IconSettings, } from '@tabler/icons-preact'; import {ComponentChildren} from 'preact'; -import {useRouter} from 'preact-router'; +import {useLocation} from 'preact-iso'; import type {CMSBuiltInSidebarTool} from '../../core/plugin.js'; import packageJson from '../../package.json' assert {type: 'json'}; import {RootCMSLogo} from '../components/RootCMSLogo/RootCMSLogo.js'; @@ -60,8 +60,8 @@ Layout.Top = () => { }; Layout.Side = () => { - const [route] = useRouter(); - const currentUrl = route.url.replace(/\/*$/g, ''); + const {url} = useLocation(); + const currentUrl = url.replace(/\/*$/g, ''); const user = window.firebase.user; const sidebarTools = window.__ROOT_CTX.sidebar?.tools; const hiddenBuiltInTools = new Set( diff --git a/packages/root-cms/ui/pages/CollectionPage/CollectionPage.tsx b/packages/root-cms/ui/pages/CollectionPage/CollectionPage.tsx index cfa31e232..ab5acd5f0 100644 --- a/packages/root-cms/ui/pages/CollectionPage/CollectionPage.tsx +++ b/packages/root-cms/ui/pages/CollectionPage/CollectionPage.tsx @@ -2,8 +2,8 @@ import './CollectionPage.css'; import {Button, Loader, Select, Tabs, Tooltip} from '@mantine/core'; import {IconArrowRoundaboutRight, IconCirclePlus} from '@tabler/icons-preact'; +import {useLocation} from 'preact-iso'; import {useEffect, useState} from 'preact/hooks'; -import {route} from 'preact-router'; import {CollectionTree} from '../../components/CollectionTree/CollectionTree.js'; import {ConditionalTooltip} from '../../components/ConditionalTooltip/ConditionalTooltip.js'; import {DocActionsMenu} from '../../components/DocActionsMenu/DocActionsMenu.js'; @@ -26,6 +26,7 @@ interface CollectionPageProps { } export function CollectionPage(props: CollectionPageProps) { + const {route} = useLocation(); const [query, setQuery] = useState(''); const projectId = window.__ROOT_CTX.rootConfig.projectId; @@ -106,6 +107,7 @@ interface CollectionProps { } CollectionPage.Collection = (props: CollectionProps) => { + const {route} = useLocation(); const {roles} = useProjectRoles(); const currentUserEmail = window.firebase.user.email || ''; const canEdit = testCanEdit(roles, currentUserEmail); diff --git a/packages/root-cms/ui/pages/EditDataSourcePage/EditDataSourcePage.tsx b/packages/root-cms/ui/pages/EditDataSourcePage/EditDataSourcePage.tsx index 52a65b637..f1d9bda8f 100644 --- a/packages/root-cms/ui/pages/EditDataSourcePage/EditDataSourcePage.tsx +++ b/packages/root-cms/ui/pages/EditDataSourcePage/EditDataSourcePage.tsx @@ -2,7 +2,7 @@ import {ActionIcon, Breadcrumbs, Tooltip} from '@mantine/core'; import {useModals} from '@mantine/modals'; import {showNotification, updateNotification} from '@mantine/notifications'; import {IconTrashFilled} from '@tabler/icons-preact'; -import {route} from 'preact-router'; +import {useLocation} from 'preact-iso'; import {DataSourceForm} from '../../components/DataSourceForm/DataSourceForm.js'; import {Heading} from '../../components/Heading/Heading.js'; import {Text} from '../../components/Text/Text.js'; @@ -12,6 +12,7 @@ import {deleteDataSource} from '../../utils/data-source.js'; import './EditDataSourcePage.css'; export function EditDataSourcePage(props: {id: string}) { + const {route} = useLocation(); const modals = useModals(); const modalTheme = useModalTheme(); diff --git a/packages/root-cms/ui/pages/EditReleasePage/EditReleasePage.tsx b/packages/root-cms/ui/pages/EditReleasePage/EditReleasePage.tsx index bcb9e8d0c..de2f3401c 100644 --- a/packages/root-cms/ui/pages/EditReleasePage/EditReleasePage.tsx +++ b/packages/root-cms/ui/pages/EditReleasePage/EditReleasePage.tsx @@ -2,7 +2,7 @@ import {ActionIcon, Breadcrumbs, Tooltip} from '@mantine/core'; import {useModals} from '@mantine/modals'; import {showNotification, updateNotification} from '@mantine/notifications'; import {IconTrashFilled} from '@tabler/icons-preact'; -import {route} from 'preact-router'; +import {useLocation} from 'preact-iso'; import {ConditionalTooltip} from '../../components/ConditionalTooltip/ConditionalTooltip.js'; import {Heading} from '../../components/Heading/Heading.js'; import {ReleaseForm} from '../../components/ReleaseForm/ReleaseForm.js'; @@ -15,6 +15,7 @@ import './EditReleasePage.css'; import {deleteRelease} from '../../utils/release.js'; export function EditReleasePage(props: {id: string}) { + const {route} = useLocation(); const modals = useModals(); const modalTheme = useModalTheme(); const {roles} = useProjectRoles(); diff --git a/packages/root-cms/ui/preact-router.d.ts b/packages/root-cms/ui/preact-router.d.ts deleted file mode 100644 index 4e6b5ecd9..000000000 --- a/packages/root-cms/ui/preact-router.d.ts +++ /dev/null @@ -1,108 +0,0 @@ -// NOTE(stevenle): The package.json in preact-router doesn't correctly tag -// the types, so we copy/paste the type definitions here. -// -// Files included: -// node_modules/preact-router/index.d.ts -// node_modules/preact-router/match/index.d.ts - -declare module 'preact-router' { - export function route(url: string, replace?: boolean): boolean; - export function route(options: {url: string; replace?: boolean}): boolean; - - export function getCurrentUrl(): string; - - export interface Location { - pathname: string; - search: string; - } - - export interface CustomHistory { - listen(callback: (location: Location) => void): () => void; - location: Location; - push(path: string): void; - replace(path: string): void; - } - - export interface RoutableProps { - path?: string; - default?: boolean; - } - - export interface RouterOnChangeArgs< - RouteParams extends Record | null = Record< - string, - string | undefined - > | null, - > { - router: Router; - url: string; - previous?: string; - active: preact.VNode[]; - current: preact.VNode; - path: string | null; - matches: RouteParams; - } - - export interface RouterProps< - RouteParams extends Record | null = Record< - string, - string | undefined - > | null, - > extends RoutableProps { - history?: CustomHistory; - static?: boolean; - url?: string; - onChange?: (args: RouterOnChangeArgs) => void; - } - - export class Router extends preact.Component { - canRoute(url: string): boolean; - getMatchingChildren( - children: preact.VNode[], - url: string, - invoke: boolean - ): preact.VNode[]; - routeTo(url: string): boolean; - render(props: RouterProps): preact.VNode; - } - - type AnyComponent = - | preact.FunctionalComponent - | preact.ComponentConstructor; - - export interface RouteProps extends RoutableProps { - component: AnyComponent; - } - - export function Route( - props: RouteProps & Partial - ): preact.VNode; - - export function Link( - props: {activeClassName?: string} & preact.JSX.HTMLAttributes - ): preact.VNode; - - export function useRouter< - RouteParams extends Record | null = Record< - string, - string | undefined - > | null, - >(): [ - RouterOnChangeArgs, - ( - urlOrOptions: string | {url: string; replace?: boolean}, - replace?: boolean - ) => boolean, - ]; - - export default Router; -} - -declare module 'preact-router/match' { - export interface LinkProps extends preact.JSX.HTMLAttributes { - activeClassName?: string; - children?: preact.ComponentChildren; - } - - export function Link(props: LinkProps): preact.VNode; -} diff --git a/packages/root-cms/ui/ui.tsx b/packages/root-cms/ui/ui.tsx index 8c1c78b65..25ffd0399 100644 --- a/packages/root-cms/ui/ui.tsx +++ b/packages/root-cms/ui/ui.tsx @@ -9,7 +9,7 @@ import {User, getAuth} from 'firebase/auth'; import {initializeFirestore} from 'firebase/firestore'; import {getStorage} from 'firebase/storage'; import {render} from 'preact'; -import {Route, Router} from 'preact-router'; +import {Route, Router} from 'preact-iso'; import type {CMSBuiltInSidebarTool} from '../core/plugin.js'; import {Collection} from '../core/schema.js'; import {AiEditModal} from './components/AiEditModal/AiEditModal.js';