mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): iterate on builder (WIP)
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import { Flex, Grid, GridItem } from '@invoke-ai/ui-library';
|
||||
import {
|
||||
ContainerElementComponent,
|
||||
useContainerContext,
|
||||
} from 'features/nodes/components/sidePanel/builder/ContainerElementComponent';
|
||||
import { DividerElementComponent } from 'features/nodes/components/sidePanel/builder/DividerElementComponent';
|
||||
import { HeadingElementComponent } from 'features/nodes/components/sidePanel/builder/HeadingElementComponent';
|
||||
import { NodeFieldElementComponent } from 'features/nodes/components/sidePanel/builder/NodeFieldElementComponent';
|
||||
import { TextElementComponent } from 'features/nodes/components/sidePanel/builder/TextElementComponent';
|
||||
import type { ColumnChildElement, ColumnElement } from 'features/nodes/types/workflow';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { createContext, memo, useContext, useMemo } from 'react';
|
||||
import type { Equals } from 'tsafe';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const _ColumnContext = createContext<{ columnId: string; columnNumber: number } | null>(null);
|
||||
const ColumnContextProvider = ({
|
||||
columnId,
|
||||
columnNumber,
|
||||
children,
|
||||
}: PropsWithChildren<{ columnId: string; columnNumber: number }>) => {
|
||||
const ctx = useMemo(() => ({ columnId, columnNumber }), [columnId, columnNumber]);
|
||||
return <_ColumnContext.Provider value={ctx}>{children}</_ColumnContext.Provider>;
|
||||
};
|
||||
export const useColumnContext = () => {
|
||||
const context = useContext(_ColumnContext);
|
||||
assert(context !== null);
|
||||
return context;
|
||||
};
|
||||
|
||||
const ColumnElementChildComponent = memo(({ element }: { element: ColumnChildElement }) => {
|
||||
const { type, id } = element;
|
||||
switch (type) {
|
||||
case 'container':
|
||||
return <ContainerElementComponent key={id} element={element} />;
|
||||
case 'node-field':
|
||||
return <NodeFieldElementComponent key={id} element={element} />;
|
||||
case 'divider':
|
||||
return <DividerElementComponent key={id} element={element} />;
|
||||
case 'heading':
|
||||
return <HeadingElementComponent key={id} element={element} />;
|
||||
case 'text':
|
||||
return <TextElementComponent key={id} element={element} />;
|
||||
default:
|
||||
assert<Equals<typeof type, never>>(false, `Unhandled type ${type}`);
|
||||
}
|
||||
});
|
||||
ColumnElementChildComponent.displayName = 'ColumnElementChildComponent';
|
||||
|
||||
export const ColumnElementComponent = memo(({ element }: { element: ColumnElement }) => {
|
||||
const containerCtx = useContainerContext();
|
||||
const columnNumber = useMemo(
|
||||
() => containerCtx.columnIds.indexOf(element.id) + 1,
|
||||
[containerCtx.columnIds, element.id]
|
||||
);
|
||||
const withDivider = useMemo(
|
||||
() => containerCtx.columnIds.indexOf(element.id) + 1 < containerCtx.columnIds.length,
|
||||
[containerCtx.columnIds, element.id]
|
||||
);
|
||||
return (
|
||||
<ColumnContextProvider columnId={element.id} columnNumber={columnNumber}>
|
||||
<>
|
||||
<GridItem
|
||||
as={Grid}
|
||||
id={`column:${element.id}_${columnNumber}`}
|
||||
gap={4}
|
||||
gridAutoRows="min-content"
|
||||
gridAutoFlow="row"
|
||||
>
|
||||
{element.data.elements.map((element) => (
|
||||
<ColumnElementChildComponent key={element.id} element={element} />
|
||||
))}
|
||||
</GridItem>
|
||||
{withDivider && <Flex w="1px" bg="base.800" flexShrink={0} />}
|
||||
</>
|
||||
</ColumnContextProvider>
|
||||
);
|
||||
});
|
||||
|
||||
ColumnElementComponent.displayName = 'ColumnElementComponent';
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Grid } from '@invoke-ai/ui-library';
|
||||
import { ColumnElementComponent } from 'features/nodes/components/sidePanel/builder/ColumnElementComponent';
|
||||
import type { ContainerElement } from 'features/nodes/types/workflow';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { createContext, memo, useContext, useMemo } from 'react';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const _ContainerContext = createContext<{ containerId: string; columnIds: string[]; depth: number } | null>(null);
|
||||
const ContainerContextProvider = ({
|
||||
containerId,
|
||||
columnIds,
|
||||
children,
|
||||
}: PropsWithChildren<{ containerId: string; columnIds: string[] }>) => {
|
||||
const parentCtx = useContext(_ContainerContext);
|
||||
const ctx = useMemo(
|
||||
() => ({ containerId, columnIds, depth: parentCtx ? parentCtx.depth + 1 : 0 }),
|
||||
[columnIds, containerId, parentCtx]
|
||||
);
|
||||
return <_ContainerContext.Provider value={ctx}>{children}</_ContainerContext.Provider>;
|
||||
};
|
||||
export const useContainerContext = () => {
|
||||
const context = useContext(_ContainerContext);
|
||||
assert(context !== null);
|
||||
return context;
|
||||
};
|
||||
|
||||
const getGridTemplateColumns = (count: number) => {
|
||||
return Array.from({ length: count }, () => '1fr').join(' auto ');
|
||||
};
|
||||
|
||||
export const ContainerElementComponent = memo(({ element }: { element: ContainerElement }) => {
|
||||
const { id, data } = element;
|
||||
const { columns } = data;
|
||||
const columnIds = useMemo(() => columns.map((column) => column.id), [columns]);
|
||||
|
||||
return (
|
||||
<ContainerContextProvider containerId={id} columnIds={columnIds}>
|
||||
<Grid id={id} gap={4} gridTemplateColumns={getGridTemplateColumns(columns.length)} gridAutoFlow="column">
|
||||
{columns.map((element, i) => {
|
||||
return <ColumnElementComponent key={`column:${id}_${i + 1}`} element={element} />;
|
||||
})}
|
||||
</Grid>
|
||||
</ContainerContextProvider>
|
||||
);
|
||||
});
|
||||
ContainerElementComponent.displayName = 'ContainerElementComponent';
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import type { DividerElement } from 'features/nodes/types/workflow';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const DividerElementComponent = memo(({ element }: { element: DividerElement }) => {
|
||||
const { id } = element;
|
||||
|
||||
return <Flex id={id} h="1px" bg="base.800" flexShrink={0} />;
|
||||
});
|
||||
|
||||
DividerElementComponent.displayName = 'DividerElementComponent';
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Heading } from '@invoke-ai/ui-library';
|
||||
import type { HeadingElement } from 'features/nodes/types/workflow';
|
||||
import { memo } from 'react';
|
||||
|
||||
const LEVEL_TO_SIZE = {
|
||||
1: 'xl',
|
||||
2: 'lg',
|
||||
3: 'md',
|
||||
4: 'sm',
|
||||
5: 'xs',
|
||||
} as const;
|
||||
|
||||
export const HeadingElementComponent = memo(({ element }: { element: HeadingElement }) => {
|
||||
const { id, data } = element;
|
||||
const { content, level } = data;
|
||||
|
||||
return (
|
||||
<Heading id={id} size={LEVEL_TO_SIZE[level]}>
|
||||
{content}
|
||||
</Heading>
|
||||
);
|
||||
});
|
||||
|
||||
HeadingElementComponent.displayName = 'HeadingElementComponent';
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { InputFieldGate } from 'features/nodes/components/flow/nodes/Invocation/fields/InputFieldGate';
|
||||
import { InputFieldViewMode } from 'features/nodes/components/flow/nodes/Invocation/fields/InputFieldViewMode';
|
||||
import type { NodeFieldElement } from 'features/nodes/types/workflow';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const NodeFieldElementComponent = memo(({ element }: { element: NodeFieldElement }) => {
|
||||
const { id, data } = element;
|
||||
const { fieldIdentifier } = data;
|
||||
|
||||
return (
|
||||
<Flex id={id} flexBasis="100%">
|
||||
<InputFieldGate nodeId={fieldIdentifier.nodeId} fieldName={fieldIdentifier.fieldName}>
|
||||
<InputFieldViewMode nodeId={fieldIdentifier.nodeId} fieldName={fieldIdentifier.fieldName} />
|
||||
</InputFieldGate>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
NodeFieldElementComponent.displayName = 'NodeFieldElementComponent';
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Text } from '@invoke-ai/ui-library';
|
||||
import type { TextElement } from 'features/nodes/types/workflow';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const TextElementComponent = memo(({ element }: { element: TextElement }) => {
|
||||
const { id, data } = element;
|
||||
const { content, fontSize } = data;
|
||||
|
||||
return (
|
||||
<Text id={id} fontSize={fontSize}>
|
||||
{content}
|
||||
</Text>
|
||||
);
|
||||
});
|
||||
|
||||
TextElementComponent.displayName = 'TextElementComponent';
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { ContainerElementComponent } from 'features/nodes/components/sidePanel/builder/ContainerElementComponent';
|
||||
import { data } from 'features/nodes/types/workflow';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const WorkflowBuilder = memo(() => {
|
||||
return (
|
||||
<ScrollableContent>
|
||||
<Flex w="full" h="full" justifyContent="center">
|
||||
<Flex w="full" h="full" maxW={512}>
|
||||
<ContainerElementComponent element={data} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ScrollableContent>
|
||||
);
|
||||
});
|
||||
|
||||
WorkflowBuilder.displayName = 'WorkflowBuilder';
|
||||
@@ -1,134 +0,0 @@
|
||||
import { Flex, Heading, Text } from '@invoke-ai/ui-library';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { InputFieldGate } from 'features/nodes/components/flow/nodes/Invocation/fields/InputFieldGate';
|
||||
import { InputFieldViewMode } from 'features/nodes/components/flow/nodes/Invocation/fields/InputFieldViewMode';
|
||||
import type {
|
||||
BuilderElement,
|
||||
DividerElement,
|
||||
FieldElement,
|
||||
HeadingElement,
|
||||
StackElement,
|
||||
TextElement,
|
||||
} from 'features/nodes/types/workflow';
|
||||
import { data } from 'features/nodes/types/workflow';
|
||||
import { createContext, memo, useContext, useMemo } from 'react';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const StackContext = createContext<{ direction: StackElement['data']['direction']; depth: number } | null>(null);
|
||||
|
||||
const useStackContext = () => {
|
||||
const context = useContext(StackContext);
|
||||
assert(context !== null);
|
||||
return context;
|
||||
};
|
||||
|
||||
export const WorkflowBuilder = memo(() => {
|
||||
return (
|
||||
<ScrollableContent>
|
||||
<ElementComponent element={data} />
|
||||
</ScrollableContent>
|
||||
);
|
||||
});
|
||||
|
||||
WorkflowBuilder.displayName = 'WorkflowBuilder';
|
||||
|
||||
const ElementComponent = ({ element }: { element: BuilderElement }) => {
|
||||
switch (element.type) {
|
||||
case 'stack':
|
||||
return <StackElementComponent element={element} />;
|
||||
case 'field':
|
||||
return <FieldElementComponent element={element} />;
|
||||
case 'heading':
|
||||
return <HeadingElementComponent element={element} />;
|
||||
case 'text':
|
||||
return <TextElementComponent element={element} />;
|
||||
case 'divider':
|
||||
return <DividerElementComponent element={element} />;
|
||||
default:
|
||||
assert(false, `Unhandled element type: ${element}`);
|
||||
}
|
||||
};
|
||||
|
||||
const DIRECTION_TO_FLEXDIR = {
|
||||
horizontal: 'row',
|
||||
vertical: 'column',
|
||||
} as const;
|
||||
const StackElementComponent = ({ element }: { element: StackElement }) => {
|
||||
const { id, data } = element;
|
||||
const { children, direction } = data;
|
||||
|
||||
const parentCtx = useContext(StackContext);
|
||||
const depth = useMemo(() => (parentCtx ? parentCtx.depth + 1 : 0), [parentCtx]);
|
||||
const ctx = useMemo(() => ({ direction, depth }), [depth, direction]);
|
||||
|
||||
return (
|
||||
<StackContext.Provider value={ctx}>
|
||||
<Flex id={id} gap={2} flexDir={DIRECTION_TO_FLEXDIR[direction]}>
|
||||
{children.map((child) => (
|
||||
<ElementComponent key={child.id} element={child} />
|
||||
))}
|
||||
</Flex>
|
||||
</StackContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const FieldElementComponent = ({ element }: { element: FieldElement }) => {
|
||||
const { id, data } = element;
|
||||
const { fieldIdentifier } = data;
|
||||
|
||||
return (
|
||||
<Flex id={id} flexBasis="100%">
|
||||
<InputFieldGate nodeId={fieldIdentifier.nodeId} fieldName={fieldIdentifier.fieldName}>
|
||||
<InputFieldViewMode nodeId={fieldIdentifier.nodeId} fieldName={fieldIdentifier.fieldName} />
|
||||
</InputFieldGate>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const LEVEL_TO_SIZE = {
|
||||
1: 'xl',
|
||||
2: 'lg',
|
||||
3: 'md',
|
||||
4: 'sm',
|
||||
5: 'xs',
|
||||
} as const;
|
||||
const HeadingElementComponent = ({ element }: { element: HeadingElement }) => {
|
||||
const { id, data } = element;
|
||||
const { content, level } = data;
|
||||
|
||||
return (
|
||||
<Heading id={id} size={LEVEL_TO_SIZE[level]}>
|
||||
{content}
|
||||
</Heading>
|
||||
);
|
||||
};
|
||||
|
||||
const TextElementComponent = ({ element }: { element: TextElement }) => {
|
||||
const { id, data } = element;
|
||||
const { content, fontSize } = data;
|
||||
|
||||
return (
|
||||
<Text id={id} fontSize={fontSize}>
|
||||
{content}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
const DIRECTION_TO_WIDTH = {
|
||||
horizontal: '1px',
|
||||
vertical: undefined,
|
||||
};
|
||||
|
||||
const DIRECTION_TO_HEIGHT = {
|
||||
horizontal: undefined,
|
||||
vertical: '1px',
|
||||
};
|
||||
|
||||
const DividerElementComponent = ({ element }: { element: DividerElement }) => {
|
||||
const { id } = element;
|
||||
const { direction } = useStackContext();
|
||||
|
||||
return (
|
||||
<Flex id={id} w={DIRECTION_TO_WIDTH[direction]} h={DIRECTION_TO_HEIGHT[direction]} bg="base.700" flexShrink={0} />
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||
import { WorkflowBuilder } from 'features/nodes/components/sidePanel/workflow/WorkflowBuilder';
|
||||
import { WorkflowBuilder } from 'features/nodes/components/sidePanel/builder/WorkflowBuilder';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
||||
@@ -78,34 +78,28 @@ export type WorkflowV3 = z.infer<typeof zWorkflowV3>;
|
||||
// #endregion
|
||||
|
||||
// #region Workflow Builder
|
||||
const buildElementBuilder =
|
||||
<T extends BuilderElement['type']>(type: T) =>
|
||||
(data: Extract<BuilderElement, { type: T }>['data']): Extract<BuilderElement, { type: T }> =>
|
||||
({
|
||||
id: nanoid(),
|
||||
type,
|
||||
data,
|
||||
}) as Extract<BuilderElement, { type: T }>;
|
||||
const zFieldElementNumberConfig = z.object({
|
||||
display: z.enum(['slider', 'input', 'input-with-slider-inline', 'input-with-slider-popover']),
|
||||
});
|
||||
const zFieldElementStringConfig = z.object({
|
||||
display: z.enum(['input', 'textarea']),
|
||||
});
|
||||
|
||||
const zElementBase = z.object({
|
||||
id: z.string().trim().min(1),
|
||||
});
|
||||
|
||||
const zFieldElement = zElementBase.extend({
|
||||
type: z.literal('field'),
|
||||
const zNodeFieldElement = zElementBase.extend({
|
||||
type: z.literal('node-field'),
|
||||
data: z.object({
|
||||
fieldIdentifier: zFieldIdentifier,
|
||||
fieldConfig: z.union([zFieldElementNumberConfig, zFieldElementStringConfig]).optional(),
|
||||
}),
|
||||
});
|
||||
export type FieldElement = z.infer<typeof zFieldElement>;
|
||||
const buildFieldElement = buildElementBuilder('field');
|
||||
export type NodeFieldElement = z.infer<typeof zNodeFieldElement>;
|
||||
const nodeField = (
|
||||
nodeId: NodeFieldElement['data']['fieldIdentifier']['nodeId'],
|
||||
fieldName: NodeFieldElement['data']['fieldIdentifier']['fieldName']
|
||||
): NodeFieldElement => ({
|
||||
id: nanoid(),
|
||||
type: 'node-field',
|
||||
data: {
|
||||
fieldIdentifier: { nodeId, fieldName },
|
||||
},
|
||||
});
|
||||
|
||||
const zHeadingElement = zElementBase.extend({
|
||||
type: z.literal('heading'),
|
||||
@@ -115,7 +109,17 @@ const zHeadingElement = zElementBase.extend({
|
||||
}),
|
||||
});
|
||||
export type HeadingElement = z.infer<typeof zHeadingElement>;
|
||||
const buildHeadingElement = buildElementBuilder('heading');
|
||||
const heading = (
|
||||
content: HeadingElement['data']['content'],
|
||||
level: HeadingElement['data']['level']
|
||||
): HeadingElement => ({
|
||||
id: nanoid(),
|
||||
type: 'heading',
|
||||
data: {
|
||||
content,
|
||||
level,
|
||||
},
|
||||
});
|
||||
|
||||
const zTextElement = zElementBase.extend({
|
||||
type: z.literal('text'),
|
||||
@@ -125,134 +129,132 @@ const zTextElement = zElementBase.extend({
|
||||
}),
|
||||
});
|
||||
export type TextElement = z.infer<typeof zTextElement>;
|
||||
const buildTextElement = buildElementBuilder('text');
|
||||
const text = (content: TextElement['data']['content'], fontSize: TextElement['data']['fontSize']): TextElement => ({
|
||||
id: nanoid(),
|
||||
type: 'text',
|
||||
data: {
|
||||
content,
|
||||
fontSize,
|
||||
},
|
||||
});
|
||||
|
||||
const zDividerElement = zElementBase.extend({
|
||||
type: z.literal('divider'),
|
||||
data: z.void(),
|
||||
});
|
||||
export type DividerElement = z.infer<typeof zDividerElement>;
|
||||
const buildDividerElement = buildElementBuilder('divider');
|
||||
const divider = (): DividerElement => ({
|
||||
id: nanoid(),
|
||||
type: 'divider',
|
||||
});
|
||||
|
||||
export type StackElement = {
|
||||
export type ColumnElement = {
|
||||
id: string;
|
||||
type: 'stack';
|
||||
type: 'column';
|
||||
data: {
|
||||
children: BuilderElement[];
|
||||
direction: 'horizontal' | 'vertical';
|
||||
elements: ColumnChildElement[];
|
||||
};
|
||||
};
|
||||
|
||||
const zStackElement: z.ZodType<StackElement> = zElementBase.extend({
|
||||
type: z.literal('stack'),
|
||||
const zColumnElement = zElementBase.extend({
|
||||
type: z.literal('column'),
|
||||
data: z.object({
|
||||
children: z.lazy(() => z.array(zElement)),
|
||||
direction: z.enum(['horizontal', 'vertical']),
|
||||
elements: z.lazy(() => z.array(zColumnChildElement)),
|
||||
}),
|
||||
});
|
||||
const buildStackElement = buildElementBuilder('stack');
|
||||
const column = (elements: ColumnElement['data']['elements']): ColumnElement => ({
|
||||
id: nanoid(),
|
||||
type: 'column',
|
||||
data: {
|
||||
elements,
|
||||
},
|
||||
});
|
||||
|
||||
export type ContainerElement = {
|
||||
id: string;
|
||||
type: 'container';
|
||||
data: {
|
||||
columns: ColumnElement[];
|
||||
};
|
||||
};
|
||||
|
||||
const zContainerElement: z.ZodType<ContainerElement> = zElementBase.extend({
|
||||
type: z.literal('container'),
|
||||
data: z.object({
|
||||
columns: z.lazy(() => z.array(zColumnElement)),
|
||||
}),
|
||||
});
|
||||
const container = (columns: ContainerElement['data']['columns']): ContainerElement => ({
|
||||
id: nanoid(),
|
||||
type: 'container',
|
||||
data: {
|
||||
columns,
|
||||
},
|
||||
});
|
||||
|
||||
// export type CollapsibleElement = {
|
||||
// id: string;
|
||||
// type: 'collapsible';
|
||||
// children: BuilderElement[];
|
||||
// columns: BuilderElement[];
|
||||
// title: string;
|
||||
// collapsed: boolean;
|
||||
// };
|
||||
|
||||
// const zCollapsibleElement: z.ZodType<CollapsibleElement> = z.object({
|
||||
// type: z.literal('collapsible'),
|
||||
// children: z.lazy(() => z.array(zElement)),
|
||||
// columns: z.lazy(() => z.array(zElement)),
|
||||
// title: z.string(),
|
||||
// collapsed: z.boolean(),
|
||||
// });
|
||||
|
||||
const zElement = z.union([
|
||||
zStackElement,
|
||||
const zColumnChildElement = z.union([
|
||||
zContainerElement,
|
||||
// zCollapsibleElement
|
||||
zFieldElement,
|
||||
zNodeFieldElement,
|
||||
zHeadingElement,
|
||||
zTextElement,
|
||||
zDividerElement,
|
||||
]);
|
||||
|
||||
export type BuilderElement =
|
||||
| StackElement
|
||||
// | CollapsibleElement
|
||||
| FieldElement
|
||||
| HeadingElement
|
||||
| TextElement
|
||||
| DividerElement;
|
||||
export type ColumnChildElement = z.infer<typeof zColumnChildElement>;
|
||||
|
||||
export const data: StackElement = buildStackElement({
|
||||
direction: 'vertical',
|
||||
children: [
|
||||
buildHeadingElement({ content: 'My Cool Workflow', level: 1 }),
|
||||
buildTextElement({ content: 'This is a description of what my workflow does. It does things.', fontSize: 'md' }),
|
||||
buildDividerElement(),
|
||||
buildHeadingElement({ content: 'First Section', level: 2 }),
|
||||
buildTextElement({
|
||||
content: 'The first section includes fields relevant to the first section. This note describes that fact.',
|
||||
fontSize: 'sm',
|
||||
}),
|
||||
buildStackElement({
|
||||
direction: 'horizontal',
|
||||
children: [
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', fieldName: 'image' },
|
||||
}),
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', fieldName: 'image' },
|
||||
}),
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', fieldName: 'image' },
|
||||
}),
|
||||
],
|
||||
}),
|
||||
buildFieldElement({ fieldIdentifier: { nodeId: '9c058600-8d73-4702-912b-0ccf37403bfd', fieldName: 'value' } }),
|
||||
buildFieldElement({ fieldIdentifier: { nodeId: '7a8bbab2-6919-4cfc-bd7c-bcfda3c79ecf', fieldName: 'value' } }),
|
||||
buildFieldElement({ fieldIdentifier: { nodeId: '4e16cbf6-457c-46fb-9ab7-9cb262fa1e03', fieldName: 'value' } }),
|
||||
buildFieldElement({ fieldIdentifier: { nodeId: '39cb5272-a9d7-4da9-9c35-32e02b46bb34', fieldName: 'color' } }),
|
||||
buildStackElement({
|
||||
direction: 'horizontal',
|
||||
children: [
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '4f609a81-0e25-47d1-ba0d-f24fedd5273f', fieldName: 'value' },
|
||||
}),
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '4f609a81-0e25-47d1-ba0d-f24fedd5273f', fieldName: 'value' },
|
||||
}),
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '4f609a81-0e25-47d1-ba0d-f24fedd5273f', fieldName: 'value' },
|
||||
}),
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '4f609a81-0e25-47d1-ba0d-f24fedd5273f', fieldName: 'value' },
|
||||
}),
|
||||
],
|
||||
}),
|
||||
buildFieldElement({ fieldIdentifier: { nodeId: '14744f68-9000-4694-b4d6-cbe83ee231ee', fieldName: 'model' } }),
|
||||
buildDividerElement(),
|
||||
buildTextElement({ content: 'These are some text that are definitely super helpful.', fontSize: 'sm' }),
|
||||
buildDividerElement(),
|
||||
buildStackElement({
|
||||
direction: 'horizontal',
|
||||
children: [
|
||||
buildStackElement({
|
||||
direction: 'vertical',
|
||||
children: [
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', fieldName: 'image' },
|
||||
}),
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', fieldName: 'image' },
|
||||
}),
|
||||
],
|
||||
}),
|
||||
buildDividerElement(),
|
||||
buildFieldElement({
|
||||
fieldIdentifier: { nodeId: '7a8bbab2-6919-4cfc-bd7c-bcfda3c79ecf', fieldName: 'value' },
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
export const data: ContainerElement = container([
|
||||
column([
|
||||
heading('My Cool Workflow', 1),
|
||||
text('This is a description of what my workflow does. It does things.', 'md'),
|
||||
divider(),
|
||||
heading('First Section', 2),
|
||||
text('The first section includes fields relevant to the first section. This note describes that fact.', 'sm'),
|
||||
container([
|
||||
column([nodeField('7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', 'image')]),
|
||||
column([nodeField('7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', 'image')]),
|
||||
column([nodeField('7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', 'image')]),
|
||||
]),
|
||||
nodeField('9c058600-8d73-4702-912b-0ccf37403bfd', 'value'),
|
||||
nodeField('7a8bbab2-6919-4cfc-bd7c-bcfda3c79ecf', 'value'),
|
||||
nodeField('4e16cbf6-457c-46fb-9ab7-9cb262fa1e03', 'value'),
|
||||
nodeField('39cb5272-a9d7-4da9-9c35-32e02b46bb34', 'color'),
|
||||
container([
|
||||
column([
|
||||
nodeField('4f609a81-0e25-47d1-ba0d-f24fedd5273f', 'value'),
|
||||
nodeField('4f609a81-0e25-47d1-ba0d-f24fedd5273f', 'value'),
|
||||
]),
|
||||
column([
|
||||
nodeField('4f609a81-0e25-47d1-ba0d-f24fedd5273f', 'value'),
|
||||
nodeField('4f609a81-0e25-47d1-ba0d-f24fedd5273f', 'value'),
|
||||
nodeField('4f609a81-0e25-47d1-ba0d-f24fedd5273f', 'value'),
|
||||
nodeField('4f609a81-0e25-47d1-ba0d-f24fedd5273f', 'value'),
|
||||
]),
|
||||
]),
|
||||
nodeField('14744f68-9000-4694-b4d6-cbe83ee231ee', 'model'),
|
||||
divider(),
|
||||
text('These are some text that are definitely super helpful.', 'sm'),
|
||||
divider(),
|
||||
container([
|
||||
column([
|
||||
nodeField('7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', 'image'),
|
||||
nodeField('7aed1a5f-7fd7-4184-abe8-ddea0ea5e706', 'image'),
|
||||
]),
|
||||
column([nodeField('7a8bbab2-6919-4cfc-bd7c-bcfda3c79ecf', 'value')]),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user