diff --git a/craco.config.js b/craco.config.js
index 8e921d31..1a175a2d 100644
--- a/craco.config.js
+++ b/craco.config.js
@@ -1,7 +1,8 @@
const { whenDev } = require("@craco/craco");
const CracoAlias = require("craco-alias");
const CracoSwcPlugin = require("craco-swc");
-
+const BundleAnalyzerPlugin =
+ require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = {
plugins: [
{
@@ -65,6 +66,7 @@ module.exports = {
},
},
webpack: {
+ plugins: [new BundleAnalyzerPlugin({ analyzerMode: "server" })],
configure: {
resolve: {
// Polyfill for monaco-editor-auto-typings
diff --git a/package.json b/package.json
index d646de6f..2b990b1f 100644
--- a/package.json
+++ b/package.json
@@ -176,7 +176,8 @@
"raw-loader": "^4.0.2",
"source-map-explorer": "^2.5.2",
"ts-jest": "^28.0.2",
- "typedoc": "^0.22.17"
+ "typedoc": "^0.22.17",
+ "webpack-bundle-analyzer": "^4.5.0"
},
"resolutions": {
"@types/react": "^18"
diff --git a/public/index.html b/public/index.html
index a9153101..84198f78 100644
--- a/public/index.html
+++ b/public/index.html
@@ -75,6 +75,12 @@
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap"
/>
+
+
Rowy
diff --git a/src/components/ColorPickerInput.tsx b/src/components/ColorPickerInput.tsx
new file mode 100644
index 00000000..89ebdb21
--- /dev/null
+++ b/src/components/ColorPickerInput.tsx
@@ -0,0 +1,76 @@
+import { useState, useRef, MutableRefObject, useLayoutEffect } from "react";
+import { Box, useTheme } from "@mui/material";
+
+import { Color, ColorPicker } from "react-color-palette";
+
+const useResponsiveWidth = (): [
+ width: number,
+ setRef: MutableRefObject
+] => {
+ const ref = useRef(null);
+ const [width, setWidth] = useState(0);
+
+ useLayoutEffect(() => {
+ if (!ref || !ref.current) {
+ return;
+ }
+ const resizeObserver = new ResizeObserver((targets) => {
+ const { width: currentWidth } = targets[0].contentRect;
+ setWidth(currentWidth);
+ });
+
+ resizeObserver.observe(ref.current);
+
+ return () => {
+ resizeObserver.disconnect();
+ };
+ }, []);
+
+ return [width, ref];
+};
+
+export interface IColorPickerProps {
+ value: Color;
+ onChangeComplete: (color: Color) => void;
+ disabled?: boolean;
+}
+
+export default function ColorPickerInput({
+ value,
+ onChangeComplete,
+ disabled = false,
+}: IColorPickerProps) {
+ const [localValue, setLocalValue] = useState(value);
+ const [width, setRef] = useResponsiveWidth();
+ const theme = useTheme();
+
+ return (
+
+ setLocalValue(color)}
+ onChangeComplete={onChangeComplete}
+ />
+
+ );
+}
diff --git a/src/components/fields/Action/ActionFab.tsx b/src/components/fields/Action/ActionFab.tsx
index 26a32187..67f00847 100644
--- a/src/components/fields/Action/ActionFab.tsx
+++ b/src/components/fields/Action/ActionFab.tsx
@@ -93,10 +93,9 @@ export default function ActionFab({
return resp;
};
const handleCallableAction = async (data: any) => {
- const resp: any = await httpsCallable(
- firebaseFunctions,
- callableName
- )(data);
+ const resp: any = await httpsCallable(firebaseFunctions, callableName, {
+ timeout: 550000,
+ })(data);
return resp.data;
};
diff --git a/src/components/fields/Percentage/BasicCell.tsx b/src/components/fields/Percentage/BasicCell.tsx
index 05fcfac6..c2dbef58 100644
--- a/src/components/fields/Percentage/BasicCell.tsx
+++ b/src/components/fields/Percentage/BasicCell.tsx
@@ -1,41 +1,21 @@
import { IBasicCellProps } from "@src/components/fields/types";
import { useTheme } from "@mui/material";
-import { resultColorsScale } from "@src/utils/color";
export default function Percentage({ value }: IBasicCellProps) {
const theme = useTheme();
- if (typeof value === "number")
- return (
- <>
-
-
- {Math.round(value * 100)}%
-
- >
- );
-
- return null;
+ const percentage = typeof value === "number" ? value : 0;
+ return (
+
+ {Math.round(percentage * 100)}%
+
+ );
}
diff --git a/src/components/fields/Percentage/Settings.tsx b/src/components/fields/Percentage/Settings.tsx
new file mode 100644
index 00000000..e28dde2c
--- /dev/null
+++ b/src/components/fields/Percentage/Settings.tsx
@@ -0,0 +1,171 @@
+import { useState } from "react";
+
+import {
+ Box,
+ Checkbox,
+ Grid,
+ InputLabel,
+ MenuItem,
+ TextField,
+ Typography,
+ useTheme,
+} from "@mui/material";
+import ColorPickerInput from "@src/components/ColorPickerInput";
+import { ISettingsProps } from "@src/components/fields/types";
+
+import { Color, toColor } from "react-color-palette";
+import { fieldSx } from "@src/components/SideDrawer/utils";
+import { resultColorsScale, defaultColors } from "@src/utils/color";
+
+const colorLabels: { [key: string]: string } = {
+ 0: "Start",
+ 1: "Middle",
+ 2: "End",
+};
+
+export default function Settings({ onChange, config }: ISettingsProps) {
+ const colors: string[] = config.colors ?? defaultColors;
+
+ const [checkStates, setCheckStates] = useState(
+ colors.map(Boolean)
+ );
+
+ const onCheckboxChange = (index: number, checked: boolean) => {
+ onChange("colors")(
+ colors.map((value: any, idx: number) =>
+ index === idx ? (checked ? value || defaultColors[idx] : null) : value
+ )
+ );
+ setCheckStates(
+ checkStates.map((value, idx) => (index === idx ? checked : value))
+ );
+ };
+
+ const handleColorChange = (index: number, color: Color): void => {
+ onChange("colors")(
+ colors.map((value, idx) => (index === idx ? color.hex : value))
+ );
+ };
+
+ return (
+ <>
+
+ {checkStates.map((checked: boolean, index: number) => {
+ const colorHex = colors[index];
+ return (
+
+ onCheckboxChange(index, !checked)}
+ />
+
+
+ {colorHex && (
+
+
+ handleColorChange(index, color)
+ }
+ disabled={!checkStates[index]}
+ />
+
+ )}
+
+
+ );
+ })}
+
+
+ >
+ );
+}
+
+const Preview = ({ colors }: { colors: any }) => {
+ const theme = useTheme();
+ return (
+
+ Preview:
+
+ {[0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1].map((value) => {
+ return (
+
+
+
+ {Math.floor(value * 100)}%
+
+
+ );
+ })}
+
+
+ );
+};
diff --git a/src/components/fields/Percentage/SideDrawerField.tsx b/src/components/fields/Percentage/SideDrawerField.tsx
index ea4e0681..a6cc4366 100644
--- a/src/components/fields/Percentage/SideDrawerField.tsx
+++ b/src/components/fields/Percentage/SideDrawerField.tsx
@@ -1,7 +1,6 @@
import { ISideDrawerFieldProps } from "@src/components/fields/types";
-import { TextField, InputAdornment, Box } from "@mui/material";
-import { emphasize } from "@mui/material/styles";
+import { TextField, InputAdornment, Box, useTheme } from "@mui/material";
import { resultColorsScale } from "@src/utils/color";
import { getFieldId } from "@src/components/SideDrawer/utils";
@@ -12,6 +11,8 @@ export default function Percentage({
onSubmit,
disabled,
}: ISideDrawerFieldProps) {
+ const { colors } = (column as any).config;
+ const theme = useTheme();
return (
- `0 0 0 1px ${theme.palette.divider} inest`,
+ boxShadow: `0 0 0 1px ${theme.palette.divider} inset`,
backgroundColor:
typeof value === "number"
- ? resultColorsScale(value).toHex() + "!important"
+ ? resultColorsScale(
+ value,
+ colors,
+ theme.palette.background.paper
+ ).toHex() + "!important"
: undefined,
}}
/>
diff --git a/src/components/fields/Percentage/TableCell.tsx b/src/components/fields/Percentage/TableCell.tsx
new file mode 100644
index 00000000..29174a39
--- /dev/null
+++ b/src/components/fields/Percentage/TableCell.tsx
@@ -0,0 +1,41 @@
+import { IHeavyCellProps } from "@src/components/fields/types";
+
+import { useTheme } from "@mui/material";
+import { resultColorsScale } from "@src/utils/color";
+
+export default function Percentage({ column, value }: IHeavyCellProps) {
+ const theme = useTheme();
+ const { colors } = (column as any).config;
+
+ const percentage = typeof value === "number" ? value : 0;
+ return (
+ <>
+
+
+ {Math.round(percentage * 100)}%
+
+ >
+ );
+}
diff --git a/src/components/fields/Percentage/index.tsx b/src/components/fields/Percentage/index.tsx
index c44690e3..f353774f 100644
--- a/src/components/fields/Percentage/index.tsx
+++ b/src/components/fields/Percentage/index.tsx
@@ -1,12 +1,20 @@
import { lazy } from "react";
import { IFieldConfig, FieldType } from "@src/components/fields/types";
-import withBasicCell from "@src/components/fields/_withTableCell/withBasicCell";
+import withHeavyCell from "@src/components/fields/_withTableCell/withHeavyCell";
import { Percentage as PercentageIcon } from "@src/assets/icons";
-import BasicCell from "./BasicCell";
import TextEditor from "@src/components/Table/editors/TextEditor";
import { filterOperators } from "@src/components/fields/Number/Filter";
import BasicContextMenuActions from "@src/components/fields/_BasicCell/BasicCellContextMenuActions";
+
+const BasicCell = lazy(
+ () => import("./BasicCell" /* webpackChunkName: "BasicCell-Percentage" */)
+);
+
+const TableCell = lazy(
+ () => import("./TableCell" /* webpackChunkName: "TableCell-Percentage" */)
+);
+
const SideDrawerField = lazy(
() =>
import(
@@ -14,6 +22,10 @@ const SideDrawerField = lazy(
)
);
+const Settings = lazy(
+ () => import("./Settings" /* webpackChunkName: "Settings-Percentage" */)
+);
+
export const config: IFieldConfig = {
type: FieldType.percentage,
name: "Percentage",
@@ -22,11 +34,13 @@ export const config: IFieldConfig = {
initialValue: 0,
initializable: true,
icon: ,
+ requireConfiguration: true,
description: "Percentage stored as a number between 0 and 1.",
contextMenuActions: BasicContextMenuActions,
- TableCell: withBasicCell(BasicCell),
+ TableCell: withHeavyCell(BasicCell, TableCell),
TableEditor: TextEditor,
SideDrawerField,
+ settings: Settings,
filter: {
operators: filterOperators,
},
diff --git a/src/utils/color.ts b/src/utils/color.ts
index 58dbfa53..686e2716 100644
--- a/src/utils/color.ts
+++ b/src/utils/color.ts
@@ -1,12 +1,18 @@
import { colord } from "colord";
-export const resultColors = {
- No: "#ED4747",
- Maybe: "#f3c900",
- Yes: "#1fad5f",
-};
+export const defaultColors = ["#ED4747", "#F3C900", "#1FAD5F"];
-export const resultColorsScale = (value: number) =>
+export const resultColorsScale = (
+ value: number,
+ colors: any = defaultColors,
+ defaultColor: string = "#fff"
+) =>
value <= 0.5
- ? colord(resultColors.No).mix(resultColors.Maybe, value * 2)
- : colord(resultColors.Maybe).mix(resultColors.Yes, (value - 0.5) * 2);
+ ? colord(colors[0] || defaultColor).mix(
+ colors[1] || defaultColor,
+ value * 2
+ )
+ : colord(colors[1] || defaultColor).mix(
+ colors[2] || defaultColor,
+ (value - 0.5) * 2
+ );
diff --git a/yarn.lock b/yarn.lock
index da7f9913..b095480f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3092,6 +3092,11 @@
schema-utils "^3.0.0"
source-map "^0.7.3"
+"@polka/url@^1.0.0-next.20":
+ version "1.0.0-next.21"
+ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
+ integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
+
"@popperjs/core@^2.11.4":
version "2.11.5"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64"
@@ -4388,7 +4393,7 @@ acorn-walk@^7.0.0, acorn-walk@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
-acorn-walk@^8.1.1:
+acorn-walk@^8.0.0, acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
@@ -4398,6 +4403,11 @@ acorn@^7.0.0, acorn@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+acorn@^8.0.4:
+ version "8.7.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
+ integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
+
acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
@@ -9761,6 +9771,11 @@ mri@^1.1.0:
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
+mrmime@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
+ integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==
+
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -10060,6 +10075,11 @@ open@^8.0.9, open@^8.4.0:
is-docker "^2.1.1"
is-wsl "^2.2.0"
+opener@^1.5.2:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
+ integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
+
optionator@^0.8.1:
version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
@@ -12246,6 +12266,15 @@ signal-exit@^3.0.2, signal-exit@^3.0.3:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
+sirv@^1.0.7:
+ version "1.0.19"
+ resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49"
+ integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==
+ dependencies:
+ "@polka/url" "^1.0.0-next.20"
+ mrmime "^1.0.0"
+ totalist "^1.0.0"
+
sisteransi@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
@@ -12958,6 +12987,11 @@ toposort@^2.0.2:
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
+totalist@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
+ integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
+
tough-cookie@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
@@ -13554,6 +13588,21 @@ webidl-conversions@^6.1.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
+webpack-bundle-analyzer@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
+ integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==
+ dependencies:
+ acorn "^8.0.4"
+ acorn-walk "^8.0.0"
+ chalk "^4.1.0"
+ commander "^7.2.0"
+ gzip-size "^6.0.0"
+ lodash "^4.17.20"
+ opener "^1.5.2"
+ sirv "^1.0.7"
+ ws "^7.3.1"
+
webpack-dev-middleware@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz#aa079a8dedd7e58bfeab358a9af7dab304cee57f"
@@ -13979,6 +14028,11 @@ ws@>=7.4.6:
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.0.tgz#0b738cd484bfc9303421914b11bb4011e07615bb"
integrity sha512-uYhVJ/m9oXwEI04iIVmgLmugh2qrZihkywG9y5FfZV2ATeLIzHf93qs+tUNqlttbQK957/VX3mtwAS+UfIwA4g==
+ws@^7.3.1:
+ version "7.5.8"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a"
+ integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==
+
ws@^7.4.6:
version "7.5.7"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67"