@scrabble-solver/scrabble-solver 2.10.1 → 2.10.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 (135) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +19 -31
  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-development/0.pack +0 -0
  7. package/.next/cache/webpack/client-development/1.pack +0 -0
  8. package/.next/cache/webpack/client-development/2.pack +0 -0
  9. package/.next/cache/webpack/client-development/3.pack +0 -0
  10. package/.next/cache/webpack/client-development/4.pack +0 -0
  11. package/.next/cache/webpack/client-development/index.pack +0 -0
  12. package/.next/cache/webpack/client-development/index.pack.old +0 -0
  13. package/.next/cache/webpack/client-production/0.pack +0 -0
  14. package/.next/cache/webpack/client-production/index.pack +0 -0
  15. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  16. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  17. package/.next/cache/webpack/server-development/0.pack +0 -0
  18. package/.next/cache/webpack/server-development/10.pack_ +0 -0
  19. package/.next/cache/webpack/server-development/5.pack_ +0 -0
  20. package/.next/cache/webpack/server-development/8.pack_ +0 -0
  21. package/.next/cache/webpack/server-development/index.pack +0 -0
  22. package/.next/cache/webpack/server-production/0.pack +0 -0
  23. package/.next/cache/webpack/server-production/index.pack +0 -0
  24. package/.next/images-manifest.json +1 -1
  25. package/.next/next-server.js.nft.json +1 -1
  26. package/.next/prerender-manifest.json +1 -1
  27. package/.next/required-server-files.json +1 -1
  28. package/.next/routes-manifest.json +1 -1
  29. package/.next/server/chunks/131.js +1399 -117
  30. package/.next/server/chunks/{515.js → 176.js} +771 -679
  31. package/.next/server/chunks/210.js +122 -0
  32. package/.next/server/chunks/{939.js → 290.js} +3 -3
  33. package/.next/server/chunks/44.js +33 -33
  34. package/.next/server/chunks/50.js +5 -9
  35. package/.next/server/chunks/{413.js → 579.js} +136 -87
  36. package/.next/server/chunks/664.js +537 -350
  37. package/.next/server/chunks/859.js +56 -23
  38. package/.next/server/chunks/865.js +1 -1
  39. package/.next/server/middleware-build-manifest.js +1 -1
  40. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  41. package/.next/server/pages/404.html +2 -2
  42. package/.next/server/pages/404.js.nft.json +1 -1
  43. package/.next/server/pages/500.html +2 -2
  44. package/.next/server/pages/_app.js +23 -5
  45. package/.next/server/pages/_app.js.nft.json +1 -1
  46. package/.next/server/pages/_document.js +2 -2
  47. package/.next/server/pages/_error.js +206 -25
  48. package/.next/server/pages/_error.js.nft.json +1 -1
  49. package/.next/server/pages/api/dictionary/[locale]/[word].js +10 -10
  50. package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
  51. package/.next/server/pages/api/dictionary/[locale].js +10 -10
  52. package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
  53. package/.next/server/pages/api/solve.js +14 -13
  54. package/.next/server/pages/api/solve.js.nft.json +1 -1
  55. package/.next/server/pages/api/verify.js +10 -10
  56. package/.next/server/pages/api/verify.js.nft.json +1 -1
  57. package/.next/server/pages/api/visit.js +3 -3
  58. package/.next/server/pages/api/visit.js.nft.json +1 -1
  59. package/.next/server/pages/index.html +2 -2
  60. package/.next/server/pages/index.js +44 -128
  61. package/.next/server/pages/index.js.nft.json +1 -1
  62. package/.next/server/pages/index.json +1 -1
  63. package/.next/server/pages-manifest.json +3 -3
  64. package/.next/static/FjrbXpI5fkt4lmko-vL_7/_buildManifest.js +1 -0
  65. package/.next/static/{hf94cues-LcXZRCpAzQ6w → FjrbXpI5fkt4lmko-vL_7}/_ssgManifest.js +0 -0
  66. package/.next/static/chunks/361-d16f336a9752a55a.js +1 -0
  67. package/.next/static/chunks/724-eb48df4d1ba3df8b.js +5 -0
  68. package/.next/static/chunks/amp.js +720 -0
  69. package/.next/static/chunks/framework-2c79e2a64abdb08b.js +33 -0
  70. package/.next/static/chunks/main-f11614d8aa7ee555.js +1 -0
  71. package/.next/static/chunks/main.js +1076 -0
  72. package/.next/static/chunks/pages/404-24f9617eeb8d6dc1.js +1 -0
  73. package/.next/static/chunks/pages/_app-959e495f0f221247.js +24 -0
  74. package/.next/static/chunks/pages/_app.js +2121 -0
  75. package/.next/static/chunks/pages/_error-8353112a01355ec2.js +1 -0
  76. package/.next/static/chunks/pages/_error.js +28 -0
  77. package/.next/static/chunks/pages/index-1e30dafa41bddb80.js +1 -0
  78. package/.next/static/chunks/pages/index.js +5314 -0
  79. package/.next/static/chunks/react-refresh.js +62 -0
  80. package/.next/static/chunks/webpack-59c5c889f52620d6.js +1 -0
  81. package/.next/static/chunks/webpack.js +1237 -0
  82. package/.next/static/css/aafd07997120f1e4.css +1 -0
  83. package/.next/static/css/c8d26240c04079b9.css +1 -0
  84. package/.next/static/css/eb9d57f7103525ab.css +1 -0
  85. package/.next/static/development/_buildManifest.js +1 -0
  86. package/.next/static/development/_ssgManifest.js +1 -0
  87. package/.next/static/webpack/fb4b50d5e70ee127.webpack.hot-update.json +1 -0
  88. package/.next/static/webpack/pages/index.fb4b50d5e70ee127.hot-update.js +171 -0
  89. package/.next/static/webpack/webpack.fb4b50d5e70ee127.hot-update.js +18 -0
  90. package/.next/trace +52 -52
  91. package/package.json +21 -21
  92. package/public/og.png +0 -0
  93. package/src/components/Board/Board.module.scss +1 -2
  94. package/src/components/Board/BoardPure.tsx +2 -0
  95. package/src/components/Board/components/Cell/Cell.module.scss +65 -55
  96. package/src/components/Board/components/Cell/Cell.tsx +9 -0
  97. package/src/components/Board/components/Cell/CellPure.tsx +11 -2
  98. package/src/components/NavButtons/NavButtons.tsx +11 -1
  99. package/src/components/Rack/RackTile.tsx +10 -1
  100. package/src/components/RemainingTiles/Character.module.scss +0 -1
  101. package/src/components/RemainingTiles/Character.tsx +1 -0
  102. package/src/components/Solver/Solver.module.scss +85 -0
  103. package/src/components/Solver/Solver.tsx +75 -0
  104. package/src/components/Solver/index.ts +1 -0
  105. package/src/components/Tile/Tile.module.scss +51 -7
  106. package/src/components/Tile/Tile.tsx +3 -0
  107. package/src/components/Tile/TilePure.tsx +8 -1
  108. package/src/components/index.ts +1 -0
  109. package/src/hooks/index.ts +1 -0
  110. package/src/hooks/useIsTouchDevice.ts +7 -0
  111. package/src/icons/ExclamationSquareFill.svg +4 -0
  112. package/src/icons/index.ts +1 -0
  113. package/src/pages/index.module.scss +1 -75
  114. package/src/pages/index.tsx +11 -73
  115. package/src/parameters/index.ts +2 -0
  116. package/src/service-worker/average.ts +9 -0
  117. package/src/service-worker/routeSolveRequests.ts +46 -7
  118. package/src/state/selectors.ts +20 -0
  119. package/src/state/slices/boardInitialState.ts +27 -0
  120. package/src/styles/mixins.scss +6 -1
  121. package/src/styles/variables.scss +6 -3
  122. package/.next/InjectManifest.js.nft.json +0 -1
  123. package/.next/static/chunks/368-8b386c3106556f62.js +0 -1
  124. package/.next/static/chunks/546-447e243fc9de2c59.js +0 -1
  125. package/.next/static/chunks/framework-4556c45dd113b893.js +0 -1
  126. package/.next/static/chunks/main-a75cf611e061d8f8.js +0 -1
  127. package/.next/static/chunks/pages/404-932294135c3206dd.js +0 -1
  128. package/.next/static/chunks/pages/_app-8f0df20f771045ed.js +0 -1
  129. package/.next/static/chunks/pages/_error-a4ba2246ff8fb532.js +0 -1
  130. package/.next/static/chunks/pages/index-8af7a9d7a2cd98a7.js +0 -1
  131. package/.next/static/chunks/webpack-5752944655d749a0.js +0 -1
  132. package/.next/static/css/6b1833fd19d3a74a.css +0 -1
  133. package/.next/static/css/a6154e4ca046ca13.css +0 -1
  134. package/.next/static/css/bad53af6f8616677.css +0 -1
  135. package/.next/static/hf94cues-LcXZRCpAzQ6w/_buildManifest.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/scrabble-solver",
3
- "version": "2.10.1",
3
+ "version": "2.10.2",
4
4
  "description": "Scrabble Solver 2 - App",
5
5
  "engines": {
6
6
  "node": ">=16"
@@ -30,27 +30,27 @@
30
30
  "dependencies": {
31
31
  "@kamilmielnik/trie": "^2.0.1",
32
32
  "@popperjs/core": "^2.11.6",
33
- "@reduxjs/toolkit": "^1.8.6",
34
- "@scrabble-solver/configs": "^2.10.1",
35
- "@scrabble-solver/constants": "^2.10.1",
36
- "@scrabble-solver/dictionaries": "^2.10.1",
37
- "@scrabble-solver/logger": "^2.10.1",
38
- "@scrabble-solver/solver": "^2.10.1",
39
- "@scrabble-solver/types": "^2.10.1",
40
- "@scrabble-solver/word-definitions": "^2.10.1",
33
+ "@reduxjs/toolkit": "^1.9.2",
34
+ "@scrabble-solver/configs": "^2.10.2",
35
+ "@scrabble-solver/constants": "^2.10.2",
36
+ "@scrabble-solver/dictionaries": "^2.10.2",
37
+ "@scrabble-solver/logger": "^2.10.2",
38
+ "@scrabble-solver/solver": "^2.10.2",
39
+ "@scrabble-solver/types": "^2.10.2",
40
+ "@scrabble-solver/word-definitions": "^2.10.2",
41
41
  "classnames": "^2.3.2",
42
- "next": "^12.3.1",
42
+ "next": "^13.1.6",
43
43
  "normalize.css": "^8.0.1",
44
44
  "react": "^18.2.0",
45
45
  "react-dom": "^18.2.0",
46
46
  "react-modal": "^3.16.1",
47
47
  "react-popper": "^2.3.0",
48
48
  "react-portal": "^4.2.2",
49
- "react-redux": "^8.0.4",
49
+ "react-redux": "^8.0.5",
50
50
  "react-use": "^17.4.0",
51
- "react-window": "^1.8.7",
52
- "redux": "^4.2.0",
53
- "redux-saga": "^1.2.1",
51
+ "react-window": "^1.8.8",
52
+ "redux": "^4.2.1",
53
+ "redux-saga": "^1.2.2",
54
54
  "store2": "^2.14.2",
55
55
  "uuid": "^9.0.0",
56
56
  "workbox-expiration": "^6.5.4",
@@ -59,20 +59,20 @@
59
59
  "workbox-window": "^6.5.4"
60
60
  },
61
61
  "devDependencies": {
62
- "@svgr/webpack": "^6.5.0",
62
+ "@svgr/webpack": "^6.5.1",
63
63
  "@types/classnames": "^2.3.0",
64
- "@types/react": "^18.0.21",
65
- "@types/react-dom": "^18.0.6",
64
+ "@types/react": "^18.0.27",
65
+ "@types/react-dom": "^18.0.10",
66
66
  "@types/react-modal": "^3.13.1",
67
67
  "@types/react-portal": "^4.0.4",
68
- "@types/react-redux": "^7.1.24",
68
+ "@types/react-redux": "^7.1.25",
69
69
  "@types/react-window": "^1.8.5",
70
70
  "@types/redux": "^3.6.31",
71
71
  "@types/redux-saga": "^0.10.5",
72
- "@types/uuid": "^8.3.4",
72
+ "@types/uuid": "^9.0.0",
73
73
  "env-cmd": "^10.1.0",
74
- "sass": "^1.55.0",
74
+ "sass": "^1.57.1",
75
75
  "workbox-webpack-plugin": "^6.5.4"
76
76
  },
77
- "gitHead": "506586e8bb9e71f9f784dabffae2229c587c5e5a"
77
+ "gitHead": "4feaabfad2a10079c28e04f446c98dbf0383d520"
78
78
  }
package/public/og.png CHANGED
Binary file
@@ -1,8 +1,7 @@
1
1
  .board {
2
2
  display: table;
3
3
  box-shadow: var(--box-shadow);
4
- border-top: var(--border);
5
- border-left: var(--border);
4
+ border: var(--border);
6
5
  }
7
6
 
8
7
  .row {
@@ -53,7 +53,9 @@ const BoardPure: FunctionComponent<Props> = ({
53
53
  cell={cell}
54
54
  direction={direction}
55
55
  inputRef={refs[y][x]}
56
+ isBottom={y === rows.length - 1}
56
57
  isCenter={center.x === x && center.y === y}
58
+ isRight={x === cells.length - 1}
57
59
  key={x}
58
60
  size={cellSize}
59
61
  onChange={onChange}
@@ -8,104 +8,102 @@ $icon-size: 16px;
8
8
  position: relative;
9
9
  display: table-cell;
10
10
  background-color: white;
11
- border-right: var(--border);
12
- border-bottom: var(--border);
11
+ border-right: var(--border--width) dotted var(--border--color--light);
12
+ border-bottom: var(--border--width) dotted var(--border--color--light);
13
13
  transition: var(--transition);
14
14
  background-clip: padding-box;
15
15
 
16
- &.bonusWord2,
17
- &.bonusWord3 {
18
- position: relative;
16
+ &.bottom {
17
+ border-bottom: none;
18
+ }
19
19
 
20
- &:before {
21
- display: flex;
22
- justify-content: center;
23
- align-items: center;
24
- position: absolute;
25
- top: 0;
26
- bottom: 0;
27
- left: 0;
28
- right: 0;
29
- font-weight: bold;
30
- pointer-events: none;
31
- }
20
+ &.right {
21
+ border-right: none;
32
22
  }
33
23
 
34
24
  &.bonusStart {
35
- background-color: var(--color--violet--light);
25
+ &:before {
26
+ background-color: var(--color--violet--light);
27
+ }
36
28
  }
37
29
 
38
30
  &.bonusCharacter1 {
39
- background-color: var(--color--yellow--light);
31
+ &:before {
32
+ background-color: var(--color--yellow--light);
33
+ }
40
34
  }
41
35
 
42
36
  &.bonusCharacter2 {
43
- background-color: var(--color--green--light);
37
+ &:before {
38
+ background-color: var(--color--green--light);
39
+ }
44
40
  }
45
41
 
46
42
  &.bonusCharacter3 {
47
- background-color: var(--color--blue--light);
43
+ &:before {
44
+ background-color: var(--color--blue--light);
45
+ }
48
46
  }
49
47
 
50
48
  &.bonusCharacter5 {
51
- background-color: var(--color--red--light);
49
+ &:before {
50
+ background-color: var(--color--red--light);
51
+ }
52
52
  }
53
53
 
54
54
  &.bonusCharacterMultiplier2 {
55
- background-color: var(--color--light-blue--light);
55
+ &:before {
56
+ background-color: var(--color--light-blue--light);
57
+ }
56
58
  }
57
59
 
58
60
  &.bonusCharacterMultiplier3 {
59
- background-color: var(--color--dark-blue--light);
60
- }
61
-
62
- &.bonusWord2,
63
- &.bonusWord3 {
64
- background-color: var(--color--inactive);
65
-
66
61
  &:before {
67
- font-size: 75%;
68
- color: white;
69
-
70
- [lang='fa-IR'] & {
71
- font-family: 'Open Sans', sans-serif;
72
- }
62
+ background-color: var(--color--dark-blue--light);
73
63
  }
74
64
  }
75
65
 
76
66
  &.bonusWord2 {
77
67
  &:before {
78
68
  content: 'x2';
69
+ background-color: var(--color--orange);
79
70
  }
80
71
  }
81
72
 
82
73
  &.bonusWord3 {
83
74
  &:before {
84
75
  content: 'x3';
76
+ background-color: var(--color--pink);
85
77
  }
86
78
  }
87
79
 
88
- &.characterPoints1 {
89
- background-color: var(--color--yellow);
90
- }
91
-
92
- &.characterPoints2 {
93
- background-color: var(--color--green);
94
- }
95
-
96
- &.characterPoints3 {
97
- background-color: var(--color--blue);
98
- }
99
-
100
- &.characterPoints5 {
101
- background-color: var(--color--red);
80
+ &.candidate {
81
+ &:before {
82
+ content: ' ';
83
+ background-color: var(--color--primary);
84
+ }
102
85
  }
103
86
 
104
- &.candidate {
105
- background-color: var(--color--primary);
87
+ &:before {
88
+ $size: 80%;
89
+
90
+ display: flex;
91
+ justify-content: center;
92
+ align-items: center;
93
+ position: absolute;
94
+ top: (100% - $size) / 2;
95
+ left: (100% - $size) / 2;
96
+ width: $size;
97
+ height: $size;
98
+ border-radius: var(--border--radius);
99
+ pointer-events: none;
100
+ font-size: 60%;
101
+ font-weight: bold;
102
+ color: white;
103
+ content: ' ';
106
104
 
107
- &:before {
108
- display: none;
105
+ [lang='fa-IR'] & {
106
+ font-family: 'Open Sans', sans-serif;
109
107
  }
110
108
  }
111
109
 
@@ -207,6 +205,8 @@ $icon-size: 16px;
207
205
  }
208
206
 
209
207
  .toggleDirection {
208
+ transition: var(--transition);
209
+
210
210
  &.right {
211
211
  transform: rotate(-90deg);
212
212
 
@@ -249,9 +249,19 @@ $icon-size: 16px;
249
249
 
250
250
  .flag,
251
251
  .star {
252
+ color: white;
253
+ }
254
+
255
+ .star {
256
+ $size: 40%;
257
+
258
+ width: $size;
259
+ height: $size;
260
+ }
261
+
262
+ .flag {
252
263
  $size: 50%;
253
264
 
254
265
  width: $size;
255
266
  height: $size;
256
- color: white;
257
267
  }
@@ -9,6 +9,7 @@ import {
9
9
  cellFilterSlice,
10
10
  selectCellBonus,
11
11
  selectCellIsFiltered,
12
+ selectCellIsValid,
12
13
  selectTilePoints,
13
14
  useTranslate,
14
15
  useTypedSelector,
@@ -21,7 +22,9 @@ interface Props {
21
22
  className?: string;
22
23
  direction: 'horizontal' | 'vertical';
23
24
  inputRef: RefObject<HTMLInputElement>;
25
+ isBottom: boolean;
24
26
  isCenter: boolean;
27
+ isRight: boolean;
25
28
  size: number;
26
29
  onChange: ChangeEventHandler<HTMLInputElement>;
27
30
  onDirectionToggle: () => void;
@@ -33,7 +36,9 @@ const Cell: FunctionComponent<Props> = ({
33
36
  className,
34
37
  direction,
35
38
  inputRef,
39
+ isBottom,
36
40
  isCenter,
41
+ isRight,
37
42
  size,
38
43
  onChange,
39
44
  onDirectionToggle,
@@ -45,6 +50,7 @@ const Cell: FunctionComponent<Props> = ({
45
50
  const bonus = useTypedSelector((state) => selectCellBonus(state, cell));
46
51
  const points = useTypedSelector((state) => selectTilePoints(state, cell.tile));
47
52
  const isFiltered = useTypedSelector((state) => selectCellIsFiltered(state, cell));
53
+ const isValid = useTypedSelector((state) => selectCellIsValid(state, cell));
48
54
  const { tileFontSize } = getTileSizes(size);
49
55
  const isEmpty = tile.character === EMPTY_CELL;
50
56
  const style = useMemo(() => ({ fontSize: tileFontSize }), [tileFontSize]);
@@ -82,9 +88,12 @@ const Cell: FunctionComponent<Props> = ({
82
88
  className={className}
83
89
  direction={direction}
84
90
  inputRef={inputRef}
91
+ isBottom={isBottom}
85
92
  isCenter={isCenter}
93
+ isRight={isRight}
86
94
  isEmpty={isEmpty}
87
95
  isFiltered={isFiltered}
96
+ isValid={isValid}
88
97
  points={points}
89
98
  size={size}
90
99
  style={style}
@@ -25,9 +25,12 @@ interface Props {
25
25
  className?: string;
26
26
  direction: 'horizontal' | 'vertical';
27
27
  inputRef: RefObject<HTMLInputElement>;
28
+ isBottom: boolean;
28
29
  isCenter: boolean;
30
+ isRight: boolean;
29
31
  isEmpty: boolean;
30
32
  isFiltered: boolean;
33
+ isValid: boolean;
31
34
  points?: number;
32
35
  size: number;
33
36
  style?: CSSProperties;
@@ -46,9 +49,12 @@ const CellPure: FunctionComponent<Props> = ({
46
49
  className,
47
50
  direction,
48
51
  inputRef,
52
+ isBottom,
49
53
  isCenter,
54
+ isRight,
50
55
  isEmpty,
51
56
  isFiltered,
57
+ isValid,
52
58
  points,
53
59
  size,
54
60
  style,
@@ -61,12 +67,14 @@ const CellPure: FunctionComponent<Props> = ({
61
67
  onToggleFilterCellClick,
62
68
  }) => (
63
69
  <div
64
- className={classNames(styles.cell, getBonusClassname(cell, bonus, isCenter), className, {
70
+ className={classNames(styles.cell, className, getBonusClassname(cell, bonus, isCenter), {
71
+ [styles.bottom]: isBottom,
65
72
  [styles.candidate]: cell.isCandidate(),
73
+ [styles.right]: isRight,
66
74
  })}
67
75
  style={style}
68
76
  >
69
- {isCenter && (
77
+ {isCenter && isEmpty && (
70
78
  <div className={classNames(styles.iconContainer)}>
71
79
  <Star className={styles.star} />
72
80
  </div>
@@ -84,6 +92,7 @@ const CellPure: FunctionComponent<Props> = ({
84
92
  highlighted={cell.isCandidate()}
85
93
  inputRef={inputRef}
86
94
  isBlank={tile.isBlank}
95
+ isValid={isValid}
87
96
  points={points}
88
97
  raised={!isEmpty}
89
98
  size={size}
@@ -1,6 +1,7 @@
1
1
  import classNames from 'classnames';
2
2
  import { FunctionComponent } from 'react';
3
3
 
4
+ import { useIsTouchDevice } from 'hooks';
4
5
  import { BookHalf, Cog, Eraser, Github, Keyboard, Sack } from 'icons';
5
6
  import { GITHUB_PROJECT_URL } from 'parameters';
6
7
  import { selectHasInvalidWords, selectHasOverusedTiles, useTranslate, useTypedSelector } from 'state';
@@ -27,6 +28,7 @@ const NavButtons: FunctionComponent<Props> = ({
27
28
  const translate = useTranslate();
28
29
  const hasOverusedTiles = useTypedSelector(selectHasOverusedTiles);
29
30
  const hasInvalidWords = useTypedSelector(selectHasInvalidWords);
31
+ const isTouchDevice = useIsTouchDevice();
30
32
 
31
33
  return (
32
34
  <div className={styles.navButtons}>
@@ -72,7 +74,15 @@ const NavButtons: FunctionComponent<Props> = ({
72
74
  <div className={styles.separator} />
73
75
 
74
76
  <div className={styles.group}>
75
- <SquareButton className={styles.button} Icon={Keyboard} tooltip={translate('keyMap')} onClick={onShowKeyMap} />
77
+ {!isTouchDevice && (
78
+ <SquareButton
79
+ className={styles.button}
80
+ Icon={Keyboard}
81
+ tooltip={translate('keyMap')}
82
+ onClick={onShowKeyMap}
83
+ />
84
+ )}
85
+
76
86
  <SquareButton className={styles.button} Icon={Cog} tooltip={translate('settings')} onClick={onShowSettings} />
77
87
  </div>
78
88
  </div>
@@ -14,7 +14,14 @@ import { useDispatch } from 'react-redux';
14
14
 
15
15
  import { createKeyboardNavigation, extractCharacters, extractInputValue, isCtrl } from 'lib';
16
16
  import { TILE_SIZE } from 'parameters';
17
- import { rackSlice, selectCharacterPoints, selectConfig, useTranslate, useTypedSelector } from 'state';
17
+ import {
18
+ rackSlice,
19
+ selectCharacterIsValid,
20
+ selectCharacterPoints,
21
+ selectConfig,
22
+ useTranslate,
23
+ useTypedSelector,
24
+ } from 'state';
18
25
 
19
26
  import Tile from '../Tile';
20
27
 
@@ -43,6 +50,7 @@ const RackTile: FunctionComponent<Props> = ({
43
50
  const translate = useTranslate();
44
51
  const config = useTypedSelector(selectConfig);
45
52
  const points = useTypedSelector((state) => selectCharacterPoints(state, character));
53
+ const isValid = useTypedSelector((state) => selectCharacterIsValid(state, character));
46
54
 
47
55
  const handleFocus = useCallback(() => {
48
56
  activeIndexRef.current = index;
@@ -88,6 +96,7 @@ const RackTile: FunctionComponent<Props> = ({
88
96
  highlighted={tile !== null}
89
97
  inputRef={inputRef}
90
98
  isBlank={character === BLANK}
99
+ isValid={isValid}
91
100
  key={index}
92
101
  placeholder={translate('rack.placeholder')[index]}
93
102
  points={points}
@@ -9,7 +9,6 @@
9
9
  }
10
10
 
11
11
  &.overused {
12
- color: red;
13
12
  opacity: 1;
14
13
 
15
14
  .tile {
@@ -37,6 +37,7 @@ const Character: FunctionComponent<Props> = ({ tile }) => {
37
37
  className={styles.tile}
38
38
  disabled
39
39
  isBlank={character === BLANK}
40
+ isValid={remainingCount >= 0}
40
41
  points={points}
41
42
  raised
42
43
  size={REMAINING_TILES_TILE_SIZE}
@@ -0,0 +1,85 @@
1
+ @import 'styles/mixins';
2
+
3
+ .solver {
4
+ display: flex;
5
+ flex-direction: column;
6
+ height: 100%;
7
+ }
8
+
9
+ .contentWrapper {
10
+ $board-size: 15;
11
+ $tile-size-max: 60px;
12
+ $tile-border-size: 1px;
13
+
14
+ flex: 1;
15
+ padding: 0 var(--spacing--xl);
16
+ max-height: $board-size * ($tile-size-max + $tile-border-size) + $tile-border-size;
17
+ }
18
+
19
+ .content {
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ height: 100%;
24
+ gap: var(--spacing--xl);
25
+
26
+ @include tablet {
27
+ gap: var(--spacing--l);
28
+ }
29
+ }
30
+
31
+ .boardContainer {
32
+ display: flex;
33
+ position: relative;
34
+ }
35
+
36
+ .sidebar {
37
+ display: flex;
38
+ flex-direction: column;
39
+ flex: 0 0 var(--sidebar--width);
40
+ gap: var(--spacing--xl);
41
+
42
+ @include tablet {
43
+ gap: var(--spacing--l);
44
+ }
45
+ }
46
+
47
+ .dictionary {
48
+ display: flex;
49
+ flex-direction: column;
50
+ }
51
+
52
+ .dictionaryOutput {
53
+ flex: 1;
54
+ border-bottom: var(--border);
55
+ }
56
+
57
+ .dictionaryInput {
58
+ flex: 0 0 auto;
59
+ }
60
+
61
+ .resultsContainer {
62
+ flex: 1;
63
+ position: relative;
64
+ }
65
+
66
+ .rackContainer {
67
+ position: relative;
68
+ z-index: 1;
69
+ flex: 0 0 auto;
70
+ display: flex;
71
+ justify-content: center;
72
+ margin: var(--spacing--xl) var(--spacing--l);
73
+
74
+ @include tablet {
75
+ margin: var(--spacing--l);
76
+ }
77
+ }
78
+
79
+ .rack {
80
+ border: var(--border);
81
+ }
82
+
83
+ .submitInput {
84
+ display: none;
85
+ }
@@ -0,0 +1,75 @@
1
+ import classNames from 'classnames';
2
+ import { FormEvent, FunctionComponent } from 'react';
3
+ import { useDispatch } from 'react-redux';
4
+ import { useMeasure } from 'react-use';
5
+
6
+ import { useIsTablet } from 'hooks';
7
+ import { getCellSize } from 'lib';
8
+ import { COMPONENTS_SPACING, COMPONENTS_SPACING_MOBILE, DICTIONARY_HEIGHT } from 'parameters';
9
+ import { selectConfig, solveSlice, useTypedSelector } from 'state';
10
+
11
+ import Board from '../Board';
12
+ import Dictionary from '../Dictionary';
13
+ import DictionaryInput from '../DictionaryInput';
14
+ import Rack from '../Rack';
15
+ import Results from '../Results';
16
+ import Well from '../Well';
17
+
18
+ import styles from './Solver.module.scss';
19
+
20
+ interface Props {
21
+ className?: string;
22
+ }
23
+
24
+ const Solver: FunctionComponent<Props> = ({ className }) => {
25
+ const dispatch = useDispatch();
26
+ const isTablet = useIsTablet();
27
+ const [boardRef, { height: boardHeight }] = useMeasure<HTMLDivElement>();
28
+ const [contentRef, { height: contentHeight, width: contentWidth }] = useMeasure<HTMLDivElement>();
29
+ const [resultsContainerRef, { height: resultsContainerHeight, width: resultsContainerWidth }] =
30
+ useMeasure<HTMLDivElement>();
31
+ const config = useTypedSelector(selectConfig);
32
+ const cellSize = getCellSize(config, contentWidth - resultsContainerWidth, contentHeight);
33
+ const componentsSpacing = isTablet ? COMPONENTS_SPACING_MOBILE : COMPONENTS_SPACING;
34
+ const resultsHeight = boardHeight - DICTIONARY_HEIGHT - componentsSpacing;
35
+
36
+ const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
37
+ event.preventDefault();
38
+ dispatch(solveSlice.actions.submit());
39
+ };
40
+
41
+ return (
42
+ <div className={classNames(styles.solver, className)}>
43
+ <div className={styles.contentWrapper}>
44
+ <div className={styles.content} ref={contentRef}>
45
+ <form className={styles.boardContainer} onSubmit={handleSubmit}>
46
+ {contentWidth > 0 && <Board cellSize={cellSize} innerRef={boardRef} />}
47
+ <input className={styles.submitInput} tabIndex={-1} type="submit" />
48
+ </form>
49
+
50
+ <div className={styles.sidebar} style={{ height: boardHeight + 1 }}>
51
+ <Well className={styles.resultsContainer} ref={resultsContainerRef}>
52
+ {resultsContainerWidth > 0 && resultsContainerHeight > 0 && (
53
+ <Results height={resultsHeight} width={resultsContainerWidth} />
54
+ )}
55
+ </Well>
56
+
57
+ <Well>
58
+ <div className={styles.dictionary} style={{ height: DICTIONARY_HEIGHT }}>
59
+ <Dictionary className={styles.dictionaryOutput} />
60
+ <DictionaryInput className={styles.dictionaryInput} />
61
+ </div>
62
+ </Well>
63
+ </div>
64
+ </div>
65
+ </div>
66
+
67
+ <form className={styles.rackContainer} onSubmit={handleSubmit}>
68
+ <Rack className={styles.rack} />
69
+ <input className={styles.submitInput} tabIndex={-1} type="submit" />
70
+ </form>
71
+ </div>
72
+ );
73
+ };
74
+
75
+ export default Solver;
@@ -0,0 +1 @@
1
+ export { default } from './Solver';