mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-07 02:21:23 +00:00
uievent more structurized, uieventdispathcers now has structure, imagetoolbar events
This commit is contained in:
1 parent
6b7506c5f1
commit
c3f1837b6d
9 files changed
+168
-98
No files matched your search
@@ -1,23 +1,45 @@
|
||||
import { MouseEventHandler } from "react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { ToolbarImage } from "~/components/features/ToolbarImage";
|
||||
import { uiAccess } from "~/signals/uiAccess";
|
||||
import { dispatchUiEvents } from "~/signals/uiEvents";
|
||||
|
||||
import { ToolbarImageButtonNames } from "~/components/features/ToolbarImage/enums";
|
||||
import { transitionTimingStyles } from "~/components/styles";
|
||||
|
||||
export const ContextualToolbarImage = () => {
|
||||
useSignals();
|
||||
|
||||
const { isShowing, ...rest } = uiAccess.imageToolbar.signal.value;
|
||||
|
||||
const { isShowing, position, ...rest } = uiAccess.imageToolbar.signal.value;
|
||||
const buttonsProps = Object.values(ToolbarImageButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = {
|
||||
onClick: dispatchUiEvents.imageToolbar[buttonName],
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ToolbarImageButtonNames]: {
|
||||
onClick: MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
},
|
||||
);
|
||||
return (
|
||||
<Transition show={isShowing}>
|
||||
<div
|
||||
className={twMerge(transitionTimingStyles, "data-[closed]:opacity-0")}
|
||||
className={twMerge(
|
||||
transitionTimingStyles,
|
||||
"fixed data-[closed]:opacity-0",
|
||||
)}
|
||||
style={{
|
||||
top: position.y,
|
||||
left: position.x,
|
||||
}}
|
||||
>
|
||||
<ToolbarImage {...rest} />
|
||||
<ToolbarImage {...rest} buttonsProps={buttonsProps} />
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
|
||||
+12
-4
@@ -6,33 +6,41 @@ import {
|
||||
} from "@fortawesome/react-fontawesome";
|
||||
import { IconDefinition } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
export const ToolbarButtons = ({
|
||||
export interface ToolbarButtonProps
|
||||
extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export const ToolbarButton = ({
|
||||
icon,
|
||||
onClick,
|
||||
buttonProps = { className: "" },
|
||||
buttonProps = {},
|
||||
iconProps,
|
||||
}: {
|
||||
icon: IconDefinition;
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||
buttonProps?: ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
buttonProps?: ToolbarButtonProps;
|
||||
iconProps?: Omit<FontAwesomeIconProps, "icon">;
|
||||
}) => {
|
||||
const {
|
||||
className: customButtonClassNames,
|
||||
disabled,
|
||||
active,
|
||||
onClick: customOnClick,
|
||||
...restButtonProps
|
||||
} = buttonProps;
|
||||
const mergedButtonClasses = twMerge(
|
||||
"size-10 rounded-2xl p-2 hover:bg-secondary-500 hover:text-white",
|
||||
disabled && "pointer-events-none text-secondary-300",
|
||||
active && "pointer-events-none text-primary ",
|
||||
customButtonClassNames,
|
||||
);
|
||||
return (
|
||||
<button
|
||||
className={mergedButtonClasses}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
{...restButtonProps}
|
||||
onClick={onClick ? onClick : customOnClick}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} {...iconProps} />
|
||||
</button>
|
||||
@@ -1,66 +1,42 @@
|
||||
import { MouseEventHandler } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { faImage } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { ToolbarButtons } from "~/components/features/ToolbarButtons";
|
||||
import {
|
||||
ToolbarButton,
|
||||
ToolbarButtonProps,
|
||||
} from "~/components/features/ToolbarButton";
|
||||
import { paperWrapperStyles } from "~/components/styles";
|
||||
import { ToolbarImageButtonNames } from "./enums";
|
||||
import { ToolbarImageButtonData } from "./data";
|
||||
|
||||
export const ToolbarImage = ({
|
||||
position,
|
||||
disabled,
|
||||
buttonStates,
|
||||
buttonCallbacks,
|
||||
}: {
|
||||
position: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
export interface ToolbarImageProps {
|
||||
disabled?: boolean;
|
||||
buttonStates?: {
|
||||
[key in ToolbarImageButtonNames]: {
|
||||
disabled: boolean;
|
||||
};
|
||||
buttonsProps?: {
|
||||
[key in ToolbarImageButtonNames]: ToolbarButtonProps;
|
||||
};
|
||||
buttonCallbacks?: {
|
||||
[key in ToolbarImageButtonNames]:
|
||||
| MouseEventHandler<HTMLButtonElement>
|
||||
| undefined;
|
||||
};
|
||||
}) => {
|
||||
}
|
||||
export const ToolbarImage = ({ disabled, buttonsProps }: ToolbarImageProps) => {
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
paperWrapperStyles,
|
||||
disabled && "pointer-events-none cursor-default bg-ui-border shadow-md",
|
||||
"fixed flex gap-2 transition",
|
||||
"flex gap-2 transition",
|
||||
)}
|
||||
style={{
|
||||
top: position.y,
|
||||
left: position.x,
|
||||
}}
|
||||
>
|
||||
<div className="flex size-10 items-center justify-center rounded-3xl bg-ui-border p-2">
|
||||
<FontAwesomeIcon icon={faImage} />
|
||||
</div>
|
||||
{ToolbarImageButtonData.map((buttonDatum, idx) => {
|
||||
const buttonProps =
|
||||
buttonStates && buttonStates[buttonDatum.name]
|
||||
? { disabled: buttonStates[buttonDatum.name].disabled }
|
||||
: undefined;
|
||||
const buttonCallback =
|
||||
buttonCallbacks && buttonCallbacks[buttonDatum.name]
|
||||
? buttonCallbacks[buttonDatum.name]
|
||||
: undefined;
|
||||
const buttonProps = buttonsProps?.[buttonDatum.name];
|
||||
|
||||
return (
|
||||
<ToolbarButtons
|
||||
<ToolbarButton
|
||||
icon={buttonDatum.icon}
|
||||
key={idx}
|
||||
buttonProps={buttonProps}
|
||||
onClick={buttonCallback}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -9,17 +9,13 @@ import {
|
||||
faFilePlus,
|
||||
faFilm,
|
||||
faFloppyDisk,
|
||||
// faHand,
|
||||
faHatWizard,
|
||||
faImage,
|
||||
faLocationArrow,
|
||||
|
||||
// faPlus,
|
||||
// faMinus,
|
||||
faSquareDashed,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { ToolbarButtons } from "../ToolbarButtons";
|
||||
import { ToolbarButton } from "../ToolbarButton";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { UploadImage } from "../UploadImage";
|
||||
@@ -76,14 +72,14 @@ export const ToolbarMain = () => {
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2 px-2">
|
||||
<ToolbarButtons icon={faBars} />
|
||||
<ToolbarButtons
|
||||
<ToolbarButton icon={faBars} />
|
||||
<ToolbarButton
|
||||
icon={faLocationArrow}
|
||||
iconProps={{ className: "fa-flip-horizontal" }}
|
||||
/>
|
||||
<ToolbarButtons icon={faSquareDashed} />
|
||||
<ToolbarButton icon={faSquareDashed} />
|
||||
<div className="relative">
|
||||
<ToolbarButtons
|
||||
<ToolbarButton
|
||||
icon={faFilePlus}
|
||||
onClick={() => {
|
||||
setState({ ...state, isUploadSubmenuOpen: true });
|
||||
@@ -96,7 +92,7 @@ export const ToolbarMain = () => {
|
||||
paperWrapperStyles,
|
||||
)}
|
||||
>
|
||||
<ToolbarButtons
|
||||
<ToolbarButton
|
||||
icon={faImage}
|
||||
onClick={() =>
|
||||
setState({
|
||||
@@ -106,7 +102,7 @@ export const ToolbarMain = () => {
|
||||
})
|
||||
}
|
||||
/>
|
||||
<ToolbarButtons
|
||||
<ToolbarButton
|
||||
icon={faFilm}
|
||||
onClick={() =>
|
||||
setState({
|
||||
@@ -119,14 +115,14 @@ export const ToolbarMain = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ToolbarButtons icon={faCameraRotate} />
|
||||
<ToolbarButtons icon={faHatWizard} />
|
||||
<ToolbarButton icon={faCameraRotate} />
|
||||
<ToolbarButton icon={faHatWizard} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-2">
|
||||
<ToolbarButtons icon={faArrowRotateLeft} />
|
||||
<ToolbarButtons icon={faArrowRotateRight} />
|
||||
<ToolbarButtons icon={faFloppyDisk} />
|
||||
<ToolbarButtons icon={faDownload} />
|
||||
<ToolbarButton icon={faArrowRotateLeft} />
|
||||
<ToolbarButton icon={faArrowRotateRight} />
|
||||
<ToolbarButton icon={faFloppyDisk} />
|
||||
<ToolbarButton icon={faDownload} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { FileUploader, IMAGE_FILE_TYPE } from "../FileUploader";
|
||||
import { Button } from "~/components/ui";
|
||||
|
||||
import { paperWrapperStyles } from "~/components/styles";
|
||||
import { addImageToEngine } from "~/signals/uiEvents";
|
||||
import { dispatchUiEvents } from "~/signals/uiEvents/";
|
||||
|
||||
export const UploadImage = ({
|
||||
isOpen,
|
||||
@@ -24,7 +24,7 @@ export const UploadImage = ({
|
||||
|
||||
function handleEnter() {
|
||||
if (assetFile) {
|
||||
addImageToEngine(assetFile);
|
||||
dispatchUiEvents.addImageToEngine(assetFile);
|
||||
}
|
||||
handleClose();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { FileUploader, VIDEO_FILE_TYPE } from "../FileUploader";
|
||||
import { Button } from "~/components/ui";
|
||||
|
||||
import { paperWrapperStyles } from "~/components/styles";
|
||||
import { addVideoToEngine } from "~/signals/uiEvents";
|
||||
import { dispatchUiEvents } from "~/signals/uiEvents";
|
||||
|
||||
export const UploadVideo = ({
|
||||
isOpen,
|
||||
@@ -24,7 +24,7 @@ export const UploadVideo = ({
|
||||
|
||||
function handleEnter() {
|
||||
if (assetFile) {
|
||||
addVideoToEngine(assetFile);
|
||||
dispatchUiEvents.addVideoToEngine(assetFile);
|
||||
}
|
||||
handleClose();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
import { effect } from "@preact/signals-react";
|
||||
|
||||
const stagedImage = signal<File | null>(null);
|
||||
|
||||
const addImageToEngine = (image: File) => {
|
||||
stagedImage.value = image;
|
||||
};
|
||||
|
||||
const onGetStagedImage = (callback: (file: File) => void) => {
|
||||
effect(() => {
|
||||
if (stagedImage.value) {
|
||||
callback(stagedImage.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const stagedVideo = signal<File | null>(null);
|
||||
|
||||
const addVideoToEngine = (video: File) => {
|
||||
stagedVideo.value = video;
|
||||
};
|
||||
|
||||
const onGetStagedVideo = (callback: (file: File) => void) => {
|
||||
effect(() => {
|
||||
if (stagedVideo.value) {
|
||||
callback(stagedVideo.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const dispatchers = {
|
||||
addImageToEngine,
|
||||
addVideoToEngine,
|
||||
};
|
||||
|
||||
export const events = {
|
||||
onGetStagedImage,
|
||||
onGetStagedVideo,
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import { MouseEventHandler } from "react";
|
||||
import { signal, effect } from "@preact/signals-core";
|
||||
|
||||
import { ToolbarImageButtonNames } from "~/components/features/ToolbarImage/enums";
|
||||
|
||||
const events = signal<{
|
||||
[key in ToolbarImageButtonNames]?: React.MouseEvent<
|
||||
HTMLButtonElement,
|
||||
MouseEvent
|
||||
>;
|
||||
}>();
|
||||
|
||||
export const eventsHandlers = Object.values(ToolbarImageButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = {
|
||||
onClick: (callback: MouseEventHandler<HTMLButtonElement>) => {
|
||||
effect(() => {
|
||||
if (events.value?.[buttonName]) {
|
||||
callback(events.value?.[buttonName]);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ToolbarImageButtonNames]: {
|
||||
onClick: (callback: MouseEventHandler<HTMLButtonElement>) => void;
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const dispatchers = Object.values(ToolbarImageButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
events.value = {
|
||||
...events.value,
|
||||
[buttonName]: e,
|
||||
};
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as {
|
||||
[key in ToolbarImageButtonNames]: MouseEventHandler<HTMLButtonElement>;
|
||||
},
|
||||
);
|
||||
@@ -1,35 +1,17 @@
|
||||
import { signal } from "@preact/signals-core";
|
||||
import { effect } from "@preact/signals-react";
|
||||
|
||||
const stagedImage = signal<File | null>(null);
|
||||
|
||||
export const addImageToEngine = (image: File) => {
|
||||
stagedImage.value = image;
|
||||
};
|
||||
|
||||
const onGetStagedImage = (callback: (file: File) => void) => {
|
||||
effect(() => {
|
||||
if (stagedImage.value) {
|
||||
callback(stagedImage.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const stagedVideo = signal<File | null>(null);
|
||||
|
||||
export const addVideoToEngine = (video: File) => {
|
||||
stagedVideo.value = video;
|
||||
};
|
||||
|
||||
const onGetStagedVideo = (callback: (file: File) => void) => {
|
||||
effect(() => {
|
||||
if (stagedVideo.value) {
|
||||
callback(stagedVideo.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
import {
|
||||
dispatchers as addMediaDispatchers,
|
||||
events as addMediaEvents,
|
||||
} from "./addMedia";
|
||||
import {
|
||||
dispatchers as imageToolbarDispatchers,
|
||||
eventsHandlers as imageToolbarEvents,
|
||||
} from "./imageToolbar";
|
||||
|
||||
export const uiEvents = {
|
||||
onGetStagedImage,
|
||||
onGetStagedVideo,
|
||||
...addMediaEvents,
|
||||
imageToolbar: imageToolbarEvents,
|
||||
};
|
||||
export const dispatchUiEvents = {
|
||||
...addMediaDispatchers,
|
||||
imageToolbar: imageToolbarDispatchers,
|
||||
};
|
||||
Reference in new issue
Block a user