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

  1. import React, { useCallback, useContext, useEffect, useMemo } from "react";
  2. import chroma from "chroma-js";
  3. import Select, { createFilter, Styles } from "react-select";
  4. import LogListContext from "../../hooks/LogListContext";
  5. import LogFilterChoiceContext from "../../hooks/LogFilterChoiceContext";
  6. import { Config } from "react-select/src/filters";
  7. import cssStyles from "./LogFilterSelector.module.sass";
  8. export default function LogFilterSelector(props: {}) {
  9. const {filter, updateFilter} = useContext(LogListContext);
  10. const {channelNames, eventNames, characters} = useContext(LogFilterChoiceContext);
  11. const options = useMemo(() => [
  12. ...channelNames.map(n => ({type: 0, color: "#77BB77", value: n, label: n})),
  13. ...eventNames.map(n => ({type: 1, color: "#BB7777", value: n, label: n})),
  14. ...characters.map(c => ({type: 2, color: "#3377BB", value: c.id, label: c.name})),
  15. ], [channelNames, eventNames, characters])
  16. const value = useMemo(() => {
  17. const value = [];
  18. if (filter.characters && filter.characters.length > 0) {
  19. value.push(...filter.characters.map(ch => options.find(c => c.type === 2 && c.value === ch)))
  20. }
  21. if (filter.events && filter.events.length > 0) {
  22. value.push(...filter.events.map(v => options.find(c => c.type === 1 && c.value === v)))
  23. }
  24. if (filter.channels && filter.channels.length > 0) {
  25. value.push(...filter.channels.map(v => options.find(c => c.type === 0 && c.value === v)))
  26. }
  27. return value
  28. }, [filter, options])
  29. const setValue = useCallback((value: {type:number, value:string}[] ) => {
  30. if (value == null) {
  31. value = [];
  32. }
  33. updateFilter({
  34. ...filter,
  35. channels: value.filter(v => v.type === 0).map(v => v.value),
  36. events: value.filter(v => v.type === 1).map(v => v.value),
  37. characters: value.filter(v => v.type === 2).map(v => v.value),
  38. })
  39. }, [filter, updateFilter]);
  40. return (
  41. <Select
  42. className={cssStyles.multiSelect}
  43. closeMenuOnSelect={false}
  44. styles={colourStyles}
  45. filterOption={createFilter(filterConfig)}
  46. defaultValue={[]}
  47. isMulti={true}
  48. options={options}
  49. value={value}
  50. onChange={setValue}
  51. theme={defaultTheme => ({
  52. ...defaultTheme,
  53. colors: {
  54. ...defaultTheme.colors,
  55. ...customTheme,
  56. },
  57. })}
  58. />
  59. );
  60. }
  61. const customTheme = {
  62. "primary": "#333333",
  63. "primary75": "#333333",
  64. "primary50": "#333333",
  65. "primary25": "#333333",
  66. "danger": "#000000",
  67. "dangerLight": "#000000",
  68. "neutral0": "#000000",
  69. "neutral5": "#000000",
  70. "neutral10": "#000000",
  71. "neutral20": "#000000",
  72. "neutral30": "#000000",
  73. "neutral40": "#000000",
  74. "neutral50": "#000000",
  75. "neutral60": "#000000",
  76. "neutral70": "#000000",
  77. "neutral80": "#000000",
  78. "neutral90": "#000000",
  79. }
  80. const filterConfig: Config = {
  81. ignoreCase: true,
  82. ignoreAccents: true,
  83. trim: true,
  84. matchFrom: 'start',
  85. };
  86. const colourStyles: Styles = {
  87. control: styles => ({ ...styles, backgroundColor: 'rgba(0, 0, 0, 0.25)' }),
  88. option: (styles, { data, isDisabled, isFocused, isSelected }) => {
  89. const color = chroma(data.color);
  90. return {
  91. ...styles,
  92. backgroundColor: isDisabled
  93. ? null
  94. : isSelected
  95. ? data.color
  96. : isFocused
  97. ? color.alpha(0.1).css()
  98. : null,
  99. color: isDisabled
  100. ? '#ccc'
  101. : isSelected
  102. ? chroma.contrast(color, 'white') > 2
  103. ? 'white'
  104. : 'black'
  105. : data.color,
  106. cursor: isDisabled ? 'not-allowed' : 'default',
  107. ':active': {
  108. ...styles[':active'],
  109. backgroundColor: !isDisabled && (isSelected ? data.color : color.alpha(0.3).css()),
  110. },
  111. };
  112. },
  113. multiValue: (styles, { data }) => {
  114. const color = chroma(data.color);
  115. return {
  116. ...styles,
  117. backgroundColor: color.alpha(0.1).css(),
  118. };
  119. },
  120. multiValueLabel: (styles, { data }) => ({
  121. ...styles,
  122. color: data.color,
  123. }),
  124. multiValueRemove: (styles, { data }) => ({
  125. ...styles,
  126. color: data.color,
  127. ':hover': {
  128. backgroundColor: data.color,
  129. color: 'white',
  130. },
  131. }),
  132. };