mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-07 02:21:23 +00:00
contextual button for retry done (#12)
Co-authored-by: wil <wil@storyteller.ai>
This commit is contained in:
1 parent
b8b7668d56
commit
aeb2c7d519
9 files changed
+205
-4
No files matched your search
@@ -0,0 +1,35 @@
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { faRotateRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { Button } from "~/components/ui";
|
||||
import { uiAccess } from "~/signals/uiAccess";
|
||||
import { dispatchUiEvents } from "~/signals";
|
||||
import { transitionTimingStyles } from "~/components/styles";
|
||||
|
||||
export const ContextualButtonRetry = () => {
|
||||
const { isShowing, position, ...rest } = uiAccess.buttonRetry.signal.value;
|
||||
const handleOnClick = (
|
||||
e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
||||
) => {
|
||||
dispatchUiEvents.buttonRetry.onClick(e);
|
||||
};
|
||||
return (
|
||||
<Transition show={isShowing}>
|
||||
<div
|
||||
className={twMerge(
|
||||
transitionTimingStyles,
|
||||
"fixed data-[closed]:opacity-0",
|
||||
)}
|
||||
style={{
|
||||
top: position.y,
|
||||
left: position.x,
|
||||
}}
|
||||
>
|
||||
<Button icon={faRotateRight} onClick={handleOnClick} {...rest}>
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useRef } from "react";
|
||||
import { useRenderCounter } from "~/hooks/useRenderCounter";
|
||||
|
||||
import { KonvaCanvasContainer } from "./KonvaCanvasContainer";
|
||||
import { ContextualButtonRetry } from "./ContextualButtonRetry";
|
||||
import { ContextualToolbarImage } from "./ContextualToolbarImage";
|
||||
import { ContextualLoadingBar } from "./ContextualLoadingBar";
|
||||
import { SignaledToolbarMain } from "./SignaledToolbarMain";
|
||||
@@ -41,15 +42,14 @@ export const KonvaRootComponent = ({ className }: { className: string }) => {
|
||||
>
|
||||
Error Dialogue Test
|
||||
</Button>
|
||||
|
||||
<KonvaCanvasContainer
|
||||
ref={konvaContainerCallbackRef}
|
||||
className={className}
|
||||
// retreive the classNames from the parent for sizing/styling
|
||||
/>
|
||||
|
||||
<SignaledToolbarMain />
|
||||
<ContextualToolbarImage />
|
||||
<ContextualButtonRetry />
|
||||
<ContextualLoadingBar />
|
||||
<SignaledDialogError />
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { useState } from "react";
|
||||
import { uiAccess } from "~/signals/uiAccess";
|
||||
import { uiEvents } from "~/signals/uiEvents";
|
||||
|
||||
import { Input, Button } from "~/components/ui";
|
||||
|
||||
export const ContextualButtonRetryForm = () => {
|
||||
uiEvents.buttonRetry.onClick(() => {
|
||||
console.log(
|
||||
"uiEvents.buttonRetry.onClick calls uiAccess.buttonRetry.disable",
|
||||
);
|
||||
uiAccess.buttonRetry.disable();
|
||||
});
|
||||
const buttonRetry = uiAccess.buttonRetry;
|
||||
|
||||
const [state, setState] = useState({
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
isShowing: false,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="font-bold">Retry Button Props</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<label>X:</label>
|
||||
<Input
|
||||
className="w-20"
|
||||
type="text"
|
||||
placeholder="X"
|
||||
value={state.position.x}
|
||||
onChange={(e) => {
|
||||
setState((curr) => ({
|
||||
...curr,
|
||||
position: {
|
||||
...curr.position,
|
||||
x: parseInt(e.target.value) || 0,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<label>Y:</label>
|
||||
<Input
|
||||
className="w-20"
|
||||
type="text"
|
||||
placeholder="Y"
|
||||
value={state.position.y}
|
||||
onChange={(e) => {
|
||||
setState((curr) => ({
|
||||
...curr,
|
||||
position: {
|
||||
...curr.position,
|
||||
y: parseInt(e.target.value) || 0,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<Button onClick={() => buttonRetry.show(state)}>Show</Button>
|
||||
<Button onClick={() => buttonRetry.updatePosition(state.position)}>
|
||||
Update
|
||||
</Button>
|
||||
<Button onClick={() => buttonRetry.hide()}>Hide</Button>
|
||||
<Button onClick={() => buttonRetry.enable()}>Enable</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,12 +1,13 @@
|
||||
import { ContextualToolbarImage } from "~/KonvaRootComponent/ContextualToolbarImage";
|
||||
import { ContextualLoadingBar } from "~/KonvaRootComponent/ContextualLoadingBar";
|
||||
|
||||
import { ContextualButtonRetry } from "~/KonvaRootComponent/ContextualButtonRetry";
|
||||
import { SignaledDialogError } from "~/KonvaRootComponent/SignaledComponents";
|
||||
|
||||
import { useRenderCounter } from "~/hooks/useRenderCounter";
|
||||
|
||||
import { ContextualToolbarForm } from "./ContextualToolbarForm";
|
||||
import { ContextualLoadingBarForm } from "./ContextualLoadingBarForm";
|
||||
import { ContextualButtonRetryForm } from "./ContextualButtonRetryForm";
|
||||
import { DialogErrorForm } from "./DialogErrorForm";
|
||||
|
||||
import { ButtonTestTester } from "./ButtonTestTester";
|
||||
@@ -21,11 +22,13 @@ export const Sandbox = () => {
|
||||
<ButtonTestTester />
|
||||
<ContextualToolbarForm />
|
||||
<ContextualLoadingBarForm />
|
||||
<ContextualButtonRetryForm />
|
||||
<DialogErrorForm />
|
||||
</div>
|
||||
|
||||
<ContextualToolbarImage />
|
||||
<ContextualLoadingBar />
|
||||
<ContextualButtonRetry />
|
||||
<SignaledDialogError />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { ContextualButtonRetryProps } from "./type";
|
||||
const buttonRetrySignal = signal<ContextualButtonRetryProps>({
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
isShowing: false,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
export const buttonRetry = {
|
||||
signal: buttonRetrySignal,
|
||||
disable: () => {
|
||||
buttonRetrySignal.value = { ...buttonRetrySignal.value, disabled: true };
|
||||
},
|
||||
updatePosition(position: { x: number; y: number }) {
|
||||
buttonRetrySignal.value = {
|
||||
...buttonRetrySignal.value,
|
||||
position,
|
||||
};
|
||||
},
|
||||
enable: () => {
|
||||
buttonRetrySignal.value = {
|
||||
...buttonRetrySignal.value,
|
||||
disabled: false,
|
||||
};
|
||||
},
|
||||
show(props: Omit<ContextualButtonRetryProps, "isShowing">) {
|
||||
if (buttonRetrySignal.value.isShowing) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn(
|
||||
"Loading bar is already showing",
|
||||
"use the `updatePosition` methods instead",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
buttonRetrySignal.value = {
|
||||
...props,
|
||||
isShowing: true,
|
||||
};
|
||||
},
|
||||
hide() {
|
||||
buttonRetrySignal.value = {
|
||||
...buttonRetrySignal.value,
|
||||
isShowing: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -6,8 +6,8 @@ type uiAccessType = {
|
||||
|
||||
export type { uiAccessType };
|
||||
|
||||
import { buttonRetry } from "./buttonRetry";
|
||||
import { buttonTest } from "./buttonTest";
|
||||
|
||||
import { dialogueError } from "./dialogueError";
|
||||
import { loadingBar } from "./loadingBar";
|
||||
import { toolbarImage } from "./toolbarImage";
|
||||
@@ -15,6 +15,7 @@ import { toolbarVideo } from "./toolbarVideo";
|
||||
import { toolbarMain } from "./toolbarMain";
|
||||
|
||||
export const uiAccess = {
|
||||
buttonRetry,
|
||||
buttonTest,
|
||||
dialogueError,
|
||||
toolbarImage,
|
||||
|
||||
@@ -11,3 +11,7 @@ export interface ContextualUi {
|
||||
export interface ContextualLoadingBarProps
|
||||
extends ContextualUi,
|
||||
Omit<LoadingBarProps, "position"> {}
|
||||
|
||||
export interface ContextualButtonRetryProps extends ContextualUi {
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { MouseEventHandler } from "react";
|
||||
import { signal, effect } from "@preact/signals-react";
|
||||
|
||||
const event = signal<
|
||||
React.MouseEvent<HTMLButtonElement, MouseEvent> | undefined
|
||||
>(undefined);
|
||||
|
||||
let effectCleanup: (() => void) | undefined = undefined;
|
||||
|
||||
export const eventsHandler = {
|
||||
onClick: (callback: MouseEventHandler<HTMLButtonElement>) => {
|
||||
if (effectCleanup) {
|
||||
effectCleanup();
|
||||
}
|
||||
effectCleanup = effect(() => {
|
||||
if (event.value) {
|
||||
callback(event.value);
|
||||
event.value = undefined;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const dispatcher = {
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
console.log("Button Retry dispatcher");
|
||||
event.value = e;
|
||||
},
|
||||
};
|
||||
@@ -18,11 +18,16 @@ import {
|
||||
dispatcher as buttonTestDispatcher,
|
||||
eventsHandler as buttonTestEvent,
|
||||
} from "./buttonTest";
|
||||
import {
|
||||
dispatcher as buttonRetryDispatcher,
|
||||
eventsHandler as buttonRetryEvent,
|
||||
} from "./buttonRetry";
|
||||
import { requestAiStylize, onRequestAiStylize } from "./aiStylize";
|
||||
|
||||
export const uiEvents = {
|
||||
...addMediaEvents,
|
||||
onRequestAiStylize,
|
||||
buttonRetry: buttonRetryEvent,
|
||||
buttonTest: buttonTestEvent,
|
||||
toolbarImage: toolbarImageEvents,
|
||||
toolbarMain: toolbarMainEvents,
|
||||
@@ -31,6 +36,7 @@ export const uiEvents = {
|
||||
export const dispatchUiEvents = {
|
||||
...addMediaDispatchers,
|
||||
requestAiStylize,
|
||||
buttonRetry: buttonRetryDispatcher,
|
||||
buttonTest: buttonTestDispatcher,
|
||||
toolbarImage: toolbarImageDispatchers,
|
||||
toolbarMain: toolbarMainDispatchers,
|
||||
|
||||
Reference in new issue
Block a user