From bd3b99b3ee286116756574263d3d28fd9df7eb29 Mon Sep 17 00:00:00 2001 From: Richard R Date: Mon, 1 Jun 2026 08:27:27 -0600 Subject: [PATCH] Converge modal and drawer frames --- src/components/ConfirmDialog.tsx | 84 +++--------- src/components/SettingsModal.tsx | 46 ++++--- src/components/admin/AdminFeaturesPanel.tsx | 10 +- src/components/admin/AdminProvidersPanel.tsx | 14 +- src/components/doclist/CreateFolderDialog.tsx | 67 +++------ src/components/layout/LibraryFrame.tsx | 39 +----- src/components/ui/dialog.tsx | 127 +++++++++++++++++- 7 files changed, 215 insertions(+), 172 deletions(-) diff --git a/src/components/ConfirmDialog.tsx b/src/components/ConfirmDialog.tsx index 44213c2..48e1439 100644 --- a/src/components/ConfirmDialog.tsx +++ b/src/components/ConfirmDialog.tsx @@ -1,6 +1,5 @@ -import { Fragment, KeyboardEvent } from 'react'; -import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react'; -import { Button, dialogPanelStyles } from '@/components/ui'; +import { KeyboardEvent } from 'react'; +import { Button, ModalFrame, ModalTitle } from '@/components/ui'; interface ConfirmDialogProps { isOpen: boolean; @@ -31,66 +30,25 @@ export function ConfirmDialog({ }; return ( - - - + {title} +
+

{message}

+
+ +
+ + - -
- -
- - -
-
+ {confirmText} + + + ); } diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx index 47026a2..70847d6 100644 --- a/src/components/SettingsModal.tsx +++ b/src/components/SettingsModal.tsx @@ -11,8 +11,6 @@ import { ListboxButton, ListboxOptions, ListboxOption, - Button, - Input, } from '@headlessui/react'; import Link from 'next/link'; import { useTheme } from '@/contexts/ThemeContext'; @@ -55,8 +53,10 @@ import { SidebarNav, SidebarNavItem, SegmentedControl, + Button, + IconButton, + Input, inputClass, - buttonClass, listboxButtonClass, listboxOptionClass, listboxOptionsClass, @@ -142,8 +142,10 @@ export function SettingsTrigger({ }) { return ( @@ -1140,14 +1149,16 @@ export function SettingsModal({ @@ -1226,7 +1237,8 @@ export function SettingsModal({ <> @@ -1236,7 +1248,8 @@ export function SettingsModal({ @@ -1259,19 +1272,19 @@ export function SettingsModal({

- {runtimeConfig.enableUserSignups && ( - )} - @@ -1418,16 +1431,15 @@ function SettingsChangelogPanel({ return (
- +

Changelog

diff --git a/src/components/admin/AdminFeaturesPanel.tsx b/src/components/admin/AdminFeaturesPanel.tsx index 4ca0fa4..cb41ab2 100644 --- a/src/components/admin/AdminFeaturesPanel.tsx +++ b/src/components/admin/AdminFeaturesPanel.tsx @@ -1,7 +1,7 @@ 'use client'; import { Fragment, useEffect, useMemo, useState } from 'react'; -import { Button, Listbox, ListboxButton, ListboxOption, ListboxOptions, Transition } from '@headlessui/react'; +import { Listbox, ListboxButton, ListboxOption, ListboxOptions, Transition } from '@headlessui/react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import toast from 'react-hot-toast'; import { ChevronUpDownIcon, CheckIcon } from '@/components/icons/Icons'; @@ -13,7 +13,7 @@ import { listboxButtonClass, listboxOptionClass, listboxOptionsClass, - buttonClass, + Button, } from '@/components/ui'; import { type TtsProviderId } from '@/lib/shared/tts-provider-catalog'; import { useSharedProviders, type SharedProviderEntry } from '@/hooks/useSharedProviders'; @@ -581,14 +581,16 @@ export function AdminFeaturesPanel() { diff --git a/src/components/admin/AdminProvidersPanel.tsx b/src/components/admin/AdminProvidersPanel.tsx index 09c07da..dec300d 100644 --- a/src/components/admin/AdminProvidersPanel.tsx +++ b/src/components/admin/AdminProvidersPanel.tsx @@ -1,7 +1,7 @@ 'use client'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { Button, Input, Listbox, ListboxButton, ListboxOption, ListboxOptions, Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'; +import { Listbox, ListboxButton, ListboxOption, ListboxOptions, Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'; import { Fragment } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import toast from 'react-hot-toast'; @@ -17,7 +17,8 @@ import { listboxButtonClass, listboxOptionClass, listboxOptionsClass, - buttonClass, + Button, + Input, } from '@/components/ui'; type ProviderType = TtsProviderId; @@ -364,7 +365,9 @@ export function AdminProvidersPanel() { {!editingId ? ( diff --git a/src/components/doclist/CreateFolderDialog.tsx b/src/components/doclist/CreateFolderDialog.tsx index 9787811..2e161c3 100644 --- a/src/components/doclist/CreateFolderDialog.tsx +++ b/src/components/doclist/CreateFolderDialog.tsx @@ -1,6 +1,5 @@ -import { Fragment, KeyboardEvent } from 'react'; -import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react'; -import { dialogPanelStyles, Input } from '@/components/ui'; +import { KeyboardEvent } from 'react'; +import { Input, ModalFrame, ModalTitle } from '@/components/ui'; interface CreateFolderDialogProps { isOpen: boolean; @@ -18,52 +17,20 @@ export function CreateFolderDialog({ onClose, }: CreateFolderDialogProps) { return ( - -

- -
- - -
-
- - - - Create New Folder - -
- onFolderNameChange(e.target.value)} - onKeyDown={onKeyDown} - placeholder="Enter folder name" - controlSize="lg" - autoFocus - /> -

Press Enter to create or Escape to cancel

-
-
-
-
-
-
- + + Create New Folder +
+ onFolderNameChange(e.target.value)} + onKeyDown={onKeyDown} + placeholder="Enter folder name" + controlSize="lg" + autoFocus + /> +

Press Enter to create or Escape to cancel

+
+
); } diff --git a/src/components/layout/LibraryFrame.tsx b/src/components/layout/LibraryFrame.tsx index de1bad5..88c47b8 100644 --- a/src/components/layout/LibraryFrame.tsx +++ b/src/components/layout/LibraryFrame.tsx @@ -1,8 +1,8 @@ 'use client'; -import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react'; -import { Fragment, useEffect, useState, type ReactNode } from 'react'; +import { useEffect, useState, type ReactNode } from 'react'; import { cn } from '@/components/ui/cn'; +import { DrawerFrame } from '@/components/ui'; const NARROW_QUERY = '(max-width: 767px)'; @@ -65,35 +65,10 @@ function LibrarySidebarDrawer({ children: ReactNode; }) { return ( - - undefined)} className="relative z-40 md:hidden"> - -
- -
- - - {children} - - -
-
-
+
+ undefined)} side="left" className="w-[80vw] max-w-[280px]"> + {children} + +
); } diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 80fd833..6ba92f4 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -1,5 +1,9 @@ -import type { ReactNode } from 'react'; +'use client'; + +import { Fragment, type KeyboardEventHandler, type ReactNode } from 'react'; +import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react'; import { variants } from './variants'; +import { cn } from './cn'; export type DialogSize = 'sm' | 'md' | 'lg' | 'xl'; @@ -29,3 +33,124 @@ export function DialogShell({ }) { return
{children}
; } + +export function ModalFrame({ + open, + onClose, + children, + size = 'md', + className, + panelClassName, + panelTestId, + onKeyDown, +}: { + open: boolean; + onClose: () => void; + children: ReactNode; + size?: DialogSize; + className?: string; + panelClassName?: string; + panelTestId?: string; + onKeyDown?: KeyboardEventHandler; +}) { + return ( + + + +
+ + +
+
+ + + {children} + + +
+
+
+
+ ); +} + +export function ModalTitle({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) { + return ( + + {children} + + ); +} + +export function DrawerFrame({ + open, + onClose, + children, + side = 'left', + className, +}: { + open: boolean; + onClose: () => void; + children: ReactNode; + side?: 'left' | 'right'; + className?: string; +}) { + const sideClass = side === 'left' ? 'left-0' : 'right-0'; + const enterFrom = side === 'left' ? '-translate-x-full' : 'translate-x-full'; + const leaveTo = enterFrom; + + return ( + + + +
+ +
+ + + {children} + + +
+
+
+ ); +}