mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 21:38:07 -05:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6526cac55e |
@@ -4,8 +4,6 @@ import { SelectionManager } from "../NodesManagers";
|
||||
import { highlightStrokeWidth, primaryOrange } from "./constants";
|
||||
import { NodeData, Position } from "../types";
|
||||
import { RealTimeDrawEngine } from "../RenderingPrimitives/RealTimeDrawEngine";
|
||||
import { MouseEventHandler } from "react";
|
||||
import { KonvaEventObject } from "konva/lib/Node";
|
||||
|
||||
export abstract class BaseNode {
|
||||
protected selectionManagerRef: SelectionManager;
|
||||
@@ -153,18 +151,15 @@ export abstract class BaseNode {
|
||||
}
|
||||
};
|
||||
|
||||
const handleRightClick = (e: KonvaEventObject<MouseEvent>) => {
|
||||
const handleRightClick = () => {
|
||||
this.selectionManagerRef.clearSelection();
|
||||
this.selectionManagerRef.selectNode(this, undefined, {
|
||||
show: true,
|
||||
at: { x: e.evt.x, y: e.evt.y }
|
||||
});
|
||||
this.selectionManagerRef.selectNode(this, undefined, true);
|
||||
}
|
||||
|
||||
this.kNode.on("contextmenu", (e) => {
|
||||
console.log("CONTEXT MENU", e);
|
||||
e.evt.preventDefault();
|
||||
handleRightClick(e);
|
||||
handleRightClick();
|
||||
});
|
||||
|
||||
this.kNode.on("mousedown", (e) => {
|
||||
|
||||
@@ -80,12 +80,7 @@ export class SelectionManager {
|
||||
public isDisabled() {
|
||||
return this.disable;
|
||||
}
|
||||
public selectNode(node: MediaNode, doNotDraw?: boolean, contextMenu: {
|
||||
show?: boolean,
|
||||
at?: { x: number, y: number }
|
||||
} = {
|
||||
show: false,
|
||||
}): boolean {
|
||||
public selectNode(node: MediaNode, doNotDraw?: boolean, showContextMenu = false): boolean {
|
||||
if (
|
||||
this.disabled || // no-op when disabled
|
||||
this.selectedNodes.has(node) || // if the node is already selected
|
||||
@@ -106,9 +101,9 @@ export class SelectionManager {
|
||||
this.updateNodeTransformer();
|
||||
this.updateContextComponents();
|
||||
|
||||
console.log("SelectionManager > selectNode > showContextMenu", contextMenu.show);
|
||||
if (contextMenu.show) {
|
||||
this.showContextComponents(contextMenu.at);
|
||||
console.log("SelectionManager > selectNode > showContextMenu", showContextMenu);
|
||||
if (showContextMenu) {
|
||||
this.showContextComponents();
|
||||
}
|
||||
|
||||
if (!doNotDraw) {
|
||||
@@ -271,15 +266,10 @@ export class SelectionManager {
|
||||
uiAccess.loadingBar.hide();
|
||||
}
|
||||
|
||||
public showContextComponents(at?: { x: number, y: number }) {
|
||||
public showContextComponents() {
|
||||
const showOrUpdate = uiAccess.toolbarNode.isShowing()
|
||||
? uiAccess.toolbarNode.update
|
||||
: uiAccess.toolbarNode.show;
|
||||
|
||||
// If a position is received, set it
|
||||
// Otherwise use the previous position
|
||||
const position = at ? at : uiAccess.toolbarNode.signal.value.position;
|
||||
|
||||
const node = this.selectedNodes.values().next().value;
|
||||
if (node === undefined) {
|
||||
this.hideContextComponents();
|
||||
@@ -291,7 +281,6 @@ export class SelectionManager {
|
||||
knodeIds: [node.kNode.id()],
|
||||
locked: node.isLocked(),
|
||||
buttonStates: getImageNodeButtonStates({ locked: node.isLocked() }),
|
||||
position: position
|
||||
});
|
||||
}
|
||||
else if (node instanceof ShapeNode) {
|
||||
@@ -300,8 +289,7 @@ export class SelectionManager {
|
||||
knodeIds: [node.kNode.id()],
|
||||
locked: node.isLocked(),
|
||||
buttonStates: getShapeNodeButtonStates({ locked: node.isLocked() }),
|
||||
color: node.color,
|
||||
position: position
|
||||
color: node.color
|
||||
});
|
||||
}
|
||||
else if (node instanceof TextNode) {
|
||||
@@ -310,14 +298,12 @@ export class SelectionManager {
|
||||
locked: node.isLocked(),
|
||||
buttonStates: getTextNodeButtonStates({ locked: node.isLocked() }),
|
||||
color: node.getNodeData({ x: 0, y: 0 })?.textNodeData?.color ?? "#000000",
|
||||
position: position
|
||||
});
|
||||
}
|
||||
else if (node instanceof VideoNode) {
|
||||
showOrUpdate({
|
||||
locked: node.isLocked(),
|
||||
buttonStates: getVideoNodeButtonStates({ locked: node.isLocked() }),
|
||||
position: position
|
||||
});
|
||||
}
|
||||
|
||||
@@ -333,7 +319,6 @@ export class SelectionManager {
|
||||
showOrUpdate({
|
||||
locked: node.isLocked(),
|
||||
buttonStates: getMultiSelectButtonStates({ locked: node.isLocked() }),
|
||||
position: position
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -363,7 +348,6 @@ export class SelectionManager {
|
||||
// console.log("setting lock");
|
||||
uiAccess.toolbarNode.setLocked(node.isLocked());
|
||||
}
|
||||
console.debug("Setting context position to:", coord)
|
||||
uiAccess.toolbarNode.setPosition({
|
||||
x: coord.x,
|
||||
y: coord.y,
|
||||
|
||||
@@ -75,7 +75,7 @@ export const KonvaRootComponent = ({
|
||||
resetAll={appUiContext.resetAll}
|
||||
/>
|
||||
|
||||
{/* <ContextualLoadingBar /> */}
|
||||
<ContextualLoadingBar />
|
||||
<ContextualToolbarNode />
|
||||
{/* <ContextualButtonRetry /> */}
|
||||
</>
|
||||
|
||||
@@ -33,7 +33,6 @@ export const SignaledPromptText = ({}) => {
|
||||
onChange={onTextChanged}
|
||||
value={textContent}
|
||||
spellCheck={false}
|
||||
forceBlurOnOutsideClick={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const Sandbox = () => {
|
||||
</div>
|
||||
|
||||
<ContextualToolbarNode />
|
||||
{/* <ContextualLoadingBar /> */}
|
||||
<ContextualLoadingBar />
|
||||
<ContextualButtonRetry />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,8 +15,8 @@ interface SliderProps {
|
||||
tooltipContent?: ReactNode;
|
||||
suffix?: string;
|
||||
innerLabel?: string;
|
||||
showDecrement?: boolean;
|
||||
showIncrement?: boolean;
|
||||
showDecrement?: boolean,
|
||||
showIncrement?: boolean,
|
||||
}
|
||||
|
||||
export const Slider = ({
|
||||
@@ -31,7 +31,7 @@ export const Slider = ({
|
||||
suffix,
|
||||
innerLabel,
|
||||
showDecrement,
|
||||
showIncrement,
|
||||
showIncrement
|
||||
}: SliderProps) => {
|
||||
const [localValue, setLocalValue] = useState(value);
|
||||
const sliderRef = useRef<HTMLDivElement>(null);
|
||||
@@ -47,7 +47,7 @@ export const Slider = ({
|
||||
const clampedValue = Math.min(Math.max(roundedValue, min), max);
|
||||
setLocalValue(clampedValue);
|
||||
onChange(clampedValue);
|
||||
};
|
||||
}
|
||||
|
||||
const updatePosition = (x: number) => {
|
||||
if (!sliderRef.current) return;
|
||||
@@ -93,11 +93,11 @@ export const Slider = ({
|
||||
|
||||
const handleDecrement = () => {
|
||||
setClampedValue(localValue - step);
|
||||
};
|
||||
}
|
||||
|
||||
const handleIncrement = () => {
|
||||
setClampedValue(localValue + step);
|
||||
};
|
||||
}
|
||||
|
||||
const percentage = ((localValue - min) / (max - min)) * 100;
|
||||
const displayValue =
|
||||
@@ -109,7 +109,7 @@ export const Slider = ({
|
||||
{showDecrement && (
|
||||
<Button
|
||||
icon={faMinus}
|
||||
className="my-auto mr-1 size-6 rounded-full bg-transparent text-white/80 hover:bg-white/10 focus-visible:outline-primary active:bg-primary/30"
|
||||
className="size-6 text-white/80 bg-transparent rounded-full hover:bg-white/10 focus-visible:outline-primary active:bg-primary/30 my-auto mr-1"
|
||||
onClick={handleDecrement}
|
||||
/>
|
||||
)}
|
||||
@@ -125,7 +125,7 @@ export const Slider = ({
|
||||
>
|
||||
<div
|
||||
className={twMerge(
|
||||
"absolute h-full bg-primary/30 transition-colors duration-300 group-hover:bg-primary/50",
|
||||
"absolute h-7 bg-primary/30 transition-colors duration-300 group-hover:bg-primary/50",
|
||||
isDragging && "!bg-primary/50",
|
||||
)}
|
||||
style={{ width: `${percentage}%` }}
|
||||
@@ -142,7 +142,7 @@ export const Slider = ({
|
||||
)}
|
||||
<div
|
||||
className={twMerge(
|
||||
"absolute right-0 top-1/2 mr-1 h-4 w-0.5 -translate-y-1/2 rounded-full",
|
||||
"absolute right-0 top-1/2 mr-1.5 h-3.5 w-0.5 -translate-y-1/2",
|
||||
isDragging ? "bg-white" : "bg-white/50",
|
||||
)}
|
||||
onMouseDown={handleMouseDown}
|
||||
@@ -155,7 +155,7 @@ export const Slider = ({
|
||||
{showIncrement && (
|
||||
<Button
|
||||
icon={faPlus}
|
||||
className="my-auto ml-1 size-6 rounded-full bg-transparent text-white/80 hover:bg-white/10 focus-visible:outline-primary active:bg-primary/30"
|
||||
className="size-6 text-white/80 bg-transparent rounded-full hover:bg-white/10 focus-visible:outline-primary active:bg-primary/30 my-auto ml-1"
|
||||
onClick={handleIncrement}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TextareaHTMLAttributes, forwardRef, useEffect, useRef } from "react";
|
||||
import { TextareaHTMLAttributes, forwardRef } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { kebabCase } from "~/utilities";
|
||||
|
||||
@@ -21,58 +21,20 @@ export interface TextareaInterface
|
||||
extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
label?: string;
|
||||
resize?: ResizeType;
|
||||
forceBlurOnOutsideClick?: boolean;
|
||||
}
|
||||
|
||||
export const Textarea = forwardRef<HTMLTextAreaElement, TextareaInterface>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
label,
|
||||
resize = "vertical",
|
||||
id,
|
||||
forceBlurOnOutsideClick,
|
||||
...rest
|
||||
}: TextareaInterface,
|
||||
{ className, label, resize = "vertical", id, ...rest }: TextareaInterface,
|
||||
ref,
|
||||
) => {
|
||||
const resizeStyle = resizeStyles[resize];
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
// Function to blur the textarea when clicking outside of it
|
||||
useEffect(() => {
|
||||
if (!forceBlurOnOutsideClick) return;
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
textareaRef.current &&
|
||||
!textareaRef.current.contains(event.target as Node)
|
||||
) {
|
||||
textareaRef.current.blur();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, [forceBlurOnOutsideClick]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{label && <label htmlFor={id ? id : kebabCase(label)}>{label}</label>}
|
||||
|
||||
<textarea
|
||||
ref={(node) => {
|
||||
if (typeof ref === "function") {
|
||||
ref(node);
|
||||
} else if (ref) {
|
||||
(ref as any).current = node;
|
||||
}
|
||||
if (textareaRef) {
|
||||
(textareaRef as any).current = node;
|
||||
}
|
||||
}}
|
||||
ref={ref}
|
||||
id={id ? id : label ? kebabCase(label) : undefined}
|
||||
className={twMerge(
|
||||
"rounded-lg border border-ui-border px-4 py-2.5 text-white outline-none focus:outline-none",
|
||||
|
||||
@@ -18,7 +18,6 @@ export interface ContextualToolbarProps extends ContextualUi {
|
||||
lockDisabled: boolean;
|
||||
buttonStates: ButtonStates;
|
||||
color: string;
|
||||
position: { x: number, y: number };
|
||||
}
|
||||
interface PartialContextualToolbarProps
|
||||
extends Partial<Omit<ContextualToolbarProps, "buttonStates">> {
|
||||
|
||||
Reference in New Issue
Block a user