@scrabble-solver/scrabble-solver 2.13.4 → 2.13.5-alpha.1

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 (190) hide show
  1. package/.next/build-manifest.json +15 -24
  2. package/.next/cache/.tsbuildinfo +1 -1
  3. package/.next/cache/eslint/.cache_8dgz12 +1 -1
  4. package/.next/cache/webpack/client-development/0.pack.gz +0 -0
  5. package/.next/cache/webpack/client-development/1.pack.gz +0 -0
  6. package/.next/cache/webpack/client-development/10.pack.gz +0 -0
  7. package/.next/cache/webpack/client-development/11.pack.gz +0 -0
  8. package/.next/cache/webpack/client-development/12.pack.gz +0 -0
  9. package/.next/cache/webpack/client-development/13.pack.gz +0 -0
  10. package/.next/cache/webpack/client-development/14.pack.gz +0 -0
  11. package/.next/cache/webpack/client-development/15.pack.gz +0 -0
  12. package/.next/cache/webpack/client-development/2.pack.gz +0 -0
  13. package/.next/cache/webpack/client-development/3.pack.gz +0 -0
  14. package/.next/cache/webpack/client-development/4.pack.gz +0 -0
  15. package/.next/cache/webpack/client-development/5.pack.gz +0 -0
  16. package/.next/cache/webpack/client-development/6.pack.gz +0 -0
  17. package/.next/cache/webpack/client-development/7.pack.gz +0 -0
  18. package/.next/cache/webpack/client-development/8.pack.gz +0 -0
  19. package/.next/cache/webpack/client-development/9.pack.gz +0 -0
  20. package/.next/cache/webpack/client-development/index.pack.gz +0 -0
  21. package/.next/cache/webpack/client-development/index.pack.gz.old +0 -0
  22. package/.next/cache/webpack/client-development-fallback/0.pack.gz +0 -0
  23. package/.next/cache/webpack/client-development-fallback/index.pack.gz +0 -0
  24. package/.next/cache/webpack/client-production/0.pack +0 -0
  25. package/.next/cache/webpack/client-production/1.pack +0 -0
  26. package/.next/cache/webpack/client-production/2.pack +0 -0
  27. package/.next/cache/webpack/client-production/3.pack +0 -0
  28. package/.next/cache/webpack/client-production/4.pack +0 -0
  29. package/.next/cache/webpack/client-production/index.pack +0 -0
  30. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  31. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  32. package/.next/cache/webpack/edge-server-production/1.pack +0 -0
  33. package/.next/cache/webpack/edge-server-production/2.pack +0 -0
  34. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  35. package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  36. package/.next/cache/webpack/server-development/0.pack.gz +0 -0
  37. package/.next/cache/webpack/server-development/1.pack.gz +0 -0
  38. package/.next/cache/webpack/server-development/10.pack.gz +0 -0
  39. package/.next/cache/webpack/server-development/11.pack.gz +0 -0
  40. package/.next/cache/webpack/server-development/12.pack.gz +0 -0
  41. package/.next/cache/webpack/server-development/13.pack.gz +0 -0
  42. package/.next/cache/webpack/server-development/14.pack.gz +0 -0
  43. package/.next/cache/webpack/server-development/15.pack.gz +0 -0
  44. package/.next/cache/webpack/server-development/16.pack.gz +0 -0
  45. package/.next/cache/webpack/server-development/17.pack.gz +0 -0
  46. package/.next/cache/webpack/server-development/18.pack.gz +0 -0
  47. package/.next/cache/webpack/server-development/19.pack.gz +0 -0
  48. package/.next/cache/webpack/server-development/2.pack.gz +0 -0
  49. package/.next/cache/webpack/server-development/20.pack.gz +0 -0
  50. package/.next/cache/webpack/server-development/21.pack.gz +0 -0
  51. package/.next/cache/webpack/server-development/22.pack.gz +0 -0
  52. package/.next/cache/webpack/server-development/23.pack.gz +0 -0
  53. package/.next/cache/webpack/server-development/3.pack.gz +0 -0
  54. package/.next/cache/webpack/server-development/4.pack.gz +0 -0
  55. package/.next/cache/webpack/server-development/5.pack.gz +0 -0
  56. package/.next/cache/webpack/server-development/6.pack.gz +0 -0
  57. package/.next/cache/webpack/server-development/7.pack.gz +0 -0
  58. package/.next/cache/webpack/server-development/8.pack.gz +0 -0
  59. package/.next/cache/webpack/server-development/9.pack.gz +0 -0
  60. package/.next/cache/webpack/server-development/index.pack.gz +0 -0
  61. package/.next/cache/webpack/server-development/index.pack.gz.old +0 -0
  62. package/.next/cache/webpack/server-production/0.pack +0 -0
  63. package/.next/cache/webpack/server-production/1.pack +0 -0
  64. package/.next/cache/webpack/server-production/2.pack +0 -0
  65. package/.next/cache/webpack/server-production/3.pack +0 -0
  66. package/.next/cache/webpack/server-production/4.pack +0 -0
  67. package/.next/cache/webpack/server-production/index.pack +0 -0
  68. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  69. package/.next/server/middleware-build-manifest.js +1 -1
  70. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  71. package/.next/server/next-font-manifest.js +1 -1
  72. package/.next/server/pages/_app.js +3720 -1
  73. package/.next/server/pages/_document.js +86 -1
  74. package/.next/server/pages/_error.js +3742 -1
  75. package/.next/server/pages/api/dictionary/[locale]/[word].js +1116 -2
  76. package/.next/server/pages/api/solve.js +1496 -1
  77. package/.next/server/pages/api/verify.js +946 -1
  78. package/.next/server/pages/api/visit.js +176 -1
  79. package/.next/server/pages/index.js +4312 -1
  80. package/.next/server/pages-manifest.json +10 -1
  81. package/.next/server/vendor-chunks/@swc.js +35 -0
  82. package/.next/server/vendor-chunks/next.js +517 -0
  83. package/.next/server/webpack-api-runtime.js +168 -1
  84. package/.next/server/webpack-runtime.js +237 -1
  85. package/.next/static/chunks/main.js +1285 -0
  86. package/.next/static/chunks/pages/_app.js +4854 -0
  87. package/.next/static/chunks/pages/_error.js +28 -0
  88. package/.next/static/chunks/pages/index.js +717 -0
  89. package/.next/static/chunks/react-refresh.js +62 -0
  90. package/.next/static/chunks/webpack.js +1261 -0
  91. package/.next/static/development/_buildManifest.js +1 -0
  92. package/.next/static/development/_ssgManifest.js +1 -0
  93. package/.next/static/webpack/29a2bd94727d51e6.webpack.hot-update.json +1 -0
  94. package/.next/static/webpack/3754455b7d774808.webpack.hot-update.json +1 -0
  95. package/.next/static/webpack/633457081244afec._.hot-update.json +1 -0
  96. package/.next/static/webpack/b71388f846dd4d8b.webpack.hot-update.json +1 -0
  97. package/.next/static/webpack/e7718e8f7cbe05a0.webpack.hot-update.json +1 -0
  98. package/.next/static/webpack/pages/_app.29a2bd94727d51e6.hot-update.js +22 -0
  99. package/.next/static/webpack/pages/_app.3754455b7d774808.hot-update.js +262 -0
  100. package/.next/static/webpack/pages/_app.e7718e8f7cbe05a0.hot-update.js +251 -0
  101. package/.next/static/webpack/pages/index.3754455b7d774808.hot-update.js +75 -0
  102. package/.next/static/webpack/pages/index.b71388f846dd4d8b.hot-update.js +22 -0
  103. package/.next/static/webpack/pages/index.e7718e8f7cbe05a0.hot-update.js +32 -0
  104. package/.next/static/webpack/webpack.29a2bd94727d51e6.hot-update.js +18 -0
  105. package/.next/static/webpack/webpack.3754455b7d774808.hot-update.js +18 -0
  106. package/.next/static/webpack/webpack.b71388f846dd4d8b.hot-update.js +18 -0
  107. package/.next/static/webpack/webpack.e7718e8f7cbe05a0.hot-update.js +18 -0
  108. package/.next/trace +17 -44
  109. package/package.json +10 -10
  110. package/src/components/Board/Board.module.scss +14 -0
  111. package/src/components/Board/Board.tsx +8 -1
  112. package/src/components/Board/BoardPure.tsx +76 -21
  113. package/src/components/Board/hooks/useBackgroundImage.tsx +46 -7
  114. package/src/components/Board/hooks/useBoardStyle.ts +8 -3
  115. package/src/components/Board/lib/getCoordinate.ts +11 -0
  116. package/src/components/Board/lib/index.ts +1 -0
  117. package/src/components/Dictionary/Dictionary.tsx +17 -18
  118. package/src/components/EmptyState/EmptyState.tsx +1 -1
  119. package/src/components/Results/Results.module.scss +1 -0
  120. package/src/hooks/useAppLayout.ts +18 -4
  121. package/src/i18n/i18n.ts +15 -15
  122. package/src/i18n/{en.json → languages/english.json} +4 -0
  123. package/src/i18n/{fr.json → languages/french.json} +4 -0
  124. package/src/i18n/{de.json → languages/german.json} +4 -0
  125. package/src/i18n/{fa.json → languages/persian.json} +4 -0
  126. package/src/i18n/{pl.json → languages/polish.json} +4 -0
  127. package/src/i18n/{ro.json → languages/romanian.json} +4 -0
  128. package/src/i18n/{es.json → languages/spanish.json} +4 -0
  129. package/src/lib/extractCharacters.test.ts +3 -5
  130. package/src/lib/extractCharactersByCase.test.ts +3 -5
  131. package/src/modals/SettingsModal/SettingsModal.tsx +11 -1
  132. package/src/modals/SettingsModal/components/ShowCoordinatesSetting/ShowCoordinatesSetting.module.scss +12 -0
  133. package/src/modals/SettingsModal/components/ShowCoordinatesSetting/ShowCoordinatesSetting.tsx +59 -0
  134. package/src/modals/SettingsModal/components/ShowCoordinatesSetting/index.ts +1 -0
  135. package/src/modals/SettingsModal/components/index.ts +1 -0
  136. package/src/parameters/index.ts +1 -0
  137. package/src/state/localStorage.ts +10 -1
  138. package/src/state/sagas.ts +6 -3
  139. package/src/state/selectors.ts +2 -0
  140. package/src/state/slices/settingsInitialState.ts +6 -4
  141. package/src/state/slices/settingsSlice.ts +6 -1
  142. package/src/state/store.ts +1 -1
  143. package/src/styles/mixins.scss +9 -2
  144. package/src/types/index.ts +4 -0
  145. package/tsconfig.json +1 -0
  146. package/.next/BUILD_ID +0 -1
  147. package/.next/export-marker.json +0 -1
  148. package/.next/images-manifest.json +0 -1
  149. package/.next/next-minimal-server.js.nft.json +0 -1
  150. package/.next/next-server.js.nft.json +0 -1
  151. package/.next/prerender-manifest.js +0 -1
  152. package/.next/prerender-manifest.json +0 -1
  153. package/.next/required-server-files.json +0 -1
  154. package/.next/routes-manifest.json +0 -1
  155. package/.next/server/InjectManifest.js.nft.json +0 -1
  156. package/.next/server/chunks/28.js +0 -1
  157. package/.next/server/chunks/331.js +0 -6
  158. package/.next/server/chunks/577.js +0 -1
  159. package/.next/server/chunks/911.js +0 -1
  160. package/.next/server/chunks/977.js +0 -1
  161. package/.next/server/chunks/font-manifest.json +0 -1
  162. package/.next/server/font-manifest.json +0 -1
  163. package/.next/server/functions-config-manifest.json +0 -1
  164. package/.next/server/pages/404.html +0 -1
  165. package/.next/server/pages/404.js.nft.json +0 -1
  166. package/.next/server/pages/500.html +0 -1
  167. package/.next/server/pages/_app.js.nft.json +0 -1
  168. package/.next/server/pages/_document.js.nft.json +0 -1
  169. package/.next/server/pages/_error.js.nft.json +0 -1
  170. package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +0 -1
  171. package/.next/server/pages/api/dictionary/[locale].js +0 -1
  172. package/.next/server/pages/api/dictionary/[locale].js.nft.json +0 -1
  173. package/.next/server/pages/api/solve.js.nft.json +0 -1
  174. package/.next/server/pages/api/verify.js.nft.json +0 -1
  175. package/.next/server/pages/api/visit.js.nft.json +0 -1
  176. package/.next/server/pages/index.html +0 -1
  177. package/.next/server/pages/index.js.nft.json +0 -1
  178. package/.next/server/pages/index.json +0 -1
  179. package/.next/static/chunks/framework-1728d40275bc359a.js +0 -49
  180. package/.next/static/chunks/main-5fb7abbfc135de15.js +0 -1
  181. package/.next/static/chunks/pages/404-b6e238bb4117d451.js +0 -1
  182. package/.next/static/chunks/pages/_app-b95404c40a0092e6.js +0 -17
  183. package/.next/static/chunks/pages/_error-db043fa3544f8809.js +0 -1
  184. package/.next/static/chunks/pages/index-59d569ef79bc2580.js +0 -1
  185. package/.next/static/chunks/webpack-6ef43a8d4a395f49.js +0 -1
  186. package/.next/static/css/8fa2fb99b90e109e.css +0 -1
  187. package/.next/static/css/c80ebc6fd13311f1.css +0 -2
  188. package/.next/static/jyVoCYlAT5PMzOeavr5AX/_buildManifest.js +0 -1
  189. package/.next/static/jyVoCYlAT5PMzOeavr5AX/_ssgManifest.js +0 -1
  190. /package/.next/static/chunks/{polyfills-c67a75d1b6f99dc8.js → polyfills.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/scrabble-solver",
3
- "version": "2.13.4",
3
+ "version": "2.13.5-alpha.1",
4
4
  "description": "Scrabble Solver 2 - App",
5
5
  "engines": {
6
6
  "node": ">=16"
@@ -30,14 +30,15 @@
30
30
  "@floating-ui/react": "^0.26.9",
31
31
  "@kamilmielnik/trie": "^2.0.1",
32
32
  "@reduxjs/toolkit": "^2.1.0",
33
- "@scrabble-solver/configs": "^2.13.4",
34
- "@scrabble-solver/constants": "^2.13.4",
35
- "@scrabble-solver/dictionaries": "^2.13.4",
36
- "@scrabble-solver/logger": "^2.13.4",
37
- "@scrabble-solver/solver": "^2.13.4",
38
- "@scrabble-solver/types": "^2.13.4",
39
- "@scrabble-solver/word-definitions": "^2.13.4",
33
+ "@scrabble-solver/configs": "^2.13.5-alpha.1",
34
+ "@scrabble-solver/constants": "^2.13.5-alpha.1",
35
+ "@scrabble-solver/dictionaries": "^2.13.5-alpha.1",
36
+ "@scrabble-solver/logger": "^2.13.5-alpha.1",
37
+ "@scrabble-solver/solver": "^2.13.5-alpha.1",
38
+ "@scrabble-solver/types": "^2.13.5-alpha.1",
39
+ "@scrabble-solver/word-definitions": "^2.13.5-alpha.1",
40
40
  "classnames": "^2.5.1",
41
+ "env-cmd": "^10.1.0",
41
42
  "include-media": "^2.0.0",
42
43
  "include-media-query-builder": "^1.1.0",
43
44
  "next": "^14.1.0",
@@ -70,8 +71,7 @@
70
71
  "@types/react-window": "^1.8.8",
71
72
  "@types/redux": "^3.6.31",
72
73
  "@types/redux-saga": "^0.10.5",
73
- "env-cmd": "^10.1.0",
74
74
  "sass": "^1.70.0"
75
75
  },
76
- "gitHead": "852ecdf9eb6aaefb9befb0cb1ea671868da7d0de"
76
+ "gitHead": "0843db4c5016829979718054193926bf71d67038"
77
77
  }
@@ -66,3 +66,17 @@
66
66
 
67
67
  color: var(--color--white);
68
68
  }
69
+
70
+ .coordinateColumn {
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ font-size: var(--font--size--h2);
75
+ }
76
+
77
+ .coordinateRow {
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ font-size: var(--font--size--h2);
82
+ }
@@ -8,6 +8,7 @@ import useOnclickOutside from 'react-cool-onclickoutside';
8
8
  import { useDispatch } from 'react-redux';
9
9
 
10
10
  import { useAppLayout } from 'hooks';
11
+ import { LOCALE_FEATURES } from 'i18n';
11
12
  import { TRANSITION } from 'parameters';
12
13
  import {
13
14
  boardSlice,
@@ -16,6 +17,7 @@ import {
16
17
  selectInputMode,
17
18
  selectLocale,
18
19
  selectRowsWithCandidate,
20
+ selectShowCoordinates,
19
21
  solveSlice,
20
22
  useTypedSelector,
21
23
  } from 'state';
@@ -35,7 +37,8 @@ const Board: FunctionComponent<Props> = ({ className }) => {
35
37
  const rows = useTypedSelector(selectRowsWithCandidate);
36
38
  const inputMode = useTypedSelector(selectInputMode);
37
39
  const filteredCells = useTypedSelector(selectFilteredCells);
38
- const { cellSize } = useAppLayout();
40
+ const showCoordinates = useTypedSelector(selectShowCoordinates);
41
+ const { cellSize, coordinatesFontSize, coordinatesSize } = useAppLayout();
39
42
  const [
40
43
  { activeIndex, direction, inputRefs },
41
44
  { insertValue, onChange, onDirectionToggle, onFocus, onKeyDown, onPaste },
@@ -149,10 +152,14 @@ const Board: FunctionComponent<Props> = ({ className }) => {
149
152
  <BoardPure
150
153
  className={className}
151
154
  cellSize={cellSize}
155
+ coordinatesFontSize={coordinatesFontSize}
156
+ coordinatesSize={coordinatesSize}
157
+ direction={LOCALE_FEATURES[locale].direction}
152
158
  filteredCells={filteredCells}
153
159
  inputRefs={inputRefs}
154
160
  ref={ref}
155
161
  rows={rows}
162
+ showCoordinates={showCoordinates}
156
163
  style={boardStyle}
157
164
  onBlur={handleBlur}
158
165
  onChange={onChange}
@@ -1,15 +1,15 @@
1
- import { Cell as CellModel } from '@scrabble-solver/types';
1
+ import { Cell as CellModel, ShowCoordinates } from '@scrabble-solver/types';
2
2
  import classNames from 'classnames';
3
3
  import {
4
+ CSSProperties,
4
5
  ChangeEventHandler,
5
6
  ClipboardEventHandler,
6
- CSSProperties,
7
7
  FocusEventHandler,
8
- forwardRef,
9
8
  Fragment,
10
9
  KeyboardEventHandler,
11
- memo,
12
10
  RefObject,
11
+ forwardRef,
12
+ memo,
13
13
  } from 'react';
14
14
 
15
15
  import { FlagFill } from 'icons';
@@ -18,13 +18,18 @@ import { Point } from 'types';
18
18
 
19
19
  import styles from './Board.module.scss';
20
20
  import { Cell } from './components';
21
+ import { getCoordinate } from './lib';
21
22
 
22
23
  interface Props {
23
24
  className?: string;
24
25
  cellSize: number;
26
+ coordinatesFontSize: number;
27
+ coordinatesSize: number;
28
+ direction: 'ltr' | 'rtl';
25
29
  filteredCells: Point[];
26
30
  inputRefs: RefObject<HTMLInputElement>[][];
27
31
  rows: CellModel[][];
32
+ showCoordinates: ShowCoordinates;
28
33
  style?: CSSProperties;
29
34
  onBlur: FocusEventHandler;
30
35
  onChange: ChangeEventHandler<HTMLInputElement>;
@@ -35,7 +40,23 @@ interface Props {
35
40
 
36
41
  const BoardPure = forwardRef<HTMLDivElement, Props>(
37
42
  (
38
- { className, cellSize, filteredCells, inputRefs, rows, style, onBlur, onChange, onFocus, onKeyDown, onPaste },
43
+ {
44
+ className,
45
+ cellSize,
46
+ coordinatesFontSize,
47
+ coordinatesSize,
48
+ direction,
49
+ filteredCells,
50
+ inputRefs,
51
+ rows,
52
+ showCoordinates,
53
+ style,
54
+ onBlur,
55
+ onChange,
56
+ onFocus,
57
+ onKeyDown,
58
+ onPaste,
59
+ },
39
60
  ref,
40
61
  ) => (
41
62
  <div
@@ -46,8 +67,58 @@ const BoardPure = forwardRef<HTMLDivElement, Props>(
46
67
  onKeyDown={onKeyDown}
47
68
  onPaste={onPaste}
48
69
  >
70
+ {showCoordinates !== 'hidden' && (
71
+ <>
72
+ <div style={{ width: coordinatesSize, height: coordinatesSize }} />
73
+
74
+ {rows[0].map((_column, index) => (
75
+ <div
76
+ className={styles.coordinateColumn}
77
+ key={index}
78
+ style={{
79
+ width: cellSize,
80
+ height: coordinatesSize,
81
+ fontSize: coordinatesFontSize,
82
+ }}
83
+ >
84
+ {getCoordinate(index, showCoordinates === 'original' ? 'letter' : 'number')}
85
+ </div>
86
+ ))}
87
+ </>
88
+ )}
89
+
90
+ {filteredCells.map(({ x, y }) => (
91
+ <div
92
+ className={styles.iconContainer}
93
+ key={[x, y].join('-')}
94
+ style={{
95
+ height: cellSize,
96
+ width: cellSize,
97
+ left: direction === 'ltr' ? coordinatesSize + x * (cellSize + BORDER_WIDTH) : undefined,
98
+ right: direction === 'rtl' ? coordinatesSize + x * (cellSize + BORDER_WIDTH) : undefined,
99
+ top: coordinatesSize + y * (cellSize + BORDER_WIDTH),
100
+ }}
101
+ >
102
+ <div className={styles.iconBackground} />
103
+ <FlagFill className={styles.icon} />
104
+ </div>
105
+ ))}
106
+
49
107
  {rows.map((cells, y) => (
50
108
  <Fragment key={y}>
109
+ {showCoordinates !== 'hidden' && (
110
+ <div
111
+ className={styles.coordinateRow}
112
+ style={{
113
+ width: coordinatesSize,
114
+ height: cellSize,
115
+ fontSize: coordinatesFontSize,
116
+ }}
117
+ >
118
+ {getCoordinate(y, showCoordinates === 'original' ? 'number' : 'letter')}
119
+ </div>
120
+ )}
121
+
51
122
  {cells.map((cell, x) => (
52
123
  <Cell
53
124
  className={styles.cell}
@@ -65,22 +136,6 @@ const BoardPure = forwardRef<HTMLDivElement, Props>(
65
136
  ))}
66
137
  </Fragment>
67
138
  ))}
68
-
69
- {filteredCells.map(({ x, y }) => (
70
- <div
71
- className={styles.iconContainer}
72
- key={[x, y].join('-')}
73
- style={{
74
- height: cellSize,
75
- width: cellSize,
76
- left: x * (cellSize + BORDER_WIDTH),
77
- top: y * (cellSize + BORDER_WIDTH),
78
- }}
79
- >
80
- <div className={styles.iconBackground} />
81
- <FlagFill className={styles.icon} />
82
- </div>
83
- ))}
84
139
  </div>
85
140
  ),
86
141
  );
@@ -6,10 +6,18 @@ import { renderToString } from 'react-dom/server';
6
6
  import { Provider } from 'react-redux';
7
7
 
8
8
  import { useAppLayout, useMediaQueries } from 'hooks';
9
+ import { LOCALE_FEATURES } from 'i18n';
9
10
  import { FlagFill, Star } from 'icons';
10
11
  import { dataUrlToBlob, getTileSizes } from 'lib';
11
- import { BORDER_COLOR_LIGHT, BORDER_RADIUS, BORDER_WIDTH, COLOR_BONUS_START, COLOR_FILTERED } from 'parameters';
12
- import { selectConfig, store, useTypedSelector } from 'state';
12
+ import {
13
+ BORDER_COLOR_LIGHT,
14
+ BORDER_RADIUS,
15
+ BORDER_WIDTH,
16
+ COLOR_BACKGROUND,
17
+ COLOR_BONUS_START,
18
+ COLOR_FILTERED,
19
+ } from 'parameters';
20
+ import { selectConfig, selectLocale, selectShowCoordinates, store, useTypedSelector } from 'state';
13
21
  import { Point } from 'types';
14
22
 
15
23
  import { getBonusColor } from '../lib';
@@ -25,7 +33,10 @@ const BONUS_WORD_4 = 'b4';
25
33
  const CELL_FILTER = 'c';
26
34
 
27
35
  const useBackgroundImage = () => {
28
- const { boardSize, cellSize } = useAppLayout();
36
+ const { boardSize, cellSize, coordinatesSize } = useAppLayout();
37
+ const locale = useTypedSelector(selectLocale);
38
+ const { direction } = LOCALE_FEATURES[locale];
39
+ const showCoordinates = useTypedSelector(selectShowCoordinates);
29
40
  const { isLessThanXs } = useMediaQueries();
30
41
  const borderRadius = isLessThanXs ? BORDER_RADIUS_XS : BORDER_RADIUS;
31
42
  const config = useTypedSelector(selectConfig);
@@ -44,9 +55,10 @@ const useBackgroundImage = () => {
44
55
  const word3Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 3);
45
56
  const word4Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 4);
46
57
 
47
- const getX = (point: Point): number => point.x * (cellSize + BORDER_WIDTH);
58
+ const getX = (point: Point): number =>
59
+ (direction === 'ltr' ? coordinatesSize : 0) + point.x * (cellSize + BORDER_WIDTH);
48
60
 
49
- const getY = (point: Point): number => point.y * (cellSize + BORDER_WIDTH);
61
+ const getY = (point: Point): number => coordinatesSize + point.y * (cellSize + BORDER_WIDTH);
50
62
 
51
63
  const backgroundSvg = renderToString(
52
64
  <Provider store={store}>
@@ -136,12 +148,39 @@ const useBackgroundImage = () => {
136
148
 
137
149
  <rect fill="white" height={viewBoxHeight} rx={borderRadius} width={viewBoxWidth} x="0" y="0" />
138
150
 
151
+ {showCoordinates !== 'hidden' && (
152
+ <>
153
+ <rect fill={COLOR_BACKGROUND} height={coordinatesSize} rx={borderRadius} width={viewBoxWidth} x="0" y="0" />
154
+ <rect
155
+ fill={COLOR_BACKGROUND}
156
+ height={viewBoxHeight}
157
+ rx={borderRadius}
158
+ x={direction === 'ltr' ? 0 : viewBoxWidth - coordinatesSize}
159
+ y="0"
160
+ width={coordinatesSize}
161
+ />
162
+ <use href={`#${HORIZONTAL_LINE}`} y={coordinatesSize} />
163
+ <use
164
+ href={`#${VERTICAL_LINE}`}
165
+ x={direction === 'ltr' ? coordinatesSize : viewBoxWidth - coordinatesSize - BORDER_WIDTH}
166
+ />
167
+ </>
168
+ )}
169
+
139
170
  {Array.from({ length: config.boardHeight - 1 }).map((_value, index) => (
140
- <use key={index} href={`#${HORIZONTAL_LINE}`} y={(index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH} />
171
+ <use
172
+ key={index}
173
+ href={`#${HORIZONTAL_LINE}`}
174
+ y={coordinatesSize + (index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH}
175
+ />
141
176
  ))}
142
177
 
143
178
  {Array.from({ length: config.boardWidth - 1 }).map((_value, index) => (
144
- <use key={index} href={`#${VERTICAL_LINE}`} x={(index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH} />
179
+ <use
180
+ key={index}
181
+ href={`#${VERTICAL_LINE}`}
182
+ x={(direction === 'ltr' ? coordinatesSize : 0) + (index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH}
183
+ />
145
184
  ))}
146
185
 
147
186
  {characterBonuses.map((bonus, index) => (
@@ -2,7 +2,7 @@ import { CSSProperties, useMemo } from 'react';
2
2
 
3
3
  import { useAppLayout } from 'hooks';
4
4
  import { getTileSizes } from 'lib';
5
- import { selectConfig, useTypedSelector } from 'state';
5
+ import { selectConfig, selectShowCoordinates, useTypedSelector } from 'state';
6
6
 
7
7
  import useBackgroundImage from './useBackgroundImage';
8
8
 
@@ -11,12 +11,17 @@ const useBoardStyle = () => {
11
11
  const { cellSize } = useAppLayout();
12
12
  const { tileFontSize } = getTileSizes(cellSize);
13
13
  const backgroundImage = useBackgroundImage();
14
+ const showCoordinates = useTypedSelector(selectShowCoordinates);
14
15
  const boardStyle = useMemo<CSSProperties>(
15
16
  () => ({
16
17
  backgroundImage,
17
18
  fontSize: tileFontSize,
18
- gridTemplateColumns: `repeat(${config.boardWidth}, 1fr)`,
19
- gridTemplateRows: `repeat(${config.boardHeight}, 1fr)`,
19
+ gridTemplateColumns:
20
+ showCoordinates === 'hidden' ? `repeat(${config.boardWidth}, 1fr)` : `0.5fr repeat(${config.boardWidth}, 1fr)`,
21
+ gridTemplateRows:
22
+ showCoordinates === 'hidden'
23
+ ? `repeat(${config.boardHeight}, 1fr)`
24
+ : `0.5fr repeat(${config.boardHeight}, 1fr)`,
20
25
  }),
21
26
  [backgroundImage, config.boardHeight, config.boardWidth, tileFontSize],
22
27
  );
@@ -0,0 +1,11 @@
1
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
2
+
3
+ const getCoordinate = (index: number, type: 'letter' | 'number'): string => {
4
+ if (type === 'number') {
5
+ return String(index + 1);
6
+ }
7
+
8
+ return alphabet[index];
9
+ };
10
+
11
+ export default getCoordinate;
@@ -1,2 +1,3 @@
1
1
  export { default as getBonusColor } from './getBonusColor';
2
+ export { default as getCoordinate } from './getCoordinate';
2
3
  export { default as getPositionInGrid } from './getPositionInGrid';
@@ -16,7 +16,7 @@ const Dictionary: FunctionComponent<Props> = ({ className }) => {
16
16
  const translate = useTranslate();
17
17
  const { results, isLoading } = useTypedSelector(selectDictionary);
18
18
  const error = useTypedSelector(selectDictionaryError);
19
- const isLastAllowed = results.length > 0 ? results[results.length - 1].isAllowed : undefined;
19
+ const isLastAllowed = results.at(-1)?.isAllowed;
20
20
 
21
21
  return (
22
22
  <div
@@ -44,27 +44,26 @@ const Dictionary: FunctionComponent<Props> = ({ className }) => {
44
44
 
45
45
  {isAllowed === false && <div>{translate('dictionary.empty-state.not-allowed')}</div>}
46
46
 
47
- {isAllowed === true && (
47
+ {isAllowed === true && definitions.length === 0 && (
48
48
  <>
49
- {definitions.length === 0 && (
50
- <>
51
- {exists && <div>{translate('dictionary.empty-state.no-definitions')}</div>}
52
- {!exists && <div>{translate('dictionary.empty-state.no-results')}</div>}
53
- </>
54
- )}
55
-
56
- {definitions.length > 0 && (
57
- <ul className={styles.definitions}>
58
- {definitions.map((result, index) => (
59
- <li key={index} className={styles.definition}>
60
- {result}
61
- </li>
62
- ))}
63
- </ul>
64
- )}
49
+ <div>
50
+ {exists
51
+ ? translate('dictionary.empty-state.no-definitions')
52
+ : translate('dictionary.empty-state.no-results')}
53
+ </div>
65
54
  </>
66
55
  )}
67
56
 
57
+ {isAllowed === true && definitions.length > 0 && (
58
+ <ul className={styles.definitions}>
59
+ {definitions.map((result, index) => (
60
+ <li key={index} className={styles.definition}>
61
+ {result}
62
+ </li>
63
+ ))}
64
+ </ul>
65
+ )}
66
+
68
67
  {!isLoading && isAllowed === null && <div>{translate('dictionary.empty-state.no-results')}</div>}
69
68
  </div>
70
69
  </div>
@@ -36,7 +36,7 @@ const EmptyState: FunctionComponent<Props> = ({ children, className, variant })
36
36
  const { direction } = LOCALE_FEATURES[locale];
37
37
  const title = useMemo(() => translate(TITLE_KEY_PER_TYPE[variant]), [translate]);
38
38
  const message = direction === 'ltr' ? title : title.split('').reverse().join('');
39
- const content = useMemo(() => [[message.toUpperCase()]], [title]);
39
+ const content = useMemo(() => [message.toUpperCase().split(' ')], [title]);
40
40
 
41
41
  return (
42
42
  <div className={classNames(styles.emptyState, className)}>
@@ -60,6 +60,7 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
60
60
  cursor: pointer;
61
61
  text-transform: uppercase;
62
62
  transition: var(--transition);
63
+ background-color: var(--color--background);
63
64
 
64
65
  &:focus,
65
66
  &:hover {
@@ -17,7 +17,7 @@ import {
17
17
  RACK_TILE_SIZE_MAX,
18
18
  SOLVER_COLUMN_WIDTH,
19
19
  } from 'parameters';
20
- import { selectConfig, useTypedSelector } from 'state';
20
+ import { selectConfig, selectShowCoordinates, useTypedSelector } from 'state';
21
21
 
22
22
  import useIsTouchDevice from './useIsTouchDevice';
23
23
  import useMediaQueries from './useMediaQueries';
@@ -26,6 +26,7 @@ import useViewportSize from './useViewportSize';
26
26
  const useAppLayout = () => {
27
27
  const { viewportHeight, viewportWidth } = useViewportSize();
28
28
  const config = useTypedSelector(selectConfig);
29
+ const showCoordinates = useTypedSelector(selectShowCoordinates);
29
30
  const isTouchDevice = useIsTouchDevice();
30
31
  const { isLessThanXs, isLessThanS, isLessThanM, isLessThanL, isLessThanXl } = useMediaQueries();
31
32
  const isBoardFullWidth = isLessThanM;
@@ -44,10 +45,21 @@ const useAppLayout = () => {
44
45
  const maxBoardHeight = isBoardFullWidth
45
46
  ? Number.POSITIVE_INFINITY
46
47
  : Math.max(solverHeight - bottomContainerHeight, 0);
47
- const cellWidth = (maxBoardWidth - (config.boardWidth + 1) * BORDER_WIDTH) / config.boardWidth;
48
- const cellHeight = (maxBoardHeight - (config.boardHeight + 1) * BORDER_WIDTH) / config.boardHeight;
48
+
49
+ const coordinatesSizeRatio = showCoordinates === 'hidden' ? 0 : 0.5;
50
+ const coordinatesBorderWidth = showCoordinates === 'hidden' ? 0 : 1;
51
+ const cellWidth =
52
+ (maxBoardWidth - (config.boardWidth + 1 + coordinatesBorderWidth) * BORDER_WIDTH) /
53
+ (config.boardWidth + coordinatesSizeRatio);
54
+ const cellHeight =
55
+ (maxBoardHeight - (config.boardHeight + 1 + coordinatesBorderWidth) * BORDER_WIDTH) /
56
+ (config.boardHeight + coordinatesSizeRatio);
49
57
  const cellSize = Math.min(Math.min(cellWidth, cellHeight), BOARD_TILE_SIZE_MAX);
50
- const boardSize = (cellSize + BORDER_WIDTH) * config.boardWidth + BORDER_WIDTH;
58
+ const coordinatesSize = coordinatesSizeRatio * cellSize;
59
+ const boardSize =
60
+ (cellSize + BORDER_WIDTH) * config.boardWidth +
61
+ BORDER_WIDTH +
62
+ (showCoordinates === 'hidden' ? 0 : coordinatesSize + BORDER_WIDTH);
51
63
  const maxControlsWidth = tileSize * config.maximumCharactersCount + 2 * BORDER_WIDTH;
52
64
  const showResultsInModal = isLessThanL;
53
65
  const dictionaryHeight = showResultsInModal ? DICTIONARY_HEIGHT_MOBILE : DICTIONARY_HEIGHT;
@@ -62,6 +74,8 @@ const useAppLayout = () => {
62
74
  animateTile: !isLessThanXs,
63
75
  boardSize,
64
76
  cellSize,
77
+ coordinatesFontSize: coordinatesSize * 0.6,
78
+ coordinatesSize,
65
79
  dictionaryHeight,
66
80
  isModalFullWidth: isLessThanS,
67
81
  logoHeight,
package/src/i18n/i18n.ts CHANGED
@@ -2,23 +2,23 @@ import { Locale } from '@scrabble-solver/types';
2
2
 
3
3
  import { Translations } from 'types';
4
4
 
5
- import de from './de.json';
6
- import en from './en.json';
7
- import es from './es.json';
8
- import fa from './fa.json';
9
- import fr from './fr.json';
10
- import pl from './pl.json';
11
- import ro from './ro.json';
5
+ import english from './languages/english.json';
6
+ import french from './languages/french.json';
7
+ import german from './languages/german.json';
8
+ import persian from './languages/persian.json';
9
+ import polish from './languages/polish.json';
10
+ import romanian from './languages/romanian.json';
11
+ import spanish from './languages/spanish.json';
12
12
 
13
13
  const i18n: Record<Locale, Translations> = {
14
- [Locale.DE_DE]: de,
15
- [Locale.EN_GB]: en,
16
- [Locale.EN_US]: en,
17
- [Locale.ES_ES]: es,
18
- [Locale.FA_IR]: fa,
19
- [Locale.FR_FR]: fr,
20
- [Locale.PL_PL]: pl,
21
- [Locale.RO_RO]: ro,
14
+ [Locale.DE_DE]: german,
15
+ [Locale.EN_GB]: english,
16
+ [Locale.EN_US]: english,
17
+ [Locale.ES_ES]: spanish,
18
+ [Locale.FA_IR]: persian,
19
+ [Locale.FR_FR]: french,
20
+ [Locale.PL_PL]: polish,
21
+ [Locale.RO_RO]: romanian,
22
22
  };
23
23
 
24
24
  export default i18n;
@@ -64,6 +64,10 @@
64
64
  "settings.inputMode.keyboard": "Keyboard",
65
65
  "settings.inputMode.touchscreen": "Touchscreen",
66
66
  "settings.language": "Language",
67
+ "settings.showCoordinates": "Coordinates",
68
+ "settings.showCoordinates.alternative": "Alternative",
69
+ "settings.showCoordinates.hidden": "Hidden",
70
+ "settings.showCoordinates.original": "Original",
67
71
  "words": "Created words",
68
72
  "words.invalid": "Invalid",
69
73
  "words.valid": "Valid"
@@ -64,6 +64,10 @@
64
64
  "settings.inputMode.keyboard": "Clavier",
65
65
  "settings.inputMode.touchscreen": "Écran tactile",
66
66
  "settings.language": "Langue",
67
+ "settings.showCoordinates": "Coordonnés",
68
+ "settings.showCoordinates.alternative": "Alternative",
69
+ "settings.showCoordinates.hidden": "Caché",
70
+ "settings.showCoordinates.original": "Original",
67
71
  "words": "Mots créés",
68
72
  "words.invalid": "Incorrect",
69
73
  "words.valid": "Corriger"
@@ -64,6 +64,10 @@
64
64
  "settings.inputMode.keyboard": "Tastatur",
65
65
  "settings.inputMode.touchscreen": "Touchscreen",
66
66
  "settings.language": "Sprache",
67
+ "settings.showCoordinates": "Koordinaten",
68
+ "settings.showCoordinates.alternative": "Alternative",
69
+ "settings.showCoordinates.hidden": "Versteckt",
70
+ "settings.showCoordinates.original": "Original",
67
71
  "words": "Gebildete Wörter",
68
72
  "words.invalid": "Falsch",
69
73
  "words.valid": "Korrekt"
@@ -64,6 +64,10 @@
64
64
  "settings.inputMode.keyboard": "صفحه کلید",
65
65
  "settings.inputMode.touchscreen": "صفحه لمسی",
66
66
  "settings.language": "زبان",
67
+ "settings.showCoordinates": "مختصات",
68
+ "settings.showCoordinates.alternative": "بديل",
69
+ "settings.showCoordinates.hidden": "مختفي",
70
+ "settings.showCoordinates.original": "إبداعي",
67
71
  "words": "کلمات ساخته شده",
68
72
  "words.invalid": "نا معتبر",
69
73
  "words.valid": "معتبر"
@@ -64,6 +64,10 @@
64
64
  "settings.inputMode.keyboard": "Klawiatura",
65
65
  "settings.inputMode.touchscreen": "Ekran dotykowy",
66
66
  "settings.language": "Język",
67
+ "settings.showCoordinates": "Współrzędne",
68
+ "settings.showCoordinates.alternative": "Alternatywne",
69
+ "settings.showCoordinates.hidden": "Ukryte",
70
+ "settings.showCoordinates.original": "Oryginalne",
67
71
  "words": "Utworzone słowa",
68
72
  "words.invalid": "Niepoprawne",
69
73
  "words.valid": "Poprawne"
@@ -64,6 +64,10 @@
64
64
  "settings.inputMode.keyboard": "Tastatura",
65
65
  "settings.inputMode.touchscreen": "Ecran",
66
66
  "settings.language": "Limba",
67
+ "settings.showCoordinates": "Coordonate",
68
+ "settings.showCoordinates.alternative": "Alternativă",
69
+ "settings.showCoordinates.hidden": "Ascuns",
70
+ "settings.showCoordinates.original": "Original",
67
71
  "words": "Cuvinte create",
68
72
  "words.invalid": "Invalid",
69
73
  "words.valid": "Valid"
@@ -64,6 +64,10 @@
64
64
  "settings.inputMode.keyboard": "Teclado",
65
65
  "settings.inputMode.touchscreen": "Pantalla táctil",
66
66
  "settings.language": "Idioma",
67
+ "settings.showCoordinates": "Coordenadas",
68
+ "settings.showCoordinates.alternative": "Alternativa",
69
+ "settings.showCoordinates.hidden": "Oculto",
70
+ "settings.showCoordinates.original": "Original",
67
71
  "words": "Palabras creadas",
68
72
  "words.invalid": "Incorrecto",
69
73
  "words.valid": "Correcto"
@@ -18,9 +18,7 @@ describe('extractCharacters', () => {
18
18
  const locale = Locale.ES_ES;
19
19
  const config = getConfig(Game.Scrabble, locale);
20
20
 
21
- for (const { input, expected } of tests) {
22
- it(`[${locale}] "${input}"`, () => {
23
- expect(extractCharacters(config, input)).toEqual(expected);
24
- });
25
- }
21
+ it.each(tests)(`[${locale}] "$input"`, ({ input, expected }) => {
22
+ expect(extractCharacters(config, input)).toEqual(expected);
23
+ });
26
24
  });
@@ -23,9 +23,7 @@ describe('extractCharactersByCase', () => {
23
23
  const locale = Locale.ES_ES;
24
24
  const config = getConfig(Game.Scrabble, locale);
25
25
 
26
- for (const { input, expected } of tests) {
27
- it(`[${locale}] "${input}"`, () => {
28
- expect(extractCharactersByCase(config, input)).toEqual(expected);
29
- });
30
- }
26
+ it.each(tests)(`[${locale}] "$input"`, ({ input, expected }) => {
27
+ expect(extractCharactersByCase(config, input)).toEqual(expected);
28
+ });
31
29
  });