diff --git a/resources/js/wysiwyg/ui/framework/blocks/table-creator.ts b/resources/js/wysiwyg/ui/framework/blocks/table-creator.ts index 6f026ca18..03e55ce64 100644 --- a/resources/js/wysiwyg/ui/framework/blocks/table-creator.ts +++ b/resources/js/wysiwyg/ui/framework/blocks/table-creator.ts @@ -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; diff --git a/resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts b/resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts index 890d5b325..f86482fdc 100644 --- a/resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts +++ b/resources/js/wysiwyg/ui/framework/helpers/dropdowns.ts @@ -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 = new WeakMap(); protected openDropdowns: Set = 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); diff --git a/resources/js/wysiwyg/ui/framework/manager.ts b/resources/js/wysiwyg/ui/framework/manager.ts index 3b4d5b495..f8525bc7c 100644 --- a/resources/js/wysiwyg/ui/framework/manager.ts +++ b/resources/js/wysiwyg/ui/framework/manager.ts @@ -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 { diff --git a/resources/js/wysiwyg/utils/rtl.ts b/resources/js/wysiwyg/utils/rtl.ts new file mode 100644 index 000000000..e04554f34 --- /dev/null +++ b/resources/js/wysiwyg/utils/rtl.ts @@ -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); +}