(defaultState);
diff --git a/src/index.tsx b/src/index.tsx
index a5b5f71..fb7929a 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -22,10 +22,12 @@
import {Auth0Provider} from "@auth0/auth0-react";
import {QAuthenticationMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAuthenticationMetaData";
import React from "react";
-import {render} from "react-dom";
+import {createRoot} from "react-dom/client";
import {BrowserRouter, useNavigate, useSearchParams} from "react-router-dom";
import App from "App";
import "qqq/styles/qqq-override-styles.css";
+import "qqq/styles/globals.scss";
+import "qqq/styles/raycast.scss";
import HandleAuthorizationError from "HandleAuthorizationError";
import ProtectedRoute from "qqq/authorization/auth0/ProtectedRoute";
import {MaterialUIControllerProvider} from "qqq/context";
@@ -73,6 +75,9 @@ authenticationMetaDataPromise.then((authenticationMetaData) =>
}
}
+ const container = document.getElementById("root");
+ const root = createRoot(container);
+
if (authenticationMetaData.type === "AUTH_0")
{
// @ts-ignore
@@ -86,9 +91,8 @@ authenticationMetaDataPromise.then((authenticationMetaData) =>
if(!domain || !clientId)
{
- render(
- Error: AUTH0 authenticationMetaData is missing domain [{domain}] and/or clientId [{clientId}].
,
- document.getElementById("root"),
+ root.render(
+ Error: AUTH0 authenticationMetaData is missing domain [{domain}] and/or clientId [{clientId}].
);
return;
}
@@ -101,7 +105,7 @@ authenticationMetaDataPromise.then((authenticationMetaData) =>
domain = domain.replace(/\/$/, "");
}
- render(
+ root.render(
- ,
- document.getElementById("root"),
+
);
}
else
{
- render(
+ root.render(
- , document.getElementById("root"));
+ );
}
})
diff --git a/src/qqq/components/forms/DynamicFormField.tsx b/src/qqq/components/forms/DynamicFormField.tsx
index e2fb6f8..57a71f5 100644
--- a/src/qqq/components/forms/DynamicFormField.tsx
+++ b/src/qqq/components/forms/DynamicFormField.tsx
@@ -23,8 +23,9 @@ import {InputAdornment, InputLabel} from "@mui/material";
import Box from "@mui/material/Box";
import Switch from "@mui/material/Switch";
import {ErrorMessage, Field, useFormikContext} from "formik";
-import React, {useState} from "react";
+import React, {useContext, useState} from "react";
import AceEditor from "react-ace";
+import QContext from "QContext";
import BooleanFieldSwitch from "qqq/components/forms/BooleanFieldSwitch";
import MDInput from "qqq/components/legacy/MDInput";
import MDTypography from "qqq/components/legacy/MDTypography";
@@ -52,6 +53,7 @@ function QDynamicFormField({
{
const [switchChecked, setSwitchChecked] = useState(false);
const [isDisabled, setIsDisabled] = useState(!isEditable || bulkEditMode);
+ const {setAllowShortcuts} = useContext(QContext);
const {setFieldValue} = useFormikContext();
@@ -125,6 +127,14 @@ function QDynamicFormField({
field = (
<>
+ {
+ setAllowShortcuts(true);
+ }}
+ onFocus={(e: any) =>
+ {
+ setAllowShortcuts(false);
+ }}
onKeyPress={(e: any) =>
{
if (e.key === "Enter")
diff --git a/src/qqq/components/horseshoe/NavBar.tsx b/src/qqq/components/horseshoe/NavBar.tsx
index 6d77082..51963ba 100644
--- a/src/qqq/components/horseshoe/NavBar.tsx
+++ b/src/qqq/components/horseshoe/NavBar.tsx
@@ -30,8 +30,9 @@ import ListItemIcon from "@mui/material/ListItemIcon";
import Menu from "@mui/material/Menu";
import TextField from "@mui/material/TextField";
import Toolbar from "@mui/material/Toolbar";
-import React, {useEffect, useState} from "react";
+import React, {useContext, useEffect, useState} from "react";
import {useLocation, useNavigate} from "react-router-dom";
+import QContext from "QContext";
import QBreadcrumbs, {routeToLabel} from "qqq/components/horseshoe/Breadcrumbs";
import {navbar, navbarContainer, navbarIconButton, navbarRow,} from "qqq/components/horseshoe/Styles";
import {setTransparentNavbar, useMaterialUIController,} from "qqq/context";
@@ -62,6 +63,7 @@ function NavBar({absolute, light, isMini}: Props): JSX.Element
const [autocompleteValue, setAutocompleteValue] = useState(null);
const route = useLocation().pathname.split("/").slice(1);
const navigate = useNavigate();
+ const {setAllowShortcuts} = useContext(QContext);
useEffect(() =>
{
@@ -93,7 +95,6 @@ function NavBar({absolute, light, isMini}: Props): JSX.Element
buildHistoryEntries();
const history = HistoryUtils.get();
- setHistory([ {label: "The Godfather", id: 1}, {label: "Pulp Fiction", id: 2}]);
const options = [] as any;
history.entries.reverse().forEach((entry, index) =>
options.push({label: `${entry.label} index`, id: index, key: index, path: entry.path, iconName: entry.iconName})
@@ -119,6 +120,17 @@ function NavBar({absolute, light, isMini}: Props): JSX.Element
setHistory(options);
}
+ function handleHistoryOnOpen()
+ {
+ setAllowShortcuts(false);
+ buildHistoryEntries();
+ }
+
+ function handleHistoryOnClose()
+ {
+ setAllowShortcuts(true);
+ }
+
const handleOpenMenu = (event: any) => setOpenMenu(event.currentTarget);
const handleCloseMenu = () => setOpenMenu(false);
@@ -152,7 +164,8 @@ function NavBar({absolute, light, isMini}: Props): JSX.Element
autoHighlight
blurOnSelect
style={{width: "200px"}}
- onOpen={buildHistoryEntries}
+ onOpen={handleHistoryOnOpen}
+ onClose={handleHistoryOnClose}
onChange={handleAutocompleteOnChange}
PopperComponent={CustomPopper}
isOptionEqualToValue={(option, value) => option.id === value.id}
diff --git a/src/qqq/pages/records/view/RecordView.tsx b/src/qqq/pages/records/view/RecordView.tsx
index 3b7b490..33e1c3e 100644
--- a/src/qqq/pages/records/view/RecordView.tsx
+++ b/src/qqq/pages/records/view/RecordView.tsx
@@ -26,8 +26,9 @@ import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
-import {Alert, Box, Typography} from "@mui/material";
+import {Alert, Typography} from "@mui/material";
import Avatar from "@mui/material/Avatar";
+import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Card from "@mui/material/Card";
import Dialog from "@mui/material/Dialog";
@@ -79,7 +80,6 @@ function RecordView({table, launchProcess}: Props): JSX.Element
const location = useLocation();
const navigate = useNavigate();
- const {accentColor} = useContext(QContext);
const pathParts = location.pathname.replace(/\/+$/, "").split("/");
const tableName = table.name;
@@ -102,7 +102,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
const [errorMessage, setErrorMessage] = useState(null as string)
const [successMessage, setSuccessMessage] = useState(null as string);
const [warningMessage, setWarningMessage] = useState(null as string);
- const {setPageHeader} = useContext(QContext);
+ const {accentColor, setPageHeader, allowShortcuts} = useContext(QContext);
const [activeModalProcess, setActiveModalProcess] = useState(null as QProcessMetaData);
const [reloadCounter, setReloadCounter] = useState(0);
@@ -113,6 +113,8 @@ function RecordView({table, launchProcess}: Props): JSX.Element
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
const closeActionsMenu = () => setActionsMenu(null);
+
+
const reload = () =>
{
setSuccessMessage(null);
@@ -128,6 +130,47 @@ function RecordView({table, launchProcess}: Props): JSX.Element
setShowAudit(false);
};
+ // Toggle the menu when ⌘K is pressed
+ useEffect(() =>
+ {
+ const down = (e: { key: string; metaKey: any; ctrlKey: any; preventDefault: () => void; }) =>
+ {
+ if(allowShortcuts)
+ {
+ if (e.key === "e" && table.capabilities.has(Capability.TABLE_UPDATE) && table.editPermission)
+ {
+ e.preventDefault()
+ navigate("edit");
+ }
+ else if (e.key === "c" && table.capabilities.has(Capability.TABLE_INSERT) && table.insertPermission)
+ {
+ e.preventDefault()
+ gotoCreate();
+ }
+ else if (e.key === "d" && table.capabilities.has(Capability.TABLE_DELETE) && table.deletePermission)
+ {
+ e.preventDefault()
+ handleClickDeleteButton();
+ }
+ }
+ }
+
+ document.addEventListener("keydown", down)
+ return () => document.removeEventListener("keydown", down)
+ }, [allowShortcuts])
+
+ const gotoCreate = () =>
+ {
+ const path = `${pathParts.slice(0, -1).join("/")}/create`;
+ navigate(path);
+ }
+
+ const gotoEdit = () =>
+ {
+ const path = `${pathParts.slice(0, -1).join("/")}/${record.values.get(table.primaryKeyField)}/edit`;
+ navigate(path);
+ }
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// monitor location changes - if we've clicked a link from viewing one record to viewing another, //
// we'll stay in this component, but we'll need to reload all data for the new record. //
@@ -506,12 +549,6 @@ function RecordView({table, launchProcess}: Props): JSX.Element
let hasEditOrDelete = (table.capabilities.has(Capability.TABLE_UPDATE) && table.editPermission) || (table.capabilities.has(Capability.TABLE_DELETE) && table.deletePermission);
- function gotoCreate()
- {
- const path = `${pathParts.slice(0, -1).join("/")}/create`;
- navigate(path);
- }
-
const renderActionsMenu = (