-
-
Notifications
You must be signed in to change notification settings - Fork 18
AutoSize blocks on custom page when field or lang changes (BL-15996) #7748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,8 +71,10 @@ import { | |
| kBloomButtonClass, | ||
| kBloomCanvasSelector, | ||
| } from "../toolbox/canvas/canvasElementUtils"; | ||
| import { getString, post, useApiObject } from "../../utils/bloomApi"; | ||
| import { wrapWithRequestPageContentDelay } from "./bloomEditing"; | ||
| import { get, post, useApiObject } from "../../utils/bloomApi"; | ||
| import { ILanguageNameValues } from "../bookSettings/FieldVisibilityGroup"; | ||
| import OverflowChecker from "../OverflowChecker/OverflowChecker"; | ||
|
|
||
| interface IMenuItemWithSubmenu extends ILocalizableMenuItemProps { | ||
| subMenu?: ILocalizableMenuItemProps[]; | ||
|
|
@@ -938,6 +940,76 @@ const CanvasElementContextControls: React.FunctionComponent<{ | |
| // So until I get a better idea, I'm just putting in a hard-coded list. | ||
| const fieldsControlledByAppearanceSystem = ["bookTitle"]; | ||
|
|
||
| function adjustAutoSizeForVisibleEditableInTranslationGroup(tg: HTMLElement) { | ||
| const visibleEditable = tg.getElementsByClassName( | ||
| "bloom-editable bloom-visibility-code-on", | ||
| )[0] as HTMLElement; | ||
| if (!visibleEditable) { | ||
| return; | ||
| } | ||
| OverflowChecker.AdjustSizeOrMarkOverflow(visibleEditable); | ||
| } | ||
|
|
||
| function setEditableContentFromKnownDataBookValueIfAny( | ||
| editable: HTMLElement, | ||
| dataBook: string | null, | ||
| tg: HTMLElement, | ||
| ) { | ||
| if (!dataBook) { | ||
| return; | ||
| } | ||
| wrapWithRequestPageContentDelay( | ||
| () => | ||
| new Promise<void>((resolve, reject) => { | ||
| get( | ||
| `editView/getDataBookValue?lang=${editable.getAttribute("lang")}&dataBook=${dataBook}`, | ||
| (result) => { | ||
| try { | ||
| const content = result.data; | ||
| // content comes from a source that looked empty, we don't want to overwrite something the user may | ||
| // already have typed here. | ||
| // But it may well have something in it, because we usually have an empty paragraph to start with. | ||
| // To test whether it looks empty, we put the text into a newly created element and then | ||
| // see whether it's textContent is empty. | ||
| // The logic of overwriting something which the user has typed here is that if we keep what's here, | ||
| // then the user may never know that there was already something in that field. But if we overwrite, then | ||
| // the user can always correct it back to what he just typed. | ||
| const temp = document.createElement("div"); | ||
| temp.innerHTML = content || ""; | ||
| if (temp.textContent.trim() !== "") | ||
| editable.innerHTML = content; | ||
| adjustAutoSizeForVisibleEditableInTranslationGroup( | ||
| tg, | ||
| ); | ||
| resolve(); | ||
| } catch (error) { | ||
| reject(error); | ||
| } | ||
| }, | ||
| (error) => { | ||
| reject(error); | ||
| }, | ||
| ); | ||
| }), | ||
| "setCanvasFieldValueFromDataBook", | ||
| ); | ||
| } | ||
|
|
||
| function applyAppearanceClassForEditable(editable: HTMLElement) { | ||
| editable.classList.remove( | ||
| "bloom-contentFirst", | ||
| "bloom-contentSecond", | ||
| "bloom-contentThird", | ||
| ); | ||
| if (editable.classList.contains("bloom-content1")) { | ||
| editable.classList.add("bloom-contentFirst"); | ||
| } else if (editable.classList.contains("bloom-contentNational1")) { | ||
| editable.classList.add("bloom-contentSecond"); | ||
| } else if (editable.classList.contains("bloom-contentNational2")) { | ||
| editable.classList.add("bloom-contentThird"); | ||
| } | ||
| } | ||
|
|
||
| function makeLanguageMenuItem( | ||
| ce: HTMLElement, | ||
| menuOptions: IMenuItemWithSubmenu[], | ||
|
|
@@ -962,7 +1034,7 @@ function makeLanguageMenuItem( | |
| tg.setAttribute("data-default-languages", dataDefaultLang); | ||
| const editables = Array.from( | ||
| tg.getElementsByClassName("bloom-editable"), | ||
| ); | ||
| ) as HTMLElement[]; | ||
| if (editables.length === 0) return; // not able to handle this yet. | ||
| let editableInLang = editables.find( | ||
| (e) => e.getAttribute("lang") === langCode, | ||
|
|
@@ -1013,11 +1085,18 @@ function makeLanguageMenuItem( | |
| } | ||
| } | ||
|
|
||
| setEditableContentFromKnownDataBookValueIfAny( | ||
| editableInLang, | ||
| dataBookValue, | ||
| tg, | ||
| ); | ||
|
|
||
| // and conversely remove them from the others | ||
| for (const editable of editables) { | ||
| // Ensure visibility code is off for others. | ||
| editable.classList.remove("bloom-visibility-code-on"); | ||
| } | ||
| adjustAutoSizeForVisibleEditableInTranslationGroup(tg); | ||
| setMenuOpen(false); | ||
| }; | ||
|
|
||
|
|
@@ -1183,6 +1262,37 @@ function makeFieldTypeMenuItem( | |
| } | ||
| }; | ||
|
|
||
| const removeConflictingStyleClasses = ( | ||
| fieldType: { | ||
| editableClasses: string[]; | ||
| classes: string[]; | ||
| }, | ||
| editables: HTMLElement[], | ||
| ) => { | ||
| const newStyleClasses = new Set( | ||
| [...fieldType.classes, ...fieldType.editableClasses].filter((c) => | ||
| c.endsWith("-style"), | ||
| ), | ||
| ); | ||
| if (newStyleClasses.size === 0) { | ||
| return; | ||
| } | ||
|
|
||
| const stripStyleClasses = (element: HTMLElement) => { | ||
| Array.from(element.classList).forEach((className) => { | ||
| if ( | ||
| className.endsWith("-style") && | ||
| !newStyleClasses.has(className) | ||
| ) { | ||
| element.classList.remove(className); | ||
| } | ||
| }); | ||
| }; | ||
|
|
||
| stripStyleClasses(tg); | ||
| editables.forEach((editable) => stripStyleClasses(editable)); | ||
| }; | ||
|
Comment on lines
+1265
to
+1294
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚩 removeConflictingStyleClasses strips all non-matching -style classes, including non-field-type ones The new Was this helpful? React with 👍 or 👎 to provide feedback.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no way to get back to the class it might have had before the Field was first set, unless we save it in some data- attribute. Having more than one -style class on an element is asking for trouble (e.g., which one would style editor let you edit?), so I think removing all the others is necessary. I did add restoring the usual default style for comic bubbles, since I'm not sure what might fail if there is not at least one -style class. We might still want some improvement here, but I think we need users to tell us what. |
||
|
|
||
| const activeType = tg | ||
| .getElementsByClassName("bloom-editable bloom-visibility-code-on")[0] | ||
| ?.getAttribute("data-book"); | ||
|
|
@@ -1194,9 +1304,21 @@ function makeFieldTypeMenuItem( | |
| clearFieldTypeClasses(); | ||
| for (const editable of Array.from( | ||
| tg.getElementsByClassName("bloom-editable"), | ||
| )) { | ||
| ) as HTMLElement[]) { | ||
| editable.removeAttribute("data-book"); | ||
| // There's a bit of guess-work involved in what would be most helpful here. | ||
| // clearFieldTypeClasses removes any field-type-specific style class, | ||
| // and we generally expect a bloom-editable to have some style class. | ||
| // Should it be Normal-style or Bubble-style? Bubble-style is the default | ||
| // for canvas elements, so I decided to go with that. | ||
| const hasStyleClass = Array.from(editable.classList).some( | ||
| (className) => className.endsWith("-style"), | ||
| ); | ||
| if (!hasStyleClass) { | ||
| editable.classList.add("Bubble-style"); | ||
| } | ||
| } | ||
| adjustAutoSizeForVisibleEditableInTranslationGroup(tg); | ||
| setMenuOpen(false); | ||
| }, | ||
| icon: !activeType && <CheckIcon css={getMenuIconCss()} />, | ||
|
|
@@ -1210,7 +1332,7 @@ function makeFieldTypeMenuItem( | |
| clearFieldTypeClasses(); | ||
| const editables = Array.from( | ||
| tg.getElementsByClassName("bloom-editable"), | ||
| ); | ||
| ) as HTMLElement[]; | ||
| if (fieldType.readOnly) { | ||
| const readOnlyDiv = document.createElement("div"); | ||
| readOnlyDiv.setAttribute( | ||
|
|
@@ -1232,34 +1354,37 @@ function makeFieldTypeMenuItem( | |
| // Reload the page to get the derived content loaded. | ||
| post("common/saveChangesAndRethinkPageEvent", () => {}); | ||
| } else { | ||
| removeConflictingStyleClasses(fieldType, editables); | ||
| tg.classList.add(...fieldType.classes); | ||
| for (const editable of editables) { | ||
| editable.classList.add(...fieldType.editableClasses); | ||
| editable.setAttribute("data-book", fieldType.dataBook); | ||
| if ( | ||
| fieldsControlledByAppearanceSystem.includes( | ||
| fieldType.dataBook, | ||
| ) | ||
| ) { | ||
| applyAppearanceClassForEditable(editable); | ||
| } else { | ||
| editable.classList.remove( | ||
| "bloom-contentFirst", | ||
| "bloom-contentSecond", | ||
| "bloom-contentThird", | ||
| ); | ||
| } | ||
| if ( | ||
| editable.classList.contains( | ||
| "bloom-visibility-code-on", | ||
| ) | ||
| ) { | ||
| getString( | ||
| `editView/getDataBookValue?lang=${editable.getAttribute("lang")}&dataBook=${fieldType.dataBook}`, | ||
| (content) => { | ||
| // content comes from a source that looked empty, we don't want to overwrite something the user may | ||
| // already have typed here. | ||
| // But it may well have something in it, because we usually have an empty paragraph to start with. | ||
| // To test whether it looks empty, we put the text into a newly created element and then | ||
| // see whether it's textContent is empty. | ||
| // The logic of overwriting something which the user has typed here is that if we keep what's here, | ||
| // then the user may never know that there was already something in that field. But if we overwrite, then | ||
| // the user can always correct it back to what he just typed. | ||
| const temp = document.createElement("div"); | ||
| temp.innerHTML = content || ""; | ||
| if (temp.textContent.trim() !== "") | ||
| editable.innerHTML = content; | ||
| }, | ||
| setEditableContentFromKnownDataBookValueIfAny( | ||
| editable, | ||
| fieldType.dataBook, | ||
| tg, | ||
| ); | ||
| } | ||
| } | ||
| adjustAutoSizeForVisibleEditableInTranslationGroup(tg); | ||
| } | ||
| setMenuOpen(false); | ||
| }, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.