<commit_before>import * as React from "react"
import { computed, action } from "mobx"
import Select, { ValueType } from "react-select"
import { ColorScheme, ColorSchemes } from "charts/ColorSchemes"
import { observer } from "mobx-react"

export interface ColorSchemeOption {
    colorScheme?: ColorScheme
    gradient?: string
    label: string
    value: string
}

export interface ColorSchemeDropdownProps {
    additionalOptions: ColorSchemeOption[]
    value?: string
    gradientColorCount: number
    invertedColorScheme: boolean
    onChange: (selected: ColorSchemeOption) => void
}

@observer
export class ColorSchemeDropdown extends React.Component<
    ColorSchemeDropdownProps
> {
    static defaultProps = {
        additionalOptions: [],
        gradientColorCount: 6,
        invertedColorScheme: false
    }

    @computed get additionalOptions() {
        return this.props.additionalOptions
    }

    @computed get gradientColorCount() {
        return this.props.gradientColorCount
    }

    @computed get colorSchemeOptions() {
        return Object.entries(ColorSchemes)
            .filter(([, v]) => v !== undefined)
            .map(([key, scheme]) => {
                return {
                    colorScheme: scheme as ColorScheme,
                    gradient: this.createLinearGradient(
                        scheme as ColorScheme,
                        this.gradientColorCount
                    ),
                    label: (scheme as ColorScheme).name,
                    value: key
                }
            })
    }

    @computed get allOptions() {
        const { additionalOptions } = this

        return additionalOptions.concat(this.colorSchemeOptions)
    }

    createLinearGradient(colorScheme: ColorScheme, count: number) {
        const colors = colorScheme.getColors(count)

        const step = 100 / count
        const gradientEntries = colors.map(
            (color, i) => `${color} ${i * step}%, ${color} ${(i + 1) * step}%`
        )

        return `linear-gradient(90deg, ${gradientEntries.join(", ")})`
    }

    @action.bound onChange(selected: ValueType<ColorSchemeOption>) {
        // The onChange method can return an array of values (when multiple
        // items can be selected) or a single value. Since we are certain that
        // we are not using the multi-option select we can force the type to be
        // a single value.

        this.props.onChange(selected as ColorSchemeOption)
    }

    @action.bound formatOptionLabel(option: ColorSchemeOption) {
        const { invertedColorScheme } = this.props

        return (
            <div
                style={{
                    display: "flex",
                    justifyContent: "space-between",
                    alignItems: "center"
                }}
            >
                <div>{option.label}</div>

                {option.gradient && (
                    <span
                        style={{
                            backgroundImage: option.gradient,
                            width: "6rem",
                            height: "1.25rem",
                            border: "1px solid #aaa",

                            // Mirror the element if color schemes are inverted
                            transform: invertedColorScheme
                                ? "scaleX(-1)"
                                : undefined
                        }}
                    />
                )}
            </div>
        )
    }

    render() {
        return (
            <Select
                options={this.allOptions}
                formatOptionLabel={this.formatOptionLabel}
                onChange={this.onChange}
                value={this.allOptions.find(
                    scheme => scheme.value === this.props.value
                )}
                components={{
                    IndicatorSeparator: null
                }}
                styles={{
                    singleValue: provided => {
                        return {
                            ...provided,
                            width: "calc(100% - 10px)"
                        }
                    }
                }}
            />
        )
    }
}<commit_msg>Open dropdown to the top if there's no space below<commit_after>import * as React from "react"
import { computed, action } from "mobx"
import Select, { ValueType } from "react-select"
import { ColorScheme, ColorSchemes } from "charts/ColorSchemes"
import { observer } from "mobx-react"

export interface ColorSchemeOption {
    colorScheme?: ColorScheme
    gradient?: string
    label: string
    value: string
}

export interface ColorSchemeDropdownProps {
    additionalOptions: ColorSchemeOption[]
    value?: string
    gradientColorCount: number
    invertedColorScheme: boolean
    onChange: (selected: ColorSchemeOption) => void
}

@observer
export class ColorSchemeDropdown extends React.Component<
    ColorSchemeDropdownProps
> {
    static defaultProps = {
        additionalOptions: [],
        gradientColorCount: 6,
        invertedColorScheme: false
    }

    @computed get additionalOptions() {
        return this.props.additionalOptions
    }

    @computed get gradientColorCount() {
        return this.props.gradientColorCount
    }

    @computed get colorSchemeOptions() {
        return Object.entries(ColorSchemes)
            .filter(([, v]) => v !== undefined)
            .map(([key, scheme]) => {
                return {
                    colorScheme: scheme as ColorScheme,
                    gradient: this.createLinearGradient(
                        scheme as ColorScheme,
                        this.gradientColorCount
                    ),
                    label: (scheme as ColorScheme).name,
                    value: key
                }
            })
    }

    @computed get allOptions() {
        const { additionalOptions } = this

        return additionalOptions.concat(this.colorSchemeOptions)
    }

    createLinearGradient(colorScheme: ColorScheme, count: number) {
        const colors = colorScheme.getColors(count)

        const step = 100 / count
        const gradientEntries = colors.map(
            (color, i) => `${color} ${i * step}%, ${color} ${(i + 1) * step}%`
        )

        return `linear-gradient(90deg, ${gradientEntries.join(", ")})`
    }

    @action.bound onChange(selected: ValueType<ColorSchemeOption>) {
        // The onChange method can return an array of values (when multiple
        // items can be selected) or a single value. Since we are certain that
        // we are not using the multi-option select we can force the type to be
        // a single value.

        this.props.onChange(selected as ColorSchemeOption)
    }

    @action.bound formatOptionLabel(option: ColorSchemeOption) {
        const { invertedColorScheme } = this.props

        return (
            <div
                style={{
                    display: "flex",
                    justifyContent: "space-between",
                    alignItems: "center"
                }}
            >
                <div>{option.label}</div>

                {option.gradient && (
                    <span
                        style={{
                            backgroundImage: option.gradient,
                            width: "6rem",
                            height: "1.25rem",
                            border: "1px solid #aaa",

                            // Mirror the element if color schemes are inverted
                            transform: invertedColorScheme
                                ? "scaleX(-1)"
                                : undefined
                        }}
                    />
                )}
            </div>
        )
    }

    render() {
        return (
            <Select
                options={this.allOptions}
                formatOptionLabel={this.formatOptionLabel}
                onChange={this.onChange}
                value={this.allOptions.find(
                    scheme => scheme.value === this.props.value
                )}
                components={{
                    IndicatorSeparator: null
                }}
                styles={{
                    singleValue: provided => {
                        return {
                            ...provided,
                            width: "calc(100% - 10px)"
                        }
                    }
                }}
                menuPlacement="auto"
            />
        )
    }
}
{'fraction_non_alphanumeric': 0.08911819887429644, 'fraction_numerical': 0.004221388367729831, 'mean_word_length': 1.3685087475701194, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2998103', 'n_tokens_mistral': 2086, 'n_tokens_neox': 1937, 'n_words': 502}