@scrabble-solver/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 (137) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +7 -7
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/eslint/.cache_8dgz12 +1 -1
  5. package/.next/cache/next-server.js.nft.json +1 -1
  6. package/.next/cache/webpack/client-production/0.pack +0 -0
  7. package/.next/cache/webpack/client-production/index.pack +0 -0
  8. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  9. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  10. package/.next/cache/webpack/server-production/0.pack +0 -0
  11. package/.next/cache/webpack/server-production/index.pack +0 -0
  12. package/.next/next-server.js.nft.json +1 -1
  13. package/.next/prerender-manifest.json +1 -1
  14. package/.next/routes-manifest.json +1 -1
  15. package/.next/server/chunks/277.js +523 -611
  16. package/.next/server/middleware-build-manifest.js +1 -1
  17. package/.next/server/pages/404.html +2 -2
  18. package/.next/server/pages/404.js.nft.json +1 -1
  19. package/.next/server/pages/500.html +1 -1
  20. package/.next/server/pages/_app.js +0 -24
  21. package/.next/server/pages/_app.js.nft.json +1 -1
  22. package/.next/server/pages/_document.js.nft.json +1 -1
  23. package/.next/server/pages/api/dictionary/[locale]/[word].js +0 -1
  24. package/.next/server/pages/api/dictionary/[locale].js +0 -1
  25. package/.next/server/pages/api/solve.js +28 -8
  26. package/.next/server/pages/api/verify.js +0 -1
  27. package/.next/server/pages/api/visit.js +0 -1
  28. package/.next/server/pages/index.html +1 -1
  29. package/.next/server/pages/index.js +49 -90
  30. package/.next/server/pages/index.js.nft.json +1 -1
  31. package/.next/server/pages/index.json +1 -1
  32. package/.next/static/Mdvi3FY0PqkILKLbPlVBU/_buildManifest.js +1 -0
  33. package/.next/static/chunks/pages/{404-e0f30450e9920dc3.js → 404-448ba28510855455.js} +1 -1
  34. package/.next/static/chunks/pages/_app-495e6f4ccc278bb2.js +28 -0
  35. package/.next/static/chunks/pages/index-5ecc51900ca29685.js +1 -0
  36. package/.next/static/css/17b0a2db8742105f.css +1 -0
  37. package/.next/static/css/e1ffeb2558330c55.css +2 -0
  38. package/.next/trace +54 -55
  39. package/package.json +9 -14
  40. package/src/components/{Solver/components/EmptyState/EmptyState.module.scss → Alert/Alert.module.scss} +11 -7
  41. package/src/components/{Solver/components/EmptyState/EmptyState.tsx → Alert/Alert.tsx} +8 -6
  42. package/src/components/Alert/index.ts +1 -0
  43. package/src/components/Board/Board.module.scss +55 -0
  44. package/src/components/Board/BoardPure.tsx +4 -0
  45. package/src/components/Board/components/Cell/Cell.module.scss +51 -2
  46. package/src/components/Board/components/Cell/Cell.tsx +12 -0
  47. package/src/components/Board/components/Cell/CellPure.tsx +12 -0
  48. package/src/components/Board/hooks/useGrid.ts +9 -26
  49. package/src/components/Board/lib/getPositionInGrid.ts +1 -1
  50. package/src/components/Dictionary/Dictionary.module.scss +4 -0
  51. package/src/components/DictionaryInput/DictionaryInput.module.scss +1 -0
  52. package/src/components/EmptyState/EmptyState.module.scss +2 -1
  53. package/src/components/EmptyState/EmptyState.tsx +1 -2
  54. package/src/components/Loading/Loading.module.scss +1 -1
  55. package/src/components/Logo/Logo.tsx +5 -0
  56. package/src/components/LogoSplashScreen/LogoSplashScreen.module.scss +4 -1
  57. package/src/components/Modal/Modal.module.scss +14 -0
  58. package/src/components/Modal/Modal.tsx +4 -1
  59. package/src/components/PlainTiles/PlainTiles.module.scss +1 -1
  60. package/src/components/PlainTiles/Tile.tsx +3 -3
  61. package/src/components/Rack/Rack.module.scss +25 -0
  62. package/src/components/Rack/Rack.tsx +5 -4
  63. package/src/components/Rack/RackTile.tsx +6 -13
  64. package/src/components/Results/HeaderButton.tsx +5 -12
  65. package/src/components/Results/Result.tsx +5 -3
  66. package/src/components/Results/Results.module.scss +41 -1
  67. package/src/components/Results/Results.tsx +29 -43
  68. package/src/components/Results/types.ts +1 -1
  69. package/src/components/ResultsInput/ResultsInput.module.scss +1 -0
  70. package/src/components/Solver/Solver.module.scss +9 -4
  71. package/src/components/Solver/Solver.tsx +17 -19
  72. package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +5 -6
  73. package/src/components/Solver/components/index.ts +0 -1
  74. package/src/components/Tile/Tile.module.scss +42 -61
  75. package/src/components/Tile/Tile.tsx +4 -3
  76. package/src/components/Tile/TilePure.tsx +4 -13
  77. package/src/components/Tooltip/Tooltip.module.scss +1 -72
  78. package/src/components/Tooltip/useTooltip.tsx +25 -35
  79. package/src/components/index.ts +1 -0
  80. package/src/hooks/index.ts +0 -1
  81. package/src/hooks/useAppLayout.ts +3 -1
  82. package/src/i18n/de.json +0 -1
  83. package/src/i18n/en.json +0 -1
  84. package/src/i18n/es.json +0 -1
  85. package/src/i18n/fa.json +0 -1
  86. package/src/i18n/fr.json +0 -1
  87. package/src/i18n/pl.json +0 -1
  88. package/src/lib/createRegExp.ts +13 -0
  89. package/src/lib/groupResults.ts +38 -0
  90. package/src/lib/guessLocale.ts +22 -0
  91. package/src/lib/index.ts +4 -2
  92. package/src/lib/sortResults.ts +6 -10
  93. package/src/modals/DictionaryModal/DictionaryModal.module.scss +6 -2
  94. package/src/modals/KeyMapModal/KeyMapModal.tsx +5 -21
  95. package/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +4 -0
  96. package/src/modals/MenuModal/MenuModal.module.scss +7 -0
  97. package/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -0
  98. package/src/modals/ResultsModal/ResultsModal.module.scss +3 -3
  99. package/src/modals/ResultsModal/ResultsModal.tsx +23 -11
  100. package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +2 -0
  101. package/src/modals/WordsModal/WordsModal.module.scss +8 -1
  102. package/src/pages/api/dictionary/[locale]/[word].ts +0 -1
  103. package/src/pages/api/dictionary/[locale]/index.ts +0 -1
  104. package/src/pages/api/solve.ts +0 -1
  105. package/src/pages/api/verify.ts +0 -1
  106. package/src/pages/api/visit.ts +0 -1
  107. package/src/pages/index.tsx +13 -15
  108. package/src/parameters/index.ts +2 -2
  109. package/src/state/createAppStore.ts +26 -10
  110. package/src/state/sagas.ts +1 -0
  111. package/src/state/selectors.ts +37 -37
  112. package/src/state/slices/boardInitialState.ts +3 -1
  113. package/src/state/slices/cellFilterInitialState.ts +3 -3
  114. package/src/state/slices/cellFilterSlice.ts +3 -1
  115. package/src/state/slices/dictionaryInitialState.ts +2 -2
  116. package/src/state/slices/rackInitialState.ts +3 -1
  117. package/src/state/slices/resultsInitialState.ts +11 -4
  118. package/src/state/slices/settingsInitialState.ts +10 -21
  119. package/src/state/slices/solveInitialState.ts +2 -2
  120. package/src/state/slices/solveSlice.ts +2 -0
  121. package/src/state/slices/verifyInitialState.ts +13 -4
  122. package/src/state/types.ts +20 -2
  123. package/src/styles/mixins.scss +25 -1
  124. package/src/styles/variables.scss +17 -1
  125. package/src/types/index.ts +10 -2
  126. package/.next/static/45ye7793DY705HOcuK9lJ/_buildManifest.js +0 -1
  127. package/.next/static/chunks/pages/_app-d7acee5e526752d9.js +0 -28
  128. package/.next/static/chunks/pages/index-35d2c1c79a201ae2.js +0 -1
  129. package/.next/static/css/a48caa6f57de6e98.css +0 -1
  130. package/.next/static/css/c49bbe944ddd1b39.css +0 -2
  131. package/src/components/Board/types/index.ts +0 -4
  132. package/src/components/Solver/components/EmptyState/index.ts +0 -1
  133. package/src/components/Tooltip/constants.ts +0 -28
  134. package/src/hooks/useUniqueId.ts +0 -9
  135. package/src/lib/isCtrl.ts +0 -7
  136. package/src/state/rootReducer.ts +0 -25
  137. /package/.next/static/{45ye7793DY705HOcuK9lJ → Mdvi3FY0PqkILKLbPlVBU}/_ssgManifest.js +0 -0
@@ -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
  }
@@ -11,21 +11,21 @@ import {
11
11
  selectAreResultsOutdated,
12
12
  selectConfig,
13
13
  selectResultCandidate,
14
+ selectResults,
14
15
  selectSolveError,
15
- selectSortedFilteredResults,
16
- selectSortedResults,
17
16
  solveSlice,
18
17
  useTranslate,
19
18
  useTypedSelector,
20
19
  } from 'state';
21
20
 
21
+ import Alert from '../Alert';
22
22
  import Board from '../Board';
23
23
  import Dictionary from '../Dictionary';
24
24
  import DictionaryInput from '../DictionaryInput';
25
25
  import Rack from '../Rack';
26
26
  import Results from '../Results';
27
27
 
28
- import { EmptyState, FloatingSolveButton, ResultCandidatePicker } from './components';
28
+ import { FloatingSolveButton, ResultCandidatePicker } from './components';
29
29
  import styles from './Solver.module.scss';
30
30
 
31
31
  interface Props {
@@ -40,18 +40,16 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
40
40
  const dispatch = useDispatch();
41
41
  const translate = useTranslate();
42
42
  const isTouchDevice = useIsTouchDevice();
43
- const { componentsSpacing, isBoardFullWidth, showColumn, showCompactControls, showFloatingSolveButton } =
43
+ const { columnWidth, componentsSpacing, isBoardFullWidth, showColumn, showCompactControls, showFloatingSolveButton } =
44
44
  useAppLayout();
45
- const [bottomContainerRef, { height: bottomContainerHeight }] = useMeasure<HTMLDivElement>();
46
- const [columnRef, { width: columnWidth }] = useMeasure<HTMLDivElement>();
47
- const maxBoardWidth = width - columnWidth - (showColumn ? componentsSpacing : 0) - 2 * componentsSpacing;
48
- const maxBoardHeight = isBoardFullWidth ? Number.POSITIVE_INFINITY : Math.max(height - bottomContainerHeight, 0);
49
45
  const config = useTypedSelector(selectConfig);
50
46
  const error = useTypedSelector(selectSolveError);
51
47
  const isOutdated = useTypedSelector(selectAreResultsOutdated);
52
48
  const resultCandidate = useTypedSelector(selectResultCandidate);
53
- const allResults = useTypedSelector(selectSortedResults);
54
- const results = useTypedSelector(selectSortedFilteredResults);
49
+ const results = useTypedSelector(selectResults);
50
+ const [bottomContainerRef, { height: bottomContainerHeight }] = useMeasure<HTMLDivElement>();
51
+ const maxBoardWidth = width - columnWidth - (showColumn ? componentsSpacing : 0) - 2 * componentsSpacing;
52
+ const maxBoardHeight = isBoardFullWidth ? Number.POSITIVE_INFINITY : Math.max(height - bottomContainerHeight, 0);
55
53
  const [bestResult] = results || [];
56
54
  const cellWidth = (maxBoardWidth - (config.boardWidth + 1) * BORDER_WIDTH) / config.boardWidth;
57
55
  const cellHeight = (maxBoardHeight - (config.boardHeight + 1) * BORDER_WIDTH) / config.boardHeight;
@@ -111,13 +109,13 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
111
109
  <div className={classNames(styles.solver, className)}>
112
110
  <div className={styles.container}>
113
111
  <div className={styles.content}>
114
- <form id="a" className={styles.boardContainer} onSubmit={handleSubmit}>
112
+ <form className={styles.boardContainer} onSubmit={handleSubmit}>
115
113
  <Board cellSize={cellSizeSafe} className={styles.board} />
116
114
  <input className={styles.submitInput} tabIndex={-1} type="submit" />
117
115
  </form>
118
116
 
119
- <div className={styles.column} ref={columnRef}>
120
- <Results callbacks={callbacks} />
117
+ <div className={styles.column}>
118
+ <Results callbacks={callbacks} className={styles.results} />
121
119
 
122
120
  <div className={styles.dictionaryContainer}>
123
121
  <Dictionary className={styles.dictionary} />
@@ -129,7 +127,7 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
129
127
 
130
128
  <div className={styles.bottomContainer} ref={bottomContainerRef}>
131
129
  <div className={styles.bottomContent}>
132
- <form id="b" onSubmit={handleSubmit}>
130
+ <form onSubmit={handleSubmit}>
133
131
  <Rack className={styles.rack} tileSize={tileSize} />
134
132
  <input className={styles.submitInput} tabIndex={-1} type="submit" />
135
133
  </form>
@@ -139,15 +137,15 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
139
137
  <ResultCandidatePicker onResultClick={onShowResults} />
140
138
 
141
139
  {error && (
142
- <EmptyState className={styles.emptyState} variant="error">
140
+ <Alert className={styles.emptyState} variant="error">
143
141
  {error.message}
144
- </EmptyState>
142
+ </Alert>
145
143
  )}
146
144
 
147
- {allResults && allResults.length === 0 && !isOutdated && (
148
- <EmptyState className={styles.emptyState} variant="warning">
145
+ {results && results.length === 0 && !isOutdated && (
146
+ <Alert className={styles.emptyState} variant="warning">
149
147
  {translate('results.empty-state.no-results')}
150
- </EmptyState>
148
+ </Alert>
151
149
  )}
152
150
  </div>
153
151
  )}
@@ -10,7 +10,7 @@ import {
10
10
  selectIsLoading,
11
11
  selectLocale,
12
12
  selectResultCandidate,
13
- selectSortedResults,
13
+ selectResults,
14
14
  useTranslate,
15
15
  useTypedSelector,
16
16
  } from 'state';
@@ -31,13 +31,12 @@ const ResultCandidatePicker: FunctionComponent<Props> = ({ className, onResultCl
31
31
  const locale = useTypedSelector(selectLocale);
32
32
  const isLoading = useTypedSelector(selectIsLoading);
33
33
  const isOutdated = useTypedSelector(selectAreResultsOutdated);
34
- const sortedResults = useTypedSelector(selectSortedResults);
35
- const results = sortedResults || [];
34
+ const results = useTypedSelector(selectResults);
36
35
  const resultCandidate = useTypedSelector(selectResultCandidate);
37
- const index = resultCandidate ? results.findIndex((result) => result.id === resultCandidate.id) : -1;
36
+ const index = resultCandidate && results ? results.findIndex((result) => result.id === resultCandidate.id) : -1;
38
37
  const disabled = isOutdated || !resultCandidate;
39
- const isPreviousDisabled = index <= 0 || disabled;
40
- const isNextDisabled = index >= results.length - 1 || disabled;
38
+ const isPreviousDisabled = !results || index <= 0 || disabled;
39
+ const isNextDisabled = !results || index >= results.length - 1 || disabled;
41
40
  const bothEnabled = !isPreviousDisabled && !isNextDisabled;
42
41
  const { showFloatingSolveButton } = useAppLayout();
43
42
 
@@ -1,4 +1,3 @@
1
- export { default as EmptyState } from './EmptyState';
2
1
  export { default as FloatingSolveButton } from './FloatingSolveButton';
3
2
  export { default as InsertButton } from './InsertButton';
4
3
  export { default as ResultCandidatePicker } from './ResultCandidatePicker';