mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-07-15 21:31:36 +03:00
Lexical: Added RTL support for UI dropdown menus
They now show in the correct direction and do not overlap. Added new helper for RTL bounding box handling.
This commit is contained in:
@@ -27,7 +27,7 @@ export class EditorTableCreator extends EditorUiElement {
|
||||
}, rowCells));
|
||||
}
|
||||
|
||||
const display = el('div', {class: 'editor-table-creator-display'}, ['0 x 0']);
|
||||
const display = el('div', {class: 'editor-table-creator-display', dir: 'ltr'}, ['0 x 0']);
|
||||
const grid = el('div', {class: 'editor-table-creator-grid'}, rows);
|
||||
grid.addEventListener('mousemove', event => {
|
||||
const cell = (event.target as HTMLElement).closest('.editor-table-creator-cell') as HTMLElement|null;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import {getViewportRect, RTLRect} from "../../../utils/rtl";
|
||||
|
||||
interface HandleDropdownParams {
|
||||
toggle: HTMLElement;
|
||||
menu: HTMLElement;
|
||||
@@ -7,30 +9,31 @@ interface HandleDropdownParams {
|
||||
showAside?: boolean;
|
||||
}
|
||||
|
||||
function positionMenu(menu: HTMLElement, toggle: HTMLElement, showAside: boolean) {
|
||||
const toggleRect = toggle.getBoundingClientRect();
|
||||
const menuBounds = menu.getBoundingClientRect();
|
||||
function positionMenu(menu: HTMLElement, toggle: HTMLElement, showAside: boolean, isRTL: boolean) {
|
||||
const toggleRect = new RTLRect(toggle, isRTL);
|
||||
const menuBounds = new RTLRect(menu, isRTL);
|
||||
const viewport = getViewportRect();
|
||||
|
||||
menu.style.position = 'fixed';
|
||||
|
||||
if (showAside) {
|
||||
let targetLeft = toggleRect.right;
|
||||
const isRightOOB = toggleRect.right + menuBounds.width > window.innerWidth;
|
||||
if (isRightOOB) {
|
||||
targetLeft = Math.max(toggleRect.left - menuBounds.width, 0);
|
||||
let targetLeft = toggleRect.inlineEnd;
|
||||
const isEndOOB = toggleRect.inlineEnd + menuBounds.width > viewport.width;
|
||||
if (isEndOOB) {
|
||||
targetLeft = Math.max(toggleRect.inlineStart - menuBounds.width, 0);
|
||||
}
|
||||
|
||||
menu.style.top = toggleRect.top + 'px';
|
||||
menu.style.left = targetLeft + 'px';
|
||||
menu.style.top = toggleRect.blockStart + 'px';
|
||||
menu.style.insetInlineStart = targetLeft + 'px';
|
||||
} else {
|
||||
const isRightOOB = toggleRect.left + menuBounds.width > window.innerWidth;
|
||||
let targetLeft = toggleRect.left;
|
||||
if (isRightOOB) {
|
||||
targetLeft = Math.max(toggleRect.right - menuBounds.width, 0);
|
||||
const isEndOOB = toggleRect.inlineStart + menuBounds.width > viewport.width;
|
||||
let targetLeft = toggleRect.inlineStart;
|
||||
if (isEndOOB) {
|
||||
targetLeft = Math.max(toggleRect.inlineEnd - menuBounds.width, 0);
|
||||
}
|
||||
|
||||
menu.style.top = toggleRect.bottom + 'px';
|
||||
menu.style.left = targetLeft + 'px';
|
||||
menu.style.top = toggleRect.blockEnd + 'px';
|
||||
menu.style.insetInlineStart = targetLeft + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +41,7 @@ export class DropDownManager {
|
||||
|
||||
protected dropdownOptions: WeakMap<HTMLElement, HandleDropdownParams> = new WeakMap();
|
||||
protected openDropdowns: Set<HTMLElement> = new Set();
|
||||
protected isRTL: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.onMenuMouseOver = this.onMenuMouseOver.bind(this);
|
||||
@@ -46,6 +50,10 @@ export class DropDownManager {
|
||||
window.addEventListener('click', this.onWindowClick);
|
||||
}
|
||||
|
||||
setIsRTL(isRTL: boolean): void {
|
||||
this.isRTL = isRTL;
|
||||
}
|
||||
|
||||
teardown(): void {
|
||||
window.removeEventListener('click', this.onWindowClick);
|
||||
}
|
||||
@@ -80,7 +88,7 @@ export class DropDownManager {
|
||||
protected closeDropdown(menu: HTMLElement): void {
|
||||
menu.hidden = true;
|
||||
menu.style.removeProperty('position');
|
||||
menu.style.removeProperty('left');
|
||||
menu.style.removeProperty('inset-inline-start');
|
||||
menu.style.removeProperty('top');
|
||||
|
||||
this.openDropdowns.delete(menu);
|
||||
@@ -94,8 +102,8 @@ export class DropDownManager {
|
||||
|
||||
protected openDropdown(menu: HTMLElement): void {
|
||||
const {toggle, showAside, onOpen} = this.getOptions(menu);
|
||||
menu.hidden = false
|
||||
positionMenu(menu, toggle, Boolean(showAside));
|
||||
menu.hidden = false;
|
||||
positionMenu(menu, toggle, Boolean(showAside), this.isRTL);
|
||||
|
||||
this.openDropdowns.add(menu);
|
||||
menu.addEventListener('mouseover', this.onMenuMouseOver);
|
||||
|
||||
@@ -30,6 +30,7 @@ export class EditorUIManager {
|
||||
this.context = context;
|
||||
this.setupEventListeners();
|
||||
this.setupEditor(context.editor, context);
|
||||
this.dropdowns.setIsRTL(this.context.manager.getDefaultDirection() === 'rtl');
|
||||
}
|
||||
|
||||
getContext(): EditorUiContext {
|
||||
|
||||
49
resources/js/wysiwyg/utils/rtl.ts
Normal file
49
resources/js/wysiwyg/utils/rtl.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Create a viewport relative rect for an element which provides
|
||||
* logical property support.
|
||||
*/
|
||||
export class RTLRect {
|
||||
protected rect: DOMRect;
|
||||
protected isRTL: boolean;
|
||||
|
||||
constructor(element: HTMLElement, isRTL = false) {
|
||||
this.rect = element.getBoundingClientRect();
|
||||
this.isRTL = isRTL;
|
||||
}
|
||||
|
||||
get blockStart(): number {
|
||||
return this.rect.top;
|
||||
}
|
||||
|
||||
get inlineStart(): number {
|
||||
if (!this.isRTL) {
|
||||
return this.rect.left;
|
||||
}
|
||||
|
||||
return window.innerWidth - this.rect.right;
|
||||
}
|
||||
|
||||
get blockEnd(): number {
|
||||
return this.rect.bottom;
|
||||
}
|
||||
|
||||
get inlineEnd(): number {
|
||||
if (!this.isRTL) {
|
||||
return this.rect.right;
|
||||
}
|
||||
|
||||
return window.innerWidth - this.rect.left;
|
||||
}
|
||||
|
||||
get width(): number {
|
||||
return this.rect.width;
|
||||
}
|
||||
|
||||
get height(): number {
|
||||
return this.rect.height;
|
||||
}
|
||||
}
|
||||
|
||||
export function getViewportRect(): RTLRect {
|
||||
return new RTLRect(document.documentElement, false);
|
||||
}
|
||||
Reference in New Issue
Block a user