Skip to content

Commit

Permalink
Merge pull request #36 from FRC2713/inlineEdit
Browse files Browse the repository at this point in the history
Add in-app editor.
  • Loading branch information
tytremblay authored Mar 7, 2024
2 parents c089965 + 33c1dfd commit 703fc62
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 45 deletions.
1 change: 0 additions & 1 deletion config/2024/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"$schema": "../schema.json",
"title": "QRScout",
"page_title": "Crescendo",
"sections": [
Expand Down
94 changes: 92 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
"preview": "vite preview"
},
"dependencies": {
"@headlessui/react": "^1.7.18",
"@heroicons/react": "^2.1.1",
"@monaco-editor/react": "^4.6.0",
"immer": "^10.0.3",
"lodash": "^4.17.21",
"next-themes": "^0.2.1",
"preact": "^10.19.3",
"qrcode.react": "^3.1.0",
Expand All @@ -19,6 +22,7 @@
"devDependencies": {
"@preact/preset-vite": "^2.7.0",
"@tailwindcss/forms": "^0.5.7",
"@types/lodash": "^4.14.202",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.33",
"tailwindcss": "^3.4.0",
Expand Down
60 changes: 60 additions & 0 deletions src/components/ConfigEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Editor, { useMonaco } from '@monaco-editor/react';
import { useEffect, useMemo, useState } from 'preact/hooks';
import schema from '../../config/schema.json';
import { getConfig, useQRScoutState } from '../store/store';
import Button, { Variant } from './core/Button';

type ConfigEditorProps = {
onCancel?: () => void;
onSave?: (config: string) => void;
};

export function ConfigEditor(props: ConfigEditorProps) {
const monaco = useMonaco();
const formData = useQRScoutState(state => state.formData);
const config = useMemo(() => getConfig(), [formData]);
const [currentConfigText, setCurrentConfigText] = useState<string>('');
const [errorCount, setErrorCount] = useState<number>(0);

useEffect(() => {
monaco?.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [
{
uri: '../../config/schema.json',
fileMatch: ['*'],
schema,
},
],
});
}, [monaco]);
return (
<div className="h-screen w-screen bg-gray-500 bg-opacity-50 dark:bg-opacity-70 backdrop-blur p-4 ">
<div className="flex flex-col gap-2 h-full shadow-md p-2 rounded-lg bg-gray-50 bg-opacity-20">
<div className="flex-grow rounded-lg overflow-clip ">
<Editor
defaultLanguage="json"
defaultValue={JSON.stringify(config, null, 2)}
theme="vs-dark"
onValidate={markers => {
setErrorCount(markers.length);
}}
onChange={value => value && setCurrentConfigText(value)}
/>
</div>
<div className="flex flex-grow-0 justify-end gap-2">
<Button
variant={Variant.Danger}
onClick={() => props.onSave && props.onSave(currentConfigText)}
disabled={currentConfigText.length === 0 || errorCount > 0}
>
Save
</Button>
<Button variant={Variant.Primary} onClick={props.onCancel}>
Cancel
</Button>
</div>
</div>
</div>
);
}
108 changes: 71 additions & 37 deletions src/components/Sections/ConfigSection/SettingsModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { Transition } from '@headlessui/react';
import { useState } from 'preact/hooks';
import {
resetToDefaultConfig,
setConfig,
uploadConfig,
useQRScoutState,
} from '../../../store/store';
import { ConfigEditor } from '../../ConfigEditor';
import Button, { Variant } from '../../core/Button';
import { Modal } from '../../core/Modal';
import { Config } from '../../inputs/BaseInputProps';
Expand Down Expand Up @@ -49,44 +53,74 @@ function downloadConfig(formData: Config) {

export function SettingsModal(props: ModalProps) {
const formData = useQRScoutState(state => state.formData);
const [showEditor, setShowEditor] = useState(false);
return (
<Modal show={props.show} onDismiss={props.onDismiss}>
<div className="flex flex-col justify-start rounded bg-white dark:bg-gray-600 gap-2 p-2">
<Button
variant={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={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>
<>
<Modal show={props.show} onDismiss={props.onDismiss}>
<div className="flex flex-col justify-start rounded bg-white dark:bg-gray-600 gap-2 p-2">
<Button
variant={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={Variant.Secondary}
onClick={() => setShowEditor(true)}
>
Edit Config
</Button>
<Button
variant={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>

<ThemeSelector />
<Button variant={Variant.Danger} onClick={() => resetToDefaultConfig()}>
Reset Config to Default
</Button>
</div>
</Modal>
<ThemeSelector />
<Button
variant={Variant.Danger}
onClick={() => resetToDefaultConfig()}
>
Reset Config to Default
</Button>
</div>
</Modal>
<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>
</>
);
}
2 changes: 1 addition & 1 deletion src/components/core/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function Modal(props: ModalProps) {
{props.show && (
<>
<div
className="fixed inset-0 h-full w-full overflow-y-auto bg-gray-500 bg-opacity-50 dark:bg-opacity-70 backdrop-blur "
className="fixed inset-0 h-full w-full overflow-y-auto bg-gray-500 bg-opacity-40 dark:bg-opacity-70 backdrop-blur "
id="my-modal"
/>
<div
Expand Down
2 changes: 0 additions & 2 deletions src/components/inputs/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ export default function NumberInput(data: NumberInputProps) {
data.onChange(e.currentTarget.value);
}

console.log(data);

return (
<input
className="w-full rounded py-2 dark:bg-gray-700 dark:text-white"
Expand Down
Loading

0 comments on commit 703fc62

Please sign in to comment.