mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Refactors gallery resizing, persists width
This commit is contained in:
parent
9fc6ee0c4c
commit
f22f81b4ff
@ -59,6 +59,7 @@ const galleryPersistConfig = {
|
|||||||
key: 'gallery',
|
key: 'gallery',
|
||||||
storage,
|
storage,
|
||||||
whitelist: [
|
whitelist: [
|
||||||
|
'galleryWidth',
|
||||||
'shouldPinGallery',
|
'shouldPinGallery',
|
||||||
'shouldShowGallery',
|
'shouldShowGallery',
|
||||||
'galleryScrollPosition',
|
'galleryScrollPosition',
|
||||||
|
@ -52,9 +52,15 @@
|
|||||||
|
|
||||||
.image-gallery-header {
|
.image-gallery-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 0.5rem;
|
column-gap: 0.5rem;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
column-gap: 0.5rem;
|
||||||
|
column-gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.image-gallery-icon-btn {
|
.image-gallery-icon-btn {
|
||||||
background-color: var(--btn-load-more) !important;
|
background-color: var(--btn-load-more) !important;
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
setGalleryImageMinimumWidth,
|
setGalleryImageMinimumWidth,
|
||||||
setGalleryImageObjectFit,
|
setGalleryImageObjectFit,
|
||||||
setGalleryScrollPosition,
|
setGalleryScrollPosition,
|
||||||
|
setGalleryWidth,
|
||||||
setShouldAutoSwitchToNewImages,
|
setShouldAutoSwitchToNewImages,
|
||||||
setShouldHoldGalleryOpen,
|
setShouldHoldGalleryOpen,
|
||||||
setShouldPinGallery,
|
setShouldPinGallery,
|
||||||
@ -33,6 +34,8 @@ import IAICheckbox from '../../common/components/IAICheckbox';
|
|||||||
import { setNeedsCache } from '../tabs/Inpainting/inpaintingSlice';
|
import { setNeedsCache } from '../tabs/Inpainting/inpaintingSlice';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 320;
|
||||||
|
|
||||||
export default function ImageGallery() {
|
export default function ImageGallery() {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@ -51,71 +54,39 @@ export default function ImageGallery() {
|
|||||||
shouldHoldGalleryOpen,
|
shouldHoldGalleryOpen,
|
||||||
shouldAutoSwitchToNewImages,
|
shouldAutoSwitchToNewImages,
|
||||||
areMoreImagesAvailable,
|
areMoreImagesAvailable,
|
||||||
|
galleryWidth,
|
||||||
} = useAppSelector(imageGallerySelector);
|
} = useAppSelector(imageGallerySelector);
|
||||||
|
|
||||||
const [gallerySize, setGallerySize] = useState<Size>({
|
const [galleryMinWidth, setGalleryMinWidth] = useState<number>(300);
|
||||||
width: '300',
|
const [galleryMaxWidth, setGalleryMaxWidth] = useState<number>(590);
|
||||||
height: '100%',
|
|
||||||
});
|
|
||||||
|
|
||||||
const [galleryMaxSize, setGalleryMaxSize] = useState<Size>({
|
const [shouldShowButtons, setShouldShowButtons] = useState<boolean>(
|
||||||
width: '590', // keep max at 590 for any tab
|
galleryWidth >= GALLERY_SHOW_BUTTONS_MIN_WIDTH
|
||||||
height: '100%',
|
);
|
||||||
});
|
|
||||||
|
|
||||||
const [galleryMinSize, setGalleryMinSize] = useState<Size>({
|
|
||||||
width: '300', // keep max at 590 for any tab
|
|
||||||
height: '100%',
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(gallerySize, galleryMaxSize, galleryMinSize);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeTabName === 'inpainting' && shouldPinGallery) {
|
if (!shouldPinGallery) return;
|
||||||
setGalleryMinSize((prevSize) => {
|
|
||||||
return { ...prevSize, width: 220 };
|
if (activeTabName === 'inpainting') {
|
||||||
});
|
dispatch(setGalleryWidth(220));
|
||||||
setGalleryMaxSize((prevSize) => {
|
setGalleryMinWidth(220);
|
||||||
return { ...prevSize, width: 220 };
|
setGalleryMaxWidth(220);
|
||||||
});
|
} else if (activeTabName === 'img2img') {
|
||||||
setGallerySize((prevSize) => {
|
dispatch(
|
||||||
return {
|
setGalleryWidth(Math.min(Math.max(Number(galleryWidth), 0), 490))
|
||||||
...prevSize,
|
);
|
||||||
width: Math.min(Math.max(Number(prevSize.width), 0), 220),
|
setGalleryMaxWidth(490);
|
||||||
};
|
|
||||||
});
|
|
||||||
} else if (activeTabName === 'img2img' && shouldPinGallery) {
|
|
||||||
setGalleryMaxSize((prevSize) => {
|
|
||||||
return { ...prevSize, width: 490, height: '100%' };
|
|
||||||
});
|
|
||||||
setGallerySize((prevSize) => {
|
|
||||||
return {
|
|
||||||
...prevSize,
|
|
||||||
width: Math.min(Math.max(Number(prevSize.width), 0), 490),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
setGalleryMaxSize((prevSize) => {
|
dispatch(
|
||||||
return { ...prevSize, width: 590, height: '100%' };
|
setGalleryWidth(Math.min(Math.max(Number(galleryWidth), 0), 590))
|
||||||
});
|
);
|
||||||
setGallerySize((prevSize) => {
|
setGalleryMaxWidth(590);
|
||||||
return {
|
|
||||||
...prevSize,
|
|
||||||
width: Math.min(Math.max(Number(prevSize.width), 0), 590),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [activeTabName, shouldPinGallery]);
|
}, [dispatch, activeTabName, shouldPinGallery, galleryWidth]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!shouldPinGallery) {
|
if (!shouldPinGallery) {
|
||||||
setGalleryMaxSize((prevSize) => {
|
setGalleryMaxWidth(window.innerWidth);
|
||||||
// calculate vh in px
|
|
||||||
return {
|
|
||||||
...prevSize,
|
|
||||||
width: window.innerWidth,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [shouldPinGallery]);
|
}, [shouldPinGallery]);
|
||||||
|
|
||||||
@ -126,10 +97,6 @@ export default function ImageGallery() {
|
|||||||
const handleSetShouldPinGallery = () => {
|
const handleSetShouldPinGallery = () => {
|
||||||
dispatch(setNeedsCache(true));
|
dispatch(setNeedsCache(true));
|
||||||
dispatch(setShouldPinGallery(!shouldPinGallery));
|
dispatch(setShouldPinGallery(!shouldPinGallery));
|
||||||
setGallerySize({
|
|
||||||
...gallerySize,
|
|
||||||
height: shouldPinGallery ? '100vh' : '100%',
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggleGallery = () => {
|
const handleToggleGallery = () => {
|
||||||
@ -300,9 +267,9 @@ export default function ImageGallery() {
|
|||||||
onMouseOver={!shouldPinGallery ? cancelCloseGalleryTimer : undefined}
|
onMouseOver={!shouldPinGallery ? cancelCloseGalleryTimer : undefined}
|
||||||
>
|
>
|
||||||
<Resizable
|
<Resizable
|
||||||
minWidth={galleryMinSize.width}
|
minWidth={galleryMinWidth}
|
||||||
maxWidth={galleryMaxSize.width}
|
maxWidth={galleryMaxWidth}
|
||||||
maxHeight={'100%'}
|
// maxHeight={'100%'}
|
||||||
className={'image-gallery-popup'}
|
className={'image-gallery-popup'}
|
||||||
handleStyles={{ left: { width: '15px' } }}
|
handleStyles={{ left: { width: '15px' } }}
|
||||||
enable={{
|
enable={{
|
||||||
@ -315,17 +282,25 @@ export default function ImageGallery() {
|
|||||||
bottomLeft: false,
|
bottomLeft: false,
|
||||||
topLeft: false,
|
topLeft: false,
|
||||||
}}
|
}}
|
||||||
size={gallerySize}
|
size={{
|
||||||
|
width: galleryWidth,
|
||||||
|
height: shouldPinGallery ? '100%' : '100vh',
|
||||||
|
}}
|
||||||
onResizeStop={(
|
onResizeStop={(
|
||||||
_event: MouseEvent | TouchEvent,
|
_event: MouseEvent | TouchEvent,
|
||||||
_direction: Direction,
|
_direction: Direction,
|
||||||
elementRef: HTMLElement,
|
elementRef: HTMLElement,
|
||||||
delta: NumberSize
|
delta: NumberSize
|
||||||
) => {
|
) => {
|
||||||
setGallerySize({
|
dispatch(
|
||||||
width: _.clamp(Number(gallerySize.width) + delta.width, 0, Number(galleryMaxSize.width)),
|
setGalleryWidth(
|
||||||
height: '100%',
|
_.clamp(
|
||||||
});
|
Number(galleryWidth) + delta.width,
|
||||||
|
0,
|
||||||
|
Number(galleryMaxWidth)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
elementRef.removeAttribute('data-resize-alert');
|
elementRef.removeAttribute('data-resize-alert');
|
||||||
}}
|
}}
|
||||||
onResize={(
|
onResize={(
|
||||||
@ -335,18 +310,21 @@ export default function ImageGallery() {
|
|||||||
delta: NumberSize
|
delta: NumberSize
|
||||||
) => {
|
) => {
|
||||||
const newWidth = _.clamp(
|
const newWidth = _.clamp(
|
||||||
Number(gallerySize.width) + delta.width,
|
Number(galleryWidth) + delta.width,
|
||||||
0,
|
0,
|
||||||
Number(galleryMaxSize.width)
|
Number(galleryMaxWidth)
|
||||||
);
|
);
|
||||||
if (newWidth >= galleryMaxSize.width) {
|
|
||||||
|
if (newWidth >= 320 && !shouldShowButtons) {
|
||||||
|
setShouldShowButtons(true);
|
||||||
|
} else if (newWidth < 320 && shouldShowButtons) {
|
||||||
|
setShouldShowButtons(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newWidth >= galleryMaxWidth) {
|
||||||
elementRef.setAttribute('data-resize-alert', 'true');
|
elementRef.setAttribute('data-resize-alert', 'true');
|
||||||
} else {
|
} else {
|
||||||
elementRef.removeAttribute('data-resize-alert');
|
elementRef.removeAttribute('data-resize-alert');
|
||||||
setGallerySize({
|
|
||||||
width: newWidth,
|
|
||||||
height: '100%',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -358,7 +336,7 @@ export default function ImageGallery() {
|
|||||||
variant="solid"
|
variant="solid"
|
||||||
className="image-gallery-category-btn-group"
|
className="image-gallery-category-btn-group"
|
||||||
>
|
>
|
||||||
{gallerySize.width > 320 ? (
|
{shouldShowButtons ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
data-selected={currentCategory === 'result'}
|
data-selected={currentCategory === 'result'}
|
||||||
@ -393,88 +371,92 @@ export default function ImageGallery() {
|
|||||||
)}
|
)}
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
<IAIPopover
|
<div>
|
||||||
trigger="hover"
|
<IAIPopover
|
||||||
hasArrow={activeTabName === 'inpainting' ? false : true}
|
trigger="hover"
|
||||||
placement={'left'}
|
hasArrow={activeTabName === 'inpainting' ? false : true}
|
||||||
triggerComponent={
|
placement={'left'}
|
||||||
<IAIIconButton
|
triggerComponent={
|
||||||
size={'sm'}
|
|
||||||
aria-label={'Gallery Settings'}
|
|
||||||
icon={<FaWrench />}
|
|
||||||
className="image-gallery-icon-btn"
|
|
||||||
cursor={'pointer'}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="image-gallery-settings-popover">
|
|
||||||
<div>
|
|
||||||
<IAISlider
|
|
||||||
value={galleryImageMinimumWidth}
|
|
||||||
onChange={handleChangeGalleryImageMinimumWidth}
|
|
||||||
min={32}
|
|
||||||
max={256}
|
|
||||||
width={100}
|
|
||||||
label={'Image Size'}
|
|
||||||
formLabelProps={{ style: { fontSize: '0.9rem' } }}
|
|
||||||
sliderThumbTooltipProps={{
|
|
||||||
label: `${galleryImageMinimumWidth}px`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
aria-label={'Reset'}
|
aria-label={'Gallery Settings'}
|
||||||
tooltip={'Reset Size'}
|
icon={<FaWrench />}
|
||||||
onClick={() => dispatch(setGalleryImageMinimumWidth(64))}
|
className="image-gallery-icon-btn"
|
||||||
icon={<BiReset />}
|
cursor={'pointer'}
|
||||||
data-selected={shouldPinGallery}
|
|
||||||
styleClass="image-gallery-icon-btn"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
}
|
||||||
<div>
|
>
|
||||||
<IAICheckbox
|
<div className="image-gallery-settings-popover">
|
||||||
label="Maintain Aspect Ratio"
|
<div>
|
||||||
isChecked={galleryImageObjectFit === 'contain'}
|
<IAISlider
|
||||||
onChange={() =>
|
value={galleryImageMinimumWidth}
|
||||||
dispatch(
|
onChange={handleChangeGalleryImageMinimumWidth}
|
||||||
setGalleryImageObjectFit(
|
min={32}
|
||||||
galleryImageObjectFit === 'contain'
|
max={256}
|
||||||
? 'cover'
|
width={100}
|
||||||
: 'contain'
|
label={'Image Size'}
|
||||||
|
formLabelProps={{ style: { fontSize: '0.9rem' } }}
|
||||||
|
sliderThumbTooltipProps={{
|
||||||
|
label: `${galleryImageMinimumWidth}px`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IAIIconButton
|
||||||
|
size={'sm'}
|
||||||
|
aria-label={'Reset'}
|
||||||
|
tooltip={'Reset Size'}
|
||||||
|
onClick={() => dispatch(setGalleryImageMinimumWidth(64))}
|
||||||
|
icon={<BiReset />}
|
||||||
|
data-selected={shouldPinGallery}
|
||||||
|
styleClass="image-gallery-icon-btn"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<IAICheckbox
|
||||||
|
label="Maintain Aspect Ratio"
|
||||||
|
isChecked={galleryImageObjectFit === 'contain'}
|
||||||
|
onChange={() =>
|
||||||
|
dispatch(
|
||||||
|
setGalleryImageObjectFit(
|
||||||
|
galleryImageObjectFit === 'contain'
|
||||||
|
? 'cover'
|
||||||
|
: 'contain'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
/>
|
||||||
/>
|
</div>
|
||||||
|
<div>
|
||||||
|
<IAICheckbox
|
||||||
|
label="Auto-Switch to New Images"
|
||||||
|
isChecked={shouldAutoSwitchToNewImages}
|
||||||
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
dispatch(
|
||||||
|
setShouldAutoSwitchToNewImages(e.target.checked)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</IAIPopover>
|
||||||
<IAICheckbox
|
|
||||||
label="Auto-Switch to New Images"
|
|
||||||
isChecked={shouldAutoSwitchToNewImages}
|
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
|
||||||
dispatch(setShouldAutoSwitchToNewImages(e.target.checked))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</IAIPopover>
|
|
||||||
|
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
aria-label={'Pin Gallery'}
|
aria-label={'Pin Gallery'}
|
||||||
tooltip={'Pin Gallery (Shift+P)'}
|
tooltip={'Pin Gallery (Shift+P)'}
|
||||||
onClick={handleSetShouldPinGallery}
|
onClick={handleSetShouldPinGallery}
|
||||||
icon={<BsPinAngleFill />}
|
icon={<BsPinAngleFill />}
|
||||||
data-selected={shouldPinGallery}
|
data-selected={shouldPinGallery}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
aria-label={'Close Gallery'}
|
aria-label={'Close Gallery'}
|
||||||
tooltip={'Close Gallery (G)'}
|
tooltip={'Close Gallery (G)'}
|
||||||
onClick={handleCloseGallery}
|
onClick={handleCloseGallery}
|
||||||
className="image-gallery-icon-btn"
|
className="image-gallery-icon-btn"
|
||||||
icon={<MdClear />}
|
icon={<MdClear />}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="image-gallery-container" ref={galleryContainerRef}>
|
<div className="image-gallery-container" ref={galleryContainerRef}>
|
||||||
{images.length || areMoreImagesAvailable ? (
|
{images.length || areMoreImagesAvailable ? (
|
||||||
|
@ -36,6 +36,7 @@ export interface GalleryState {
|
|||||||
result: Gallery;
|
result: Gallery;
|
||||||
};
|
};
|
||||||
currentCategory: GalleryCategory;
|
currentCategory: GalleryCategory;
|
||||||
|
galleryWidth: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: GalleryState = {
|
const initialState: GalleryState = {
|
||||||
@ -62,6 +63,7 @@ const initialState: GalleryState = {
|
|||||||
areMoreImagesAvailable: true,
|
areMoreImagesAvailable: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
galleryWidth: 300,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const gallerySlice = createSlice({
|
export const gallerySlice = createSlice({
|
||||||
@ -248,6 +250,9 @@ export const gallerySlice = createSlice({
|
|||||||
setCurrentCategory: (state, action: PayloadAction<GalleryCategory>) => {
|
setCurrentCategory: (state, action: PayloadAction<GalleryCategory>) => {
|
||||||
state.currentCategory = action.payload;
|
state.currentCategory = action.payload;
|
||||||
},
|
},
|
||||||
|
setGalleryWidth: (state, action: PayloadAction<number>) => {
|
||||||
|
state.galleryWidth = action.payload;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -268,6 +273,7 @@ export const {
|
|||||||
setShouldHoldGalleryOpen,
|
setShouldHoldGalleryOpen,
|
||||||
setShouldAutoSwitchToNewImages,
|
setShouldAutoSwitchToNewImages,
|
||||||
setCurrentCategory,
|
setCurrentCategory,
|
||||||
|
setGalleryWidth,
|
||||||
} = gallerySlice.actions;
|
} = gallerySlice.actions;
|
||||||
|
|
||||||
export default gallerySlice.reducer;
|
export default gallerySlice.reducer;
|
||||||
|
@ -18,6 +18,7 @@ export const imageGallerySelector = createSelector(
|
|||||||
galleryImageObjectFit,
|
galleryImageObjectFit,
|
||||||
shouldHoldGalleryOpen,
|
shouldHoldGalleryOpen,
|
||||||
shouldAutoSwitchToNewImages,
|
shouldAutoSwitchToNewImages,
|
||||||
|
galleryWidth,
|
||||||
} = gallery;
|
} = gallery;
|
||||||
|
|
||||||
const { activeTab } = options;
|
const { activeTab } = options;
|
||||||
@ -37,6 +38,7 @@ export const imageGallerySelector = createSelector(
|
|||||||
areMoreImagesAvailable:
|
areMoreImagesAvailable:
|
||||||
categories[currentCategory].areMoreImagesAvailable,
|
categories[currentCategory].areMoreImagesAvailable,
|
||||||
currentCategory,
|
currentCategory,
|
||||||
|
galleryWidth,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user