未验证 提交 03f31128 编写于 作者: P Peter Pan 提交者: GitHub

minor improvements of hyper-parameter page (#964)

* fix: text overflow ellipsis in hyper-parameter page

* fix: minor bugs fix

* fix: minor bugs fix

* fix: error when high-dimensional list is empty
上级 d946813b
......@@ -3,6 +3,7 @@
"hparams": "Hyper Parameters",
"metrics": "Metrics"
},
"empty": "<0>No Hparams Data was Found.</0><1>You can try the following solutions:</1><2><0>make sure that you have used `add_scalar` to record the metrics.</0><0>make sure that the `metrics_list` of `add_hparams` includes the `tag` of `add_scalar`.</0></2><3>For detailed tutorials, please refer to the <1>VisualDL Hparams Instructions</1>.</3>",
"hyper-parameter": {
"order-default": "Default"
},
......
......@@ -3,6 +3,7 @@
"hparams": "超参数",
"metrics": "度量指标"
},
"empty": "<0>无超参可视化结果展示</0><1>参考以下几种解决方案:</1><2><0>请确保您已使用 `add_scalar` 接口记录所需展示的模型度量指标(metrics)。</0><0>请确保 `add_hparams` 接口的 `metrics_list` 参数中包含 `add_scalar` 接口的 `tag` 参数。</0></2><3>您可以参考<1>Hparams使用文档</1>获取详细教程。</3>",
"hyper-parameter": {
"order-default": "默认"
},
......
......@@ -40,7 +40,7 @@ const Wrapper = styled.div`
}
> .inner {
width: calc(50% - ${rem(280)});
width: max(calc(50% - ${rem(280)}), ${rem(280)});
color: var(--text-light-color);
${transitionProps('color')}
${link}
......@@ -56,7 +56,8 @@ const Wrapper = styled.div`
margin: 0;
}
ol {
ol,
ul {
padding-left: 2em;
line-height: 1.857142857;
}
......@@ -116,7 +117,7 @@ const Error: FunctionComponent<WithStyled> = ({className, children}) => {
</li>
<li>
<Trans i18nKey="errors:common.3">
Log files are generated and data is writte. Please try to&nbsp;
Log files are generated and data is written. Please try to&nbsp;
<a onClick={reload}>Refresh</a>.
</Trans>
</li>
......
......@@ -195,6 +195,9 @@ const HistogramChart: FunctionComponent<HistogramChartProps> = ({run, tag, mode,
xAxis: {
axisPointer: {
snap: mode === Modes.Overlay
},
splitLine: {
show: false
}
},
yAxis: {
......
/**
* Copyright 2020 Baidu Inc. All Rights Reserved.
*
* Licensed 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 React, {FunctionComponent} from 'react';
import {Trans, useTranslation} from 'react-i18next';
import Error from '~/components/Error';
const DocumentMap: Record<string, string> = {
zh: 'https://github.com/PaddlePaddle/VisualDL/blob/develop/docs/components/README_CN.md#Hparams--超参可视化',
en: 'https://github.com/PaddlePaddle/VisualDL/tree/develop/docs/components#Hparams--HyperParameters-Visualization'
};
const Empty: FunctionComponent = () => {
const {i18n} = useTranslation('hyper-parameter');
return (
<Error>
<Trans i18nKey="hyper-parameter:empty">
<h4>No Hparams Data was Found.</h4>
<p>You can try the following solutions:</p>
<ul>
<li>make sure that you have used `add_scalar` to record the metrics.</li>
<li>make sure that the `metrics_list` of `add_hparams` includes the `tag` of `add_scalar`.</li>
</ul>
<p>
For detailed tutorials, please refer to the&nbsp;
<a
href={DocumentMap[i18n.language || String(i18n.options.fallbackLng)] ?? DocumentMap.en}
target="_blank"
rel="noreferrer"
>
VisualDL Hparams Instructions
</a>
.
</p>
</Trans>
</Error>
);
};
export default Empty;
......@@ -18,6 +18,7 @@ import React, {FunctionComponent, useCallback, useEffect, useRef, useState} from
import NumberInput from './NumberInput';
import type {Range} from '~/resource/hyper-parameter';
import {rem} from '~/utils/style';
import styled from 'styled-components';
import {useTranslation} from 'react-i18next';
......@@ -37,6 +38,8 @@ const Row = styled.div`
flex-grow: 0;
flex-shrink: 0;
margin-right: 1em;
text-align: right;
min-width: ${rem(36)};
}
`;
......
......@@ -17,9 +17,15 @@
import React, {FunctionComponent} from 'react';
import type {CellProps} from 'react-table';
import {ellipsis} from '~/utils/style';
import styled from 'styled-components';
const Span = styled.span`
${ellipsis()}
`;
const Cell = <D extends Record<string, unknown>>({cell}: CellProps<D>): ReturnType<FunctionComponent> => {
return <span>{cell.value}</span>;
return <Span title={cell.value}>{cell.value}</Span>;
};
export default Cell;
......@@ -19,6 +19,12 @@ import React, {FunctionComponent} from 'react';
import {Resizer} from '~/components/Table';
import type {HeaderProps as TableHeaderProps} from 'react-table';
import type {WithStyled} from '~/utils/style';
import {ellipsis} from '~/utils/style';
import styled from 'styled-components';
const Span = styled.span`
${ellipsis()}
`;
const Header: FunctionComponent<TableHeaderProps<Record<string, unknown>> & WithStyled> = ({
column,
......@@ -27,7 +33,9 @@ const Header: FunctionComponent<TableHeaderProps<Record<string, unknown>> & With
}) => {
return (
<>
<span className={className}>{children ?? column.id}</span>
<Span className={className} title={children ? ('string' === typeof children ? children : '') : column.id}>
{children ?? column.id}
</Span>
{column.canResize ? (
<Resizer {...column.getResizerProps()} className={column.isResizing ? 'is-resizing' : ''} />
) : null}
......
......@@ -18,11 +18,22 @@ import React, {FunctionComponent} from 'react';
import type {CellProps} from 'react-table';
import {Expander} from '~/components/Table';
import {ellipsis} from '~/utils/style';
import styled from 'styled-components';
const Cell = styled.span`
display: inline-flex;
align-items: center;
max-width: 100%;
> ${Expander} {
flex: none;
}
> .cell {
flex: auto;
${ellipsis()}
}
`;
const ExpandableCell = <D extends Record<string, unknown>>({
......@@ -32,7 +43,9 @@ const ExpandableCell = <D extends Record<string, unknown>>({
return (
<Cell>
<Expander {...row.getToggleRowExpandedProps()} isExpanded={row.isExpanded} />
<span>{cell.value}</span>
<span className="cell" title={cell.value}>
{cell.value}
</span>
</Cell>
);
};
......
......@@ -37,6 +37,8 @@ import {useSticky} from 'react-table-sticky';
type TableViewTableProps = ViewData & {
sortBy?: SortingRule<string>[];
columnOrder?: string[];
onOrderChange?: (order: string[]) => unknown;
expand?: boolean;
expandAll?: boolean;
};
......@@ -46,6 +48,8 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
indicators,
list: data,
sortBy,
columnOrder,
onOrderChange,
expand,
expandAll
}) => {
......@@ -73,12 +77,15 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
accessor: `${group}.${name}` as IndicatorGroup, // fix react-table's type error
id: name,
Header: group === 'metrics' ? MetricsHeader : Header,
Cell,
minWidth: 200
}))
],
[expand, indicators]
);
const order = useMemo(() => (columnOrder ? ['name', ...columnOrder] : []), [columnOrder]);
const {
getTableProps,
headerGroups,
......@@ -96,7 +103,8 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
data,
defaultColumn,
initialState: {
sortBy: sortBy ?? []
sortBy: sortBy ?? [],
columnOrder: order
},
autoResetExpanded: false
},
......@@ -127,6 +135,9 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
() => (state.columnOrder.length ? state.columnOrder : tableColumns.map(c => c.id)),
[state.columnOrder, tableColumns]
);
useEffect(() => {
onOrderChange?.(orderedColumnIds.filter(id => id !== 'name'));
}, [onOrderChange, orderedColumnIds]);
const droppableColumnId = useMemo(() => {
if (draggingColumnId != null && droppableColumn != null) {
const [id, side] = droppableColumn;
......@@ -144,16 +155,16 @@ const TableViewTable: FunctionComponent<TableViewTableProps> = ({
draggingColumnId != null &&
droppableColumn &&
droppableColumn[1] === 'before' &&
tableColumns[0]?.id === droppableColumn[0],
[draggingColumnId, droppableColumn, tableColumns]
orderedColumnIds[0] === droppableColumn[0],
[draggingColumnId, droppableColumn, orderedColumnIds]
);
const isTableDroppableRight = useMemo(
() =>
draggingColumnId != null &&
droppableColumn &&
droppableColumn[1] === 'after' &&
tableColumns[tableColumns.length - 1]?.id === droppableColumn[0],
[draggingColumnId, droppableColumn, tableColumns]
orderedColumnIds[orderedColumnIds.length - 1] === droppableColumn[0],
[draggingColumnId, droppableColumn, orderedColumnIds]
);
const drop = useCallback(
(id: string, side: 'before' | 'after') => {
......
......@@ -15,16 +15,19 @@
*/
import {DEFAULT_ORDER_INDICATOR, OrderDirection} from '~/resource/hyper-parameter';
import React, {FunctionComponent, useMemo, useState} from 'react';
import React, {FunctionComponent, useCallback, useMemo, useState} from 'react';
import Select from '~/components/Select';
import Table from './Table';
import View from '~/components/HyperParameterPage/View';
import type {ViewData} from '~/resource/hyper-parameter';
import {rem} from '~/utils/style';
import {safeSplit} from '~/utils';
import styled from 'styled-components';
import {useTranslation} from 'react-i18next';
const TABLE_ORDER_STORAGE_KEY = 'hyper-parameter-table-view-table-order';
const Wrapper = styled(View)`
display: flex;
width: 100%;
......@@ -91,6 +94,18 @@ const TableView: FunctionComponent<TableViewProps> = ({indicators, list, data})
[orderDirection, indicatorOrder]
);
const [columnOrder, setColumnOrder] = useState<string[]>(
safeSplit(window.sessionStorage.getItem(TABLE_ORDER_STORAGE_KEY) ?? '', ',')
);
const changeOrder = useCallback(
(order: string[]) => {
const filterOrder = order.filter(o => indicatorNameList.includes(o));
setColumnOrder(filterOrder);
window.sessionStorage.setItem(TABLE_ORDER_STORAGE_KEY, filterOrder.join(','));
},
[indicatorNameList]
);
return (
<Wrapper>
<OrderSection>
......@@ -114,7 +129,15 @@ const TableView: FunctionComponent<TableViewProps> = ({indicators, list, data})
) : null}
</OrderSection>
<TableSection>
<Table indicators={indicators} list={list} data={data} sortBy={sortBy} expand />
<Table
indicators={indicators}
list={list}
data={data}
sortBy={sortBy}
expand
columnOrder={columnOrder}
onOrderChange={changeOrder}
/>
</TableSection>
</Wrapper>
);
......
......@@ -216,10 +216,6 @@ const Select = <T extends unknown>({
[multiple, propValue]
);
const isSelected = useMemo(
() => !!(multiple ? (value as T[]) && (value as T[]).length !== 0 : value != (null as T)),
[multiple, value]
);
const changeValue = useCallback(
({value: mutateValue, disabled}: SelectListItem<T>) => {
if (disabled) {
......@@ -265,6 +261,14 @@ const Select = <T extends unknown>({
);
const isListEmpty = useMemo(() => list.length === 0, [list]);
const isSelected = useMemo(
() =>
!!(multiple
? (value as T[]) && (value as T[]).length !== 0
: !(value == (null as T) || list.findIndex(i => i.value === value) === -1)),
[list, multiple, value]
);
const findLabelByValue = useCallback((v: T) => list.find(item => item.value === v)?.label ?? '', [list]);
const label = useMemo(
() =>
......
......@@ -311,6 +311,9 @@ const HighDimensional: FunctionComponent = () => {
setMetadata(data.metadata);
} else if (data !== null) {
setLoadingPhase('parsing');
} else {
setLoading(false);
setLoadingPhase('');
}
}, [result, showError]);
const hasVector = useMemo(() => dim !== 0, [dim]);
......
......@@ -23,8 +23,9 @@ import {filter, format, formatIndicators} from '~/resource/hyper-parameter';
import BodyLoading from '~/components/BodyLoading';
import Button from '~/components/Button';
import Content from '~/components/Content';
import Empty from '~/components/HyperParameterPage/Empty';
import Field from '~/components/Field';
import ImportanceDialog from '~/components/HyperParameterPage/ImportanceDialog';
// import ImportanceDialog from '~/components/HyperParameterPage/ImportanceDialog';
import IndicatorFilter from '~/components/HyperParameterPage/IndicatorFilter/IndicatorFilter';
import ParallelCoordinatesView from '~/components/HyperParameterPage/ParallelCoordinatesView';
import ScatterPlotMatrixView from '~/components/HyperParameterPage/ScatterPlotMatrixView';
......@@ -41,11 +42,13 @@ import {useTranslation} from 'react-i18next';
// width: 100%;
// `;
const HParamsImportanceDialog = styled(ImportanceDialog)`
position: fixed;
right: calc(${asideWidth} + ${rem(20)});
bottom: ${rem(20)};
`;
// const HParamsImportanceDialog = styled(ImportanceDialog)`
// position: fixed;
// right: calc(${asideWidth} + ${rem(20)});
// bottom: ${rem(20)};
// `;
// NOTICE: remove it!!!
asideWidth;
const DownloadButtons = styled.div`
display: flex;
......@@ -79,6 +82,14 @@ const HyperParameter: FunctionComponent = () => {
const {t} = useTranslation(['hyper-parameter', 'common']);
const {data: indicatorsData, loading: loadingIndicators} = useRequest<IndicatorData>('/hparams/indicators');
const isEmpty = useMemo(() => {
if (indicatorsData) {
const {hparams, metrics} = indicatorsData;
return hparams.length === 0 || metrics.length === 0;
}
return true;
}, [indicatorsData]);
const indicators = useMemo(
() => [
...formatIndicators(indicatorsData?.hparams ?? [], 'hparams'),
......@@ -128,7 +139,7 @@ const HyperParameter: FunctionComponent = () => {
}
}, [tabView, viewData]);
const [importanceDialogVisible, setImportanceDialogVisible] = useState(false);
// const [importanceDialogVisible, setImportanceDialogVisible] = useState(false);
const downloadData = useCallback(
(type: 'tsv' | 'csv') =>
......@@ -188,11 +199,11 @@ const HyperParameter: FunctionComponent = () => {
<HPWrapper>
<Tab list={tabs} value={tabView} onChange={setTabView} />
<ViewWrapper>
{view}
<HParamsImportanceDialog
{isEmpty ? <Empty /> : view}
{/* <HParamsImportanceDialog
visible={importanceDialogVisible}
onClickClose={() => setImportanceDialogVisible(false)}
/>
/> */}
</ViewWrapper>
</HPWrapper>
</Content>
......
......@@ -183,6 +183,10 @@ export const GlobalStyle = createGlobalStyle`
${transitionProps(['background-color', 'color'])}
}
body {
overflow-anchor: none;
}
a {
text-decoration: none;
color: inherit;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册