feat(ui): add contexts/hooks to access entity adapters directly

This commit is contained in:
psychedelicious 2024-08-21 19:01:28 +10:00
parent 5a3127949b
commit 757bd62ebe
8 changed files with 146 additions and 50 deletions

View File

@ -7,6 +7,7 @@ import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/c
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
import { ControlLayerControlAdapter } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapter';
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { EntityLayerAdapterProviderGate } from 'features/controlLayers/hooks/useEntityLayerAdapter';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { memo, useMemo } from 'react';
@ -19,17 +20,19 @@ export const ControlLayer = memo(({ id }: Props) => {
return (
<EntityIdentifierContext.Provider value={entityIdentifier}>
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityDeleteButton />
</CanvasEntityHeader>
<CanvasEntitySettingsWrapper>
<ControlLayerControlAdapter />
</CanvasEntitySettingsWrapper>
</CanvasEntityContainer>
<EntityLayerAdapterProviderGate>
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityDeleteButton />
</CanvasEntityHeader>
<CanvasEntitySettingsWrapper>
<ControlLayerControlAdapter />
</CanvasEntitySettingsWrapper>
</CanvasEntityContainer>
</EntityLayerAdapterProviderGate>
</EntityIdentifierContext.Provider>
);
});

View File

@ -4,6 +4,7 @@ import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/com
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { EntityMaskAdapterProviderGate } from 'features/controlLayers/hooks/useEntityMaskAdapter';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { memo, useMemo } from 'react';
@ -18,14 +19,16 @@ export const InpaintMask = memo(({ id }: Props) => {
return (
<EntityIdentifierContext.Provider value={entityIdentifier}>
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<InpaintMaskMaskFillColorPicker />
</CanvasEntityHeader>
</CanvasEntityContainer>
<EntityMaskAdapterProviderGate>
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<InpaintMaskMaskFillColorPicker />
</CanvasEntityHeader>
</CanvasEntityContainer>
</EntityMaskAdapterProviderGate>
</EntityIdentifierContext.Provider>
);
});

View File

@ -5,6 +5,7 @@ import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/com
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { EntityLayerAdapterProviderGate } from 'features/controlLayers/hooks/useEntityLayerAdapter';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { memo, useMemo } from 'react';
@ -17,14 +18,16 @@ export const RasterLayer = memo(({ id }: Props) => {
return (
<EntityIdentifierContext.Provider value={entityIdentifier}>
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityDeleteButton />
</CanvasEntityHeader>
</CanvasEntityContainer>
<EntityLayerAdapterProviderGate>
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityDeleteButton />
</CanvasEntityHeader>
</CanvasEntityContainer>
</EntityLayerAdapterProviderGate>
</EntityIdentifierContext.Provider>
);
});

View File

@ -7,6 +7,7 @@ import { CanvasEntityEditableTitle } from 'features/controlLayers/components/com
import { RegionalGuidanceBadges } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges';
import { RegionalGuidanceSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings';
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { EntityMaskAdapterProviderGate } from 'features/controlLayers/hooks/useEntityMaskAdapter';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { memo, useMemo } from 'react';
@ -22,18 +23,20 @@ export const RegionalGuidance = memo(({ id }: Props) => {
return (
<EntityIdentifierContext.Provider value={entityIdentifier}>
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<RegionalGuidanceBadges />
<RegionalGuidanceMaskFillColorPicker />
<RegionalGuidanceSettingsPopover />
<CanvasEntityDeleteButton />
</CanvasEntityHeader>
<RegionalGuidanceSettings />
</CanvasEntityContainer>
<EntityMaskAdapterProviderGate>
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<RegionalGuidanceBadges />
<RegionalGuidanceMaskFillColorPicker />
<RegionalGuidanceSettingsPopover />
<CanvasEntityDeleteButton />
</CanvasEntityHeader>
<RegionalGuidanceSettings />
</CanvasEntityContainer>
</EntityMaskAdapterProviderGate>
</EntityIdentifierContext.Provider>
);
});

View File

@ -6,12 +6,6 @@ import { assert } from 'tsafe';
const CanvasManagerContext = createContext<CanvasManager | null>(null);
export const useCanvasManager = (): CanvasManager => {
const canvasManager = useContext(CanvasManagerContext);
assert(canvasManager, 'useCanvasManagerContext must be used within a CanvasManagerContext');
return canvasManager;
};
export const CanvasManagerProviderGate = memo(({ children }: PropsWithChildren) => {
const canvasManager = useStore($canvasManager);
@ -23,3 +17,9 @@ export const CanvasManagerProviderGate = memo(({ children }: PropsWithChildren)
});
CanvasManagerProviderGate.displayName = 'CanvasManagerProviderGate';
export const useCanvasManager = (): CanvasManager => {
const canvasManager = useContext(CanvasManagerContext);
assert(canvasManager, 'useCanvasManagerContext must be used within a CanvasManagerProviderGate');
return canvasManager;
};

View File

@ -1,8 +1,18 @@
import { useStore } from '@nanostores/react';
import { $canvasManager } from 'features/controlLayers/konva/CanvasManager';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { useMemo } from 'react';
import { assert } from 'tsafe';
export const useEntityAdapter = (entityIdentifier: CanvasEntityIdentifier) => {
const canvasManager = useStore($canvasManager);
console.log(canvasManager);
export const useEntityAdapter = (entityIdentifier: CanvasEntityIdentifier): CanvasLayerAdapter | CanvasMaskAdapter => {
const canvasManager = useCanvasManager();
const adapter = useMemo(() => {
const entity = canvasManager.stateApi.getEntity(entityIdentifier);
assert(entity, 'Entity adapter not found');
return entity.adapter;
}, [canvasManager, entityIdentifier]);
return adapter;
};

View File

@ -0,0 +1,37 @@
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
import type { PropsWithChildren } from 'react';
import { createContext, memo, useContext, useMemo } from 'react';
import { assert } from 'tsafe';
const EntityLayerAdapterContext = createContext<CanvasLayerAdapter | null>(null);
export const EntityLayerAdapterProviderGate = memo(({ children }: PropsWithChildren) => {
const entityIdentifier = useEntityIdentifierContext();
const canvasManager = useCanvasManager();
const adapter = useMemo(() => {
if (entityIdentifier.type === 'raster_layer') {
return canvasManager.rasterLayerAdapters.get(entityIdentifier.id) ?? null;
} else if (entityIdentifier.type === 'control_layer') {
return canvasManager.controlLayerAdapters.get(entityIdentifier.id) ?? null;
}
assert(false, 'EntityLayerAdapterProviderGate must be used with a valid EntityIdentifierContext');
}, [canvasManager, entityIdentifier]);
if (!canvasManager) {
return null;
}
return <EntityLayerAdapterContext.Provider value={adapter}>{children}</EntityLayerAdapterContext.Provider>;
});
EntityLayerAdapterProviderGate.displayName = 'EntityLayerAdapterProviderGate';
export const useEntityLayerAdapter = (): CanvasLayerAdapter => {
const adapter = useContext(EntityLayerAdapterContext);
assert(adapter, 'useEntityLayerAdapter must be used within a EntityLayerAdapterProviderGate');
return adapter;
};

View File

@ -0,0 +1,37 @@
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
import type { PropsWithChildren } from 'react';
import { createContext, memo, useContext, useMemo } from 'react';
import { assert } from 'tsafe';
const EntityMaskAdapterContext = createContext<CanvasMaskAdapter | null>(null);
export const EntityMaskAdapterProviderGate = memo(({ children }: PropsWithChildren) => {
const entityIdentifier = useEntityIdentifierContext();
const canvasManager = useCanvasManager();
const adapter = useMemo(() => {
if (entityIdentifier.type === 'inpaint_mask') {
return canvasManager.inpaintMaskAdapters.get(entityIdentifier.id) ?? null;
} else if (entityIdentifier.type === 'regional_guidance') {
return canvasManager.regionalGuidanceAdapters.get(entityIdentifier.id) ?? null;
}
assert(false, 'EntityMaskAdapterProviderGate must be used with a valid EntityIdentifierContext');
}, [canvasManager, entityIdentifier]);
if (!canvasManager) {
return null;
}
return <EntityMaskAdapterContext.Provider value={adapter}>{children}</EntityMaskAdapterContext.Provider>;
});
EntityMaskAdapterProviderGate.displayName = 'EntityMaskAdapterProviderGate';
export const useEntityMaskAdapter = (): CanvasMaskAdapter => {
const adapter = useContext(EntityMaskAdapterContext);
assert(adapter, 'useEntityMaskAdapter must be used within a EntityLayerAdapterProviderGate');
return adapter;
};