Merge branch 'data-layer-rewrite' of https://github.com/rowyio/rowy into data-layer-rewrite

This commit is contained in:
shamsmosowi
2022-06-09 13:28:36 +10:00
31 changed files with 328 additions and 266 deletions

View File

@@ -0,0 +1,6 @@
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { SvgIconProps } from "@mui/material/SvgIcon";
export function ChevronDown(props: SvgIconProps) {
return <ExpandMoreIcon fontSize="small" {...props} />;
}

View File

@@ -0,0 +1,6 @@
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import { SvgIconProps } from "@mui/material/SvgIcon";
export function ChevronRight(props: SvgIconProps) {
return <ChevronRightIcon fontSize="small" {...props} />;
}

View File

@@ -2,7 +2,7 @@ import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon";
export function Clear(props: SvgIconProps) {
return (
<SvgIcon {...props}>
<SvgIcon viewBox="-1 0 24 24" {...props}>
<path d="M20 6v12a2 2 0 0 1-2 2H7l-6-8 6-8h11a2 2 0 0 1 2 2Zm-2.001-.001h-10l-4.5 6 4.5 6h10v-12ZM9.12 7.71 7.71 9.12 10.59 12l-2.88 2.88 1.41 1.41L12 13.41l2.88 2.88 1.41-1.41L13.41 12l2.88-2.88-1.41-1.41L12 10.59" />
</SvgIcon>
);

View File

@@ -85,7 +85,12 @@ export { CarBrakeAlert as Critical };
import { LanguageMarkdownOutline } from "mdi-material-ui";
export { LanguageMarkdownOutline as Markdown };
import { TableRow } from "mdi-material-ui";
export { TableRow as Row };
export * from "./AddRow";
export * from "./ChevronDown";
export * from "./ChevronRight";
export * from "./Clear";
export * from "./ConnectTable";
export * from "./Copy";

View File

@@ -1,13 +1,6 @@
import { atom } from "jotai";
import { atomWithReducer, atomWithHash } from "jotai/utils";
import {
uniqBy,
sortBy,
findIndex,
cloneDeep,
unset,
orderBy,
} from "lodash-es";
import { uniqBy, findIndex, cloneDeep, unset, orderBy } from "lodash-es";
import {
TableSettings,
@@ -166,11 +159,8 @@ export const tableRowsLocalAtom = atomWithReducer(
export const tableRowsDbAtom = atom<TableRow[]>([]);
/** Combine tableRowsLocal and tableRowsDb */
export const tableRowsAtom = atom<TableRow[]>((get) =>
sortBy(
uniqBy(
[...get(tableRowsLocalAtom), ...get(tableRowsDbAtom)],
"_rowy_ref.path"
),
uniqBy(
[...get(tableRowsLocalAtom), ...get(tableRowsDbAtom)],
"_rowy_ref.path"
)
);

View File

@@ -111,3 +111,7 @@ export function InlineErrorFallback(props: IErrorFallbackProps) {
/>
);
}
export function NonFullScreenErrorFallback(props: IErrorFallbackProps) {
return <ErrorFallback {...props} fullScreen={false} />;
}

View File

@@ -18,6 +18,7 @@ import { WIKI_LINKS } from "@src/constants/externalLinks";
import useUpdateCheck from "@src/hooks/useUpdateCheck";
import { runRoutes } from "@src/constants/runRoutes";
import RegionSelect from "@src/components/Settings/RegionSelect";
export default function RowyRun({
settings,
updateSettings,
@@ -191,22 +192,17 @@ export default function RowyRun({
</Grid>
</Grid>
</div>
<div>
<Grid container spacing={1} alignItems="center" direction="row">
<Grid item>
<RegionSelect
label={"Cloud functions Region"}
value={settings.region}
onChange={(v) => updateSettings({ region: v || "" })}
fullWidth
/>
</Grid>
<Typography variant="caption">
Select the region where your Cloud Functions are deployed. All new
deployments of Rowy cloud functions will be deployed to this region.
</Typography>
</Grid>
</div>
<RegionSelect
label="Cloud Functions region"
value={settings.region}
onChange={(v) => updateSettings({ region: v || "" })}
fullWidth
TextFieldProps={{
helperText:
"All new deployments of Rowy Cloud Functions will be deployed to this region",
}}
/>
</>
);
}

View File

@@ -1,10 +1,12 @@
import { sortBy } from "lodash-es";
import MultiSelect from "@rowy/multiselect";
import { Grid } from "@mui/material";
import LeafIcon from "@src/assets/icons/Leaf";
import _sortBy from "lodash-es/sortBy";
import { CLOUD_RUN_REGIONS } from "@src/constants/regions";
const REGIONS = _sortBy(CLOUD_RUN_REGIONS, ["group", "value"]);
const REGIONS = sortBy(CLOUD_RUN_REGIONS, ["group", "value"]);
export interface ICloudRunRegionSelectProps {
value: string;
@@ -28,7 +30,7 @@ export default function CloudRunRegionSelect({
options={REGIONS}
clearable={false}
itemRenderer={(option: any) => (
<Grid container spacing={0} sx={{ my: 0.5, maxWidth: 300 }}>
<Grid container spacing={0} sx={{ my: 0.5 }}>
<Grid item xs>
{option.value}
</Grid>
@@ -54,7 +56,6 @@ export default function CloudRunRegionSelect({
{...({
AutocompleteProps: { groupBy: (option: any) => option.group },
} as any)}
TextFieldProps={{ style: { maxWidth: 364 }, ...props.TextFieldProps }}
/>
);
}

View File

@@ -1,4 +1,6 @@
import { useAtom } from "jotai";
import { ErrorBoundary } from "react-error-boundary";
import { NonFullScreenErrorFallback } from "@src/components/ErrorFallback";
import { Menu } from "@mui/material";
import MenuContents from "./MenuContents";
@@ -22,13 +24,11 @@ export default function ContextMenu() {
onClose={handleClose}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
transformOrigin={{ vertical: "top", horizontal: "left" }}
// sx={{
// "& .MuiMenu-paper": {
// backgroundColor: "background.default",
// },
// }}
sx={{ "& .MuiMenu-paper": { minWidth: 150 } }}
>
<MenuContents onClose={handleClose} />
<ErrorBoundary FallbackComponent={NonFullScreenErrorFallback}>
<MenuContents onClose={handleClose} />
</ErrorBoundary>
</Menu>
);
}

View File

@@ -1,3 +1,5 @@
import { useState } from "react";
import {
ListItemIcon,
ListItemSecondaryAction,
@@ -6,9 +8,9 @@ import {
MenuItemProps,
Typography,
Menu,
Divider,
} from "@mui/material";
import ArrowRightIcon from "@mui/icons-material/ArrowRight";
import { useState } from "react";
import { ChevronRight as ChevronRightIcon } from "@src/assets/icons";
export interface IContextMenuItem extends Partial<MenuItemProps> {
onClick?: () => void;
@@ -16,6 +18,7 @@ export interface IContextMenuItem extends Partial<MenuItemProps> {
label: string;
disabled?: boolean;
hotkeyLabel?: string;
divider?: boolean;
}
export interface IContextMenuItemProps extends IContextMenuItem {
@@ -29,29 +32,42 @@ export default function ContextMenuItem({
subItems,
...props
}: IContextMenuItemProps) {
const [subMenu, setSubMenu] = useState<EventTarget | null>(null);
const [subMenu, setSubMenu] = useState<HTMLElement | null>(null);
if (subItems && subItems.length > 0) {
return (
<>
<MenuItem onClick={(e) => setSubMenu(e.target)}>
{label}
<ListItemSecondaryAction style={{ pointerEvents: "none" }}>
<ArrowRightIcon style={{ display: "block" }} />
<MenuItem onClick={(e) => setSubMenu(e.currentTarget)}>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemText>{label}</ListItemText>
<ListItemSecondaryAction
sx={{
pointerEvents: "none",
position: "static",
transform: "none",
ml: 1,
mr: -1,
}}
>
<ChevronRightIcon color="action" style={{ display: "block" }} />
</ListItemSecondaryAction>
</MenuItem>
{subMenu && (
<Menu
anchorEl={subMenu as any}
id={`${label}-sub-menu`}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
transformOrigin={{ vertical: "top", horizontal: "left" }}
open
onClose={() => setSubMenu(null)}
sx={{ "& .MuiPaper-root": { mt: -0.5 } }}
>
{subItems?.map((itemProps) => (
<MenuItem {...itemProps}>
<Menu
anchorEl={subMenu}
id={`${label}-sub-menu`}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
transformOrigin={{ vertical: "top", horizontal: "left" }}
open={Boolean(subMenu)}
onClose={() => setSubMenu(null)}
sx={{ "& .MuiPaper-root": { mt: -0.5 } }}
PaperProps={{ elevation: 16 }}
>
{subItems.map((itemProps) =>
itemProps.divider ? (
<Divider variant="middle" />
) : (
<MenuItem key={itemProps.label} {...itemProps}>
<ListItemIcon>{itemProps.icon}</ListItemIcon>
<ListItemText>{itemProps.label}</ListItemText>
{itemProps.hotkeyLabel && (
@@ -60,12 +76,12 @@ export default function ContextMenuItem({
</Typography>
)}
</MenuItem>
))}
</Menu>
)}
)
)}
</Menu>
</>
);
} else
} else {
return (
<MenuItem {...props} onClick={onClick}>
<ListItemIcon>{icon}</ListItemIcon>
@@ -77,4 +93,5 @@ export default function ContextMenuItem({
)}
</MenuItem>
);
}
}

View File

@@ -1,18 +1,23 @@
import { Fragment } from "react";
import { useAtom, useSetAtom } from "jotai";
import { getFieldProp } from "@src/components/fields";
import { find } from "lodash-es";
import { Divider } from "@mui/material";
import {
Copy as CopyIcon,
CopyCells as DuplicateIcon,
Clear as ClearIcon,
Row as RowIcon,
} from "@src/assets/icons";
import DeleteIcon from "@mui/icons-material/DeleteOutlined";
import OpenIcon from "@mui/icons-material/OpenInNewOutlined";
import MenuItem from "./MenuItem";
import ContextMenuItem, { IContextMenuItem } from "./ContextMenuItem";
import {
globalScope,
projectIdAtom,
altPressAtom,
tableAddRowIdTypeAtom,
confirmDialogAtom,
@@ -27,7 +32,6 @@ import {
deleteRowAtom,
updateFieldAtom,
} from "@src/atoms/tableScope";
import { IContextMenuItem } from "./MenuItem";
import { FieldType } from "@src/constants/fields";
interface IMenuContentsProps {
@@ -35,6 +39,7 @@ interface IMenuContentsProps {
}
export default function MenuContents({ onClose }: IMenuContentsProps) {
const [projectId] = useAtom(projectIdAtom, globalScope);
const [altPress] = useAtom(altPressAtom, globalScope);
const [addRowIdType] = useAtom(tableAddRowIdTypeAtom, globalScope);
const confirm = useSetAtom(confirmDialogAtom, globalScope);
@@ -84,6 +89,7 @@ export default function MenuContents({ onClose }: IMenuContentsProps) {
label: altPress ? "Clear value" : "Clear value…",
color: "error",
icon: <ClearIcon />,
disabled: selectedColumn.editable === false,
onClick: altPress
? handleClearValue
: () => {
@@ -104,41 +110,79 @@ export default function MenuContents({ onClose }: IMenuContentsProps) {
const handleDelete = () => deleteRow(row._rowy_ref.path);
const rowActions = [
{
label: "Duplicate row",
icon: <DuplicateIcon />,
disabled: tableSettings.tableType === "collectionGroup",
onClick: () => {
addRow({
row,
setId: addRowIdType === "custom" ? "decrement" : addRowIdType,
});
onClose();
},
},
{
label: altPress ? "Delete row" : "Delete row…",
color: "error",
icon: <DeleteIcon />,
onClick: altPress
? handleDelete
: () => {
confirm({
title: "Delete row?",
body: (
<>
Row path:
<br />
<code style={{ userSelect: "all", wordBreak: "break-all" }}>
{row._rowy_ref.path}
</code>
</>
),
confirm: "Delete",
confirmColor: "error",
handleConfirm: handleDelete,
label: "Row",
icon: <RowIcon />,
subItems: [
{
label: "Copy ID",
icon: <CopyIcon />,
onClick: () => {
navigator.clipboard.writeText(row._rowy_ref.id);
onClose();
},
},
{
label: "Copy path",
icon: <CopyIcon />,
onClick: () => {
navigator.clipboard.writeText(row._rowy_ref.path);
onClose();
},
},
{
label: "Open in Firebase Console",
icon: <OpenIcon />,
onClick: () => {
window.open(
`https://console.firebase.google.com/project/${projectId}/firestore/data/~2F${row._rowy_ref.path.replace(
/\//g,
"~2F"
)}`
);
onClose();
},
},
{ label: "Divider", divider: true },
{
label: "Duplicate",
icon: <DuplicateIcon />,
disabled: tableSettings.tableType === "collectionGroup",
onClick: () => {
addRow({
row,
setId: addRowIdType === "custom" ? "decrement" : addRowIdType,
});
onClose();
},
},
{
label: altPress ? "Delete" : "Delete…",
color: "error",
icon: <DeleteIcon />,
onClick: altPress
? handleDelete
: () => {
confirm({
title: "Delete row?",
body: (
<>
Row path:
<br />
<code
style={{ userSelect: "all", wordBreak: "break-all" }}
>
{row._rowy_ref.path}
</code>
</>
),
confirm: "Delete",
confirmColor: "error",
handleConfirm: handleDelete,
});
onClose();
},
},
],
},
];
actionGroups.push(rowActions);
@@ -147,12 +191,15 @@ export default function MenuContents({ onClose }: IMenuContentsProps) {
return (
<>
{actionGroups.map((items, groupIndex) => (
<>
<Fragment key={groupIndex}>
{groupIndex > 0 && <Divider variant="middle" />}
{items.map((item, index: number) => (
<MenuItem key={`contextMenu-${groupIndex}-${index}`} {...item} />
<ContextMenuItem
key={`contextMenu-${groupIndex}-${index}`}
{...item}
/>
))}
</>
</Fragment>
))}
</>
);

View File

@@ -4,7 +4,7 @@ import { ColorPicker, toColor } from "react-color-palette";
import "react-color-palette/lib/css/styles.css";
import { ButtonBase, Box, Collapse } from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { ChevronDown } from "@src/assets/icons";
import { fieldSx, getFieldId } from "@src/components/SideDrawer/utils";
@@ -60,7 +60,7 @@ export default function Color({
<div style={{ flexGrow: 1 }}>{value?.hex ?? "Choose a color…"}</div>
<ArrowDropDownIcon
<ChevronDown
color="action"
sx={{
transition: (theme) => theme.transitions.create("transform"),

View File

@@ -2,7 +2,7 @@ import { forwardRef } from "react";
import { IPopoverInlineCellProps } from "@src/components/fields/types";
import { ButtonBase, Grid, Chip } from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { ChevronDown } from "@src/assets/icons";
import ChipList from "@src/components/Table/formatters/ChipList";
import { get } from "lodash-es";
@@ -38,13 +38,13 @@ export const ConnectService = forwardRef(function ConnectService(
</ChipList>
{!disabled && (
<ArrowDropDownIcon
<ChevronDown
className="row-hover-iconButton"
sx={{
flexShrink: 0,
mr: 0.5,
borderRadius: 1,
p: (32 - 24) / 2 / 8,
p: (32 - 20) / 2 / 8,
boxSizing: "content-box !important",
}}
/>

View File

@@ -2,7 +2,7 @@ import { forwardRef } from "react";
import { IPopoverInlineCellProps } from "@src/components/fields/types";
import { ButtonBase, Grid, Chip } from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { ChevronDown } from "@src/assets/icons";
import ChipList from "@src/components/Table/formatters/ChipList";
@@ -50,13 +50,13 @@ export const ConnectTable = forwardRef(function ConnectTable(
</ChipList>
{!disabled && (
<ArrowDropDownIcon
<ChevronDown
className="row-hover-iconButton"
sx={{
flexShrink: 0,
mr: 0.5,
borderRadius: 1,
p: (32 - 24) / 2 / 8,
p: (32 - 20) / 2 / 8,
boxSizing: "content-box !important",
}}
/>

View File

@@ -2,7 +2,7 @@ import { forwardRef } from "react";
import { IPopoverInlineCellProps } from "@src/components/fields/types";
import { ButtonBase, Grid, Chip } from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { ChevronDown } from "@src/assets/icons";
import ChipList from "@src/components/Table/formatters/ChipList";
import { get } from "lodash-es";
@@ -39,13 +39,13 @@ export const Connector = forwardRef(function Connector(
</ChipList>
{!disabled && (
<ArrowDropDownIcon
<ChevronDown
className="row-hover-iconButton"
sx={{
flexShrink: 0,
mr: 0.5,
borderRadius: 1,
p: (32 - 24) / 2 / 8,
p: (32 - 20) / 2 / 8,
boxSizing: "content-box !important",
}}
/>

View File

@@ -2,11 +2,11 @@ import { ISideDrawerFieldProps } from "@src/components/fields/types";
import DatePicker from "@mui/lab/DatePicker";
import { TextField } from "@mui/material";
import { ChevronDown } from "@src/assets/icons";
import { getFieldId } from "@src/components/SideDrawer/utils";
import { transformValue, sanitizeValue } from "./utils";
import { DATE_FORMAT } from "@src/constants/dates";
import { DateIcon } from ".";
export interface IDateProps extends ISideDrawerFieldProps {}
@@ -36,12 +36,6 @@ export default function Date_({
label=""
hiddenLabel
aria-label={column.name as string}
InputProps={{
...props.InputProps,
endAdornment: props.InputProps?.endAdornment || (
<DateIcon color="action" />
),
}}
sx={{
"& .MuiInputBase-input": { fontVariantNumeric: "tabular-nums" },
"& .MuiInputAdornment-root": { m: 0 },
@@ -59,8 +53,11 @@ export default function Date_({
inputFormat={format}
mask={format.replace(/[A-Za-z]/g, "_")}
clearable
OpenPickerButtonProps={{ size: "small" }}
components={{ OpenPickerIcon: DateIcon }}
OpenPickerButtonProps={{
size: "small",
sx: { width: 32, height: 32 },
}}
components={{ OpenPickerIcon: ChevronDown }}
disableOpenPicker={false}
disabled={disabled}
/>

View File

@@ -3,11 +3,11 @@ import { IHeavyCellProps } from "@src/components/fields/types";
import DatePicker from "@mui/lab/DatePicker";
import { TextField } from "@mui/material";
import { ChevronDown } from "@src/assets/icons";
import { transformValue, sanitizeValue } from "./utils";
import { DATE_FORMAT } from "@src/constants/dates";
import BasicCell from "./BasicCell";
import { DateIcon } from ".";
export default function Date_({
column,
@@ -43,20 +43,6 @@ export default function Date_({
label=""
hiddenLabel
aria-label={column.name as string}
InputProps={{
...props.InputProps,
endAdornment: props.InputProps?.endAdornment || (
<DateIcon
className="row-hover-iconButton"
sx={{
borderRadius: 1,
p: (32 - 24) / 2 / 8,
boxSizing: "content-box",
mr: 0.5,
}}
/>
),
}}
className="cell-collapse-padding"
sx={{
width: "100%",
@@ -107,9 +93,9 @@ export default function Date_({
size: "small",
className: "row-hover-iconButton",
edge: false,
sx: { mr: 0.5 },
sx: { mr: 3 / 8, width: 32, height: 32 },
}}
components={{ OpenPickerIcon: DateIcon }}
components={{ OpenPickerIcon: ChevronDown }}
disableOpenPicker={false}
/>
);

View File

@@ -3,13 +3,13 @@ import { setSeconds } from "date-fns";
import DateTimePicker from "@mui/lab/DateTimePicker";
import { TextField } from "@mui/material";
import { ChevronDown } from "@src/assets/icons";
import {
transformValue,
sanitizeValue,
} from "@src/components/fields/Date/utils";
import { DATE_TIME_FORMAT } from "@src/constants/dates";
import { DateTimeIcon } from ".";
import { getFieldId } from "@src/components/SideDrawer/utils";
export interface IDateProps extends ISideDrawerFieldProps {}
@@ -40,12 +40,6 @@ export default function Date_({
label=""
hiddenLabel
aria-label={column.name as string}
InputProps={{
...props.InputProps,
endAdornment: props.InputProps?.endAdornment || (
<DateTimeIcon color="action" />
),
}}
sx={{
"& .MuiInputBase-input": { fontVariantNumeric: "tabular-nums" },
"& .MuiInputAdornment-root": { m: 0 },
@@ -63,8 +57,11 @@ export default function Date_({
inputFormat={format}
mask={format.replace(/[A-Za-z]/g, "_")}
clearable
OpenPickerButtonProps={{ size: "small" }}
components={{ OpenPickerIcon: DateTimeIcon }}
OpenPickerButtonProps={{
size: "small",
sx: { width: 32, height: 32 },
}}
components={{ OpenPickerIcon: ChevronDown }}
disableOpenPicker={false}
disabled={disabled}
/>

View File

@@ -4,6 +4,7 @@ import { setSeconds } from "date-fns";
import DateTimePicker from "@mui/lab/DateTimePicker";
import { TextField } from "@mui/material";
import { ChevronDown } from "@src/assets/icons";
import {
transformValue,
@@ -11,7 +12,6 @@ import {
} from "@src/components/fields/Date/utils";
import { DATE_TIME_FORMAT } from "@src/constants/dates";
import BasicCell from "./BasicCell";
import { DateTimeIcon } from ".";
export default function DateTime({
column,
@@ -48,20 +48,6 @@ export default function DateTime({
label=""
hiddenLabel
aria-label={column.name as string}
InputProps={{
...props.InputProps,
endAdornment: props.InputProps?.endAdornment || (
<DateTimeIcon
className="row-hover-iconButton"
sx={{
borderRadius: 1,
p: (32 - 24) / 2 / 8,
boxSizing: "content-box",
mr: 0.5,
}}
/>
),
}}
className="cell-collapse-padding"
sx={{
width: "100%",
@@ -112,9 +98,9 @@ export default function DateTime({
size: "small",
className: "row-hover-iconButton",
edge: false,
sx: { mr: 0.5 },
sx: { mr: 3 / 8, width: 32, height: 32 },
}}
components={{ OpenPickerIcon: DateTimeIcon }}
components={{ OpenPickerIcon: ChevronDown }}
disableOpenPicker={false}
/>
);

View File

@@ -2,8 +2,8 @@ import { useAtom } from "jotai";
import { find, get } from "lodash-es";
import { useSnackbar } from "notistack";
import ReEvalIcon from "@mui/icons-material/Replay";
import EvalIcon from "@mui/icons-material/PlayCircle";
import ReEvalIcon from "@mui/icons-material/ReplayOutlined";
import EvalIcon from "@mui/icons-material/PlayCircleOutline";
import { globalScope, rowyRunAtom } from "@src/atoms/globalScope";
import {

View File

@@ -2,14 +2,16 @@ import { useAtom } from "jotai";
import { find, get } from "lodash-es";
import { useSnackbar } from "notistack";
import Thumbnail from "@src/components/Thumbnail";
import OpenIcon from "@mui/icons-material/OpenInNewOutlined";
import { Copy } from "@src/assets/icons";
import {
tableScope,
tableSchemaAtom,
tableRowsAtom,
} from "@src/atoms/tableScope";
import { IFieldConfig } from "@src/components/fields/types";
import OpenIcon from "@mui/icons-material/OpenInNewOutlined";
export interface IContextMenuActions {
label: string;
@@ -23,78 +25,83 @@ export const ContextMenuActions: IFieldConfig["contextMenuActions"] = (
) => {
const [tableSchema] = useAtom(tableSchemaAtom, tableScope);
const [tableRows] = useAtom(tableRowsAtom, tableScope);
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const { enqueueSnackbar } = useSnackbar();
const selectedCol = tableSchema.columns?.[selectedCell.columnKey];
if (!selectedCol) return [];
const selectedRow = find(tableRows, ["_rowy_ref.path", selectedCell.path]);
const cellValue = get(selectedRow, selectedCol.fieldName);
const cellValue = get(selectedRow, selectedCol.fieldName) || [];
const isEmpty =
cellValue === "" ||
cellValue === null ||
cellValue === undefined ||
cellValue.length === 0;
if (isEmpty)
return [
{
label: "Copy Image URL",
icon: <Copy />,
disabled: true,
onClick: () => {},
},
{
label: "View Image",
icon: <OpenIcon />,
onClick: () => {},
disabled: true,
},
];
const isSingleValue = isEmpty || cellValue?.length === 1;
const handleCopyImageURL = (imgObj: RowyFile) => () => {
navigator.clipboard.writeText(imgObj.downloadURL);
enqueueSnackbar("Copied image URL to clipboard", {
variant: "success",
});
enqueueSnackbar("Copied image URL");
reset();
};
const handleViewImage = (imgObj: RowyFile) => () => {
window.open(imgObj.downloadURL, "_blank");
reset();
};
if (cellValue.length === 1)
return [
{
label: "Copy Image URL",
icon: <Copy />,
onClick: handleCopyImageURL(cellValue[0]),
},
{
label: "View Image",
icon: <OpenIcon />,
onClick: handleViewImage(cellValue[0]),
},
];
else
return [
{
label: "Copy Image URL",
//onClick: handleCopyImageURL,
subItems: cellValue.map((imgObj: RowyFile, index: number) => ({
label: "Image " + (index + 1),
icon: <Copy />,
onClick: handleCopyImageURL(imgObj),
})),
},
{
label: "View Image",
subItems: cellValue.map((imgObj: RowyFile, index: number) => ({
label: "Image " + (index + 1),
icon: <OpenIcon />,
onClick: handleViewImage(imgObj),
})),
},
];
return [
{
label: "Copy image URL",
icon: <Copy />,
onClick: isSingleValue ? handleCopyImageURL(cellValue[0]) : undefined,
disabled: isEmpty,
subItems: isSingleValue
? []
: cellValue.map((imgObj: RowyFile, index: number) => ({
label: imgObj.name || "Image " + (index + 1),
icon: (
<Thumbnail
imageUrl={imgObj.downloadURL}
size="100x100"
objectFit="contain"
sx={{
width: 24,
height: 24,
boxShadow: (theme) =>
`0 0 0 1px ${theme.palette.divider} inset`,
}}
/>
),
onClick: handleCopyImageURL(imgObj),
})),
},
{
label: "View image",
icon: <OpenIcon />,
onClick: isSingleValue ? handleViewImage(cellValue[0]) : undefined,
disabled: isEmpty,
subItems: isSingleValue
? []
: cellValue.map((imgObj: RowyFile, index: number) => ({
label: imgObj.name || "Image " + (index + 1),
icon: (
<Thumbnail
imageUrl={imgObj.downloadURL}
size="100x100"
objectFit="contain"
sx={{
width: 24,
height: 24,
boxShadow: (theme) =>
`0 0 0 1px ${theme.palette.divider} inset`,
}}
/>
),
onClick: handleViewImage(imgObj),
})),
},
];
};
export default ContextMenuActions;

View File

@@ -87,7 +87,6 @@ const deleteImgHoverSx = {
export default function Image_({
column,
row,
value,
onSubmit,
disabled,

View File

@@ -2,7 +2,7 @@ import { forwardRef } from "react";
import { IPopoverInlineCellProps } from "@src/components/fields/types";
import { ButtonBase, Grid } from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { ChevronDown } from "@src/assets/icons";
import { sanitiseValue } from "./utils";
import ChipList from "@src/components/Table/formatters/ChipList";
@@ -43,13 +43,13 @@ export const MultiSelect = forwardRef(function MultiSelect(
</ChipList>
{!disabled && (
<ArrowDropDownIcon
<ChevronDown
className="row-hover-iconButton"
sx={{
flexShrink: 0,
mr: 0.5,
borderRadius: 1,
p: (32 - 24) / 2 / 8,
p: (32 - 20) / 2 / 8,
boxSizing: "content-box !important",
}}
/>

View File

@@ -2,7 +2,7 @@ import { forwardRef } from "react";
import { IPopoverInlineCellProps } from "@src/components/fields/types";
import { ButtonBase } from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { ChevronDown } from "@src/assets/icons";
import { sanitiseValue } from "./utils";
@@ -33,13 +33,13 @@ export const SingleSelect = forwardRef(function SingleSelect(
</div>
{!disabled && (
<ArrowDropDownIcon
<ChevronDown
className="row-hover-iconButton"
sx={{
flexShrink: 0,
mr: 0.5,
borderRadius: 1,
p: (32 - 24) / 2 / 8,
p: (32 - 20) / 2 / 8,
boxSizing: "content-box !important",
}}
/>

View File

@@ -2,7 +2,7 @@ import { forwardRef, useMemo } from "react";
import { IPopoverInlineCellProps } from "@src/components/fields/types";
import { ButtonBase } from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { ChevronDown } from "@src/assets/icons";
import getLabel from "./utils/getLabelHelper";
export const StatusSingleSelect = forwardRef(function StatusSingleSelect(
@@ -51,13 +51,13 @@ export const StatusSingleSelect = forwardRef(function StatusSingleSelect(
<div style={{ flexGrow: 1, overflow: "hidden" }}>{label}</div>
{!disabled && (
<ArrowDropDownIcon
<ChevronDown
className="row-hover-iconButton"
sx={{
flexShrink: 0,
mr: 0.5,
borderRadius: 1,
p: (32 - 24) / 2 / 8,
p: (32 - 20) / 2 / 8,
boxSizing: "content-box !important",
}}
/>

View File

@@ -2,7 +2,6 @@ import { FieldType } from "@src/constants/fields";
import { FormatterProps, EditorProps } from "react-data-grid";
import { Control, UseFormReturn } from "react-hook-form";
import { PopoverProps } from "@mui/material";
import { WhereFilterOp } from "firebase/firestore";
import {
ColumnConfig,
TableRow,
@@ -10,7 +9,7 @@ import {
TableFilter,
} from "@src/types/table";
import { SelectedCell } from "@src/atoms/tableScope";
import { IContextMenuItem } from "@src/components/Table/ContextMenu/MenuItem";
import { IContextMenuItem } from "@src/components/Table/ContextMenu/ContextMenuItem";
export { FieldType };

View File

@@ -4,7 +4,7 @@ import { useLocation } from "react-router-dom";
import { List, ListItemIcon, ListItemText, Collapse } from "@mui/material";
import FolderIcon from "@mui/icons-material/FolderOutlined";
import FavoriteIcon from "@mui/icons-material/FavoriteBorder";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { ChevronRight } from "@src/assets/icons";
import NavItem from "./NavItem";
import { TableSettings } from "@src/types/table";
@@ -43,7 +43,7 @@ export default function NavTableSection({
<ListItemText primary={section} style={{ textAlign: "left" }} />
<ArrowDropDownIcon
<ChevronRight
sx={{
color: "action.active",
mr: -0.5,

View File

@@ -17,7 +17,7 @@ import {
Grow,
} from "@mui/material";
import AccountCircleIcon from "@mui/icons-material/AccountCircleOutlined";
import ArrowRightIcon from "@mui/icons-material/ArrowRight";
import { ChevronRight as ChevronRightIcon } from "@src/assets/icons";
import {
globalScope,
@@ -31,7 +31,7 @@ import { ROUTES } from "@src/constants/routes";
export default function UserMenu(props: IconButtonProps) {
const anchorEl = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(false);
const [themeSubMenu, setThemeSubMenu] = useState<EventTarget | null>(null);
const [themeSubMenu, setThemeSubMenu] = useState<HTMLElement | null>(null);
const [projectId] = useAtom(projectIdAtom, globalScope);
const [userSettings] = useAtom(userSettingsAtom, globalScope);
@@ -117,43 +117,41 @@ export default function UserMenu(props: IconButtonProps) {
<Divider variant="middle" sx={{ mt: 0.5, mb: 0.5 }} />
<MenuItem onClick={(e) => setThemeSubMenu(e.target)}>
<MenuItem onClick={(e) => setThemeSubMenu(e.currentTarget)}>
Theme
<ListItemSecondaryAction style={{ pointerEvents: "none" }}>
<ArrowRightIcon style={{ display: "block" }} />
<ChevronRightIcon style={{ display: "block" }} color="action" />
</ListItemSecondaryAction>
</MenuItem>
{themeSubMenu && (
<Menu
anchorEl={themeSubMenu as any}
id="theme-sub-menu"
anchorOrigin={{ vertical: "top", horizontal: "left" }}
transformOrigin={{ vertical: "top", horizontal: "right" }}
open
onClose={() => setThemeSubMenu(null)}
sx={{ "& .MuiPaper-root": { mt: -0.5 } }}
<Menu
anchorEl={themeSubMenu}
id="theme-sub-menu"
anchorOrigin={{ vertical: "top", horizontal: "left" }}
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={Boolean(themeSubMenu)}
onClose={() => setThemeSubMenu(null)}
sx={{ "& .MuiPaper-root": { mt: -0.5 } }}
>
<MenuItem
onClick={() => changeTheme("system")}
selected={!themeOverridden}
>
<MenuItem
onClick={() => changeTheme("system")}
selected={!themeOverridden}
>
System
</MenuItem>
<MenuItem
onClick={() => changeTheme("light")}
selected={themeOverridden && theme === "light"}
>
Light
</MenuItem>
<MenuItem
onClick={() => changeTheme("dark")}
selected={themeOverridden && theme === "dark"}
>
Dark
</MenuItem>
</Menu>
)}
System
</MenuItem>
<MenuItem
onClick={() => changeTheme("light")}
selected={themeOverridden && theme === "light"}
>
Light
</MenuItem>
<MenuItem
onClick={() => changeTheme("dark")}
selected={themeOverridden && theme === "dark"}
>
Dark
</MenuItem>
</Menu>
<MenuItem
component={Link}

View File

@@ -8,6 +8,7 @@ import RadioIcon from "@src/theme/RadioIcon";
import CheckboxIcon from "@src/theme/CheckboxIcon";
import CheckboxIndeterminateIcon from "@src/theme/CheckboxIndeterminateIcon";
import AddCircleIcon from "@mui/icons-material/AddCircleOutline";
import { ChevronDown as ChevronDownIcon } from "@src/assets/icons";
import { SvgIcon } from "@mui/material";
import CircularProgressOptical from "@src/components/CircularProgressOptical";
@@ -440,6 +441,9 @@ export const components = (theme: Theme): ThemeOptions => {
},
MuiSelect: {
defaultProps: {
IconComponent: ChevronDownIcon,
},
styleOverrides: {
select: {
// If Select option is a MenuItem, dont add spacing

View File

@@ -0,0 +1,15 @@
@font-face {
font-family: "Space Grotesk";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("@src/assets/SpaceGrotesk-Bold.woff2") format("woff2");
}
@font-face {
font-family: "Space Grotesk";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("@src/assets/SpaceGrotesk-Regular.woff2") format("woff2");
}

View File

@@ -4,6 +4,8 @@ import {
TypographyStyleOptions,
} from "@mui/material/styles/createTypography";
import "./space-grotesk.css";
declare module "@mui/material/styles/createTypography" {
interface FontStyle {
fontFamilyMono: string;