diff --git a/www/src/components/Table/withPopoverCell.tsx b/www/src/components/Table/withPopoverCell.tsx
index e53493d6..ca3d173c 100644
--- a/www/src/components/Table/withPopoverCell.tsx
+++ b/www/src/components/Table/withPopoverCell.tsx
@@ -73,13 +73,14 @@ export default function withPopoverCell(
const basicCell = (
);
diff --git a/www/src/components/fields/Color/PopoverBasicCell.tsx b/www/src/components/fields/Color/PopoverBasicCell.tsx
index 131aaf3e..f3fb75c2 100644
--- a/www/src/components/fields/Color/PopoverBasicCell.tsx
+++ b/www/src/components/fields/Color/PopoverBasicCell.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import clsx from "clsx";
import { IPopoverBasicCellProps } from "../types";
import { makeStyles, createStyles, Grid, ButtonBase } from "@material-ui/core";
@@ -6,17 +7,12 @@ import { makeStyles, createStyles, Grid, ButtonBase } from "@material-ui/core";
const useStyles = makeStyles((theme) =>
createStyles({
root: {
- position: "absolute",
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
- paddingLeft: theme.spacing(1.5),
-
font: "inherit",
color: "inherit !important",
letterSpacing: "inherit",
textAlign: "inherit",
+
+ padding: theme.spacing(0, 1),
},
colorIndicator: {
@@ -40,7 +36,7 @@ export const Color = React.forwardRef(function Color(
container
alignItems="center"
spacing={1}
- className={classes.root}
+ className={clsx("cell-collapse-padding", classes.root)}
component={ButtonBase}
onClick={() => setShowComplexCell(true)}
ref={ref}
diff --git a/www/src/components/fields/ConnectTable/ConnectTableSelect.tsx b/www/src/components/fields/ConnectTable/ConnectTableSelect.tsx
new file mode 100644
index 00000000..d462b4dc
--- /dev/null
+++ b/www/src/components/fields/ConnectTable/ConnectTableSelect.tsx
@@ -0,0 +1,151 @@
+import React, { useState, useEffect } from "react";
+import useAlgolia from "use-algolia";
+import _find from "lodash/find";
+import { useDebounce } from "use-debounce";
+
+import MultiSelect, { MultiSelectProps } from "@antlerengineering/multiselect";
+import Loading from "components/Loading";
+
+export type ConnectTableValue = {
+ docPath: string;
+ snapshot: Record;
+};
+
+export interface IConnectTableSelectProps {
+ value: ConnectTableValue[];
+ onChange: (value: ConnectTableValue[]) => void;
+ column: any;
+ config: {
+ filters: string;
+ primaryKeys: string[];
+ secondaryKeys?: string[];
+ multiple?: boolean;
+ searchLabel?: string;
+ [key: string]: any;
+ };
+ disabled?: boolean;
+ /** Optional style overrides for root MUI `TextField` component */
+ className?: string;
+ /** Override any props of the root MUI `TextField` component */
+ TextFieldProps?: MultiSelectProps["TextFieldProps"];
+ onClose?: MultiSelectProps["onClose"];
+ /** Load the Algolia index before the MultiSelect onOpen function is triggered */
+ loadBeforeOpen?: boolean;
+}
+
+export default function ConnectTableSelect({
+ value = [],
+ onChange,
+ column,
+
+ config,
+ disabled,
+ className,
+
+ TextFieldProps = {},
+ onClose,
+ loadBeforeOpen,
+}: IConnectTableSelectProps) {
+ // Store a local copy of the value so the dropdown doesn’t automatically close
+ // when the user selects a new item and we allow for multiple selections
+ const [localValue, setLocalValue] = useState(
+ Array.isArray(value) ? value : []
+ );
+
+ const algoliaIndex = config.index;
+ const [algoliaState, requestDispatch, , setAlgoliaConfig] = useAlgolia(
+ process.env.REACT_APP_ALGOLIA_APP_ID!,
+ process.env.REACT_APP_ALGOLIA_SEARCH_API_KEY!,
+ // Don’t choose the index until the user opens the dropdown if !loadBeforeOpen
+ loadBeforeOpen ? algoliaIndex : ""
+ );
+ const options = algoliaState.hits.map((hit) => ({
+ label: config.primaryKeys?.map((key: string) => hit[key]).join(" "),
+ value: hit.objectID,
+ }));
+
+ // Pass a list of objectIDs to MultiSelect
+ const sanitisedValue = localValue.map(
+ (item) => item.docPath.split("/")[item.docPath.split("/").length - 1]
+ );
+
+ const handleChange = (_newValue) => {
+ // Ensure we return an array
+ const newValue = Array.isArray(_newValue)
+ ? _newValue
+ : _newValue !== null
+ ? [_newValue]
+ : [];
+
+ // Calculate new value
+ const newLocalValue = newValue.map((objectID) => {
+ // If this objectID is already in the previous value, use that previous
+ // value’s snapshot (in case it points to an object not in the current
+ // Algolia query)
+ const existingMatch = _find(localValue, {
+ docPath: `${algoliaIndex}/${objectID}`,
+ });
+ if (existingMatch) return existingMatch;
+
+ // If this is a completely new selection, grab the snapshot from the
+ // current Algolia query
+ const match = _find(algoliaState.hits, { objectID });
+ const { _highlightResult, ...snapshot } = match;
+ return {
+ snapshot,
+ docPath: `${algoliaIndex}/${snapshot.objectID}`,
+ };
+ });
+
+ // If !multiple, we MUST change the value (bypassing localValue),
+ // otherwise `setLocalValue` won’t be called in time for the new
+ // `localValue` to be read by `handleSave`
+ if (config.multiple === false) onChange(newLocalValue);
+ // Otherwise, `setLocalValue` until user closes dropdown
+ else setLocalValue(newLocalValue);
+ };
+
+ // Save when user closes dropdown
+ const handleSave = () => {
+ if (config.multiple !== false) onChange(localValue);
+ if (onClose) onClose();
+ };
+
+ // Change MultiSelect input field to search Algolia directly
+ const [search, setSearch] = useState("");
+ const [debouncedSearch] = useDebounce(search, 1000);
+ useEffect(() => {
+ requestDispatch({ query: debouncedSearch });
+ }, [debouncedSearch]);
+
+ return (
+ setAlgoliaConfig({ indexName: algoliaIndex })}
+ onClose={handleSave}
+ options={options}
+ TextFieldProps={{
+ className,
+ hiddenLabel: true,
+ ...TextFieldProps,
+ }}
+ label={column?.name}
+ labelPlural={config.searchLabel}
+ multiple={(config.multiple ?? true) as any}
+ AutocompleteProps={{
+ loading: algoliaState.loading,
+ loadingText: ,
+ inputValue: search,
+ onInputChange: (_, value, reason) => {
+ if (reason === "input") setSearch(value);
+ },
+ filterOptions: () => options,
+ }}
+ countText={`${localValue.length} of ${
+ algoliaState.response?.nbHits ?? "?"
+ }`}
+ disabled={disabled}
+ />
+ );
+}
diff --git a/www/src/components/fields/ConnectTable/PopoverBasicCell.tsx b/www/src/components/fields/ConnectTable/PopoverBasicCell.tsx
new file mode 100644
index 00000000..f894fab2
--- /dev/null
+++ b/www/src/components/fields/ConnectTable/PopoverBasicCell.tsx
@@ -0,0 +1,89 @@
+import React from "react";
+import clsx from "clsx";
+import { IPopoverBasicCellProps } from "../types";
+
+import {
+ makeStyles,
+ createStyles,
+ ButtonBase,
+ Grid,
+ Chip,
+} from "@material-ui/core";
+import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
+
+const useStyles = makeStyles((theme) =>
+ createStyles({
+ root: {
+ height: "100%",
+ padding: theme.spacing(0, 1, 0, 1.5),
+
+ font: "inherit",
+ color: "inherit !important",
+ letterSpacing: "inherit",
+ textAlign: "inherit",
+ justifyContent: "flex-start",
+ },
+
+ value: {
+ flex: 1,
+ maxWidth: `calc(100% - 24px + 4px)`,
+ overflow: "hidden",
+ marginRight: 0,
+ },
+ chip: {
+ display: "flex",
+ cursor: "inherit",
+ },
+ chipLabel: { whiteSpace: "nowrap" },
+
+ icon: {
+ display: "block",
+ color: theme.palette.action.active,
+ },
+ disabled: {
+ color: theme.palette.action.disabled,
+ },
+ })
+);
+
+export const ConnectTable = React.forwardRef(function ConnectTable(
+ { value, setShowComplexCell, disabled, column }: IPopoverBasicCellProps,
+ ref: React.Ref
+) {
+ const classes = useStyles();
+ const config = column.config ?? {};
+
+ return (
+ setShowComplexCell(true)}
+ ref={ref}
+ disabled={disabled}
+ >
+
+ {Array.isArray(value) &&
+ value.map((doc: any) => (
+
+ doc.snapshot[key])
+ .join(" ")}
+ className={classes.chip}
+ />
+
+ ))}
+
+
+
+
+ );
+});
+export default ConnectTable;
diff --git a/www/src/components/fields/ConnectTable/PopoverCell.tsx b/www/src/components/fields/ConnectTable/PopoverCell.tsx
new file mode 100644
index 00000000..2af5a4ed
--- /dev/null
+++ b/www/src/components/fields/ConnectTable/PopoverCell.tsx
@@ -0,0 +1,40 @@
+import React from "react";
+import { IPopoverCellProps } from "../types";
+import _get from "lodash/get";
+
+import ConnectTableSelect from "./ConnectTableSelect";
+
+export default function ConnectTable({
+ value,
+ onSubmit,
+ column,
+ parentRef,
+ setShowComplexCell,
+ disabled,
+}: IPopoverCellProps) {
+ const config = column.config ?? {};
+ if (!config || !config.primaryKeys) return null;
+
+ return (
+ setShowComplexCell(false)}
+ loadBeforeOpen
+ />
+ );
+}
diff --git a/www/src/components/fields/ConnectTable/SideDrawerField.tsx b/www/src/components/fields/ConnectTable/SideDrawerField.tsx
new file mode 100644
index 00000000..217654cf
--- /dev/null
+++ b/www/src/components/fields/ConnectTable/SideDrawerField.tsx
@@ -0,0 +1,74 @@
+import React from "react";
+import { Controller } from "react-hook-form";
+import { ISideDrawerFieldProps } from "../types";
+
+import { useTheme, Grid, Chip } from "@material-ui/core";
+
+import ConnectTableSelect from "./ConnectTableSelect";
+
+export default function ConnectTable({
+ column,
+ control,
+ disabled,
+}: ISideDrawerFieldProps) {
+ const theme = useTheme();
+
+ const config = column.config ?? {};
+
+ return (
+ {
+ const handleDelete = (hit: any) => () => {
+ // if (multiple)
+ onChange(value.filter((v) => v.snapshot.objectID !== hit.objectID));
+ // else form.setFieldValue(field.name, []);
+ };
+
+ return (
+ <>
+ {!disabled && (
+ `${value?.length ?? 0} selected`,
+ },
+ }}
+ />
+ )}
+
+ {Array.isArray(value) && (
+
+ {value.map(({ snapshot }) => (
+
+ snapshot[key])
+ .join(" ")}
+ onDelete={disabled ? undefined : handleDelete(snapshot)}
+ />
+
+ ))}
+
+ )}
+ >
+ );
+ }}
+ />
+ );
+}
diff --git a/www/src/components/fields/ConnectTable/index.tsx b/www/src/components/fields/ConnectTable/index.tsx
new file mode 100644
index 00000000..e17165c5
--- /dev/null
+++ b/www/src/components/fields/ConnectTable/index.tsx
@@ -0,0 +1,35 @@
+import React, { lazy } from "react";
+import { IFieldConfig, FieldType } from "components/fields/types";
+import withPopoverCell from "components/Table/withPopoverCell";
+
+import ConnectTableIcon from "assets/icons/ConnectTable";
+import PopoverBasicCell from "./PopoverBasicCell";
+import NullEditor from "components/Table/editors/NullEditor";
+
+const PopoverCell = lazy(
+ () =>
+ import("./PopoverCell" /* webpackChunkName: "PopoverCell-ConnectTable" */)
+);
+const SideDrawerField = lazy(
+ () =>
+ import(
+ "./SideDrawerField" /* webpackChunkName: "SideDrawerField-ConnectTable" */
+ )
+);
+
+export const config: IFieldConfig = {
+ type: FieldType.connectTable,
+ name: "Connect Table",
+ dataType: "{ docPath: string; snapshot: Record; }",
+ initialValue: [],
+ icon: ,
+ description:
+ "Connects to an existing table to fetch a snapshot of values from a row. Requires Algolia integration.",
+ TableCell: withPopoverCell(PopoverCell, PopoverBasicCell, {
+ anchorOrigin: { horizontal: "left", vertical: "bottom" },
+ transparent: true,
+ }),
+ TableEditor: NullEditor,
+ SideDrawerField,
+};
+export default config;
diff --git a/www/src/components/fields/ConnectTable/utils.ts b/www/src/components/fields/ConnectTable/utils.ts
new file mode 100644
index 00000000..d2feec47
--- /dev/null
+++ b/www/src/components/fields/ConnectTable/utils.ts
@@ -0,0 +1,4 @@
+export const sanitiseValue = (value: any) => {
+ if (value === undefined || value === null || value === "") return [];
+ else return value as string[];
+};
diff --git a/www/src/components/fields/MultiSelect/PopoverBasicCell.tsx b/www/src/components/fields/MultiSelect/PopoverBasicCell.tsx
index ff8a9686..2dce0a49 100644
--- a/www/src/components/fields/MultiSelect/PopoverBasicCell.tsx
+++ b/www/src/components/fields/MultiSelect/PopoverBasicCell.tsx
@@ -44,7 +44,7 @@ const useStyles = makeStyles((theme) =>
})
);
-export const SingleSelect = React.forwardRef(function SingleSelect(
+export const MultiSelect = React.forwardRef(function MultiSelect(
{ value, setShowComplexCell, disabled, onSubmit }: IPopoverBasicCellProps,
ref: React.Ref
) {
@@ -86,4 +86,4 @@ export const SingleSelect = React.forwardRef(function SingleSelect(
);
});
-export default SingleSelect;
+export default MultiSelect;
diff --git a/www/src/components/fields/MultiSelect/PopoverCell.tsx b/www/src/components/fields/MultiSelect/PopoverCell.tsx
index 6f7efec2..16115e95 100644
--- a/www/src/components/fields/MultiSelect/PopoverCell.tsx
+++ b/www/src/components/fields/MultiSelect/PopoverCell.tsx
@@ -6,7 +6,7 @@ import MultiSelect_ from "@antlerengineering/multiselect";
import { sanitiseValue } from "./utils";
-export default function SingleSelect({
+export default function MultiSelect({
value,
onSubmit,
column,
diff --git a/www/src/components/fields/MultiSelect/SideDrawerField.tsx b/www/src/components/fields/MultiSelect/SideDrawerField.tsx
index beecd21c..593d6a15 100644
--- a/www/src/components/fields/MultiSelect/SideDrawerField.tsx
+++ b/www/src/components/fields/MultiSelect/SideDrawerField.tsx
@@ -9,7 +9,7 @@ import FormattedChip from "components/FormattedChip";
import { sanitiseValue } from "./utils";
import { ConvertStringToArray } from "./ConvertStringToArray";
-export default function SingleSelect({
+export default function MultiSelect({
column,
control,
disabled,
diff --git a/www/src/components/fields/MultiSelect/index.tsx b/www/src/components/fields/MultiSelect/index.tsx
index 0db14658..7ee68e4b 100644
--- a/www/src/components/fields/MultiSelect/index.tsx
+++ b/www/src/components/fields/MultiSelect/index.tsx
@@ -19,7 +19,7 @@ const SideDrawerField = lazy(
export const config: IFieldConfig = {
type: FieldType.multiSelect,
- name: "MultiSelect",
+ name: "Multi Select",
dataType: "string[]",
initialValue: [],
icon: ,
diff --git a/www/src/components/fields/SingleSelect/index.tsx b/www/src/components/fields/SingleSelect/index.tsx
index eee9eaa5..a861cb45 100644
--- a/www/src/components/fields/SingleSelect/index.tsx
+++ b/www/src/components/fields/SingleSelect/index.tsx
@@ -19,7 +19,7 @@ const SideDrawerField = lazy(
export const config: IFieldConfig = {
type: FieldType.singleSelect,
- name: "SingleSelect",
+ name: "Single Select",
dataType: "string | null",
initialValue: null,
icon: ,
diff --git a/www/src/components/fields/index.tsx b/www/src/components/fields/index.tsx
index cc34bd41..978301a2 100644
--- a/www/src/components/fields/index.tsx
+++ b/www/src/components/fields/index.tsx
@@ -24,6 +24,7 @@ import File_ from "./File";
import SingleSelect from "./SingleSelect";
import MultiSelect from "./MultiSelect";
import SubTable from "./SubTable";
+import ConnectTable from "./ConnectTable";
import Json from "./Json";
import Code from "./Code";
import RichText from "./RichText";
@@ -60,7 +61,7 @@ export const FIELDS: IFieldConfig[] = [
MultiSelect,
// CONNECTION
SubTable,
- // TODO: connectTable,
+ ConnectTable,
// TODO: connectService,
// CODE
Json,
diff --git a/www/src/components/fields/types.ts b/www/src/components/fields/types.ts
index 54c3a1e6..d907ee6e 100644
--- a/www/src/components/fields/types.ts
+++ b/www/src/components/fields/types.ts
@@ -39,10 +39,8 @@ export interface IPopoverCellProps extends ICustomCellProps {
setShowComplexCell: React.Dispatch>;
parentRef: PopoverProps["anchorEl"];
}
-export interface IPopoverBasicCellProps extends IBasicCellProps {
+export interface IPopoverBasicCellProps extends ICustomCellProps {
setShowComplexCell: React.Dispatch>;
- disabled: boolean;
- onSubmit: (value: any) => void;
}
export interface ISideDrawerFieldProps {