@scrabble-solver/scrabble-solver 2.12.1 → 2.12.3

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 (97) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +14 -14
  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/required-server-files.json +1 -1
  15. package/.next/routes-manifest.json +1 -1
  16. package/.next/server/chunks/131.js +478 -106
  17. package/.next/server/chunks/277.js +1202 -1013
  18. package/.next/server/chunks/44.js +36 -6
  19. package/.next/server/chunks/675.js +35 -13
  20. package/.next/server/chunks/859.js +21 -4
  21. package/.next/server/chunks/865.js +478 -106
  22. package/.next/server/chunks/911.js +894 -773
  23. package/.next/server/middleware-build-manifest.js +1 -1
  24. package/.next/server/pages/404.html +1 -1
  25. package/.next/server/pages/404.js.nft.json +1 -1
  26. package/.next/server/pages/500.html +1 -1
  27. package/.next/server/pages/_app.js +3 -1
  28. package/.next/server/pages/_app.js.nft.json +1 -1
  29. package/.next/server/pages/api/dictionary/[locale]/[word].js +4 -2
  30. package/.next/server/pages/api/solve.js +51 -20
  31. package/.next/server/pages/api/verify.js +8 -5
  32. package/.next/server/pages/index.html +1 -1
  33. package/.next/server/pages/index.js +55 -65
  34. package/.next/server/pages/index.js.nft.json +1 -1
  35. package/.next/server/pages/index.json +1 -1
  36. package/.next/server/pages-manifest.json +2 -2
  37. package/.next/static/chunks/{framework-2c5cac93e8c637b5.js → framework-2c29dc3cd933590b.js} +2 -2
  38. package/.next/static/chunks/main-4dcb7f9b52833aba.js +1 -0
  39. package/.next/static/chunks/pages/_app-e7f3d1c9c09c8f91.js +32 -0
  40. package/.next/static/chunks/pages/index-82b2939158c7729f.js +1 -0
  41. package/.next/static/css/4e8b47fe382a8a8f.css +2 -0
  42. package/.next/static/css/cfae5256f1689f57.css +1 -0
  43. package/.next/static/{_yG9K-PD5kWT_p4sMdCSV → wNIKOJJzkMSJYb2nOL21o}/_buildManifest.js +1 -1
  44. package/.next/trace +51 -52
  45. package/package.json +14 -14
  46. package/src/components/Board/Board.module.scss +33 -0
  47. package/src/components/Board/Board.tsx +3 -0
  48. package/src/components/Board/BoardPure.tsx +25 -1
  49. package/src/components/Board/components/Cell/Cell.module.scss +4 -12
  50. package/src/components/Board/components/Cell/Cell.tsx +4 -0
  51. package/src/components/Board/components/InputPrompt/InputPrompt.tsx +1 -0
  52. package/src/components/Board/hooks/useBackgroundImage.tsx +24 -6
  53. package/src/components/PlainTiles/lib/createPlainTile.ts +4 -4
  54. package/src/components/Rack/components/InputPrompt/InputPrompt.tsx +1 -0
  55. package/src/components/Radio/Radio.module.scss +4 -0
  56. package/src/components/Radio/Radio.tsx +1 -0
  57. package/src/components/SeoMessage/SeoMessage.tsx +3 -3
  58. package/src/hooks/useAppLayout.ts +1 -2
  59. package/src/hooks/useLocalStorage.ts +5 -5
  60. package/src/i18n/constants.ts +31 -61
  61. package/src/lib/extractCharacters.test.ts +3 -3
  62. package/src/lib/extractCharactersByCase.test.ts +3 -3
  63. package/src/lib/getCellSize.ts +2 -2
  64. package/src/modals/MenuModal/MenuModal.module.scss +0 -1
  65. package/src/modals/MenuModal/MenuModal.tsx +3 -3
  66. package/src/modals/SettingsModal/SettingsModal.tsx +7 -3
  67. package/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.tsx +16 -7
  68. package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +1 -4
  69. package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.tsx +8 -8
  70. package/src/pages/_app.tsx +3 -1
  71. package/src/pages/api/dictionary/[locale]/[word].ts +6 -3
  72. package/src/pages/api/solve.ts +11 -7
  73. package/src/pages/api/verify.ts +11 -7
  74. package/src/pages/index.module.scss +0 -1
  75. package/src/parameters/index.ts +4 -6
  76. package/src/sdk/solve.ts +3 -3
  77. package/src/sdk/verify.ts +3 -3
  78. package/src/service-worker/routeSolveRequests.ts +3 -3
  79. package/src/state/localStorage.ts +6 -6
  80. package/src/state/sagas.ts +18 -7
  81. package/src/state/selectors.ts +10 -10
  82. package/src/state/slices/boardInitialState.ts +3 -3
  83. package/src/state/slices/boardSlice.ts +20 -6
  84. package/src/state/slices/settingsInitialState.ts +3 -4
  85. package/src/state/slices/settingsSlice.ts +5 -5
  86. package/src/styles/variables.scss +0 -9
  87. package/.next/static/chunks/main-0ecb9ccfcb6c9b24.js +0 -1
  88. package/.next/static/chunks/pages/_app-bea4539a6b8042de.js +0 -32
  89. package/.next/static/chunks/pages/index-4e8566409753e1c3.js +0 -1
  90. package/.next/static/css/58053f9594647860.css +0 -2
  91. package/.next/static/css/60e8258da7362a1a.css +0 -1
  92. package/src/i18n/i18n.module.scss +0 -27
  93. package/src/modals/SettingsModal/components/ConfigSetting/options.ts +0 -19
  94. package/src/modals/SettingsModal/components/ConfigSetting/types.ts +0 -9
  95. package/src/modals/SettingsModal/components/InputModeSetting/types.ts +0 -7
  96. package/src/modals/SettingsModal/components/LocaleSetting/types.ts +0 -9
  97. /package/.next/static/{_yG9K-PD5kWT_p4sMdCSV → wNIKOJJzkMSJYb2nOL21o}/_ssgManifest.js +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/scrabble-solver",
3
- "version": "2.12.1",
3
+ "version": "2.12.3",
4
4
  "description": "Scrabble Solver 2 - App",
5
5
  "engines": {
6
6
  "node": ">=16"
@@ -18,7 +18,7 @@
18
18
  "bugs": {
19
19
  "url": "https://github.com/kamilmielnik/scrabble-solver/issues"
20
20
  },
21
- "homepage": "https://github.com/kamilmielnik/scrabble-solver#readme",
21
+ "homepage": "https://scrabble-solver.org",
22
22
  "scripts": {
23
23
  "build": "env-cmd next build",
24
24
  "clean": "rimraf .next/ node_modules/",
@@ -28,20 +28,20 @@
28
28
  "start": "env-cmd next start -p 3333"
29
29
  },
30
30
  "dependencies": {
31
- "@floating-ui/react": "^0.22.2",
31
+ "@floating-ui/react": "^0.22.3",
32
32
  "@kamilmielnik/trie": "^2.0.1",
33
33
  "@reduxjs/toolkit": "^1.9.3",
34
- "@scrabble-solver/configs": "^2.12.1",
35
- "@scrabble-solver/constants": "^2.12.1",
36
- "@scrabble-solver/dictionaries": "^2.12.1",
37
- "@scrabble-solver/logger": "^2.12.1",
38
- "@scrabble-solver/solver": "^2.12.1",
39
- "@scrabble-solver/types": "^2.12.1",
40
- "@scrabble-solver/word-definitions": "^2.12.1",
34
+ "@scrabble-solver/configs": "^2.12.3",
35
+ "@scrabble-solver/constants": "^2.12.3",
36
+ "@scrabble-solver/dictionaries": "^2.12.3",
37
+ "@scrabble-solver/logger": "^2.12.3",
38
+ "@scrabble-solver/solver": "^2.12.3",
39
+ "@scrabble-solver/types": "^2.12.3",
40
+ "@scrabble-solver/word-definitions": "^2.12.3",
41
41
  "classnames": "^2.3.2",
42
42
  "include-media": "^2.0.0",
43
43
  "include-media-query-builder": "^1.1.0",
44
- "next": "^13.2.4",
44
+ "next": "^13.3.0",
45
45
  "normalize.css": "^8.0.1",
46
46
  "react": "^18.2.0",
47
47
  "react-cool-onclickoutside": "^1.7.0",
@@ -61,7 +61,7 @@
61
61
  "devDependencies": {
62
62
  "@svgr/webpack": "^7.0.0",
63
63
  "@types/classnames": "^2.3.0",
64
- "@types/react": "^18.0.31",
64
+ "@types/react": "^18.0.33",
65
65
  "@types/react-dom": "^18.0.11",
66
66
  "@types/react-highlight-words": "^0.16.4",
67
67
  "@types/react-modal": "^3.13.1",
@@ -71,8 +71,8 @@
71
71
  "@types/redux": "^3.6.31",
72
72
  "@types/redux-saga": "^0.10.5",
73
73
  "env-cmd": "^10.1.0",
74
- "sass": "^1.60.0",
74
+ "sass": "^1.61.0",
75
75
  "workbox-webpack-plugin": "^6.5.4"
76
76
  },
77
- "gitHead": "4f9a96d05558a90c90c5614c4929ce636f696f11"
77
+ "gitHead": "5400e276feee01557044e4e758b11e86b83fb085"
78
78
  }
@@ -33,3 +33,36 @@
33
33
  user-select: none;
34
34
  animation: var(--transition--duration) var(--transition--easing) hide;
35
35
  }
36
+
37
+ .iconContainer {
38
+ position: absolute;
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ pointer-events: none;
43
+ user-select: none;
44
+ }
45
+
46
+ .iconBackground,
47
+ .icon {
48
+ position: absolute;
49
+ top: calc((100% - var(--size)) / 2);
50
+ right: calc((100% - var(--size)) / 2);
51
+ bottom: calc((100% - var(--size)) / 2);
52
+ left: calc((100% - var(--size)) / 2);
53
+ width: var(--size);
54
+ height: var(--size);
55
+ }
56
+
57
+ .iconBackground {
58
+ --size: 80%;
59
+
60
+ border-radius: var(--border--radius);
61
+ background-color: var(--color--foreground--secondary);
62
+ }
63
+
64
+ .icon {
65
+ --size: 40%;
66
+
67
+ color: var(--color--white);
68
+ }
@@ -12,6 +12,7 @@ import { TRANSITION } from 'parameters';
12
12
  import {
13
13
  boardSlice,
14
14
  cellFilterSlice,
15
+ selectFilteredCells,
15
16
  selectInputMode,
16
17
  selectLocale,
17
18
  selectRowsWithCandidate,
@@ -33,6 +34,7 @@ const Board: FunctionComponent<Props> = ({ className }) => {
33
34
  const locale = useTypedSelector(selectLocale);
34
35
  const rows = useTypedSelector(selectRowsWithCandidate);
35
36
  const inputMode = useTypedSelector(selectInputMode);
37
+ const filteredCells = useTypedSelector(selectFilteredCells);
36
38
  const { cellSize } = useAppLayout();
37
39
  const [
38
40
  { activeIndex, direction, inputRefs },
@@ -147,6 +149,7 @@ const Board: FunctionComponent<Props> = ({ className }) => {
147
149
  <BoardPure
148
150
  className={className}
149
151
  cellSize={cellSize}
152
+ filteredCells={filteredCells}
150
153
  inputRefs={inputRefs}
151
154
  ref={ref}
152
155
  rows={rows}
@@ -12,12 +12,17 @@ import {
12
12
  RefObject,
13
13
  } from 'react';
14
14
 
15
+ import { FlagFill } from 'icons';
16
+ import { BORDER_WIDTH } from 'parameters';
17
+ import { Point } from 'types';
18
+
15
19
  import styles from './Board.module.scss';
16
20
  import { Cell } from './components';
17
21
 
18
22
  interface Props {
19
23
  className?: string;
20
24
  cellSize: number;
25
+ filteredCells: Point[];
21
26
  inputRefs: RefObject<HTMLInputElement>[][];
22
27
  rows: CellModel[][];
23
28
  style?: CSSProperties;
@@ -29,7 +34,10 @@ interface Props {
29
34
  }
30
35
 
31
36
  const BoardPure = forwardRef<HTMLDivElement, Props>(
32
- ({ className, cellSize, inputRefs, rows, style, onBlur, onChange, onFocus, onKeyDown, onPaste }, ref) => (
37
+ (
38
+ { className, cellSize, filteredCells, inputRefs, rows, style, onBlur, onChange, onFocus, onKeyDown, onPaste },
39
+ ref,
40
+ ) => (
33
41
  <div
34
42
  className={classNames(styles.board, className)}
35
43
  ref={ref}
@@ -57,6 +65,22 @@ const BoardPure = forwardRef<HTMLDivElement, Props>(
57
65
  ))}
58
66
  </Fragment>
59
67
  ))}
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
+ ))}
60
84
  </div>
61
85
  ),
62
86
  );
@@ -17,18 +17,14 @@
17
17
  }
18
18
 
19
19
  [dir='ltr'] & {
20
- &:nth-child(15n + 1),
21
- &:nth-child(15n + 2),
22
- &:nth-child(15n + 3) {
20
+ &.first3 {
23
21
  input {
24
22
  left: 0;
25
23
  clip-path: polygon(0 (100% / 3), (100% / 3) (100% / 3), (100% / 3) (200% / 3), 0 (200% / 3));
26
24
  }
27
25
  }
28
26
 
29
- &:nth-last-child(15n + 1),
30
- &:nth-last-child(15n + 2),
31
- &:nth-last-child(15n + 3) {
27
+ &.last3 {
32
28
  input {
33
29
  left: -200%;
34
30
  clip-path: polygon((200% / 3) (100% / 3), 100% (100% / 3), 100% (200% / 3), (200% / 3) (200% / 3));
@@ -37,9 +33,7 @@
37
33
  }
38
34
 
39
35
  [dir='rtl'] & {
40
- &:nth-child(15n + 1),
41
- &:nth-child(15n + 2),
42
- &:nth-child(15n + 3) {
36
+ &.first3 {
43
37
  input {
44
38
  left: -200%;
45
39
  right: 0;
@@ -47,9 +41,7 @@
47
41
  }
48
42
  }
49
43
 
50
- &:nth-last-child(15n + 1),
51
- &:nth-last-child(15n + 2),
52
- &:nth-last-child(15n + 3) {
44
+ &.last3 {
53
45
  input {
54
46
  left: 0;
55
47
  right: -200%;
@@ -13,6 +13,7 @@ import {
13
13
 
14
14
  import {
15
15
  selectCellIsValid,
16
+ selectConfig,
16
17
  selectInputMode,
17
18
  selectLocale,
18
19
  selectTilePoints,
@@ -52,6 +53,7 @@ const Cell: FunctionComponent<Props> = ({
52
53
  const { tile, x, y } = cell;
53
54
  const translate = useTranslate();
54
55
  const locale = useTypedSelector(selectLocale);
56
+ const config = useTypedSelector(selectConfig);
55
57
  const inputMode = useTypedSelector(selectInputMode);
56
58
  const points = useTypedSelector((state) => selectTilePoints(state, cell.tile));
57
59
  const isValid = useTypedSelector((state) => selectCellIsValid(state, cell));
@@ -98,6 +100,8 @@ const Cell: FunctionComponent<Props> = ({
98
100
  y: (y + 1).toLocaleString(locale),
99
101
  })}
100
102
  className={classNames(styles.tile, className, {
103
+ [styles.first3]: x < 3,
104
+ [styles.last3]: config.boardWidth - x - 1 < 3,
101
105
  [styles.sharpTopLeft]: cellTop?.hasTile() || cellLeft?.hasTile(),
102
106
  [styles.sharpTopRight]: cellTop?.hasTile() || cellRight?.hasTile(),
103
107
  [styles.sharpBottomLeft]: cellBottom?.hasTile() || cellLeft?.hasTile(),
@@ -37,6 +37,7 @@ const InputPrompt = forwardRef<HTMLFormElement, Props>(
37
37
  if (inputRef) {
38
38
  inputRef.focus();
39
39
  inputRef.select();
40
+ inputRef.scrollIntoView({ block: 'start', inline: 'center' });
40
41
  }
41
42
  }, [inputRef]);
42
43
 
@@ -9,7 +9,7 @@ import { useAppLayout, useMediaQueries } from 'hooks';
9
9
  import { FlagFill, Star } from 'icons';
10
10
  import { dataUrlToBlob, getTileSizes } from 'lib';
11
11
  import { BORDER_COLOR_LIGHT, BORDER_RADIUS, BORDER_WIDTH, COLOR_BONUS_START, COLOR_FILTERED } from 'parameters';
12
- import { selectCellFilter, selectConfig, store, useTypedSelector } from 'state';
12
+ import { selectConfig, store, useTypedSelector } from 'state';
13
13
  import { Point } from 'types';
14
14
 
15
15
  import { getBonusColor } from '../lib';
@@ -21,6 +21,7 @@ const VERTICAL_LINE = 'v';
21
21
  const BONUS = 'b';
22
22
  const BONUS_WORD_2 = 'b2';
23
23
  const BONUS_WORD_3 = 'b3';
24
+ const BONUS_WORD_4 = 'b4';
24
25
  const CELL_FILTER = 'c';
25
26
 
26
27
  const useBackgroundImage = () => {
@@ -29,7 +30,6 @@ const useBackgroundImage = () => {
29
30
  const borderRadius = isLessThanXs ? BORDER_RADIUS_XS : BORDER_RADIUS;
30
31
  const config = useTypedSelector(selectConfig);
31
32
  const center = { x: Math.floor(config.boardWidth / 2), y: Math.floor(config.boardHeight / 2) };
32
- const cellFilter = useTypedSelector(selectCellFilter);
33
33
  const viewBoxHeight = boardSize;
34
34
  const viewBoxWidth = boardSize;
35
35
  const bonusSize = cellSize * 0.8;
@@ -42,6 +42,7 @@ const useBackgroundImage = () => {
42
42
  const characterBonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_CHARACTER);
43
43
  const word2Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 2);
44
44
  const word3Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 3);
45
+ const word4Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 4);
45
46
 
46
47
  const getX = (point: Point): number => point.x * (cellSize + BORDER_WIDTH);
47
48
 
@@ -102,6 +103,23 @@ const useBackgroundImage = () => {
102
103
  </text>
103
104
  </symbol>
104
105
 
106
+ <symbol id={BONUS_WORD_4}>
107
+ <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
108
+
109
+ <text
110
+ dominantBaseline="central"
111
+ fill="white"
112
+ fontFamily="system-ui, sans-serif"
113
+ fontSize={fontSize}
114
+ fontWeight="bold"
115
+ textAnchor="middle"
116
+ x={fontOffset}
117
+ y={fontOffset}
118
+ >
119
+ x4
120
+ </text>
121
+ </symbol>
122
+
105
123
  <symbol id={CELL_FILTER}>
106
124
  <rect
107
125
  fill={COLOR_FILTERED}
@@ -138,6 +156,10 @@ const useBackgroundImage = () => {
138
156
  <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_3}`} x={getX(bonus)} y={getY(bonus)} />
139
157
  ))}
140
158
 
159
+ {word4Bonuses.map((bonus, index) => (
160
+ <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_4}`} x={getX(bonus)} y={getY(bonus)} />
161
+ ))}
162
+
141
163
  <rect
142
164
  fill={COLOR_BONUS_START}
143
165
  height={bonusSize}
@@ -154,10 +176,6 @@ const useBackgroundImage = () => {
154
176
  x={getX(center) + iconOffset}
155
177
  y={getY(center) + iconOffset}
156
178
  />
157
-
158
- {cellFilter.map((cell, index) => (
159
- <use key={index} href={`#${CELL_FILTER}`} x={getX(cell)} y={getY(cell)} />
160
- ))}
161
179
  </svg>
162
180
  </Provider>,
163
181
  );
@@ -1,11 +1,11 @@
1
- import { literaki } from '@scrabble-solver/configs';
2
- import { Locale } from '@scrabble-solver/types';
1
+ import { getConfig } from '@scrabble-solver/configs';
2
+ import { Game, Locale } from '@scrabble-solver/types';
3
3
 
4
4
  import {
5
5
  PLAIN_TILES_COLOR_DEFAULT,
6
6
  PLAIN_TILES_POINTS_COLORS,
7
- PLAIN_TILES_TILE_MAX_SCATTER,
8
7
  PLAIN_TILES_TILE_MAX_ROTATE,
8
+ PLAIN_TILES_TILE_MAX_SCATTER,
9
9
  PLAIN_TILES_TILE_SIZE,
10
10
  } from 'parameters';
11
11
 
@@ -16,7 +16,7 @@ import getY from './getY';
16
16
  import randomize from './randomize';
17
17
 
18
18
  const createPlainTile = ({ cellIndex, character, color, rowIndex, showPoints }: CreatePlainTileOptions): PlainTile => {
19
- const configPoints = literaki[Locale.EN_US].getCharacterPoints(character.toLowerCase());
19
+ const configPoints = getConfig(Game.Literaki, Locale.EN_US).getCharacterPoints(character.toLowerCase());
20
20
  const points = showPoints ? configPoints : undefined;
21
21
  const defaultColor =
22
22
  typeof configPoints === 'number' ? PLAIN_TILES_POINTS_COLORS[configPoints] : PLAIN_TILES_COLOR_DEFAULT;
@@ -52,6 +52,7 @@ const InputPrompt = forwardRef<HTMLFormElement, Props>(
52
52
  if (inputRef) {
53
53
  inputRef.focus();
54
54
  inputRef.select();
55
+ inputRef.scrollIntoView({ block: 'end', inline: 'center' });
55
56
  }
56
57
  }, [inputRef]);
57
58
 
@@ -22,6 +22,10 @@ $radio-box-size: $radio-size + 2 * $radio-inner-border;
22
22
  }
23
23
  }
24
24
  }
25
+
26
+ &.disabled {
27
+ opacity: var(--opacity--disabled);
28
+ }
25
29
  }
26
30
 
27
31
  .input {
@@ -17,6 +17,7 @@ const Radio: FunctionComponent<Props> = ({ checked, children, className, disable
17
17
  <label
18
18
  className={classNames(styles.radio, className, {
19
19
  [styles.checked]: checked,
20
+ [styles.disabled]: disabled,
20
21
  })}
21
22
  >
22
23
  <input
@@ -10,9 +10,9 @@ const INVISIBLE_STYLE: CSSProperties = {
10
10
 
11
11
  const SeoMessage: FunctionComponent = () => (
12
12
  <p style={INVISIBLE_STYLE}>
13
- Scrabble Solver 2 is a free and open-source analysis tool for Scrabble and Literaki. Quickly find top scoring words
14
- using given letters and board state. Available in English, French, German, Polish & Spanish. Source code is
15
- available on GitHub - contributions are welcome!
13
+ Scrabble Solver 2 is a free and open-source analysis tool for Scrabble, Super Scrabble &amp; Literaki. Quickly find
14
+ top scoring words using given letters and board state. Available in English, French, German, Polish & Spanish.
15
+ Source code is available on GitHub - contributions are welcome!
16
16
  </p>
17
17
  );
18
18
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  import {
4
4
  BOARD_TILE_SIZE_MAX,
5
- BOARD_TILE_SIZE_MIN,
6
5
  BORDER_WIDTH,
7
6
  BUTTON_HEIGHT,
8
7
  COMPONENTS_SPACING,
@@ -47,7 +46,7 @@ const useAppLayout = () => {
47
46
  : Math.max(solverHeight - bottomContainerHeight, 0);
48
47
  const cellWidth = (maxBoardWidth - (config.boardWidth + 1) * BORDER_WIDTH) / config.boardWidth;
49
48
  const cellHeight = (maxBoardHeight - (config.boardHeight + 1) * BORDER_WIDTH) / config.boardHeight;
50
- const cellSize = Math.min(Math.max(Math.min(cellWidth, cellHeight), BOARD_TILE_SIZE_MIN), BOARD_TILE_SIZE_MAX);
49
+ const cellSize = Math.min(Math.min(cellWidth, cellHeight), BOARD_TILE_SIZE_MAX);
51
50
  const boardSize = (cellSize + BORDER_WIDTH) * config.boardWidth + BORDER_WIDTH;
52
51
  const maxControlsWidth = tileSize * config.maximumCharactersCount + 2 * BORDER_WIDTH;
53
52
  const showResultsInModal = isLessThanL;
@@ -4,7 +4,7 @@ import {
4
4
  localStorage,
5
5
  selectAutoGroupTiles,
6
6
  selectBoard,
7
- selectConfigId,
7
+ selectGame,
8
8
  selectInputMode,
9
9
  selectLocale,
10
10
  selectRack,
@@ -14,7 +14,7 @@ import {
14
14
  const useLocalStorage = () => {
15
15
  const autoGroupTiles = useTypedSelector(selectAutoGroupTiles);
16
16
  const board = useTypedSelector(selectBoard);
17
- const configId = useTypedSelector(selectConfigId);
17
+ const game = useTypedSelector(selectGame);
18
18
  const inputMode = useTypedSelector(selectInputMode);
19
19
  const locale = useTypedSelector(selectLocale);
20
20
  const rack = useTypedSelector(selectRack);
@@ -32,10 +32,10 @@ const useLocalStorage = () => {
32
32
  }, [board]);
33
33
 
34
34
  useEffect(() => {
35
- if (configId) {
36
- localStorage.setConfigId(configId);
35
+ if (game) {
36
+ localStorage.setGame(game);
37
37
  }
38
- }, [configId]);
38
+ }, [game]);
39
39
 
40
40
  useEffect(() => {
41
41
  if (inputMode) {
@@ -4,12 +4,14 @@ import { FunctionComponent, SVGAttributes } from 'react';
4
4
 
5
5
  import { FlagDe, FlagEs, FlagFa, FlagFr, FlagGb, FlagPl, FlagUs } from 'icons';
6
6
 
7
- import styles from './i18n.module.scss';
8
-
9
7
  interface LocaleFeatures {
10
8
  comma: string;
11
9
  consonants: boolean;
12
10
  direction: 'ltr' | 'rtl';
11
+ Icon: FunctionComponent<SVGAttributes<SVGElement>>;
12
+ label: string;
13
+ locale: Locale;
14
+ name: string;
13
15
  separator: string;
14
16
  vowels: boolean;
15
17
  }
@@ -19,6 +21,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
19
21
  comma: COMMA_LATIN,
20
22
  consonants: true,
21
23
  direction: 'ltr',
24
+ Icon: FlagDe,
25
+ label: 'Deutsch',
26
+ locale: Locale.DE_DE,
27
+ name: 'German',
22
28
  separator: `${COMMA_LATIN} `,
23
29
  vowels: true,
24
30
  },
@@ -26,6 +32,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
26
32
  comma: COMMA_LATIN,
27
33
  consonants: true,
28
34
  direction: 'ltr',
35
+ Icon: FlagGb,
36
+ label: 'English (GB)',
37
+ locale: Locale.EN_GB,
38
+ name: 'English (GB)',
29
39
  separator: `${COMMA_LATIN} `,
30
40
  vowels: true,
31
41
  },
@@ -33,6 +43,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
33
43
  comma: COMMA_LATIN,
34
44
  consonants: true,
35
45
  direction: 'ltr',
46
+ Icon: FlagUs,
47
+ label: 'English (US)',
48
+ locale: Locale.EN_US,
49
+ name: 'English (US)',
36
50
  separator: `${COMMA_LATIN} `,
37
51
  vowels: true,
38
52
  },
@@ -40,6 +54,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
40
54
  comma: COMMA_LATIN,
41
55
  consonants: true,
42
56
  direction: 'ltr',
57
+ Icon: FlagEs,
58
+ label: 'Español',
59
+ locale: Locale.ES_ES,
60
+ name: 'Spanish',
43
61
  separator: `${COMMA_LATIN} `,
44
62
  vowels: true,
45
63
  },
@@ -47,6 +65,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
47
65
  comma: COMMA_ARABIC,
48
66
  consonants: false,
49
67
  direction: 'rtl',
68
+ Icon: FlagFa,
69
+ label: 'فارسی',
70
+ locale: Locale.FA_IR,
71
+ name: 'Persian',
50
72
  separator: `${COMMA_ARABIC} `,
51
73
  vowels: false,
52
74
  },
@@ -54,6 +76,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
54
76
  comma: COMMA_LATIN,
55
77
  consonants: true,
56
78
  direction: 'ltr',
79
+ Icon: FlagFr,
80
+ label: 'Français',
81
+ locale: Locale.FR_FR,
82
+ name: 'French',
57
83
  separator: `${COMMA_LATIN} `,
58
84
  vowels: true,
59
85
  },
@@ -61,67 +87,11 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
61
87
  comma: COMMA_LATIN,
62
88
  consonants: true,
63
89
  direction: 'ltr',
64
- separator: `${COMMA_LATIN} `,
65
- vowels: true,
66
- },
67
- };
68
-
69
- interface Flag {
70
- className: string;
71
- Icon: FunctionComponent<SVGAttributes<SVGElement>>;
72
- label: string;
73
- name: string;
74
- value: Locale;
75
- }
76
-
77
- export const LOCALE_FLAGS: Record<Locale, Flag> = {
78
- [Locale.DE_DE]: {
79
- className: styles.de,
80
- Icon: FlagDe,
81
- label: 'Deutsch',
82
- name: 'German',
83
- value: Locale.DE_DE,
84
- },
85
- [Locale.EN_GB]: {
86
- className: styles.gb,
87
- Icon: FlagGb,
88
- label: 'English (GB)',
89
- name: 'English (GB)',
90
- value: Locale.EN_GB,
91
- },
92
- [Locale.EN_US]: {
93
- className: styles.us,
94
- Icon: FlagUs,
95
- label: 'English (US)',
96
- name: 'English (US)',
97
- value: Locale.EN_US,
98
- },
99
- [Locale.ES_ES]: {
100
- className: styles.es,
101
- Icon: FlagEs,
102
- label: 'Español',
103
- name: 'Spanish',
104
- value: Locale.ES_ES,
105
- },
106
- [Locale.FA_IR]: {
107
- className: styles.fa,
108
- Icon: FlagFa,
109
- label: 'فارسی',
110
- name: 'Persian',
111
- value: Locale.FA_IR,
112
- },
113
- [Locale.FR_FR]: {
114
- className: styles.fr,
115
- Icon: FlagFr,
116
- label: 'Français',
117
- name: 'French',
118
- value: Locale.FR_FR,
119
- },
120
- [Locale.PL_PL]: {
121
- className: styles.pl,
122
90
  Icon: FlagPl,
123
91
  label: 'Polski',
92
+ locale: Locale.PL_PL,
124
93
  name: 'Polish',
125
- value: Locale.PL_PL,
94
+ separator: `${COMMA_LATIN} `,
95
+ vowels: true,
126
96
  },
127
97
  };
@@ -1,6 +1,6 @@
1
- import { scrabble } from '@scrabble-solver/configs';
1
+ import { getConfig } from '@scrabble-solver/configs';
2
2
  import { BLANK } from '@scrabble-solver/constants';
3
- import { Locale } from '@scrabble-solver/types';
3
+ import { Game, Locale } from '@scrabble-solver/types';
4
4
 
5
5
  import extractCharacters from './extractCharacters';
6
6
 
@@ -16,7 +16,7 @@ const tests = [
16
16
 
17
17
  describe('extractCharacters', () => {
18
18
  const locale = Locale.ES_ES;
19
- const config = scrabble[locale];
19
+ const config = getConfig(Game.Scrabble, locale);
20
20
 
21
21
  for (const { input, expected } of tests) {
22
22
  it(`[${locale}] "${input}"`, () => {
@@ -1,6 +1,6 @@
1
- import { scrabble } from '@scrabble-solver/configs';
1
+ import { getConfig } from '@scrabble-solver/configs';
2
2
  import { BLANK } from '@scrabble-solver/constants';
3
- import { Locale } from '@scrabble-solver/types';
3
+ import { Game, Locale } from '@scrabble-solver/types';
4
4
 
5
5
  import extractCharactersByCase from './extractCharactersByCase';
6
6
 
@@ -21,7 +21,7 @@ const tests = [
21
21
 
22
22
  describe('extractCharactersByCase', () => {
23
23
  const locale = Locale.ES_ES;
24
- const config = scrabble[locale];
24
+ const config = getConfig(Game.Scrabble, locale);
25
25
 
26
26
  for (const { input, expected } of tests) {
27
27
  it(`[${locale}] "${input}"`, () => {
@@ -1,12 +1,12 @@
1
1
  import { Config } from '@scrabble-solver/types';
2
2
 
3
- import { BOARD_CELL_BORDER_WIDTH, BOARD_TILE_SIZE_MAX, BOARD_TILE_SIZE_MIN } from 'parameters';
3
+ import { BOARD_CELL_BORDER_WIDTH, BOARD_TILE_SIZE_MAX } from 'parameters';
4
4
 
5
5
  const getCellSize = (config: Config, width: number, height: number): number => {
6
6
  const maxWidth = (width - BOARD_CELL_BORDER_WIDTH) / config.boardWidth - BOARD_CELL_BORDER_WIDTH;
7
7
  const maxHeight = (height - BOARD_CELL_BORDER_WIDTH) / config.boardHeight - BOARD_CELL_BORDER_WIDTH;
8
8
  const cellSize = Math.min(maxWidth, maxHeight);
9
- return Math.floor(Math.min(Math.max(cellSize, BOARD_TILE_SIZE_MIN), BOARD_TILE_SIZE_MAX));
9
+ return Math.floor(Math.min(cellSize, BOARD_TILE_SIZE_MAX));
10
10
  };
11
11
 
12
12
  export default getCellSize;
@@ -42,7 +42,6 @@
42
42
 
43
43
  .flag {
44
44
  position: fixed;
45
- width: calc(var(--button--icon--size) * var(--aspect-ratio));
46
45
  height: var(--button--icon--size);
47
46
  border-radius: var(--border--radius);
48
47
  box-shadow: 0 0 0 1px var(--box-shadow--color);