Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,39 @@ exports[`common initial render should be successfull 1`] = `
class="dx-widget dx-cardview"
>

<div
class="dx-cardview-headers"
>
<div
class="dx-scrollable dx-visibility-change-handler dx-scrollable-horizontal dx-scrollable-simulated"
>
<div
class="dx-scrollable-wrapper"
>
<div
class="dx-scrollable-container"
tabindex="0"
>
<div
class="dx-scrollable-content"
style="left: 0px; top: 0px; transform: none;"
/>
<div
class="dx-scrollable-scrollbar dx-widget dx-scrollbar-horizontal"
style="display: none;"
>
<div
class="dx-scrollable-scroll dx-state-invisible"
>
<div
class="dx-scrollable-scroll-content"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div>

</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import $ from '@js/core/renderer';
import type { ComponentType, InfernoNode } from 'inferno';
import { Component, render } from 'inferno';

import type { Column } from '../../grid_core/columns_controller/types';
import type { Props as SortableProps } from '../../grid_core/inferno_wrappers/sortable';
import { Sortable } from '../../grid_core/inferno_wrappers/sortable';

export type Status = 'forbid' | 'show' | 'moving' | 'none';

export interface Props extends Omit<SortableProps, 'onAdd' | 'onReorder' | 'dragTemplate'> {
source: string;

visibleColumns: Column[];

allowColumnReordering: boolean;

onMove: (column: Column, toIndex: number, source: string) => void;

dragTemplate?: ComponentType<{ column: Column; status: Status }>;
}

interface State {
status: Status;
}

export class ColumnSortable extends Component<Props, State> {
status: Status = 'moving';

dragItemProps?: {
container: HTMLElement;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
props: any;
};

private readonly onDragStart = (e): void => {
Comment thread
pomahtri marked this conversation as resolved.
Outdated
const column = this.props.visibleColumns[e.fromIndex];

if (!column.allowReordering) {
e.cancel = true;
return;
}

e.itemData = {
column,
source: this.props.source,
};
};

private readonly onDragMove = (e): void => {
const containerCoords = $(e.element).get(0).getBoundingClientRect();
const dragCoords = {
x: e.event.clientX,
y: e.event.clientY,
};

const yDistance = Math.min(
Math.abs(dragCoords.y - containerCoords.y),
Math.abs(dragCoords.y - containerCoords.y + containerCoords.height),
);

if (yDistance <= 64) {
Comment thread
pomahtri marked this conversation as resolved.
Outdated
this.status = 'moving';
} else {
this.status = 'forbid';
}

this.renderDragTemplate();
};

private readonly onDragChange = (e): void => {
if (this.status === 'forbid') {
e.cancel = true;
}
};

private readonly onMove = (e): void => {
this.props.onMove(
e.itemData.column,
e.toIndex,
e.itemData.source,
);
};

// TODO: move all none-native approaches to sortable wrapper
private readonly renderDragTemplate = (): void => {
if (this.dragItemProps) {
Comment thread
pomahtri marked this conversation as resolved.
Outdated
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const DragTemplate = this.props.dragTemplate!;
render(
// @ts-expect-error
<DragTemplate
column={this.dragItemProps.props.itemData.column}
status={this.status}
/>,
this.dragItemProps.container,
);
}
};

render(): InfernoNode {
if (!this.props.allowColumnReordering) {
// @ts-expect-error
return this.props.children;
}

const {
source,
visibleColumns,
dragTemplate,
...restProps
} = this.props;

const sortableDragTemplate = dragTemplate ? (e, container): void => {
this.dragItemProps = {
props: e,
// @ts-expect-error
container: $(container).get(0),
};
this.renderDragTemplate();
} : undefined;

return (
<Sortable
{...restProps}
dropFeedbackMode='indicate'
onDragStart={this.onDragStart}
group='dx-cardview-columns'
onAdd={this.onMove}
onReorder={this.onMove}
onDragMove={this.onDragMove}
onDragChange={this.onDragChange}
dragTemplate={sortableDragTemplate}
>
{this.props.children}
</Sortable>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { Column } from '@ts/grids/new/grid_core/columns_controller/types';
import { Scrollable } from '@ts/grids/new/grid_core/inferno_wrappers/scrollable';
import { Component } from 'inferno';

import { ColumnSortable } from './column_sortable';
import { CLASSES as itemClasses, Item } from './item';

export const CLASSES = {
headers: 'dx-cardview-headers',
content: 'dx-cardview-headers-content',
};

export interface HeaderPanelProps {
columns: Column[];

onMove: (column: Column, toIndex: number) => void;

allowColumnReordering: boolean;
}

export class HeaderPanel extends Component<HeaderPanelProps> {
public render(): JSX.Element {
return (
<div className={CLASSES.headers}>
<ColumnSortable
allowColumnReordering={this.props.allowColumnReordering}
source="header-panel-main"
visibleColumns={this.props.columns}
itemOrientation="horizontal"
onMove={(column, index): void => this.props.onMove?.(column, index)}
filter={`.${itemClasses.item}`}
dragTemplate={Item}
>
<Scrollable
direction='horizontal'
showScrollbar='never'
useNative={false}
scrollByContent={true}
>
<div className={CLASSES.content}>
{this.props.columns.map((column) => (
<Item
column={column}
/>
))}
</div>
</Scrollable>
</ColumnSortable>
</div>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './options';
export { HeaderPanelView as View } from './view';
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Column } from '@ts/grids/new/grid_core/columns_controller/types';
import type { ComponentType } from 'inferno';

import type { Status } from './column_sortable';

export const CLASSES = {
item: 'dx-cardview-header-item',
button: 'dx-cardview-header-item-button',
};

export interface HeaderItemProps {
column: Pick<Column, 'caption' | 'visible'>;
buttons?: ComponentType;

status?: Status;
}

const ICONS = {
// TODO: move to icons
forbid: (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8ZM15 8C15 6.24696 14.3556 4.64442 13.2907 3.41636L3.41636 13.2907C4.64442 14.3556 6.24696 15 8 15C11.866 15 15 11.866 15 8ZM2.70925 12.5836L12.5836 2.70925C11.3556 1.6444 9.75303 1 8 1C4.13401 1 1 4.13401 1 8C1 9.75303 1.6444 11.3556 2.70925 12.5836Z" fill="#242424"/>
</svg>
),
moving: (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.35355 0.146447C8.25978 0.0526784 8.13261 0 8 0C7.86739 0 7.74021 0.0526784 7.64645 0.146447L5.14645 2.64645C4.95118 2.84171 4.95118 3.15829 5.14645 3.35355C5.34171 3.54882 5.65829 3.54882 5.85355 3.35355L7.5 1.70711V5.5C7.5 5.77614 7.72386 6 8 6C8.27614 6 8.5 5.77614 8.5 5.5V1.70711L10.1464 3.35355C10.3417 3.54882 10.6583 3.54882 10.8536 3.35355C11.0488 3.15829 11.0488 2.84171 10.8536 2.64645L8.35355 0.146447ZM0.146447 7.64645C0.0526784 7.74021 0 7.86739 0 8C0 8.13261 0.0526784 8.25979 0.146447 8.35355L2.64645 10.8536C2.84171 11.0488 3.15829 11.0488 3.35355 10.8536C3.54882 10.6583 3.54882 10.3417 3.35355 10.1464L1.70711 8.5H5.5C5.77614 8.5 6 8.27614 6 8C6 7.72386 5.77614 7.5 5.5 7.5H1.70711L3.35355 5.85355C3.54882 5.65829 3.54882 5.34171 3.35355 5.14645C3.15829 4.95118 2.84171 4.95118 2.64645 5.14645L0.146447 7.64645ZM8 16C7.86739 16 7.74021 15.9473 7.64645 15.8536L5.14645 13.3536C4.95118 13.1583 4.95118 12.8417 5.14645 12.6464C5.34171 12.4512 5.65829 12.4512 5.85355 12.6464L7.5 14.2929L7.5 10.5C7.5 10.2239 7.72386 10 8 10C8.27614 10 8.5 10.2239 8.5 10.5V14.2929L10.1464 12.6464C10.3417 12.4512 10.6583 12.4512 10.8536 12.6464C11.0488 12.8417 11.0488 13.1583 10.8536 13.3536L8.35355 15.8536C8.25979 15.9473 8.13261 16 8 16ZM15.8536 8.35355C15.9473 8.25978 16 8.13261 16 8C16 7.86739 15.9473 7.74021 15.8536 7.64645L13.3536 5.14645C13.1583 4.95118 12.8417 4.95118 12.6464 5.14645C12.4512 5.34171 12.4512 5.65829 12.6464 5.85355L14.2929 7.5L10.5 7.5C10.2239 7.5 10 7.72386 10 8C10 8.27614 10.2239 8.5 10.5 8.5L14.2929 8.5L12.6464 10.1464C12.4512 10.3417 12.4512 10.6583 12.6464 10.8536C12.8417 11.0488 13.1583 11.0488 13.3536 10.8536L15.8536 8.35355Z" fill="#242424"/>
</svg>
),
};

export function Item(props: HeaderItemProps): JSX.Element {
return (
<div className={CLASSES.item} tabIndex={0}>
{ props.status && ICONS[props.status]}
{props.column.caption}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* eslint-disable @typescript-eslint/ban-types */
import type * as Sortable from '@js/ui/sortable';

import type { Template } from '../../grid_core/types';

type SortableProperties = 'dropFeedbackMode' | 'scrollSpeed' | 'scrollSensitivity' | 'onDragChange' | 'onDragEnd' | 'onDragMove' | 'onDragStart' | 'onRemove' | 'onReorder';

export interface Options {
headerPanel?: Pick<Sortable.Properties, SortableProperties>;
}

export const defaultOptions = {
} satisfies Options;

export interface ColumnOptions {
headerItemTemplate?: Template<{}>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* eslint-disable spellcheck/spell-checker */
import type { SubsGets } from '@ts/core/reactive/index';
import { combined, computed } from '@ts/core/reactive/index';
import { ColumnsController } from '@ts/grids/new/grid_core/columns_controller/columns_controller';
import { View } from '@ts/grids/new/grid_core/core/view';

import type { Column } from '../../grid_core/columns_controller/types';
import type { HeaderPanelProps } from './header_panel';
import { HeaderPanel } from './header_panel';

export class HeaderPanelView extends View<HeaderPanelProps> {
// @ts-expect-error
protected component = HeaderPanel;

public static dependencies = [ColumnsController] as const;

constructor(
private readonly columnsController: ColumnsController,
) {
super();
}

protected override getProps(): SubsGets<HeaderPanelProps> {
return combined({
columns: computed(
(columns) => [...columns].sort((a, b) => a.visibleIndex - b.visibleIndex),
[this.columnsController.columns],
),
onMove: this.onMove.bind(this),
onRemove: this.onRemove.bind(this),
allowColumnReordering: this.columnsController.allowColumnReordering,
});
}

public onRemove(column: Column): void {
this.columnsController.columnOption(column, 'visible', !column.visible);
}

public onMove(column: Column, toIndex: number): void {
this.columnsController.columnOption(column, 'visible', true);
this.columnsController.columnOption(column, 'visibleIndex', toIndex);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,24 @@ import { PagerView } from '@ts/grids/new/grid_core/pager/view';
import { ToolbarView } from '@ts/grids/new/grid_core/toolbar/view';
import type { ComponentType } from 'inferno';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
import { HeaderPanelView } from './header_panel/view';

interface MainViewProps {
Toolbar: ComponentType;
Pager: ComponentType;
HeaderPanel: ComponentType;
}

function MainViewComponent({
Toolbar, Pager,
Toolbar, Pager, HeaderPanel,
}: MainViewProps): JSX.Element {
return (<>
{/* @ts-expect-error */}
<Toolbar/>
{/* @ts-expect-error */}
<HeaderPanel/>
<div>
{/*
TODO:
Pager, as renovated component, has strange disposing.
See `inferno_renderer.remove` method.
It somehow mutates $V prop of parent element.
Expand All @@ -37,12 +40,13 @@ export class MainView extends View<MainViewProps> {
protected override component = MainViewComponent;

public static dependencies = [
PagerView, ToolbarView,
PagerView, ToolbarView, HeaderPanelView,
] as const;

constructor(
private readonly pager: PagerView,
private readonly toolbar: ToolbarView,
private readonly headerPanel: HeaderPanelView,
) {
super();
}
Expand All @@ -53,6 +57,7 @@ export class MainView extends View<MainViewProps> {
return combined({
Toolbar: this.toolbar.asInferno(),
Pager: this.pager.asInferno(),
HeaderPanel: this.headerPanel.asInferno(),
});
}
}
Loading