Skip to content

Commit

Permalink
feat(pipeline-builder): implement the new component output reference …
Browse files Browse the repository at this point in the history
…hint design (#905)

Because

- implement the new component output reference hint design

This commit

- implement the new component output reference hint design
  • Loading branch information
EiffelFly authored Jan 28, 2024
1 parent 7b395d8 commit 4a7bb67
Show file tree
Hide file tree
Showing 24 changed files with 420 additions and 23 deletions.
62 changes: 62 additions & 0 deletions packages/toolkit/src/components/ReferenceHintTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import cn from "clsx";
import * as React from "react";
import { Icons } from "@instill-ai/design-system";

export const ReferenceHintTagRoot = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) => {
return (
<div
className={cn(
"flex h-5 flex-row items-center gap-x-1 rounded-full bg-semantic-accent-bg px-2 py-1",
className
)}
>
{children}
</div>
);
};

export const ReferenceHintTagIcon = ({
type,
className,
}: {
type: "x" | "check";
className?: string;
}) => {
return type === "x" ? (
<Icons.ReferenceIconX
className={cn("h-[9px] w-[18px] stroke-semantic-fg-secondary", className)}
/>
) : (
<Icons.ReferenceIconCheck
className={cn(
"h-[9px] w-[18px] stroke-semantic-accent-default",
className
)}
/>
);
};
export const ReferenceHintTagLabel = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) => {
return (
<p className={cn("font-sans text-[11px] font-medium", className)}>
{children}
</p>
);
};

export const ReferenceHintTag = {
Root: ReferenceHintTagRoot,
Icon: ReferenceHintTagIcon,
Label: ReferenceHintTagLabel,
};
1 change: 1 addition & 0 deletions packages/toolkit/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export * from "./ObjectViewer";
export * from "./PageBase";
export * from "./PageHead";
export * from "./PageTitle";
export * from "./ReferenceHintTag";
export * from "./cells";
export * from "./TableError";
export * from "./UserProfileCard";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ReferenceHintTag } from "../../../../components";

export const ListField = ({
componentID,
path,
title,
instillFormat,
description,
}: {
componentID?: string;
path: string;
title: string;
instillFormat: string;
description?: string;
}) => {
return (
<div className="flex flex-col">
<p className="mb-2 text-semantic-fg-secondary product-body-text-4-medium">
{`${title} [${instillFormat}]`}
</p>
<div className="mb-1 flex">
<ReferenceHintTag.Root>
<ReferenceHintTag.Label className="text-semantic-accent-default">
{componentID ? `${componentID}.` + path : path}
</ReferenceHintTag.Label>
</ReferenceHintTag.Root>
</div>
{description ? (
<p className="text-[#1D243380] product-body-text-4-regular">
{description}
</p>
) : null}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ComponentOutputReferenceHints } from ".";
import { ComponentOutoutReferenceHint } from "../../type";

export const ObjectArrayField = ({
hints,
parentPath,
componentID,
}: {
hints: ComponentOutoutReferenceHint[];
parentPath: string;
componentID?: string;
}) => {
return (
<div className="flex flex-col gap-y-2">
<p className="mb-1 text-semantic-fg-secondary product-body-text-4-medium">
{`${parentPath} [array]`}
</p>
<div className="flex flex-col gap-y-2 rounded border border-semantic-bg-line p-2">
<p className="text-semantic-fg-secondary product-body-text-4-regular">
<span>{"The "}</span>
<span className="product-body-text-4-medium">{parentPath}</span>
<span>
{
" is an array with this object, you need to add the index to correctly reference its value"
}
</span>
</p>
{hints.map((hint) => {
return (
<ComponentOutputReferenceHints.ListField
key={componentID + hint.path}
componentID={componentID}
title={hint.title}
path={hint.path.replace(parentPath, `${parentPath}[index]`)}
instillFormat={hint.instillFormat}
description={hint.description}
/>
);
})}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ListField } from "./ListField";
import { ObjectArrayField } from "./ObjectArrayField";

export const ComponentOutputReferenceHints = {
ListField,
ObjectArrayField,
};
1 change: 1 addition & 0 deletions packages/toolkit/src/lib/use-instill-form/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./type";
export * from "./useComponentOutputFields";
export * from "./useInstillForm";
export * from "./useStartOperatorTriggerPipelineForm";
export * from "./useComponentOutputReferenceHintFields";
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export function pickComponentOutputFieldsFromInstillFormTree(
}

// Process objectArray
// Becase we don't know the index of the output objectArray, we need to user
// Becase we don't know the index of the output objectArray, we need to use
// the data as a hint here

if (tree._type === "objectArray") {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { groupBy } from "../../utility";
import { ComponentOutputReferenceHints } from "../components/component-output-reference-hints";
import { ComponentOutoutReferenceHint } from "../type";

export type PickComponentsFromReferenceHintsOptions = {
mode?: "groupByFormat" | "list";
componentID?: string;
};

export function pickComponentsFromReferenceHints(
hints: ComponentOutoutReferenceHint[],
options?: PickComponentsFromReferenceHintsOptions
) {
const mode = options?.mode ?? "list";
const componentID = options?.componentID ?? undefined;
const fields: React.ReactNode[] = [];

if (mode === "list") {
const nonObjectArrayHints: ComponentOutoutReferenceHint[] = [];
const objectArrayHints: ComponentOutoutReferenceHint[] = [];

hints.forEach((hint) => {
if (hint.isObjectArrayChild) {
objectArrayHints.push(hint);
} else {
nonObjectArrayHints.push(hint);
}
});

const groupedObjectArrayHints = groupBy(objectArrayHints, (hint) =>
hint.isObjectArrayChild ? hint.objectArrayParentPath : ""
);

nonObjectArrayHints.forEach((hint) => {
fields.push(
<ComponentOutputReferenceHints.ListField
title={hint.title}
path={hint.path}
instillFormat={hint.instillFormat}
description={hint.description}
componentID={componentID}
/>
);
});

Object.entries(groupedObjectArrayHints).forEach(([parentPath, hints]) => {
fields.push(
<ComponentOutputReferenceHints.ObjectArrayField
parentPath={parentPath}
hints={hints}
componentID={componentID}
/>
);
});
}

return fields;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { ComponentOutoutReferenceHint, InstillFormTree } from "../type";

export function transformInstillFormTreeToReferenceHints(
tree: InstillFormTree,
isObjectArrayChild?: boolean,
objectArrayParentPath?: string
): ComponentOutoutReferenceHint[] {
// 1. Preprocess
const title = tree.title ?? tree.fieldKey;
let referenceHints: ComponentOutoutReferenceHint[] = [];

// Normall a objectArray child will be a formGroup with the same
// path, so we only need to pass the isObjectArrayChild flag and
// objectArrayParentPath to the children

// 2. Process

if (tree._type === "formGroup") {
for (const property of tree.properties) {
const hints = transformInstillFormTreeToReferenceHints(
property,
isObjectArrayChild,
objectArrayParentPath
);
referenceHints = [...referenceHints, ...hints];
}
return referenceHints;
}

// The component output don't have formCondition
if (tree._type === "formCondition") {
return referenceHints;
}

if (tree._type === "objectArray") {
// ObjectArray need to have the path (by instill protocol the top level won't be the
// objectArray, so we can safely assume that the path is not null)
if (tree.path) {
const hints = transformInstillFormTreeToReferenceHints(
tree.properties,
true,
tree.path
);
referenceHints = [...referenceHints, ...hints];
}
return referenceHints;
}

// Process const field
if (tree.const || !tree.path) {
return referenceHints;
}

// We don't need to hint a field that is lacking instillFormat
if (!tree.instillFormat) {
return referenceHints;
}

// We don't need to hint a field that is lacking title and key
if (!title) {
return referenceHints;
}

// Process a normal field

const hint: ComponentOutoutReferenceHint =
isObjectArrayChild && objectArrayParentPath
? {
title,
path: tree.path,
instillFormat: tree.instillFormat,
isObjectArrayChild: isObjectArrayChild ?? false,
objectArrayParentPath,
}
: {
title,
path: tree.path,
instillFormat: tree.instillFormat,
isObjectArrayChild: false,
};

return [...referenceHints, hint];
}
17 changes: 17 additions & 0 deletions packages/toolkit/src/lib/use-instill-form/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,20 @@ export type StartOperatorFreeFormFieldBaseProps = {
disabledFieldControl?: boolean;
disabledReferenceHint?: boolean;
};

export type ComponentOutoutReferenceHint =
| {
instillFormat: string;
path: string;
title: string;
description?: string;
isObjectArrayChild: false;
}
| {
instillFormat: string;
path: string;
title: string;
description?: string;
isObjectArrayChild: true;
objectArrayParentPath: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export function useComponentOutputFields(props: UseComponentOutputFieldsProps) {

const outputFormTree = transformInstillJSONSchemaToFormTree(props.schema);

console.log(outputFormTree);

const fields = pickComponentOutputFieldsFromInstillFormTree({
...props,
tree: outputFormTree,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from "react";

import { InstillJSONSchema } from "./type";
import { Nullable } from "../type";
import { transformInstillJSONSchemaToFormTree } from "./transform";
import { pickComponentsFromReferenceHints } from "./pick/pickComponentsFromReferenceHints";
import { transformInstillFormTreeToReferenceHints } from "./transform/transformInstillFormTreeToReferenceHints";

export type UseComponentOutputReferenceHintFieldsOptions = {
componentID: string;
};

export function useComponentOutputReferenceHintFields(
schema: Nullable<InstillJSONSchema>,
options?: UseComponentOutputReferenceHintFieldsOptions
) {
const componentID = options?.componentID ?? undefined;

const fields = React.useMemo(() => {
if (!schema) {
return [];
}

const outputFormTree = transformInstillJSONSchemaToFormTree(schema);
const hints = transformInstillFormTreeToReferenceHints(outputFormTree);
const fields = pickComponentsFromReferenceHints(hints, { componentID });

return fields;
}, [schema, componentID]);

return fields;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
import { useInstillForm } from "./useInstillForm";
import { InstillJSONSchema } from "./type";
import { Form } from "@instill-ai/design-system";
import exp from "constants";

const SimpleFormSchema: InstillJSONSchema = {
title: "Simple Form JSON",
Expand Down
2 changes: 1 addition & 1 deletion packages/toolkit/src/lib/vdp-sdk/connector/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Nullable } from "../../type";
import { createInstillAxiosClient } from "../helper";
import { Connector, ConnectorState, ConnectorWithDefinition } from "./types";
import { ConnectorState, ConnectorWithDefinition } from "./types";

export type TestUserConnectorConnectionResponse = {
state: ConnectorState;
Expand Down
Loading

0 comments on commit 4a7bb67

Please sign in to comment.