Skip to content

Commit

Permalink
add dynamic settings modal
Browse files Browse the repository at this point in the history
  • Loading branch information
tytremblay committed Dec 13, 2024
1 parent f3965c3 commit 7b58842
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 87 deletions.
105 changes: 87 additions & 18 deletions src/components/Sections/ConfigSection/ConfigSection.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import { ConfigEditor } from '@/components/ConfigEditor';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import {
Drawer,
DrawerContent,
Expand All @@ -6,33 +16,92 @@ import {
DrawerTitle,
DrawerTrigger,
} from '@/components/ui/drawer';
import { useMediaQuery } from '@/hooks';
import { setConfig } from '@/store/store';
import { Transition } from '@headlessui/react';
import { Cog6ToothIcon } from '@heroicons/react/20/solid';
import { useMemo, useState } from 'preact/hooks';
import { Section } from '../../core/Section';
import { Settings } from './Settings';
import { ThemeSelector } from './ThemeSelector';

export function ConfigSection() {
return (
<Section>
<div className="flex flex-col items-center justify-center pt-4">
<Drawer>
<DrawerTrigger>
<div className="flex gap-2">
const [open, setOpen] = useState(false);
const [showEditor, setShowEditor] = useState(false);
const isDesktop = useMediaQuery('(min-width: 768px)');

const handleShowEditor = (show: boolean) => {
if (show) {
setOpen(false);
}
setShowEditor(show);
};

const dialogOrDrawer = useMemo(() => {
if (isDesktop) {
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline">
<Cog6ToothIcon className="h-5 w-5" />
Settings
</div>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Settings</DrawerTitle>
</DrawerHeader>
<Settings />
<DrawerFooter>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Settings</DialogTitle>
</DialogHeader>
<Settings setShowEditor={handleShowEditor} />
<DialogFooter>
<ThemeSelector />
</DrawerFooter>
</DrawerContent>
</Drawer>
</div>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
return (
<Drawer open={open} onOpenChange={setOpen}>
<DrawerTrigger asChild>
<Button variant="outline">
<Cog6ToothIcon className="h-5 w-5" />
Settings
</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Settings</DrawerTitle>
</DrawerHeader>
<Settings setShowEditor={handleShowEditor} />
<DrawerFooter>
<ThemeSelector />
</DrawerFooter>
</DrawerContent>
</Drawer>
);
}, [isDesktop, open, setShowEditor]);

return (
<Section>
{dialogOrDrawer}

<Transition
show={showEditor}
enter="transition ease-out duration-300 transform"
enterFrom="translate-y-full"
enterTo="translate-y-0"
leave="transition ease-in duration-300 transform"
leaveFrom="translate-y-0"
leaveTo="translate-y-full"
className="z-50 fixed inset-0"
>
<ConfigEditor
onCancel={() => setShowEditor(false)}
onSave={configString => {
setConfig(configString);
setShowEditor(false);
}}
/>
</Transition>
</Section>
);
}
95 changes: 37 additions & 58 deletions src/components/Sections/ConfigSection/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { Button } from '@/components/ui/button';
import { Transition } from '@headlessui/react';
import { useState } from 'preact/hooks';
import {
resetToDefaultConfig,
setConfig,
uploadConfig,
useQRScoutState,
} from '../../../store/store';
import { ConfigEditor } from '../../ConfigEditor';
import { Config } from '../../inputs/BaseInputProps';

/**
Expand Down Expand Up @@ -44,63 +40,46 @@ function downloadConfig(formData: Config) {
download('QRScout_config.json', JSON.stringify(configDownload));
}

export function Settings() {
type SettingsProps = {
setShowEditor: (showEditor: boolean) => void;
};

export function Settings({ setShowEditor }: SettingsProps) {
const formData = useQRScoutState(state => state.formData);
const [showEditor, setShowEditor] = useState(false);
return (
<>
<div className="flex flex-col gap-4 px-6">
<Button
variant="secondary"
onClick={() =>
navigator.clipboard.writeText(
formData.sections
.map(s => s.fields)
.flat()
.map(f => f.title)
.join('\t'),
)
}
>
Copy Column Names
</Button>
<Button variant="secondary" onClick={() => setShowEditor(true)}>
Edit Config
</Button>
<Button variant="secondary" onClick={() => downloadConfig(formData)}>
Download Config
</Button>
<label className="mx-2 flex cursor-pointer flex-row justify-center rounded bg-gray-500 py-2 text-center font-bold text-white shadow-sm hover:bg-gray-600">
<span className="text-base leading-normal">Upload Config</span>
<input
type="file"
className="hidden"
accept=".json"
onChange={e => uploadConfig(e)}
/>
</label>
<Button variant="destructive" onClick={() => resetToDefaultConfig()}>
Reset Config to Default
</Button>
</div>
<Transition
show={showEditor}
enter="transition ease-out duration-300 transform"
enterFrom="translate-y-full"
enterTo="translate-y-0"
leave="transition ease-in duration-300 transform"
leaveFrom="translate-y-0"
leaveTo="translate-y-full"
className="z-50 fixed inset-0"
<div className="flex flex-col gap-4 px-6">
<Button
variant="secondary"
onClick={() =>
navigator.clipboard.writeText(
formData.sections
.map(s => s.fields)
.flat()
.map(f => f.title)
.join('\t'),
)
}
>
<ConfigEditor
onCancel={() => setShowEditor(false)}
onSave={configString => {
setConfig(configString);
setShowEditor(false);
}}
Copy Column Names
</Button>
<Button variant="secondary" onClick={() => setShowEditor(true)}>
Edit Config
</Button>
<Button variant="secondary" onClick={() => downloadConfig(formData)}>
Download Config
</Button>
<label className="mx-2 flex cursor-pointer flex-row justify-center rounded bg-gray-500 py-2 text-center font-bold text-white shadow-sm hover:bg-gray-600">
<span className="text-base leading-normal">Upload Config</span>
<input
type="file"
className="hidden"
accept=".json"
onChange={e => uploadConfig(e)}
/>
</Transition>
</>
</label>
<Button variant="destructive" onClick={() => resetToDefaultConfig()}>
Reset Config to Default
</Button>
</div>
);
}
25 changes: 14 additions & 11 deletions src/components/Sections/ConfigSection/ThemeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ export function ThemeSelector() {
const { theme, setTheme, resolvedTheme } = useTheme();

return (
<ToggleGroup type="single" value={theme} onValueChange={setTheme}>
<ToggleGroupItem value="light" aria-label="light theme">
<Sun className="h-4 w-4" />
</ToggleGroupItem>
<ToggleGroupItem value="dark" aria-label="dark theme">
<Moon className="h-4 w-4" />
</ToggleGroupItem>
<ToggleGroupItem value="system" aria-label="system theme">
<Computer className="h-4 w-4" />
</ToggleGroupItem>
</ToggleGroup>
<div className="flex items-center space-x-2">
<span>Theme:</span>
<ToggleGroup type="single" value={theme} onValueChange={setTheme}>
<ToggleGroupItem value="light" aria-label="light theme">
<Sun className="h-4 w-4" />
</ToggleGroupItem>
<ToggleGroupItem value="dark" aria-label="dark theme">
<Moon className="h-4 w-4" />
</ToggleGroupItem>
<ToggleGroupItem value="system" aria-label="system theme">
<Computer className="h-4 w-4" />
</ToggleGroupItem>
</ToggleGroup>
</div>
);
}
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './useMediaQuery';
export * from './useOnClickOutside';
17 changes: 17 additions & 0 deletions src/hooks/useMediaQuery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useEffect, useState } from 'react';

export function useMediaQuery(query: string) {
const [matches, setMatches] = useState(false);

useEffect(() => {
const media = window.matchMedia(query);
if (media.matches !== matches) {
setMatches(media.matches);
}
const listener = () => setMatches(media.matches);
window.addEventListener('resize', listener);
return () => window.removeEventListener('resize', listener);
}, [matches, query]);

return matches;
}

0 comments on commit 7b58842

Please sign in to comment.