Skip to content
Draft
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
2 changes: 1 addition & 1 deletion src/BloomBrowserUI/bookEdit/StyleEditor/StyleEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { kBloomYellow } from "../../bloomMaterialUITheme";
import { RenderRoot } from "./AudioHilitePage";
import { RenderCanvasElementRoot } from "./CanvasElementFormatPage";
import { CanvasElementManager } from "../js/CanvasElementManager";
import { kCanvasElementSelector } from "../toolbox/canvas/canvasElementUtils";
import { kCanvasElementSelector } from "../toolbox/canvas/canvasElementConstants";
import { getPageIFrame } from "../../utils/shared";

// Controls the CSS text-align value
Expand Down
2 changes: 1 addition & 1 deletion src/BloomBrowserUI/bookEdit/editViewFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export { showRegistrationDialogForEditTab as showRegistrationDialog };
import { showAboutDialog } from "../react_components/aboutDialog";
export { showAboutDialog };
import { reportError } from "../lib/errorHandler";
import { IToolboxFrameExports } from "./toolbox/toolboxBootstrap";
import type { IToolboxFrameExports } from "./toolbox/toolboxBootstrap";
import { showCopyrightAndLicenseInfoOrDialog } from "./copyrightAndLicense/CopyrightAndLicenseDialog";
import { showTopicChooserDialog } from "./TopicChooser/TopicChooserDialog";
import * as ReactDOM from "react-dom";
Expand Down
1,221 changes: 692 additions & 529 deletions src/BloomBrowserUI/bookEdit/js/CanvasElementContextControls.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Originally this was wired into CanvasSnapProvider.ts, but we're going to do that PR separately and later.
// And the way it was wired in, just using the grid size, may not be enough. We may need to ask the snap provider
// to give us the snap location. We'll see.
import { kBackgroundImageClass } from "./CanvasElementManager";
import { kBackgroundImageClass } from "../toolbox/canvas/canvasElementConstants";
import { CanvasSnapProvider } from "./CanvasSnapProvider";

export interface ICanvasElementKeyboardActions {
Expand Down
425 changes: 118 additions & 307 deletions src/BloomBrowserUI/bookEdit/js/CanvasElementManager.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Small public helpers that other modules can call without importing the full
// CanvasElementManager class.
//
// This file exists to keep CanvasElementManager.ts smaller and to reduce coupling
// between the page bundle and toolbox UI code.

import { kCanvasToolId } from "../toolbox/toolIds";
import { getToolboxBundleExports } from "./bloomFrames";
import { kImageContainerClass } from "./bloomImages";

// This is just for debugging. It produces a string that describes the canvas element, generally
// well enough to identify it in console.log.
export const canvasElementDescription = (
e: Element | null | undefined,
): string => {
const elt = e as HTMLElement;
if (!elt) {
return "no canvas element";
}
const result =
"canvas element at (" + elt.style.left + ", " + elt.style.top + ") ";
const imageContainer = elt.getElementsByClassName(kImageContainerClass)[0];
if (imageContainer) {
const img = (imageContainer as HTMLElement).getElementsByTagName(
"img",
)[0];
if (img) {
return result + "with image : " + img.getAttribute("src");
}
}
const videoSrc = elt.getElementsByTagName("source")[0];
if (videoSrc) {
return result + "with video " + videoSrc.getAttribute("src");
}
// Enhance: look for videoContainer similarly
return result + "with text " + elt.innerText;
};

export const showCanvasTool = () => {
getToolboxBundleExports()
?.getTheOneToolbox()
.activateToolFromId(kCanvasToolId);
};
2 changes: 1 addition & 1 deletion src/BloomBrowserUI/bookEdit/js/CanvasGuideProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This class that helps visually align elements during drag operations by showing red lines
// and highlighting elements with equal dimensions during resize operations.

import { kBackgroundImageClass } from "./CanvasElementManager";
import { kBackgroundImageClass } from "../toolbox/canvas/canvasElementConstants";

// ALIGNMENT RULES:
// 1. When a dragged element aligns horizontally (top/middle/bottom) or vertically (left/center/right)
Expand Down
4 changes: 2 additions & 2 deletions src/BloomBrowserUI/bookEdit/js/bloomEditing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import {
initializeCanvasElementManager,
theOneCanvasElementManager,
} from "./CanvasElementManager";
import { getCanvasElementManager } from "../toolbox/canvas/canvasElementUtils";
import {
getCanvasElementManager,
kCanvasElementClass,
kCanvasElementSelector,
} from "../toolbox/canvas/canvasElementUtils";
} from "../toolbox/canvas/canvasElementConstants";
import { showTopicChooserDialog } from "../TopicChooser/TopicChooserDialog";
import "../../modified_libraries/jquery-ui/jquery-ui-1.10.3.custom.min.js";
import "./jquery.hasAttr.js"; //reviewSlog for CenterVerticallyInParent
Expand Down
6 changes: 3 additions & 3 deletions src/BloomBrowserUI/bookEdit/js/bloomFrames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
to hide the details so that we can easily change it later.
*/

import { IPageFrameExports } from "../editablePage";
import { IEditViewFrameExports } from "../editViewFrame";
import { IToolboxFrameExports } from "../toolbox/toolboxBootstrap";
import type { IPageFrameExports } from "../editablePage";
import type { IEditViewFrameExports } from "../editViewFrame";
import type { IToolboxFrameExports } from "../toolbox/toolboxBootstrap";

interface WindowWithExports extends Window {
editTabBundle: any;
Expand Down
18 changes: 7 additions & 11 deletions src/BloomBrowserUI/bookEdit/js/bloomImages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@ import theOneLocalizationManager from "../../lib/localizationManager/localizatio

import {
kBackgroundImageClass,
theOneCanvasElementManager,
updateCanvasElementClass,
} from "./CanvasElementManager";
import {
kCanvasElementSelector,
kBloomCanvasClass,
kBloomCanvasSelector,
} from "../toolbox/canvas/canvasElementUtils";
kCanvasElementSelector,
} from "../toolbox/canvas/canvasElementConstants";
import { updateCanvasElementClass } from "../toolbox/canvas/canvasElementDomUtils";

import { farthest } from "../../utils/elementUtils";
import { EditableDivUtils } from "./editableDivUtils";
Expand Down Expand Up @@ -415,7 +412,7 @@ function DisableImageTooltip(container: HTMLElement | undefined | null) {
}

// If the canvas element manager hasn't been set up at all we can at least clear the current one.
const bloomCanvas = container.closest(kBloomCanvasClass) as HTMLElement; // this is the one we want to clear the title on, if any
const bloomCanvas = container.closest(kBloomCanvasSelector) as HTMLElement; // this is the one we want to clear the title on, if any

if (bloomCanvas) {
bloomCanvas.title = "";
Expand Down Expand Up @@ -821,18 +818,17 @@ export function SetupResizableElement(element) {
// caption centered, but currently we are NOT centering it. However, it makes sense
// to resize the picture and its captions together anyway. We at least want the text
// boxes to stay the same size as the bloom-canvas.)
const canvasElementManager = getCanvasElementManager();
const img = $(bloomCanvas).find("img");
$(element).resizable({
handles: "nw, ne, sw, se",
containment: "parent",
alsoResize: bloomCanvas,
start(e, ui) {
theOneCanvasElementManager.suspendComicEditing(
"forJqueryResize",
);
canvasElementManager?.suspendComicEditing("forJqueryResize");
},
stop(e, ui) {
theOneCanvasElementManager.resumeComicEditing();
canvasElementManager?.resumeComicEditing();
},
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/BloomBrowserUI/bookEdit/js/bloomVideo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { selectVideoContainer } from "./videoUtils";
import { getPlayIcon } from "../img/playIcon";
import { getPauseIcon } from "../img/pauseIcon";
import { getReplayIcon } from "../img/replayIcon";
import { kCanvasElementSelector } from "../toolbox/canvas/canvasElementUtils";
import { kCanvasElementSelector } from "../toolbox/canvas/canvasElementConstants";
import $ from "jquery";

export function SetupVideoEditing(container) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Helper functions extracted from CanvasElementManager.
//
// This module handles saving canvas element state as the current-language alternate
// (primarily for bubbles). Keeping it separate helps reduce the size and coupling
// of CanvasElementManager.

export const saveStateOfCanvasElementAsCurrentLangAlternate = (
canvasElement: HTMLElement,
canvasElementLangIn?: string,
): void => {
const canvasElementLang =
canvasElementLangIn ?? GetSettings().languageForNewTextBoxes;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Missing GetSettings() type declaration in CanvasElementAlternates.ts causes TypeScript error

The new file CanvasElementAlternates.ts uses the globally injected GetSettings() function at line 12 but lacks the required triple-slash reference directive to declare it.

Click to expand

Problem

The file calls GetSettings().languageForNewTextBoxes but GetSettings is not declared in scope. This function is injected by C# at runtime, but TypeScript needs the type declaration to compile.

Evidence

Other files in the codebase that use GetSettings() include the reference directive:

  • CanvasElementManager.ts:8: /// <reference path="./collectionSettings.d.ts"/>
  • BloomSourceBubbles.tsx:10: /// <reference path="../js/collectionSettings.d.ts"/>
  • StyleEditor.ts:11: /// <reference path="../js/collectionSettings.d.ts"/>
  • PlaceholderProvider.ts:5: /// <reference path="./collectionSettings.d.ts" />
  • BloomHintBubbles.ts:5: /// <reference path="./collectionSettings.d.ts" />

Impact

This will cause a TypeScript compilation error: Cannot find name 'GetSettings'.

Recommendation: Add the triple-slash reference directive at the top of the file:

/// <reference path="../collectionSettings.d.ts"/>
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


const editable = Array.from(
canvasElement.getElementsByClassName("bloom-editable"),
).find((e) => e.getAttribute("lang") === canvasElementLang);
if (editable) {
const bubbleData = canvasElement.getAttribute("data-bubble") ?? "";
const bubbleDataObj = JSON.parse(bubbleData.replace(/`/g, '"'));
const alternate = {
lang: canvasElementLang,
style: canvasElement.getAttribute("style") ?? "",
tails: bubbleDataObj.tails as object[],
};
editable.setAttribute(
"data-bubble-alternate",
JSON.stringify(alternate).replace(/"/g, "`"),
);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Helper functions extracted from CanvasElementManager.
//
// These are geometry/pixel conversion utilities used by the editable-page bundle
// when positioning and hit-testing canvas elements. They intentionally avoid taking
// a dependency on the full CanvasElementManager class to help keep that file smaller
// and reduce import coupling.

import { Point, PointScaling } from "../point";
import { reportError } from "../../../lib/errorHandler";

export const convertPointFromViewportToElementFrame = (
pointRelativeToViewport: Point,
element: Element,
): Point => {
const referenceBounds = element.getBoundingClientRect();
const origin = new Point(
referenceBounds.left,
referenceBounds.top,
PointScaling.Scaled,
"BoundingClientRect (Relative to viewport)",
);

const border = getLeftAndTopBorderWidths(element);
const padding = getLeftAndTopPaddings(element);
const borderAndPadding = border.add(padding);

const scroll = getScrollAmount(element);
if (scroll.length() > 0.001) {
const error = new Error(
`Assert failed. container.scroll expected to be (0, 0), but it was: (${scroll.getScaledX()}, ${scroll.getScaledY()})`,
);
reportError(error.message, error.stack || "");
}

return pointRelativeToViewport.subtract(origin).subtract(borderAndPadding);
};

export const getLeftAndTopBorderWidths = (element: Element): Point => {
return new Point(
element.clientLeft,
element.clientTop,
PointScaling.Unscaled,
"Element ClientLeft/Top (Unscaled)",
);
};

export const getRightAndBottomBorderWidths = (
element: Element,
styleInfo?: CSSStyleDeclaration,
): Point => {
if (!styleInfo) {
styleInfo = window.getComputedStyle(element);
}

const borderRight: number = extractNumber(
styleInfo.getPropertyValue("border-right-width"),
);
const borderBottom: number = extractNumber(
styleInfo.getPropertyValue("border-bottom-width"),
);

return new Point(
borderRight,
borderBottom,
PointScaling.Unscaled,
"Element ClientRight/Bottom (Unscaled)",
);
};

export const getCombinedBorderWidths = (
element: Element,
styleInfo?: CSSStyleDeclaration,
): Point => {
if (!styleInfo) {
styleInfo = window.getComputedStyle(element);
}

return getLeftAndTopBorderWidths(element).add(
getRightAndBottomBorderWidths(element, styleInfo),
);
};

export const getPadding = (
side: string,
styleInfo: CSSStyleDeclaration,
): number => {
const propertyKey = `padding-${side}`;
const paddingString = styleInfo.getPropertyValue(propertyKey);
return extractNumber(paddingString);
};

export const getLeftAndTopPaddings = (
element: Element,
styleInfo?: CSSStyleDeclaration,
): Point => {
if (!styleInfo) {
styleInfo = window.getComputedStyle(element);
}

return new Point(
getPadding("left", styleInfo),
getPadding("top", styleInfo),
PointScaling.Unscaled,
"CSSStyleDeclaration padding",
);
};

export const getRightAndBottomPaddings = (
element: Element,
styleInfo?: CSSStyleDeclaration,
): Point => {
if (!styleInfo) {
styleInfo = window.getComputedStyle(element);
}

return new Point(
getPadding("right", styleInfo),
getPadding("bottom", styleInfo),
PointScaling.Unscaled,
"Padding",
);
};

export const getCombinedPaddings = (
element: Element,
styleInfo?: CSSStyleDeclaration,
): Point => {
if (!styleInfo) {
styleInfo = window.getComputedStyle(element);
}

return getLeftAndTopPaddings(element, styleInfo).add(
getRightAndBottomPaddings(element, styleInfo),
);
};

export const getCombinedBordersAndPaddings = (element: Element): Point => {
const styleInfo = window.getComputedStyle(element);
const borders = getCombinedBorderWidths(element);
const paddings = getCombinedPaddings(element, styleInfo);
return borders.add(paddings);
};

export const getScrollAmount = (element: Element): Point => {
return new Point(
element.scrollLeft,
element.scrollTop,
PointScaling.Unscaled,
"Element ScrollLeft/Top (Unscaled)",
);
};

export const extractNumber = (text: string | undefined | null): number => {
if (!text) {
return 0;
}

let i = 0;
for (i = 0; i < text.length; ++i) {
const c = text.charAt(i);
if ((c < "0" || c > "9") && c !== "-" && c !== "+" && c !== ".") {
break;
}
}

let numberStr = "";
if (i > 0) {
numberStr = text.substring(0, i);
}

return Number(numberStr);
};
Loading