scrabble-solver 2.11.5 → 2.11.7

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 (118) hide show
  1. package/README.md +2 -2
  2. package/lerna.json +1 -1
  3. package/nx.json +19 -0
  4. package/package.json +1 -1
  5. package/packages/configs/package.json +3 -3
  6. package/packages/constants/package.json +1 -1
  7. package/packages/dictionaries/package-lock.json +2 -2
  8. package/packages/dictionaries/package.json +4 -4
  9. package/packages/logger/package-lock.json +2 -2
  10. package/packages/logger/package.json +1 -1
  11. package/packages/scrabble-solver/.next/BUILD_ID +1 -1
  12. package/packages/scrabble-solver/.next/build-manifest.json +11 -11
  13. package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
  14. package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
  15. package/packages/scrabble-solver/.next/cache/next-server.js.nft.json +1 -1
  16. package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
  17. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
  18. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/0.pack +0 -0
  19. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack +0 -0
  20. package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
  21. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
  22. package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
  23. package/packages/scrabble-solver/.next/prerender-manifest.json +1 -1
  24. package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
  25. package/packages/scrabble-solver/.next/server/chunks/277.js +778 -746
  26. package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
  27. package/packages/scrabble-solver/.next/server/pages/404.html +1 -5
  28. package/packages/scrabble-solver/.next/server/pages/404.js.nft.json +1 -1
  29. package/packages/scrabble-solver/.next/server/pages/500.html +1 -1
  30. package/packages/scrabble-solver/.next/server/pages/_app.js +9 -2
  31. package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
  32. package/packages/scrabble-solver/.next/server/pages/api/solve.js +29 -1
  33. package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
  34. package/packages/scrabble-solver/.next/server/pages/index.js +10 -4
  35. package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
  36. package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
  37. package/packages/scrabble-solver/.next/static/chunks/framework-2c5cac93e8c637b5.js +49 -0
  38. package/packages/scrabble-solver/.next/static/chunks/pages/{404-d30fe85d005ce32b.js → 404-ca203fa27afc37d8.js} +1 -1
  39. package/packages/scrabble-solver/.next/static/chunks/pages/_app-e89a3c225b87516a.js +28 -0
  40. package/packages/scrabble-solver/.next/static/chunks/pages/index-58744f49bf6b891f.js +1 -0
  41. package/packages/scrabble-solver/.next/static/css/34adfcf12a7d9bb6.css +1 -0
  42. package/packages/scrabble-solver/.next/static/css/edaeaa48321b4cf2.css +2 -0
  43. package/packages/scrabble-solver/.next/static/uhB6d-q63uRC6RubwepLq/_buildManifest.js +1 -0
  44. package/packages/scrabble-solver/.next/trace +50 -50
  45. package/packages/scrabble-solver/package-lock.json +2 -2
  46. package/packages/scrabble-solver/package.json +8 -8
  47. package/packages/scrabble-solver/src/components/Board/Board.module.scss +2 -59
  48. package/packages/scrabble-solver/src/components/Board/Board.tsx +22 -10
  49. package/packages/scrabble-solver/src/components/Board/BoardPure.tsx +13 -8
  50. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +8 -144
  51. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.tsx +18 -37
  52. package/packages/scrabble-solver/src/components/Board/hooks/index.ts +1 -0
  53. package/packages/scrabble-solver/src/components/Board/hooks/useBackgroundImage.tsx +170 -0
  54. package/packages/scrabble-solver/src/components/Board/lib/getBonusColor.ts +18 -0
  55. package/packages/scrabble-solver/src/components/Board/lib/index.ts +1 -0
  56. package/packages/scrabble-solver/src/components/Dictionary/Dictionary.module.scss +0 -1
  57. package/packages/scrabble-solver/src/components/DictionaryInput/DictionaryInput.tsx +5 -3
  58. package/packages/scrabble-solver/src/components/EmptyState/EmptyState.module.scss +0 -1
  59. package/packages/scrabble-solver/src/components/Key/Key.module.scss +1 -1
  60. package/packages/scrabble-solver/src/components/PlainTiles/PlainTiles.tsx +0 -10
  61. package/packages/scrabble-solver/src/components/PlainTiles/Tile.tsx +1 -4
  62. package/packages/scrabble-solver/src/components/Results/Cell.tsx +2 -2
  63. package/packages/scrabble-solver/src/components/Results/Result.tsx +4 -8
  64. package/packages/scrabble-solver/src/components/Results/Results.module.scss +5 -4
  65. package/packages/scrabble-solver/src/components/Solver/Solver.tsx +2 -2
  66. package/packages/scrabble-solver/src/components/Tooltip/Tooltip.module.scss +2 -0
  67. package/packages/scrabble-solver/src/components/index.ts +0 -2
  68. package/packages/scrabble-solver/src/hooks/useAppLayout.ts +1 -0
  69. package/packages/scrabble-solver/src/hooks/useDirection.ts +2 -2
  70. package/packages/scrabble-solver/src/hooks/useLanguage.ts +2 -2
  71. package/packages/scrabble-solver/src/i18n/constants.ts +25 -16
  72. package/packages/scrabble-solver/src/i18n/de.json +2 -2
  73. package/packages/scrabble-solver/src/i18n/en.json +2 -2
  74. package/packages/scrabble-solver/src/i18n/es.json +2 -2
  75. package/packages/scrabble-solver/src/i18n/fa.json +2 -2
  76. package/packages/scrabble-solver/src/i18n/fr.json +2 -2
  77. package/packages/scrabble-solver/src/i18n/pl.json +2 -2
  78. package/packages/scrabble-solver/src/lib/dataUrlToBlob.ts +20 -0
  79. package/packages/scrabble-solver/src/lib/index.ts +1 -0
  80. package/packages/scrabble-solver/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +0 -1
  81. package/packages/scrabble-solver/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.module.scss +0 -1
  82. package/packages/scrabble-solver/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +0 -5
  83. package/packages/scrabble-solver/src/pages/_app.tsx +1 -3
  84. package/packages/scrabble-solver/src/pages/index.tsx +1 -3
  85. package/packages/scrabble-solver/src/parameters/index.ts +33 -2
  86. package/packages/scrabble-solver/src/state/index.ts +1 -1
  87. package/packages/scrabble-solver/src/state/sagas.ts +3 -4
  88. package/packages/scrabble-solver/src/state/store.ts +34 -0
  89. package/packages/scrabble-solver/src/styles/global.scss +4 -8
  90. package/packages/scrabble-solver/src/styles/mixins.scss +0 -1
  91. package/packages/scrabble-solver/src/styles/variables.scss +5 -5
  92. package/packages/solver/package-lock.json +2 -2
  93. package/packages/solver/package.json +5 -5
  94. package/packages/types/package.json +2 -2
  95. package/packages/word-definitions/package-lock.json +156 -6
  96. package/packages/word-definitions/package.json +2 -2
  97. package/packages/word-lists/package-lock.json +2 -2
  98. package/packages/word-lists/package.json +2 -2
  99. package/screencast.gif +0 -0
  100. package/img/screencast.gif +0 -0
  101. package/packages/scrabble-solver/.next/static/UzQCOB6CHhyOupkEq8oZM/_buildManifest.js +0 -1
  102. package/packages/scrabble-solver/.next/static/chunks/framework-2c79e2a64abdb08b.js +0 -33
  103. package/packages/scrabble-solver/.next/static/chunks/pages/_app-e27464a187a58684.js +0 -28
  104. package/packages/scrabble-solver/.next/static/chunks/pages/index-3fd280f406cc00fd.js +0 -1
  105. package/packages/scrabble-solver/.next/static/css/4bd04cebe207859c.css +0 -1
  106. package/packages/scrabble-solver/.next/static/css/5b3b78170f4c5875.css +0 -2
  107. package/packages/scrabble-solver/src/components/Board/components/Cell/CellPure.tsx +0 -93
  108. package/packages/scrabble-solver/src/components/Board/components/Cell/lib.ts +0 -59
  109. package/packages/scrabble-solver/src/components/SvgFontCss/SvgFontCss.tsx +0 -14
  110. package/packages/scrabble-solver/src/components/SvgFontCss/createCss.ts +0 -11
  111. package/packages/scrabble-solver/src/components/SvgFontCss/createStyle.ts +0 -9
  112. package/packages/scrabble-solver/src/components/SvgFontCss/createSvg.ts +0 -10
  113. package/packages/scrabble-solver/src/components/SvgFontCss/index.ts +0 -1
  114. package/packages/scrabble-solver/src/components/SvgFontFix/SvgFontFix.module.scss +0 -5
  115. package/packages/scrabble-solver/src/components/SvgFontFix/SvgFontFix.tsx +0 -21
  116. package/packages/scrabble-solver/src/components/SvgFontFix/index.ts +0 -1
  117. package/packages/scrabble-solver/src/state/createAppStore.ts +0 -38
  118. /package/packages/scrabble-solver/.next/static/{UzQCOB6CHhyOupkEq8oZM → uhB6d-q63uRC6RubwepLq}/_ssgManifest.js +0 -0
@@ -0,0 +1,170 @@
1
+ /* eslint-disable max-statements */
2
+
3
+ import { BONUS_CHARACTER, BONUS_WORD } from '@scrabble-solver/constants';
4
+ import { useMemo } from 'react';
5
+ import { renderToString } from 'react-dom/server';
6
+ import { Provider } from 'react-redux';
7
+
8
+ import { useAppLayout } from 'hooks';
9
+ import { FlagFill, Star } from 'icons';
10
+ import { dataUrlToBlob, getTileSizes } from 'lib';
11
+ import { BORDER_COLOR_LIGHT, BORDER_RADIUS, BORDER_WIDTH, COLOR_BONUS_START, COLOR_FILTERED } from 'parameters';
12
+ import { selectCellFilter, selectConfig, store, useTypedSelector } from 'state';
13
+ import { Point } from 'types';
14
+
15
+ import { getBonusColor } from '../lib';
16
+
17
+ const HORIZONTAL_LINE = 'h';
18
+ const VERTICAL_LINE = 'v';
19
+ const BONUS = 'b';
20
+ const BONUS_WORD_2 = 'b2';
21
+ const BONUS_WORD_3 = 'b3';
22
+ const CELL_FILTER = 'c';
23
+
24
+ const useBackgroundImage = () => {
25
+ const { boardSize, cellSize } = useAppLayout();
26
+ const config = useTypedSelector(selectConfig);
27
+ const center = { x: Math.floor(config.boardWidth / 2), y: Math.floor(config.boardHeight / 2) };
28
+ const cellFilter = useTypedSelector(selectCellFilter);
29
+ const viewBoxHeight = boardSize;
30
+ const viewBoxWidth = boardSize;
31
+ const bonusSize = cellSize * 0.8;
32
+ const bonusOffset = cellSize * 0.1;
33
+ const iconSize = cellSize * 0.4;
34
+ const iconOffset = (cellSize - iconSize) / 2;
35
+ const { tileFontSize } = getTileSizes(cellSize);
36
+ const fontSize = tileFontSize * 0.6;
37
+ const fontOffset = cellSize / 2;
38
+ const characterBonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_CHARACTER);
39
+ const word2Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 2);
40
+ const word3Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 3);
41
+
42
+ const getX = (point: Point): number => point.x * (cellSize + BORDER_WIDTH);
43
+
44
+ const getY = (point: Point): number => point.y * (cellSize + BORDER_WIDTH);
45
+
46
+ const backgroundSvg = renderToString(
47
+ <Provider store={store}>
48
+ <svg
49
+ height={viewBoxHeight}
50
+ viewBox={`0 0 ${viewBoxWidth} ${viewBoxHeight}`}
51
+ width={viewBoxWidth}
52
+ xmlns="http://www.w3.org/2000/svg"
53
+ >
54
+ <defs>
55
+ <symbol id={HORIZONTAL_LINE}>
56
+ <rect fill={BORDER_COLOR_LIGHT} height={1} width={viewBoxWidth} />
57
+ </symbol>
58
+
59
+ <symbol id={VERTICAL_LINE}>
60
+ <rect fill={BORDER_COLOR_LIGHT} height={viewBoxHeight} width={1} />
61
+ </symbol>
62
+
63
+ <symbol id={BONUS}>
64
+ <rect height={bonusSize} rx={BORDER_RADIUS} width={bonusSize} x={bonusOffset} y={bonusOffset} />
65
+ </symbol>
66
+
67
+ <symbol id={BONUS_WORD_2}>
68
+ <rect height={bonusSize} rx={BORDER_RADIUS} width={bonusSize} x={bonusOffset} y={bonusOffset} />
69
+
70
+ <text
71
+ dominantBaseline="central"
72
+ fill="white"
73
+ fontFamily="system-ui, sans-serif"
74
+ fontSize={fontSize}
75
+ fontWeight="bold"
76
+ textAnchor="middle"
77
+ x={fontOffset}
78
+ y={fontOffset}
79
+ >
80
+ x2
81
+ </text>
82
+ </symbol>
83
+
84
+ <symbol id={BONUS_WORD_3}>
85
+ <rect height={bonusSize} rx={BORDER_RADIUS} width={bonusSize} x={bonusOffset} y={bonusOffset} />
86
+
87
+ <text
88
+ dominantBaseline="central"
89
+ fill="white"
90
+ fontFamily="system-ui, sans-serif"
91
+ fontSize={fontSize}
92
+ fontWeight="bold"
93
+ textAnchor="middle"
94
+ x={fontOffset}
95
+ y={fontOffset}
96
+ >
97
+ x3
98
+ </text>
99
+ </symbol>
100
+
101
+ <symbol id={CELL_FILTER}>
102
+ <rect
103
+ fill={COLOR_FILTERED}
104
+ height={bonusSize}
105
+ rx={BORDER_RADIUS}
106
+ width={bonusSize}
107
+ x={bonusOffset}
108
+ y={bonusOffset}
109
+ />
110
+
111
+ <FlagFill color="white" height={iconSize} width={iconSize} x={iconOffset} y={iconOffset} />
112
+ </symbol>
113
+ </defs>
114
+
115
+ <rect fill="white" height={viewBoxHeight} rx={BORDER_RADIUS} width={viewBoxWidth} x="0" y="0" />
116
+
117
+ {Array.from({ length: config.boardHeight - 1 }).map((_value, index) => (
118
+ <use key={index} href={`#${HORIZONTAL_LINE}`} y={(index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH} />
119
+ ))}
120
+
121
+ {Array.from({ length: config.boardWidth - 1 }).map((_value, index) => (
122
+ <use key={index} href={`#${VERTICAL_LINE}`} x={(index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH} />
123
+ ))}
124
+
125
+ {characterBonuses.map((bonus, index) => (
126
+ <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS}`} x={getX(bonus)} y={getY(bonus)} />
127
+ ))}
128
+
129
+ {word2Bonuses.map((bonus, index) => (
130
+ <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_2}`} x={getX(bonus)} y={getY(bonus)} />
131
+ ))}
132
+
133
+ {word3Bonuses.map((bonus, index) => (
134
+ <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_3}`} x={getX(bonus)} y={getY(bonus)} />
135
+ ))}
136
+
137
+ <rect
138
+ fill={COLOR_BONUS_START}
139
+ height={bonusSize}
140
+ rx={BORDER_RADIUS}
141
+ width={bonusSize}
142
+ x={getX(center) + bonusOffset}
143
+ y={getY(center) + bonusOffset}
144
+ />
145
+
146
+ <Star
147
+ color="white"
148
+ height={iconSize}
149
+ width={iconSize}
150
+ x={getX(center) + iconOffset}
151
+ y={getY(center) + iconOffset}
152
+ />
153
+
154
+ {cellFilter.map((cell, index) => (
155
+ <use key={index} href={`#${CELL_FILTER}`} x={getX(cell)} y={getY(cell)} />
156
+ ))}
157
+ </svg>
158
+ </Provider>,
159
+ );
160
+
161
+ const encodedSvg = globalThis.btoa(backgroundSvg);
162
+ const dataUrl = `data:image/svg+xml;base64,${encodedSvg}`;
163
+ const blob = useMemo(() => dataUrlToBlob(dataUrl), [dataUrl]);
164
+ const blobUrl = useMemo(() => URL.createObjectURL(blob), [blob]);
165
+ const url = `url(${blobUrl})`;
166
+
167
+ return url;
168
+ };
169
+
170
+ export default useBackgroundImage;
@@ -0,0 +1,18 @@
1
+ import { BONUS_WORD } from '@scrabble-solver/constants';
2
+ import { Bonus } from '@scrabble-solver/types';
3
+
4
+ import { COLOR_BONUS_CHARACTER, COLOR_BONUS_CHARACTER_MULTIPLIER, COLOR_BONUS_WORD } from 'parameters';
5
+
6
+ const getBonusColor = (bonus: Bonus): string => {
7
+ if (bonus.type === BONUS_WORD) {
8
+ return COLOR_BONUS_WORD[bonus.multiplier];
9
+ }
10
+
11
+ if (bonus.score) {
12
+ return COLOR_BONUS_CHARACTER[bonus.score];
13
+ }
14
+
15
+ return COLOR_BONUS_CHARACTER_MULTIPLIER[bonus.multiplier];
16
+ };
17
+
18
+ export default getBonusColor;
@@ -1 +1,2 @@
1
+ export { default as getBonusColor } from './getBonusColor';
1
2
  export { default as getPositionInGrid } from './getPositionInGrid';
@@ -49,7 +49,6 @@
49
49
 
50
50
  .word {
51
51
  margin-bottom: var(--spacing--m);
52
- font-family: var(--font--family--title);
53
52
  font-size: var(--font--size--h2);
54
53
  text-transform: uppercase;
55
54
  }
@@ -1,9 +1,9 @@
1
- import { COMMA_ARABIC, COMMA_LATIN } from '@scrabble-solver/constants';
2
1
  import classNames from 'classnames';
3
2
  import { ChangeEvent, FormEvent, FunctionComponent } from 'react';
4
3
  import { useDispatch } from 'react-redux';
5
4
 
6
- import { dictionarySlice, selectDictionary, useTranslate, useTypedSelector } from 'state';
5
+ import { LOCALE_FEATURES } from 'i18n';
6
+ import { dictionarySlice, selectDictionary, selectLocale, useTranslate, useTypedSelector } from 'state';
7
7
 
8
8
  import styles from './DictionaryInput.module.scss';
9
9
 
@@ -14,7 +14,9 @@ interface Props {
14
14
  const DictionaryInput: FunctionComponent<Props> = ({ className }) => {
15
15
  const dispatch = useDispatch();
16
16
  const translate = useTranslate();
17
+ const locale = useTypedSelector(selectLocale);
17
18
  const { input } = useTypedSelector(selectDictionary);
19
+ const { comma } = LOCALE_FEATURES[locale];
18
20
 
19
21
  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
20
22
  dispatch(dictionarySlice.actions.changeInput(event.target.value));
@@ -29,7 +31,7 @@ const DictionaryInput: FunctionComponent<Props> = ({ className }) => {
29
31
  <form className={classNames(styles.dictionaryInput, className)} onSubmit={handleSubmit}>
30
32
  <input
31
33
  className={styles.input}
32
- pattern={`.*[^\\s${COMMA_ARABIC}${COMMA_LATIN}].*`}
34
+ pattern={`.*[^\\s${comma}].*`}
33
35
  placeholder={translate('dictionary.input.placeholder')}
34
36
  required
35
37
  title={translate('dictionary.input.title')}
@@ -1,6 +1,5 @@
1
1
  .emptyState {
2
2
  padding: var(--spacing--xl);
3
- font-family: var(--font--family);
4
3
  color: var(--color--foreground--secondary);
5
4
  text-align: center;
6
5
  text-transform: none;
@@ -1,4 +1,4 @@
1
- $key-size: 28px;
1
+ $key-size: 36px;
2
2
  $icon-size: 15px;
3
3
 
4
4
  .key {
@@ -1,11 +1,6 @@
1
1
  import classNames from 'classnames';
2
2
  import { CSSProperties, FunctionComponent, useMemo } from 'react';
3
3
 
4
- import { LOCALE_FEATURES } from 'i18n';
5
- import { selectLocale, useTypedSelector } from 'state';
6
-
7
- import SvgFontCss from '../SvgFontCss';
8
-
9
4
  import { createPlainTiles, getViewbox } from './lib';
10
5
  import styles from './PlainTiles.module.scss';
11
6
  import Tile from './Tile';
@@ -21,8 +16,6 @@ interface Props {
21
16
  }
22
17
 
23
18
  const PlainTiles: FunctionComponent<Props> = ({ className, color, content, dropShadow, showPoints, style, wave }) => {
24
- const locale = useTypedSelector(selectLocale);
25
- const { fontFamily } = LOCALE_FEATURES[locale];
26
19
  const tiles = useMemo(() => createPlainTiles({ color, content, showPoints }), [color, content, showPoints]);
27
20
 
28
21
  return (
@@ -35,14 +28,11 @@ const PlainTiles: FunctionComponent<Props> = ({ className, color, content, dropS
35
28
  viewBox={getViewbox(content)}
36
29
  xmlns="http://www.w3.org/2000/svg"
37
30
  >
38
- <SvgFontCss fontFamily={fontFamily} />
39
-
40
31
  {tiles.map((tile, index) => (
41
32
  <Tile
42
33
  character={tile.character}
43
34
  className={styles.tile}
44
35
  color={tile.color}
45
- fontFamily={fontFamily}
46
36
  key={index}
47
37
  points={tile.points}
48
38
  size={tile.size}
@@ -4,7 +4,6 @@ interface Props {
4
4
  character: string;
5
5
  className?: string;
6
6
  color: string;
7
- fontFamily: string;
8
7
  points?: number;
9
8
  size: number;
10
9
  transform?: string;
@@ -12,13 +11,12 @@ interface Props {
12
11
  y: number;
13
12
  }
14
13
 
15
- const Tile: FunctionComponent<Props> = ({ character, className, color, fontFamily, points, size, transform, x, y }) => (
14
+ const Tile: FunctionComponent<Props> = ({ character, className, color, points, size, transform, x, y }) => (
16
15
  <g className={className} transform={transform}>
17
16
  <rect fill={color} height={size} rx={size * 0.15} width={size} x={x} y={y} />
18
17
 
19
18
  <text
20
19
  dominantBaseline="central"
21
- fontFamily={fontFamily}
22
20
  fontSize={size * 0.6}
23
21
  fontWeight="bold"
24
22
  textAnchor="middle"
@@ -31,7 +29,6 @@ const Tile: FunctionComponent<Props> = ({ character, className, color, fontFamil
31
29
  {typeof points === 'number' && (
32
30
  <text
33
31
  dominantBaseline="text-after-edge"
34
- fontFamily={fontFamily}
35
32
  fontSize={size * 0.25}
36
33
  fontWeight="bold"
37
34
  textAnchor="end"
@@ -22,9 +22,9 @@ const Cell: FunctionComponent<Props> = ({ className, translationKey, tooltip, va
22
22
  const triggerProps = useTooltip(`${translate(translationKey)}: ${tooltip || formattedValue}`);
23
23
 
24
24
  return (
25
- <span className={classNames(styles.cell, className)} {...triggerProps}>
25
+ <div className={classNames(styles.cell, className)} {...triggerProps}>
26
26
  {formattedValue}
27
- </span>
27
+ </div>
28
28
  );
29
29
  };
30
30
 
@@ -30,10 +30,10 @@ const Result = ({ data, index, style }: Props): ReactElement => {
30
30
  const ref = useRef<HTMLButtonElement>(null);
31
31
  const columns = useColumns();
32
32
  const locale = useTypedSelector(selectLocale);
33
- const { consonants, vowels } = LOCALE_FEATURES[locale];
33
+ const { consonants, direction, separator, vowels } = LOCALE_FEATURES[locale];
34
34
  const result = results[index];
35
35
  const isMatching = useTypedSelector((state) => selectIsResultMatching(state, index));
36
- const otherWords = result.words.slice(1).join(' / ').toLocaleUpperCase();
36
+ const words = direction === 'rtl' ? [...result.words].reverse() : result.words;
37
37
  const enabledColumns = Object.fromEntries(columns.map((column) => [column.id, true]));
38
38
 
39
39
  const handleClick: MouseEventHandler = (event) => onClick(result, event);
@@ -60,11 +60,7 @@ const Result = ({ data, index, style }: Props): ReactElement => {
60
60
  >
61
61
  <span className={styles.resultContent}>
62
62
  {enabledColumns[ResultColumn.Word] && (
63
- <Cell
64
- className={styles.word}
65
- translationKey="common.word"
66
- value={`${result.word.toLocaleUpperCase()}${otherWords.length > 0 ? ` (${otherWords})` : ''}`}
67
- />
63
+ <Cell className={styles.word} translationKey="common.word" value={result.word} />
68
64
  )}
69
65
 
70
66
  {enabledColumns[ResultColumn.TilesCount] && (
@@ -87,7 +83,7 @@ const Result = ({ data, index, style }: Props): ReactElement => {
87
83
  <Cell
88
84
  className={styles.stat}
89
85
  translationKey="common.words"
90
- tooltip={`${result.wordsCount} (${result.words.join(' / ').toLocaleUpperCase()})`}
86
+ tooltip={`${result.wordsCount.toLocaleString(locale)} (${words.join(separator)})`}
91
87
  value={result.wordsCount}
92
88
  />
93
89
  )}
@@ -10,7 +10,6 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
10
10
  background: var(--color--background--element);
11
11
  border: var(--border);
12
12
  border-radius: var(--border--radius);
13
- font-family: var(--font--family--title);
14
13
  }
15
14
 
16
15
  .emptyState {
@@ -61,7 +60,6 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
61
60
  cursor: pointer;
62
61
  text-transform: uppercase;
63
62
  transition: var(--transition);
64
- padding: var(--spacing--s) 0;
65
63
 
66
64
  &:focus,
67
65
  &:hover {
@@ -136,6 +134,7 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
136
134
  display: flex;
137
135
  align-items: center;
138
136
  justify-content: space-between;
137
+ height: 100%;
139
138
 
140
139
  .word {
141
140
  @include ellipsis;
@@ -146,8 +145,10 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
146
145
  display: flex;
147
146
  align-items: center;
148
147
  justify-content: center;
148
+ height: 100%;
149
149
  padding: 0 var(--spacing--s);
150
150
  gap: var(--spacing--s);
151
+ line-height: var(--results--item--height);
151
152
 
152
153
  .result &:first-child,
153
154
  .headerButton:first-child & {
@@ -181,12 +182,12 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
181
182
  }
182
183
 
183
184
  .word {
184
- flex: 1 0 180px;
185
+ flex: 1 0;
185
186
  text-transform: uppercase;
186
187
  }
187
188
 
188
189
  .stat {
189
- $width: 50px;
190
+ $width: 55px;
190
191
 
191
192
  flex: 0 0 $width;
192
193
  max-width: $width;
@@ -34,7 +34,7 @@ const Solver: FunctionComponent<Props> = ({ className, onShowResults }) => {
34
34
  const dispatch = useDispatch();
35
35
  const translate = useTranslate();
36
36
  const isTouchDevice = useIsTouchDevice();
37
- const { cellSize, maxControlsWidth, showCompactControls, showFloatingSolveButton, tileSize } = useAppLayout();
37
+ const { maxControlsWidth, showCompactControls, showFloatingSolveButton, tileSize } = useAppLayout();
38
38
  const error = useTypedSelector(selectSolveError);
39
39
  const isOutdated = useTypedSelector(selectAreResultsOutdated);
40
40
  const resultCandidate = useTypedSelector(selectResultCandidate);
@@ -93,7 +93,7 @@ const Solver: FunctionComponent<Props> = ({ className, onShowResults }) => {
93
93
  <div className={styles.container}>
94
94
  <div className={styles.content}>
95
95
  <form className={styles.boardContainer} onSubmit={handleSubmit}>
96
- <Board cellSize={cellSize} className={styles.board} />
96
+ <Board className={styles.board} />
97
97
  <input className={styles.submitInput} tabIndex={-1} type="submit" />
98
98
  </form>
99
99
 
@@ -1,10 +1,12 @@
1
1
  .tooltip {
2
+ max-width: var(--tooltip--max-width);
2
3
  padding: var(--spacing--s) var(--spacing--m);
3
4
  box-shadow: var(--box-shadow);
4
5
  border-radius: var(--border--radius);
5
6
  background-color: var(--color--tooltip--background);
6
7
  color: var(--color--tooltip--foreground);
7
8
  z-index: var(--z-index--tooltip);
9
+ text-align: center;
8
10
  }
9
11
 
10
12
  .arrow {
@@ -22,7 +22,5 @@ export { default as ResultsInput } from './ResultsInput';
22
22
  export { default as SeoMessage } from './SeoMessage';
23
23
  export { default as Solver } from './Solver';
24
24
  export { default as Spinner } from './Spinner';
25
- export { default as SvgFontCss } from './SvgFontCss';
26
- export { default as SvgFontFix } from './SvgFontFix';
27
25
  export { default as Tile } from './Tile';
28
26
  export { useTooltip } from './Tooltip';
@@ -60,6 +60,7 @@ const useAppLayout = () => {
60
60
  return {
61
61
  actionsWidth: 2 * BUTTON_HEIGHT - BORDER_WIDTH,
62
62
  animateTile: !isLessThanXs,
63
+ boardSize,
63
64
  cellSize,
64
65
  dictionaryHeight,
65
66
  isModalFullWidth: isLessThanS,
@@ -1,9 +1,9 @@
1
- import { useLayoutEffect } from 'react';
1
+ import { useEffect } from 'react';
2
2
 
3
3
  import { noop } from 'lib';
4
4
 
5
5
  const useDirection = (direction: 'ltr' | 'rtl') => {
6
- useLayoutEffect(() => {
6
+ useEffect(() => {
7
7
  const html = document.body.parentElement;
8
8
 
9
9
  if (!html) {
@@ -1,9 +1,9 @@
1
- import { useLayoutEffect } from 'react';
1
+ import { useEffect } from 'react';
2
2
 
3
3
  import { noop } from 'lib';
4
4
 
5
5
  const useLanguage = (language: string) => {
6
- useLayoutEffect(() => {
6
+ useEffect(() => {
7
7
  const html = document.body.parentElement;
8
8
 
9
9
  if (!html) {
@@ -1,3 +1,4 @@
1
+ import { COMMA_ARABIC, COMMA_LATIN } from '@scrabble-solver/constants';
1
2
  import { Locale } from '@scrabble-solver/types';
2
3
  import { FunctionComponent, SVGAttributes } from 'react';
3
4
 
@@ -6,53 +7,61 @@ import { FlagDe, FlagEs, FlagFa, FlagFr, FlagGb, FlagPl, FlagUs } from 'icons';
6
7
  import styles from './i18n.module.scss';
7
8
 
8
9
  interface LocaleFeatures {
9
- direction: 'ltr' | 'rtl';
10
- fontFamily: string;
10
+ comma: string;
11
11
  consonants: boolean;
12
+ direction: 'ltr' | 'rtl';
13
+ separator: string;
12
14
  vowels: boolean;
13
15
  }
14
16
 
15
17
  export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
16
18
  [Locale.DE_DE]: {
17
- direction: 'ltr',
18
- fontFamily: 'Open Sans',
19
+ comma: COMMA_LATIN,
19
20
  consonants: true,
21
+ direction: 'ltr',
22
+ separator: `${COMMA_LATIN} `,
20
23
  vowels: true,
21
24
  },
22
25
  [Locale.EN_GB]: {
23
- direction: 'ltr',
24
- fontFamily: 'Open Sans',
26
+ comma: COMMA_LATIN,
25
27
  consonants: true,
28
+ direction: 'ltr',
29
+ separator: `${COMMA_LATIN} `,
26
30
  vowels: true,
27
31
  },
28
32
  [Locale.EN_US]: {
29
- direction: 'ltr',
30
- fontFamily: 'Open Sans',
33
+ comma: COMMA_LATIN,
31
34
  consonants: true,
35
+ direction: 'ltr',
36
+ separator: `${COMMA_LATIN} `,
32
37
  vowels: true,
33
38
  },
34
39
  [Locale.ES_ES]: {
35
- direction: 'ltr',
36
- fontFamily: 'Open Sans',
40
+ comma: COMMA_LATIN,
37
41
  consonants: true,
42
+ direction: 'ltr',
43
+ separator: `${COMMA_LATIN} `,
38
44
  vowels: true,
39
45
  },
40
46
  [Locale.FA_IR]: {
41
- direction: 'rtl',
42
- fontFamily: 'Vazirmatn',
47
+ comma: COMMA_ARABIC,
43
48
  consonants: false,
49
+ direction: 'rtl',
50
+ separator: `${COMMA_ARABIC} `,
44
51
  vowels: false,
45
52
  },
46
53
  [Locale.FR_FR]: {
47
- direction: 'ltr',
48
- fontFamily: 'Open Sans',
54
+ comma: COMMA_LATIN,
49
55
  consonants: true,
56
+ direction: 'ltr',
57
+ separator: `${COMMA_LATIN} `,
50
58
  vowels: true,
51
59
  },
52
60
  [Locale.PL_PL]: {
53
- direction: 'ltr',
54
- fontFamily: 'Open Sans',
61
+ comma: COMMA_LATIN,
55
62
  consonants: true,
63
+ direction: 'ltr',
64
+ separator: `${COMMA_LATIN} `,
56
65
  vowels: true,
57
66
  },
58
67
  };
@@ -1,9 +1,9 @@
1
1
  {
2
- "cell.filter-cell": "Zielort - klicken zum Wechseln",
2
+ "cell.filter-cell": "Zielort",
3
3
  "cell.set-blank": "Als Blanko markieren",
4
4
  "cell.set-not-blank": "Nicht als Blanko markieren",
5
5
  "cell.tile.location": "Brett: Stein ({{x}}, {{y}})",
6
- "cell.toggle-direction": "Schreibrichtung - klicken zum Wechseln",
6
+ "cell.toggle-direction": "Schreibrichtung",
7
7
  "common.blanks": "Blankos",
8
8
  "common.clear": "Löschen",
9
9
  "common.close": "Schließen",
@@ -1,9 +1,9 @@
1
1
  {
2
- "cell.filter-cell": "Target destination - click to toggle",
2
+ "cell.filter-cell": "Target destination",
3
3
  "cell.set-blank": "Mark it a blank",
4
4
  "cell.set-not-blank": "Mark it not a blank",
5
5
  "cell.tile.location": "Board: tile ({{x}}, {{y}})",
6
- "cell.toggle-direction": "Typing direction - click to toggle",
6
+ "cell.toggle-direction": "Typing direction",
7
7
  "common.blanks": "Blanks",
8
8
  "common.clear": "Clear",
9
9
  "common.close": "Close",
@@ -1,9 +1,9 @@
1
1
  {
2
- "cell.filter-cell": "Destino objetivo: haga clic para alternar",
2
+ "cell.filter-cell": "Destino objetivo",
3
3
  "cell.set-blank": "Marcar como en blanco",
4
4
  "cell.set-not-blank": "Marcar como no en blanco",
5
5
  "cell.tile.location": "Tablero: espacio ({{x}}, {{y}})",
6
- "cell.toggle-direction": "Dirección de escritura: haga clic para alternar",
6
+ "cell.toggle-direction": "Dirección de escritura",
7
7
  "common.blanks": "Blancos",
8
8
  "common.clear": "Borrar",
9
9
  "common.close": "Cerrar",
@@ -1,9 +1,9 @@
1
1
  {
2
- "cell.filter-cell": "مقصد - کلیک برای تغییر",
2
+ "cell.filter-cell": "مقصد",
3
3
  "cell.set-blank": "علامت گذاری به عنوان خالی",
4
4
  "cell.set-not-blank": "علامت گذاری به عنوان غیر خالی",
5
- "cell.toggle-direction": "جهت تایپ - کلیک برای تغییر",
6
5
  "cell.tile.location": "({{x}}، {{y}}) کاشی: صفحه",
6
+ "cell.toggle-direction": "جهت تایپ",
7
7
  "common.blanks": "خالی",
8
8
  "common.clear": "پاک کردن",
9
9
  "common.close": "بستن",
@@ -1,9 +1,9 @@
1
1
  {
2
- "cell.filter-cell": "Destination cible - cliquer pour changer",
2
+ "cell.filter-cell": "Destination cible",
3
3
  "cell.set-blank": "Marquer comme vide",
4
4
  "cell.set-not-blank": "Marquer comme non vide",
5
5
  "cell.tile.location": "Plateau: la case ({{x}}, {{y}})",
6
- "cell.toggle-direction": "Direction d'écriture - cliquer pour changer",
6
+ "cell.toggle-direction": "Direction d'écriture",
7
7
  "common.blanks": "Cases vides",
8
8
  "common.clear": "Effacer",
9
9
  "common.close": "Fermer",
@@ -1,9 +1,9 @@
1
1
  {
2
- "cell.filter-cell": "Miejsce docelowe - kliknij aby zmienić",
2
+ "cell.filter-cell": "Miejsce docelowe",
3
3
  "cell.set-blank": "Oznacz jako blank",
4
4
  "cell.set-not-blank": "Oznacz jako nie blank",
5
5
  "cell.tile.location": "Plansza: płytka ({{x}}, {{y}})",
6
- "cell.toggle-direction": "Kierunek wpisywania - kliknij aby zmienić",
6
+ "cell.toggle-direction": "Kierunek wpisywania",
7
7
  "common.blanks": "Blanki",
8
8
  "common.clear": "Wyczyść",
9
9
  "common.close": "Zamknij",