Skip to content
Open
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
8 changes: 5 additions & 3 deletions apps/roam/src/components/Export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ import {
} from "@tldraw/editor";
import calcCanvasNodeSizeAndImg from "~/utils/calcCanvasNodeSizeAndImg";
import {
createNodeShapeUtils,
DiscourseNodeUtil,
DISCOURSE_NODE_SHAPE_TYPE,
DiscourseNodeShape,
} from "~/components/canvas/DiscourseNodeUtil";
import { discourseContext, MAX_WIDTH } from "~/components/canvas/Tldraw";
Expand Down Expand Up @@ -360,7 +361,7 @@ const ExportDialog: ExportDialogComponent = ({
);

// UTILS
const discourseNodeUtils = createNodeShapeUtils(allNodes);
const discourseNodeUtils = [DiscourseNodeUtil];
const discourseRelationUtils =
createAllRelationShapeUtils(allRelationIds);
const referencedNodeUtils = createAllReferencedNodeUtils(
Expand Down Expand Up @@ -551,12 +552,13 @@ const ExportDialog: ExportDialogComponent = ({
index: currentIndex,
rotation: 0,
isLocked: false,
type: nodeType,
type: DISCOURSE_NODE_SHAPE_TYPE,
props: {
w,
h,
uid: r.uid,
title: String(r[firstColumnKey]),
nodeTypeId: nodeType,
imageUrl,
size: "s",
fontFamily: "sans",
Expand Down
4 changes: 3 additions & 1 deletion apps/roam/src/components/canvas/Clipboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import getAllReferencesOnPage from "~/utils/getAllReferencesOnPage";
import {
DiscourseNodeShape,
DEFAULT_STYLE_PROPS,
DISCOURSE_NODE_SHAPE_TYPE,
FONT_SIZES,
} from "./DiscourseNodeUtil";
import { openBlockInSidebar, createBlock } from "roamjs-components/writes";
Expand Down Expand Up @@ -702,7 +703,7 @@ const ClipboardPageSection = ({
const shapeId = createShapeId();
const shape = {
id: shapeId,
type: nodeType.type,
type: DISCOURSE_NODE_SHAPE_TYPE,
x: pagePoint.x - w / 2,
y: pagePoint.y - h / 2,
props: {
Expand All @@ -713,6 +714,7 @@ const ClipboardPageSection = ({
imageUrl,
size: "s" as TLDefaultSizeStyle,
fontFamily: "sans" as TLDefaultFontStyle,
nodeTypeId: nodeType.type,
},
};
editor.createShape<DiscourseNodeShape>(shape);
Expand Down
91 changes: 52 additions & 39 deletions apps/roam/src/components/canvas/DiscourseNodeUtil.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
TLBaseShape,
useEditor,
DefaultColorStyle,
Editor,
createShapeId,
TLDefaultHorizontalAlignStyle,
TLDefaultVerticalAlignStyle,
Expand All @@ -16,6 +15,7 @@ import {
DefaultSizeStyle,
T,
FONT_FAMILIES,
TLShape,
TLDefaultFontStyle,
DefaultFontStyle,
toDomPrecision,
Expand Down Expand Up @@ -94,6 +94,8 @@ export const COLOR_PALETTE: Record<string, string> = {
};
/* eslint-disable @typescript-eslint/naming-convention */

export const DISCOURSE_NODE_SHAPE_TYPE = "discourse-node";

const getRelationIds = () =>
new Set(
Object.values(discourseContext.relations).flatMap((rs) =>
Expand All @@ -109,7 +111,7 @@ export const createNodeShapeTools = (
static id = n.type;
static initial = "idle";
static isLockable = true;
shapeType = n.type;
nodeTypeId = n.type;

override onEnter = () => {
this.editor.setCursor({
Expand All @@ -123,34 +125,39 @@ export const createNodeShapeTools = (
const shapeId = createShapeId();
this.editor.createShape({
id: shapeId,
type: this.shapeType,
type: DISCOURSE_NODE_SHAPE_TYPE,
x: currentPagePoint.x,
y: currentPagePoint.y,
props: { fontFamily: "sans", size: "s" },
props: {
fontFamily: "sans",
size: "s",
nodeTypeId: this.nodeTypeId,
},
});
this.editor.setEditingShape(shapeId);
};
};
});
};

export const createNodeShapeUtils = (nodes: DiscourseNode[]) => {
return nodes.map((node) => {
class DiscourseNodeUtil extends BaseDiscourseNodeUtil {
constructor(editor: Editor) {
super(editor, node.type);
}
static override type = node.type; // removing this gives undefined error
// getDefaultProps(): DiscourseNodeShape["props"] {
// const baseProps = super.getDefaultProps();
// return {
// ...baseProps,
// color: node.color,
// };
// }
}
return DiscourseNodeUtil;
});
type ShapeWithOptionalNodeTypeId = TLShape & {
props?: {
nodeTypeId?: string;
};
};

export const getDiscourseNodeTypeId = ({
shape,
}: {
shape: ShapeWithOptionalNodeTypeId;
}): string => {
return shape.props?.nodeTypeId || shape.type;
};

export const isDiscourseNodeShape = (
shape: TLShape,
): shape is DiscourseNodeShape => {
return shape.type === DISCOURSE_NODE_SHAPE_TYPE;
};

export type DiscourseNodeShape = TLBaseShape<
Expand All @@ -161,25 +168,22 @@ export type DiscourseNodeShape = TLBaseShape<
// opacity: TLOpacityType;
uid: string;
title: string;
nodeTypeId: string;
imageUrl?: string;
size: TLDefaultSizeStyle;
fontFamily: TLDefaultFontStyle;
}
>;
export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape> {
type: string;

constructor(editor: Editor, type: string) {
super(editor);
this.type = type;
}
export class DiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape> {
static override type = DISCOURSE_NODE_SHAPE_TYPE;

static override props = {
w: T.number,
h: T.number,
// opacity: T.number,
uid: T.string,
title: T.string,
nodeTypeId: T.string,
imageUrl: T.optional(T.string),
size: DefaultSizeStyle,
fontFamily: DefaultFontStyle,
Expand All @@ -196,6 +200,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
h: 64,
uid: window.roamAlphaAPI.util.generateUID(),
title: "",
nodeTypeId: "",
size: "s",
fontFamily: "sans",
};
Expand Down Expand Up @@ -241,7 +246,11 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
const nodesInCanvas = Object.fromEntries(
allRecords
.filter((r): r is DiscourseNodeShape => {
return r.typeName === "shape" && nodeIds.has(r.type);
if (r.typeName !== "shape") return false;
const nodeTypeId = getDiscourseNodeTypeId({ shape: r });
return (
r.typeName === "shape" && !!nodeTypeId && nodeIds.has(nodeTypeId)
);
})
.map((r) => [r.props.uid, r] as const),
);
Expand Down Expand Up @@ -332,12 +341,14 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
editor.createShapes(shapesToCreate).createBindings(bindingsToCreate);
}

getColors() {
return getDiscourseNodeColors({ nodeType: this.type });
getColors(shape: DiscourseNodeShape) {
return getDiscourseNodeColors({
nodeType: getDiscourseNodeTypeId({ shape }),
});
}

async toSvg(shape: DiscourseNodeShape): Promise<JSX.Element> {
const { backgroundColor, textColor } = this.getColors();
const { backgroundColor, textColor } = this.getColors(shape);
const padding = Number(DEFAULT_STYLE_PROPS.padding.replace("px", ""));
const props = shape.props;
const bounds = new Box(0, 0, props.w, props.h);
Expand Down Expand Up @@ -434,7 +445,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
const extensionAPI = useExtensionAPI();
const {
canvasSettings: { alias = "", "key-image": isKeyImage = "" } = {},
} = discourseContext.nodes[shape.type] || {};
} = discourseContext.nodes[getDiscourseNodeTypeId({ shape })] || {};
// eslint-disable-next-line react-hooks/rules-of-hooks
const isOverlayEnabled = useMemo(
() => getPersonalSetting<boolean>([PERSONAL_KEYS.overlayInCanvas]),
Expand All @@ -450,7 +461,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
// Detect discourse node tags in block text for blck-node shapes
// eslint-disable-next-line react-hooks/rules-of-hooks
const matchedNodeForConversion = useMemo(() => {
if (shape.type !== "blck-node") return null;
if (getDiscourseNodeTypeId({ shape }) !== "blck-node") return null;
if (!isLiveBlock(shape.props.uid)) return null;
const blockText = getTextByBlockUid(shape.props.uid);
if (!blockText) return null;
Expand All @@ -471,9 +482,9 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
}
}
return null;
}, [shape.type, shape.props.uid]);
}, [shape]);

const { backgroundColor, textColor } = this.getColors();
const { backgroundColor, textColor } = this.getColors(shape);
const showEmbeddedRoamBlock =
!isPageUid(shape.props.uid) && isLiveBlock(shape.props.uid);

Expand All @@ -492,7 +503,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
const { h, w, imageUrl } = await calcCanvasNodeSizeAndImg({
nodeText: text,
uid,
nodeType: this.type,
nodeType: getDiscourseNodeTypeId({ shape }),
extensionAPI,
});
this.updateProps(shape.id, shape.type, { h, w, imageUrl });
Expand All @@ -513,7 +524,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>

renderModifyNodeDialog({
mode: isCreating ? "create" : "edit",
nodeType: shape.type,
nodeType: getDiscourseNodeTypeId({ shape }),
initialValue: { text: shape.props.title, uid: shape.props.uid },
// Only pass it when editing an existing node that has a valid Roam block UID
sourceBlockUid:
Expand All @@ -538,6 +549,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
this.updateProps(shape.id, shape.type, {
title: text,
uid,
nodeTypeId: getDiscourseNodeTypeId({ shape }),
});

const autoCanvasRelations = getPersonalSetting<boolean>([
Expand Down Expand Up @@ -660,7 +672,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
});
editor.createShapes([
{
type: node.type,
type: DISCOURSE_NODE_SHAPE_TYPE,
id: createShapeId(),
props: {
uid,
Expand All @@ -670,6 +682,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil<DiscourseNodeShape>
imageUrl: nodeImageUrl,
fontFamily: "sans",
size: "s",
nodeTypeId: node.type,
},
x,
y,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from "./DiscourseRelationUtil";
import { discourseContext } from "~/components/canvas/Tldraw";
import { dispatchToastEvent } from "~/components/canvas/ToastListener";
import { getDiscourseNodeTypeId } from "~/components/canvas/DiscourseNodeUtil";

export type AddReferencedNodeType = Record<string, ReferenceFormatType[]>;
type ReferenceFormatType = {
Expand Down Expand Up @@ -78,7 +79,10 @@ export const createAllReferencedNodeTools = (

const sourceType = allAddReferencedNodeByAction[action][0].sourceType;
const sourceName = allAddReferencedNodeByAction[action][0].sourceName;
if (target?.type === sourceType) {
if (
target &&
getDiscourseNodeTypeId({ shape: target }) === sourceType
) {
this.shapeType = `${action}`;
} else {
this.cancelAndWarn(`Starting node must be one of ${sourceName}`);
Expand Down Expand Up @@ -360,8 +364,13 @@ export const createAllRelationShapeTools = (
// }
);

const targetNodeTypeId = target
? getDiscourseNodeTypeId({ shape: target })
: undefined;
const relation = discourseContext.relations[name].find(
(r) => r.source === target?.type || r.destination === target?.type,
(r) =>
r.source === targetNodeTypeId ||
r.destination === targetNodeTypeId,
);
if (relation) {
this.shapeType = relation.id;
Expand Down
Loading
Loading