mirror of
https://github.com/codex-team/editor.js
synced 2026-03-17 16:10:07 +01:00
fix: render popup directly in <body>
This commit is contained in:
parent
acb287b932
commit
9b0351f6db
8 changed files with 94 additions and 11 deletions
|
|
@ -128,8 +128,9 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
|
|||
* Open Block Settings pane
|
||||
*
|
||||
* @param targetBlock - near which Block we should open BlockSettings
|
||||
* @param trigger - element to position the popover relative to
|
||||
*/
|
||||
public async open(targetBlock?: Block): Promise<void> {
|
||||
public async open(targetBlock?: Block, trigger?: HTMLElement): Promise<void> {
|
||||
const block = targetBlock ?? this.Editor.BlockManager.currentBlock;
|
||||
|
||||
if (block === undefined) {
|
||||
|
|
@ -159,6 +160,7 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
|
|||
const PopoverClass = isMobileScreen() ? PopoverMobile : PopoverDesktop;
|
||||
const popoverParams: PopoverParams & { flipper?: Flipper } = {
|
||||
searchable: true,
|
||||
trigger: trigger || this.nodes.wrapper,
|
||||
items: await this.getTunesItems(block, commonTunes, toolTunes),
|
||||
scopeElement: this.Editor.API.methods.ui.nodes.redactor,
|
||||
messages: {
|
||||
|
|
@ -175,8 +177,6 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
|
|||
|
||||
this.popover.on(PopoverEvent.Closed, this.onPopoverClose);
|
||||
|
||||
this.nodes.wrapper?.append(this.popover.getElement());
|
||||
|
||||
this.popover.show();
|
||||
if (PopoverClass === PopoverDesktop) {
|
||||
this.flipperInstance.focusItem(0);
|
||||
|
|
|
|||
|
|
@ -523,6 +523,7 @@ export default class Toolbar extends Module<ToolbarNodes> {
|
|||
filter: I18n.ui(I18nInternalNS.ui.popover, 'Filter'),
|
||||
nothingFound: I18n.ui(I18nInternalNS.ui.popover, 'Nothing found'),
|
||||
},
|
||||
triggerElement: this.nodes.plusButton,
|
||||
});
|
||||
|
||||
this.toolboxInstance.on(ToolboxEvent.Opened, () => {
|
||||
|
|
@ -671,7 +672,7 @@ export default class Toolbar extends Module<ToolbarNodes> {
|
|||
if (this.Editor.BlockSettings.opened) {
|
||||
this.Editor.BlockSettings.close();
|
||||
} else {
|
||||
void this.Editor.BlockSettings.open(hoveredBlock);
|
||||
void this.Editor.BlockSettings.open(hoveredBlock, this.nodes.settingsToggler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,19 +123,31 @@ export default class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Element relative to which the popover should be positioned
|
||||
*/
|
||||
private triggerElement?: HTMLElement;
|
||||
|
||||
/**
|
||||
* Toolbox constructor
|
||||
*
|
||||
* @param options - available parameters
|
||||
* @param options.api - Editor API methods
|
||||
* @param options.tools - Tools available to check whether some of them should be displayed at the Toolbox or not
|
||||
* @param options.triggerElement - Element relative to which the popover should be positioned
|
||||
*/
|
||||
constructor({ api, tools, i18nLabels }: {api: API; tools: ToolsCollection<BlockToolAdapter>; i18nLabels: Record<ToolboxTextLabelsKeys, string>}) {
|
||||
constructor({ api, tools, i18nLabels, triggerElement }: {
|
||||
api: API;
|
||||
tools: ToolsCollection<BlockToolAdapter>;
|
||||
i18nLabels: Record<ToolboxTextLabelsKeys, string>;
|
||||
triggerElement?: HTMLElement;
|
||||
}) {
|
||||
super();
|
||||
|
||||
this.api = api;
|
||||
this.tools = tools;
|
||||
this.i18nLabels = i18nLabels;
|
||||
this.triggerElement = triggerElement;
|
||||
|
||||
this.enableShortcuts();
|
||||
|
||||
|
|
@ -245,6 +257,7 @@ export default class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|||
|
||||
this.popover = new PopoverClass({
|
||||
scopeElement: this.api.ui.nodes.redactor,
|
||||
trigger: this.triggerElement || this.nodes.toolbox,
|
||||
searchable: true,
|
||||
messages: {
|
||||
nothingFound: this.i18nLabels.nothingFound,
|
||||
|
|
@ -254,7 +267,6 @@ export default class Toolbox extends EventsDispatcher<ToolboxEventMap> {
|
|||
});
|
||||
|
||||
this.popover.on(PopoverEvent.Closed, this.onPopoverClose);
|
||||
this.nodes.toolbox?.append(this.popover.getElement());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ export class PopoverItemDefault extends PopoverItem {
|
|||
* Item title
|
||||
*/
|
||||
public get title(): string | undefined {
|
||||
return this.params.title;
|
||||
// eslint-disable-next-line deprecation/deprecation -- TODO: remove this once label is removed
|
||||
return this.params.title || this.params.label;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -173,9 +174,12 @@ export class PopoverItemDefault extends PopoverItem {
|
|||
|
||||
el.appendChild(this.nodes.icon);
|
||||
|
||||
if (params.title !== undefined) {
|
||||
// eslint-disable-next-line deprecation/deprecation -- TODO: remove this once label is removed
|
||||
const title = params.title || params.label;
|
||||
|
||||
if (title !== undefined) {
|
||||
el.appendChild(Dom.make('div', css.title, {
|
||||
innerHTML: params.title || '',
|
||||
innerHTML: title || '',
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,10 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
|
|||
* Open popover
|
||||
*/
|
||||
public show(): void {
|
||||
if (!this.nodes.popover.isConnected) {
|
||||
document.body.appendChild(this.nodes.popover);
|
||||
}
|
||||
|
||||
this.nodes.popover.classList.add(css.popoverOpened);
|
||||
this.nodes.popover.setAttribute('data-popover-opened', 'true');
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,11 @@ export class PopoverDesktop extends PopoverAbstract {
|
|||
*/
|
||||
private scopeElement: HTMLElement = document.body;
|
||||
|
||||
/**
|
||||
* Element relative to which the popover should be positioned
|
||||
*/
|
||||
private trigger: HTMLElement | undefined;
|
||||
|
||||
/**
|
||||
* Construct the instance
|
||||
*
|
||||
|
|
@ -63,6 +68,10 @@ export class PopoverDesktop extends PopoverAbstract {
|
|||
constructor(params: PopoverParams, itemsRenderParams?: PopoverItemRenderParamsMap) {
|
||||
super(params, itemsRenderParams);
|
||||
|
||||
if (params.trigger) {
|
||||
this.trigger = params.trigger;
|
||||
}
|
||||
|
||||
if (params.nestingLevel !== undefined) {
|
||||
this.nestingLevel = params.nestingLevel;
|
||||
}
|
||||
|
|
@ -144,13 +153,24 @@ export class PopoverDesktop extends PopoverAbstract {
|
|||
* Open popover
|
||||
*/
|
||||
public show(): void {
|
||||
if (this.trigger) {
|
||||
document.body.appendChild(this.nodes.popover);
|
||||
const { top, left } = this.calculatePosition();
|
||||
|
||||
this.nodes.popover.style.position = 'absolute';
|
||||
this.nodes.popover.style.top = `${top}px`;
|
||||
this.nodes.popover.style.left = `${left}px`;
|
||||
this.nodes.popover.style.setProperty('--popover-top', '0px');
|
||||
this.nodes.popover.style.setProperty('--popover-left', '0px');
|
||||
}
|
||||
|
||||
this.nodes.popover.style.setProperty(CSSVariables.PopoverHeight, this.size.height + 'px');
|
||||
|
||||
if (!this.shouldOpenBottom) {
|
||||
if (!this.trigger && !this.shouldOpenBottom) {
|
||||
this.nodes.popover.classList.add(css.popoverOpenTop);
|
||||
}
|
||||
|
||||
if (!this.shouldOpenRight) {
|
||||
if (!this.trigger && !this.shouldOpenRight) {
|
||||
this.nodes.popover.classList.add(css.popoverOpenLeft);
|
||||
}
|
||||
|
||||
|
|
@ -158,6 +178,38 @@ export class PopoverDesktop extends PopoverAbstract {
|
|||
this.flipper?.activate(this.flippableElements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates position for the popover
|
||||
*/
|
||||
private calculatePosition(): { top: number; left: number } {
|
||||
if (!this.trigger) {
|
||||
return {
|
||||
top: 0,
|
||||
left: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const triggerRect = this.trigger.getBoundingClientRect();
|
||||
const popoverRect = this.size;
|
||||
const windowWidth = window.innerWidth;
|
||||
const windowHeight = window.innerHeight;
|
||||
const offset = 8;
|
||||
|
||||
const initialTop = triggerRect.bottom + offset + window.scrollY;
|
||||
const shouldFlipTop = (triggerRect.bottom + offset + popoverRect.height > windowHeight + window.scrollY) &&
|
||||
(triggerRect.top - offset - popoverRect.height > window.scrollY);
|
||||
const top = shouldFlipTop ? triggerRect.top - offset - popoverRect.height + window.scrollY : initialTop;
|
||||
|
||||
const initialLeft = triggerRect.left + window.scrollX;
|
||||
const shouldFlipLeft = initialLeft + popoverRect.width > windowWidth + window.scrollX;
|
||||
const left = shouldFlipLeft ? Math.max(0, triggerRect.right - popoverRect.width + window.scrollX) : initialLeft;
|
||||
|
||||
return {
|
||||
top,
|
||||
left,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes popover
|
||||
*/
|
||||
|
|
|
|||
5
types/utils/popover/popover-item.d.ts
vendored
5
types/utils/popover/popover-item.d.ts
vendored
|
|
@ -128,6 +128,11 @@ export interface PopoverItemDefaultBaseParams {
|
|||
*/
|
||||
title?: string;
|
||||
|
||||
/**
|
||||
* @deprecated Use title instead
|
||||
*/
|
||||
label?: string;
|
||||
|
||||
/**
|
||||
* Item icon to be appeared near a title
|
||||
*/
|
||||
|
|
|
|||
5
types/utils/popover/popover.d.ts
vendored
5
types/utils/popover/popover.d.ts
vendored
|
|
@ -17,6 +17,11 @@ export interface PopoverParams {
|
|||
*/
|
||||
scopeElement?: HTMLElement;
|
||||
|
||||
/**
|
||||
* Element relative to which the popover should be positioned
|
||||
*/
|
||||
trigger?: HTMLElement;
|
||||
|
||||
/**
|
||||
* True if popover should contain search field
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue