Commit b40e3db7 authored by quzha's avatar quzha
Browse files

Merge branch 'master' of github.com:Microsoft/nni into dev-retiarii

parents efa4e31c 95f731e4
......@@ -6,24 +6,29 @@ export const Command1 = (): any => {
const tuner = EXPERIMENT.profile.params.tuner;
const advisor = EXPERIMENT.profile.params.advisor;
const assessor = EXPERIMENT.profile.params.assessor;
let title = '';
let builtinName = '';
const title: string[] = [];
const builtinName: string[] = [];
if (tuner !== undefined) {
title = title.concat('Tuner');
title.push('Tuner');
if (tuner.builtinTunerName !== undefined) {
builtinName = builtinName.concat(tuner.builtinTunerName);
builtinName.push(tuner.builtinTunerName);
}
}
if (advisor !== undefined) {
title = title.concat('/ Assessor');
title.push('Advisor');
if (advisor.builtinAdvisorName !== undefined) {
builtinName = builtinName.concat(advisor.builtinAdvisorName);
builtinName.push(advisor.builtinAdvisorName);
}
if (advisor.className !== undefined) {
builtinName.push(advisor.className);
}
}
if (assessor !== undefined) {
title = title.concat('/ Addvisor');
title.push('Assessor');
if (assessor.builtinAssessorName !== undefined) {
builtinName = builtinName.concat(assessor.builtinAssessorName);
builtinName.push(assessor.builtinAssessorName);
}
}
......@@ -32,8 +37,8 @@ export const Command1 = (): any => {
<div>
<p className='command'>Training platform</p>
<div className='nowrap'>{EXPERIMENT.profile.params.trainingServicePlatform}</div>
<p className='lineMargin'>{title}</p>
<div className='nowrap'>{builtinName}</div>
<p className='lineMargin'>{title.join('/')}</p>
<div className='nowrap'>{builtinName.join('/')}</div>
</div>
</div>
);
......
......@@ -188,16 +188,16 @@ export const EditExperimentParam = (): any => {
/>
)}
{isShowPencil && (
<span className='edit' onClick={hidePencil}>
<span className='edit cursor' onClick={hidePencil}>
{Edit}
</span>
)}
{!isShowPencil && (
<span className='series'>
<span className='confirm' onClick={confirmEdit}>
<span className='confirm cursor' onClick={confirmEdit}>
{CheckMark}
</span>
<span className='cancel' onClick={cancelEdit}>
<span className='cancel cursor' onClick={cancelEdit}>
{Cancel}
</span>
</span>
......
......@@ -6,7 +6,7 @@ import { convertDuration, convertTimeAsUnit } from '../../../static/function';
import { EditExperimentParam } from './EditExperimentParam';
import { ExpDurationContext } from './ExpDurationContext';
import { EditExpeParamContext } from './context';
import { durationItem1, durationItem2 } from './commonStyle';
import { leftProgress, durationItem2, progressHeight } from './commonStyle';
import '../../../static/style/overview/count.scss';
export const ExpDuration = (): any => (
......@@ -19,7 +19,7 @@ export const ExpDuration = (): any => (
const maxExecDurationStr = convertTimeAsUnit(maxDurationUnit, maxExecDuration).toString();
return (
<Stack horizontal className='ExpDuration'>
<div style={durationItem1}>
<div style={leftProgress}>
<TooltipHost
content={`${convertDuration(tooltip)} remaining`}
directionalHint={DirectionalHint.bottomCenter}
......@@ -33,7 +33,11 @@ export const ExpDuration = (): any => (
}
}}
>
<ProgressIndicator className={EXPERIMENT.status} percentComplete={percent} barHeight={15} />
<ProgressIndicator
className={EXPERIMENT.status}
percentComplete={percent}
barHeight={progressHeight}
/>
</TooltipHost>
{/* execDuration / maxDuration: 20min / 1h */}
<div className='exp-progress'>
......
......@@ -5,7 +5,7 @@ import { CONTROLTYPE, TOOLTIP_BACKGROUND_COLOR, MAX_TRIAL_NUMBERS } from '../../
import { EditExperimentParam } from './EditExperimentParam';
import { EditExpeParamContext } from './context';
import { ExpDurationContext } from './ExpDurationContext';
import { trialCountItem1, trialCountItem2 } from './commonStyle';
import { leftProgress, trialCountItem2, progressHeight } from './commonStyle';
export const TrialCount = (): any => {
const count = TRIALS.countStatus();
......@@ -23,9 +23,9 @@ export const TrialCount = (): any => {
return (
<React.Fragment>
<Stack horizontal horizontalAlign='space-between' className='ExpDuration'>
<div style={trialCountItem1}>
<div style={leftProgress}>
<TooltipHost
content={bar2.toString()}
content={`${bar2.toString()} trials`}
directionalHint={DirectionalHint.bottomCenter}
tooltipProps={{
calloutProps: {
......@@ -40,7 +40,7 @@ export const TrialCount = (): any => {
<ProgressIndicator
className={EXPERIMENT.status}
percentComplete={bar2Percent}
barHeight={15}
barHeight={progressHeight}
/>
</TooltipHost>
<div className='exp-progress'>
......@@ -81,7 +81,7 @@ export const TrialCount = (): any => {
</EditExpeParamContext.Provider>
</div>
</Stack>
<Stack horizontal horizontalAlign='space-between' className='mess'>
<Stack horizontal horizontalAlign='space-between' className='trialStatus'>
<div className='basic'>
<p>Running</p>
<div>{count.get('RUNNING')}</div>
......
const durationItem1: React.CSSProperties = {
width: '33%'
const leftProgress: React.CSSProperties = {
width: '33%',
position: 'relative',
top: 6
};
const durationItem2: React.CSSProperties = {
width: '52%',
width: '51.5%',
paddingLeft: '15%'
};
const trialCountItem1: React.CSSProperties = {
width: '33%'
};
const trialCountItem2: React.CSSProperties = {
width: '52%'
width: '51.5%'
};
export { durationItem1, durationItem2, trialCountItem1, trialCountItem2 };
const progressHeight = 8;
export { leftProgress, durationItem2, trialCountItem2, progressHeight };
......@@ -23,11 +23,12 @@ export const ReBasicInfo = (): any => {
return (
<div>
<Stack horizontal horizontalAlign='space-between' className='mess'>
<div className='basic'>
<p>
ID: <span>{EXPERIMENT.profile.id}</span>
</p>
<div>{EXPERIMENT.profile.params.experimentName}</div>
<p>Name</p>
<div className='nowrap'>{EXPERIMENT.profile.params.experimentName}</div>
<p className='margin'>ID</p>
<div className='nowrap'>{EXPERIMENT.profile.id}</div>
</div>
<div className='basic'>
<p>Status</p>
......@@ -53,12 +54,12 @@ export const ReBasicInfo = (): any => {
setInitialFocus={true}
>
<div className={styles.header}>
<p className={styles.title} id={labelId} style={{ color: '#333' }}>
<p className={`${styles.title} color`} id={labelId}>
Error
</p>
</div>
<div className={styles.inner}>
<p className={styles.subtext} id={descriptionId} style={{ color: '#333' }}>
<p className={`${styles.subtext} color`} id={descriptionId}>
{EXPERIMENT.error}
</p>
<div className={styles.actions}>
......@@ -72,12 +73,10 @@ export const ReBasicInfo = (): any => {
</div>
) : null}
</Stack>
</div>
<div className='basic'>
<BestMetricContext.Consumer>
{(value): React.ReactNode => (
<Stack className='bestMetric'>
<p>Best metric</p>
<p className='margin'>Best metric</p>
<div className={EXPERIMENT.status}>
{isNaN(value.bestAccuracy) ? 'N/A' : value.bestAccuracy.toFixed(6)}
</div>
......@@ -88,11 +87,10 @@ export const ReBasicInfo = (): any => {
<div className='basic'>
<p>Start time</p>
<div className='nowrap'>{formatTimestamp(EXPERIMENT.profile.startTime)}</div>
</div>
<div className='basic'>
<p>End time</p>
<p className='margin'>End time</p>
<div className='nowrap'>{formatTimestamp(EXPERIMENT.profile.endTime)}</div>
</div>
</Stack>
{/* learn about click -> default active key is dispatcher. */}
{isShowLogDrawer ? <LogDrawer closeDrawer={closeLogDrawer} activeTab='dispatcher' /> : null}
</div>
......
import * as React from 'react';
import { DetailsRow, IDetailsRowBaseProps } from '@fluentui/react';
import OpenRow from '../../public-child/OpenRow';
interface DetailsProps {
detailsProps: IDetailsRowBaseProps;
}
interface DetailsState {
isExpand: boolean;
}
class Details extends React.Component<DetailsProps, DetailsState> {
constructor(props: DetailsProps) {
super(props);
this.state = { isExpand: false };
}
render(): React.ReactNode {
const { detailsProps } = this.props;
const { isExpand } = this.state;
return (
<div>
<div
onClick={(): void => {
this.setState(() => ({ isExpand: !isExpand }));
}}
>
<DetailsRow {...detailsProps} />
</div>
{isExpand && <OpenRow trialId={detailsProps.item.id} />}
</div>
);
}
}
export default Details;
import * as React from 'react';
import { DetailsList, IDetailsListProps, IColumn } from '@fluentui/react';
import {
DetailsList,
IDetailsListProps,
IColumn,
Icon,
DetailsRow,
IRenderFunction,
IDetailsHeaderProps,
Sticky,
StickyPositionType,
ScrollablePane,
ScrollbarVisibility
} from '@fluentui/react';
import DefaultMetric from '../../public-child/DefaultMetric';
import Details from './Details';
import { convertDuration } from '../../../static/function';
import OpenRow from '../../public-child/OpenRow';
import { convertDuration, copyAndSort } from '../../../static/function';
import { TRIALS } from '../../../static/datamodel';
import { SortInfo } from '../../../static/interface';
import { DETAILTABS } from '../../stateless-component/NNItabs';
import '../../../static/style/succTable.scss';
import '../../../static/style/tableStatus.css';
......@@ -11,12 +24,15 @@ import '../../../static/style/openRow.scss';
interface SuccessTableProps {
trialIds: string[];
// eslint-disable-next-line @typescript-eslint/no-unused-vars
updateOverviewPage: () => void;
}
interface SuccessTableState {
columns: IColumn[];
source: Array<any>;
innerWidth: number;
expandRowIdList: Set<string>;
sortInfo: SortInfo;
}
class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> {
......@@ -25,18 +41,42 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
this.state = {
columns: this.columns,
source: TRIALS.table(this.props.trialIds),
innerWidth: window.innerWidth
sortInfo: { field: '', isDescend: false },
expandRowIdList: new Set() // store expanded row's trial id
};
}
private onRenderRow: IDetailsListProps['onRenderRow'] = props => {
if (props) {
return <Details detailsProps={props} />;
componentDidUpdate(prevProps: SuccessTableProps): void {
if (this.props.trialIds !== prevProps.trialIds) {
const { trialIds } = this.props;
this.setState(() => ({ source: TRIALS.table(trialIds) }));
}
}
render(): React.ReactNode {
const { columns, source, sortInfo } = this.state;
const keepSortedSource = copyAndSort(source, sortInfo.field, sortInfo.isDescend);
const isNoneData = source.length === 0 ? true : false;
return (
<div id='succTable'>
<ScrollablePane className='scrollPanel' scrollbarVisibility={ScrollbarVisibility.auto}>
<DetailsList
columns={columns}
items={keepSortedSource}
setKey='set'
compact={true}
onRenderRow={this.onRenderRow}
onRenderDetailsHeader={this.onRenderDetailsHeader}
selectionMode={0} // close selector function
className='succTable'
/>
</ScrollablePane>
{isNoneData && <div className='succTable-tooltip'>{this.tooltipStr}</div>}
</div>
);
}
return null;
};
onColumnClick = (ev: React.MouseEvent<HTMLElement>, getColumn: IColumn): void => {
private onColumnClick = (_ev: React.MouseEvent<HTMLElement>, getColumn: IColumn): void => {
const { columns, source } = this.state;
const newColumns: IColumn[] = columns.slice();
const currColumn: IColumn = newColumns.filter(item => getColumn.key === item.key)[0];
......@@ -50,32 +90,51 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
}
});
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const newItems = this.copyAndSort(source, currColumn.fieldName!, currColumn.isSortedDescending);
const newItems = copyAndSort(source, currColumn.fieldName!, currColumn.isSortedDescending);
this.setState({
columns: newColumns,
source: newItems
source: newItems,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
sortInfo: { field: currColumn.fieldName!, isDescend: currColumn.isSortedDescending }
});
};
private copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): T[] {
const key = columnKey as keyof T;
return items.slice(0).sort((a: T, b: T) => ((isSortedDescending ? a[key] < b[key] : a[key] > b[key]) ? 1 : -1));
}
tooltipStr = (
private tooltipStr = (
<React.Fragment>
The experiment is running, please wait for the final metric patiently. You could also find status of trial
job with <span>{DETAILTABS}</span> button.
</React.Fragment>
);
columns = [
private columns = [
{
key: '_expand',
name: '',
onRender: (item: any): any => (
<Icon
aria-hidden={true}
iconName='ChevronRight'
styles={{
root: {
transition: 'all 0.2s',
transform: `rotate(${this.state.expandRowIdList.has(item.id) ? 90 : 0}deg)`
}
}}
className='cursor'
onClick={this.expandTrialId.bind(this, Event, item.id)}
/>
),
fieldName: 'expand',
isResizable: false,
minWidth: 20,
maxWidth: 20
},
{
name: 'Trial No.',
key: 'sequenceId',
fieldName: 'sequenceId', // required!
minWidth: 50,
maxWidth: 87,
minWidth: 60,
maxWidth: 100,
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
......@@ -85,8 +144,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name: 'ID',
key: 'id',
fieldName: 'id',
minWidth: 50,
maxWidth: 87,
minWidth: 60,
maxWidth: 118,
isResizable: true,
className: 'tableHead leftTitle',
data: 'string',
......@@ -96,8 +155,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
{
name: 'Duration',
key: 'duration',
minWidth: 65,
maxWidth: 150,
minWidth: 85,
maxWidth: 166,
isResizable: true,
fieldName: 'duration',
data: 'number',
......@@ -111,8 +170,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
{
name: 'Status',
key: 'status',
minWidth: 80,
maxWidth: 150,
minWidth: 98,
maxWidth: 160,
isResizable: true,
fieldName: 'status',
onRender: (item: any): React.ReactNode => (
......@@ -124,7 +183,7 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
key: 'accuracy',
fieldName: 'accuracy',
minWidth: 100,
maxWidth: 160,
maxWidth: 166,
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
......@@ -132,43 +191,49 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
}
];
setInnerWidth = (): void => {
this.setState(() => ({ innerWidth: window.innerWidth }));
};
componentDidMount(): void {
window.addEventListener('resize', this.setInnerWidth);
}
componentWillUnmount(): void {
window.removeEventListener('resize', this.setInnerWidth);
}
componentDidUpdate(prevProps: SuccessTableProps): void {
if (this.props.trialIds !== prevProps.trialIds) {
const { trialIds } = this.props;
this.setState(() => ({ source: TRIALS.table(trialIds) }));
}
private onRenderDetailsHeader: IRenderFunction<IDetailsHeaderProps> = (props, defaultRender) => {
if (!props) {
return null;
}
return (
<Sticky stickyPosition={StickyPositionType.Header} isScrollSynced>
{// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
defaultRender!({
...props
})}
</Sticky>
);
};
render(): React.ReactNode {
const { columns, source } = this.state;
const isNoneData = source.length === 0 ? true : false;
private onRenderRow: IDetailsListProps['onRenderRow'] = props => {
const { expandRowIdList } = this.state;
if (props) {
return (
<div id='succTable'>
<DetailsList
columns={columns}
items={source}
setKey='set'
compact={true}
onRenderRow={this.onRenderRow}
selectionMode={0} // close selector function
className='succTable'
/>
{isNoneData && <div className='succTable-tooltip'>{this.tooltipStr}</div>}
<div>
<div>
<DetailsRow {...props} />
</div>
{Array.from(expandRowIdList).map(
item => item === props.item.id && <OpenRow key={item} trialId={item} />
)}
</div>
);
}
return null;
};
private expandTrialId = (_event: any, id: string): void => {
const { expandRowIdList } = this.state;
const { updateOverviewPage } = this.props;
const copyExpandList = expandRowIdList;
if (copyExpandList.has(id)) {
copyExpandList.delete(id);
} else {
copyExpandList.add(id);
}
this.setState(() => ({ expandRowIdList: copyExpandList }));
updateOverviewPage();
};
}
export default SuccessTable;
......@@ -27,7 +27,7 @@ class MonacoHTML extends React.Component<MonacoEditorProps, {}> {
render(): React.ReactNode {
const { content, loading, height } = this.props;
return (
<div className='just-for-log'>
<React.Fragment>
{loading ? (
<Spinner
label='Wait, wait...'
......@@ -46,7 +46,7 @@ class MonacoHTML extends React.Component<MonacoEditorProps, {}> {
) : (
<MonacoEditor width='100%' height={height} language='json' value={content} options={DRAWEROPTION} />
)}
</div>
</React.Fragment>
);
}
}
......
import React from 'react';
import { DefaultButton, Icon } from '@fluentui/react';
interface ButtonProps {
icon: string;
btuName: string;
event: any;
}
class IconButtonTemplet extends React.Component<ButtonProps, {}> {
constructor(props: ButtonProps) {
super(props);
}
render(): React.ReactNode {
const { icon, btuName, event } = this.props;
return (
<div className='container'>
<DefaultButton className='icon'>
<Icon iconName={icon} />
</DefaultButton>
<DefaultButton className='integralBtn' onClick={event}>
<Icon iconName={icon} />
<span className='margin'>{btuName}</span>
</DefaultButton>
</div>
);
}
}
export default IconButtonTemplet;
import React, { useState, useCallback } from 'react';
import { DefaultButton, Stack } from '@fluentui/react';
import { Stack } from '@fluentui/react';
import TrialConfigPanel from './TrialConfigPanel';
import '../../../static/style/overview/config.scss';
import LogPanel from '../modals/LogPanel';
import IconButtonTemplate from './IconButtonTemplet';
import '../../static/style/overview/panel.scss';
export const TrialConfigButton = (): any => {
export const SlideNavBtns = (): any => {
const [isShowConfigPanel, setShowConfigPanle] = useState(false);
const [activeTab, setActiveTab] = useState('1');
const [isShowLogPanel, setShowLogPanel] = useState(false);
const [panelName, setPanelName] = useState('');
const hideConfigPanel = useCallback(() => setShowConfigPanle(false), []);
const showTrialConfigpPanel = useCallback(() => {
setShowConfigPanle(true);
setActiveTab('config');
setPanelName('config');
}, []);
const showSearchSpacePanel = useCallback(() => {
setShowConfigPanle(true);
setActiveTab('search space');
setPanelName('search space');
}, []);
const showLogPanel = useCallback(() => {
setShowLogPanel(true);
}, []);
const hideLogPanel = useCallback(() => {
setShowLogPanel(false);
}, []);
return (
// right side nav buttons
<React.Fragment>
<Stack className='config'>
<DefaultButton text='Search space' onClick={showSearchSpacePanel} />
<DefaultButton text='Config' onClick={showTrialConfigpPanel} />
<IconButtonTemplate icon='DocumentSearch' btuName='Search space' event={showSearchSpacePanel} />
<IconButtonTemplate icon='Archive' btuName='Config' event={showTrialConfigpPanel} />
<IconButtonTemplate icon='FilePDB' btuName='Log files' event={showLogPanel} />
</Stack>
{isShowConfigPanel && <TrialConfigPanel hideConfigPanel={hideConfigPanel} activeTab={activeTab} />}
{isShowConfigPanel && <TrialConfigPanel panelName={panelName} hideConfigPanel={hideConfigPanel} />}
{/* the panel for dispatcher & nnimanager log message */}
{isShowLogPanel && <LogPanel closeDrawer={hideLogPanel} />}
</React.Fragment>
);
};
import * as React from 'react';
import { Stack, Panel, Pivot, PivotItem, PrimaryButton } from '@fluentui/react';
import { EXPERIMENT } from '../../../static/datamodel';
import { Stack, Panel, PrimaryButton } from '@fluentui/react';
import { EXPERIMENT } from '../../static/datamodel';
import MonacoEditor from 'react-monaco-editor';
import { MONACO } from '../../../static/const';
import { AppContext } from '../../../App';
import { convertDuration, convertTimeAsUnit } from '../../../static/function';
import { prettyStringify } from '../../../static/json_util';
import { MONACO } from '../../static/const';
import { AppContext } from '../../App';
import { convertDuration, convertTimeAsUnit, caclMonacoEditorHeight } from '../../static/function';
import { prettyStringify } from '../../static/json_util';
import lodash from 'lodash';
import '../../../static/style/logDrawer.scss';
import '../../static/style/logDrawer.scss';
interface LogDrawerProps {
hideConfigPanel: () => void;
activeTab?: string;
panelName: string;
}
interface LogDrawerState {
......@@ -19,6 +19,12 @@ interface LogDrawerState {
innerWidth: number;
}
/**
* search space
* config
* model
*/
class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
constructor(props: LogDrawerProps) {
super(props);
......@@ -43,11 +49,9 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
}
render(): React.ReactNode {
const { hideConfigPanel, activeTab } = this.props;
const { hideConfigPanel, panelName } = this.props;
const { panelInnerHeight, innerWidth } = this.state;
// [marginTop 16px] + [Search space 46px] +
// button[height: 32px, marginTop: 45px, marginBottom: 25px] + [padding-bottom: 20px]
const monacoEditorHeight = panelInnerHeight - 184;
const monacoEditorHeight = caclMonacoEditorHeight(panelInnerHeight);
const blacklist = [
'id',
'logDir',
......@@ -83,12 +87,10 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
isLightDismiss={true}
onLightDismissClick={hideConfigPanel}
>
<div className='log-tab-body'>
<Pivot
initialSelectedKey={activeTab}
style={{ minHeight: 190, paddingTop: '16px' }}
>
<PivotItem headerText='Search space' itemKey='search space'>
<div className='panel'>
{panelName === 'search space' ? (
<div>
<div className='panelName'>Search space</div>
<MonacoEditor
height={monacoEditorHeight}
language='json'
......@@ -96,9 +98,10 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
value={prettyStringify(EXPERIMENT.searchSpace, prettyWidth, 2)}
options={MONACO}
/>
</PivotItem>
<PivotItem headerText='Config' itemKey='config'>
</div>
) : (
<div className='profile'>
<div className='panelName'>Config</div>
<MonacoEditor
width='100%'
height={monacoEditorHeight}
......@@ -108,10 +111,9 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
options={MONACO}
/>
</div>
</PivotItem>
</Pivot>
</div>
)}
<PrimaryButton text='Close' className='configClose' onClick={hideConfigPanel} />
</div>
</Panel>
</Stack>
);
......
......@@ -15,8 +15,8 @@ import {
import React from 'react';
import { EXPERIMENT, TRIALS } from '../../static/datamodel';
import { TOOLTIP_BACKGROUND_COLOR } from '../../static/const';
import { convertDuration, formatTimestamp } from '../../static/function';
import { TableObj } from '../../static/interface';
import { convertDuration, formatTimestamp, copyAndSort } from '../../static/function';
import { TableObj, SortInfo } from '../../static/interface';
import '../../static/style/search.scss';
import '../../static/style/tableStatus.css';
import '../../static/style/logPath.scss';
......@@ -56,36 +56,6 @@ const searchOptionLiterals = {
const defaultDisplayedColumns = ['sequenceId', 'id', 'duration', 'status', 'latestAccuracy'];
interface SortInfo {
field: string;
isDescend?: boolean;
}
function _copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): any {
const key = columnKey as keyof T;
return items.slice(0).sort(function(a: T, b: T): any {
if (
a[key] === undefined ||
Object.is(a[key], NaN) ||
Object.is(a[key], Infinity) ||
Object.is(a[key], -Infinity) ||
typeof a[key] === 'object'
) {
return 1;
}
if (
b[key] === undefined ||
Object.is(b[key], NaN) ||
Object.is(b[key], Infinity) ||
Object.is(b[key], -Infinity) ||
typeof b[key] === 'object'
) {
return -1;
}
return (isSortedDescending ? a[key] < b[key] : a[key] > b[key]) ? 1 : -1;
});
}
function _inferColumnTitle(columnKey: string): string {
if (columnKey === 'sequenceId') {
return 'Trial No.';
......@@ -238,7 +208,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
const { sortInfo } = this.state;
if (sortInfo.field !== '') {
return _copyAndSort(items, sortInfo.field, sortInfo.isDescend);
return copyAndSort(items, sortInfo.field, sortInfo.isDescend);
} else {
return items;
}
......@@ -255,6 +225,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
<Icon
aria-hidden={true}
iconName='ChevronRight'
className='cursor'
styles={{
root: {
transition: 'all 0.2s',
......
......@@ -262,6 +262,37 @@ function formatComplexTypeValue(value: any): string | number {
}
}
function caclMonacoEditorHeight(height): number {
// [Search space 56px] + [marginBottom 18px] +
// button[height: 32px, marginTop: 45px, marginBottom: 7px]
// panel own padding-bottom: 20px;
return height - 178;
}
function copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): any {
const key = columnKey as keyof T;
return items.slice(0).sort(function(a: T, b: T): any {
if (
a[key] === undefined ||
Object.is(a[key], NaN) ||
Object.is(a[key], Infinity) ||
Object.is(a[key], -Infinity) ||
typeof a[key] === 'object'
) {
return 1;
}
if (
b[key] === undefined ||
Object.is(b[key], NaN) ||
Object.is(b[key], Infinity) ||
Object.is(b[key], -Infinity) ||
typeof b[key] === 'object'
) {
return -1;
}
return (isSortedDescending ? a[key] < b[key] : a[key] > b[key]) ? 1 : -1;
});
}
export {
convertTime,
convertDuration,
......@@ -280,5 +311,7 @@ export {
isArrayType,
requestAxios,
isNaNorInfinity,
formatComplexTypeValue
formatComplexTypeValue,
caclMonacoEditorHeight,
copyAndSort
};
......@@ -131,7 +131,7 @@ interface MetricDataRecord {
}
interface TrialJobInfo {
id: string;
trialJobId: string;
sequenceId: number;
status: string;
startTime?: number;
......@@ -212,6 +212,12 @@ interface EventMap {
[key: string]: () => void;
}
// table column sort
interface SortInfo {
field: string;
isDescend?: boolean;
}
export {
TableObj,
TableRecord,
......@@ -233,5 +239,6 @@ export {
NNIManagerStatus,
EventMap,
SingleAxis,
MultipleAxes
MultipleAxes,
SortInfo
};
......@@ -144,9 +144,9 @@ class Trial implements TableObj {
}
return {
key: this.info.id,
key: this.info.trialJobId,
sequenceId: this.info.sequenceId,
id: this.info.id,
id: this.info.trialJobId,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
startTime: this.info.startTime!,
endTime: this.info.endTime,
......@@ -169,7 +169,7 @@ class Trial implements TableObj {
}
get id(): string {
return this.info.id;
return this.info.trialJobId;
}
get duration(): number {
......
......@@ -172,11 +172,11 @@ class TrialManager {
requestAxios(`${MANAGER_IP}/trial-jobs`)
.then(data => {
for (const trialInfo of data as TrialJobInfo[]) {
if (this.trials.has(trialInfo.id)) {
if (this.trials.has(trialInfo.trialJobId)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
updated = this.trials.get(trialInfo.id)!.updateTrialJobInfo(trialInfo) || updated;
updated = this.trials.get(trialInfo.trialJobId)!.updateTrialJobInfo(trialInfo) || updated;
} else {
this.trials.set(trialInfo.id, new Trial(trialInfo, undefined));
this.trials.set(trialInfo.trialJobId, new Trial(trialInfo, undefined));
updated = true;
}
this.maxSequenceId = Math.max(this.maxSequenceId, trialInfo.sequenceId);
......
.cursor{
&:hover, & i:hover{
cursor: pointer;
}
}
\ No newline at end of file
$buttonsMarginTop: 45px;
$buttonsMarginBottom: 7px;
$navBarHeight: 56px;
$navBarMarginBottom: 18px;
.download {
button,
button:active,
......@@ -7,22 +12,51 @@
}
}
.log-tab-body {
.refresh {
margin-left: 10px;
display: none;
}
}
/* office-fabric-ui */
.ms-Panel-main {
width: 55%;
background: #fff;
.ms-Panel-content {
padding: 0;
}
.ms-Pivot {
line-height: 56px;
padding: 0 38px;
background: #f2f2f2;
.ms-Pivot-linkContent:hover {
border-bottom: 2px solid #ccc;
}
.is-selected {
.ms-Pivot-linkContent:hover {
border: none;
}
}
}
}
.panel {
padding: 0 38px;
}
// for log panel editor
.logMargin {
margin-top: 20px;
}
.panelName {
line-height: $navBarHeight;
font-size: 14px;
margin-bottom: $navBarMarginBottom;
font-weight: 500;
}
/* log drawer download & close button's row */
.buttons {
margin-top: 16px;
margin: $buttonsMarginTop 0 $buttonsMarginBottom 0;
.close {
text-align: right;
......@@ -30,5 +64,5 @@
}
.configClose {
margin: 45px 0 25px 0;
margin: $buttonsMarginTop 0 $buttonsMarginBottom 0;
}
$seriesIconMargin: 8px;
$seriesIconMargin: 10px;
.ExpDuration {
margin-top: 28px;
span:hover {
cursor: pointer;
}
margin-top: 20px;
.maxTrialNum {
margin-bottom: 10px;
......@@ -13,7 +9,7 @@ $seriesIconMargin: 8px;
}
.exp-progress {
margin-top: 10px;
margin-top: 16px;
.bold {
font-weight: 500;
......@@ -57,17 +53,17 @@ $seriesIconMargin: 8px;
}
&-dropdown {
width: 48px;
width: 65px;
display: inline-block;
position: relative;
top: 13px;
left: 4px;
margin-right: 3px;
}
}
.ExpDuration .series .confirm {
margin: 0 6px;
.ms-Dropdown-title {
padding-right: 0;
}
}
}
.series {
......@@ -114,4 +110,12 @@ $seriesIconMargin: 8px;
.basic p {
margin-top: 0;
}
p.margin {
margin-top: 20px;
}
}
.trialStatus {
margin-top: 8px;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment