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
2 changes: 2 additions & 0 deletions DistFiles/localization/en/BloomMediumPriority.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,7 @@
<trans-unit id="BookSettings.CoverIsImage" sil:dynamic="true">
<source xml:lang="en">Fill the front cover with a single image</source>
<note>BookSettings.CoverIsImage</note>
<note>Obsolete as of Bloom 6.4</note>
</trans-unit>
<trans-unit id="BookSettings.CoverIsImage.Description" sil:dynamic="true">
<source xml:lang="en">Using this option turns on the [Print Bleed](https://en.wikipedia.org/wiki/Bleed_%28printing%29) indicators on paper layouts. See [Full Page Cover Images](https://docs.bloomlibrary.org/full-page-cover-images) for information on sizing your image to fit.</source>
Expand All @@ -778,6 +779,7 @@
<trans-unit id="BookSettings.CoverIsImage.Description.V2" sil:dynamic="true">
<source xml:lang="en">Replace the front cover content with a single full-bleed image. See [Full Page Cover Images](https://docs.bloomlibrary.org/full-page-cover-images) for information on sizing your image to fit.</source>
<note>BookSettings.CoverIsImage.Description.V2</note>
<note>Obsolete as of Bloom 6.4</note>
</trans-unit>
<trans-unit id="BookSettings.FullBleed" sil:dynamic="true">
<source xml:lang="en">Use full bleed page layout</source>
Expand Down
42 changes: 39 additions & 3 deletions src/BloomBrowserUI/bookEdit/StyleEditor/StyleEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,34 @@ export default class StyleEditor {

private uiLang: string;

// We allow read-only fields to have a style dialog on custom-layout pages
// for elements that have a -style class to edit.
public static shouldAllowNonEditableStyleDialogTarget(
targetBox: HTMLElement,
): boolean {
if (targetBox.classList.contains("bloom-editable")) {
return false;
}
if (StyleEditor.GetStyleClassFromElement(targetBox) === null) {
return false;
}
return (
targetBox.closest(
".bloom-page.bloom-customLayout[data-custom-layout-id]",
) !== null
);
}

// When we allow an element that's not a bloom-editable to have a format dialog,
// we always use the default (not langauge-dependent) version of the rules,
// since we don't have the structure of a translation group containing multiple
// bloom-editables that might need different formatting.
private targetUsesLanguageIndependentRules(
targetBox: HTMLElement,
): boolean {
return StyleEditor.shouldAllowNonEditableStyleDialogTarget(targetBox);
}

public AttachToBox(targetBox: HTMLElement) {
this.uiLang = theOneLocalizationManager.getCurrentUILocale();

Expand Down Expand Up @@ -1498,7 +1526,11 @@ export default class StyleEditor {
// BL-5616 This also applies if the textbox's default language is '*',
// like it is for an Arithmetic Equation.
const tag = $(this.boxBeingEdited).attr("lang");
if (this.shouldSetDefaultRule() || tag === "*") {
if (
this.shouldSetDefaultRule() ||
this.targetUsesLanguageIndependentRules(this.boxBeingEdited) ||
tag === "*"
) {
theOneLocalizationManager
.asyncGetText(
"BookEditor.DefaultForText",
Expand Down Expand Up @@ -2217,11 +2249,15 @@ export default class StyleEditor {
if (!styleName) {
return null; // bizarre, since we put up the dialog
}
const langAttrValue = StyleEditor.GetLangValueOrNull(target);
const useLanguageIndependentRule =
ignoreLanguage || this.targetUsesLanguageIndependentRules(target);
const langAttrValue = useLanguageIndependentRule
? null
: StyleEditor.GetLangValueOrNull(target);
return this.GetOrCreateRuleForStyle(
styleName,
langAttrValue,
ignoreLanguage,
useLanguageIndependentRule,
forChildPara,
);
}
Expand Down
41 changes: 0 additions & 41 deletions src/BloomBrowserUI/bookEdit/bookSettings/BookSettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,6 @@ export const BookSettingsDialog: React.FunctionComponent<{
"BookSettings.Gutter.DefaultLabel",
);

const coverIsImageLabel = useL10n(
"Fill the front cover with a single image",
"BookSettings.CoverIsImage",
);
const coverIsImageDescription = useL10n(
"Replace the front cover content with a single full-bleed image. See [Full Page Cover Images](https://docs.bloomlibrary.org/full-page-cover-images) for information on sizing your image to fit.",
"BookSettings.CoverIsImage.Description.V2",
);

const fullBleedLabel = useL10n(
"Use full bleed page layout",
"BookSettings.FullBleed",
Expand Down Expand Up @@ -389,9 +380,6 @@ export const BookSettingsDialog: React.FunctionComponent<{
setMigratedTheme("");
};

const tierAllowsFullPageCoverImage =
useGetFeatureStatus("fullPageCoverImage")?.enabled;

const tierAllowsFullBleed = useGetFeatureStatus("PrintshopReady")?.enabled;

function saveSettingsAndCloseDialog() {
Expand Down Expand Up @@ -490,35 +478,6 @@ export const BookSettingsDialog: React.FunctionComponent<{
</ConfigrStatic>
)}
<ConfigrGroup label={whatToShowOnCoverLabel}>
<div>
<ConfigrBoolean
label={coverIsImageLabel}
description={coverIsImageDescription}
{...getAdditionalProps<boolean>(
`coverIsImage`,
)}
disabled={
appearanceDisabled ||
!tierAllowsFullPageCoverImage
}
/>
<div
css={css`
display: flex;
padding-bottom: 5px;
font-size: 12px;
font-weight: bold;
`}
>
<BloomSubscriptionIndicatorIconAndText
feature="fullPageCoverImage"
css={css`
margin-left: auto;
`}
disabled={appearanceDisabled}
/>
</div>
</div>
<FieldVisibilityGroup
field="cover-title"
labelFrame="Show Title in {0}"
Expand Down
61 changes: 58 additions & 3 deletions src/BloomBrowserUI/bookEdit/js/CanvasElementContextControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import {
import { wrapWithRequestPageContentDelay } from "./bloomEditing";
import { get, post, useApiObject } from "../../utils/bloomApi";
import { ILanguageNameValues } from "../bookSettings/FieldVisibilityGroup";
import StyleEditor from "../StyleEditor/StyleEditor";
import OverflowChecker from "../OverflowChecker/OverflowChecker";

interface IMenuItemWithSubmenu extends ILocalizableMenuItemProps {
Expand All @@ -87,6 +88,24 @@ function isNavigationButton(canvasElement: HTMLElement) {
return canvasElement.classList.contains(kBloomButtonClass);
}

function getFormatTargetElement(
canvasElement: HTMLElement,
): HTMLElement | undefined {
const editable = canvasElement.getElementsByClassName(
"bloom-editable bloom-visibility-code-on",
)[0] as HTMLElement | undefined;
if (editable) {
return editable;
}

const candidates = Array.from(
canvasElement.querySelectorAll("[class]"),
) as HTMLElement[];
return candidates.find((candidate) =>
StyleEditor.shouldAllowNonEditableStyleDialogTarget(candidate),
);
}

// This is the controls bar that appears beneath a canvas element when it is selected. It contains buttons
// for the most common operations that apply to the canvas element in its current state, and a menu for less common
// operations.
Expand Down Expand Up @@ -358,6 +377,8 @@ const CanvasElementContextControls: React.FunctionComponent<{
const editableTextElement = props.canvasElement.getElementsByClassName(
"bloom-editable bloom-visibility-code-on",
)[0] as HTMLElement;
const formatTargetElement = getFormatTargetElement(props.canvasElement);
const showFormatButton = !!formatTargetElement && !isNavButton;

if (isNavButton) {
menuOptions.splice(0, 0, {
Expand Down Expand Up @@ -458,6 +479,13 @@ const CanvasElementContextControls: React.FunctionComponent<{
languageNameValues,
noneLabel,
);
} else if (formatTargetElement && !isNavButton) {
menuOptions.push({
l10nId: "EditTab.Toolbox.ComicTool.Options.Format",
english: "Format",
onClick: () => GetEditor().runFormatDialog(formatTargetElement),
icon: <CogIcon css={getMenuIconCss()} />,
});
}

const runMetadataDialog = () => {
Expand Down Expand Up @@ -619,15 +647,15 @@ const CanvasElementContextControls: React.FunctionComponent<{
)}
</Fragment>
)}
{editableTextElement && !isNavButton && (
{showFormatButton && (
<ButtonWithTooltip
tipL10nKey="EditTab.Toolbox.ComicTool.Options.Format"
icon={CogIcon}
relativeSize={0.8}
onClick={() => {
if (!props.canvasElement) return;
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
GetEditor().runFormatDialog(
editableTextElement,
formatTargetElement,
);
}}
/>
Expand All @@ -652,7 +680,7 @@ const CanvasElementContextControls: React.FunctionComponent<{
</Fragment>
)}
{(!(hasImage && isPlaceHolder) &&
!editableTextElement &&
!showFormatButton &&
!(hasVideo && !videoAlreadyChosen)) || (
// Add a spacer if there is any button before these
<div
Expand Down Expand Up @@ -1876,6 +1904,33 @@ function addImageMenuOptions(
theOneCanvasElementManager.updateCanvasElementForChangedImage(
bgImg,
);

// We want to make it active. However, if it used to be a placeholder, it was
// previously hidden. This interferes with making the measurements to move the
// control frame (at least), so we make a few attempts to activate it.
// Don't make a long timeout here, because it could override some other
// selection that the user quickly makes.
const activateConvertedBackground = () => {
requestAnimationFrame(() => {
theOneCanvasElementManager.setActiveElement(
bgImageCe,
);
});
};
activateConvertedBackground();
if (!haveRealBgImage) {
const fallbackHandle = setTimeout(() => {
activateConvertedBackground();
}, 120);
bgImg.addEventListener(
"load",
() => {
clearTimeout(fallbackHandle);
activateConvertedBackground();
},
{ once: true },
);
}
Comment thread
hatton marked this conversation as resolved.
setMenuOpen(false);
},
});
Expand Down
74 changes: 74 additions & 0 deletions src/BloomBrowserUI/bookEdit/toolbox/canvas/customXmatterPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ async function convertXmatterPageToCustom(page: HTMLElement): Promise<void> {
// where to put its new canvas element.
ceContent = elem.cloneNode(true) as HTMLElement;
}

preserveHintMetadata(elem as HTMLElement, ceContent as HTMLElement);

// make a new canvas element to hold this. Not sure what problems it will
// cause when it's NOT a TG, but we have at least topic and language name that
// never are and are commonly on the front cover.
Expand Down Expand Up @@ -231,6 +234,77 @@ async function getLanguageNameValues(): Promise<ILanguageNameValues> {
.data as ILanguageNameValues;
}

function copyHintAttributes(source: Element, target: HTMLElement): void {
[
"data-hint",
"data-i18n",
"data-link-text",
"data-link-target",
"data-functiononhintclick",
].forEach((attr) => {
const value = source.getAttribute(attr);
if (value && !target.hasAttribute(attr)) {
target.setAttribute(attr, value);
}
});
}

function preserveHintMetadata(
sourceElement: HTMLElement,
convertedElement: HTMLElement,
): void {
// If we've already preserved label content or explicit hint metadata, leave it alone.
if (
convertedElement.querySelector("label.bubble") ||
convertedElement.hasAttribute("data-hint")
) {
return;
}

const translationGroup = sourceElement.closest(
".bloom-translationGroup",
) as HTMLElement | null;
if (!translationGroup) {
copyHintAttributes(sourceElement, convertedElement);
if (
!convertedElement.hasAttribute("data-hint") &&
sourceElement.parentElement
) {
copyHintAttributes(sourceElement.parentElement, convertedElement);
}
return;
}

copyHintAttributes(translationGroup, convertedElement);

const label = translationGroup.querySelector("label.bubble");
if (!label) {
if (
!convertedElement.hasAttribute("data-hint") &&
translationGroup.parentElement
) {
copyHintAttributes(
translationGroup.parentElement,
convertedElement,
);
}
return;
}

const labelText = label.textContent?.trim();
if (labelText && !convertedElement.hasAttribute("data-hint")) {
convertedElement.setAttribute("data-hint", labelText);
}
copyHintAttributes(label, convertedElement);

if (
!convertedElement.hasAttribute("data-hint") &&
translationGroup.parentElement
) {
copyHintAttributes(translationGroup.parentElement, convertedElement);
}
}

function setDataDefault(
ceContent: HTMLElement,
lang: string,
Expand Down
Loading