mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): migrate to @invoke-ai/ui
This commit is contained in:
parent
8e2ccab1f0
commit
5d068c1da1
@ -93,6 +93,27 @@ module.exports = {
|
||||
'@typescript-eslint/no-import-type-side-effects': 'error',
|
||||
'simple-import-sort/imports': '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: [
|
||||
{
|
||||
|
@ -32,8 +32,8 @@
|
||||
"fix": "eslint --fix . && prettier --log-level warn --write .",
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"postinstall": "pnpm run theme",
|
||||
"theme": "chakra-cli tokens src/theme/theme.ts",
|
||||
"theme:watch": "chakra-cli tokens src/theme/theme.ts --watch",
|
||||
"theme": "chakra-cli tokens --strict-component-types --strict-token-types node_modules/@invoke-ai/ui",
|
||||
"theme:watch": "chakra-cli tokens --strict-component-types --strict-token-types node_modules/@invoke-ai/ui --watch",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build",
|
||||
"unimported": "npx unimported"
|
||||
@ -66,7 +66,7 @@
|
||||
"@emotion/react": "^11.11.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@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",
|
||||
"@nanostores/react": "^0.7.1",
|
||||
"@reduxjs/toolkit": "2.0.1",
|
||||
|
@ -53,8 +53,8 @@ dependencies:
|
||||
specifier: ^5.0.16
|
||||
version: 5.0.16
|
||||
'@invoke-ai/ui':
|
||||
specifier: ^0.0.3
|
||||
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)
|
||||
specifier: file:/home/bat/Documents/Code/ui
|
||||
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':
|
||||
specifier: 6.0.21
|
||||
version: 6.0.21(react@18.2.0)
|
||||
@ -3688,48 +3688,6 @@ packages:
|
||||
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
|
||||
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:
|
||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -13787,3 +13745,53 @@ packages:
|
||||
react: 18.2.0
|
||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||
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
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { Box } from '@invoke-ai/ui';
|
||||
import { useSocketIO } from 'app/hooks/useSocketIO';
|
||||
import { useLogger } from 'app/logging/useLogger';
|
||||
import { appStarted } from 'app/store/middleware/listenerMiddleware/listeners/appStarted';
|
||||
|
@ -1,6 +1,4 @@
|
||||
import { Flex, Heading, Link, useToast } from '@chakra-ui/react';
|
||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
import { Button, Flex, Heading, Link, Text, useToast } from '@invoke-ai/ui';
|
||||
import newGithubIssueUrl from 'new-github-issue-url';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -65,24 +63,24 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<InvText fontWeight="semibold" color="error.400">
|
||||
<Text fontWeight="semibold" color="error.400">
|
||||
{error.name}: {error.message}
|
||||
</InvText>
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex gap={4}>
|
||||
<InvButton
|
||||
<Button
|
||||
leftIcon={<PiArrowCounterClockwiseBold />}
|
||||
onClick={resetErrorBoundary}
|
||||
>
|
||||
{t('accessibility.resetUI')}
|
||||
</InvButton>
|
||||
<InvButton leftIcon={<PiCopyBold />} onClick={handleCopy}>
|
||||
</Button>
|
||||
<Button leftIcon={<PiCopyBold />} onClick={handleCopy}>
|
||||
{t('common.copyError')}
|
||||
</InvButton>
|
||||
</Button>
|
||||
<Link href={url} isExternal>
|
||||
<InvButton leftIcon={<PiArrowSquareOutBold />}>
|
||||
<Button leftIcon={<PiArrowSquareOutBold />}>
|
||||
{t('accessibility.createIssue')}
|
||||
</InvButton>
|
||||
</Button>
|
||||
</Link>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
@ -1,12 +1,16 @@
|
||||
import '@fontsource-variable/inter';
|
||||
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 { memo, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { theme as invokeAITheme, TOAST_OPTIONS } from 'theme/theme';
|
||||
|
||||
type ThemeLocaleProviderProps = {
|
||||
children: ReactNode;
|
||||
@ -19,7 +23,7 @@ function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) {
|
||||
|
||||
const theme = useMemo(() => {
|
||||
return extendTheme({
|
||||
...invokeAITheme,
|
||||
..._theme,
|
||||
direction,
|
||||
});
|
||||
}, [direction]);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
import { useToast } from '@invoke-ai/ui';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { addToast, clearToastQueue } from 'features/system/store/systemSlice';
|
||||
import type { MakeToastArg } from 'features/system/util/makeToast';
|
||||
|
@ -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 { parseify } from 'common/util/serialize';
|
||||
import { zPydanticValidationError } from 'features/system/store/zodSchemas';
|
||||
import { t } from 'i18next';
|
||||
import { truncate, upperFirst } from 'lodash-es';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
import { theme, TOAST_OPTIONS } from 'theme/theme';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
|
@ -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 { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { MenuItemProps } from '@chakra-ui/react';
|
||||
import type { MenuItemProps } from '@invoke-ai/ui';
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export type CustomStarUi = {
|
||||
|
@ -1,5 +1,10 @@
|
||||
import type { ChakraProps } from '@chakra-ui/react';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import type { ChakraProps } from '@invoke-ai/ui';
|
||||
import {
|
||||
CompositeNumberInput,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
} from '@invoke-ai/ui';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { RgbaColorPicker } from 'react-colorful';
|
||||
@ -8,9 +13,6 @@ import type {
|
||||
RgbaColor,
|
||||
} from 'react-colorful/dist/types';
|
||||
|
||||
import { InvControl } from './InvControl/InvControl';
|
||||
import { InvNumberInput } from './InvNumberInput/InvNumberInput';
|
||||
|
||||
type IAIColorPickerProps = ColorPickerBaseProps<RgbaColor> & {
|
||||
withNumberInput?: boolean;
|
||||
};
|
||||
@ -61,8 +63,9 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
|
||||
/>
|
||||
{withNumberInput && (
|
||||
<Flex>
|
||||
<InvControl label="Red">
|
||||
<InvNumberInput
|
||||
<FormControl>
|
||||
<FormLabel>Red</FormLabel>
|
||||
<CompositeNumberInput
|
||||
value={color.r}
|
||||
onChange={handleChangeR}
|
||||
min={0}
|
||||
@ -71,9 +74,10 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
|
||||
w={numberInputWidth}
|
||||
defaultValue={90}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label="Green">
|
||||
<InvNumberInput
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Green</FormLabel>
|
||||
<CompositeNumberInput
|
||||
value={color.g}
|
||||
onChange={handleChangeG}
|
||||
min={0}
|
||||
@ -82,9 +86,10 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
|
||||
w={numberInputWidth}
|
||||
defaultValue={90}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label="Blue">
|
||||
<InvNumberInput
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Blue</FormLabel>
|
||||
<CompositeNumberInput
|
||||
value={color.b}
|
||||
onChange={handleChangeB}
|
||||
min={0}
|
||||
@ -93,9 +98,10 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
|
||||
w={numberInputWidth}
|
||||
defaultValue={255}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label="Alpha">
|
||||
<InvNumberInput
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Alpha</FormLabel>
|
||||
<CompositeNumberInput
|
||||
value={color.a}
|
||||
onChange={handleChangeA}
|
||||
step={0.1}
|
||||
@ -104,7 +110,7 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
|
||||
w={numberInputWidth}
|
||||
defaultValue={1}
|
||||
/>
|
||||
</InvControl>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
|
@ -1,9 +1,5 @@
|
||||
import type {
|
||||
ChakraProps,
|
||||
FlexProps,
|
||||
SystemStyleObject,
|
||||
} from '@chakra-ui/react';
|
||||
import { Flex, Icon, Image } from '@chakra-ui/react';
|
||||
import type { ChakraProps, FlexProps, SystemStyleObject } from '@invoke-ai/ui';
|
||||
import { Flex, Icon, Image } from '@invoke-ai/ui';
|
||||
import {
|
||||
IAILoadingImageFallback,
|
||||
IAINoContentFallback,
|
||||
|
@ -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 { memo, useMemo } from 'react';
|
||||
|
||||
import { InvIconButton } from './InvIconButton/InvIconButton';
|
||||
|
||||
type Props = {
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) => void;
|
||||
tooltip: string;
|
||||
@ -26,7 +25,7 @@ const IAIDndImageIcon = (props: Props) => {
|
||||
transitionDuration: 'normal',
|
||||
fill: 'base.100',
|
||||
_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,
|
||||
}),
|
||||
@ -34,7 +33,7 @@ const IAIDndImageIcon = (props: Props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<InvIconButton
|
||||
<IconButton
|
||||
onClick={onClick}
|
||||
aria-label={tooltip}
|
||||
tooltip={tooltip}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import type { BoxProps } from '@invoke-ai/ui';
|
||||
import { Box } from '@invoke-ai/ui';
|
||||
import { useDraggableTypesafe } from 'features/dnd/hooks/typesafeHooks';
|
||||
import type { TypesafeDraggableData } from 'features/dnd/types';
|
||||
import { memo, useRef } from 'react';
|
||||
|
@ -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 { motion } from 'framer-motion';
|
||||
import type { ReactNode } from 'react';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { Box } from '@invoke-ai/ui';
|
||||
import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks';
|
||||
import type { TypesafeDroppableData } from 'features/dnd/types';
|
||||
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import { Box, Skeleton } from '@chakra-ui/react';
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui';
|
||||
import { Box, Skeleton } from '@invoke-ai/ui';
|
||||
import { memo } from 'react';
|
||||
|
||||
const skeletonStyles: SystemStyleObject = {
|
||||
|
@ -1,11 +1,9 @@
|
||||
import type { As, FlexProps, StyleProps } from '@chakra-ui/react';
|
||||
import { Flex, Icon, Skeleton, Spinner } from '@chakra-ui/react';
|
||||
import type { As, ChakraProps, FlexProps } from '@invoke-ai/ui';
|
||||
import { Flex, Icon, Skeleton, Spinner, Text } from '@invoke-ai/ui';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { PiImageBold } from 'react-icons/pi';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
import { InvText } from './InvText/wrapper';
|
||||
|
||||
type Props = { image: ImageDTO | undefined };
|
||||
|
||||
export const IAILoadingImageFallback = memo((props: Props) => {
|
||||
@ -39,7 +37,7 @@ IAILoadingImageFallback.displayName = 'IAILoadingImageFallback';
|
||||
type IAINoImageFallbackProps = FlexProps & {
|
||||
label?: string;
|
||||
icon?: As | null;
|
||||
boxSize?: StyleProps['boxSize'];
|
||||
boxSize?: ChakraProps['boxSize'];
|
||||
};
|
||||
|
||||
export const IAINoContentFallback = memo((props: IAINoImageFallbackProps) => {
|
||||
@ -66,9 +64,9 @@ export const IAINoContentFallback = memo((props: IAINoImageFallbackProps) => {
|
||||
<Flex sx={styles} {...rest}>
|
||||
{icon && <Icon as={icon} boxSize={boxSize} opacity={0.7} />}
|
||||
{props.label && (
|
||||
<InvText textAlign="center" fontSize="md">
|
||||
<Text textAlign="center" fontSize="md">
|
||||
{props.label}
|
||||
</InvText>
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
@ -102,7 +100,7 @@ export const IAINoContentFallbackWithSpinner = memo(
|
||||
return (
|
||||
<Flex sx={styles} {...rest}>
|
||||
<Spinner size="xl" />
|
||||
{props.label && <InvText textAlign="center">{props.label}</InvText>}
|
||||
{props.label && <Text textAlign="center">{props.label}</Text>}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
@ -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 {
|
||||
InvPopover,
|
||||
InvPopoverBody,
|
||||
InvPopoverCloseButton,
|
||||
InvPopoverContent,
|
||||
InvPopoverTrigger,
|
||||
} from 'common/components/InvPopover/wrapper';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
Button,
|
||||
Divider,
|
||||
Flex,
|
||||
Heading,
|
||||
Image,
|
||||
Popover,
|
||||
PopoverBody,
|
||||
PopoverCloseButton,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
Portal,
|
||||
Text,
|
||||
} from '@invoke-ai/ui';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { merge, omit } from 'lodash-es';
|
||||
import type { ReactElement } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@ -47,7 +50,7 @@ const IAIInformationalPopover = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<InvPopover
|
||||
<Popover
|
||||
isLazy
|
||||
closeOnBlur={false}
|
||||
trigger="hover"
|
||||
@ -57,26 +60,26 @@ const IAIInformationalPopover = ({
|
||||
placement="top"
|
||||
{...popoverProps}
|
||||
>
|
||||
<InvPopoverTrigger>{children}</InvPopoverTrigger>
|
||||
<PopoverTrigger>{children}</PopoverTrigger>
|
||||
{inPortal ? (
|
||||
<Portal>
|
||||
<PopoverContent data={data} feature={feature} />
|
||||
<Content data={data} feature={feature} />
|
||||
</Portal>
|
||||
) : (
|
||||
<PopoverContent data={data} feature={feature} />
|
||||
<Content data={data} feature={feature} />
|
||||
)}
|
||||
</InvPopover>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(IAIInformationalPopover);
|
||||
|
||||
type PopoverContentProps = {
|
||||
type ContentProps = {
|
||||
data?: PopoverData;
|
||||
feature: Feature;
|
||||
};
|
||||
|
||||
const PopoverContent = ({ data, feature }: PopoverContentProps) => {
|
||||
const Content = ({ data, feature }: ContentProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const heading = useMemo<string | undefined>(
|
||||
@ -100,13 +103,13 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
|
||||
}, [data?.href]);
|
||||
|
||||
return (
|
||||
<InvPopoverContent w={96}>
|
||||
<InvPopoverCloseButton />
|
||||
<InvPopoverBody>
|
||||
<PopoverContent w={96}>
|
||||
<PopoverCloseButton />
|
||||
<PopoverBody>
|
||||
<Flex gap={2} flexDirection="column" alignItems="flex-start">
|
||||
{heading && (
|
||||
<>
|
||||
<InvHeading size="sm">{heading}</InvHeading>
|
||||
<Heading size="sm">{heading}</Heading>
|
||||
<Divider />
|
||||
</>
|
||||
)}
|
||||
@ -124,12 +127,12 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
|
||||
</>
|
||||
)}
|
||||
{paragraphs.map((p) => (
|
||||
<InvText key={p}>{p}</InvText>
|
||||
<Text key={p}>{p}</Text>
|
||||
))}
|
||||
{data?.href && (
|
||||
<>
|
||||
<Divider />
|
||||
<InvButton
|
||||
<Button
|
||||
pt={1}
|
||||
onClick={handleClick}
|
||||
leftIcon={<PiArrowSquareOutBold />}
|
||||
@ -137,11 +140,11 @@ const PopoverContent = ({ data, feature }: PopoverContentProps) => {
|
||||
variant="link"
|
||||
>
|
||||
{t('common.learnMore') ?? heading}
|
||||
</InvButton>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</InvPopoverBody>
|
||||
</InvPopoverContent>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { PopoverProps } from '@chakra-ui/react';
|
||||
import type { PopoverProps } from '@invoke-ai/ui';
|
||||
|
||||
export type Feature =
|
||||
| 'clipSkip'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Badge, Flex } from '@chakra-ui/react';
|
||||
import { Badge, Flex } from '@invoke-ai/ui';
|
||||
import { memo } from 'react';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
|
@ -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 { motion } from 'framer-motion';
|
||||
import { memo } from 'react';
|
||||
|
@ -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' 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'm lying in bed Just to get it all
|
||||
out what'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
|
||||
"What's going on?"
|
||||
</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 "Hey, a-what's going
|
||||
on?" And I say, hey-ey-ey Hey-ey-ey I said "Hey,
|
||||
a-what's going on?"
|
||||
</InvText>
|
||||
</InvAccordionPanel>
|
||||
</InvAccordionItem>
|
||||
</InvAccordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default: Story = {
|
||||
render: Component,
|
||||
};
|
@ -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>
|
||||
);
|
||||
};
|
@ -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',
|
||||
},
|
||||
});
|
@ -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)[];
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
export {
|
||||
Accordion as InvAccordion,
|
||||
AccordionIcon as InvAccordionIcon,
|
||||
AccordionItem as InvAccordionItem,
|
||||
AccordionPanel as InvAccordionPanel,
|
||||
} from '@chakra-ui/react';
|
@ -1,4 +0,0 @@
|
||||
/**
|
||||
* AlertDialog is a chakra Modal internally and uses those props.
|
||||
*/
|
||||
export type { AlertDialogProps as InvAlertDialogProps } from '@chakra-ui/react';
|
@ -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';
|
@ -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 { stopPastePropagation } from 'common/util/stopPastePropagation';
|
||||
import type { KeyboardEvent } from 'react';
|
||||
|
@ -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';
|
||||
|
||||
export type InvAutosizeTextareaProps = Omit<
|
||||
|
@ -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,
|
||||
};
|
@ -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',
|
||||
},
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
import type { BadgeProps as ChakraBadgeProps } from '@chakra-ui/react';
|
||||
|
||||
export type InvBadgeProps = ChakraBadgeProps;
|
@ -1 +0,0 @@
|
||||
export { Badge as InvBadge } from '@chakra-ui/react';
|
@ -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,
|
||||
};
|
@ -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';
|
@ -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',
|
||||
},
|
||||
});
|
@ -1,7 +0,0 @@
|
||||
import type { ButtonProps } from '@chakra-ui/react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export type InvButtonProps = ButtonProps & {
|
||||
isChecked?: boolean;
|
||||
tooltip?: ReactNode;
|
||||
};
|
@ -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,
|
||||
};
|
@ -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';
|
@ -1 +0,0 @@
|
||||
export type { ButtonGroupProps as InvButtonGroupProps } from '@chakra-ui/react';
|
@ -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,
|
||||
};
|
@ -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 },
|
||||
});
|
@ -1,6 +0,0 @@
|
||||
export type {
|
||||
CardBodyProps as InvCardBodyProps,
|
||||
CardFooterProps as InvCardFooterProps,
|
||||
CardHeaderProps as InvCardHeaderProps,
|
||||
CardProps as InvCardProps,
|
||||
} from '@chakra-ui/react';
|
@ -1,6 +0,0 @@
|
||||
export {
|
||||
Card as InvCard,
|
||||
CardBody as InvCardBody,
|
||||
CardFooter as InvCardFooter,
|
||||
CardHeader as InvCardHeader,
|
||||
} from '@chakra-ui/react';
|
@ -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,
|
||||
};
|
@ -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',
|
||||
},
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
import type { CheckboxProps as ChakraCheckboxProps } from '@chakra-ui/react';
|
||||
|
||||
export interface InvCheckboxProps extends ChakraCheckboxProps {}
|
@ -1 +0,0 @@
|
||||
export { Checkbox as InvCheckbox } from '@chakra-ui/react';
|
@ -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';
|
@ -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;
|
||||
}>;
|
@ -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,
|
||||
};
|
@ -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' };
|
@ -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,
|
||||
};
|
@ -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';
|
@ -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';
|
@ -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';
|
@ -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',
|
||||
},
|
||||
},
|
||||
});
|
@ -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;
|
||||
};
|
@ -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,
|
||||
};
|
@ -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',
|
||||
},
|
||||
});
|
@ -1,6 +0,0 @@
|
||||
export type {
|
||||
EditableInputProps as InvEditableInputProps,
|
||||
EditablePreviewProps as InvEditablePreviewProps,
|
||||
EditableProps as InvEditableProps,
|
||||
EditableTextareaProps as InvEditableTextareaProps,
|
||||
} from '@chakra-ui/react';
|
@ -1,6 +0,0 @@
|
||||
export {
|
||||
Editable as InvEditable,
|
||||
EditableInput as InvEditableInput,
|
||||
EditablePreview as InvEditablePreview,
|
||||
EditableTextarea as InvEditableTextarea,
|
||||
} from '@chakra-ui/react';
|
@ -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,
|
||||
};
|
@ -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>
|
||||
);
|
||||
};
|
@ -1,7 +0,0 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
export type InvExpanderProps = PropsWithChildren<{
|
||||
label?: string;
|
||||
defaultIsOpen?: boolean;
|
||||
id?: string;
|
||||
}>;
|
@ -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,
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
import { defineStyle, defineStyleConfig } from '@chakra-ui/react';
|
||||
|
||||
const invokeBlue = defineStyle(() => ({
|
||||
color: 'invokeBlue.300',
|
||||
}));
|
||||
|
||||
export const headingTheme = defineStyleConfig({
|
||||
variants: {
|
||||
invokeBlue,
|
||||
},
|
||||
});
|
@ -1 +0,0 @@
|
||||
export type { HeadingProps as InvHeadingProps } from '@chakra-ui/react';
|
@ -1 +0,0 @@
|
||||
export { Heading as InvHeading } from '@chakra-ui/react';
|
@ -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,
|
||||
};
|
@ -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';
|
@ -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;
|
||||
};
|
@ -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,
|
||||
};
|
@ -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';
|
@ -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',
|
||||
},
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
import type { InputProps as ChakraInputProps } from '@chakra-ui/react';
|
||||
|
||||
export type InvInputProps = ChakraInputProps;
|
@ -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,
|
||||
};
|
@ -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';
|
@ -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';
|
@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
@ -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',
|
||||
},
|
||||
});
|
@ -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;
|
@ -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';
|
@ -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,
|
||||
};
|
@ -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' },
|
||||
});
|
@ -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';
|
@ -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';
|
@ -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,
|
||||
};
|
@ -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';
|
@ -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';
|
@ -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';
|
@ -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',
|
||||
},
|
||||
});
|
@ -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;
|
||||
};
|
@ -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,
|
||||
};
|
@ -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',
|
||||
},
|
||||
});
|
@ -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';
|
@ -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';
|
@ -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
Loading…
Reference in New Issue
Block a user