From eb12f728d16eb84c1a2f82dcfc640e0bd1734494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Thu, 4 Apr 2024 23:18:57 +0200 Subject: [PATCH] update modals to use pushmodal --- apps/dashboard/package.json | 1 + apps/dashboard/src/components/ui/dialog.tsx | 11 +- apps/dashboard/src/modals/AddClient.tsx | 3 +- apps/dashboard/src/modals/AddDashboard.tsx | 2 - apps/dashboard/src/modals/AddProject.tsx | 2 - apps/dashboard/src/modals/AddReference.tsx | 2 - apps/dashboard/src/modals/Confirm.tsx | 6 +- apps/dashboard/src/modals/EditClient.tsx | 2 - apps/dashboard/src/modals/EditDashboard.tsx | 2 - apps/dashboard/src/modals/EditProject.tsx | 2 - apps/dashboard/src/modals/EditReport.tsx | 6 +- apps/dashboard/src/modals/Modal/Container.tsx | 7 +- apps/dashboard/src/modals/SaveReport.tsx | 7 +- .../src/modals/ShareOverviewModal.tsx | 2 - apps/dashboard/src/modals/index.tsx | 182 +----------------- tooling/eslint/base.js | 2 + 16 files changed, 19 insertions(+), 220 deletions(-) diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 2d542e6c..642aa40d 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -71,6 +71,7 @@ "next-themes": "^0.2.1", "nuqs": "^1.16.1", "prisma-error-enum": "^0.1.3", + "pushmodal": "^0.0.7", "ramda": "^0.29.1", "random-animal-name": "^0.1.1", "react": "18.2.0", diff --git a/apps/dashboard/src/components/ui/dialog.tsx b/apps/dashboard/src/components/ui/dialog.tsx index c0070c5e..31dce063 100644 --- a/apps/dashboard/src/components/ui/dialog.tsx +++ b/apps/dashboard/src/components/ui/dialog.tsx @@ -20,7 +20,7 @@ const DialogOverlay = React.forwardRef< {children} - - - Close - )); diff --git a/apps/dashboard/src/modals/AddClient.tsx b/apps/dashboard/src/modals/AddClient.tsx index ee651099..fd5c7810 100644 --- a/apps/dashboard/src/modals/AddClient.tsx +++ b/apps/dashboard/src/modals/AddClient.tsx @@ -11,8 +11,7 @@ import { useAppParams } from '@/hooks/useAppParams'; import { api, handleError } from '@/trpc/client'; import { cn } from '@/utils/cn'; import { zodResolver } from '@hookform/resolvers/zod'; -import { SaveIcon, WallpaperIcon } from 'lucide-react'; -import Link from 'next/link'; +import { SaveIcon } from 'lucide-react'; import { useRouter } from 'next/navigation'; import type { SubmitHandler } from 'react-hook-form'; import { Controller, useForm } from 'react-hook-form'; diff --git a/apps/dashboard/src/modals/AddDashboard.tsx b/apps/dashboard/src/modals/AddDashboard.tsx index 99e60b80..e204981e 100644 --- a/apps/dashboard/src/modals/AddDashboard.tsx +++ b/apps/dashboard/src/modals/AddDashboard.tsx @@ -1,5 +1,3 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; diff --git a/apps/dashboard/src/modals/AddProject.tsx b/apps/dashboard/src/modals/AddProject.tsx index 38090307..ec6ff79c 100644 --- a/apps/dashboard/src/modals/AddProject.tsx +++ b/apps/dashboard/src/modals/AddProject.tsx @@ -1,5 +1,3 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; diff --git a/apps/dashboard/src/modals/AddReference.tsx b/apps/dashboard/src/modals/AddReference.tsx index 853d6faf..1f08a84e 100644 --- a/apps/dashboard/src/modals/AddReference.tsx +++ b/apps/dashboard/src/modals/AddReference.tsx @@ -1,5 +1,3 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; diff --git a/apps/dashboard/src/modals/Confirm.tsx b/apps/dashboard/src/modals/Confirm.tsx index 395e9765..6abff680 100644 --- a/apps/dashboard/src/modals/Confirm.tsx +++ b/apps/dashboard/src/modals/Confirm.tsx @@ -1,17 +1,15 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { Button } from '@/components/ui/button'; import { popModal } from '.'; import { ModalContent, ModalHeader } from './Modal/Container'; -export interface ConfirmProps { +export type ConfirmProps = { title: string; text: string; onConfirm: () => void; onCancel?: () => void; -} +}; export default function Confirm({ title, diff --git a/apps/dashboard/src/modals/EditClient.tsx b/apps/dashboard/src/modals/EditClient.tsx index 68582f24..db076fe2 100644 --- a/apps/dashboard/src/modals/EditClient.tsx +++ b/apps/dashboard/src/modals/EditClient.tsx @@ -1,5 +1,3 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; diff --git a/apps/dashboard/src/modals/EditDashboard.tsx b/apps/dashboard/src/modals/EditDashboard.tsx index 9d25ef2e..1bdad906 100644 --- a/apps/dashboard/src/modals/EditDashboard.tsx +++ b/apps/dashboard/src/modals/EditDashboard.tsx @@ -1,5 +1,3 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; diff --git a/apps/dashboard/src/modals/EditProject.tsx b/apps/dashboard/src/modals/EditProject.tsx index 38a1920c..12e45bc4 100644 --- a/apps/dashboard/src/modals/EditProject.tsx +++ b/apps/dashboard/src/modals/EditProject.tsx @@ -1,5 +1,3 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; diff --git a/apps/dashboard/src/modals/EditReport.tsx b/apps/dashboard/src/modals/EditReport.tsx index 42f299be..35459eff 100644 --- a/apps/dashboard/src/modals/EditReport.tsx +++ b/apps/dashboard/src/modals/EditReport.tsx @@ -1,5 +1,3 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; @@ -17,10 +15,10 @@ const validator = z.object({ type IForm = z.infer; -interface EditReportProps { +type EditReportProps = { form: IForm; onSubmit: SubmitHandler; -} +}; export default function EditReport({ form, onSubmit }: EditReportProps) { const { register, handleSubmit, reset, formState } = useForm({ diff --git a/apps/dashboard/src/modals/Modal/Container.tsx b/apps/dashboard/src/modals/Modal/Container.tsx index 8b660a08..8b194791 100644 --- a/apps/dashboard/src/modals/Modal/Container.tsx +++ b/apps/dashboard/src/modals/Modal/Container.tsx @@ -1,6 +1,7 @@ 'use client'; import { Button } from '@/components/ui/button'; +import { DialogContent } from '@/components/ui/dialog'; import { X } from 'lucide-react'; import { popModal } from '..'; @@ -10,11 +11,7 @@ interface ModalContentProps { } export function ModalContent({ children }: ModalContentProps) { - return ( -
- {children} -
- ); + return {children}; } interface ModalHeaderProps { diff --git a/apps/dashboard/src/modals/SaveReport.tsx b/apps/dashboard/src/modals/SaveReport.tsx index cacbc814..6a82e9a4 100644 --- a/apps/dashboard/src/modals/SaveReport.tsx +++ b/apps/dashboard/src/modals/SaveReport.tsx @@ -1,5 +1,3 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; @@ -18,10 +16,10 @@ import type { IChartInput } from '@openpanel/validation'; import { popModal } from '.'; import { ModalContent, ModalHeader } from './Modal/Container'; -interface SaveReportProps { +type SaveReportProps = { report: IChartInput; reportId?: string; -} +}; const validator = z.object({ name: z.string().min(1, 'Required'), @@ -74,7 +72,6 @@ export default function SaveReport({ report }: SaveReportProps) { const dashboardQuery = api.dashboard.list.useQuery({ projectId, }); - const dashboards = (dashboardQuery.data ?? []).map((item) => ({ value: item.id, label: item.name, diff --git a/apps/dashboard/src/modals/ShareOverviewModal.tsx b/apps/dashboard/src/modals/ShareOverviewModal.tsx index bba3fa3b..2a4114c5 100644 --- a/apps/dashboard/src/modals/ShareOverviewModal.tsx +++ b/apps/dashboard/src/modals/ShareOverviewModal.tsx @@ -1,5 +1,3 @@ -'use client'; - import { ButtonContainer } from '@/components/button-container'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; diff --git a/apps/dashboard/src/modals/index.tsx b/apps/dashboard/src/modals/index.tsx index f5c4ec89..74837922 100644 --- a/apps/dashboard/src/modals/index.tsx +++ b/apps/dashboard/src/modals/index.tsx @@ -1,18 +1,12 @@ 'use client'; -import { Suspense, useEffect, useRef, useState } from 'react'; import { Loader } from 'lucide-react'; -import mitt from 'mitt'; import dynamic from 'next/dynamic'; -import { useOnClickOutside } from 'usehooks-ts'; +import { createPushModal } from 'pushmodal'; import type { ConfirmProps } from './Confirm'; -const Loading = () => ( -
- -
-); +const Loading = () => ; const modals = { EditProject: dynamic(() => import('./EditProject'), { @@ -50,175 +44,9 @@ const modals = { }), }; -const emitter = mitt<{ - push: { - name: ModalRoutes; - props: Record; - }; - replace: { - name: ModalRoutes; - props: Record; - }; - pop: { name?: ModalRoutes }; - unshift: { name: ModalRoutes }; -}>(); - -type ModalRoutes = keyof typeof modals; - -interface StateItem { - key: string; - name: ModalRoutes; - props: Record; -} - -interface ModalWrapperProps { - children: React.ReactNode; - name?: ModalRoutes; - isOnTop?: boolean; -} - -export function ModalWrapper({ children, name, isOnTop }: ModalWrapperProps) { - const ref = useRef(null); - - useOnClickOutside(ref, (event) => { - const target = event.target as HTMLElement; - const isPortal = - typeof target.closest === 'function' - ? !!target.closest('[data-radix-popper-content-wrapper]') - : false; - - if (isOnTop && !isPortal && name) { - emitter.emit('pop', { - name, - }); - } - }); - - return ( -
-
- {children} -
-
- ); -} - -export function ModalProvider() { - const [state, setState] = useState([]); - - useEffect(() => { - document.addEventListener('keydown', (event) => { - if (event.key === 'Escape' && state.length > 0) { - setState((p) => { - p.pop(); - return [...p]; - }); - } - }); - }, [state]); - - useEffect(() => { - emitter.on('push', ({ name, props }) => { - setState((p) => [ - ...p, - { - key: Math.random().toString(), - name, - props, - }, - ]); - }); - - emitter.on('replace', ({ name, props }) => { - setState([ - { - key: Math.random().toString(), - name, - props, - }, - ]); - }); - - emitter.on('pop', ({ name }) => { - setState((items) => { - const match = - name === undefined - ? // Pick last item if no name is provided - items.length - 1 - : items.findLastIndex((item) => item.name === name); - return items.filter((_, index) => index !== match); - }); - }); - - emitter.on('unshift', ({ name }) => { - setState((items) => { - const match = items.findIndex((item) => item.name === name); - return items.filter((_, index) => index !== match); - }); - }); - - return () => emitter.all.clear(); - }); - return ( - <> - {!!state.length && ( -
- )} - {state.map((item, index) => { - const Modal = modals[item.name]; - return ( - - {/* @ts-expect-error */} - - - ); - })} - - ); -} - -type GetComponentProps = T extends - | React.ComponentType - | React.Component - ? P - : never; -type OrUndefined = T extends Record ? undefined : T; - -export const pushModal = < - T extends StateItem['name'], - B extends OrUndefined>, ->( - name: T, - ...rest: B extends undefined ? [] : [B] -) => - emitter.emit('push', { - name, - // @ts-expect-error - props: Array.isArray(rest) && rest[0] ? rest[0] : {}, - }); -export const replaceModal = < - T extends StateItem['name'], - B extends OrUndefined>, ->( - name: T, - ...rest: B extends undefined ? [] : [B] -) => - emitter.emit('replace', { - name, - // @ts-expect-error - props: Array.isArray(rest) && rest[0] ? rest[0] : {}, - }); -export const popModal = (name?: StateItem['name']) => - emitter.emit('pop', { - name, - }); -export const unshiftModal = (name: StateItem['name']) => - emitter.emit('unshift', { - name, +export const { pushModal, popModal, popAllModals, ModalProvider } = + createPushModal({ + modals, }); export const showConfirm = (props: ConfirmProps) => pushModal('Confirm', props); diff --git a/tooling/eslint/base.js b/tooling/eslint/base.js index 7ad6389d..5ae37e64 100644 --- a/tooling/eslint/base.js +++ b/tooling/eslint/base.js @@ -40,6 +40,8 @@ const config = { '@typescript-eslint/no-unsafe-member-access': 'warn', '@typescript-eslint/no-unsafe-argument': 'warn', '@typescript-eslint/prefer-nullish-coalescing': 'off', + '@typescript-eslint/prefer-interface': 'off', + '@typescript-eslint/consistent-type-definitions': 'off', }, ignorePatterns: [ '**/.eslintrc.cjs',