mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
Merge branch 'develop' into feature/functions-logging
This commit is contained in:
File diff suppressed because it is too large
Load Diff
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
@@ -0,0 +1 @@
|
||||
yarnPath: .yarn/releases/yarn-1.22.19.cjs
|
||||
@@ -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
|
||||
|
||||
|
||||
41
package.json
41
package.json
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 user’s 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({
|
||||
|
||||
@@ -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]
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
}}
|
||||
|
||||
@@ -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>);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
28
src/components/fields/Email/Settings.tsx
Normal file
28
src/components/fields/Email/Settings.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -41,7 +41,6 @@ export const StatusSingleSelect = forwardRef(function StatusSingleSelect({
|
||||
paddingLeft: "var(--cell-padding)",
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
{getLabel(value, sortedConditions)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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 isn’t 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);
|
||||
}
|
||||
|
||||
@@ -88,15 +88,12 @@ export function useFirestoreDocWithAtom<T = TableRow>(
|
||||
{ includeMetadataChanges: true },
|
||||
(docSnapshot) => {
|
||||
try {
|
||||
// Create doc if it doesn’t exist and we’re online
|
||||
// WARNING: If offline and we doc doesn’t 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 doesn’t exist, set data atom to default value
|
||||
// But don’t 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({
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user