scrabble-solver 2.11.0 → 2.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/lerna.json +1 -1
  2. package/package.json +1 -1
  3. package/packages/configs/package.json +3 -3
  4. package/packages/constants/package.json +1 -1
  5. package/packages/dictionaries/package-lock.json +2 -2
  6. package/packages/dictionaries/package.json +4 -4
  7. package/packages/logger/package-lock.json +2 -2
  8. package/packages/logger/package.json +1 -1
  9. package/packages/scrabble-solver/.next/BUILD_ID +1 -1
  10. package/packages/scrabble-solver/.next/build-manifest.json +7 -7
  11. package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
  12. package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
  13. package/packages/scrabble-solver/.next/cache/next-server.js.nft.json +1 -1
  14. package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
  15. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
  16. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/0.pack +0 -0
  17. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack +0 -0
  18. package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
  19. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
  20. package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
  21. package/packages/scrabble-solver/.next/prerender-manifest.json +1 -1
  22. package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
  23. package/packages/scrabble-solver/.next/server/chunks/277.js +523 -611
  24. package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
  25. package/packages/scrabble-solver/.next/server/pages/404.html +2 -2
  26. package/packages/scrabble-solver/.next/server/pages/404.js.nft.json +1 -1
  27. package/packages/scrabble-solver/.next/server/pages/500.html +1 -1
  28. package/packages/scrabble-solver/.next/server/pages/_app.js +0 -24
  29. package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
  30. package/packages/scrabble-solver/.next/server/pages/_document.js.nft.json +1 -1
  31. package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js +0 -1
  32. package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js +0 -1
  33. package/packages/scrabble-solver/.next/server/pages/api/solve.js +28 -8
  34. package/packages/scrabble-solver/.next/server/pages/api/verify.js +0 -1
  35. package/packages/scrabble-solver/.next/server/pages/api/visit.js +0 -1
  36. package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
  37. package/packages/scrabble-solver/.next/server/pages/index.js +49 -90
  38. package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
  39. package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
  40. package/packages/scrabble-solver/.next/static/Mdvi3FY0PqkILKLbPlVBU/_buildManifest.js +1 -0
  41. package/packages/scrabble-solver/.next/static/chunks/pages/{404-e0f30450e9920dc3.js → 404-448ba28510855455.js} +1 -1
  42. package/packages/scrabble-solver/.next/static/chunks/pages/_app-495e6f4ccc278bb2.js +28 -0
  43. package/packages/scrabble-solver/.next/static/chunks/pages/index-5ecc51900ca29685.js +1 -0
  44. package/packages/scrabble-solver/.next/static/css/17b0a2db8742105f.css +1 -0
  45. package/packages/scrabble-solver/.next/static/css/e1ffeb2558330c55.css +2 -0
  46. package/packages/scrabble-solver/.next/trace +54 -55
  47. package/packages/scrabble-solver/package-lock.json +2 -79
  48. package/packages/scrabble-solver/package.json +8 -13
  49. package/packages/scrabble-solver/src/components/{Solver/components/EmptyState/EmptyState.module.scss → Alert/Alert.module.scss} +11 -7
  50. package/packages/scrabble-solver/src/components/{Solver/components/EmptyState/EmptyState.tsx → Alert/Alert.tsx} +8 -6
  51. package/packages/scrabble-solver/src/components/Alert/index.ts +1 -0
  52. package/packages/scrabble-solver/src/components/Board/Board.module.scss +55 -0
  53. package/packages/scrabble-solver/src/components/Board/BoardPure.tsx +4 -0
  54. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +51 -2
  55. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.tsx +12 -0
  56. package/packages/scrabble-solver/src/components/Board/components/Cell/CellPure.tsx +12 -0
  57. package/packages/scrabble-solver/src/components/Board/hooks/useGrid.ts +9 -26
  58. package/packages/scrabble-solver/src/components/Board/lib/getPositionInGrid.ts +1 -1
  59. package/packages/scrabble-solver/src/components/Dictionary/Dictionary.module.scss +4 -0
  60. package/packages/scrabble-solver/src/components/DictionaryInput/DictionaryInput.module.scss +1 -0
  61. package/packages/scrabble-solver/src/components/EmptyState/EmptyState.module.scss +2 -1
  62. package/packages/scrabble-solver/src/components/EmptyState/EmptyState.tsx +1 -2
  63. package/packages/scrabble-solver/src/components/Loading/Loading.module.scss +1 -1
  64. package/packages/scrabble-solver/src/components/Logo/Logo.tsx +5 -0
  65. package/packages/scrabble-solver/src/components/LogoSplashScreen/LogoSplashScreen.module.scss +4 -1
  66. package/packages/scrabble-solver/src/components/Modal/Modal.module.scss +14 -0
  67. package/packages/scrabble-solver/src/components/Modal/Modal.tsx +4 -1
  68. package/packages/scrabble-solver/src/components/PlainTiles/PlainTiles.module.scss +1 -1
  69. package/packages/scrabble-solver/src/components/PlainTiles/Tile.tsx +3 -3
  70. package/packages/scrabble-solver/src/components/Rack/Rack.module.scss +25 -0
  71. package/packages/scrabble-solver/src/components/Rack/Rack.tsx +5 -4
  72. package/packages/scrabble-solver/src/components/Rack/RackTile.tsx +6 -13
  73. package/packages/scrabble-solver/src/components/Results/HeaderButton.tsx +5 -12
  74. package/packages/scrabble-solver/src/components/Results/Result.tsx +5 -3
  75. package/packages/scrabble-solver/src/components/Results/Results.module.scss +41 -1
  76. package/packages/scrabble-solver/src/components/Results/Results.tsx +29 -43
  77. package/packages/scrabble-solver/src/components/Results/types.ts +1 -1
  78. package/packages/scrabble-solver/src/components/ResultsInput/ResultsInput.module.scss +1 -0
  79. package/packages/scrabble-solver/src/components/Solver/Solver.module.scss +9 -4
  80. package/packages/scrabble-solver/src/components/Solver/Solver.tsx +17 -19
  81. package/packages/scrabble-solver/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +5 -6
  82. package/packages/scrabble-solver/src/components/Solver/components/index.ts +0 -1
  83. package/packages/scrabble-solver/src/components/Tile/Tile.module.scss +42 -61
  84. package/packages/scrabble-solver/src/components/Tile/Tile.tsx +4 -3
  85. package/packages/scrabble-solver/src/components/Tile/TilePure.tsx +4 -13
  86. package/packages/scrabble-solver/src/components/Tooltip/Tooltip.module.scss +1 -72
  87. package/packages/scrabble-solver/src/components/Tooltip/useTooltip.tsx +25 -35
  88. package/packages/scrabble-solver/src/components/index.ts +1 -0
  89. package/packages/scrabble-solver/src/hooks/index.ts +0 -1
  90. package/packages/scrabble-solver/src/hooks/useAppLayout.ts +3 -1
  91. package/packages/scrabble-solver/src/i18n/de.json +0 -1
  92. package/packages/scrabble-solver/src/i18n/en.json +0 -1
  93. package/packages/scrabble-solver/src/i18n/es.json +0 -1
  94. package/packages/scrabble-solver/src/i18n/fa.json +0 -1
  95. package/packages/scrabble-solver/src/i18n/fr.json +0 -1
  96. package/packages/scrabble-solver/src/i18n/pl.json +0 -1
  97. package/packages/scrabble-solver/src/lib/createRegExp.ts +13 -0
  98. package/packages/scrabble-solver/src/lib/groupResults.ts +38 -0
  99. package/packages/scrabble-solver/src/lib/guessLocale.ts +22 -0
  100. package/packages/scrabble-solver/src/lib/index.ts +4 -2
  101. package/packages/scrabble-solver/src/lib/sortResults.ts +6 -10
  102. package/packages/scrabble-solver/src/modals/DictionaryModal/DictionaryModal.module.scss +6 -2
  103. package/packages/scrabble-solver/src/modals/KeyMapModal/KeyMapModal.tsx +5 -21
  104. package/packages/scrabble-solver/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +4 -0
  105. package/packages/scrabble-solver/src/modals/MenuModal/MenuModal.module.scss +7 -0
  106. package/packages/scrabble-solver/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -0
  107. package/packages/scrabble-solver/src/modals/ResultsModal/ResultsModal.module.scss +3 -3
  108. package/packages/scrabble-solver/src/modals/ResultsModal/ResultsModal.tsx +23 -11
  109. package/packages/scrabble-solver/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +2 -0
  110. package/packages/scrabble-solver/src/modals/WordsModal/WordsModal.module.scss +8 -1
  111. package/packages/scrabble-solver/src/pages/api/dictionary/[locale]/[word].ts +0 -1
  112. package/packages/scrabble-solver/src/pages/api/dictionary/[locale]/index.ts +0 -1
  113. package/packages/scrabble-solver/src/pages/api/solve.ts +0 -1
  114. package/packages/scrabble-solver/src/pages/api/verify.ts +0 -1
  115. package/packages/scrabble-solver/src/pages/api/visit.ts +0 -1
  116. package/packages/scrabble-solver/src/pages/index.tsx +13 -15
  117. package/packages/scrabble-solver/src/parameters/index.ts +2 -2
  118. package/packages/scrabble-solver/src/state/createAppStore.ts +26 -10
  119. package/packages/scrabble-solver/src/state/sagas.ts +1 -0
  120. package/packages/scrabble-solver/src/state/selectors.ts +37 -37
  121. package/packages/scrabble-solver/src/state/slices/boardInitialState.ts +3 -1
  122. package/packages/scrabble-solver/src/state/slices/cellFilterInitialState.ts +3 -3
  123. package/packages/scrabble-solver/src/state/slices/cellFilterSlice.ts +3 -1
  124. package/packages/scrabble-solver/src/state/slices/dictionaryInitialState.ts +2 -2
  125. package/packages/scrabble-solver/src/state/slices/rackInitialState.ts +3 -1
  126. package/packages/scrabble-solver/src/state/slices/resultsInitialState.ts +11 -4
  127. package/packages/scrabble-solver/src/state/slices/settingsInitialState.ts +10 -21
  128. package/packages/scrabble-solver/src/state/slices/solveInitialState.ts +2 -2
  129. package/packages/scrabble-solver/src/state/slices/solveSlice.ts +2 -0
  130. package/packages/scrabble-solver/src/state/slices/verifyInitialState.ts +13 -4
  131. package/packages/scrabble-solver/src/state/types.ts +20 -2
  132. package/packages/scrabble-solver/src/styles/mixins.scss +25 -1
  133. package/packages/scrabble-solver/src/styles/variables.scss +17 -1
  134. package/packages/scrabble-solver/src/types/index.ts +10 -2
  135. package/packages/solver/package-lock.json +2 -2
  136. package/packages/solver/package.json +5 -5
  137. package/packages/types/build/Bonus.d.ts +2 -1
  138. package/packages/types/package.json +2 -2
  139. package/packages/types/src/Bonus.ts +3 -1
  140. package/packages/word-definitions/package-lock.json +8 -8
  141. package/packages/word-definitions/package.json +2 -2
  142. package/packages/word-lists/package-lock.json +2 -2
  143. package/packages/word-lists/package.json +2 -2
  144. package/packages/scrabble-solver/.next/static/45ye7793DY705HOcuK9lJ/_buildManifest.js +0 -1
  145. package/packages/scrabble-solver/.next/static/chunks/pages/_app-d7acee5e526752d9.js +0 -28
  146. package/packages/scrabble-solver/.next/static/chunks/pages/index-35d2c1c79a201ae2.js +0 -1
  147. package/packages/scrabble-solver/.next/static/css/a48caa6f57de6e98.css +0 -1
  148. package/packages/scrabble-solver/.next/static/css/c49bbe944ddd1b39.css +0 -2
  149. package/packages/scrabble-solver/src/components/Board/types/index.ts +0 -4
  150. package/packages/scrabble-solver/src/components/Solver/components/EmptyState/index.ts +0 -1
  151. package/packages/scrabble-solver/src/components/Tooltip/constants.ts +0 -28
  152. package/packages/scrabble-solver/src/hooks/useUniqueId.ts +0 -9
  153. package/packages/scrabble-solver/src/lib/isCtrl.ts +0 -7
  154. package/packages/scrabble-solver/src/state/rootReducer.ts +0 -25
  155. /package/packages/scrabble-solver/.next/static/{45ye7793DY705HOcuK9lJ → Mdvi3FY0PqkILKLbPlVBU}/_ssgManifest.js +0 -0
@@ -1,4 +1,4 @@
1
- import { Point } from '../types';
1
+ import { Point } from 'types';
2
2
 
3
3
  const getPositionInGrid = <T>(grid: T[][], constraint: (value: T) => boolean): Point | undefined => {
4
4
  for (let y = 0; y < grid.length; ++y) {
@@ -1,4 +1,8 @@
1
+ @import 'styles/mixins';
2
+
1
3
  .dictionary {
4
+ @include scrollbars;
5
+
2
6
  position: relative;
3
7
  max-height: var(--dictionary--height);
4
8
  height: var(--dictionary--height);
@@ -8,4 +8,5 @@
8
8
  @include text-input;
9
9
 
10
10
  border: none;
11
+ border-radius: inherit;
11
12
  }
@@ -8,7 +8,8 @@
8
8
 
9
9
  .tiles {
10
10
  display: inline-block;
11
+ max-width: 100%;
11
12
  margin-bottom: var(--spacing--m);
12
- height: 40px;
13
+ height: 48px;
13
14
  overflow: visible;
14
15
  }
@@ -41,8 +41,7 @@ const EmptyState: FunctionComponent<Props> = ({ children, className, variant })
41
41
  return (
42
42
  <div className={classNames(styles.emptyState, className)}>
43
43
  <PlainTiles className={styles.tiles} color={COLORS_PER_TYPE[variant]} content={content} />
44
-
45
- <div className={styles.content}>{children}</div>
44
+ <div>{children}</div>
46
45
  </div>
47
46
  );
48
47
  };
@@ -30,6 +30,6 @@
30
30
  }
31
31
 
32
32
  .tiles {
33
- width: 298px;
33
+ width: 100% * 2 / 3;
34
34
  height: 100%;
35
35
  }
@@ -1,3 +1,8 @@
1
+ /**
2
+ * This component is unused, but it serves as a blueprint for the Logo.svg.
3
+ * Logo.svg is what this component generates with all the text nodes transformed
4
+ * into paths (manually with Inkscape), and corner radius removed from tiles.
5
+ */
1
6
  import { CSSProperties, FunctionComponent } from 'react';
2
7
 
3
8
  import PlainTiles from '../PlainTiles';
@@ -2,7 +2,7 @@
2
2
 
3
3
  $loading-duration: 250ms;
4
4
  $loaded-duration: 100ms;
5
- $hiding-duration: 200ms;
5
+ $hiding-duration: 150ms;
6
6
 
7
7
  .logoSplashScreen {
8
8
  display: flex;
@@ -16,18 +16,21 @@ $hiding-duration: 200ms;
16
16
  animation-delay: $loading-duration + $loaded-duration;
17
17
  animation-duration: $hiding-duration;
18
18
  animation-fill-mode: forwards;
19
+ animation-timing-function: linear;
19
20
 
20
21
  .logos {
21
22
  animation: pulse;
22
23
  animation-delay: $loading-duration + $loaded-duration;
23
24
  animation-duration: $hiding-duration;
24
25
  animation-fill-mode: forwards;
26
+ animation-timing-function: linear;
25
27
  }
26
28
 
27
29
  .logoColor {
28
30
  animation: progress;
29
31
  animation-duration: $loading-duration;
30
32
  animation-fill-mode: forwards;
33
+ animation-timing-function: linear;
31
34
  }
32
35
  }
33
36
  }
@@ -112,9 +112,23 @@
112
112
  }
113
113
 
114
114
  .content {
115
+ @include scrollbars;
116
+
115
117
  position: relative;
116
118
  flex: 1;
117
119
  min-height: 0;
118
120
  padding: var(--spacing--l);
119
121
  overflow: auto;
120
122
  }
123
+
124
+ .footer {
125
+ flex: 0 0 auto;
126
+ display: flex;
127
+ justify-content: flex-end;
128
+ padding: var(--spacing--l);
129
+ box-shadow: var(--box-shadow);
130
+
131
+ @include media('>s') {
132
+ display: none;
133
+ }
134
+ }
@@ -15,12 +15,13 @@ import styles from './Modal.module.scss';
15
15
  export interface Props {
16
16
  children: ReactNode;
17
17
  className?: string;
18
+ footer?: ReactNode;
18
19
  isOpen: boolean;
19
20
  title: string;
20
21
  onClose: () => void;
21
22
  }
22
23
 
23
- const Modal: FunctionComponent<Props> = ({ children, className, isOpen, title, onClose }) => {
24
+ const Modal: FunctionComponent<Props> = ({ children, className, footer, isOpen, title, onClose }) => {
24
25
  const translate = useTranslate();
25
26
  const [shouldReturnFocusAfterClose, setShouldReturnFocusAfterClose] = useState(true);
26
27
 
@@ -68,6 +69,8 @@ const Modal: FunctionComponent<Props> = ({ children, className, isOpen, title, o
68
69
  </div>
69
70
 
70
71
  <div className={styles.content}>{children}</div>
72
+
73
+ {footer && <div className={styles.footer}>{footer}</div>}
71
74
  </div>
72
75
  </ReactModal>
73
76
  );
@@ -20,5 +20,5 @@ $duration: 750ms;
20
20
  }
21
21
 
22
22
  .dropShadow {
23
- filter: drop-shadow(0 0 4px rgba(#000, 0.4));
23
+ filter: drop-shadow(0 3px 3px rgba(0, 0, 0, 0.4));
24
24
  }
@@ -14,12 +14,12 @@ interface Props {
14
14
 
15
15
  const Tile: FunctionComponent<Props> = ({ character, className, color, fontFamily, points, size, transform, x, y }) => (
16
16
  <g className={className} transform={transform}>
17
- <rect fill={color} height={size} width={size} x={x} y={y} />
17
+ <rect fill={color} height={size} rx={size * 0.15} width={size} x={x} y={y} />
18
18
 
19
19
  <text
20
20
  dominantBaseline="central"
21
21
  fontFamily={fontFamily}
22
- fontSize={0.6 * size}
22
+ fontSize={size * 0.6}
23
23
  fontWeight="bold"
24
24
  textAnchor="middle"
25
25
  x={x + size / 2}
@@ -32,7 +32,7 @@ const Tile: FunctionComponent<Props> = ({ character, className, color, fontFamil
32
32
  <text
33
33
  dominantBaseline="text-after-edge"
34
34
  fontFamily={fontFamily}
35
- fontSize={0.25 * size}
35
+ fontSize={size * 0.25}
36
36
  fontWeight="bold"
37
37
  textAnchor="end"
38
38
  x={x + size * 0.9}
@@ -3,6 +3,7 @@
3
3
  .rack {
4
4
  display: flex;
5
5
  box-shadow: var(--box-shadow);
6
+ border-radius: var(--border--radius);
6
7
  }
7
8
 
8
9
  .tile {
@@ -15,3 +16,27 @@
15
16
  z-index: 2;
16
17
  }
17
18
  }
19
+
20
+ .sharpLeft {
21
+ [dir='ltr'] & {
22
+ border-top-left-radius: 0;
23
+ border-bottom-left-radius: 0;
24
+ }
25
+
26
+ [dir='rtl'] & {
27
+ border-top-right-radius: 0;
28
+ border-bottom-right-radius: 0;
29
+ }
30
+ }
31
+
32
+ .sharpRight {
33
+ [dir='ltr'] & {
34
+ border-top-right-radius: 0;
35
+ border-bottom-right-radius: 0;
36
+ }
37
+
38
+ [dir='rtl'] & {
39
+ border-top-left-radius: 0;
40
+ border-bottom-left-radius: 0;
41
+ }
42
+ }
@@ -8,7 +8,6 @@ import {
8
8
  createKeyboardNavigation,
9
9
  extractCharacters,
10
10
  extractInputValue,
11
- isCtrl,
12
11
  zipCharactersAndTiles,
13
12
  } from 'lib';
14
13
  import { rackSlice, selectConfig, selectLocale, selectRack, selectResultCandidateTiles, useTypedSelector } from 'state';
@@ -99,9 +98,7 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
99
98
  changeActiveIndex(1);
100
99
  },
101
100
  onKeyDown: (event) => {
102
- if (isCtrl(event) && config.isTwoCharacterTilePrefix(event.key)) {
103
- changeActiveIndex(1);
104
- } else if (event.currentTarget.value === event.key) {
101
+ if (event.currentTarget.value === event.key) {
105
102
  // change event did not fire because the same character was typed over the current one
106
103
  // but we still want to move the caret
107
104
  event.preventDefault();
@@ -118,6 +115,10 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
118
115
  <RackTile
119
116
  activeIndexRef={activeIndexRef}
120
117
  character={character}
118
+ className={classNames({
119
+ [styles.sharpLeft]: index !== 0,
120
+ [styles.sharpRight]: index !== tiles.length - 1,
121
+ })}
121
122
  index={index}
122
123
  inputRef={tilesRefs[index]}
123
124
  key={index}
@@ -1,5 +1,6 @@
1
1
  import { BLANK } from '@scrabble-solver/constants';
2
2
  import { Tile as TileModel } from '@scrabble-solver/types';
3
+ import classNames from 'classnames';
3
4
  import {
4
5
  ChangeEvent,
5
6
  ChangeEventHandler,
@@ -12,7 +13,7 @@ import {
12
13
  } from 'react';
13
14
  import { useDispatch } from 'react-redux';
14
15
 
15
- import { createKeyboardNavigation, extractCharacters, extractInputValue, isCtrl } from 'lib';
16
+ import { createKeyboardNavigation, extractCharacters, extractInputValue } from 'lib';
16
17
  import {
17
18
  rackSlice,
18
19
  selectCharacterIsValid,
@@ -30,6 +31,7 @@ import styles from './Rack.module.scss';
30
31
  interface Props {
31
32
  activeIndexRef: MutableRefObject<number | undefined>;
32
33
  character: string | null;
34
+ className?: string;
33
35
  index: number;
34
36
  inputRef: RefObject<HTMLInputElement>;
35
37
  size: number;
@@ -41,6 +43,7 @@ interface Props {
41
43
  const RackTile: FunctionComponent<Props> = ({
42
44
  activeIndexRef,
43
45
  character,
46
+ className,
44
47
  index,
45
48
  inputRef,
46
49
  size,
@@ -77,17 +80,7 @@ const RackTile: FunctionComponent<Props> = ({
77
80
  event.preventDefault();
78
81
  dispatch(rackSlice.actions.changeCharacter({ character: null, index }));
79
82
  },
80
- onKeyDown: (event) => {
81
- if (isCtrl(event) && config.isTwoCharacterTilePrefix(event.key)) {
82
- event.preventDefault();
83
- event.stopPropagation();
84
- const twoTilesCharacter = config.getTwoCharacterTileByPrefix(event.key);
85
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
86
- dispatch(rackSlice.actions.changeCharacter({ character: twoTilesCharacter!, index }));
87
- }
88
-
89
- onKeyDown(event);
90
- },
83
+ onKeyDown,
91
84
  });
92
85
  }, [index, onKeyDown]);
93
86
 
@@ -97,7 +90,7 @@ const RackTile: FunctionComponent<Props> = ({
97
90
  index: (index + 1).toLocaleString(locale),
98
91
  })}
99
92
  autoFocus={index === 0}
100
- className={styles.tile}
93
+ className={classNames(styles.tile, className)}
101
94
  character={character === null ? undefined : character}
102
95
  highlighted={tile !== null}
103
96
  inputRef={inputRef}
@@ -3,13 +3,7 @@ import { ReactElement } from 'react';
3
3
  import { useDispatch } from 'react-redux';
4
4
 
5
5
  import { SortDown, SortUp } from 'icons';
6
- import {
7
- resultsSlice,
8
- selectResultsSortColumn,
9
- selectResultsSortDirection,
10
- useTranslate,
11
- useTypedSelector,
12
- } from 'state';
6
+ import { resultsSlice, selectResultsSort, useTranslate, useTypedSelector } from 'state';
13
7
  import { ResultColumn, SortDirection } from 'types';
14
8
 
15
9
  import { useTooltip } from '../Tooltip';
@@ -24,8 +18,7 @@ interface Props {
24
18
  const HeaderButton = ({ column }: Props): ReactElement => {
25
19
  const dispatch = useDispatch();
26
20
  const translate = useTranslate();
27
- const sortColumn = useTypedSelector(selectResultsSortColumn);
28
- const sortDirection = useTypedSelector(selectResultsSortDirection);
21
+ const sort = useTypedSelector(selectResultsSort);
29
22
  const triggerProps = useTooltip(translate(column.translationKey));
30
23
 
31
24
  const handleOrderChange = (columnId: ResultColumn) => {
@@ -44,10 +37,10 @@ const HeaderButton = ({ column }: Props): ReactElement => {
44
37
  <span className={styles.cell}>
45
38
  <span className={styles.headerButtonLabel}>{translate(column.translationKey)}</span>
46
39
 
47
- {sortColumn === column.id && (
40
+ {sort.column === column.id && (
48
41
  <>
49
- {sortDirection === SortDirection.Ascending && <SortUp className={styles.sortIcon} />}
50
- {sortDirection === SortDirection.Descending && <SortDown className={styles.sortIcon} />}
42
+ {sort.direction === SortDirection.Ascending && <SortUp className={styles.sortIcon} />}
43
+ {sort.direction === SortDirection.Descending && <SortDown className={styles.sortIcon} />}
51
44
  </>
52
45
  )}
53
46
  </span>
@@ -3,7 +3,7 @@ import { CSSProperties, FocusEventHandler, MouseEventHandler, ReactElement, useR
3
3
 
4
4
  import { LOCALE_FEATURES } from 'i18n';
5
5
  import { noop } from 'lib';
6
- import { selectLocale, useTypedSelector } from 'state';
6
+ import { selectIsResultMatching, selectLocale, useTypedSelector } from 'state';
7
7
  import { ResultColumn } from 'types';
8
8
 
9
9
  import Cell from './Cell';
@@ -20,7 +20,7 @@ interface Props {
20
20
  const Result = ({ data, index, style }: Props): ReactElement => {
21
21
  const {
22
22
  highlightedIndex,
23
- results,
23
+ results = [],
24
24
  onBlur = noop,
25
25
  onClick = noop,
26
26
  onFocus = noop,
@@ -28,11 +28,12 @@ const Result = ({ data, index, style }: Props): ReactElement => {
28
28
  onMouseLeave = noop,
29
29
  } = data;
30
30
  const ref = useRef<HTMLButtonElement>(null);
31
+ const columns = useColumns();
31
32
  const locale = useTypedSelector(selectLocale);
32
33
  const { consonants, vowels } = LOCALE_FEATURES[locale];
33
34
  const result = results[index];
35
+ const isMatching = useTypedSelector((state) => selectIsResultMatching(state, index));
34
36
  const otherWords = result.words.slice(1).join(' / ').toLocaleUpperCase();
35
- const columns = useColumns();
36
37
  const enabledColumns = Object.fromEntries(columns.map((column) => [column.id, true]));
37
38
 
38
39
  const handleClick: MouseEventHandler = (event) => onClick(result, event);
@@ -46,6 +47,7 @@ const Result = ({ data, index, style }: Props): ReactElement => {
46
47
  aria-label={result.word}
47
48
  className={classNames(styles.result, {
48
49
  [styles.highlighted]: index === highlightedIndex,
50
+ [styles.notMatching]: !isMatching,
49
51
  })}
50
52
  ref={ref}
51
53
  style={style}
@@ -9,7 +9,7 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
9
9
  height: 100%;
10
10
  background: var(--color--background--element);
11
11
  border: var(--border);
12
- box-shadow: var(--box-shadow);
12
+ border-radius: var(--border--radius);
13
13
  font-family: var(--font--family--title);
14
14
  }
15
15
 
@@ -29,6 +29,9 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
29
29
  }
30
30
 
31
31
  .list {
32
+ @include scrollbars;
33
+
34
+ scrollbar-gutter: stable;
32
35
  transition: var(--transition);
33
36
 
34
37
  &.outdated {
@@ -44,6 +47,8 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
44
47
  justify-content: space-between;
45
48
  font-weight: 700;
46
49
  border-bottom: var(--border);
50
+ border-top-left-radius: inherit;
51
+ border-top-right-radius: inherit;
47
52
  }
48
53
 
49
54
  .headerButton {
@@ -60,6 +65,26 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
60
65
  background-color: var(--color--primary);
61
66
  color: var(--color--primary--opposite);
62
67
  }
68
+
69
+ &:first-child {
70
+ [dir='ltr'] & {
71
+ border-top-left-radius: inherit;
72
+ }
73
+
74
+ [dir='rtl'] & {
75
+ border-top-right-radius: inherit;
76
+ }
77
+ }
78
+
79
+ &:last-child {
80
+ [dir='ltr'] & {
81
+ border-top-right-radius: inherit;
82
+ }
83
+
84
+ [dir='rtl'] & {
85
+ border-top-left-radius: inherit;
86
+ }
87
+ }
63
88
  }
64
89
 
65
90
  .headerButtonLabel {
@@ -89,6 +114,19 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
89
114
  color: var(--color--primary--opposite);
90
115
  }
91
116
  }
117
+
118
+ &.notMatching {
119
+ color: var(--color--inactive);
120
+
121
+ &:focus,
122
+ &:hover,
123
+ &.highlighted {
124
+ &:not(:disabled) {
125
+ background-color: var(--color--primary--light);
126
+ color: var(--color--primary--opposite);
127
+ }
128
+ }
129
+ }
92
130
  }
93
131
 
94
132
  .resultContent {
@@ -175,4 +213,6 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
175
213
 
176
214
  .input {
177
215
  border-top: var(--border);
216
+ border-bottom-left-radius: var(--border--radius);
217
+ border-bottom-right-radius: var(--border--radius);
178
218
  }
@@ -9,9 +9,8 @@ import {
9
9
  selectAreResultsOutdated,
10
10
  selectIsLoading,
11
11
  selectLocale,
12
+ selectResults,
12
13
  selectSolveError,
13
- selectSortedFilteredResults,
14
- selectSortedResults,
15
14
  useTranslate,
16
15
  useTypedSelector,
17
16
  } from 'state';
@@ -38,9 +37,7 @@ const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIn
38
37
  const translate = useTranslate();
39
38
  const locale = useTypedSelector(selectLocale);
40
39
  const { direction } = LOCALE_FEATURES[locale];
41
- const allResults = useTypedSelector(selectSortedResults);
42
- const filteredResults = useTypedSelector(selectSortedFilteredResults);
43
- const results = filteredResults || [];
40
+ const results = useTypedSelector(selectResults);
44
41
  const isLoading = useTypedSelector(selectIsLoading);
45
42
  const isOutdated = useTypedSelector(selectAreResultsOutdated);
46
43
  const error = useTypedSelector(selectSolveError);
@@ -50,8 +47,7 @@ const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIn
50
47
  const columns = useColumns();
51
48
  const scrollToIndex = typeof highlightedIndex === 'number' ? highlightedIndex : 0;
52
49
  const scrollToIndexRef = useLatest(scrollToIndex);
53
- const hasResults =
54
- typeof error === 'undefined' && typeof filteredResults !== 'undefined' && typeof allResults !== 'undefined';
50
+ const hasResults = typeof error === 'undefined' && typeof results !== 'undefined';
55
51
 
56
52
  useEffect(() => {
57
53
  // without setTimeout, the initial scrolling offset is calculated
@@ -65,7 +61,7 @@ const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIn
65
61
  return () => {
66
62
  globalThis.clearTimeout(timeout);
67
63
  };
68
- }, [allResults, listRef, scrollToIndexRef]);
64
+ }, [results, listRef, scrollToIndexRef]);
69
65
 
70
66
  return (
71
67
  <div className={classNames(styles.results, className)}>
@@ -84,7 +80,7 @@ const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIn
84
80
  </EmptyState>
85
81
  )}
86
82
 
87
- {typeof error === 'undefined' && typeof filteredResults === 'undefined' && (
83
+ {typeof error === 'undefined' && typeof results === 'undefined' && (
88
84
  <EmptyState className={styles.emptyState} variant="info">
89
85
  {translate('results.empty-state.uninitialized')}
90
86
 
@@ -102,45 +98,35 @@ const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIn
102
98
  </EmptyState>
103
99
  )}
104
100
 
105
- {!isOutdated && (
106
- <>
107
- {allResults.length === 0 && (
108
- <EmptyState className={styles.emptyState} variant="warning">
109
- {translate('results.empty-state.no-results')}
110
- </EmptyState>
111
- )}
112
-
113
- {allResults.length > 0 && filteredResults.length === 0 && (
114
- <EmptyState className={styles.emptyState} variant="info">
115
- {translate('results.empty-state.no-filtered-results')}
116
- </EmptyState>
117
- )}
118
-
119
- {allResults.length > 0 && filteredResults.length > 0 && (
120
- <div className={styles.listContainer}>
121
- <FixedSizeList
122
- className={classNames(styles.list, {
123
- [styles.outdated]: isOutdated,
124
- })}
125
- direction={direction}
126
- height={height}
127
- itemCount={filteredResults.length}
128
- itemData={itemData}
129
- itemSize={RESULTS_ITEM_HEIGHT}
130
- ref={setListRef}
131
- width={width}
132
- >
133
- {Result}
134
- </FixedSizeList>
135
- </div>
136
- )}
137
- </>
101
+ {!isOutdated && results.length === 0 && (
102
+ <EmptyState className={styles.emptyState} variant="warning">
103
+ {translate('results.empty-state.no-results')}
104
+ </EmptyState>
105
+ )}
106
+
107
+ {!isOutdated && results.length > 0 && (
108
+ <div className={styles.listContainer}>
109
+ <FixedSizeList
110
+ className={classNames(styles.list, {
111
+ [styles.outdated]: isOutdated,
112
+ })}
113
+ direction={direction}
114
+ height={height}
115
+ itemCount={results.length}
116
+ itemData={itemData}
117
+ itemSize={RESULTS_ITEM_HEIGHT}
118
+ ref={setListRef}
119
+ width={width}
120
+ >
121
+ {Result}
122
+ </FixedSizeList>
123
+ </div>
138
124
  )}
139
125
  </>
140
126
  )}
141
127
  </div>
142
128
 
143
- {hasResults && allResults.length > 0 && !isOutdated && <ResultsInput className={styles.input} />}
129
+ {hasResults && results.length > 0 && !isOutdated && <ResultsInput className={styles.input} />}
144
130
 
145
131
  {isLoading && <Loading />}
146
132
  </div>
@@ -19,5 +19,5 @@ export interface ResultCallbacks {
19
19
 
20
20
  export interface ResultData extends ResultCallbacks {
21
21
  highlightedIndex?: number;
22
- results: Result[];
22
+ results: Result[] | undefined;
23
23
  }
@@ -8,4 +8,5 @@
8
8
  @include text-input;
9
9
 
10
10
  border: none;
11
+ border-radius: inherit;
11
12
  }
@@ -63,6 +63,10 @@
63
63
  }
64
64
  }
65
65
 
66
+ .results {
67
+ box-shadow: var(--box-shadow);
68
+ }
69
+
66
70
  .dictionaryContainer {
67
71
  flex: 0 0 auto;
68
72
  height: var(--dictionary--height);
@@ -70,16 +74,21 @@
70
74
  flex-direction: column;
71
75
  background-color: var(--color--background--element);
72
76
  border: var(--border);
77
+ border-radius: var(--border--radius);
73
78
  box-shadow: var(--box-shadow);
74
79
  }
75
80
 
76
81
  .dictionary {
77
82
  flex: 1;
78
83
  border-bottom: var(--border);
84
+ border-top-left-radius: var(--border--radius);
85
+ border-top-right-radius: var(--border--radius);
79
86
  }
80
87
 
81
88
  .dictionaryInput {
82
89
  flex: 0 0 auto;
90
+ border-bottom-left-radius: var(--border--radius);
91
+ border-bottom-right-radius: var(--border--radius);
83
92
  }
84
93
 
85
94
  .bottomContainer {
@@ -97,10 +106,6 @@
97
106
  min-width: 0;
98
107
  }
99
108
 
100
- .rack {
101
- border: var(--border);
102
- }
103
-
104
109
  .controls {
105
110
  width: 100%;
106
111
  }