feat(ui): migrate to @invoke-ai/ui

This commit is contained in:
psychedelicious 2024-01-20 11:04:19 +11:00
parent 8e2ccab1f0
commit 5d068c1da1
478 changed files with 3585 additions and 8367 deletions

View File

@ -93,6 +93,27 @@ module.exports = {
'@typescript-eslint/no-import-type-side-effects': 'error', '@typescript-eslint/no-import-type-side-effects': 'error',
'simple-import-sort/imports': 'error', 'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error', 'simple-import-sort/exports': 'error',
// Prefer @invoke-ai/ui components over chakra
'no-restricted-imports': 'off',
'@typescript-eslint/no-restricted-imports': [
'warn',
{
paths: [
{
name: '@chakra-ui/react',
message: "Please import from '@invoke-ai/ui' instead.",
},
{
name: '@chakra-ui/layout',
message: "Please import from '@invoke-ai/ui' instead.",
},
{
name: '@chakra-ui/portal',
message: "Please import from '@invoke-ai/ui' instead.",
},
],
},
],
}, },
overrides: [ overrides: [
{ {

View File

@ -32,8 +32,8 @@
"fix": "eslint --fix . && prettier --log-level warn --write .", "fix": "eslint --fix . && prettier --log-level warn --write .",
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"postinstall": "pnpm run theme", "postinstall": "pnpm run theme",
"theme": "chakra-cli tokens src/theme/theme.ts", "theme": "chakra-cli tokens --strict-component-types --strict-token-types node_modules/@invoke-ai/ui",
"theme:watch": "chakra-cli tokens src/theme/theme.ts --watch", "theme:watch": "chakra-cli tokens --strict-component-types --strict-token-types node_modules/@invoke-ai/ui --watch",
"storybook": "storybook dev -p 6006", "storybook": "storybook dev -p 6006",
"build-storybook": "storybook build", "build-storybook": "storybook build",
"unimported": "npx unimported" "unimported": "npx unimported"
@ -66,7 +66,7 @@
"@emotion/react": "^11.11.3", "@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@fontsource-variable/inter": "^5.0.16", "@fontsource-variable/inter": "^5.0.16",
"@invoke-ai/ui": "^0.0.3", "@invoke-ai/ui": "file:/home/bat/Documents/Code/ui",
"@mantine/form": "6.0.21", "@mantine/form": "6.0.21",
"@nanostores/react": "^0.7.1", "@nanostores/react": "^0.7.1",
"@reduxjs/toolkit": "2.0.1", "@reduxjs/toolkit": "2.0.1",

View File

@ -53,8 +53,8 @@ dependencies:
specifier: ^5.0.16 specifier: ^5.0.16
version: 5.0.16 version: 5.0.16
'@invoke-ai/ui': '@invoke-ai/ui':
specifier: ^0.0.3 specifier: file:/home/bat/Documents/Code/ui
version: 0.0.3(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react-use-size@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(react-dom@18.2.0)(react-i18next@14.0.0)(react@18.2.0) version: file:../../../../ui(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(chakra-react-select@4.7.6)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(overlayscrollbars-react@0.5.3)(overlayscrollbars@2.4.6)(react-dom@18.2.0)(react-i18next@14.0.0)(react-select@5.8.0)(react@18.2.0)
'@mantine/form': '@mantine/form':
specifier: 6.0.21 specifier: 6.0.21
version: 6.0.21(react@18.2.0) version: 6.0.21(react@18.2.0)
@ -3688,48 +3688,6 @@ packages:
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
dev: true dev: true
/@invoke-ai/ui@0.0.3(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react-use-size@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(react-dom@18.2.0)(react-i18next@14.0.0)(react@18.2.0):
resolution: {integrity: sha512-EFh30NPTIWXJKNTgUghrbeB7hB59fD+cm6DIoOYMlMQOQGfHTu+6aOOx6hBoj7fse2UWWhL/dEhannz6homoCw==}
peerDependencies:
'@chakra-ui/anatomy': ^2.2.2
'@chakra-ui/icons': ^2.1.1
'@chakra-ui/layout': ^2.3.1
'@chakra-ui/portal': ^2.1.0
'@chakra-ui/react': ^2.8.2
'@chakra-ui/react-use-size': ^2.1.0
'@chakra-ui/styled-system': ^2.9.2
'@chakra-ui/theme-tools': ^2.1.2
'@emotion/react': ^11.11.3
'@emotion/styled': ^11.11.0
'@fontsource-variable/inter': ^5.0.16
'@nanostores/react': ^0.7.1
framer-motion: ^10.18.0
lodash-es: ^4.17.21
nanostores: ^0.9.5
react: ^18.2.0
react-dom: ^18.2.0
react-i18next: ^14.0.0
dependencies:
'@chakra-ui/anatomy': 2.2.2
'@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
'@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
'@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/react': 2.8.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.48)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/react-use-size': 2.1.0(react@18.2.0)
'@chakra-ui/styled-system': 2.9.2
'@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2)
'@emotion/react': 11.11.3(@types/react@18.2.48)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.3)(@types/react@18.2.48)(react@18.2.0)
'@fontsource-variable/inter': 5.0.16
'@nanostores/react': 0.7.1(nanostores@0.9.5)(react@18.2.0)
framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0)
lodash-es: 4.17.21
nanostores: 0.9.5
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-i18next: 14.0.0(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0)
dev: false
/@isaacs/cliui@8.0.2: /@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -13787,3 +13745,53 @@ packages:
react: 18.2.0 react: 18.2.0
use-sync-external-store: 1.2.0(react@18.2.0) use-sync-external-store: 1.2.0(react@18.2.0)
dev: false dev: false
file:../../../../ui(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(chakra-react-select@4.7.6)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(overlayscrollbars-react@0.5.3)(overlayscrollbars@2.4.6)(react-dom@18.2.0)(react-i18next@14.0.0)(react-select@5.8.0)(react@18.2.0):
resolution: {directory: ../../../../ui, type: directory}
id: file:../../../../ui
name: '@invoke-ai/ui'
peerDependencies:
'@chakra-ui/anatomy': ^2.2.2
'@chakra-ui/icons': ^2.1.1
'@chakra-ui/layout': ^2.3.1
'@chakra-ui/portal': ^2.1.0
'@chakra-ui/react': ^2.8.2
'@chakra-ui/styled-system': ^2.9.2
'@chakra-ui/theme-tools': ^2.1.2
'@emotion/react': ^11.11.3
'@emotion/styled': ^11.11.0
'@fontsource-variable/inter': ^5.0.16
'@nanostores/react': ^0.7.1
chakra-react-select: ^4.7.6
framer-motion: ^10.18.0
lodash-es: ^4.17.21
nanostores: ^0.9.5
overlayscrollbars: ^2.4.6
overlayscrollbars-react: ^0.5.3
react: ^18.2.0
react-dom: ^18.2.0
react-i18next: ^14.0.0
react-select: ^5.8.0
dependencies:
'@chakra-ui/anatomy': 2.2.2
'@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
'@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
'@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/react': 2.8.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.48)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/styled-system': 2.9.2
'@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2)
'@emotion/react': 11.11.3(@types/react@18.2.48)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.3)(@types/react@18.2.48)(react@18.2.0)
'@fontsource-variable/inter': 5.0.16
'@nanostores/react': 0.7.1(nanostores@0.9.5)(react@18.2.0)
chakra-react-select: 4.7.6(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@emotion/react@11.11.3)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0)
lodash-es: 4.17.21
nanostores: 0.9.5
overlayscrollbars: 2.4.6
overlayscrollbars-react: 0.5.3(overlayscrollbars@2.4.6)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-i18next: 14.0.0(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0)
react-select: 5.8.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
dev: false

View File

@ -1,4 +1,4 @@
import { Box } from '@chakra-ui/react'; import { Box } from '@invoke-ai/ui';
import { useSocketIO } from 'app/hooks/useSocketIO'; import { useSocketIO } from 'app/hooks/useSocketIO';
import { useLogger } from 'app/logging/useLogger'; import { useLogger } from 'app/logging/useLogger';
import { appStarted } from 'app/store/middleware/listenerMiddleware/listeners/appStarted'; import { appStarted } from 'app/store/middleware/listenerMiddleware/listeners/appStarted';

View File

@ -1,6 +1,4 @@
import { Flex, Heading, Link, useToast } from '@chakra-ui/react'; import { Button, Flex, Heading, Link, Text, useToast } from '@invoke-ai/ui';
import { InvButton } from 'common/components/InvButton/InvButton';
import { InvText } from 'common/components/InvText/wrapper';
import newGithubIssueUrl from 'new-github-issue-url'; import newGithubIssueUrl from 'new-github-issue-url';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -65,24 +63,24 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
justifyContent="space-between" justifyContent="space-between"
alignItems="center" alignItems="center"
> >
<InvText fontWeight="semibold" color="error.400"> <Text fontWeight="semibold" color="error.400">
{error.name}: {error.message} {error.name}: {error.message}
</InvText> </Text>
</Flex> </Flex>
<Flex gap={4}> <Flex gap={4}>
<InvButton <Button
leftIcon={<PiArrowCounterClockwiseBold />} leftIcon={<PiArrowCounterClockwiseBold />}
onClick={resetErrorBoundary} onClick={resetErrorBoundary}
> >
{t('accessibility.resetUI')} {t('accessibility.resetUI')}
</InvButton> </Button>
<InvButton leftIcon={<PiCopyBold />} onClick={handleCopy}> <Button leftIcon={<PiCopyBold />} onClick={handleCopy}>
{t('common.copyError')} {t('common.copyError')}
</InvButton> </Button>
<Link href={url} isExternal> <Link href={url} isExternal>
<InvButton leftIcon={<PiArrowSquareOutBold />}> <Button leftIcon={<PiArrowSquareOutBold />}>
{t('accessibility.createIssue')} {t('accessibility.createIssue')}
</InvButton> </Button>
</Link> </Link>
</Flex> </Flex>
</Flex> </Flex>

View File

@ -1,12 +1,16 @@
import '@fontsource-variable/inter'; import '@fontsource-variable/inter';
import 'overlayscrollbars/overlayscrollbars.css'; import 'overlayscrollbars/overlayscrollbars.css';
import 'common/components/OverlayScrollbars/overlayscrollbars.css';
import { ChakraProvider, DarkMode, extendTheme } from '@chakra-ui/react'; import {
ChakraProvider,
DarkMode,
extendTheme,
theme as _theme,
TOAST_OPTIONS,
} from '@invoke-ai/ui';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { memo, useEffect, useMemo } from 'react'; import { memo, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { theme as invokeAITheme, TOAST_OPTIONS } from 'theme/theme';
type ThemeLocaleProviderProps = { type ThemeLocaleProviderProps = {
children: ReactNode; children: ReactNode;
@ -19,7 +23,7 @@ function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) {
const theme = useMemo(() => { const theme = useMemo(() => {
return extendTheme({ return extendTheme({
...invokeAITheme, ..._theme,
direction, direction,
}); });
}, [direction]); }, [direction]);

View File

@ -1,4 +1,4 @@
import { useToast } from '@chakra-ui/react'; import { useToast } from '@invoke-ai/ui';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { addToast, clearToastQueue } from 'features/system/store/systemSlice'; import { addToast, clearToastQueue } from 'features/system/store/systemSlice';
import type { MakeToastArg } from 'features/system/util/makeToast'; import type { MakeToastArg } from 'features/system/util/makeToast';

View File

@ -1,11 +1,10 @@
import { createStandaloneToast } from '@chakra-ui/react'; import { createStandaloneToast, theme, TOAST_OPTIONS } from '@invoke-ai/ui';
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize'; import { parseify } from 'common/util/serialize';
import { zPydanticValidationError } from 'features/system/store/zodSchemas'; import { zPydanticValidationError } from 'features/system/store/zodSchemas';
import { t } from 'i18next'; import { t } from 'i18next';
import { truncate, upperFirst } from 'lodash-es'; import { truncate, upperFirst } from 'lodash-es';
import { queueApi } from 'services/api/endpoints/queue'; import { queueApi } from 'services/api/endpoints/queue';
import { theme, TOAST_OPTIONS } from 'theme/theme';
import { startAppListening } from '..'; import { startAppListening } from '..';

View File

@ -1,4 +1,4 @@
import type { UseToastOptions } from '@chakra-ui/react'; import type { UseToastOptions } from '@invoke-ai/ui';
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { import {

View File

@ -1,4 +1,4 @@
import type { MenuItemProps } from '@chakra-ui/react'; import type { MenuItemProps } from '@invoke-ai/ui';
import { atom } from 'nanostores'; import { atom } from 'nanostores';
export type CustomStarUi = { export type CustomStarUi = {

View File

@ -1,5 +1,10 @@
import type { ChakraProps } from '@chakra-ui/react'; import type { ChakraProps } from '@invoke-ai/ui';
import { Flex } from '@chakra-ui/react'; import {
CompositeNumberInput,
Flex,
FormControl,
FormLabel,
} from '@invoke-ai/ui';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { RgbaColorPicker } from 'react-colorful'; import { RgbaColorPicker } from 'react-colorful';
@ -8,9 +13,6 @@ import type {
RgbaColor, RgbaColor,
} from 'react-colorful/dist/types'; } from 'react-colorful/dist/types';
import { InvControl } from './InvControl/InvControl';
import { InvNumberInput } from './InvNumberInput/InvNumberInput';
type IAIColorPickerProps = ColorPickerBaseProps<RgbaColor> & { type IAIColorPickerProps = ColorPickerBaseProps<RgbaColor> & {
withNumberInput?: boolean; withNumberInput?: boolean;
}; };
@ -61,8 +63,9 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
/> />
{withNumberInput && ( {withNumberInput && (
<Flex> <Flex>
<InvControl label="Red"> <FormControl>
<InvNumberInput <FormLabel>Red</FormLabel>
<CompositeNumberInput
value={color.r} value={color.r}
onChange={handleChangeR} onChange={handleChangeR}
min={0} min={0}
@ -71,9 +74,10 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
w={numberInputWidth} w={numberInputWidth}
defaultValue={90} defaultValue={90}
/> />
</InvControl> </FormControl>
<InvControl label="Green"> <FormControl>
<InvNumberInput <FormLabel>Green</FormLabel>
<CompositeNumberInput
value={color.g} value={color.g}
onChange={handleChangeG} onChange={handleChangeG}
min={0} min={0}
@ -82,9 +86,10 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
w={numberInputWidth} w={numberInputWidth}
defaultValue={90} defaultValue={90}
/> />
</InvControl> </FormControl>
<InvControl label="Blue"> <FormControl>
<InvNumberInput <FormLabel>Blue</FormLabel>
<CompositeNumberInput
value={color.b} value={color.b}
onChange={handleChangeB} onChange={handleChangeB}
min={0} min={0}
@ -93,9 +98,10 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
w={numberInputWidth} w={numberInputWidth}
defaultValue={255} defaultValue={255}
/> />
</InvControl> </FormControl>
<InvControl label="Alpha"> <FormControl>
<InvNumberInput <FormLabel>Alpha</FormLabel>
<CompositeNumberInput
value={color.a} value={color.a}
onChange={handleChangeA} onChange={handleChangeA}
step={0.1} step={0.1}
@ -104,7 +110,7 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
w={numberInputWidth} w={numberInputWidth}
defaultValue={1} defaultValue={1}
/> />
</InvControl> </FormControl>
</Flex> </Flex>
)} )}
</Flex> </Flex>

View File

@ -1,9 +1,5 @@
import type { import type { ChakraProps, FlexProps, SystemStyleObject } from '@invoke-ai/ui';
ChakraProps, import { Flex, Icon, Image } from '@invoke-ai/ui';
FlexProps,
SystemStyleObject,
} from '@chakra-ui/react';
import { Flex, Icon, Image } from '@chakra-ui/react';
import { import {
IAILoadingImageFallback, IAILoadingImageFallback,
IAINoContentFallback, IAINoContentFallback,

View File

@ -1,9 +1,8 @@
import type { SystemStyleObject } from '@chakra-ui/react'; import type { SystemStyleObject } from '@invoke-ai/ui';
import { IconButton } from '@invoke-ai/ui';
import type { MouseEvent, ReactElement } from 'react'; import type { MouseEvent, ReactElement } from 'react';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
import { InvIconButton } from './InvIconButton/InvIconButton';
type Props = { type Props = {
onClick: (event: MouseEvent<HTMLButtonElement>) => void; onClick: (event: MouseEvent<HTMLButtonElement>) => void;
tooltip: string; tooltip: string;
@ -26,7 +25,7 @@ const IAIDndImageIcon = (props: Props) => {
transitionDuration: 'normal', transitionDuration: 'normal',
fill: 'base.100', fill: 'base.100',
_hover: { fill: 'base.50' }, _hover: { fill: 'base.50' },
filter: 'drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-800))', filter: 'drop-shadow(0px 0px 0.1rem var(--invoke-colors-base-800))',
}, },
...styleOverrides, ...styleOverrides,
}), }),
@ -34,7 +33,7 @@ const IAIDndImageIcon = (props: Props) => {
); );
return ( return (
<InvIconButton <IconButton
onClick={onClick} onClick={onClick}
aria-label={tooltip} aria-label={tooltip}
tooltip={tooltip} tooltip={tooltip}

View File

@ -1,5 +1,5 @@
import type { BoxProps } from '@chakra-ui/react'; import type { BoxProps } from '@invoke-ai/ui';
import { Box } from '@chakra-ui/react'; import { Box } from '@invoke-ai/ui';
import { useDraggableTypesafe } from 'features/dnd/hooks/typesafeHooks'; import { useDraggableTypesafe } from 'features/dnd/hooks/typesafeHooks';
import type { TypesafeDraggableData } from 'features/dnd/types'; import type { TypesafeDraggableData } from 'features/dnd/types';
import { memo, useRef } from 'react'; import { memo, useRef } from 'react';

View File

@ -1,4 +1,4 @@
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@invoke-ai/ui';
import type { AnimationProps } from 'framer-motion'; import type { AnimationProps } from 'framer-motion';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';

View File

@ -1,4 +1,4 @@
import { Box } from '@chakra-ui/react'; import { Box } from '@invoke-ai/ui';
import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks'; import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks';
import type { TypesafeDroppableData } from 'features/dnd/types'; import type { TypesafeDroppableData } from 'features/dnd/types';
import { isValidDrop } from 'features/dnd/util/isValidDrop'; import { isValidDrop } from 'features/dnd/util/isValidDrop';

View File

@ -1,5 +1,5 @@
import type { SystemStyleObject } from '@chakra-ui/react'; import type { SystemStyleObject } from '@invoke-ai/ui';
import { Box, Skeleton } from '@chakra-ui/react'; import { Box, Skeleton } from '@invoke-ai/ui';
import { memo } from 'react'; import { memo } from 'react';
const skeletonStyles: SystemStyleObject = { const skeletonStyles: SystemStyleObject = {

View File

@ -1,11 +1,9 @@
import type { As, FlexProps, StyleProps } from '@chakra-ui/react'; import type { As, ChakraProps, FlexProps } from '@invoke-ai/ui';
import { Flex, Icon, Skeleton, Spinner } from '@chakra-ui/react'; import { Flex, Icon, Skeleton, Spinner, Text } from '@invoke-ai/ui';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
import { PiImageBold } from 'react-icons/pi'; import { PiImageBold } from 'react-icons/pi';
import type { ImageDTO } from 'services/api/types'; import type { ImageDTO } from 'services/api/types';
import { InvText } from './InvText/wrapper';
type Props = { image: ImageDTO | undefined }; type Props = { image: ImageDTO | undefined };
export const IAILoadingImageFallback = memo((props: Props) => { export const IAILoadingImageFallback = memo((props: Props) => {
@ -39,7 +37,7 @@ IAILoadingImageFallback.displayName = 'IAILoadingImageFallback';
type IAINoImageFallbackProps = FlexProps & { type IAINoImageFallbackProps = FlexProps & {
label?: string; label?: string;
icon?: As | null; icon?: As | null;
boxSize?: StyleProps['boxSize']; boxSize?: ChakraProps['boxSize'];
}; };
export const IAINoContentFallback = memo((props: IAINoImageFallbackProps) => { export const IAINoContentFallback = memo((props: IAINoImageFallbackProps) => {
@ -66,9 +64,9 @@ export const IAINoContentFallback = memo((props: IAINoImageFallbackProps) => {
<Flex sx={styles} {...rest}> <Flex sx={styles} {...rest}>
{icon && <Icon as={icon} boxSize={boxSize} opacity={0.7} />} {icon && <Icon as={icon} boxSize={boxSize} opacity={0.7} />}
{props.label && ( {props.label && (
<InvText textAlign="center" fontSize="md"> <Text textAlign="center" fontSize="md">
{props.label} {props.label}
</InvText> </Text>
)} )}
</Flex> </Flex>
); );
@ -102,7 +100,7 @@ export const IAINoContentFallbackWithSpinner = memo(
return ( return (
<Flex sx={styles} {...rest}> <Flex sx={styles} {...rest}>
<Spinner size="xl" /> <Spinner size="xl" />
{props.label && <InvText textAlign="center">{props.label}</InvText>} {props.label && <Text textAlign="center">{props.label}</Text>}
</Flex> </Flex>
); );
} }

View File

@ -1,15 +1,18 @@
import { Divider, Flex, Image, Portal } from '@chakra-ui/react';
import { useAppSelector } from 'app/store/storeHooks';
import { InvButton } from 'common/components/InvButton/InvButton';
import { InvHeading } from 'common/components/InvHeading/wrapper';
import { import {
InvPopover, Button,
InvPopoverBody, Divider,
InvPopoverCloseButton, Flex,
InvPopoverContent, Heading,
InvPopoverTrigger, Image,
} from 'common/components/InvPopover/wrapper'; Popover,
import { InvText } from 'common/components/InvText/wrapper'; PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverTrigger,
Portal,
Text,
} from '@invoke-ai/ui';
import { useAppSelector } from 'app/store/storeHooks';
import { merge, omit } from 'lodash-es'; import { merge, omit } from 'lodash-es';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
@ -47,7 +50,7 @@ const IAIInformationalPopover = ({
} }
return ( return (
<InvPopover <Popover
isLazy isLazy
closeOnBlur={false} closeOnBlur={false}
trigger="hover" trigger="hover"
@ -57,26 +60,26 @@ const IAIInformationalPopover = ({
placement="top" placement="top"
{...popoverProps} {...popoverProps}
> >
<InvPopoverTrigger>{children}</InvPopoverTrigger> <PopoverTrigger>{children}</PopoverTrigger>
{inPortal ? ( {inPortal ? (
<Portal> <Portal>
<PopoverContent data={data} feature={feature} /> <Content data={data} feature={feature} />
</Portal> </Portal>
) : ( ) : (
<PopoverContent data={data} feature={feature} /> <Content data={data} feature={feature} />
)} )}
</InvPopover> </Popover>
); );
}; };
export default memo(IAIInformationalPopover); export default memo(IAIInformationalPopover);
type PopoverContentProps = { type ContentProps = {
data?: PopoverData; data?: PopoverData;
feature: Feature; feature: Feature;
}; };
const PopoverContent = ({ data, feature }: PopoverContentProps) => { const Content = ({ data, feature }: ContentProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const heading = useMemo<string | undefined>( const heading = useMemo<string | undefined>(
@ -100,13 +103,13 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
}, [data?.href]); }, [data?.href]);
return ( return (
<InvPopoverContent w={96}> <PopoverContent w={96}>
<InvPopoverCloseButton /> <PopoverCloseButton />
<InvPopoverBody> <PopoverBody>
<Flex gap={2} flexDirection="column" alignItems="flex-start"> <Flex gap={2} flexDirection="column" alignItems="flex-start">
{heading && ( {heading && (
<> <>
<InvHeading size="sm">{heading}</InvHeading> <Heading size="sm">{heading}</Heading>
<Divider /> <Divider />
</> </>
)} )}
@ -124,12 +127,12 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
</> </>
)} )}
{paragraphs.map((p) => ( {paragraphs.map((p) => (
<InvText key={p}>{p}</InvText> <Text key={p}>{p}</Text>
))} ))}
{data?.href && ( {data?.href && (
<> <>
<Divider /> <Divider />
<InvButton <Button
pt={1} pt={1}
onClick={handleClick} onClick={handleClick}
leftIcon={<PiArrowSquareOutBold />} leftIcon={<PiArrowSquareOutBold />}
@ -137,11 +140,11 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
variant="link" variant="link"
> >
{t('common.learnMore') ?? heading} {t('common.learnMore') ?? heading}
</InvButton> </Button>
</> </>
)} )}
</Flex> </Flex>
</InvPopoverBody> </PopoverBody>
</InvPopoverContent> </PopoverContent>
); );
}; };

View File

@ -1,4 +1,4 @@
import type { PopoverProps } from '@chakra-ui/react'; import type { PopoverProps } from '@invoke-ai/ui';
export type Feature = export type Feature =
| 'clipSkip' | 'clipSkip'

View File

@ -1,4 +1,4 @@
import { Badge, Flex } from '@chakra-ui/react'; import { Badge, Flex } from '@invoke-ai/ui';
import { memo } from 'react'; import { memo } from 'react';
import type { ImageDTO } from 'services/api/types'; import type { ImageDTO } from 'services/api/types';

View File

@ -1,4 +1,4 @@
import { Box, Flex, Heading } from '@chakra-ui/react'; import { Box, Flex, Heading } from '@invoke-ai/ui';
import type { AnimationProps } from 'framer-motion'; import type { AnimationProps } from 'framer-motion';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { memo } from 'react'; import { memo } from 'react';

View File

@ -1,70 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { InvText } from 'common/components/InvText/wrapper';
import { InvAccordionButton } from './InvAccordionButton';
import type { InvAccordionProps } from './types';
import { InvAccordion, InvAccordionItem, InvAccordionPanel } from './wrapper';
const meta: Meta<typeof InvAccordion> = {
title: 'Primitives/InvAccordion',
tags: ['autodocs'],
component: InvAccordion,
args: {
colorScheme: 'base',
},
};
export default meta;
type Story = StoryObj<typeof InvAccordion>;
const Component = (props: InvAccordionProps) => {
return (
<InvAccordion {...props} defaultIndex={[0]} allowMultiple>
<InvAccordionItem>
<InvAccordionButton badges={['and', 'i', 'said']}>
Section 1 title
</InvAccordionButton>
<InvAccordionPanel p={4}>
<InvText>
25 years and my life is still Tryin&apos; to get up that great big
hill of hope For a destination I realized quickly when I knew I
should That the world was made up of this brotherhood of man For
whatever that means
</InvText>
</InvAccordionPanel>
</InvAccordionItem>
<InvAccordionItem>
<InvAccordionButton badges={['heeeyyyyyy']}>
Section 1 title
</InvAccordionButton>
<InvAccordionPanel p={4}>
<InvText>
And so I cry sometimes when I&apos;m lying in bed Just to get it all
out what&apos;s in my head And I, I am feeling a little peculiar And
so I wake in the morning and I step outside And I take a deep breath
and I get real high And I scream from the top of my lungs
&quot;What&apos;s going on?&quot;
</InvText>
</InvAccordionPanel>
</InvAccordionItem>
<InvAccordionItem>
<InvAccordionButton badges={["what's", 'goin', 'on', '?']}>
Section 2 title
</InvAccordionButton>
<InvAccordionPanel p={4}>
<InvText>
And I say, hey-ey-ey Hey-ey-ey I said &quot;Hey, a-what&apos;s going
on?&quot; And I say, hey-ey-ey Hey-ey-ey I said &quot;Hey,
a-what&apos;s going on?&quot;
</InvText>
</InvAccordionPanel>
</InvAccordionItem>
</InvAccordion>
);
};
export const Default: Story = {
render: Component,
};

View File

@ -1,31 +0,0 @@
import {
AccordionButton as ChakraAccordionButton,
Spacer,
} from '@chakra-ui/react';
import { InvBadge } from 'common/components/InvBadge/wrapper';
import { truncate } from 'lodash-es';
import { useMemo } from 'react';
import type { InvAccordionButtonProps } from './types';
import { InvAccordionIcon } from './wrapper';
export const InvAccordionButton = (props: InvAccordionButtonProps) => {
const { children, badges: _badges, ...rest } = props;
const badges = useMemo<string[] | undefined>(
() =>
_badges?.map((b) => truncate(String(b), { length: 24, omission: '...' })),
[_badges]
);
return (
<ChakraAccordionButton {...rest}>
{children}
<Spacer />
{badges?.map((b, i) => (
<InvBadge key={`${b}.${i}`} colorScheme="invokeBlue">
{b}
</InvBadge>
))}
<InvAccordionIcon />
</ChakraAccordionButton>
);
};

View File

@ -1,71 +0,0 @@
import { accordionAnatomy as parts } from '@chakra-ui/anatomy';
import {
createMultiStyleConfigHelpers,
defineStyle,
} from '@chakra-ui/styled-system';
const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys);
const invokeAIContainer = defineStyle({
border: 'none',
bg: 'base.850',
borderRadius: 'base',
':has(&div &button:hover)': { bg: 'base.800' },
transitionProperty: 'common',
transitionDuration: '0.1s',
});
const invokeAIButton = defineStyle((_props) => {
return {
gap: 2,
fontWeight: 'semibold',
fontSize: 'sm',
border: 'none',
borderRadius: 'base',
color: 'base.300',
_hover: {},
_expanded: {
borderBottomRadius: 'none',
},
};
});
const invokeAIPanel = defineStyle((props) => {
const { colorScheme: c } = props;
return {
bg: `${c}.800`,
borderRadius: 'base',
p: 0,
transitionProperty: 'common',
transitionDuration: '0.1s',
};
});
const invokeAIIcon = defineStyle({
ms: 2,
});
const invokeAI = definePartsStyle((props) => ({
container: invokeAIContainer,
button: invokeAIButton(props),
panel: invokeAIPanel(props),
icon: invokeAIIcon,
}));
const baseStyle = definePartsStyle(() => ({
root: {
display: 'flex',
flexDirection: 'column',
gap: 4,
},
}));
export const accordionTheme = defineMultiStyleConfig({
baseStyle,
variants: { invokeAI },
defaultProps: {
variant: 'invokeAI',
colorScheme: 'base',
},
});

View File

@ -1,11 +0,0 @@
import type { AccordionButtonProps as ChakraAccordionButtonProps } from '@chakra-ui/react';
export type {
AccordionIconProps as InvAccordionIconProps,
AccordionItemProps as InvAccordionItemProps,
AccordionPanelProps as InvAccordionPanelProps,
AccordionProps as InvAccordionProps,
} from '@chakra-ui/react';
export type InvAccordionButtonProps = ChakraAccordionButtonProps & {
badges?: (string | number)[];
};

View File

@ -1,6 +0,0 @@
export {
Accordion as InvAccordion,
AccordionIcon as InvAccordionIcon,
AccordionItem as InvAccordionItem,
AccordionPanel as InvAccordionPanel,
} from '@chakra-ui/react';

View File

@ -1,4 +0,0 @@
/**
* AlertDialog is a chakra Modal internally and uses those props.
*/
export type { AlertDialogProps as InvAlertDialogProps } from '@chakra-ui/react';

View File

@ -1,9 +0,0 @@
export {
AlertDialog as InvAlertDialog,
AlertDialogBody as InvAlertDialogBody,
AlertDialogCloseButton as InvAlertDialogCloseButton,
AlertDialogContent as InvAlertDialogContent,
AlertDialogFooter as InvAlertDialogFooter,
AlertDialogHeader as InvAlertDialogHeader,
AlertDialogOverlay as InvAlertDialogOverlay,
} from '@chakra-ui/react';

View File

@ -1,4 +1,4 @@
import { Box, forwardRef, Textarea as ChakraTextarea } from '@chakra-ui/react'; import { Box, forwardRef, Textarea as ChakraTextarea } from '@invoke-ai/ui';
import { useGlobalModifiersSetters } from 'common/hooks/useGlobalModifiers'; import { useGlobalModifiersSetters } from 'common/hooks/useGlobalModifiers';
import { stopPastePropagation } from 'common/util/stopPastePropagation'; import { stopPastePropagation } from 'common/util/stopPastePropagation';
import type { KeyboardEvent } from 'react'; import type { KeyboardEvent } from 'react';

View File

@ -1,4 +1,4 @@
import type { TextareaProps as ChakraTextareaProps } from '@chakra-ui/react'; import type { TextareaProps as ChakraTextareaProps } from '@invoke-ai/ui';
import type { TextareaAutosizeProps } from 'react-textarea-autosize'; import type { TextareaAutosizeProps } from 'react-textarea-autosize';
export type InvAutosizeTextareaProps = Omit< export type InvAutosizeTextareaProps = Omit<

View File

@ -1,24 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { InvBadgeProps } from './types';
import { InvBadge } from './wrapper';
const meta: Meta<typeof InvBadge> = {
title: 'Primitives/InvBadge',
tags: ['autodocs'],
component: InvBadge,
args: {
colorScheme: 'base',
},
};
export default meta;
type Story = StoryObj<typeof InvBadge>;
const Component = (props: InvBadgeProps) => {
return <InvBadge {...props}>Invoke</InvBadge>;
};
export const Default: Story = {
render: Component,
};

View File

@ -1,25 +0,0 @@
import { defineStyle, defineStyleConfig } from '@chakra-ui/react';
const baseStyle = defineStyle((props) => ({
fontSize: 9,
px: 2,
py: 1,
minW: 4,
lineHeight: 1,
borderRadius: 'sm',
bg: `${props.colorScheme}.200`,
color: 'base.900',
fontWeight: 'bold',
letterSpacing: 0.6,
wordBreak: 'break-all',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
}));
export const badgeTheme = defineStyleConfig({
baseStyle,
defaultProps: {
colorScheme: 'base',
},
});

View File

@ -1,3 +0,0 @@
import type { BadgeProps as ChakraBadgeProps } from '@chakra-ui/react';
export type InvBadgeProps = ChakraBadgeProps;

View File

@ -1 +0,0 @@
export { Badge as InvBadge } from '@chakra-ui/react';

View File

@ -1,99 +0,0 @@
import { Flex } from '@chakra-ui/layout';
import type { Meta, StoryObj } from '@storybook/react';
import { InvHeading } from 'common/components/InvHeading/wrapper';
import { IoSparkles } from 'react-icons/io5';
import { InvButton } from './InvButton';
import type { InvButtonProps } from './types';
const meta: Meta<typeof InvButton> = {
title: 'Primitives/InvButton',
tags: ['autodocs'],
component: InvButton,
parameters: {
controls: { expanded: true },
},
argTypes: {
isLoading: {
defaultValue: false,
control: { type: 'boolean' },
},
isDisabled: {
defaultValue: false,
control: { type: 'boolean' },
},
},
};
export default meta;
type Story = StoryObj<typeof InvButton>;
const colorSchemes = [
'base',
'invokeYellow',
'invokeRed',
'invokeGreen',
'invokeBlue',
] as const;
const variants = ['solid', 'outline', 'ghost', 'link'] as const;
const sizes = ['xs', 'sm', 'md', 'lg'] as const;
const Component = (props: InvButtonProps) => {
return (
<Flex gap={4} flexDir="column">
{sizes.map((size) => (
<>
<InvHeading>Size: {size}</InvHeading>
<Flex key={size} gap={4} flexDir="column">
{colorSchemes.map((colorScheme) => (
<Flex key={colorScheme} gap={4}>
{variants.map((variant) => (
<>
<InvButton
size={size}
key={`${variant}${colorScheme}`}
variant={variant}
colorScheme={colorScheme}
{...props}
>
{variant}
</InvButton>
{['solid', 'outline'].includes(variant) && (
<InvButton
size={size}
key={`${variant}${colorScheme}leftIcon`}
variant={variant}
colorScheme={colorScheme}
leftIcon={<IoSparkles />}
{...props}
>
{variant}
</InvButton>
)}
{['solid', 'outline'].includes(variant) && (
<InvButton
size={size}
key={`${variant}${colorScheme}rightIcon`}
variant={variant}
colorScheme={colorScheme}
rightIcon={<IoSparkles />}
{...props}
>
{variant}
</InvButton>
)}
</>
))}
</Flex>
))}
</Flex>
</>
))}
</Flex>
);
};
export const Default: Story = {
render: Component,
};

View File

@ -1,37 +0,0 @@
import { Button, forwardRef } from '@chakra-ui/react';
import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
import { memo } from 'react';
import type { InvButtonProps } from './types';
export const InvButton = memo(
forwardRef<InvButtonProps, typeof Button>(
({ isChecked, tooltip, children, ...rest }: InvButtonProps, ref) => {
if (tooltip) {
return (
<InvTooltip label={tooltip}>
<Button
ref={ref}
colorScheme={isChecked ? 'invokeBlue' : 'base'}
{...rest}
>
{children}
</Button>
</InvTooltip>
);
}
return (
<Button
ref={ref}
colorScheme={isChecked ? 'invokeBlue' : 'base'}
{...rest}
>
{children}
</Button>
);
}
)
);
InvButton.displayName = 'InvButton';

View File

@ -1,228 +0,0 @@
import type { StyleFunctionProps } from '@chakra-ui/react';
import { defineStyle, defineStyleConfig } from '@chakra-ui/react';
import { buttonVariantPromptOverlay } from 'features/parameters/components/Prompts/theme';
type Variant = `solid` | `outline` | `ghost` | `link`;
const getBorders = (
props: StyleFunctionProps,
variant: Variant
): { borderWidth: string; borderStyle: string } => {
if (variant !== 'outline') {
return {
borderWidth: '0px',
borderStyle: 'none',
};
}
if (props.size === 'lg') {
return {
borderWidth: '1px',
borderStyle: 'solid',
};
}
if (props.size === 'md') {
return {
borderWidth: '1px',
borderStyle: 'solid',
};
}
return {
borderWidth: '1px',
borderStyle: 'solid',
};
};
const getColors = (
props: StyleFunctionProps,
variant: Variant
): {
bg: string;
bgDisabled: string;
bgHover: string;
fg: string;
fgDisabled: string;
fgHover: string;
borderColor: string;
borderColorDisabled: string;
borderColorHover: string;
} => {
const { colorScheme: c } = props;
const bgBase = 'base.400';
const bgColor = c === 'invokeYellow' ? `${c}.500` : `${c}.400`;
const bgBaseHover = 'base.300';
const bgColorHover = c === 'invokeYellow' ? `${c}.300` : `${c}.300`;
const notSolidFg = {
fg: c === 'base' ? 'base.300' : bgColor,
fgHover: c === 'base' ? 'base.50' : bgColorHover,
fgDisabled: 'base.600',
};
const noBg = { bg: 'none', bgHover: 'none', bgDisabled: 'none' };
const noBorder = {
borderColor: 'none',
borderColorDisabled: 'none',
borderColorHover: 'none',
};
if (variant === 'ghost') {
return {
...notSolidFg,
...noBg,
...noBorder,
bgHover: 'baseAlpha.200',
};
}
if (variant === 'link') {
return {
...notSolidFg,
...noBg,
...noBorder,
};
}
if (variant === 'outline') {
return {
...notSolidFg,
...noBg,
borderColor: c === 'invokeYellow' ? `${c}Alpha.500` : `${c}Alpha.400`,
borderColorDisabled: 'base.600',
borderColorHover:
c === 'invokeYellow' ? `${c}Alpha.700` : `${c}Alpha.600`,
};
}
// solid
return {
bg: c === 'base' ? bgBase : bgColor,
bgHover: c === 'base' ? bgBaseHover : bgColorHover,
bgDisabled: 'base.600',
fg: 'base.900',
fgHover: 'base.900',
fgDisabled: 'base.750',
...noBorder,
};
};
const getStyles = (
props: StyleFunctionProps,
variant: 'ghost' | 'solid' | 'outline' | 'link'
) => {
const { borderWidth, borderStyle } = getBorders(props, variant);
const {
bg,
bgDisabled,
bgHover,
fg,
fgDisabled,
fgHover,
borderColor,
borderColorDisabled,
borderColorHover,
} = getColors(props, variant);
const _disabled = {
bg: bgDisabled,
color: fgDisabled,
opacity: 1,
borderColor: borderColorDisabled,
svg: {
fill: fgDisabled,
},
_hover: {
bg: bgDisabled,
color: fgDisabled,
borderColor: borderColorDisabled,
svg: {
fill: fgDisabled,
},
},
};
const _base = {
bg: bg,
color: fg,
borderWidth: borderWidth,
borderStyle: borderStyle,
borderColor: borderColor,
svg: {
fill: fg,
},
};
return {
..._base,
_hover: {
bg: bgHover,
color: fgHover,
borderColor: borderColorHover,
svg: {
fill: fgHover,
},
_disabled,
},
_active: { ..._base },
_disabled,
} as const;
};
export const buttonTheme = defineStyleConfig({
baseStyle: {
fontWeight: 'semibold',
svg: {
transitionProperty: 'all',
transitionDuration: 'faster',
},
},
sizes: {
sm: {
fontSize: 'sm',
px: 3,
py: 2,
},
md: {
fontSize: 'md',
px: 4,
py: 2,
},
lg: {
fontSize: 'lg',
px: 4,
py: 2,
h: 12,
},
},
variants: {
solid: defineStyle((props) => getStyles(props, 'solid')),
appTab: defineStyle((_props) => {
// Has no disabled, invalid, loading, etc state
return {
bg: 'none',
svg: {
fill: 'base.600',
},
_hover: {
bg: 'none',
svg: {
fill: 'base.400',
},
},
'&[data-selected="true"]': {
bg: 'none',
svg: {
fill: 'base.100',
},
},
};
}),
outline: defineStyle((props) => getStyles(props, 'outline')),
ghost: defineStyle((props) => getStyles(props, 'ghost')),
link: defineStyle((props) => getStyles(props, 'link')),
promptOverlay: buttonVariantPromptOverlay,
},
defaultProps: {
variant: 'solid',
colorScheme: 'base',
size: 'md',
},
});

View File

@ -1,7 +0,0 @@
import type { ButtonProps } from '@chakra-ui/react';
import type { ReactNode } from 'react';
export type InvButtonProps = ButtonProps & {
isChecked?: boolean;
tooltip?: ReactNode;
};

View File

@ -1,47 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { InvButton } from 'common/components/InvButton/InvButton';
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
import { FaImage } from 'react-icons/fa';
import { InvButtonGroup } from './InvButtonGroup';
import type { InvButtonGroupProps } from './types';
const meta: Meta<typeof InvButtonGroup> = {
title: 'Primitives/InvButtonGroup',
tags: ['autodocs'],
component: InvButtonGroup,
args: {
colorScheme: 'base',
},
};
export default meta;
type Story = StoryObj<typeof InvButtonGroup>;
const Component = (props: InvButtonGroupProps) => {
return (
<InvButtonGroup {...props}>
<InvButton>Test</InvButton>
<InvButton>Test</InvButton>
<InvButton>Test</InvButton>
</InvButtonGroup>
);
};
const ComponentWithIconButtons = (props: InvButtonGroupProps) => {
return (
<InvButtonGroup {...props}>
<InvIconButton aria-label="test" icon={<FaImage />} />
<InvIconButton aria-label="test" icon={<FaImage />} />
<InvIconButton aria-label="test" icon={<FaImage />} />
</InvButtonGroup>
);
};
export const Default: Story = {
render: Component,
};
export const WithIconButtons: Story = {
render: ComponentWithIconButtons,
};

View File

@ -1,14 +0,0 @@
import { ButtonGroup, forwardRef } from '@chakra-ui/react';
import { memo } from 'react';
import type { InvButtonGroupProps } from './types';
export const InvButtonGroup = memo(
forwardRef<InvButtonGroupProps, typeof ButtonGroup>(
({ isAttached = true, ...rest }: InvButtonGroupProps, ref) => {
return <ButtonGroup ref={ref} isAttached={isAttached} {...rest} />;
}
)
);
InvButtonGroup.displayName = 'InvButtonGroup';

View File

@ -1 +0,0 @@
export type { ButtonGroupProps as InvButtonGroupProps } from '@chakra-ui/react';

View File

@ -1,24 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { InvCardProps } from './types';
import { InvCard } from './wrapper';
const meta: Meta<typeof InvCard> = {
title: 'Primitives/InvCard',
tags: ['autodocs'],
component: InvCard,
args: {
colorScheme: 'base',
},
};
export default meta;
type Story = StoryObj<typeof InvCard>;
const Component = (props: InvCardProps) => {
return <InvCard {...props}>Invoke</InvCard>;
};
export const Default: Story = {
render: Component,
};

View File

@ -1,11 +0,0 @@
import { cardAnatomy } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
import { cardVariantLora } from 'features/lora/components/styles';
const { defineMultiStyleConfig } = createMultiStyleConfigHelpers(
cardAnatomy.keys
);
export const cardTheme = defineMultiStyleConfig({
variants: { lora: cardVariantLora },
});

View File

@ -1,6 +0,0 @@
export type {
CardBodyProps as InvCardBodyProps,
CardFooterProps as InvCardFooterProps,
CardHeaderProps as InvCardHeaderProps,
CardProps as InvCardProps,
} from '@chakra-ui/react';

View File

@ -1,6 +0,0 @@
export {
Card as InvCard,
CardBody as InvCardBody,
CardFooter as InvCardFooter,
CardHeader as InvCardHeader,
} from '@chakra-ui/react';

View File

@ -1,21 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { InvCheckboxProps } from './types';
import { InvCheckbox } from './wrapper';
const meta: Meta<typeof InvCheckbox> = {
title: 'Primitives/InvCheckbox',
tags: ['autodocs'],
component: InvCheckbox,
};
export default meta;
type Story = StoryObj<typeof InvCheckbox>;
const Component = (props: InvCheckboxProps) => {
return <InvCheckbox {...props} />;
};
export const Default: Story = {
render: Component,
};

View File

@ -1,69 +0,0 @@
import { checkboxAnatomy as parts } from '@chakra-ui/anatomy';
import {
createMultiStyleConfigHelpers,
defineStyle,
} from '@chakra-ui/styled-system';
const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys);
const invokeAIControl = defineStyle((props) => {
const { colorScheme: c } = props;
return {
bg: 'base.700',
borderColor: 'base.600',
color: 'base.100',
_checked: {
bg: `${c}.500`,
borderColor: `${c}.500`,
color: `${c}.100`,
_hover: {
bg: `${c}.500`,
borderColor: `${c}.500`,
},
_disabled: {
borderColor: 'transparent',
bg: 'whiteAlpha.300',
color: 'whiteAlpha.500',
},
},
_indeterminate: {
bg: `${c}.600`,
borderColor: `${c}.600`,
color: `${c}.100`,
},
_disabled: {
bg: 'whiteAlpha.100',
borderColor: 'transparent',
},
_focusVisible: {
boxShadow: 'none',
outline: 'none',
},
_invalid: {
borderColor: 'error.300',
},
};
});
const invokeAI = definePartsStyle((props) => ({
control: invokeAIControl(props),
}));
export const checkboxTheme = defineMultiStyleConfig({
variants: {
invokeAI: invokeAI,
},
defaultProps: {
variant: 'invokeAI',
colorScheme: 'blue',
},
});

View File

@ -1,3 +0,0 @@
import type { CheckboxProps as ChakraCheckboxProps } from '@chakra-ui/react';
export interface InvCheckboxProps extends ChakraCheckboxProps {}

View File

@ -1 +0,0 @@
export { Checkbox as InvCheckbox } from '@chakra-ui/react';

View File

@ -1,76 +0,0 @@
import {
InvAlertDialog,
InvAlertDialogBody,
InvAlertDialogContent,
InvAlertDialogFooter,
InvAlertDialogHeader,
InvAlertDialogOverlay,
} from 'common/components/InvAlertDialog/wrapper';
import { InvButton } from 'common/components/InvButton/InvButton';
import { memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import type { InvConfirmationAlertDialogProps } from './types';
/**
* This component is a wrapper around InvAlertDialog that provides a confirmation dialog.
* Its state must be managed externally using chakra's `useDisclosure()` hook.
*/
export const InvConfirmationAlertDialog = memo(
(props: InvConfirmationAlertDialogProps) => {
const { t } = useTranslation();
const {
acceptCallback,
cancelCallback,
acceptButtonText = t('common.accept'),
cancelButtonText = t('common.cancel'),
children,
title,
isOpen,
onClose,
} = props;
const cancelRef = useRef<HTMLButtonElement | null>(null);
const handleAccept = useCallback(() => {
acceptCallback();
onClose();
}, [acceptCallback, onClose]);
const handleCancel = useCallback(() => {
cancelCallback && cancelCallback();
onClose();
}, [cancelCallback, onClose]);
return (
<InvAlertDialog
isOpen={isOpen}
leastDestructiveRef={cancelRef}
onClose={onClose}
isCentered
>
<InvAlertDialogOverlay>
<InvAlertDialogContent>
<InvAlertDialogHeader fontSize="lg" fontWeight="bold">
{title}
</InvAlertDialogHeader>
<InvAlertDialogBody>{children}</InvAlertDialogBody>
<InvAlertDialogFooter>
<InvButton ref={cancelRef} onClick={handleCancel}>
{cancelButtonText}
</InvButton>
<InvButton colorScheme="error" onClick={handleAccept} ml={3}>
{acceptButtonText}
</InvButton>
</InvAlertDialogFooter>
</InvAlertDialogContent>
</InvAlertDialogOverlay>
</InvAlertDialog>
);
}
);
InvConfirmationAlertDialog.displayName = 'InvConfirmationAlertDialog';

View File

@ -1,16 +0,0 @@
import type { AlertDialogProps } from '@chakra-ui/react';
import type { PropsWithChildren } from 'react';
export type InvConfirmationAlertDialogProps = Omit<
AlertDialogProps,
'leastDestructiveRef' | 'isOpen' | 'onClose'
> &
PropsWithChildren<{
isOpen: boolean;
onClose: () => void;
acceptButtonText?: string;
acceptCallback: () => void;
cancelButtonText?: string;
cancelCallback?: () => void;
title: string;
}>;

View File

@ -1,52 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { InvMenuItem } from 'common/components/InvMenu/InvMenuItem';
import { InvMenuList } from 'common/components/InvMenu/InvMenuList';
import { InvText } from 'common/components/InvText/wrapper';
import { useCallback } from 'react';
import { FaCopy, FaDownload, FaTrash } from 'react-icons/fa6';
import { InvContextMenu } from './InvContextMenu';
const meta: Meta<typeof InvContextMenu> = {
title: 'Primitives/InvContextMenu',
tags: ['autodocs'],
component: InvContextMenu,
};
export default meta;
type Story = StoryObj<typeof InvContextMenu>;
const Component = () => {
const renderMenuFunc = useCallback(
() => (
<InvMenuList>
<InvMenuItem icon={<FaDownload />} command="⌘S">
Download
</InvMenuItem>
<InvMenuItem icon={<FaCopy />} command="⌘C">
Create a Copy
</InvMenuItem>
<InvMenuItem>Mark as Draft</InvMenuItem>
<InvMenuItem icon={<FaTrash />} isDestructive>
Delete
</InvMenuItem>
<InvMenuItem>Attend a Workshop</InvMenuItem>
</InvMenuList>
),
[]
);
return (
<InvContextMenu<HTMLParagraphElement> renderMenu={renderMenuFunc}>
{(ref) => (
<InvText ref={ref} p={5} bg="base.500">
Right-click me
</InvText>
)}
</InvContextMenu>
);
};
export const Default: Story = {
render: Component,
};

View File

@ -1,111 +0,0 @@
/**
* Adapted from https://github.com/lukasbach/chakra-ui-contextmenu
*/
import type {
ChakraProps,
MenuButtonProps,
MenuProps,
PortalProps,
} from '@chakra-ui/react';
import { Portal, useDisclosure, useEventListener } from '@chakra-ui/react';
import { InvMenu, InvMenuButton } from 'common/components/InvMenu/wrapper';
import { useGlobalMenuClose } from 'common/hooks/useGlobalMenuClose';
import { typedMemo } from 'common/util/typedMemo';
import { useCallback, useEffect, useRef, useState } from 'react';
export interface InvContextMenuProps<T extends HTMLElement = HTMLDivElement> {
renderMenu: () => JSX.Element | null;
children: (ref: React.MutableRefObject<T | null>) => JSX.Element | null;
menuProps?: Omit<MenuProps, 'children'> & { children?: React.ReactNode };
portalProps?: Omit<PortalProps, 'children'> & { children?: React.ReactNode };
menuButtonProps?: MenuButtonProps;
}
export const InvContextMenu = typedMemo(
<T extends HTMLElement = HTMLElement>(props: InvContextMenuProps<T>) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const [position, setPosition] = useState([-1, -1]);
const targetRef = useRef<T>(null);
const lastPositionRef = useRef([-1, -1]);
const timeoutRef = useRef(0);
useGlobalMenuClose(onClose);
const onContextMenu = useCallback(
(e: MouseEvent) => {
if (e.shiftKey) {
onClose();
return;
}
if (
targetRef.current?.contains(e.target as HTMLElement) ||
e.target === targetRef.current
) {
// clear pending delayed open
window.clearTimeout(timeoutRef.current);
e.preventDefault();
if (
lastPositionRef.current[0] !== e.pageX ||
lastPositionRef.current[1] !== e.pageY
) {
// if the mouse moved, we need to close, wait for animation and reopen the menu at the new position
onClose();
timeoutRef.current = window.setTimeout(() => {
onOpen();
setPosition([e.pageX, e.pageY]);
}, 100);
} else {
// else we can just open the menu at the current position
onOpen();
setPosition([e.pageX, e.pageY]);
}
}
lastPositionRef.current = [e.pageX, e.pageY];
},
[onClose, onOpen]
);
useEffect(
() => () => {
window.clearTimeout(timeoutRef.current);
},
[]
);
useEventListener('contextmenu', onContextMenu);
return (
<>
{props.children(targetRef)}
<Portal {...props.portalProps}>
<InvMenu
isLazy
isOpen={isOpen}
gutter={0}
placement="auto-end"
onClose={onClose}
{...props.menuProps}
>
<InvMenuButton
aria-hidden={true}
w={1}
h={1}
position="absolute"
left={position[0]}
top={position[1]}
cursor="default"
bg="transparent"
size="sm"
_hover={_hover}
pointerEvents="none"
{...props.menuButtonProps}
/>
{props.renderMenu()}
</InvMenu>
</Portal>
</>
);
}
);
const _hover: ChakraProps['_hover'] = { bg: 'transparent' };

View File

@ -1,95 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
import { InvSelect } from 'common/components/InvSelect/InvSelect';
import type { InvSelectOption } from 'common/components/InvSelect/types';
import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { useState } from 'react';
import { InvControl } from './InvControl';
import type { InvControlProps } from './types';
const meta: Meta<typeof InvControl> = {
title: 'Primitives/InvControl',
tags: ['autodocs'],
component: InvControl,
args: {
label: 'My Control',
isDisabled: false,
isInvalid: false,
w: 96,
},
};
export default meta;
type Story = StoryObj<typeof InvControl>;
const InvControlWithSliderComponent = (props: InvControlProps) => {
const [value, setValue] = useState(0);
return (
<InvControl {...props}>
<InvSlider value={value} min={0} max={10} step={1} onChange={setValue} />
</InvControl>
);
};
const InvControlWithSliderAndHelperTextComponent = (props: InvControlProps) => {
const [value, setValue] = useState(0);
return (
<InvControl {...props} helperText="This is some helpful text">
<InvSlider value={value} min={0} max={10} step={1} onChange={setValue} />
</InvControl>
);
};
const InvControlWithNumberInputComponent = (props: InvControlProps) => {
const [value, setValue] = useState(0);
return (
<InvControl {...props}>
<InvNumberInput
value={value}
min={0}
max={10}
step={1}
onChange={setValue}
/>
</InvControl>
);
};
const options: InvSelectOption[] = [
{
value: 'chocolate',
label: 'Chocolate',
},
{
value: 'strawberry',
label: 'Strawberry',
},
{
value: 'vanilla',
label: 'Vanilla',
},
];
const InvControlWithSelectComponent = (props: InvControlProps) => {
return (
<InvControl {...props}>
<InvSelect defaultValue={options[0]} options={options} />
</InvControl>
);
};
export const InvControlWithSlider: Story = {
render: InvControlWithSliderComponent,
};
export const InvControlWithSliderAndHelperText: Story = {
render: InvControlWithSliderAndHelperTextComponent,
};
export const InvControlWithNumberInput: Story = {
render: InvControlWithNumberInputComponent,
};
export const InvControlWithSelect: Story = {
render: InvControlWithSelectComponent,
};

View File

@ -1,72 +0,0 @@
import {
Flex,
FormControl as ChakraFormControl,
FormErrorMessage as ChakraFormErrorMessage,
FormHelperText as ChakraFormHelperText,
forwardRef,
} from '@chakra-ui/react';
import { InvControlGroupContext } from 'common/components/InvControl/InvControlGroup';
import { memo, useContext, useMemo } from 'react';
import { InvLabel } from './InvLabel';
import type { InvControlProps } from './types';
export const InvControl = memo(
forwardRef<InvControlProps, typeof ChakraFormControl>(
(props: InvControlProps, ref) => {
const {
children,
helperText,
feature,
orientation: _orientation,
renderInfoPopoverInPortal = true,
isDisabled: _isDisabled,
labelProps,
label,
error,
...formControlProps
} = props;
const ctx = useContext(InvControlGroupContext);
const orientation = useMemo(
() => _orientation ?? ctx.orientation,
[_orientation, ctx.orientation]
);
const isDisabled = useMemo(
() => _isDisabled ?? ctx.isDisabled,
[_isDisabled, ctx.isDisabled]
);
return (
<ChakraFormControl
ref={ref}
orientation={orientation}
isDisabled={isDisabled}
{...formControlProps}
{...ctx.controlProps}
>
<Flex className="invcontrol-label-wrapper">
{label && (
<InvLabel
feature={feature}
renderInfoPopoverInPortal={renderInfoPopoverInPortal}
{...labelProps}
>
{label}
</InvLabel>
)}
<Flex className="invcontrol-input-wrapper">{children}</Flex>
</Flex>
{helperText && (
<ChakraFormHelperText>{helperText}</ChakraFormHelperText>
)}
{error && <ChakraFormErrorMessage>{error}</ChakraFormErrorMessage>}
</ChakraFormControl>
);
}
)
);
InvControl.displayName = 'InvControl';

View File

@ -1,24 +0,0 @@
import type { FormControlProps, FormLabelProps } from '@chakra-ui/react';
import type { PropsWithChildren } from 'react';
import { createContext, memo } from 'react';
export type InvControlGroupProps = {
labelProps?: FormLabelProps;
controlProps?: FormControlProps;
isDisabled?: boolean;
orientation?: 'horizontal' | 'vertical';
};
export const InvControlGroupContext = createContext<InvControlGroupProps>({});
export const InvControlGroup = memo(
({ children, ...context }: PropsWithChildren<InvControlGroupProps>) => {
return (
<InvControlGroupContext.Provider value={context}>
{children}
</InvControlGroupContext.Provider>
);
}
);
InvControlGroup.displayName = 'InvControlGroup';

View File

@ -1,43 +0,0 @@
import { Flex, FormLabel, forwardRef } from '@chakra-ui/react';
import { useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
import { InvControlGroupContext } from 'common/components/InvControl/InvControlGroup';
import { memo, useContext } from 'react';
import type { InvLabelProps } from './types';
export const InvLabel = memo(
forwardRef<InvLabelProps, typeof FormLabel>(
(
{ feature, renderInfoPopoverInPortal, children, ...rest }: InvLabelProps,
ref
) => {
const shouldEnableInformationalPopovers = useAppSelector(
(s) => s.system.shouldEnableInformationalPopovers
);
const ctx = useContext(InvControlGroupContext);
if (feature && shouldEnableInformationalPopovers) {
return (
<IAIInformationalPopover
feature={feature}
inPortal={renderInfoPopoverInPortal}
>
<Flex as="span">
<FormLabel ref={ref} {...ctx.labelProps} {...rest}>
{children}
</FormLabel>
</Flex>
</IAIInformationalPopover>
);
}
return (
<FormLabel ref={ref} {...ctx.labelProps} {...rest}>
{children}
</FormLabel>
);
}
)
);
InvLabel.displayName = 'InvLabel';

View File

@ -1,89 +0,0 @@
import {
formAnatomy as formParts,
formErrorAnatomy as formErrorParts,
} from '@chakra-ui/anatomy';
import {
createMultiStyleConfigHelpers,
defineStyle,
defineStyleConfig,
} from '@chakra-ui/styled-system';
const {
definePartsStyle: defineFormPartsStyle,
defineMultiStyleConfig: defineFormMultiStyleConfig,
} = createMultiStyleConfigHelpers(formParts.keys);
const formBaseStyle = defineFormPartsStyle((props) => ({
container: {
flexDirection: 'column',
alignItems: 'flex-start',
gap: 4,
h: 'unset',
'> .invcontrol-label-wrapper': {
display: 'flex',
flexDirection: props.orientation === 'vertical' ? 'column' : 'row',
alignItems: props.orientation === 'vertical' ? 'flex-start' : 'center',
gap: props.orientation === 'vertical' ? 0 : 4,
minH: 8,
w: 'full',
_invalid: {
color: 'error.300',
},
'> .invcontrol-input-wrapper': {
w: 'full',
display: 'flex',
gap: 4,
justifyContent: 'flex-end',
},
},
},
helperText: {
w: 'full',
fontSize: 'sm',
color: 'base.400',
m: 0,
},
}));
export const formTheme = defineFormMultiStyleConfig({
baseStyle: formBaseStyle,
});
const formLabelBaseStyle = defineStyle(() => {
return {
fontSize: 'sm',
marginEnd: 0,
mb: 0,
flexShrink: 0,
flexGrow: 0,
fontWeight: 'semibold',
transitionProperty: 'common',
transitionDuration: 'normal',
whiteSpace: 'nowrap',
userSelect: 'none',
h: 'full',
alignItems: 'center',
_disabled: {
opacity: 0.4,
},
color: 'base.300',
_invalid: {
color: 'error.300',
},
};
});
export const formLabelTheme = defineStyleConfig({
baseStyle: formLabelBaseStyle,
});
const { defineMultiStyleConfig: defineFormErrorMultiStyleConfig } =
createMultiStyleConfigHelpers(formErrorParts.keys);
export const formErrorTheme = defineFormErrorMultiStyleConfig({
baseStyle: {
text: {
color: 'error.300',
},
},
});

View File

@ -1,22 +0,0 @@
import type {
FormControlProps as ChakraFormControlProps,
FormLabelProps as ChakraFormLabelProps,
} from '@chakra-ui/react';
import type { Feature } from 'common/components/IAIInformationalPopover/constants';
export type InvControlProps = ChakraFormControlProps & {
label?: string;
helperText?: string;
error?: string;
feature?: Feature;
renderInfoPopoverInPortal?: boolean;
labelProps?: Omit<
InvLabelProps,
'children' | 'feature' | 'renderInfoPopoverInPortal'
>;
};
export type InvLabelProps = ChakraFormLabelProps & {
feature?: Feature;
renderInfoPopoverInPortal?: boolean;
};

View File

@ -1,26 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { InvEditableProps } from './types';
import { InvEditable, InvEditableInput, InvEditablePreview } from './wrapper';
const meta: Meta<typeof InvEditable> = {
title: 'Primitives/InvEditable',
tags: ['autodocs'],
component: InvEditable,
};
export default meta;
type Story = StoryObj<typeof InvEditable>;
const Component = (props: InvEditableProps) => {
return (
<InvEditable defaultValue="Take some chakra" {...props}>
<InvEditablePreview />
<InvEditableInput />
</InvEditable>
);
};
export const Default: Story = {
render: Component,
};

View File

@ -1,62 +0,0 @@
import { editableAnatomy as parts } from '@chakra-ui/anatomy';
import {
createMultiStyleConfigHelpers,
defineStyle,
} from '@chakra-ui/styled-system';
const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys);
const baseStylePreview = defineStyle({
fontSize: 'sm',
borderRadius: 'md',
py: '1',
transitionProperty: 'common',
transitionDuration: 'normal',
color: 'base.100',
_invalid: {
color: 'error.300',
},
});
const baseStyleInput = defineStyle(() => ({
color: 'base.100',
fontSize: 'sm',
borderRadius: 'md',
py: '1',
transitionProperty: 'common',
transitionDuration: 'normal',
width: 'full',
_focusVisible: { boxShadow: 'none' },
_placeholder: { opacity: 0.6 },
'::selection': {
color: 'blue.900',
bg: 'blue.300',
},
}));
const baseStyleTextarea = defineStyle({
borderRadius: 'md',
py: '1',
transitionProperty: 'common',
transitionDuration: 'normal',
width: 'full',
_focusVisible: { boxShadow: 'outline' },
_placeholder: { opacity: 0.6 },
});
const invokeAI = definePartsStyle(() => ({
preview: baseStylePreview,
input: baseStyleInput(),
textarea: baseStyleTextarea,
}));
export const editableTheme = defineMultiStyleConfig({
variants: {
invokeAI,
},
defaultProps: {
size: 'sm',
variant: 'invokeAI',
},
});

View File

@ -1,6 +0,0 @@
export type {
EditableInputProps as InvEditableInputProps,
EditablePreviewProps as InvEditablePreviewProps,
EditableProps as InvEditableProps,
EditableTextareaProps as InvEditableTextareaProps,
} from '@chakra-ui/react';

View File

@ -1,6 +0,0 @@
export {
Editable as InvEditable,
EditableInput as InvEditableInput,
EditablePreview as InvEditablePreview,
EditableTextarea as InvEditableTextarea,
} from '@chakra-ui/react';

View File

@ -1,21 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { InvExpander } from './InvExpander';
import type { InvExpanderProps } from './types';
const meta: Meta<typeof InvExpander> = {
title: 'Primitives/InvExpander',
tags: ['autodocs'],
component: InvExpander,
};
export default meta;
type Story = StoryObj<typeof InvExpander>;
const Component = (props: InvExpanderProps) => {
return <InvExpander {...props}>Invoke</InvExpander>;
};
export const Default: Story = {
render: Component,
};

View File

@ -1,77 +0,0 @@
import { Divider, Flex } from '@chakra-ui/layout';
import type { SystemStyleObject } from '@chakra-ui/react';
import { Collapse, Icon, useDisclosure } from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks';
import type { InvExpanderProps } from 'common/components/InvExpander/types';
import { InvText } from 'common/components/InvText/wrapper';
import { expanderToggled } from 'features/parameters/store/actions';
import { t } from 'i18next';
import { useCallback } from 'react';
import { BiCollapseVertical, BiExpandVertical } from 'react-icons/bi';
const buttonStyles: SystemStyleObject = {
color: 'base.400',
borderColor: 'base.400',
transitionDuration: 'normal',
transitionProperty: 'common',
':hover, :hover *': {
transitionDuration: 'normal',
transitionProperty: 'common',
color: 'base.300',
borderColor: 'base.300',
},
};
export const InvExpander = ({
children,
label = t('common.advancedOptions'),
defaultIsOpen = false,
id,
}: InvExpanderProps) => {
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen });
const dispatch = useAppDispatch();
const onToggleExpander = useCallback(() => {
if (id) {
dispatch(expanderToggled({ id, isOpen }));
}
onToggle();
}, [id, dispatch, onToggle, isOpen]);
return (
<Flex flexDir="column" w="full">
<Flex
as="button"
flexDir="row"
alignItems="center"
gap={3}
py={4}
px={2}
onClick={onToggleExpander}
sx={buttonStyles}
>
<Divider w="unset" flexGrow={1} sx={buttonStyles} />
<Flex flexDir="row" alignItems="center" gap={2}>
<Icon
as={isOpen ? BiCollapseVertical : BiExpandVertical}
fontSize="14px"
sx={buttonStyles}
/>
<InvText
variant="subtext"
fontSize="sm"
fontWeight="semibold"
flexShrink={0}
sx={buttonStyles}
>
{label}
</InvText>
</Flex>
</Flex>
<Collapse in={isOpen} animateOpacity>
{children}
</Collapse>
</Flex>
);
};

View File

@ -1,7 +0,0 @@
import type { PropsWithChildren } from 'react';
export type InvExpanderProps = PropsWithChildren<{
label?: string;
defaultIsOpen?: boolean;
id?: string;
}>;

View File

@ -1,21 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { InvHeadingProps } from './types';
import { InvHeading } from './wrapper';
const meta: Meta<typeof InvHeading> = {
title: 'Primitives/InvHeading',
tags: ['autodocs'],
component: InvHeading,
};
export default meta;
type Story = StoryObj<typeof InvHeading>;
const Component = (props: InvHeadingProps) => {
return <InvHeading {...props}>Banana sushi is delectable!</InvHeading>;
};
export const Default: Story = {
render: Component,
};

View File

@ -1,11 +0,0 @@
import { defineStyle, defineStyleConfig } from '@chakra-ui/react';
const invokeBlue = defineStyle(() => ({
color: 'invokeBlue.300',
}));
export const headingTheme = defineStyleConfig({
variants: {
invokeBlue,
},
});

View File

@ -1 +0,0 @@
export type { HeadingProps as InvHeadingProps } from '@chakra-ui/react';

View File

@ -1 +0,0 @@
export { Heading as InvHeading } from '@chakra-ui/react';

View File

@ -1,25 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { FaBoltLightning } from 'react-icons/fa6';
import { InvIconButton } from './InvIconButton';
import type { InvIconButtonProps } from './types';
const meta: Meta<typeof InvIconButton> = {
title: 'Primitives/InvIconButton',
tags: ['autodocs'],
component: InvIconButton,
args: {
icon: <FaBoltLightning />,
},
};
export default meta;
type Story = StoryObj<typeof InvIconButton>;
const Component = (props: InvIconButtonProps) => {
return <InvIconButton {...props} />;
};
export const Default: Story = {
render: Component,
};

View File

@ -1,32 +0,0 @@
import { forwardRef, IconButton } from '@chakra-ui/react';
import type { InvIconButtonProps } from 'common/components/InvIconButton/types';
import { InvTooltip } from 'common/components/InvTooltip/InvTooltip';
import { memo } from 'react';
export const InvIconButton = memo(
forwardRef<InvIconButtonProps, typeof IconButton>(
({ isChecked, tooltip, ...rest }: InvIconButtonProps, ref) => {
if (tooltip) {
return (
<InvTooltip label={tooltip}>
<IconButton
ref={ref}
colorScheme={isChecked ? 'invokeBlue' : 'base'}
{...rest}
/>
</InvTooltip>
);
}
return (
<IconButton
ref={ref}
colorScheme={isChecked ? 'invokeBlue' : 'base'}
{...rest}
/>
);
}
)
);
InvIconButton.displayName = 'InvIconButton';

View File

@ -1,7 +0,0 @@
import type { IconButtonProps as ChakraIconButtonProps } from '@chakra-ui/react';
import type { ReactNode } from 'react';
export type InvIconButtonProps = ChakraIconButtonProps & {
isChecked?: boolean;
tooltip?: ReactNode;
};

View File

@ -1,21 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { InvInput } from './InvInput';
import type { InvInputProps } from './types';
const meta: Meta<typeof InvInput> = {
title: 'Primitives/InvInput',
tags: ['autodocs'],
component: InvInput,
};
export default meta;
type Story = StoryObj<typeof InvInput>;
const Component = (props: InvInputProps) => {
return <InvInput {...props} />;
};
export const Default: Story = {
render: Component,
};

View File

@ -1,30 +0,0 @@
import { forwardRef, Input } from '@chakra-ui/react';
import { useGlobalModifiersSetters } from 'common/hooks/useGlobalModifiers';
import { stopPastePropagation } from 'common/util/stopPastePropagation';
import type { KeyboardEvent } from 'react';
import { memo, useCallback } from 'react';
import type { InvInputProps } from './types';
export const InvInput = memo(
forwardRef<InvInputProps, typeof Input>((props: InvInputProps, ref) => {
const { setShift } = useGlobalModifiersSetters();
const onKeyUpDown = useCallback(
(e: KeyboardEvent<HTMLInputElement>) => {
setShift(e.shiftKey);
},
[setShift]
);
return (
<Input
ref={ref}
onPaste={stopPastePropagation}
onKeyUp={onKeyUpDown}
onKeyDown={onKeyUpDown}
{...props}
/>
);
})
);
InvInput.displayName = 'InvInput';

View File

@ -1,21 +0,0 @@
import { inputAnatomy as parts } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
import { getInputFilledStyles } from 'theme/util/getInputFilledStyles';
const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys);
export const inputTheme = defineMultiStyleConfig({
variants: {
filled: definePartsStyle((props) => ({
field: getInputFilledStyles(props),
})),
darkFilled: definePartsStyle((props) => ({
field: getInputFilledStyles(props),
})),
},
defaultProps: {
size: 'sm',
variant: 'filled',
},
});

View File

@ -1,3 +0,0 @@
import type { InputProps as ChakraInputProps } from '@chakra-ui/react';
export type InvInputProps = ChakraInputProps;

View File

@ -1,63 +0,0 @@
import { ChevronDownIcon } from '@chakra-ui/icons';
import type { Meta, StoryObj } from '@storybook/react';
import { InvButton } from 'common/components/InvButton/InvButton';
import { FaCopy, FaDownload, FaTrash } from 'react-icons/fa6';
import { InvMenuItem } from './InvMenuItem';
import { InvMenuList } from './InvMenuList';
import type { InvMenuProps } from './types';
import { InvMenu, InvMenuButton, InvMenuGroup } from './wrapper';
const meta: Meta<typeof InvMenu> = {
title: 'Primitives/InvMenu',
tags: ['autodocs'],
component: InvMenu,
args: {
colorScheme: 'base',
},
};
export default meta;
type Story = StoryObj<typeof InvMenu>;
const Component = (props: InvMenuProps) => {
return (
<InvMenu {...props}>
<InvMenuButton as={InvButton} rightIcon={<ChevronDownIcon />}>
Actions
</InvMenuButton>
<InvMenuList>
<InvMenuGroup title="Some Category">
<InvMenuItem icon={<FaDownload />} command="⌘S">
Download
</InvMenuItem>
<InvMenuItem icon={<FaCopy />} command="⌘C">
Create a Copy
</InvMenuItem>
<InvMenuItem>Mark as Draft</InvMenuItem>
<InvMenuItem icon={<FaTrash />} isDestructive>
Delete
</InvMenuItem>
<InvMenuItem>Attend a Workshop</InvMenuItem>
</InvMenuGroup>
<InvMenuGroup title="Another Category">
<InvMenuItem icon={<FaDownload />} command="⌘S">
Download
</InvMenuItem>
<InvMenuItem icon={<FaCopy />} command="⌘C">
Create a Copy
</InvMenuItem>
<InvMenuItem>Mark as Draft</InvMenuItem>
<InvMenuItem icon={<FaTrash />} isDestructive>
Delete
</InvMenuItem>
<InvMenuItem>Attend a Workshop</InvMenuItem>
</InvMenuGroup>
</InvMenuList>
</InvMenu>
);
};
export const Default: Story = {
render: Component,
};

View File

@ -1,31 +0,0 @@
import { SpinnerIcon } from '@chakra-ui/icons';
import { forwardRef, MenuItem as ChakraMenuItem } from '@chakra-ui/react';
import { memo } from 'react';
import { spinAnimation } from 'theme/animations';
import type { InvMenuItemProps } from './types';
export const InvMenuItem = memo(
forwardRef<InvMenuItemProps, typeof ChakraMenuItem>(
(props: InvMenuItemProps, ref) => {
const {
isDestructive = false,
isLoading = false,
isDisabled,
icon,
...rest
} = props;
return (
<ChakraMenuItem
ref={ref}
icon={isLoading ? <SpinnerIcon animation={spinAnimation} /> : icon}
isDisabled={isLoading || isDisabled}
data-destructive={isDestructive}
{...rest}
/>
);
}
)
);
InvMenuItem.displayName = 'InvMenuItem';

View File

@ -1,29 +0,0 @@
import {
forwardRef,
MenuList as ChakraMenuList,
Portal,
} from '@chakra-ui/react';
import { skipMouseEvent } from 'common/util/skipMouseEvent';
import { memo } from 'react';
import { menuListMotionProps } from './constants';
import type { InvMenuListProps } from './types';
export const InvMenuList = memo(
forwardRef<InvMenuListProps, typeof ChakraMenuList>(
(props: InvMenuListProps, ref) => {
return (
<Portal>
<ChakraMenuList
ref={ref}
motionProps={menuListMotionProps}
onContextMenu={skipMouseEvent}
{...props}
/>
</Portal>
);
}
)
);
InvMenuList.displayName = 'InvMenuList';

View File

@ -1,29 +0,0 @@
import type { MotionProps } from 'framer-motion';
/**
* Props for the animation of the MenuList.
*/
export const menuListMotionProps: MotionProps = {
variants: {
enter: {
visibility: 'visible',
opacity: 1,
scale: 1,
transition: {
duration: 0.07,
ease: [0.4, 0, 0.2, 1],
},
},
exit: {
transitionEnd: {
visibility: 'hidden',
},
opacity: 0,
scale: 0.8,
transition: {
duration: 0.07,
easings: 'easeOut',
},
},
},
};

View File

@ -1,86 +0,0 @@
import { menuAnatomy } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(menuAnatomy.keys);
// define the base component styles
const invokeAI = definePartsStyle(() => ({
// define the part you're going to style
button: {
// this will style the MenuButton component
bg: 'base.500',
color: 'base.100',
_hover: {
bg: 'base.600',
color: 'base.50',
fontWeight: 'semibold',
},
},
list: {
zIndex: 9999,
color: 'base.150',
bg: 'base.800',
shadow: 'dark-lg',
border: 'none',
p: 2,
},
item: {
// this will style the MenuItem and MenuItemOption components
borderRadius: 'sm',
fontSize: 'sm',
bg: 'base.800',
_hover: {
bg: 'base.700',
svg: {
opacity: 1,
},
},
_focus: {
bg: 'base.700',
},
svg: {
opacity: 0.7,
fontSize: 14,
},
"&[data-destructive='true']": {
color: 'error.300',
fill: 'error.300',
_hover: {
bg: 'error.600',
color: 'base.50',
fill: 'base.50',
},
},
"&[aria-selected='true']": {
fontWeight: 'semibold',
bg: 'blue.300 !important',
color: 'base.800 !important',
_hover: {
color: 'base.900 !important',
bg: 'blue.400 !important',
},
},
"&[aria-selected='true'] [data-option-desc='true']": {
color: 'base.800',
},
},
divider: {
borderColor: 'base.700',
},
groupTitle: {
m: 0,
px: 3,
py: 2,
color: 'base.500',
},
}));
export const menuTheme = defineMultiStyleConfig({
variants: {
invokeAI,
},
defaultProps: {
variant: 'invokeAI',
},
});

View File

@ -1,22 +0,0 @@
import type {
MenuButtonProps as ChakraMenuButtonProps,
MenuDividerProps as ChakraMenuDividerProps,
MenuGroupProps as ChakraMenuGroupProps,
MenuItemOptionProps as ChakraMenuItemOptionProps,
MenuItemProps as ChakraMenuItemProps,
MenuListProps as ChakraMenuListProps,
MenuOptionGroupProps as ChakraMenuOptionGroupProps,
MenuProps as ChakraMenuProps,
} from '@chakra-ui/react';
export type InvMenuProps = ChakraMenuProps;
export type InvMenuButtonProps = ChakraMenuButtonProps;
export type InvMenuListProps = ChakraMenuListProps;
export type InvMenuItemProps = ChakraMenuItemProps & {
isDestructive?: boolean;
isLoading?: boolean;
};
export type InvMenuItemOptionProps = ChakraMenuItemOptionProps;
export type InvMenuGroupProps = ChakraMenuGroupProps;
export type InvMenuOptionGroupProps = ChakraMenuOptionGroupProps;
export type InvMenuDividerProps = ChakraMenuDividerProps;

View File

@ -1,8 +0,0 @@
export {
Menu as InvMenu,
MenuButton as InvMenuButton,
MenuDivider as InvMenuDivider,
MenuGroup as InvMenuGroup,
MenuItemOption as InvMenuItemOption,
MenuOptionGroup as InvMenuOptionGroup,
} from '@chakra-ui/react';

View File

@ -1,61 +0,0 @@
import { useDisclosure } from '@chakra-ui/react';
import type { Meta, StoryObj } from '@storybook/react';
import { InvButton } from 'common/components/InvButton/InvButton';
import {
InvModal,
InvModalBody,
InvModalCloseButton,
InvModalContent,
InvModalFooter,
InvModalHeader,
InvModalOverlay,
} from './wrapper';
const meta: Meta<typeof InvModal> = {
title: 'Primitives/InvModal',
tags: ['autodocs'],
component: InvModal,
args: {
colorScheme: 'base',
},
};
export default meta;
type Story = StoryObj<typeof InvModal>;
const Component = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<>
<InvButton onClick={onOpen}>Open Modal</InvButton>
<InvModal isOpen={isOpen} onClose={onClose}>
<InvModalOverlay />
<InvModalContent>
<InvModalHeader>Modal Title</InvModalHeader>
<InvModalCloseButton />
<InvModalBody>
Slices of banana are caramelized with brown sugar and butter, then
rolled in sushi rice and topped with a drizzle of caramel sauce.
This variety offers a sweet and rich flavor, combining the
creaminess of banana with the indulgent taste of caramel.
</InvModalBody>
<InvModalFooter>
<InvButton colorScheme="base" mr={3} onClick={onClose}>
Close
</InvButton>
<InvButton colorScheme="green" variant="ghost">
Secondary Action
</InvButton>
</InvModalFooter>
</InvModalContent>
</InvModal>
</>
);
};
export const Default: Story = {
render: Component,
};

View File

@ -1,33 +0,0 @@
import { modalAnatomy as parts } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
const { defineMultiStyleConfig, definePartsStyle } =
createMultiStyleConfigHelpers(parts.keys);
export const baseStyle = definePartsStyle(() => ({
overlay: {
bg: 'blackAlpha.700',
},
dialogContainer: {},
dialog: {
maxH: '80vh',
bg: 'base.800',
},
header: {
fontWeight: 'semibold',
fontSize: 'lg',
color: 'base.300',
},
closeButton: {
opacity: 0.5,
},
body: {
overflowY: 'scroll',
},
footer: {},
}));
export const modalTheme = defineMultiStyleConfig({
baseStyle,
defaultProps: { size: 'lg' },
});

View File

@ -1,9 +0,0 @@
export type {
ModalBody as InvModalBodyProps,
ModalCloseButton as InvModalCloseButtonProps,
ModalContent as InvModalContentProps,
ModalFooter as InvModalFooterProps,
ModalHeader as InvModalHeaderProps,
ModalOverlay as InvModalOverlayProps,
Modal as InvModalProps,
} from '@chakra-ui/react';

View File

@ -1,9 +0,0 @@
export {
Modal as InvModal,
ModalBody as InvModalBody,
ModalCloseButton as InvModalCloseButton,
ModalContent as InvModalContent,
ModalFooter as InvModalFooter,
ModalHeader as InvModalHeader,
ModalOverlay as InvModalOverlay,
} from '@chakra-ui/react';

View File

@ -1,33 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { useState } from 'react';
import { InvNumberInput } from './InvNumberInput';
import type { InvNumberInputProps } from './types';
const meta: Meta<typeof InvNumberInput> = {
title: 'Primitives/InvNumberInput',
tags: ['autodocs'],
component: InvNumberInput,
args: {
min: -10,
max: 10,
step: 1,
},
};
export default meta;
type Story = StoryObj<typeof InvNumberInput>;
const Component = (props: InvNumberInputProps) => {
const [value, setValue] = useState(0);
return <InvNumberInput {...props} value={value} onChange={setValue} />;
};
export const Default: Story = {
render: Component,
args: { fineStep: 0.1 },
};
export const Integer: Story = {
render: Component,
};

View File

@ -1,135 +0,0 @@
import { forwardRef, NumberInput as ChakraNumberInput } from '@chakra-ui/react';
import { useStore } from '@nanostores/react';
import { $shift } from 'common/hooks/useGlobalModifiers';
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import { stopPastePropagation } from 'common/util/stopPastePropagation';
import { clamp } from 'lodash-es';
import type { FocusEventHandler } from 'react';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { InvNumberInputField } from './InvNumberInputField';
import { InvNumberInputStepper } from './InvNumberInputStepper';
import type { InvNumberInputProps } from './types';
const isValidCharacter = (char: string) => /^[0-9\-.]$/i.test(char);
export const InvNumberInput = memo(
forwardRef<InvNumberInputProps, typeof ChakraNumberInput>(
(props: InvNumberInputProps, ref) => {
const {
value,
min = 0,
max,
step: _step = 1,
fineStep: _fineStep,
onChange: _onChange,
numberInputFieldProps,
defaultValue,
...rest
} = props;
const [valueAsString, setValueAsString] = useState<string>(String(value));
const [valueAsNumber, setValueAsNumber] = useState<number>(value);
const shift = useStore($shift);
const step = useMemo(
() => (shift ? _fineStep ?? _step : _step),
[shift, _fineStep, _step]
);
const isInteger = useMemo(
() => Number.isInteger(_step) && Number.isInteger(_fineStep ?? 1),
[_step, _fineStep]
);
const inputMode = useMemo(
() => (isInteger ? 'numeric' : 'decimal'),
[isInteger]
);
const precision = useMemo(() => (isInteger ? 0 : 3), [isInteger]);
const onChange = useCallback(
(valueAsString: string, valueAsNumber: number) => {
setValueAsString(valueAsString);
if (isNaN(valueAsNumber)) {
return;
}
setValueAsNumber(valueAsNumber);
_onChange(valueAsNumber);
},
[_onChange]
);
// This appears to be unnecessary? Cannot figure out what it did but leaving it here in case
// it was important.
// const onClickStepper = useCallback(
// () => _onChange(Number(valueAsString)),
// [_onChange, valueAsString]
// );
const onBlur: FocusEventHandler<HTMLInputElement> = useCallback(
(e) => {
if (!e.target.value) {
// If the input is empty, we set it to the minimum value
onChange(String(defaultValue ?? min), Number(defaultValue) ?? min);
} else {
// Otherwise, we round the value to the nearest multiple if integer, else 3 decimals
const roundedValue = isInteger
? roundToMultiple(valueAsNumber, _fineStep ?? _step)
: Number(valueAsNumber.toFixed(precision));
// Clamp to min/max
const clampedValue = clamp(roundedValue, min, max);
onChange(String(clampedValue), clampedValue);
}
},
[
_fineStep,
_step,
defaultValue,
isInteger,
max,
min,
onChange,
precision,
valueAsNumber,
]
);
/**
* When `value` changes (e.g. from a diff source than this component), we need
* to update the internal `valueAsString`, but only if the actual value is different
* from the current value.
*/
useEffect(() => {
if (value !== valueAsNumber) {
setValueAsString(String(value));
setValueAsNumber(value);
}
}, [value, valueAsNumber]);
return (
<ChakraNumberInput
ref={ref}
value={valueAsString}
defaultValue={defaultValue}
min={min}
max={max}
step={step}
onChange={onChange}
clampValueOnBlur={false}
isValidCharacter={isValidCharacter}
focusInputOnChange={false}
onPaste={stopPastePropagation}
inputMode={inputMode}
precision={precision}
variant="filled"
{...rest}
>
<InvNumberInputField onBlur={onBlur} {...numberInputFieldProps} />
<InvNumberInputStepper />
</ChakraNumberInput>
);
}
)
);
InvNumberInput.displayName = 'InvNumberInput';

View File

@ -1,34 +0,0 @@
import { NumberInputField as ChakraNumberInputField } from '@chakra-ui/react';
import { useGlobalModifiersSetters } from 'common/hooks/useGlobalModifiers';
import type { KeyboardEventHandler } from 'react';
import { memo, useCallback } from 'react';
import type { InvNumberInputFieldProps } from './types';
export const InvNumberInputField = memo((props: InvNumberInputFieldProps) => {
const { onKeyUp, onKeyDown, children, ...rest } = props;
const { setShift } = useGlobalModifiersSetters();
const _onKeyUp: KeyboardEventHandler<HTMLInputElement> = useCallback(
(e) => {
onKeyUp?.(e);
setShift(e.key === 'Shift');
},
[onKeyUp, setShift]
);
const _onKeyDown: KeyboardEventHandler<HTMLInputElement> = useCallback(
(e) => {
onKeyDown?.(e);
setShift(e.key === 'Shift');
},
[onKeyDown, setShift]
);
return (
<ChakraNumberInputField onKeyUp={_onKeyUp} onKeyDown={_onKeyDown} {...rest}>
{children}
</ChakraNumberInputField>
);
});
InvNumberInputField.displayName = 'InvNumberInputField';

View File

@ -1,29 +0,0 @@
import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons';
import {
forwardRef,
NumberDecrementStepper as ChakraNumberDecrementStepper,
NumberIncrementStepper as ChakraNumberIncrementStepper,
NumberInputStepper as ChakraNumberInputStepper,
} from '@chakra-ui/react';
import { memo } from 'react';
import type { InvNumberInputStepperProps } from './types';
export const InvNumberInputStepper = memo(
forwardRef<InvNumberInputStepperProps, typeof ChakraNumberInputStepper>(
(props: InvNumberInputStepperProps, ref) => {
return (
<ChakraNumberInputStepper ref={ref} {...props}>
<ChakraNumberIncrementStepper>
<ChevronUpIcon />
</ChakraNumberIncrementStepper>
<ChakraNumberDecrementStepper>
<ChevronDownIcon />
</ChakraNumberDecrementStepper>
</ChakraNumberInputStepper>
);
}
)
);
InvNumberInputStepper.displayName = 'InvNumberInputStepper';

View File

@ -1,84 +0,0 @@
import { numberInputAnatomy as parts } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
import { getInputFilledStyles } from 'theme/util/getInputFilledStyles';
import { getInputOutlineStyles } from 'theme/util/getInputOutlineStyles';
const { defineMultiStyleConfig, definePartsStyle } =
createMultiStyleConfigHelpers(parts.keys);
const invokeAI = definePartsStyle(() => ({
root: {
// height: 8,
},
field: {
border: 'none',
fontWeight: 'semibold',
height: 'auto',
py: 1,
ps: 2,
pe: 6,
...getInputOutlineStyles(),
},
stepperGroup: {
display: 'flex',
},
stepper: {
border: 'none',
svg: {
color: 'base.300',
width: 2.5,
height: 2.5,
_hover: {
color: 'base.100',
},
},
},
}));
const filled = definePartsStyle((props) => {
return {
root: { h: '28px' },
field: { ...getInputFilledStyles(props), pe: 6, h: 'full' },
stepperGroup: {
border: 'none',
w: 6,
},
stepper: {
color: 'base.200',
_hover: {
bg: 'base.700',
color: 'base.100',
},
_disabled: {
_hover: {
bg: 'base.800',
color: 'base.200',
},
},
_first: {
border: 'none',
margin: 0,
borderTopEndRadius: 'base',
borderBottomStartRadius: 'base',
},
_last: {
border: 'none',
margin: 0,
borderBottomEndRadius: 'base',
borderTopStartRadius: 'base',
},
},
};
});
export const numberInputTheme = defineMultiStyleConfig({
variants: {
invokeAI,
filled,
darkFilled: filled,
},
defaultProps: {
size: 'sm',
variant: 'filled',
},
});

View File

@ -1,46 +0,0 @@
import type {
NumberDecrementStepperProps as ChakraNumberDecrementStepperProps,
NumberIncrementStepperProps as ChakraNumberIncrementStepperProps,
NumberInputFieldProps as ChakraNumberInputFieldProps,
NumberInputProps as ChakraNumberInputProps,
NumberInputStepperProps as ChakraNumberInputStepperProps,
} from '@chakra-ui/react';
export type InvNumberInputFieldProps = ChakraNumberInputFieldProps;
export type InvNumberInputStepperProps = ChakraNumberInputStepperProps;
export type InvNumberIncrementStepperProps = ChakraNumberIncrementStepperProps;
export type InvNumberDecrementStepperProps = ChakraNumberDecrementStepperProps;
export type InvNumberInputProps = Omit<
ChakraNumberInputProps,
'onChange' | 'min' | 'max'
> & {
/**
* The value
*/
value: number;
/**
* The minimum value
*/
min: number;
/**
* The maximum value
*/
max: number;
/**
* The default step
*/
step?: number;
/**
* The fine step (used when shift is pressed)
*/
fineStep?: number;
/**
* The change handler
*/
onChange: (v: number) => void;
/**
* Override props for the Chakra NumberInputField component
*/
numberInputFieldProps?: InvNumberInputFieldProps;
};

View File

@ -1,46 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { InvButton } from 'common/components/InvButton/InvButton';
import {
InvPopover,
InvPopoverArrow,
InvPopoverBody,
InvPopoverCloseButton,
InvPopoverContent,
InvPopoverHeader,
InvPopoverTrigger,
} from './wrapper';
const meta: Meta<typeof InvPopover> = {
title: 'Primitives/InvPopover',
tags: ['autodocs'],
component: InvPopover,
args: {
colorScheme: 'base',
},
};
export default meta;
type Story = StoryObj<typeof InvPopover>;
const Component = () => {
return (
<InvPopover>
<InvPopoverTrigger>
<InvButton>Trigger</InvButton>
</InvPopoverTrigger>
<InvPopoverContent>
<InvPopoverArrow />
<InvPopoverCloseButton />
<InvPopoverHeader>Confirmation!</InvPopoverHeader>
<InvPopoverBody>
Are you sure you want to have that milkshake?
</InvPopoverBody>
</InvPopoverContent>
</InvPopover>
);
};
export const Default: Story = {
render: Component,
};

View File

@ -1,59 +0,0 @@
import { popoverAnatomy as parts } from '@chakra-ui/anatomy';
import {
createMultiStyleConfigHelpers,
defineStyle,
} from '@chakra-ui/styled-system';
import { cssVar } from '@chakra-ui/theme-tools';
const { defineMultiStyleConfig, definePartsStyle } =
createMultiStyleConfigHelpers(parts.keys);
const $popperBg = cssVar('popper-bg');
const $arrowBg = cssVar('popper-arrow-bg');
const $arrowShadowColor = cssVar('popper-arrow-shadow-color');
const invokeAIContent = defineStyle(() => {
return {
[$arrowBg.variable]: 'colors.base.800',
[$popperBg.variable]: 'colors.base.800',
[$arrowShadowColor.variable]: 'colors.base.600',
minW: 'unset',
width: 'unset',
p: 4,
bg: 'base.800',
border: 'none',
shadow: 'dark-lg',
};
});
const informationalContent = defineStyle(() => {
return {
[$arrowBg.variable]: 'colors.base.700',
[$popperBg.variable]: 'colors.base.700',
[$arrowShadowColor.variable]: 'colors.base.400',
p: 4,
bg: 'base.700',
border: 'none',
shadow: 'dark-lg',
};
});
const invokeAI = definePartsStyle(() => ({
content: invokeAIContent(),
body: { padding: 0 },
}));
const informational = definePartsStyle(() => ({
content: informationalContent(),
body: { padding: 0 },
}));
export const popoverTheme = defineMultiStyleConfig({
variants: {
invokeAI,
informational,
},
defaultProps: {
variant: 'invokeAI',
},
});

View File

@ -1,9 +0,0 @@
export type {
PopoverArrowProps as InvPopoverArrowProps,
PopoverBodyProps as InvPopoverBodyProps,
PopoverCloseButtonProps as InvPopoverCloseButtonProps,
PopoverContentProps as InvPopoverContentProps,
PopoverFooterProps as InvPopoverFooterProps,
PopoverHeaderProps as InvPopoverHeaderProps,
PopoverProps as InvPopoverProps,
} from '@chakra-ui/react';

View File

@ -1,11 +0,0 @@
export {
Popover as InvPopover,
PopoverAnchor as InvPopoverAnchor,
PopoverArrow as InvPopoverArrow,
PopoverBody as InvPopoverBody,
PopoverCloseButton as InvPopoverCloseButton,
PopoverContent as InvPopoverContent,
PopoverFooter as InvPopoverFooter,
PopoverHeader as InvPopoverHeader,
PopoverTrigger as InvPopoverTrigger,
} from '@chakra-ui/react';

View File

@ -1,21 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { InvProgressProps } from './types';
import { InvProgress } from './wrapper';
const meta: Meta<typeof InvProgress> = {
title: 'Primitives/InvProgress',
tags: ['autodocs'],
component: InvProgress,
};
export default meta;
type Story = StoryObj<typeof InvProgress>;
const Component = (props: InvProgressProps) => {
return <InvProgress {...props}>Banana sushi is delectable!</InvProgress>;
};
export const Default: Story = {
render: Component,
};

Some files were not shown because too many files have changed in this diff Show More