Merge branch 'develop' into feature/functions-logging

This commit is contained in:
Bobby Wang
2022-12-02 20:47:41 +08:00
25 changed files with 12090 additions and 11369 deletions

File diff suppressed because it is too large Load Diff

1
.yarnrc.yml Normal file
View File

@@ -0,0 +1 @@
yarnPath: .yarn/releases/yarn-1.22.19.cjs

View File

@@ -22,12 +22,11 @@ to start.
## Working on existing issues
If you are working on an [issue](https://github.com/rowyio/rowy/issues), share
that you are working on it by commenting on the issue and posting a message on
#contributions channel in Rowy's
[Discord](https://discord.com/invite/fjBugmvzZP). This allows others in the
community and the maintainers a chance to provide feedback and guidance before
you spend time working on it.
If you are working on an [issue](https://github.com/rowyio/rowy/issues), share that you are working on it by commenting on the issue and posting a message on #contributions channel in Rowy's [Discord](https://discord.com/invite/fjBugmvzZP).
This allows others in the community and the maintainers a chance to provide feedback and guidance before you spend time working on it.
Before you get started working on an [issue](https://github.com/rowyio/rowy/issues), please make sure to share that you are working on it by commenting on the issue and posting a message on #contributions channel in Rowy's [Discord](https://discord.com/invite/fjBugmvzZP). The maintainers will then assign the issue to you after making sure any relevant information or context in addition is provided before you can start on the task.
Once you are assigned a task, please provide periodic updates or share any questions or roadblocks on either discord or the Github issue, so that the commmunity or the project maintainers can provide you any feedback or guidance as needed. If you are inactive for more than 1-2 week on a issue that was assigned to you, then we will assume you have stopped working on it and we will unassign it from you - so that we can give a chance to others in the community to work on it.
## File a feature request

View File

@@ -8,15 +8,15 @@
},
"private": true,
"dependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@mdi/js": "^6.6.96",
"@monaco-editor/react": "^4.4.4",
"@mui/icons-material": "5.6.0",
"@mui/icons-material": "^5.10.16",
"@mui/lab": "^5.0.0-alpha.76",
"@mui/material": "5.6.0",
"@mui/material": "^5.10.16",
"@mui/x-date-pickers": "^5.0.0-alpha.4",
"@rowy/form-builder": "^0.7.0",
"@rowy/form-builder": "^0.8.0",
"@rowy/multiselect": "^0.4.1",
"@tanstack/react-table": "^8.5.15",
"@tinymce/tinymce-react": "^3",
@@ -44,14 +44,14 @@
"path-browserify": "^1.0.1",
"pb-util": "^1.0.3",
"quicktype-core": "^6.0.71",
"react": "^18.0.0",
"react": "^18.2.0",
"react-beautiful-dnd": "^13.1.0",
"react-color-palette": "^6.2.0",
"react-detect-offline": "^2.4.5",
"react-div-100vh": "^0.7.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.0.0",
"react-dom": "^18.2.0",
"react-dropzone": "^10",
"react-element-scroll-hook": "^1.1.0",
"react-error-boundary": "^3.1.4",
@@ -60,9 +60,9 @@
"react-image": "^4",
"react-json-view": "^1.21.3",
"react-markdown": "^8.0.3",
"react-router-dom": "^6.3.0",
"react-router-dom": "6.3.0",
"react-router-hash-link": "^2.4.3",
"react-scripts": "^5.0.0",
"react-scripts": "^5.0.1",
"react-usestateref": "^1.0.8",
"react-virtual": "^2.10.4",
"remark-gfm": "^3.0.1",
@@ -70,13 +70,14 @@
"stream-browserify": "^3.0.0",
"swr": "^1.3.0",
"tinymce": "^5",
"tss-react": "^3.6.2",
"typescript": "^4.6.3",
"tss-react": "^4.4.4",
"typescript": "^4.9.3",
"use-algolia": "^1.5.3",
"use-async-memo": "^1.2.4",
"use-debounce": "^8.0.0",
"use-memo-value": "^1.0.1",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"workbox-webpack-plugin": "^6.5.4"
},
"scripts": {
"start": "cross-env PORT=7699 craco start",
@@ -92,7 +93,7 @@
"typedoc": "typedoc --options typedoc.json"
},
"engines": {
"node": "^16"
"node": ">=16"
},
"eslintConfig": {
"plugins": [
@@ -156,21 +157,22 @@
"@types/json2csv": "^5.0.3",
"@types/lodash-es": "^4.17.6",
"@types/node": "^17.0.23",
"@types/react": "^18.0.5",
"@types/react": "^18.0.25",
"@types/react-beautiful-dnd": "^13.1.2",
"@types/react-detect-offline": "^2.4.1",
"@types/react-div-100vh": "^0.4.0",
"@types/react-dom": "^18.0.0",
"@types/react-dom": "^18.0.9",
"@types/react-router-dom": "^5.3.3",
"@types/react-router-hash-link": "^2.4.5",
"@types/seedrandom": "^3.0.2",
"@typescript-eslint/parser": "^5.18.0",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"craco-alias": "^3.0.1",
"craco-swc": "^0.5.1",
"cross-env": "^7.0.3",
"eslint": "^8.12.0",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-react-app": "^7.0.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-local-rules": "^1.1.0",
"eslint-plugin-no-relative-import-paths": "^1.2.0",
"eslint-plugin-tsdoc": "^0.2.16",
@@ -189,5 +191,6 @@
"lint-staged": {
"*.{js,ts,tsx}": "eslint --cache --fix",
"**/*": "prettier --write --ignore-unknown"
}
},
"packageManager": "yarn@1.22.19"
}

View File

@@ -6,8 +6,8 @@ import { HelmetProvider } from "react-helmet-async";
import { Provider as JotaiProvider, Atom } from "jotai";
import { projectScope } from "@src/atoms/projectScope";
import { DebugAtoms } from "@src/atoms/utils";
import LocalizationProvider from "@mui/lab/LocalizationProvider";
import AdapterDateFns from "@mui/lab/AdapterDateFns";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
import createCache from "@emotion/cache";
import { CacheProvider } from "@emotion/react";
import RowyThemeProvider from "@src/theme/RowyThemeProvider";

View File

@@ -28,6 +28,7 @@ import "tinymce/plugins/image";
import "tinymce/plugins/paste";
import "tinymce/plugins/help";
import "tinymce/plugins/code";
import "tinymce/plugins/fullscreen";
const Styles = styled("div", {
shouldForwardProp: (prop) => prop !== "focus",
@@ -48,6 +49,11 @@ const Styles = styled("div", {
boxShadow: `0 -1px 0 0 ${theme.palette.text.primary} inset,
0 0 0 1px ${theme.palette.action.inputOutline} inset`,
},
"&.tox-fullscreen": {
zIndex: theme.zIndex.modal,
backgroundColor: theme.palette.background.paper,
},
},
"& .tox-toolbar-overlord, & .tox-edit-area__iframe, & .tox-toolbar__primary":
@@ -242,10 +248,16 @@ export default function RichTextEditor({
].join("\n"),
minHeight: 300,
menubar: false,
plugins: ["autoresize", "lists link image", "paste help", "code"],
plugins: [
"fullscreen",
"autoresize",
"lists link image",
"paste help",
"code",
],
statusbar: false,
toolbar:
"formatselect | bold italic forecolor | link | bullist numlist outdent indent | removeformat code | help",
"formatselect | bold italic forecolor | link | fullscreen | bullist numlist outdent indent | removeformat code | help",
body_id: id,
}}
value={value}

View File

@@ -1,46 +1,12 @@
import { createElement } from "react";
import { styled } from "@mui/material";
import StyledTable from "@src/components/Table/Styled/StyledTable";
import StyledCell from "@src/components/Table/Styled/StyledCell";
import EmptyState from "@src/components/EmptyState";
import { FieldType } from "@src/constants/fields";
import { getFieldProp } from "@src/components/fields";
const Root = styled("div")(({ theme }) => ({
width: "100%",
height: 43,
position: "relative",
overflow: "hidden",
whiteSpace: "nowrap",
pointerEvents: "none",
border: `1px solid ${theme.palette.divider}`,
borderTopWidth: 0,
backgroundColor: theme.palette.background.paper,
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1.25),
...theme.typography.body2,
fontSize: "0.75rem",
lineHeight: "inherit",
color: theme.palette.text.secondary,
"& .cell-collapse-padding": {
margin: theme.spacing(0, -1.5),
width: `calc(100% + ${theme.spacing(3)})`,
},
}));
const Value = styled("div")(({ theme }) => ({
width: "100%",
height: 43,
display: "flex",
justifyContent: "flex-start",
alignItems: "center",
}));
import { DEFAULT_ROW_HEIGHT } from "@src/components/Table";
export interface ICellProps
extends Partial<
@@ -53,6 +19,7 @@ export interface ICellProps
type: FieldType;
value: any;
name?: string;
rowHeight?: number;
}
export default function Cell({
@@ -60,29 +27,47 @@ export default function Cell({
type,
value,
name,
rowHeight = DEFAULT_ROW_HEIGHT,
...props
}: ICellProps) {
const formatter = type ? getFieldProp("TableCell", type) : null;
const tableCell = type ? getFieldProp("TableCell", type) : null;
return (
<Root {...props}>
<Value>
{formatter ? (
createElement(formatter, {
<StyledTable>
<StyledCell
{...props}
style={
{
...props.style,
height: rowHeight,
"--row-height": rowHeight,
} as any
}
>
{tableCell ? (
createElement(tableCell, {
value,
rowIdx: 0,
column: {
type,
key: field,
name,
config: { options: [] },
editable: false,
} as any,
row: { [field]: value },
isRowSelected: false,
onRowSelectionChange: () => {},
isSummaryRow: false,
} as any)
columnDef: {
meta: {
type,
key: field,
name,
config: { options: [] },
editable: false,
},
},
},
row: {
original: {
_rowy_ref: { path: "_rowy_/_mockCell" },
[field]: value,
},
},
focusInsideCell: false,
disabled: true,
rowHeight: DEFAULT_ROW_HEIGHT,
})
) : typeof value === "string" ||
typeof value === "number" ||
value === undefined ||
@@ -93,7 +78,7 @@ export default function Cell({
) : (
<EmptyState basic wrap="nowrap" message="Invalid column type" />
)}
</Value>
</Root>
</StyledCell>
</StyledTable>
);
}

View File

@@ -7,6 +7,10 @@ import {
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";
import type {
ColumnPinningState,
VisibilityState,
} from "@tanstack/react-table";
import { DropResult } from "react-beautiful-dnd";
import { get } from "lodash-es";
@@ -15,7 +19,6 @@ import TableHeader from "./TableHeader";
import TableBody from "./TableBody";
import FinalColumn from "./FinalColumn/FinalColumn";
import ContextMenu from "./ContextMenu";
import EmptyState from "@src/components/EmptyState";
// import BulkActions from "./BulkActions";
@@ -28,11 +31,10 @@ import {
tablePageAtom,
updateColumnAtom,
} from "@src/atoms/tableScope";
import { getFieldType, getFieldProp } from "@src/components/fields";
import { TableRow, ColumnConfig } from "@src/types/table";
import { useKeyboardNavigation } from "./useKeyboardNavigation";
import { useSaveColumnSizing } from "./useSaveColumnSizing";
import type { TableRow, ColumnConfig } from "@src/types/table";
export const DEFAULT_ROW_HEIGHT = 41;
export const DEFAULT_COL_WIDTH = 150;
@@ -130,20 +132,24 @@ export default function Table({
}, [tableColumnsOrdered, canAddColumns, canEditCells]);
// Get users hidden columns from props and memoize into a `VisibilityState`
const columnVisibility = useMemo(() => {
const columnVisibility: VisibilityState = useMemo(() => {
if (!Array.isArray(hiddenColumns)) return {};
return hiddenColumns.reduce((a, c) => ({ ...a, [c]: false }), {});
}, [hiddenColumns]);
// Get frozen columns and memoize into a `ColumnPinningState`
const columnPinning = useMemo(
const columnPinning: ColumnPinningState = useMemo(
() => ({
left: columns.filter((c) => c.meta?.fixed && c.id).map((c) => c.id!),
left: columns
.filter(
(c) => c.meta?.fixed && c.id && columnVisibility[c.id] !== false
)
.map((c) => c.id!),
}),
[columns]
[columns, columnVisibility]
);
const lastFrozen: string | undefined =
columnPinning.left[columnPinning.left.length - 1];
columnPinning.left![columnPinning.left!.length - 1];
// Call TanStack Table
const table = useReactTable({

View File

@@ -68,11 +68,11 @@ export function useVirtualization(
const columnDef = leafColumns[index].columnDef;
const schemaWidth = columnDef.size;
const localWidth = columnSizing[columnDef.id || ""];
const definedWidth = localWidth || schemaWidth;
return Math.max(
MIN_COL_WIDTH,
localWidth || schemaWidth || DEFAULT_COL_WIDTH
);
if (definedWidth === undefined) return DEFAULT_COL_WIDTH;
if (definedWidth < MIN_COL_WIDTH) return MIN_COL_WIDTH;
return definedWidth;
},
[leafColumns, columnSizing]
),

View File

@@ -105,7 +105,7 @@ export default function BuildLogsSnack({
p: 2,
pt: 1,
borderRadius: 1,
zIndex: 1,
zIndex: (theme) => theme.zIndex.snackbar,
transition: (theme) => theme.transitions.create("height"),
height: expanded ? "calc(100% - 300px)" : 50,
}}

View File

@@ -184,18 +184,20 @@ export default function TableSettingsDialog() {
handleCancel: async () => {
let _schema: Record<string, any> = {};
if (hasExtensions) {
_schema.extensionObjects = get(
data,
"_schema.extensionObjects"
// TODO: types
)!.map((x: any) => ({
_schema.extensionObjects = (
get(
data,
"_schema.extensionObjects"
// TODO: types
) ?? []
).map((x: any) => ({
...x,
active: false,
}));
}
if (hasWebhooks) {
// TODO: types
_schema.webhooks = get(data, "_schema.webhooks")!.map(
_schema.webhooks = (get(data, "_schema.webhooks") ?? []).map(
(x: any) => ({
...x,
active: false,
@@ -272,7 +274,7 @@ export default function TableSettingsDialog() {
).reduce((acc, [name, err]) => {
const match = find(fields, ["name", name])?.step;
if (!match) return acc;
acc[match] = err.message;
acc[match] = (err?.message as string) ?? "";
return acc;
}, {} as Record<string, string>);

View File

@@ -1,12 +1,26 @@
import { useEffect, useRef, useMemo, useState } from "react";
import { useAtom } from "jotai";
import {
useEffect,
useRef,
useMemo,
useState,
forwardRef,
ChangeEvent,
} from "react";
import { useAtom, useSetAtom } from "jotai";
import { isEqual } from "lodash-es";
import { colord } from "colord";
import {
DragDropContext,
Droppable,
Draggable,
DropResult,
} from "react-beautiful-dnd";
import { Box, AutocompleteProps, Theme } from "@mui/material";
import VisibilityIcon from "@mui/icons-material/VisibilityOutlined";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOffOutlined";
import IconSlash from "@src/components/IconSlash";
import DragIndicatorOutlinedIcon from "@mui/icons-material/DragIndicatorOutlined";
import ColumnSelect, { ColumnItem } from "@src/components/Table/ColumnSelect";
import ButtonWithStatus from "@src/components/ButtonWithStatus";
@@ -16,7 +30,11 @@ import {
userSettingsAtom,
updateUserSettingsAtom,
} from "@src/atoms/projectScope";
import { tableScope, tableIdAtom } from "@src/atoms/tableScope";
import {
tableScope,
tableIdAtom,
updateColumnAtom,
} from "@src/atoms/tableScope";
import { formatSubTableName } from "@src/utils/table";
export default function HiddenFields() {
@@ -57,6 +75,9 @@ export default function HiddenFields() {
}
setOpen(false);
};
// disable drag if search box is not empty
const [disableDrag, setDisableDrag] = useState<boolean>(false);
const renderOption: AutocompleteProps<
any,
true,
@@ -67,49 +88,119 @@ export default function HiddenFields() {
colord(theme.palette.background.paper)
.mix("#fff", theme.palette.mode === "dark" ? 0.16 : 0)
.alpha(1);
return (
<li {...props}>
<ColumnItem option={option}>
<Box
sx={[
{ position: "relative", height: "1.5rem" },
selected
? { color: "primary.main" }
: {
opacity: 0,
".MuiAutocomplete-option.Mui-focused &": { opacity: 0.5 },
<Draggable
draggableId={option.value}
index={option.index}
isDragDisabled={disableDrag}
>
{(provided) => (
<li {...props} ref={provided.innerRef} {...provided.draggableProps}>
<Box
sx={[{ position: "relative", height: "1.5rem" }]}
{...provided.dragHandleProps}
>
<DragIndicatorOutlinedIcon
color="disabled"
sx={[
{
marginRight: "6px",
opacity: (theme) =>
disableDrag ? theme.palette.action.disabledOpacity : 1,
},
]}
>
<VisibilityIcon />
<IconSlash
sx={[
{
"& .icon-slash-mask": {
stroke: (theme) => slashColor(theme).toHslString(),
},
".Mui-focused & .icon-slash-mask": {
stroke: (theme) =>
slashColor(theme)
.mix(
theme.palette.primary.main,
theme.palette.action.selectedOpacity +
theme.palette.action.hoverOpacity
)
.alpha(1)
.toHslString(),
},
},
selected ? { strokeDashoffset: 0 } : {},
]}
/>
</Box>
</ColumnItem>
</li>
]}
/>
</Box>
<ColumnItem option={option}>
<Box
sx={[
{ position: "relative", height: "1.5rem" },
selected
? { color: "primary.main" }
: {
opacity: 0,
".MuiAutocomplete-option.Mui-focused &": {
opacity: 0.5,
},
},
]}
>
<VisibilityIcon />
<IconSlash
sx={[
{
"& .icon-slash-mask": {
stroke: (theme) => slashColor(theme).toHslString(),
},
".Mui-focused & .icon-slash-mask": {
stroke: (theme) =>
slashColor(theme)
.mix(
theme.palette.primary.main,
theme.palette.action.selectedOpacity +
theme.palette.action.hoverOpacity
)
.alpha(1)
.toHslString(),
},
},
selected ? { strokeDashoffset: 0 } : {},
]}
/>
</Box>
</ColumnItem>
</li>
)}
</Draggable>
);
};
const updateColumn = useSetAtom(updateColumnAtom, tableScope);
// updates column on drag end
function handleOnDragEnd(result: DropResult) {
if (!result.destination) return;
updateColumn({
key: result.draggableId,
config: {},
index: result.destination.index,
});
}
// checks whether to disable reordering when search filter is applied
function checkToDisableDrag(e: ChangeEvent<HTMLInputElement>) {
setDisableDrag(e.target.value !== "");
}
const ListboxComponent = forwardRef(function ListboxComponent(
props: React.HTMLAttributes<HTMLElement>,
ulRef: any /*React.ForwardedRef<HTMLUListElement>*/
) {
const { children, ...other } = props;
return (
<DragDropContext onDragEnd={handleOnDragEnd}>
<Droppable droppableId="columns_manager" direction="vertical">
{(provided) => (
<ul
{...other}
{...provided.droppableProps}
ref={(ref) => {
provided.innerRef(ref);
if (ulRef !== null) {
ulRef(ref);
}
}}
>
{props.children}
{provided.placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
);
});
return (
<>
<ButtonWithStatus
@@ -123,6 +214,7 @@ export default function HiddenFields() {
<ColumnSelect
TextFieldProps={{
style: { display: "none" },
onInput: checkToDisableDrag,
SelectProps: {
open,
MenuProps: {
@@ -138,11 +230,19 @@ export default function HiddenFields() {
},
},
}}
{...{ AutocompleteProps: { renderOption } }}
{...{
AutocompleteProps: {
renderOption,
ListboxComponent,
},
}}
label="Hidden fields"
labelPlural="fields"
value={hiddenFields ?? []}
onChange={setHiddenFields}
onChange={(updates: string[]) => {
setHiddenFields(updates);
setDisableDrag(false);
}}
onClose={handleSave}
clearText="Show all"
selectAllText="Hide all"

View File

@@ -1,7 +1,7 @@
import { useDebouncedCallback } from "use-debounce";
import { IEditorCellProps } from "@src/components/fields/types";
import DatePicker from "@mui/lab/DatePicker";
import { DatePicker } from "@mui/x-date-pickers";
import { TextField } from "@mui/material";
import { ChevronDown } from "@src/assets/icons";
@@ -63,6 +63,8 @@ export default function Date_({
},
"& .MuiInputAdornment-root": { m: 0 },
}}
autoFocus
onKeyDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
inputProps={{ ...props.inputProps, tabIndex }}
/>
@@ -72,7 +74,6 @@ export default function Date_({
onChange={handleDateChange}
inputFormat={format}
mask={format.replace(/[A-Za-z]/g, "_")}
clearable
OpenPickerButtonProps={{
size: "small",
className: "row-hover-iconButton end",

View File

@@ -1,6 +1,6 @@
import { ISideDrawerFieldProps } from "@src/components/fields/types";
import DatePicker from "@mui/lab/DatePicker";
import { DatePicker } from "@mui/x-date-pickers";
import { TextField } from "@mui/material";
import { ChevronDown } from "@src/assets/icons";
@@ -52,7 +52,6 @@ export default function Date_({
onAccept={onSubmit}
inputFormat={format}
mask={format.replace(/[A-Za-z]/g, "_")}
clearable
OpenPickerButtonProps={{
size: "small",
sx: { width: 32, height: 32 },

View File

@@ -2,7 +2,7 @@ import { useDebouncedCallback } from "use-debounce";
import { IEditorCellProps } from "@src/components/fields/types";
import { setSeconds } from "date-fns";
import DateTimePicker from "@mui/lab/DateTimePicker";
import { DateTimePicker } from "@mui/x-date-pickers";
import { TextField } from "@mui/material";
import { ChevronDown } from "@src/assets/icons";
@@ -68,6 +68,7 @@ export default function DateTime({
},
"& .MuiInputAdornment-root": { m: 0 },
}}
autoFocus
onKeyDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
inputProps={{ ...props.inputProps, tabIndex }}
@@ -77,7 +78,6 @@ export default function DateTime({
value={transformedValue}
onChange={(date) => handleDateChange(date ? setSeconds(date, 0) : null)}
inputFormat={format}
clearable
OpenPickerButtonProps={{
size: "small",
className: "row-hover-iconButton end",
@@ -88,6 +88,7 @@ export default function DateTime({
disableOpenPicker={false}
disabled={disabled}
PopperProps={{ onClick: (e) => e.stopPropagation() }}
hideTabs={false}
/>
);
}

View File

@@ -1,7 +1,7 @@
import { ISideDrawerFieldProps } from "@src/components/fields/types";
import { setSeconds } from "date-fns";
import DateTimePicker from "@mui/lab/DateTimePicker";
import { DateTimePicker } from "@mui/x-date-pickers";
import { TextField } from "@mui/material";
import { ChevronDown } from "@src/assets/icons";
@@ -52,11 +52,10 @@ export default function DateTime({
)}
label={column.name}
value={transformedValue}
onChange={(date) => handleChange(date ? setSeconds(date, 0) : null)}
onChange={(date: any) => handleChange(date ? setSeconds(date, 0) : null)}
onAccept={onSubmit}
inputFormat={format}
mask={format.replace(/[A-Za-z]/g, "_")}
clearable
OpenPickerButtonProps={{
size: "small",
sx: { width: 32, height: 32 },
@@ -64,6 +63,7 @@ export default function DateTime({
components={{ OpenPickerIcon: ChevronDown }}
disableOpenPicker={false}
disabled={disabled}
hideTabs={false}
/>
);
}

View File

@@ -1,8 +1,8 @@
import { ISideDrawerFieldProps } from "@src/components/fields/types";
import { Box, Stack, TextField } from "@mui/material";
import DatePicker from "@mui/lab/DatePicker";
import DateTimeIcon from "@mui/icons-material/AccessTime";
import { DateTimePicker } from "@mui/x-date-pickers";
import { ChevronDown } from "@src/assets/icons";
import { fieldSx, getFieldId } from "@src/components/SideDrawer/utils";
import { getDurationString } from "./utils";
@@ -42,7 +42,7 @@ export default function Duration({
</Box>
<Stack direction="row" spacing={1} sx={{ mt: 0.5 }}>
<DatePicker
<DateTimePicker
renderInput={(props) => (
<TextField
{...props}
@@ -50,12 +50,6 @@ export default function Duration({
fullWidth
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 },
@@ -71,15 +65,14 @@ export default function Duration({
onAccept={onSubmit}
inputFormat={format}
mask={format.replace(/[A-Za-z]/g, "_")}
clearable
OpenPickerButtonProps={{ size: "small" }}
components={{ OpenPickerIcon: DateTimeIcon }}
components={{ OpenPickerIcon: ChevronDown }}
disableOpenPicker={false}
showToolbar
disabled={disabled}
hideTabs={false}
/>
<DatePicker
<DateTimePicker
renderInput={(props) => (
<TextField
{...props}
@@ -87,12 +80,6 @@ export default function Duration({
label="End"
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 },
@@ -108,12 +95,11 @@ export default function Duration({
onAccept={onSubmit}
inputFormat={format}
mask={format.replace(/[A-Za-z]/g, "_")}
clearable
OpenPickerButtonProps={{ size: "small" }}
components={{ OpenPickerIcon: DateTimeIcon }}
components={{ OpenPickerIcon: ChevronDown }}
disableOpenPicker={false}
showToolbar
disabled={disabled}
hideTabs={false}
/>
</Stack>
</>

View File

@@ -0,0 +1,28 @@
import { ISettingsProps } from "@src/components/fields/types";
import { TextField, Button } from "@mui/material";
export default function Settings({ onChange, config }: ISettingsProps) {
const copyStandardRegex = () => {
onChange("validationRegex")("^[a-zA-Z0-9+_.-]+@[a-zA-Z0-9.-]+.[a-zA-z]{2,3}$");
}
return (
<>
<TextField
type="text"
label="Validation regex"
id="validation-regex"
value={config.validationRegex}
fullWidth
onChange={(e) => {
if (e.target.value === "") onChange("validationRegex")(null);
else onChange("validationRegex")(e.target.value);
}}
/>
<Button style={{ width: "200px", margin: "20px auto auto" }} onClick={copyStandardRegex}>
Use standard regex
</Button>
</>
);
}

View File

@@ -13,6 +13,10 @@ const SideDrawerField = lazy(
import("./SideDrawerField" /* webpackChunkName: "SideDrawerField-Email" */)
);
const Settings = lazy(
() => import("./Settings" /* webpackChunkName: "Settings-ShortText" */)
);
export const config: IFieldConfig = {
type: FieldType.email,
name: "Email",
@@ -25,6 +29,7 @@ export const config: IFieldConfig = {
contextMenuActions: BasicContextMenuActions,
TableCell: withRenderTableCell(DisplayCell, EditorCell),
SideDrawerField,
settings: Settings,
filter: {
operators: filterOperators,
},

View File

@@ -41,7 +41,6 @@ export const StatusSingleSelect = forwardRef(function StatusSingleSelect({
paddingLeft: "var(--cell-padding)",
}}
>
{value}
{getLabel(value, sortedConditions)}
</div>
);

View File

@@ -23,6 +23,7 @@ import {
WhereFilterOp,
documentId,
getCountFromServer,
DocumentData,
} from "firebase/firestore";
import { useErrorHandler } from "react-error-boundary";
@@ -77,7 +78,9 @@ interface IUseFirestoreCollectionWithAtomOptions<T> {
* @param path - Collection path. If falsy, the listener isnt created at all.
* @param options - {@link IUseFirestoreCollectionWithAtomOptions}
*/
export function useFirestoreCollectionWithAtom<T = TableRow>(
export function useFirestoreCollectionWithAtom<
T extends DocumentData = TableRow
>(
dataAtom: PrimitiveAtom<T[]>,
dataScope: Parameters<typeof useAtom>[1] | undefined,
path: string | undefined,
@@ -215,7 +218,7 @@ export function useFirestoreCollectionWithAtom<T = TableRow>(
setDataAtom([]);
suspended = false;
}
if (nextPageAtom) setNextPageAtom({ loading: false, available: true });
if (nextPageAtom) setNextPageAtom({ loading: false, available: false });
if (onError) onError(error);
else handleError(error);
}

View File

@@ -88,15 +88,12 @@ export function useFirestoreDocWithAtom<T = TableRow>(
{ includeMetadataChanges: true },
(docSnapshot) => {
try {
// Create doc if it doesnt exist and were online
// WARNING: If offline and we doc doesnt exist in cache, it will
// ovewrite with default values when we go online
if (
!docSnapshot.exists() &&
!!createIfNonExistent &&
!docSnapshot.metadata.fromCache
) {
setDoc(docSnapshot.ref, createIfNonExistent);
// If doc doesnt exist, set data atom to default value
// But dont create a new document in db, since this has previously
// caused documents to be reset, and the bug is hard to reproduce.
// Instead, when the user updates the document, it will be created.
if (!docSnapshot.exists() && !!createIfNonExistent) {
// Temporarily set the data atom to the default data
setDataAtom({ ...createIfNonExistent, _rowy_ref: docSnapshot.ref });
} else {
setDataAtom({

View File

@@ -1402,23 +1402,23 @@ export const components = (theme: Theme): ThemeOptions => {
},
},
MuiYearPicker: {
styleOverrides: {
root: {
"& .PrivatePickersYear-yearButton": {
...(theme.typography.button as any),
fontSize: "1rem",
},
},
},
},
MuiPickersDay: {
styleOverrides: {
root: {
background: "none",
},
},
},
// MuiYearPicker: {
// styleOverrides: {
// root: {
// "& .PrivatePickersYear-yearButton": {
// ...(theme.typography.button as any),
// fontSize: "1rem",
// },
// },
// },
// },
// MuiPickersDay: {
// styleOverrides: {
// root: {
// background: "none",
// },
// },
// },
RowyMultiSelect: {
defaultProps: {

View File

@@ -1,14 +1,10 @@
{
"plugin": "",
"entryPoints": ["src/atoms/projectScope/*.ts", "src/atoms/tableScope/*.ts"],
"includes": "src/types",
"exclude": ["**/*.test"],
"readme": "none",
"githubPages": false,
"hideInPageTOC": true,
"entryDocument": "index.md",
"name": "Rowy",
"includeVersion": true,
"publicPath": "",
"out": "typedoc"
}

7313
yarn.lock

File diff suppressed because it is too large Load Diff