high-dimensional.tsx 6.5 KB
Newer Older
P
Peter Pan 已提交
1
import Aside, {AsideSection} from '~/components/Aside';
2 3
import type {Dimension, Reduction} from '~/resource/high-dimensional';
import React, {FunctionComponent, useEffect, useMemo, useState} from 'react';
4
import Select, {SelectProps} from '~/components/Select';
5 6 7
import {em, rem} from '~/utils/style';

import Checkbox from '~/components/Checkbox';
8
import Content from '~/components/Content';
9
import Error from '~/components/Error';
10
import Field from '~/components/Field';
11 12
import HighDimensionalChart from '~/components/HighDimensionalPage/HighDimensionalChart';
import Icon from '~/components/Icon';
13
import RadioButton from '~/components/RadioButton';
14
import RadioGroup from '~/components/RadioGroup';
15
import RunningToggle from '~/components/RunningToggle';
16
import SearchInput from '~/components/SearchInput';
17
import type {TagsData} from '~/types';
18
import Title from '~/components/Title';
19
import queryString from 'query-string';
20
import styled from 'styled-components';
21
import {useLocation} from 'react-router-dom';
P
Peter Pan 已提交
22
import {useRunningRequest} from '~/hooks/useRequest';
23
import useSearchValue from '~/hooks/useSearchValue';
24
import {useTranslation} from 'react-i18next';
25 26 27 28

const dimensions = ['2d', '3d'];
const reductions = ['pca', 'tsne'];

29 30 31 32
const StyledSelect = styled<React.FunctionComponent<SelectProps<string>>>(Select)`
    min-width: ${em(160)};
`;

33 34 35 36 37 38 39 40 41 42 43 44
const StyledIcon = styled(Icon)`
    margin-right: ${em(4)};
    vertical-align: middle;
`;

const AsideTitle = styled.div`
    font-size: ${rem(16)};
    line-height: ${rem(16)};
    font-weight: 700;
    margin-bottom: ${rem(10)};
`;

45 46 47 48 49 50
type Item = {
    run: string;
    tag: string;
    label: string;
};

51
const HighDimensional: FunctionComponent = () => {
52 53
    const {t} = useTranslation(['high-dimensional', 'common']);

P
Peter Pan 已提交
54 55
    const [running, setRunning] = useState(true);

56
    const {data, error, loading} = useRunningRequest<TagsData>('/embedding/tags', running);
57 58

    const list = useMemo(() => {
59
        if (!data) {
60 61
            return [];
        }
62 63
        return data.runs.reduce<Item[]>(
            (p, run, i) => [...p, ...(data.tags[i]?.map(tag => ({run, tag, label: `${run}/${tag}`})) ?? [])],
64 65
            []
        );
66
    }, [data]);
67 68
    const labelList = useMemo(() => list.map(item => item.label), [list]);

69 70
    const location = useLocation();
    const query = useMemo(() => queryString.parse(location.search), [location]);
71 72 73 74 75 76 77
    const selectedLabel = useMemo(() => {
        const run = Array.isArray(query.run) ? query.run[0] : query.run;
        return (run && list.find(item => item.run === run)?.label) ?? list[0]?.label;
    }, [query.run, list]);

    const [label, setLabel] = useState(selectedLabel);
    useEffect(() => setLabel(selectedLabel), [selectedLabel]);
78

79 80 81 82
    const selectedItem = useMemo(() => list.find(item => item.label === label) ?? {run: '', tag: '', label: ''}, [
        list,
        label
    ]);
83

84
    const [search, setSearch] = useState('');
85
    const debounceSearch = useSearchValue(search);
86
    const [dimension, setDimension] = useState(dimensions[0] as Dimension);
87
    const [reduction, setReduction] = useState(reductions[0] as Reduction);
88
    const [labelVisibility, setLabelVisibility] = useState(true);
89

P
Peter Pan 已提交
90 91 92
    const bottom = useMemo(() => <RunningToggle running={running} onToggle={setRunning} />, [running, setRunning]);

    const aside = useMemo(
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
        () =>
            labelList.length ? (
                <Aside bottom={bottom}>
                    <AsideSection>
                        <AsideTitle>{t('common:select-runs')}</AsideTitle>
                        <StyledSelect list={labelList} value={label} onChange={setLabel} />
                    </AsideSection>
                    <AsideSection>
                        <Field>
                            <SearchInput placeholder={t('common:search')} value={search} onChange={setSearch} />
                        </Field>
                        <Field>
                            <Checkbox value={labelVisibility} onChange={setLabelVisibility}>
                                {t('high-dimensional:display-all-label')}
                            </Checkbox>
                        </Field>
                    </AsideSection>
                    <AsideSection>
                        <AsideTitle>
                            <StyledIcon type="dimension" />
                            {t('high-dimensional:dimension')}
                        </AsideTitle>
                        <Field>
                            <RadioGroup value={dimension} onChange={value => setDimension(value)}>
                                {dimensions.map(item => (
                                    <RadioButton key={item} value={item}>
                                        {t(item)}
                                    </RadioButton>
                                ))}
                            </RadioGroup>
                        </Field>
                    </AsideSection>
                    <AsideSection>
                        <AsideTitle>
                            <StyledIcon type="reduction" />
                            {t('high-dimensional:reduction-method')}
                        </AsideTitle>
                        <Field>
                            <RadioGroup value={reduction} onChange={value => setReduction(value)}>
                                {reductions.map(item => (
                                    <RadioButton key={item} value={item}>
                                        {t(item)}
                                    </RadioButton>
                                ))}
                            </RadioGroup>
                        </Field>
                    </AsideSection>
                </Aside>
            ) : null,
P
Peter Pan 已提交
142
        [t, bottom, dimension, label, labelList, labelVisibility, reduction, search]
143 144 145 146 147
    );

    return (
        <>
            <Title>{t('common:high-dimensional')}</Title>
148
            <Content aside={aside} loading={loading}>
149 150 151
                {!loading && !list.length ? (
                    <Error />
                ) : error ? (
152 153 154 155
                    <div>{t('common:error')}</div>
                ) : loading ? null : (
                    <HighDimensionalChart
                        dimension={dimension}
156
                        keyword={debounceSearch}
157 158
                        run={selectedItem.run}
                        tag={selectedItem.tag}
159 160 161 162 163
                        running={running}
                        labelVisibility={labelVisibility}
                        reduction={reduction}
                    />
                )}
164 165 166 167 168 169
            </Content>
        </>
    );
};

export default HighDimensional;