feat(ui): iterate on builder (WIP)

This commit is contained in:
psychedelicious
2025-01-23 07:13:08 +11:00
parent a366a32334
commit 14281890ce
7 changed files with 78 additions and 48 deletions

View File

@@ -1,8 +1,9 @@
import type { ContainerElement } from 'features/nodes/types/workflow';
import { createContext, useContext } from 'react';
export const ContainerDirectionContext = createContext<'row' | 'column' | null>(null);
export const ContainerContext = createContext<ContainerElement['data'] | null>(null);
export const useContainerDirectionContext = () => {
const containerDirection = useContext(ContainerDirectionContext);
export const useContainerContext = () => {
const containerDirection = useContext(ContainerContext);
return containerDirection;
};

View File

@@ -1,6 +1,10 @@
import { ContainerDirectionContext } from 'features/nodes/components/sidePanel/builder/ContainerContext';
import { DividerElementComponent } from 'features/nodes/components/sidePanel/builder/DividerElementComponent';
import { ElementWrapper } from 'features/nodes/components/sidePanel/builder/ElementWrapper';
import { Flex, type SystemStyleObject } from '@invoke-ai/ui-library';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { ContainerContext } from 'features/nodes/components/sidePanel/builder/ContainerContext';
import {
DIVIDER_CLASS_NAME,
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';
@@ -9,16 +13,30 @@ import { memo } from 'react';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
const getGridTemplateColumns = (count: number) => {
return Array.from({ length: count }, () => '1fr').join(' ');
};
const fill = (count: number, val: string, last?: string) => {
return Array.from({ length: count }, (_, i) => {
if (last && i === count - 1) {
return last;
}
return val;
}).join(' ');
const CONTAINER_CLASS_NAME = getPrefixedId('container');
const sx: SystemStyleObject = {
gap: 4,
'&[data-container-direction="column"]': {
flexDir: 'column',
flex: '1 1 0',
// Select all non-divider children (dividers have a fixed width that they define on their own)
[`> *:not(.${DIVIDER_CLASS_NAME})`]: {
// By default, all children should take up the same amount of space
flex: '0 1 0',
// The last child should take up the remaining space
'&:last-child': {
flex: '1 1 auto',
},
},
},
'&[data-container-direction="row"]': {
// Select all non-divider children (dividers have a fixed width that they define on their own)
[`> *:not(.${DIVIDER_CLASS_NAME})`]: {
// By default, all children should take up the same amount of space
flex: '1 1 0',
},
},
};
export const ContainerElementComponent = memo(({ id }: { id: string }) => {
@@ -31,13 +49,13 @@ export const ContainerElementComponent = memo(({ id }: { id: string }) => {
const { children, direction } = element.data;
return (
<ContainerDirectionContext.Provider value={direction}>
<ElementWrapper id={id} gap={4} flexDir={direction}>
<ContainerContext.Provider value={element.data}>
<Flex id={id} className={CONTAINER_CLASS_NAME} sx={sx} data-container-direction={direction}>
{children.map((childId) => (
<FormElementComponent key={childId} id={childId} />
))}
</ElementWrapper>
</ContainerDirectionContext.Provider>
</Flex>
</ContainerContext.Provider>
);
});
ContainerElementComponent.displayName = 'ContainerElementComponent';

View File

@@ -1,20 +1,18 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { Flex } from '@invoke-ai/ui-library';
import { useContainerDirectionContext } from 'features/nodes/components/sidePanel/builder/ContainerContext';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { memo } from 'react';
export const DividerElementComponent = memo(({ id }: { id: string }) => {
const containerDirection = useContainerDirectionContext();
export const DIVIDER_CLASS_NAME = getPrefixedId('divider');
return (
<Flex
flex="0 0 auto"
id={id}
h={containerDirection === 'column' ? '1px' : undefined}
w={containerDirection === 'column' ? undefined : '1px'}
bg="base.700"
flexShrink={0}
/>
);
const sx: SystemStyleObject = {
flex: '0 0 1px',
bg: 'base.700',
flexShrink: 0,
};
export const DividerElementComponent = memo(({ id }: { id: string }) => {
return <Flex id={id} className={DIVIDER_CLASS_NAME} sx={sx} />;
});
DividerElementComponent.displayName = 'DividerElementComponent';

View File

@@ -1,12 +1,27 @@
import type { FlexProps } from '@invoke-ai/ui-library';
import type { FlexProps, SystemStyleObject } from '@invoke-ai/ui-library';
import { Flex } from '@invoke-ai/ui-library';
import { useContainerDirectionContext } from 'features/nodes/components/sidePanel/builder/ContainerContext';
import { useContainerContext } from 'features/nodes/components/sidePanel/builder/ContainerContext';
import type { PropsWithChildren } from 'react';
import { memo } from 'react';
const sx: SystemStyleObject = {
// '&[data-container-direction="column"]': {
// flex: '1 1 auto',
// },
// '&[data-container-direction="column"] > :not(:last-child)': {
// bg: 'red',
// },
};
export const ElementWrapper = memo((props: PropsWithChildren<FlexProps>) => {
const containerDirection = useContainerDirectionContext();
return <Flex flex={containerDirection === 'column' ? '1 1 0' : undefined} {...props} />;
const container = useContainerContext();
return (
<Flex
sx={sx}
// data-container-direction={container?.direction}
{...props}
/>
);
});
ElementWrapper.displayName = 'ElementWrapper';

View File

@@ -1,5 +1,4 @@
import { Heading } from '@invoke-ai/ui-library';
import { ElementWrapper } from 'features/nodes/components/sidePanel/builder/ElementWrapper';
import { Flex, Heading } from '@invoke-ai/ui-library';
import { useElement } from 'features/nodes/types/workflow';
import { memo } from 'react';
@@ -21,9 +20,9 @@ export const HeadingElementComponent = memo(({ id }: { id: string }) => {
const { content, level } = data;
return (
<ElementWrapper id={id}>
<Flex id={id}>
<Heading size={LEVEL_TO_SIZE[level]}>{content}</Heading>
</ElementWrapper>
</Flex>
);
});

View File

@@ -1,6 +1,6 @@
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 { ElementWrapper } from 'features/nodes/components/sidePanel/builder/ElementWrapper';
import { useElement } from 'features/nodes/types/workflow';
import { memo } from 'react';
@@ -14,11 +14,11 @@ export const NodeFieldElementComponent = memo(({ id }: { id: string }) => {
const { fieldIdentifier } = data;
return (
<ElementWrapper id={id}>
<Flex id={id}>
<InputFieldGate nodeId={fieldIdentifier.nodeId} fieldName={fieldIdentifier.fieldName}>
<InputFieldViewMode nodeId={fieldIdentifier.nodeId} fieldName={fieldIdentifier.fieldName} />
</InputFieldGate>
</ElementWrapper>
</Flex>
);
});

View File

@@ -1,5 +1,4 @@
import { Text } from '@invoke-ai/ui-library';
import { ElementWrapper } from 'features/nodes/components/sidePanel/builder/ElementWrapper';
import { Flex, Text } from '@invoke-ai/ui-library';
import { useElement } from 'features/nodes/types/workflow';
import { memo } from 'react';
@@ -13,9 +12,9 @@ export const TextElementComponent = memo(({ id }: { id: string }) => {
const { content, fontSize } = data;
return (
<ElementWrapper id={id}>
<Flex id={id}>
<Text fontSize={fontSize}>{content}</Text>
</ElementWrapper>
</Flex>
);
});