Skip to content

Commit

Permalink
inprogress
Browse files Browse the repository at this point in the history
  • Loading branch information
pomahtri committed Dec 8, 2024
1 parent 525c4fd commit 80581d6
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { Component, createRef } from 'inferno';

import type { Column } from '../../grid_core/columns_controller/types';
import { Button } from '../../grid_core/inferno_wrappers/button';
import { Popup } from '../../grid_core/inferno_wrappers/popup';
import { Sortable } from '../../grid_core/inferno_wrappers/sortable';
import { Item } from './item';

const CLASSES = {
popup: 'dx-cardview-headerpanel-popup',
};

export interface DropDownButtonProps {
columns: Column[];

onReorder?: (fromIndex: number, toIndex: number) => void;

onAdd?: (fromIndex: number, toIndex: number) => void;

onRemove?: (name: string) => void;
}

interface DropDownButtonState {
opened: boolean;
}

export class DropDownButton extends Component<DropDownButtonProps, DropDownButtonState> {
private readonly buttonRef = createRef<HTMLDivElement>();

private readonly popupPosition = {
my: 'top right',
at: 'bottom right',
collision: 'fit flip',
offset: { v: 3 },
of: this.buttonRef,
};

state = {
opened: false,
};

private readonly popupOptionChanged = ({ name, value }): void => {
if (name === 'visible') {
this.setState({ opened: value });
}
};

public render(): JSX.Element {
return (<>
<Button
elementRef={this.buttonRef}
icon="overflow"
onClick={(): void => { this.setState(({ opened }) => ({ opened: !opened })); }
}
/>
<Popup
visible={this.state.opened}
// @ts-expect-error
onOptionChanged={this.popupOptionChanged}
deferRendering={false}
height={'auto'}
width={'auto'}
hideOnParentScroll={true}
shading={false}
dragEnabled={false}
showTitle={false}
fullScreen={false}
// @ts-expect-error
position={this.popupPosition}
wrapperAttr={{
class: CLASSES.popup,
}}
>
<Sortable
itemOrientation='horizontal'
dropFeedbackMode='indicate'
onReorder={(e): void => this.props.onReorder?.(e.fromIndex, e.toIndex)}
onAdd={(e): void => this.props.onAdd?.(e.fromIndex, e.toIndex)}
group='cardview'
>
{this.props.columns.map((column) => (
<Item
column={column}
onRemove={
(): void => this.props.onRemove?.(column.name)
}
/>
))}
</Sortable>
</Popup>
</>);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
/**
* @module
* @document headers.spec.md
*/

/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { resizeObserverSingleton } from '@ts/core/m_resize_observer';
import { getBoundingRect } from '@ts/core/utils/m_position';
import type { Column } from '@ts/grids/new/grid_core/columns_controller/types';
import { Component } from 'inferno';
import { Component, createRef } from 'inferno';

import { Sortable } from '../../grid_core/inferno_wrappers/sortable';
import { DropDownButton } from './drop_down_button';
import { Item } from './item';

export const CLASSES = {
Expand All @@ -17,32 +16,128 @@ export interface HeaderPanelProps {
columns: Column[];

onReorder?: (fromIndex: number, toIndex: number) => void;

onAdd?: (fromIndex: number, toIndex: number) => void;

onHeaderRemoveButtonClicked?: (name: string) => void;
onRemove?: (name: string) => void;
}

interface HeaderPanelState {
shownColumnCount: number;
}

export class HeaderPanel extends Component<HeaderPanelProps> {
export class HeaderPanel extends Component<HeaderPanelProps, HeaderPanelState> {
private status: 'initial' | 'shrinking' | 'normal' = 'initial';

private normalHeight = 0;

private readonly ref = createRef<HTMLDivElement>();

state = {
shownColumnCount: 1,
};

public render(): JSX.Element {
const visibleColumns = this.props.columns.filter(
(_, index) => index < this.state.shownColumnCount,
);
const nonVisibleColumns = this.props.columns.filter(
(_, index) => index >= this.state.shownColumnCount,
);

return (
<div className={CLASSES.headers}>
<div className={CLASSES.headers} ref={this.ref}>
<Sortable
itemOrientation='horizontal'
dropFeedbackMode='indicate'
onReorder={(e): void => this.props.onReorder?.(e.fromIndex, e.toIndex)}
onAdd={(e): void => this.props.onAdd?.(e.fromIndex, e.toIndex)}
group='cardview'
>
{this.props.columns.map((column) => (
{visibleColumns.map((column) => (
<Item
column={column}
onRemoveButtonClicked={
(): void => this.props.onHeaderRemoveButtonClicked?.(column.name)
onRemove={
(): void => this.props.onRemove?.(column.name)
}
/>
))}
</Sortable>
{!!nonVisibleColumns.length && (
<DropDownButton
columns={nonVisibleColumns}
onRemove={this.props.onRemove}
/>
)}
</div>
);
}

private getHeight(): number {
return getBoundingRect(this.ref.current!).height as number;
}

private updateShownColumns(): void {
// eslint-disable-next-line no-undef-init
let stateToApply: undefined | HeaderPanelState = undefined;

switch (this.status) {
case 'initial': {
if (this.state.shownColumnCount !== 1) {
throw new Error();
}

if (!this.props.columns.length) {
return;
}

this.normalHeight = this.getHeight();
this.status = 'shrinking';
stateToApply = {
shownColumnCount: this.props.columns.length,
};
break;
}

case 'shrinking': {
if (this.getHeight() > this.normalHeight) {
stateToApply = {
shownColumnCount: this.state.shownColumnCount - 1,
};
} else {
this.status = 'normal';
}
break;
}

case 'normal': {
this.status = 'shrinking';
stateToApply = {
shownColumnCount: this.props.columns.length,
};
break;
}

default: {
// eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars
const _: never = this.status;
}
}

if (stateToApply) {
this.setState(stateToApply);
}
}

componentDidMount(): void {
resizeObserverSingleton.observe(
this.ref.current!,
() => this.updateShownColumns(),
);
}

componentDidUpdate(): void {
this.updateShownColumns();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ export const CLASSES = {
export interface HeaderItemProps {
column: Column;
buttons?: ComponentType;
onRemoveButtonClicked?: () => void;
onRemove?: () => void;
}

export function Item(props: HeaderItemProps): JSX.Element {
return (
<div className={CLASSES.item}>
{props.column.caption}
<Button
icon='close'
stylingMode='text'
elementAttr={{ class: CLASSES.button }}
onClick={(): void => { props.onRemoveButtonClicked?.(); }}
/>
icon='close'
stylingMode='text'
elementAttr={{ class: CLASSES.button }}
onClick={(): void => { props.onRemove?.(); }}
/>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class HeaderPanelView extends View {
columns={columns}
onReorder={this.onReorder.bind(this)}
onAdd={this.onAdd.bind(this)}
onHeaderRemoveButtonClicked={this.onHeaderCloseButtonClick.bind(this)}
onRemove={this.onRemove.bind(this)}
/>
),
[this.columnsController.visibleColumns],
Expand All @@ -26,7 +26,7 @@ export class HeaderPanelView extends View {
super();
}

public onHeaderCloseButtonClick(name: string): void {
public onRemove(name: string): void {
const index = this.getColumnIndexByName(name);

this.columnsController.columns.updateFunc((columns) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { Properties as PopupProperties } from '@js/ui/popup';
import dxPopup from '@js/ui/popup';
import { createPortal, type InfernoNode } from 'inferno';
import type { InfernoNode, RefObject } from 'inferno';
import { createPortal } from 'inferno';

import { wrapRef } from './utils';
import { InfernoWrapper } from './widget_wrapper';

export class Popup extends InfernoWrapper<PopupProperties, dxPopup> {
Expand All @@ -19,6 +21,37 @@ export class Popup extends InfernoWrapper<PopupProperties, dxPopup> {
);
}

private transformRef(props: PopupProperties): PopupProperties {
// @ts-expect-error
if (props?.position?.of?.current) {
// eslint-disable-next-line no-param-reassign
props = {
...props,
position: {
// @ts-expect-error
...props.position,
// @ts-expect-error
of: wrapRef(props.position.of),
},
};
}
return props;
}

protected createComponent(ref: RefObject<HTMLDivElement>, props: PopupProperties): dxPopup {
return super.createComponent(
ref,
this.transformRef(props),
);
}

protected updateComponentOptions(prevProps: PopupProperties, props: PopupProperties): void {
super.updateComponentOptions(
prevProps,
this.transformRef(props),
);
}

protected getComponentFabric(): typeof dxPopup {
return dxPopup;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { dxElementWrapper } from '@js/core/renderer';
import type { RefObject } from 'inferno';

export function wrapRef(ref: RefObject<HTMLElement>): dxElementWrapper {
return {
// @ts-expect-error
dxRenderer: true,
get 0() {
return ref.current!;
},
get() {
return ref.current!;
},
length: 1,
};
}
Loading

0 comments on commit 80581d6

Please sign in to comment.