diff --git a/src/components/fields/Array/DisplayCell.tsx b/src/components/fields/Array/DisplayCell.tsx
new file mode 100644
index 00000000..e934ea5f
--- /dev/null
+++ b/src/components/fields/Array/DisplayCell.tsx
@@ -0,0 +1,24 @@
+import { useTheme } from "@mui/material";
+import { IDisplayCellProps } from "@src/components/fields/types";
+
+export default function Array({ value }: IDisplayCellProps) {
+ const theme = useTheme();
+
+ if (!value) {
+ return null;
+ }
+
+ return (
+
+ {JSON.stringify(value, null, 4)}
+
+ );
+}
diff --git a/src/components/fields/Array/SideDrawerField/AddButton.tsx b/src/components/fields/Array/SideDrawerField/AddButton.tsx
new file mode 100644
index 00000000..4719970e
--- /dev/null
+++ b/src/components/fields/Array/SideDrawerField/AddButton.tsx
@@ -0,0 +1,92 @@
+import { useRef, useState } from "react";
+import {
+ Button,
+ ButtonGroup,
+ ListItemText,
+ MenuItem,
+ Select,
+} from "@mui/material";
+import AddIcon from "@mui/icons-material/Add";
+
+import { ChevronDown as ArrowDropDownIcon } from "@src/assets/icons";
+import { FieldType } from "@src/components/fields/types";
+import { getFieldProp } from "@src/components/fields";
+
+import {
+ ArraySupportedFields,
+ ArraySupportedFiledTypes,
+} from "./SupportedTypes";
+
+function AddButton({ handleAddNew }: { handleAddNew: Function }) {
+ const anchorEl = useRef(null);
+ const [open, setOpen] = useState(false);
+ const [fieldType, setFieldType] = useState(
+ FieldType.shortText
+ );
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default AddButton;
diff --git a/src/components/fields/Array/SideDrawerField/SupportedTypes.ts b/src/components/fields/Array/SideDrawerField/SupportedTypes.ts
new file mode 100644
index 00000000..28acd30c
--- /dev/null
+++ b/src/components/fields/Array/SideDrawerField/SupportedTypes.ts
@@ -0,0 +1,105 @@
+import { DocumentReference, GeoPoint, Timestamp } from "firebase/firestore";
+
+import { FieldType } from "@src/components/fields/types";
+
+import NumberValueSidebar from "@src/components/fields/Number/SideDrawerField";
+import ShortTextValueSidebar from "@src/components/fields/ShortText/SideDrawerField";
+import JsonValueSidebar from "@src/components/fields/Json/SideDrawerField";
+import CheckBoxValueSidebar from "@src/components/fields/Checkbox/SideDrawerField";
+import GeoPointValueSidebar from "@src/components/fields/GeoPoint/SideDrawerField";
+import DateTimeValueSidebar from "@src/components/fields/DateTime/SideDrawerField";
+import ReferenceValueSidebar from "@src/components/fields/Reference/SideDrawerField";
+
+export const ArraySupportedFields = [
+ FieldType.number,
+ FieldType.shortText,
+ FieldType.json,
+ FieldType.checkbox,
+ FieldType.geoPoint,
+ FieldType.dateTime,
+ FieldType.reference,
+] as const;
+
+export type ArraySupportedFiledTypes = typeof ArraySupportedFields[number];
+
+export const SupportedTypes = {
+ [FieldType.number]: {
+ Sidebar: NumberValueSidebar,
+ initialValue: 0,
+ dataType: "common",
+ instance: Object,
+ },
+ [FieldType.shortText]: {
+ Sidebar: ShortTextValueSidebar,
+ initialValue: "",
+ dataType: "common",
+ instance: Object,
+ },
+ [FieldType.checkbox]: {
+ Sidebar: CheckBoxValueSidebar,
+ initialValue: false,
+ dataType: "common",
+ instance: Object,
+ },
+ [FieldType.json]: {
+ Sidebar: JsonValueSidebar,
+ initialValue: {},
+ sx: [
+ {
+ marginTop: "24px",
+ },
+ ],
+ dataType: "common",
+ instance: Object,
+ },
+ [FieldType.geoPoint]: {
+ Sidebar: GeoPointValueSidebar,
+ initialValue: new GeoPoint(0, 0),
+ dataType: "firestore-type",
+ instance: GeoPoint,
+ },
+ [FieldType.dateTime]: {
+ Sidebar: DateTimeValueSidebar,
+ initialValue: Timestamp.now(),
+ dataType: "firestore-type",
+ instance: Timestamp,
+ },
+ [FieldType.reference]: {
+ Sidebar: ReferenceValueSidebar,
+ initialValue: null,
+ dataType: "firestore-type",
+ instance: DocumentReference,
+ },
+};
+
+export function detectType(value: any): ArraySupportedFiledTypes {
+ if (value === null) {
+ return FieldType.reference;
+ }
+ for (const supportedField of ArraySupportedFields) {
+ if (SupportedTypes[supportedField].dataType === "firestore-type") {
+ if (value instanceof SupportedTypes[supportedField].instance) {
+ return supportedField;
+ }
+ }
+ }
+
+ switch (typeof value) {
+ case "bigint":
+ case "number": {
+ return FieldType.number;
+ }
+ case "string": {
+ return FieldType.shortText;
+ }
+ case "boolean": {
+ return FieldType.checkbox;
+ }
+ case "object": {
+ return FieldType.json;
+ }
+ default: {
+ return FieldType.shortText;
+ }
+ }
+}
diff --git a/src/components/fields/Array/SideDrawerField/index.tsx b/src/components/fields/Array/SideDrawerField/index.tsx
new file mode 100644
index 00000000..b8415eb5
--- /dev/null
+++ b/src/components/fields/Array/SideDrawerField/index.tsx
@@ -0,0 +1,205 @@
+import {
+ DragDropContext,
+ Droppable,
+ Draggable,
+ DropResult,
+} from "react-beautiful-dnd";
+
+import { Stack, Box, Button, ListItem, List } from "@mui/material";
+import ClearIcon from "@mui/icons-material/Clear";
+import DragIndicatorOutlinedIcon from "@mui/icons-material/DragIndicatorOutlined";
+import DeleteIcon from "@mui/icons-material/DeleteOutline";
+
+import { FieldType, ISideDrawerFieldProps } from "@src/components/fields/types";
+import { TableRowRef } from "@src/types/table";
+
+import AddButton from "./AddButton";
+import { getPseudoColumn } from "./utils";
+import {
+ ArraySupportedFiledTypes,
+ detectType,
+ SupportedTypes,
+} from "./SupportedTypes";
+
+function ArrayFieldInput({
+ onChange,
+ value,
+ _rowy_ref,
+ index,
+ onRemove,
+ onSubmit,
+ id,
+}: {
+ index: number;
+ onRemove: (index: number) => void;
+ onChange: (value: any) => void;
+ value: any;
+ onSubmit: () => void;
+ _rowy_ref: TableRowRef;
+ id: string;
+}) {
+ const typeDetected = detectType(value);
+
+ const Sidebar = SupportedTypes[typeDetected].Sidebar;
+ return (
+
+ {(provided) => (
+
+
+
+ false ? theme.palette.action.disabledOpacity : 1,
+ },
+ ]}
+ />
+
+
+
+
+ onRemove(index)}
+ >
+
+
+
+ )}
+
+ );
+}
+
+export default function ArraySideDrawerField({
+ column,
+ value,
+ onChange,
+ onSubmit,
+ disabled,
+ _rowy_ref,
+ onDirty,
+ ...props
+}: ISideDrawerFieldProps) {
+ const handleAddNew = (fieldType: ArraySupportedFiledTypes) => {
+ onChange([...(value || []), SupportedTypes[fieldType].initialValue]);
+ onDirty(true);
+ };
+ const handleChange = (newValue_: any, indexUpdated: number) => {
+ onChange(
+ [...(value || [])].map((v: any, i) => {
+ if (i === indexUpdated) {
+ return newValue_;
+ }
+
+ return v;
+ })
+ );
+ };
+
+ const handleRemove = (index: number) => {
+ value.splice(index, 1);
+ onChange([...value]);
+ onDirty(true);
+ onSubmit();
+ };
+
+ const handleClearField = () => {
+ onChange([]);
+ onSubmit();
+ };
+
+ function handleOnDragEnd(result: DropResult) {
+ if (
+ !result.destination ||
+ result.destination.index === result.source.index
+ ) {
+ return;
+ }
+ const list = Array.from(value);
+ const [removed] = list.splice(result.source.index, 1);
+ list.splice(result.destination.index, 0, removed);
+ onChange(list);
+ onSubmit();
+ }
+
+ if (value === undefined || Array.isArray(value)) {
+ return (
+ <>
+
+
+ {(provided) => (
+
+ {(value || []).map((v: any, index: number) => (
+ handleChange(newValue, index)}
+ onRemove={handleRemove}
+ index={index}
+ onSubmit={onSubmit}
+ />
+ ))}
+ {provided.placeholder}
+
+ )}
+
+
+
+ >
+ );
+ }
+
+ return (
+
+
+ {JSON.stringify(value, null, 4)}
+
+ }
+ >
+ Clear field
+
+
+ );
+}
diff --git a/src/components/fields/Array/SideDrawerField/utils.ts b/src/components/fields/Array/SideDrawerField/utils.ts
new file mode 100644
index 00000000..599c4694
--- /dev/null
+++ b/src/components/fields/Array/SideDrawerField/utils.ts
@@ -0,0 +1,59 @@
+import { ColumnConfig } from "@src/types/table";
+import { FieldType } from "@src/constants/fields";
+import { ArraySupportedFiledTypes } from "./SupportedTypes";
+import { GeoPoint, DocumentReference } from "firebase/firestore";
+export function getPseudoColumn(
+ fieldType: FieldType,
+ index: number,
+ value: any
+): ColumnConfig {
+ return {
+ fieldName: (+new Date()).toString(),
+ index: index,
+ key: (+new Date()).toString(),
+ name: value + "",
+ type: fieldType,
+ };
+}
+
+// archive: detectType / TODO: remove
+export function detectType(value: any): ArraySupportedFiledTypes {
+ if (value === null) {
+ return FieldType.reference;
+ }
+ console.log(typeof GeoPoint);
+ console.log(value instanceof DocumentReference, value);
+
+ if (typeof value === "object") {
+ const keys = Object.keys(value);
+ // console.log({ keys, value }, typeof value);
+ if (keys.length === 2) {
+ if (keys.includes("_lat") && keys.includes("_long")) {
+ return FieldType.geoPoint;
+ }
+ if (keys.includes("nanoseconds") && keys.includes("seconds")) {
+ return FieldType.dateTime;
+ }
+ }
+ if (+new Date(value)) {
+ return FieldType.dateTime;
+ }
+ return FieldType.json;
+ }
+
+ switch (typeof value) {
+ case "bigint":
+ case "number": {
+ return FieldType.number;
+ }
+ case "string": {
+ return FieldType.shortText;
+ }
+ case "boolean": {
+ return FieldType.checkbox;
+ }
+ default: {
+ return FieldType.shortText;
+ }
+ }
+}
diff --git a/src/components/fields/Array/index.tsx b/src/components/fields/Array/index.tsx
new file mode 100644
index 00000000..9aad247c
--- /dev/null
+++ b/src/components/fields/Array/index.tsx
@@ -0,0 +1,30 @@
+import { lazy } from "react";
+import DataArrayIcon from "@mui/icons-material/DataArray";
+
+import { IFieldConfig, FieldType } from "@src/components/fields/types";
+import withRenderTableCell from "@src/components/Table/TableCell/withRenderTableCell";
+
+import DisplayCell from "./DisplayCell";
+
+const SideDrawerField = lazy(
+ () =>
+ import("./SideDrawerField" /* webpackChunkName: "SideDrawerField-Array" */)
+);
+
+export const config: IFieldConfig = {
+ type: FieldType.array,
+ name: "Array",
+ group: "Code",
+ dataType: "object",
+ initialValue: [],
+ initializable: true,
+ icon: ,
+ description:
+ "Connects to a sub-table in the current row. Also displays number of rows inside the sub-table. Max sub-table depth: 100.",
+ TableCell: withRenderTableCell(DisplayCell, SideDrawerField, "popover", {
+ popoverProps: { PaperProps: { sx: { p: 1, minWidth: "200px" } } },
+ }),
+ SideDrawerField,
+ requireConfiguration: true,
+};
+export default config;
diff --git a/src/components/fields/index.ts b/src/components/fields/index.ts
index 0d54b0a5..4b16d1f2 100644
--- a/src/components/fields/index.ts
+++ b/src/components/fields/index.ts
@@ -31,6 +31,7 @@ import ConnectTable from "./ConnectTable";
import ConnectService from "./ConnectService";
import Json from "./Json";
import Code from "./Code";
+import Array from "./Array";
import Action from "./Action";
import Derivative from "./Derivative";
import Formula from "./Formula";
@@ -82,6 +83,7 @@ export const FIELDS: IFieldConfig[] = [
Json,
Code,
Markdown,
+ Array,
/** CLOUD FUNCTION */
Action,
Derivative,
diff --git a/src/constants/fields.ts b/src/constants/fields.ts
index 900b88db..b05c6b89 100644
--- a/src/constants/fields.ts
+++ b/src/constants/fields.ts
@@ -35,6 +35,7 @@ export enum FieldType {
json = "JSON",
code = "CODE",
markdown = "MARKDOWN",
+ array = "ARRAY",
// CLOUD FUNCTION
action = "ACTION",
derivative = "DERIVATIVE",