Skip to content

feat: nested popover #2649

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

Merged
merged 39 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
48247e8
Move popover types to separate file
TatianaFomina Feb 3, 2024
57242bf
tmp
TatianaFomina Feb 4, 2024
2609ac5
open top
TatianaFomina Feb 18, 2024
1c7925d
Fix bug with keyboard navigation
TatianaFomina Mar 9, 2024
86142f9
Fix bug with scroll
TatianaFomina Mar 9, 2024
6be9cf2
Fix mobile
TatianaFomina Mar 9, 2024
44a63ac
Add popover header class
TatianaFomina Mar 9, 2024
77d0dcd
Display nested items on mobile
TatianaFomina Mar 9, 2024
04d2948
Refactor history
TatianaFomina Mar 13, 2024
2f9d0fb
Fix positioning on desktop
TatianaFomina Mar 16, 2024
9821a3f
Fix tests
TatianaFomina Mar 16, 2024
8e65390
Fix child popover indent left
TatianaFomina Mar 16, 2024
cef51c7
Fix ts errors in popover files
TatianaFomina Mar 16, 2024
6871c0a
Move files
TatianaFomina Mar 16, 2024
de7c00e
Rename cn to bem
TatianaFomina Mar 23, 2024
d19665c
Clarify comments and rename method
TatianaFomina Mar 23, 2024
6502e71
Refactor popover css classes
TatianaFomina Mar 23, 2024
6dd5141
Rename cls to css
TatianaFomina Mar 23, 2024
1c2dc42
Split popover desktop and mobile classes
TatianaFomina Mar 27, 2024
cf222e8
Add ability to open popover to the left if not enough space to open t…
TatianaFomina Mar 30, 2024
b761e4a
Add nested popover test
TatianaFomina Mar 23, 2024
f36cb86
Add popover test for mobile screens
TatianaFomina Mar 23, 2024
da00f16
Fix tests
TatianaFomina Mar 30, 2024
fe4cdf6
Add union type for both popovers
TatianaFomina Mar 30, 2024
b056297
Merge branch 'next' into feat/nested-popover
TatianaFomina Apr 3, 2024
ce35b59
Add global window resize event
TatianaFomina Apr 3, 2024
75d8476
Multiple fixes
TatianaFomina Apr 3, 2024
7fab576
Move nodes initialization to constructor
TatianaFomina Apr 6, 2024
0bce02c
Rename handleShowingNestedItems to showNestedItems
TatianaFomina Apr 6, 2024
b13f8c0
Replace WindowResize with EditorMobileLayoutToggled
TatianaFomina Apr 6, 2024
dcdb8fb
New doze of fixes
TatianaFomina Apr 7, 2024
c2a7c8f
Review fixes
TatianaFomina Apr 8, 2024
acd2710
Fixes
TatianaFomina Apr 8, 2024
b5fbccb
Fixes
TatianaFomina Apr 8, 2024
325aeac
Make each nested popover decide itself if it should open top
TatianaFomina Apr 13, 2024
5942b18
Update changelog
TatianaFomina Apr 13, 2024
7611e0a
Update changelog
TatianaFomina Apr 13, 2024
03aa22c
Update changelog
TatianaFomina Apr 13, 2024
2d9d73a
Merge branch 'next' into feat/nested-popover
TatianaFomina Apr 13, 2024
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
6 changes: 4 additions & 2 deletions src/components/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ export default class Dom {
* @param {object} [attributes] - any attributes
* @returns {HTMLElement}
*/
public static make(tagName: string, classNames: string | string[] | null = null, attributes: object = {}): HTMLElement {
public static make(tagName: string, classNames: string | (string | undefined)[] | null = null, attributes: object = {}): HTMLElement {
const el = document.createElement(tagName);

if (Array.isArray(classNames)) {
el.classList.add(...classNames);
const validClassnames = classNames.filter(className => className !== undefined) as string[];

el.classList.add(...validClassnames);
} else if (classNames) {
el.classList.add(classNames);
}
Expand Down
9 changes: 2 additions & 7 deletions src/components/flipper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,11 @@ export default class Flipper {

/**
* Instance of flipper iterator
*
* @type {DomIterator|null}
*/
private readonly iterator: DomIterator = null;
private readonly iterator: DomIterator | null = null;

/**
* Flag that defines activation status
*
* @type {boolean}
*/
private activated = false;

Expand All @@ -77,7 +73,7 @@ export default class Flipper {
private flipCallbacks: Array<() => void> = [];

/**
* @param {FlipperOptions} options - different constructing settings
* @param options - different constructing settings
*/
constructor(options: FlipperOptions) {
this.iterator = new DomIterator(options.items, options.focusedItemClass);
Expand Down Expand Up @@ -110,7 +106,6 @@ export default class Flipper {
*/
public activate(items?: HTMLElement[], cursorPosition?: number): void {
this.activated = true;

if (items) {
this.iterator.setItems(items);
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/modules/toolbar/blockSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { I18nInternalNS } from '../../i18n/namespace-internal';
import Flipper from '../../flipper';
import { TunesMenuConfigItem } from '../../../../types/tools';
import { resolveAliases } from '../../utils/resolve-aliases';
import Popover, { PopoverEvent } from '../../utils/popover';
import Popover from '../../utils/popover';
import { PopoverEvent } from '../../utils/popover/popover.typings';

/**
* HTML Elements that used for BlockSettings
Expand Down
3 changes: 2 additions & 1 deletion src/components/ui/toolbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import BlockTool from '../tools/block';
import ToolsCollection from '../tools/collection';
import { API, BlockToolData, ToolboxConfigEntry, PopoverItem, BlockAPI } from '../../../types';
import EventsDispatcher from '../utils/events';
import Popover, { PopoverEvent } from '../utils/popover';
import Popover from '../utils/popover';
import I18n from '../i18n';
import { I18nInternalNS } from '../i18n/namespace-internal';
import { PopoverEvent } from '../utils/popover/popover.typings';

/**
* @todo the first Tab on the Block — focus Plus Button, the second — focus Block Tunes Toggler, the third — focus next Block
Expand Down
21 changes: 21 additions & 0 deletions src/components/utils/bem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const ELEMENT_DELIMITER = '__';
const MODIFIER_DELIMITER = '--';

/**
* Utility function that allows to construct class names from block and element names
*
* @param blockName - string with block name
* @param elementName - string with element name
* @param modifier - modifier to be appended
*/
export function bem(blockName: string) {
return (elementName?: string, modifier?: string) => {
const className = [blockName, elementName]
.filter(x => !!x)
.join(ELEMENT_DELIMITER);

return [className, modifier]
.filter(x => !!x)
.join(MODIFIER_DELIMITER);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './popover-header';
export * from './popover-header.typings';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { bem } from '../../../bem';

/**
* Popover header block CSS class constructor
*/
const popoverHeaderCn = bem('ce-popover-header');

/**
* CSS class names to be used in popover header class
*/
export const cls = {
root: popoverHeaderCn(),
text: popoverHeaderCn('text'),
backButton: popoverHeaderCn('back-button'),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { PopoverHeaderParams } from './popover-header.typings';
import Dom from '../../../../dom';
import { cls } from './popover-header.const';
import { IconChevronLeft } from '@codexteam/icons';
import Listeners from '../../../listeners';

/**
* Represents popover header ui element
*/
export class PopoverHeader {
/**
* Listeners util instance
*/
private listeners = new Listeners();

/**
* Header html elements
*/
private nodes: {
root: null | HTMLElement,
text: null | HTMLElement,
backButton: null | HTMLElement
} = {
root: null,
text: null,
backButton: null,
};

/**
* Text displayed inside header
*/
private readonly text: string;

/**
* Back button click handler
*/
private readonly onBackButtonClick: () => void;

/**
* Constructs the instance
*
* @param params - popover header params
*/
constructor({ text, onBackButtonClick }: PopoverHeaderParams) {
this.text = text;
this.onBackButtonClick = onBackButtonClick;
this.make();
}

/**
* Returns popover header root html element
*/
public getElement(): HTMLElement | null {
return this.nodes.root;
}

/**
* Destroys the instance
*/
public destroy(): void {
this.nodes.root.remove();
this.nodes.root = null;
this.nodes.backButton = null;
this.nodes.text = null;
this.listeners.removeAll();
this.listeners.destroy();
}

/**
* Constructs HTML elements corresponding to popover header params
*/
private make(): void {
this.nodes.root = Dom.make('div', [ cls.root ]);

this.nodes.backButton = Dom.make('button', [ cls.backButton ]);
this.nodes.backButton.innerHTML = IconChevronLeft;
this.nodes.root.appendChild(this.nodes.backButton);
this.listeners.on(this.nodes.backButton, 'click', this.onBackButtonClick);

this.nodes.text = Dom.make('div', [ cls.text ]);
this.nodes.text.innerText = this.text;
this.nodes.root.appendChild(this.nodes.text);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Popover header params
*/
export interface PopoverHeaderParams {
/**
* Text to be displayed inside header
*/
text: string;

/**
* Back button click handler
*/
onBackButtonClick: () => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './popover-item';
export * from './popover-item.const';
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { bem } from '../../../bem';

/**
* Popover item block CSS class constructor
*/
const className = bem('ce-popover-item');

/**
* CSS class names to be used in popover item class
*/
export const cls = {
container: className(),
active: className(null, 'active'),
disabled: className(null, 'disabled'),
focused: className(null, 'focused'),
hidden: className(null, 'hidden'),
confirmationState: className(null, 'confirmation'),
noHover: className(null, 'no-hover'),
noFocus: className(null, 'no-focus'),
title: className('title'),
secondaryTitle: className('secondary-title'),
icon: className('icon'),
iconTool: className('icon', 'tool'),
iconChevronRight: className('icon', 'chevron-right'),
wobbleAnimation: bem('wobble')(),
};
Loading