Second frontend, written in Next.JS + Typescript.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

144 lines
4.1 KiB

import React, { useCallback, useContext, useEffect, useMemo } from "react";
import chroma from "chroma-js";
import Select, { createFilter, Styles } from "react-select";
import LogListContext from "../../hooks/LogListContext";
import LogFilterChoiceContext from "../../hooks/LogFilterChoiceContext";
import { Config } from "react-select/src/filters";
import cssStyles from "./LogFilterSelector.module.sass";
export default function LogFilterSelector(props: {}) {
const {filter, updateFilter} = useContext(LogListContext);
const {channelNames, eventNames, characters} = useContext(LogFilterChoiceContext);
const options = useMemo(() => [
...channelNames.map(n => ({type: 0, color: "#77BB77", value: n, label: n})),
...eventNames.map(n => ({type: 1, color: "#BB7777", value: n, label: n})),
...characters.map(c => ({type: 2, color: "#3377BB", value: c.id, label: c.name})),
], [channelNames, eventNames, characters])
const value = useMemo(() => {
const value = [];
if (filter.characters && filter.characters.length > 0) {
value.push(...filter.characters.map(ch => options.find(c => c.type === 2 && c.value === ch)))
}
if (filter.events && filter.events.length > 0) {
value.push(...filter.events.map(v => options.find(c => c.type === 1 && c.value === v)))
}
if (filter.channels && filter.channels.length > 0) {
value.push(...filter.channels.map(v => options.find(c => c.type === 0 && c.value === v)))
}
return value
}, [filter, options])
const setValue = useCallback((value: {type:number, value:string}[] ) => {
if (value == null) {
value = [];
}
updateFilter({
...filter,
channels: value.filter(v => v.type === 0).map(v => v.value),
events: value.filter(v => v.type === 1).map(v => v.value),
characters: value.filter(v => v.type === 2).map(v => v.value),
})
}, [filter, updateFilter]);
return (
<Select
className={cssStyles.multiSelect}
closeMenuOnSelect={false}
styles={colourStyles}
filterOption={createFilter(filterConfig)}
defaultValue={[]}
isMulti={true}
options={options}
value={value}
onChange={setValue}
theme={defaultTheme => ({
...defaultTheme,
colors: {
...defaultTheme.colors,
...customTheme,
},
})}
/>
);
}
const customTheme = {
"primary": "#333333",
"primary75": "#333333",
"primary50": "#333333",
"primary25": "#333333",
"danger": "#000000",
"dangerLight": "#000000",
"neutral0": "#000000",
"neutral5": "#000000",
"neutral10": "#000000",
"neutral20": "#000000",
"neutral30": "#000000",
"neutral40": "#000000",
"neutral50": "#000000",
"neutral60": "#000000",
"neutral70": "#000000",
"neutral80": "#000000",
"neutral90": "#000000",
}
const filterConfig: Config = {
ignoreCase: true,
ignoreAccents: true,
trim: true,
matchFrom: 'start',
};
const colourStyles: Styles = {
control: styles => ({ ...styles, backgroundColor: 'rgba(0, 0, 0, 0.25)' }),
option: (styles, { data, isDisabled, isFocused, isSelected }) => {
const color = chroma(data.color);
return {
...styles,
backgroundColor: isDisabled
? null
: isSelected
? data.color
: isFocused
? color.alpha(0.1).css()
: null,
color: isDisabled
? '#ccc'
: isSelected
? chroma.contrast(color, 'white') > 2
? 'white'
: 'black'
: data.color,
cursor: isDisabled ? 'not-allowed' : 'default',
':active': {
...styles[':active'],
backgroundColor: !isDisabled && (isSelected ? data.color : color.alpha(0.3).css()),
},
};
},
multiValue: (styles, { data }) => {
const color = chroma(data.color);
return {
...styles,
backgroundColor: color.alpha(0.1).css(),
};
},
multiValueLabel: (styles, { data }) => ({
...styles,
color: data.color,
}),
multiValueRemove: (styles, { data }) => ({
...styles,
color: data.color,
':hover': {
backgroundColor: data.color,
color: 'white',
},
}),
};