RunAside.tsx 5.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 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
import React, {FunctionComponent, useCallback, useMemo, useState} from 'react';
import {borderColor, ellipsis, em, rem, size} from '~/utils/style';

import Checkbox from '~/components/Checkbox';
import Field from '~/components/Field';
import {Run} from '~/types';
import RunningToggle from '~/components/RunningToggle';
import SearchInput from '~/components/SearchInput';
import styled from 'styled-components';
import uniqBy from 'lodash/uniqBy';
import {useTranslation} from '~/utils/i18n';

const Aside = styled.div`
    height: 100%;
    overflow: hidden;
    display: flex;
    flex-direction: column;

    > section {
        margin: ${rem(20)} ${rem(20)} 0;
        flex: 0 0 auto;

        &:not(:last-child) {
            border-bottom: 1px solid ${borderColor};
            padding-bottom: ${rem(20)};
        }

        &.run-section {
            flex: 1 1 auto;
            overflow-x: hidden;
            overflow-y: auto;
            display: flex;
            flex-direction: column;

            .running-toggle {
                flex: 0 0 auto;
                box-shadow: 0 -${rem(5)} ${rem(16)} 0 rgba(0, 0, 0, 0.03);
            }

            .run-select {
                flex: 1 1 auto;
                overflow-x: hidden;
                overflow-y: auto;
                display: flex;
                flex-direction: column;

                > * {
                    flex: 0 0 auto;
                }

                .search-input {
                    margin-bottom: ${rem(15)};
                }

                .run-list {
                    flex: 1 1 auto;
                    overflow-x: hidden;
                    overflow-y: auto;

                    margin-top: ${rem(5)};

                    > div {
                        margin-top: ${rem(11)};

                        > * {
                            width: 100%;
                        }

                        .run-item {
                            display: flex;
                            align-items: center;
                            ${ellipsis()}

                            > i {
                                display: inline-block;
                                ${size(em(12), em(12))};
                                border-radius: ${em(6)};
                                margin-right: ${em(8)};
                            }
                        }
                    }
                }
            }
        }
    }
`;

type RunAsideProps = {
    runs?: Run[];
    selectedRuns?: Run[];
    onChangeRuns?: (runs: Run[]) => unknown;
    running?: boolean;
    onToggleRunning?: (running: boolean) => unknown;
};

const RunAside: FunctionComponent<RunAsideProps> = ({
    runs,
    selectedRuns,
    onChangeRuns,
    running,
    onToggleRunning,
    children
}) => {
    const {t} = useTranslation('common');

    const [search, setSearch] = useState('');

    const selectAll = useMemo(() => runs?.length === selectedRuns?.length, [runs, selectedRuns]);
    const toggleSelectAll = useCallback(
        (toggle: boolean) => {
            onChangeRuns?.(toggle ? runs ?? [] : []);
        },
        [onChangeRuns, runs]
    );

    const filteredRuns = useMemo(() => (search ? runs?.filter(run => run.label.indexOf(search) >= 0) : runs) ?? [], [
        runs,
        search
    ]);

    const setSelectedRuns = useCallback(
        (run: Run, toggle) => {
            let selected = selectedRuns ?? [];
            if (toggle) {
                selected = uniqBy([...selected, run], r => r.label);
            } else {
                selected = selected.filter(r => r.label !== run.label);
            }
            onChangeRuns?.(selected);
        },
        [onChangeRuns, selectedRuns]
    );

    return (
        <Aside>
            {children}
            <section className="run-section">
P
Peter Pan 已提交
138
                <Field className="run-select" label={t('common:select-runs')}>
139 140 141 142
                    <SearchInput
                        className="search-input"
                        value={search}
                        onChange={setSearch}
P
Peter Pan 已提交
143
                        placeholder={t('common:search-runs')}
144 145 146
                        rounded
                    />
                    <Checkbox value={selectAll} onChange={toggleSelectAll}>
P
Peter Pan 已提交
147
                        {t('common:select-all')}
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
                    </Checkbox>
                    <div className="run-list">
                        {filteredRuns.map((run, index) => (
                            <div key={index}>
                                <Checkbox
                                    value={selectedRuns?.map(r => r.label)?.includes(run.label)}
                                    title={run.label}
                                    onChange={value => setSelectedRuns(run, value)}
                                >
                                    <span className="run-item">
                                        <i style={{backgroundColor: run.colors[0]}}></i>
                                        {run.label}
                                    </span>
                                </Checkbox>
                            </div>
                        ))}
                    </div>
                </Field>
                <RunningToggle className="running-toggle" running={running} onToggle={onToggleRunning} />
            </section>
        </Aside>
    );
};

export default RunAside;