Migrate secondary modals to shared frame
This commit is contained in:
parent
bd3b99b3ee
commit
5e0303d065
5 changed files with 228 additions and 406 deletions
|
|
@ -1,15 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { Fragment, useState, useEffect } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogPanel,
|
||||
DialogTitle,
|
||||
Transition,
|
||||
TransitionChild,
|
||||
} from '@headlessui/react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { updateAppConfig } from '@/lib/client/dexie';
|
||||
import { Button, dialogPanelStyles } from '@/components/ui';
|
||||
import { Button, ModalFrame, ModalTitle } from '@/components/ui';
|
||||
|
||||
interface PrivacyModalProps {
|
||||
isOpen: boolean;
|
||||
|
|
@ -77,38 +70,8 @@ export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps)
|
|||
};
|
||||
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-[80]" onClose={onDismiss ?? (() => { })}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 overlay-dim backdrop-blur-sm" />
|
||||
</TransitionChild>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel data-testid="privacy-modal" className={dialogPanelStyles({ size: 'md' })}>
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-6 text-foreground"
|
||||
>
|
||||
Privacy & Data Usage
|
||||
</DialogTitle>
|
||||
<ModalFrame open={isOpen} onClose={onDismiss ?? (() => {})} panelTestId="privacy-modal" className="z-[80]">
|
||||
<ModalTitle>Privacy & Data Usage</ModalTitle>
|
||||
|
||||
<PrivacyModalBody origin={origin} />
|
||||
|
||||
|
|
@ -146,12 +109,7 @@ export function PrivacyModal({ isOpen, onAccept, onDismiss }: PrivacyModalProps)
|
|||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</ModalFrame>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -179,46 +137,15 @@ export function showPrivacyModal(): void {
|
|||
};
|
||||
|
||||
return (
|
||||
<Transition
|
||||
appear
|
||||
show={show}
|
||||
as={Fragment}
|
||||
<ModalFrame
|
||||
open={show}
|
||||
onClose={handleClose}
|
||||
afterLeave={() => {
|
||||
root.unmount();
|
||||
container.remove();
|
||||
}}
|
||||
>
|
||||
<Dialog as="div" className="relative z-50" onClose={handleClose}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 overlay-dim backdrop-blur-sm" />
|
||||
</TransitionChild>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel className={dialogPanelStyles({ size: 'md' })}>
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-6 text-foreground"
|
||||
>
|
||||
Privacy & Data Usage
|
||||
</DialogTitle>
|
||||
<ModalTitle>Privacy & Data Usage</ModalTitle>
|
||||
|
||||
<PrivacyModalBody origin={origin} />
|
||||
|
||||
|
|
@ -231,12 +158,7 @@ export function showPrivacyModal(): void {
|
|||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</ModalFrame>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,9 @@
|
|||
'use client';
|
||||
|
||||
import { Fragment, useState } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogPanel,
|
||||
DialogTitle,
|
||||
Transition,
|
||||
TransitionChild,
|
||||
} from '@headlessui/react';
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Button, dialogPanelStyles } from '@/components/ui';
|
||||
import { Button, ModalFrame, ModalTitle } from '@/components/ui';
|
||||
|
||||
export type ClaimableCounts = {
|
||||
documents: number;
|
||||
|
|
@ -74,38 +67,8 @@ export default function ClaimDataModal({
|
|||
};
|
||||
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-[80]" onClose={onDismiss}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 overlay-dim backdrop-blur-sm" />
|
||||
</TransitionChild>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel data-testid="claim-modal" className={dialogPanelStyles({ size: 'md' })}>
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-6 text-foreground mb-4"
|
||||
>
|
||||
Existing Data Found
|
||||
</DialogTitle>
|
||||
<ModalFrame open={isOpen} onClose={onDismiss} panelTestId="claim-modal" className="z-[80]">
|
||||
<ModalTitle className="mb-4">Existing Data Found</ModalTitle>
|
||||
|
||||
<p className="text-sm text-soft mb-2">
|
||||
We found existing anonymous data from before auth was enabled.
|
||||
|
|
@ -146,11 +109,6 @@ export default function ClaimDataModal({
|
|||
{isClaiming ? 'Claiming...' : 'Claim Data'}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</ModalFrame>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
'use client';
|
||||
|
||||
import { Fragment, useCallback, useEffect, useState } from 'react';
|
||||
import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { getAllEpubDocuments, getAllHtmlDocuments, getAllPdfDocuments, updateAppConfig } from '@/lib/client/dexie';
|
||||
import { listDocuments, mimeTypeForDoc, uploadDocuments } from '@/lib/client/api/documents';
|
||||
import { useDocuments } from '@/contexts/DocumentContext';
|
||||
import type { BaseDocument } from '@/types/documents';
|
||||
import { cacheStoredDocumentFromBytes } from '@/lib/client/cache/documents';
|
||||
import { Button, dialogPanelStyles } from '@/components/ui';
|
||||
import { Button, ModalFrame, ModalTitle } from '@/components/ui';
|
||||
|
||||
type DexieMigrationModalProps = {
|
||||
isOpen: boolean;
|
||||
|
|
@ -137,35 +136,15 @@ export function DexieMigrationModal({
|
|||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-[80]" onClose={() => (closeDisabled ? null : onComplete())}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
<ModalFrame
|
||||
open={isOpen}
|
||||
onClose={() => {
|
||||
if (!closeDisabled) onComplete();
|
||||
}}
|
||||
panelTestId="migration-modal"
|
||||
className="z-[80]"
|
||||
>
|
||||
<div className="fixed inset-0 overlay-dim backdrop-blur-sm" />
|
||||
</TransitionChild>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel data-testid="migration-modal" className={dialogPanelStyles({ size: 'md' })}>
|
||||
<DialogTitle as="h3" className="text-lg font-semibold leading-6 text-foreground mb-4">
|
||||
{title}
|
||||
</DialogTitle>
|
||||
<ModalTitle className="mb-4">{title}</ModalTitle>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-soft mb-2">
|
||||
Found {localCount} document{localCount === 1 ? '' : 's'} stored locally from an older version.
|
||||
|
|
@ -204,11 +183,6 @@ export function DexieMigrationModal({
|
|||
{isUploading ? 'Uploading…' : 'Upload'}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</ModalFrame>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react';
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { BaseDocument } from '@/types/documents';
|
||||
import { Button, dialogPanelStyles } from '@/components/ui';
|
||||
import { Button, ModalFrame, ModalTitle } from '@/components/ui';
|
||||
|
||||
interface DocumentSelectionModalProps {
|
||||
isOpen: boolean;
|
||||
|
|
@ -114,36 +113,8 @@ export function DocumentSelectionModal({
|
|||
};
|
||||
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-[60]" onClose={onClose}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 overlay-dim backdrop-blur-sm" />
|
||||
</TransitionChild>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-start justify-center p-4 pt-6 text-center sm:items-center sm:pt-4">
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-standard duration-slow"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-standard duration-base"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<DialogPanel className={dialogPanelStyles({ size: 'lg', className: 'flex h-[80vh] flex-col' })}>
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
className="text-lg font-semibold leading-6 text-foreground mb-4 flex-shrink-0 flex justify-between items-center"
|
||||
>
|
||||
<ModalFrame open={isOpen} onClose={onClose} size="lg" panelClassName="flex h-[80vh] flex-col" className="z-[60]">
|
||||
<ModalTitle className="mb-4 flex flex-shrink-0 items-center justify-between">
|
||||
{title}
|
||||
{files.length > 0 && (
|
||||
<div className="flex items-center text-sm font-normal">
|
||||
|
|
@ -161,7 +132,7 @@ export function DocumentSelectionModal({
|
|||
</label>
|
||||
</div>
|
||||
)}
|
||||
</DialogTitle>
|
||||
</ModalTitle>
|
||||
|
||||
<div className="flex-1 overflow-auto border border-line rounded-lg bg-background p-2 min-h-0">
|
||||
{isLoading ? (
|
||||
|
|
@ -178,9 +149,9 @@ export function DocumentSelectionModal({
|
|||
<div
|
||||
key={file.id}
|
||||
onClick={(e) => handleRowClick(e, file.id)}
|
||||
className={`flex items-center gap-3 px-3 py-2 rounded-md cursor-pointer text-sm select-none
|
||||
${isSelected ? 'bg-accent-wash' : 'hover:bg-accent-wash'}
|
||||
`}
|
||||
className={`flex items-center gap-3 px-3 py-2 rounded-md cursor-pointer text-sm select-none ${
|
||||
isSelected ? 'bg-accent-wash' : 'hover:bg-accent-wash'
|
||||
}`}
|
||||
>
|
||||
<div className="flex-shrink-0" onClick={(e) => e.stopPropagation()}>
|
||||
<input
|
||||
|
|
@ -218,12 +189,7 @@ export function DocumentSelectionModal({
|
|||
{isProcessing ? 'Processing...' : `${confirmLabel} ${selectedCount > 0 ? `(${selectedCount})` : ''}`}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</ModalFrame>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export function ModalFrame({
|
|||
panelClassName,
|
||||
panelTestId,
|
||||
onKeyDown,
|
||||
afterLeave,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
|
|
@ -52,9 +53,10 @@ export function ModalFrame({
|
|||
panelClassName?: string;
|
||||
panelTestId?: string;
|
||||
onKeyDown?: KeyboardEventHandler<HTMLDivElement>;
|
||||
afterLeave?: () => void;
|
||||
}) {
|
||||
return (
|
||||
<Transition appear show={open} as={Fragment}>
|
||||
<Transition appear show={open} as={Fragment} afterLeave={afterLeave}>
|
||||
<Dialog as="div" role={undefined} className={cn('relative z-50', className)} onClose={onClose} onKeyDown={onKeyDown}>
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
|
|
|
|||
Loading…
Reference in a new issue