Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 3 additions & 23 deletions apps/roam/src/components/canvas/DiscourseNodeUtil.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import createDiscourseNode from "~/utils/createDiscourseNode";
import { DiscourseNode } from "~/utils/getDiscourseNodes";
import { isPageUid } from "./Tldraw";
import LabelDialog from "./LabelDialog";
import { colord } from "colord";
import { discourseContext } from "./Tldraw";
import getDiscourseContextResults from "~/utils/getDiscourseContextResults";
import calcCanvasNodeSizeAndImg from "~/utils/calcCanvasNodeSizeAndImg";
Expand All @@ -46,7 +45,7 @@ import {
} from "~/data/userSettings";
import { getSetting } from "~/utils/extensionSettings";
import DiscourseContextOverlay from "~/components/DiscourseContextOverlay";
import getPleasingColors from "@repo/utils/getPleasingColors";
import { getDiscourseNodeColors } from "~/utils/getDiscourseNodeColors";

// TODO REPLACE WITH TLDRAW DEFAULTS
// https://github.com/tldraw/tldraw/pull/1580/files
Expand All @@ -58,7 +57,7 @@ const TEXT_PROPS = {
padding: "0px",
maxWidth: "auto",
};
const FONT_SIZES: Record<TLDefaultSizeStyle, number> = {
export const FONT_SIZES: Record<TLDefaultSizeStyle, number> = {
m: 25,
l: 38,
xl: 48,
Expand Down Expand Up @@ -343,26 +342,7 @@ export class BaseDiscourseNodeUtil extends ShapeUtil<DiscourseNodeShape> {
}

getColors() {
const {
canvasSettings: { color: setColor = "" } = {},
index: discourseNodeIndex = -1,
} = discourseContext.nodes[this.type] || {};
const paletteColor =
COLOR_ARRAY[
discourseNodeIndex >= 0 && discourseNodeIndex < COLOR_ARRAY.length - 1
? discourseNodeIndex
: 0
];
const formattedTextColor =
setColor && !setColor.startsWith("#") ? `#${setColor}` : setColor;

const canvasSelectedColor = formattedTextColor
? formattedTextColor
: COLOR_PALETTE[paletteColor];
const pleasingColors = getPleasingColors(colord(canvasSelectedColor));
const backgroundColor = pleasingColors.background;
const textColor = pleasingColors.text;
return { backgroundColor, textColor };
return getDiscourseNodeColors({ nodeType: this.type });
}

async toSvg(shape: DiscourseNodeShape): Promise<JSX.Element> {
Expand Down
122 changes: 80 additions & 42 deletions apps/roam/src/components/canvas/DiscourseToolPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import {
Vec,
Box,
createShapeId,
FONT_FAMILIES,
} from "tldraw";
import { DiscourseNode } from "~/utils/getDiscourseNodes";
import { formatHexColor } from "~/components/settings/DiscourseNodeCanvasSettings";
import { getRelationColor } from "./DiscourseRelationShape/DiscourseRelationUtil";
import { useAtom } from "@tldraw/state";
import { TOOL_ARROW_ICON_SVG, NODE_COLOR_ICON_SVG } from "~/icons";
import { getDiscourseNodeColors } from "~/utils/getDiscourseNodeColors";
import { DEFAULT_WIDTH, DEFAULT_HEIGHT } from "./Tldraw";
import { DEFAULT_STYLE_PROPS, FONT_SIZES } from "./DiscourseNodeUtil";

export type DiscourseGraphPanelProps = {
nodes: DiscourseNode[];
Expand All @@ -30,6 +34,8 @@ type DragState =
type: "node" | "relation";
id: string;
text: string;
backgroundColor: string;
textColor: string;
color: string;
};
startPosition: Vec;
Expand All @@ -40,6 +46,8 @@ type DragState =
type: "node" | "relation";
id: string;
text: string;
backgroundColor: string;
textColor: string;
color: string;
};
currentPosition: Vec;
Expand Down Expand Up @@ -72,20 +80,32 @@ const DiscourseGraphPanel = ({
);

const panelItems = useMemo(() => {
const nodeItems = nodes.map((node) => ({
type: "node" as const,
id: node.type,
text: node.text,
color: formatHexColor(node.canvasSettings.color) || "black",
shortcut: node.shortcut,
}));

const relationItems = uniqueRelations.map((relation, index) => ({
type: "relation" as const,
id: relation,
text: relation,
color: getRelationColor(relation, index),
}));
const nodeItems = nodes.map((node) => {
const { backgroundColor, textColor } = getDiscourseNodeColors({
nodeType: node.type,
});
return {
type: "node" as const,
id: node.type,
text: node.text,
backgroundColor: backgroundColor,
textColor: textColor,
color: formatHexColor(node.canvasSettings.color) || "black",
shortcut: node.shortcut,
};
});

const relationItems = uniqueRelations.map((relation, index) => {
const color = getRelationColor(relation, index);
return {
type: "relation" as const,
id: relation,
text: relation,
backgroundColor: color,
textColor: "black",
color: color,
};
});

return [...nodeItems, ...relationItems];
}, [nodes, uniqueRelations]);
Expand All @@ -108,6 +128,10 @@ const DiscourseGraphPanel = ({
break;
}
case "pointing_item": {
// Relations should not be draggable
if (current.item.type === "relation") {
break;
}
const dist = Vec.Dist(screenPoint, current.startPosition);
if (dist > 10) {
dragState.set({
Expand Down Expand Up @@ -153,14 +177,16 @@ const DiscourseGraphPanel = ({
case "dragging": {
// When dragging ends, create the shape at the drop position
const pagePoint = editor.screenToPage(current.currentPosition);
const offsetX = DEFAULT_WIDTH / 2;
const offsetY = DEFAULT_HEIGHT / 2;

if (current.item.type === "node") {
const shapeId = createShapeId();
editor.createShape({
id: shapeId,
type: current.item.id,
x: pagePoint.x,
y: pagePoint.y,
x: pagePoint.x - offsetX,
y: pagePoint.y - offsetY,
props: { fontFamily: "sans", size: "s" },
});
editor.setEditingShape(shapeId);
Expand Down Expand Up @@ -198,8 +224,12 @@ const DiscourseGraphPanel = ({
startPosition,
});

target.addEventListener("pointermove", handlePointerMove);
document.addEventListener("keydown", handleKeyDown);
// Relations should not be draggable, only clickable
// So we don't add the pointermove listener for relations
if (item.type !== "relation") {
target.addEventListener("pointermove", handlePointerMove);
document.addEventListener("keydown", handleKeyDown);
}
};

const handleKeyDown = (e: KeyboardEvent) => {
Expand Down Expand Up @@ -228,6 +258,11 @@ const DiscourseGraphPanel = ({

const state = useValue("dragState", () => dragState.get(), [dragState]);

const zoomLevel = Math.max(
0.5,
useValue("clipboardZoomLevel", () => editor.getZoomLevel(), [editor]),
);

// Drag preview management
useQuickReactor(
"drag-image-style",
Expand All @@ -244,39 +279,46 @@ const DiscourseGraphPanel = ({
break;
}
case "dragging": {
// Relations should not be draggable
if (current.item.type === "relation") {
imageRef.style.display = "none";
break;
}
const panelContainerRect = panelContainerRef.getBoundingClientRect();
const box = new Box(
panelContainerRect.x,
panelContainerRect.y,
panelContainerRect.width,
panelContainerRect.height,
);
const viewportScreenBounds = editor.getViewportScreenBounds();

const zoomLevel = Math.max(0.5, editor.getZoomLevel());
const height = DEFAULT_HEIGHT * zoomLevel;
const width = DEFAULT_WIDTH * zoomLevel;
const isInside = Box.ContainsPoint(box, current.currentPosition);
if (isInside) {
imageRef.style.display = "none";
} else {
imageRef.style.display = "block";
imageRef.style.position = "absolute";
const viewportScreenBounds = editor.getViewportScreenBounds();
imageRef.style.display = "flex";
imageRef.style.position = "fixed";
imageRef.style.pointerEvents = "none";
imageRef.style.left = "0px";
imageRef.style.top = "0px";
imageRef.style.transform = `translate(${current.currentPosition.x - viewportScreenBounds.x - 25}px, ${current.currentPosition.y - viewportScreenBounds.y - 25}px)`;
imageRef.style.width = "50px";
imageRef.style.height = "50px";
imageRef.style.fontSize = "40px";
imageRef.style.display = "flex";
imageRef.style.alignItems = "center";
imageRef.style.justifyContent = "center";
imageRef.style.borderRadius = "8px";
imageRef.style.backgroundColor = current.item.color;
imageRef.style.color = "white";
imageRef.style.fontWeight = "bold";
imageRef.style.transform = `translate(${current.currentPosition.x - viewportScreenBounds.x - width / 2}px, ${current.currentPosition.y - viewportScreenBounds.y - height / 2}px)`;
imageRef.style.width = `${width}px`;
imageRef.style.height = `${height}px`;
imageRef.style.zIndex = "9999";
imageRef.style.borderRadius = `${16 * zoomLevel}px`;
imageRef.style.backgroundColor = current.item.backgroundColor;
imageRef.style.color = current.item.textColor;
imageRef.className =
"roamjs-tldraw-node pointer-events-none flex fixed items-center justify-center overflow-hidden";
}
}
}
},
[dragState],
[dragState, editor],
);

// If it's a node tool, show only that node
Expand Down Expand Up @@ -384,15 +426,11 @@ const DiscourseGraphPanel = ({
{state.name === "dragging" && (
<div
style={{
backgroundColor: state.item.color,
color: "white",
fontWeight: "bold",
borderRadius: "8px",
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
height: "100%",
...DEFAULT_STYLE_PROPS,
maxWidth: "",
fontFamily: FONT_FAMILIES.sans,
fontSize: `${FONT_SIZES.s * zoomLevel}px`,
padding: `0px`,
}}
>
{state.item.text}
Expand Down
4 changes: 2 additions & 2 deletions apps/roam/src/components/canvas/Tldraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ export const discourseContext: DiscourseContextType = {
lastActions: [],
};

const DEFAULT_WIDTH = 160;
const DEFAULT_HEIGHT = 64;
export const DEFAULT_WIDTH = 160;
export const DEFAULT_HEIGHT = 64;
export const MAX_WIDTH = "400px";

export const isPageUid = (uid: string) =>
Expand Down
41 changes: 41 additions & 0 deletions apps/roam/src/utils/getDiscourseNodeColors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { colord } from "colord";
import getPleasingColors from "@repo/utils/getPleasingColors";
import {
COLOR_ARRAY,
COLOR_PALETTE,
} from "~/components/canvas/DiscourseNodeUtil";
import getDiscourseNodes, { DiscourseNode } from "./getDiscourseNodes";

type GetDiscourseNodeColorsParams = {
nodeType?: string;
discourseNodes?: DiscourseNode[];
};

export const getDiscourseNodeColors = ({
nodeType,
discourseNodes = getDiscourseNodes(),
}: GetDiscourseNodeColorsParams): {
backgroundColor: string;
textColor: string;
} => {
const discourseNodeIndex =
discourseNodes.findIndex((node) => node.type === nodeType) ?? -1;
const color = discourseNodes[discourseNodeIndex]?.canvasSettings?.color ?? "";

const paletteColor =
COLOR_ARRAY[
discourseNodeIndex >= 0 && discourseNodeIndex < COLOR_ARRAY.length
? discourseNodeIndex
: 0
];
const formattedTextColor =
color && !color.startsWith("#") ? `#${color}` : color;

const canvasSelectedColor = formattedTextColor
? formattedTextColor
: COLOR_PALETTE[paletteColor];
const pleasingColors = getPleasingColors(colord(canvasSelectedColor));
const backgroundColor = pleasingColors.background;
const textColor = pleasingColors.text;
return { backgroundColor, textColor };
};