diff --git a/package-lock.json b/package-lock.json
index cf35997..6de1ac6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,13 +1,14 @@
{
- "name": "qrscout-preact",
- "version": "0.0.0",
+ "name": "qrscout",
+ "version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "qrscout-preact",
- "version": "0.0.0",
+ "name": "qrscout",
+ "version": "2.0.0",
"dependencies": {
+ "@heroicons/react": "^2.1.1",
"immer": "^10.0.3",
"next-themes": "^0.2.1",
"preact": "^10.19.3",
@@ -775,6 +776,14 @@
"node": ">=12"
}
},
+ "node_modules/@heroicons/react": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.1.tgz",
+ "integrity": "sha512-JyyN9Lo66kirbCMuMMRPtJxtKJoIsXKS569ebHGGRKbl8s4CtUfLnyKJxteA+vIKySocO4s1SkTkGS4xtG/yEA==",
+ "peerDependencies": {
+ "react": ">= 16"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -2523,9 +2532,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.33",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
- "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
+ "version": "8.4.35",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
+ "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
"dev": true,
"funding": [
{
@@ -3212,13 +3221,13 @@
"dev": true
},
"node_modules/vite": {
- "version": "5.0.10",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz",
- "integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==",
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.5.tgz",
+ "integrity": "sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==",
"dev": true,
"dependencies": {
"esbuild": "^0.19.3",
- "postcss": "^8.4.32",
+ "postcss": "^8.4.35",
"rollup": "^4.2.0"
},
"bin": {
diff --git a/package.json b/package.json
index 8ddf926..8c13c01 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
+ "@heroicons/react": "^2.1.1",
"immer": "^10.0.3",
"next-themes": "^0.2.1",
"preact": "^10.19.3",
diff --git a/src/components/QR/QRModal.tsx b/src/components/QR/QRModal.tsx
index 3a925a5..4e831ee 100644
--- a/src/components/QR/QRModal.tsx
+++ b/src/components/QR/QRModal.tsx
@@ -1,9 +1,8 @@
-import { useMemo, useRef } from 'preact/hooks';
+import { useMemo } from 'preact/hooks';
import QRCode from 'qrcode.react';
-import { useOnClickOutside } from '../../hooks/useOnClickOutside';
import { getFieldValue, useQRScoutState } from '../../store/store';
+import { Modal } from '../core/Modal';
import { Config } from '../inputs/BaseInputProps';
-import { CloseButton } from './CloseButton';
import { PreviewText } from './PreviewText';
export interface QRModalProps {
@@ -20,9 +19,7 @@ export function getQRCodeData(formData: Config): string {
}
export function QRModal(props: QRModalProps) {
- const modalRef = useRef(null);
const formData = useQRScoutState(state => state.formData);
- useOnClickOutside(modalRef, props.onDismiss);
const title = `${getFieldValue('robot')} - M${getFieldValue(
'matchNumber',
@@ -30,26 +27,12 @@ export function QRModal(props: QRModalProps) {
const qrCodeData = useMemo(() => getQRCodeData(formData), [formData]);
return (
- <>
- {props.show && (
- <>
-
-
- >
- )}
- >
+
+
+
);
}
diff --git a/src/components/Sections/CommitAndResetSection/CommitAndResetSection.tsx b/src/components/Sections/CommitAndResetSection/CommitAndResetSection.tsx
index d7cd8bf..aa3670b 100644
--- a/src/components/Sections/CommitAndResetSection/CommitAndResetSection.tsx
+++ b/src/components/Sections/CommitAndResetSection/CommitAndResetSection.tsx
@@ -1,5 +1,6 @@
import { useMemo } from 'preact/hooks';
import { useQRScoutState } from '../../../store/store';
+import { Section } from '../../core/Section';
import { CommitButton } from './CommitButton';
import { ResetButton } from './ResetButton';
@@ -23,12 +24,12 @@ export function CommitAndResetSection({
}, [formData]);
return (
-
+
0}
onClick={onCommit}
/>
-
+
);
}
diff --git a/src/components/Sections/ConfigSection/ConfigSection.tsx b/src/components/Sections/ConfigSection/ConfigSection.tsx
index 506f65f..88f4659 100644
--- a/src/components/Sections/ConfigSection/ConfigSection.tsx
+++ b/src/components/Sections/ConfigSection/ConfigSection.tsx
@@ -1,84 +1,24 @@
-import {
- resetToDefaultConfig,
- uploadConfig,
- useQRScoutState,
-} from '../../../store/store';
+import { Cog6ToothIcon } from '@heroicons/react/20/solid';
+import { useState } from 'preact/hooks';
import Button, { Variant } from '../../core/Button';
-import { Config } from '../../inputs/BaseInputProps';
-import { ThemeSelector } from './ThemeSelector';
-
-/**
- * Download a text file
- * @param filename The name of the file
- * @param text The text to put in the file
- */
-function download(filename: string, text: string) {
- var element = document.createElement('a');
- element.setAttribute(
- 'href',
- 'data:text/plain;charset=utf-8,' + encodeURIComponent(text),
- );
- element.setAttribute('download', filename);
-
- element.style.display = 'none';
- document.body.appendChild(element);
-
- element.click();
-
- document.body.removeChild(element);
-}
-
-/**
- * Download the current form data as a json file
- * @param formData The form data to download
- */
-function downloadConfig(formData: Config) {
- const configDownload = { ...formData };
-
- configDownload.sections.forEach(s =>
- s.fields.forEach(f => (f.value = undefined)),
- );
- download('QRScout_config.json', JSON.stringify(configDownload));
-}
+import { Section } from '../../core/Section';
+import { SettingsModal } from './SettingsModal';
export function ConfigSection() {
- const formData = useQRScoutState(state => state.formData);
- return (
-
-
-
-
-
+ const [showModal, setShowModal] = useState(false);
-
-
+ return (
+
+
+ setShowModal(false)} />
+ }
+ variant={Variant.Transparent}
+ onClick={() => setShowModal(true)}
+ >
+ Settings
+
+
+
);
}
diff --git a/src/components/Sections/ConfigSection/SettingsModal.tsx b/src/components/Sections/ConfigSection/SettingsModal.tsx
new file mode 100644
index 0000000..5861e7f
--- /dev/null
+++ b/src/components/Sections/ConfigSection/SettingsModal.tsx
@@ -0,0 +1,92 @@
+import {
+ resetToDefaultConfig,
+ uploadConfig,
+ useQRScoutState,
+} from '../../../store/store';
+import Button, { Variant } from '../../core/Button';
+import { Modal } from '../../core/Modal';
+import { Config } from '../../inputs/BaseInputProps';
+import { ThemeSelector } from './ThemeSelector';
+
+export interface ModalProps {
+ show: boolean;
+ onDismiss?: () => void;
+}
+
+/**
+ * Download a text file
+ * @param filename The name of the file
+ * @param text The text to put in the file
+ */
+function download(filename: string, text: string) {
+ var element = document.createElement('a');
+ element.setAttribute(
+ 'href',
+ 'data:text/plain;charset=utf-8,' + encodeURIComponent(text),
+ );
+ element.setAttribute('download', filename);
+
+ element.style.display = 'none';
+ document.body.appendChild(element);
+
+ element.click();
+
+ document.body.removeChild(element);
+}
+
+/**
+ * Download the current form data as a json file
+ * @param formData The form data to download
+ */
+function downloadConfig(formData: Config) {
+ const configDownload = { ...formData };
+
+ configDownload.sections.forEach(s =>
+ s.fields.forEach(f => (f.value = undefined)),
+ );
+ download('QRScout_config.json', JSON.stringify(configDownload));
+}
+
+export function SettingsModal(props: ModalProps) {
+ const formData = useQRScoutState(state => state.formData);
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/Sections/FormSection.tsx b/src/components/Sections/FormSection.tsx
index e8bc634..e2d8cd9 100644
--- a/src/components/Sections/FormSection.tsx
+++ b/src/components/Sections/FormSection.tsx
@@ -1,4 +1,5 @@
import { useQRScoutState } from '../../store/store';
+import { Section } from '../core/Section';
import { InputProps } from '../inputs/BaseInputProps';
import ConfigurableInput from '../inputs/ConfigurableInput';
import InputCard from '../inputs/InputCard';
@@ -11,29 +12,17 @@ export default function FormSection(props: SectionProps) {
const formData = useQRScoutState(state => state.formData);
const inputs = formData.sections.find(s => s.name === props.name)?.fields;
return (
-
-
-
- {props.name}
-
-
-
- {inputs?.map((e: InputProps) => (
-
-
-
- ))}
-
-
+
+ {inputs?.map((e: InputProps) => (
+
+
+
+ ))}
+
);
}
diff --git a/src/components/core/Button.tsx b/src/components/core/Button.tsx
index bc9764c..53b69fb 100644
--- a/src/components/core/Button.tsx
+++ b/src/components/core/Button.tsx
@@ -1,22 +1,26 @@
-import React from 'react'
-import { classNames } from '../../util/classNames'
+import React from 'react';
+import { classNames } from '../../util/classNames';
export interface ButtonProps
- extends React.HTMLAttributes {
- variant: Variant
+ extends Omit, 'icon'> {
+ variant: Variant;
+ icon?: React.ReactNode;
}
export enum Variant {
Primary,
Secondary,
Danger,
+ Transparent,
}
const VARIANT_MAPS: Record = {
[Variant.Primary]: 'bg-gray-700 hover:bg-gray-800 disabled:bg-gray-300',
[Variant.Secondary]: 'bg-gray-500 hover:bg-gray-600 disabled:bg-gray-300',
[Variant.Danger]: 'bg-red-500 hover:bg-red-700 disabled:bg-red-300',
-}
+ [Variant.Transparent]:
+ 'bg-transparent hover:bg-gray-200 hover:text-gray-600 disabled:bg-gray-300',
+};
export default function Button(props: ButtonProps) {
return (
@@ -24,12 +28,15 @@ export default function Button(props: ButtonProps) {
type="button"
onClick={props.onClick}
className={classNames(
- 'focus:shadow-outline mx-2 rounded py-2 px-4 font-bold text-white focus:outline-none',
- VARIANT_MAPS[props.variant]
+ 'focus:shadow-outline mx-2 rounded py-2 px-4 font-bold text-gray-800 dark:text-white focus:outline-none',
+ VARIANT_MAPS[props.variant],
)}
disabled={props.disabled}
>
- {props.children}
+
+ {props.icon && props.icon}
+ {props.children}
+
- )
+ );
}
diff --git a/src/components/QR/CloseButton.tsx b/src/components/core/CloseButton.tsx
similarity index 88%
rename from src/components/QR/CloseButton.tsx
rename to src/components/core/CloseButton.tsx
index 74fe3ff..46b3fd8 100644
--- a/src/components/QR/CloseButton.tsx
+++ b/src/components/core/CloseButton.tsx
@@ -1,11 +1,11 @@
export type CloseButtonProps = {
- onClick: () => void;
+ onClick?: () => void;
};
export function CloseButton(props: CloseButtonProps) {
return (