mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): better editable title
This commit is contained in:
parent
d76509e5cb
commit
ab3eb32ec8
@ -1,12 +1,10 @@
|
|||||||
import { Spacer } from '@invoke-ai/ui-library';
|
import { Spacer } from '@invoke-ai/ui-library';
|
||||||
import { useBoolean } from 'common/hooks/useBoolean';
|
|
||||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||||
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
||||||
import { CanvasEntityTitleEdit } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
|
||||||
import { ControlLayerControlAdapter } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapter';
|
import { ControlLayerControlAdapter } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapter';
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
@ -18,14 +16,13 @@ type Props = {
|
|||||||
|
|
||||||
export const ControlLayer = memo(({ id }: Props) => {
|
export const ControlLayer = memo(({ id }: Props) => {
|
||||||
const entityIdentifier = useMemo<CanvasEntityIdentifier>(() => ({ id, type: 'control_layer' }), [id]);
|
const entityIdentifier = useMemo<CanvasEntityIdentifier>(() => ({ id, type: 'control_layer' }), [id]);
|
||||||
const editing = useBoolean(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
||||||
<CanvasEntityContainer>
|
<CanvasEntityContainer>
|
||||||
<CanvasEntityHeader onDoubleClick={editing.setTrue}>
|
<CanvasEntityHeader>
|
||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
{editing.isTrue ? <CanvasEntityTitleEdit onStopEditing={editing.setFalse} /> : <CanvasEntityTitle />}
|
<CanvasEntityEditableTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<CanvasEntityDeleteButton />
|
<CanvasEntityDeleteButton />
|
||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { Spacer } from '@invoke-ai/ui-library';
|
import { Spacer } from '@invoke-ai/ui-library';
|
||||||
import { useBoolean } from 'common/hooks/useBoolean';
|
|
||||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||||
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
||||||
import { CanvasEntityTitleEdit } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
|
||||||
import { IPAdapterSettings } from 'features/controlLayers/components/IPAdapter/IPAdapterSettings';
|
import { IPAdapterSettings } from 'features/controlLayers/components/IPAdapter/IPAdapterSettings';
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
@ -17,14 +15,13 @@ type Props = {
|
|||||||
|
|
||||||
export const IPAdapter = memo(({ id }: Props) => {
|
export const IPAdapter = memo(({ id }: Props) => {
|
||||||
const entityIdentifier = useMemo<CanvasEntityIdentifier>(() => ({ id, type: 'ip_adapter' }), [id]);
|
const entityIdentifier = useMemo<CanvasEntityIdentifier>(() => ({ id, type: 'ip_adapter' }), [id]);
|
||||||
const editing = useBoolean(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
||||||
<CanvasEntityContainer>
|
<CanvasEntityContainer>
|
||||||
<CanvasEntityHeader onDoubleClick={editing.setTrue}>
|
<CanvasEntityHeader>
|
||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
{editing.isTrue ? <CanvasEntityTitleEdit onStopEditing={editing.setFalse} /> : <CanvasEntityTitle />}
|
<CanvasEntityEditableTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<CanvasEntityDeleteButton />
|
<CanvasEntityDeleteButton />
|
||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { Spacer } from '@invoke-ai/ui-library';
|
import { Spacer } from '@invoke-ai/ui-library';
|
||||||
import { useBoolean } from 'common/hooks/useBoolean';
|
|
||||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||||
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
||||||
import { CanvasEntityTitleEdit } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
@ -16,14 +14,13 @@ type Props = {
|
|||||||
|
|
||||||
export const RasterLayer = memo(({ id }: Props) => {
|
export const RasterLayer = memo(({ id }: Props) => {
|
||||||
const entityIdentifier = useMemo<CanvasEntityIdentifier>(() => ({ id, type: 'raster_layer' }), [id]);
|
const entityIdentifier = useMemo<CanvasEntityIdentifier>(() => ({ id, type: 'raster_layer' }), [id]);
|
||||||
const editing = useBoolean(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
||||||
<CanvasEntityContainer>
|
<CanvasEntityContainer>
|
||||||
<CanvasEntityHeader onDoubleClick={editing.setTrue}>
|
<CanvasEntityHeader>
|
||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
{editing.isTrue ? <CanvasEntityTitleEdit onStopEditing={editing.setFalse} /> : <CanvasEntityTitle />}
|
<CanvasEntityEditableTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<CanvasEntityDeleteButton />
|
<CanvasEntityDeleteButton />
|
||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { Spacer } from '@invoke-ai/ui-library';
|
import { Spacer } from '@invoke-ai/ui-library';
|
||||||
import { useBoolean } from 'common/hooks/useBoolean';
|
|
||||||
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
|
||||||
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
||||||
import { CanvasEntityTitleEdit } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
|
||||||
import { RegionalGuidanceBadges } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges';
|
import { RegionalGuidanceBadges } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges';
|
||||||
import { RegionalGuidanceSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings';
|
import { RegionalGuidanceSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings';
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
@ -21,14 +19,13 @@ type Props = {
|
|||||||
|
|
||||||
export const RegionalGuidance = memo(({ id }: Props) => {
|
export const RegionalGuidance = memo(({ id }: Props) => {
|
||||||
const entityIdentifier = useMemo<CanvasEntityIdentifier>(() => ({ id, type: 'regional_guidance' }), [id]);
|
const entityIdentifier = useMemo<CanvasEntityIdentifier>(() => ({ id, type: 'regional_guidance' }), [id]);
|
||||||
const editing = useBoolean(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
||||||
<CanvasEntityContainer>
|
<CanvasEntityContainer>
|
||||||
<CanvasEntityHeader onDoubleClick={editing.setTrue}>
|
<CanvasEntityHeader>
|
||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
{editing.isTrue ? <CanvasEntityTitleEdit onStopEditing={editing.setFalse} /> : <CanvasEntityTitle />}
|
<CanvasEntityEditableTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<RegionalGuidanceBadges />
|
<RegionalGuidanceBadges />
|
||||||
<RegionalGuidanceMaskFillColorPicker />
|
<RegionalGuidanceMaskFillColorPicker />
|
||||||
|
@ -53,7 +53,7 @@ export const CanvasEntityHeader = memo(({ children, ...rest }: FlexProps) => {
|
|||||||
return (
|
return (
|
||||||
<ContextMenu renderMenu={renderMenu}>
|
<ContextMenu renderMenu={renderMenu}>
|
||||||
{(ref) => (
|
{(ref) => (
|
||||||
<Flex ref={ref} gap={2} alignItems="center" p={2} {...rest} cursor="text">
|
<Flex ref={ref} gap={2} alignItems="center" p={2} {...rest}>
|
||||||
{children}
|
{children}
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
|
import type { TextProps } from '@invoke-ai/ui-library';
|
||||||
import { Text } from '@invoke-ai/ui-library';
|
import { Text } from '@invoke-ai/ui-library';
|
||||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import { useEntityIsSelected } from 'features/controlLayers/hooks/useEntityIsSelected';
|
import { useEntityIsSelected } from 'features/controlLayers/hooks/useEntityIsSelected';
|
||||||
import { useEntityTitle } from 'features/controlLayers/hooks/useEntityTitle';
|
import { useEntityTitle } from 'features/controlLayers/hooks/useEntityTitle';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
export const CanvasEntityTitle = memo(() => {
|
export const CanvasEntityTitle = memo((props: TextProps) => {
|
||||||
const entityIdentifier = useEntityIdentifierContext();
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
const isSelected = useEntityIsSelected(entityIdentifier);
|
const isSelected = useEntityIsSelected(entityIdentifier);
|
||||||
const title = useEntityTitle(entityIdentifier);
|
const title = useEntityTitle(entityIdentifier);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text size="sm" fontWeight="semibold" userSelect="none" color={isSelected ? 'base.100' : 'base.300'}>
|
<Text size="sm" fontWeight="semibold" userSelect="none" color={isSelected ? 'base.100' : 'base.300'} {...props}>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
import { Input } from '@invoke-ai/ui-library';
|
import { Input } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import { useBoolean } from 'common/hooks/useBoolean';
|
||||||
|
import { CanvasEntityTitle } from 'features/controlLayers/components/common/CanvasEntityTitle';
|
||||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import { useEntityTitle } from 'features/controlLayers/hooks/useEntityTitle';
|
import { useEntityTitle } from 'features/controlLayers/hooks/useEntityTitle';
|
||||||
import { entityNameChanged } from 'features/controlLayers/store/canvasV2Slice';
|
import { entityNameChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import type { ChangeEvent, KeyboardEvent } from 'react';
|
import type { ChangeEvent, KeyboardEvent } from 'react';
|
||||||
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
type Props = {
|
export const CanvasEntityEditableTitle = memo(() => {
|
||||||
onStopEditing: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CanvasEntityTitleEdit = memo(({ onStopEditing }: Props) => {
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const ref = useRef<HTMLInputElement>(null);
|
|
||||||
const entityIdentifier = useEntityIdentifierContext();
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
const title = useEntityTitle(entityIdentifier);
|
const title = useEntityTitle(entityIdentifier);
|
||||||
|
const isEditing = useBoolean(false);
|
||||||
const [localTitle, setLocalTitle] = useState(title);
|
const [localTitle, setLocalTitle] = useState(title);
|
||||||
|
const ref = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||||
setLocalTitle(e.target.value);
|
setLocalTitle(e.target.value);
|
||||||
@ -28,8 +27,8 @@ export const CanvasEntityTitleEdit = memo(({ onStopEditing }: Props) => {
|
|||||||
} else if (trimmedTitle !== title) {
|
} else if (trimmedTitle !== title) {
|
||||||
dispatch(entityNameChanged({ entityIdentifier, name: trimmedTitle }));
|
dispatch(entityNameChanged({ entityIdentifier, name: trimmedTitle }));
|
||||||
}
|
}
|
||||||
onStopEditing();
|
isEditing.setFalse();
|
||||||
}, [dispatch, entityIdentifier, localTitle, onStopEditing, title]);
|
}, [dispatch, entityIdentifier, isEditing, localTitle, title]);
|
||||||
|
|
||||||
const onKeyDown = useCallback(
|
const onKeyDown = useCallback(
|
||||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
@ -37,16 +36,22 @@ export const CanvasEntityTitleEdit = memo(({ onStopEditing }: Props) => {
|
|||||||
onBlur();
|
onBlur();
|
||||||
} else if (e.key === 'Escape') {
|
} else if (e.key === 'Escape') {
|
||||||
setLocalTitle(title);
|
setLocalTitle(title);
|
||||||
onStopEditing();
|
isEditing.setFalse();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onBlur, onStopEditing, title]
|
[isEditing, onBlur, title]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current?.focus();
|
if (isEditing.isTrue) {
|
||||||
ref.current?.select();
|
ref.current?.focus();
|
||||||
}, []);
|
ref.current?.select();
|
||||||
|
}
|
||||||
|
}, [isEditing.isTrue]);
|
||||||
|
|
||||||
|
if (!isEditing.isTrue) {
|
||||||
|
return <CanvasEntityTitle cursor="text" onDoubleClick={isEditing.setTrue} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
@ -61,4 +66,4 @@ export const CanvasEntityTitleEdit = memo(({ onStopEditing }: Props) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
CanvasEntityTitleEdit.displayName = 'CanvasEntityTitleEdit';
|
CanvasEntityEditableTitle.displayName = 'CanvasEntityTitleEdit';
|
||||||
|
Loading…
Reference in New Issue
Block a user