mirror of
https://github.com/apache/superset.git
synced 2024-09-19 20:19:37 -04:00
feat: align metrics title to the right (#721)
This commit is contained in:
parent
ac1f518c09
commit
835335d3e4
@ -23,7 +23,6 @@ import {
|
|||||||
useSortBy,
|
useSortBy,
|
||||||
useGlobalFilter,
|
useGlobalFilter,
|
||||||
PluginHook,
|
PluginHook,
|
||||||
TableCellProps,
|
|
||||||
TableOptions,
|
TableOptions,
|
||||||
FilterType,
|
FilterType,
|
||||||
IdType,
|
IdType,
|
||||||
@ -34,7 +33,6 @@ import GlobalFilter, { GlobalFilterProps } from './components/GlobalFilter';
|
|||||||
import SelectPageSize, { SizeOption } from './components/SelectPageSize';
|
import SelectPageSize, { SizeOption } from './components/SelectPageSize';
|
||||||
import SimplePagination from './components/Pagination';
|
import SimplePagination from './components/Pagination';
|
||||||
import useSticky from './hooks/useSticky';
|
import useSticky from './hooks/useSticky';
|
||||||
import useColumnCellProps from './hooks/useColumnCellProps';
|
|
||||||
|
|
||||||
export interface DataTableProps<D extends object> extends TableOptions<D> {
|
export interface DataTableProps<D extends object> extends TableOptions<D> {
|
||||||
tableClassName?: string;
|
tableClassName?: string;
|
||||||
@ -76,7 +74,6 @@ export default function DataTable<D extends object>({
|
|||||||
useGlobalFilter,
|
useGlobalFilter,
|
||||||
useSortBy,
|
useSortBy,
|
||||||
usePagination,
|
usePagination,
|
||||||
useColumnCellProps,
|
|
||||||
doSticky ? useSticky : [],
|
doSticky ? useSticky : [],
|
||||||
hooks || [],
|
hooks || [],
|
||||||
].flat();
|
].flat();
|
||||||
@ -166,19 +163,10 @@ export default function DataTable<D extends object>({
|
|||||||
return (
|
return (
|
||||||
<tr key={headerGroupKey || headerGroup.id} {...headerGroupProps}>
|
<tr key={headerGroupKey || headerGroup.id} {...headerGroupProps}>
|
||||||
{headerGroup.headers.map(column => {
|
{headerGroup.headers.map(column => {
|
||||||
const { key: headerKey, className, ...props } = column.getHeaderProps(
|
return column.render('Header', {
|
||||||
column.getSortByToggleProps(),
|
key: column.id,
|
||||||
);
|
...column.getSortByToggleProps(),
|
||||||
return (
|
});
|
||||||
<th
|
|
||||||
key={headerKey || column.id}
|
|
||||||
className={column.isSorted ? `${className || ''} is-sorted` : className}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{column.render('Header')}
|
|
||||||
{column.render('SortIcon')}
|
|
||||||
</th>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@ -191,21 +179,7 @@ export default function DataTable<D extends object>({
|
|||||||
const { key: rowKey, ...rowProps } = row.getRowProps();
|
const { key: rowKey, ...rowProps } = row.getRowProps();
|
||||||
return (
|
return (
|
||||||
<tr key={rowKey || row.id} {...rowProps}>
|
<tr key={rowKey || row.id} {...rowProps}>
|
||||||
{row.cells.map(cell => {
|
{row.cells.map(cell => cell.render('Cell', { key: cell.column.id }))}
|
||||||
const cellProps = cell.getCellProps() as TableCellProps & RenderHTMLCellProps;
|
|
||||||
const { key: cellKey, cellContent, ...restProps } = cellProps;
|
|
||||||
const key = cellKey || cell.column.id;
|
|
||||||
if (cellProps.dangerouslySetInnerHTML) {
|
|
||||||
return <td key={key} {...restProps} />;
|
|
||||||
}
|
|
||||||
// If cellProps renderes textContent already, then we don't have to
|
|
||||||
// render `Cell`. This saves some time for large tables.
|
|
||||||
return (
|
|
||||||
<td key={key} {...restProps}>
|
|
||||||
{cellContent || cell.render('Cell')}
|
|
||||||
</td>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
/**
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
import { HTMLProps } from 'react';
|
|
||||||
import { Hooks, Cell, TableCellProps, ColumnInstance } from 'react-table';
|
|
||||||
|
|
||||||
export interface UseColumnCellPropsColumnOption<D extends object, V = unknown> {
|
|
||||||
cellProps?: (
|
|
||||||
cell: Cell<D, V>,
|
|
||||||
props: Partial<TableCellProps>,
|
|
||||||
) => Partial<HTMLProps<HTMLTableDataCellElement>> | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure cell props in column options.
|
|
||||||
*/
|
|
||||||
export default function useColumnCellProps<D extends object>(hooks: Hooks<D>) {
|
|
||||||
hooks.getCellProps.push((props, { cell }) => {
|
|
||||||
const column = cell.column as ColumnInstance<D>;
|
|
||||||
return (column.cellProps && column.cellProps(cell, props)) || [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
useColumnCellProps.pluginName = 'useColumnCellProps';
|
|
@ -16,7 +16,6 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
export * from './hooks/useColumnCellProps';
|
|
||||||
export * from './hooks/useSticky';
|
export * from './hooks/useSticky';
|
||||||
export * from './components/GlobalFilter';
|
export * from './components/GlobalFilter';
|
||||||
export * from './components/Pagination';
|
export * from './components/Pagination';
|
||||||
|
@ -19,10 +19,10 @@ import {
|
|||||||
UseSortByHooks,
|
UseSortByHooks,
|
||||||
TableInstance,
|
TableInstance,
|
||||||
ColumnInstance,
|
ColumnInstance,
|
||||||
Column,
|
Renderer,
|
||||||
|
HeaderProps,
|
||||||
} from 'react-table';
|
} from 'react-table';
|
||||||
|
|
||||||
import { UseColumnCellPropsColumnOption } from '../hooks/useColumnCellProps';
|
|
||||||
import { UseStickyState, UseStickyTableOptions, UseStickyInstanceProps } from '../hooks/useSticky';
|
import { UseStickyState, UseStickyTableOptions, UseStickyInstanceProps } from '../hooks/useSticky';
|
||||||
|
|
||||||
declare module 'react-table' {
|
declare module 'react-table' {
|
||||||
@ -53,13 +53,6 @@ declare module 'react-table' {
|
|||||||
UseSortByState<D>,
|
UseSortByState<D>,
|
||||||
UseStickyState {}
|
UseStickyState {}
|
||||||
|
|
||||||
export interface ColumnInterface<D extends object>
|
|
||||||
extends UseGlobalFiltersColumnOptions<D>,
|
|
||||||
UseSortByColumnOptions<D>,
|
|
||||||
UseColumnCellPropsColumnOption<D> {
|
|
||||||
SortIcon: Column['Header'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typing from @types/react-table is incomplete
|
// Typing from @types/react-table is incomplete
|
||||||
interface TableSortByToggleProps {
|
interface TableSortByToggleProps {
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
@ -67,10 +60,17 @@ declare module 'react-table' {
|
|||||||
onClick?: React.MouseEventHandler;
|
onClick?: React.MouseEventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ColumnInterface<D extends object>
|
||||||
|
extends UseGlobalFiltersColumnOptions<D>,
|
||||||
|
UseSortByColumnOptions<D> {
|
||||||
|
// must define as a new property because it's not possible to override
|
||||||
|
// the existing `Header` renderer option
|
||||||
|
Header?: Renderer<TableSortByToggleProps & HeaderProps<D>>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ColumnInstance<D extends object>
|
export interface ColumnInstance<D extends object>
|
||||||
extends UseGlobalFiltersColumnOptions<D>,
|
extends UseGlobalFiltersColumnOptions<D>,
|
||||||
UseSortByColumnProps<D>,
|
UseSortByColumnProps<D> {
|
||||||
UseColumnCellPropsColumnOption<D> {
|
|
||||||
getSortByToggleProps: (props?: Partial<TableSortByToggleProps>) => TableSortByToggleProps;
|
getSortByToggleProps: (props?: Partial<TableSortByToggleProps>) => TableSortByToggleProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import React, { useState, useMemo, useCallback } from 'react';
|
import React, { useState, useMemo, useCallback } from 'react';
|
||||||
import { ColumnInstance, Column, DefaultSortTypes } from 'react-table';
|
import { ColumnInstance, DefaultSortTypes, ColumnWithLooseAccessor } from 'react-table';
|
||||||
import { extent as d3Extent, max as d3Max } from 'd3-array';
|
import { extent as d3Extent, max as d3Max } from 'd3-array';
|
||||||
import { FaSort, FaSortUp as FaSortAsc, FaSortDown as FaSortDesc } from 'react-icons/fa';
|
import { FaSort, FaSortUp as FaSortAsc, FaSortDown as FaSortDesc } from 'react-icons/fa';
|
||||||
import { t } from '@superset-ui/translation';
|
import { t } from '@superset-ui/translation';
|
||||||
@ -83,7 +83,7 @@ function cellBar({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SortIcon({ column }: { column: ColumnInstance }) {
|
function SortIcon<D extends object>({ column }: { column: ColumnInstance<D> }) {
|
||||||
const { isSorted, isSortedDesc } = column;
|
const { isSorted, isSortedDesc } = column;
|
||||||
let sortIcon = <FaSort />;
|
let sortIcon = <FaSort />;
|
||||||
if (isSorted) {
|
if (isSorted) {
|
||||||
@ -173,53 +173,66 @@ export default function TableChart<D extends DataRecord = DataRecord>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const getColumnConfigs = useCallback(
|
const getColumnConfigs = useCallback(
|
||||||
(column: DataColumnMeta, i: number): Column<D> => {
|
(column: DataColumnMeta, i: number): ColumnWithLooseAccessor<D> => {
|
||||||
const { key, label, dataType } = column;
|
const { key, label, dataType } = column;
|
||||||
|
let className = '';
|
||||||
|
if (dataType === DataType.Number) {
|
||||||
|
className += ' dt-metric';
|
||||||
|
} else if (emitFilter) {
|
||||||
|
className += ' dt-is-filter';
|
||||||
|
}
|
||||||
const valueRange = showCellBars && getValueRange(key);
|
const valueRange = showCellBars && getValueRange(key);
|
||||||
const cellProps: Column<D>['cellProps'] = ({ value: value_ }, sharedCellProps) => {
|
|
||||||
let className = '';
|
|
||||||
const value = value_ as DataRecordValue;
|
|
||||||
if (dataType === DataType.Number) {
|
|
||||||
className += ' dt-metric';
|
|
||||||
} else if (emitFilter) {
|
|
||||||
className += ' dt-is-filter';
|
|
||||||
if (isActiveFilterValue(key, value)) {
|
|
||||||
className += ' dt-is-active-filter';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const [isHtml, text] = formatValue(column, value);
|
|
||||||
const style = {
|
|
||||||
...sharedCellProps.style,
|
|
||||||
background: valueRange
|
|
||||||
? cellBar({
|
|
||||||
value: value as number,
|
|
||||||
valueRange,
|
|
||||||
alignPositiveNegative,
|
|
||||||
colorPositiveNegative,
|
|
||||||
})
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
// show raw number in title in case of numeric values
|
|
||||||
title: typeof value === 'number' ? String(value) : undefined,
|
|
||||||
dangerouslySetInnerHTML: isHtml ? { __html: text } : undefined,
|
|
||||||
cellContent: text,
|
|
||||||
onClick: emitFilter && !valueRange ? () => toggleFilter(key, value) : undefined,
|
|
||||||
className,
|
|
||||||
style,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return {
|
return {
|
||||||
id: String(i), // to allow duplicate column keys
|
id: String(i), // to allow duplicate column keys
|
||||||
// must use custom accessor to allow `.` in column names
|
// must use custom accessor to allow `.` in column names
|
||||||
// typing is incorrect in current version of `@types/react-table`
|
// typing is incorrect in current version of `@types/react-table`
|
||||||
// so we ask TS not to check.
|
// so we ask TS not to check.
|
||||||
accessor: ((datum: D) => datum[key]) as never,
|
accessor: ((datum: D) => datum[key]) as never,
|
||||||
Header: label,
|
Cell: ({ column: col, value }: { column: ColumnInstance<D>; value: DataRecordValue }) => {
|
||||||
SortIcon,
|
const [isHtml, text] = formatValue(column, value);
|
||||||
|
const style = {
|
||||||
|
background: valueRange
|
||||||
|
? cellBar({
|
||||||
|
value: value as number,
|
||||||
|
valueRange,
|
||||||
|
alignPositiveNegative,
|
||||||
|
colorPositiveNegative,
|
||||||
|
})
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
const html = isHtml ? { __html: text } : undefined;
|
||||||
|
const cellProps = {
|
||||||
|
// show raw number in title in case of numeric values
|
||||||
|
title: typeof value === 'number' ? String(value) : undefined,
|
||||||
|
onClick: emitFilter && !valueRange ? () => toggleFilter(key, value) : undefined,
|
||||||
|
className: `${className}${
|
||||||
|
isActiveFilterValue(key, value) ? ' dt-is-active-filter' : ''
|
||||||
|
}`,
|
||||||
|
style,
|
||||||
|
};
|
||||||
|
if (html) {
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
|
return <td {...cellProps} dangerouslySetInnerHTML={html} />;
|
||||||
|
}
|
||||||
|
// If cellProps renderes textContent already, then we don't have to
|
||||||
|
// render `Cell`. This saves some time for large tables.
|
||||||
|
return <td {...cellProps}>{text}</td>;
|
||||||
|
},
|
||||||
|
Header: ({ column: col, title, onClick, style }) => {
|
||||||
|
return (
|
||||||
|
<th
|
||||||
|
title={title}
|
||||||
|
className={col.isSorted ? `${className || ''} is-sorted` : className}
|
||||||
|
style={style}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
<SortIcon column={col} />
|
||||||
|
</th>
|
||||||
|
);
|
||||||
|
},
|
||||||
sortDescFirst: sortDesc,
|
sortDescFirst: sortDesc,
|
||||||
sortType: getSortTypeByDataType(dataType),
|
sortType: getSortTypeByDataType(dataType),
|
||||||
cellProps,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
Loading…
Reference in New Issue
Block a user