@scrabble-solver/scrabble-solver 2.15.21 → 2.15.23

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 (70) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +16 -16
  3. package/.next/cache/.previewinfo +1 -1
  4. package/.next/cache/.rscinfo +1 -1
  5. package/.next/cache/.tsbuildinfo +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/client-production/index.pack.old +0 -0
  9. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  10. package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  11. package/.next/cache/webpack/server-production/0.pack +0 -0
  12. package/.next/cache/webpack/server-production/index.pack +0 -0
  13. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  14. package/.next/diagnostics/framework.json +1 -1
  15. package/.next/prerender-manifest.json +4 -4
  16. package/.next/routes-manifest.json +1 -1
  17. package/.next/server/chunks/712.js +1 -1
  18. package/.next/server/middleware-build-manifest.js +1 -1
  19. package/.next/server/pages/404.html +1 -1
  20. package/.next/server/pages/500.html +1 -1
  21. package/.next/server/pages/_app.js.nft.json +1 -1
  22. package/.next/server/pages/_error.js.nft.json +1 -1
  23. package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
  24. package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
  25. package/.next/server/pages/api/solve.js.nft.json +1 -1
  26. package/.next/server/pages/api/verify.js.nft.json +1 -1
  27. package/.next/server/pages/index.html +1 -1
  28. package/.next/server/pages/index.js +1 -1
  29. package/.next/server/pages/index.js.nft.json +1 -1
  30. package/.next/server/pages/index.json +1 -1
  31. package/.next/server/pages/not-found.html +1 -1
  32. package/.next/static/chunks/{framework-93e577820ffb186a.js → framework-f7982db529c0590c.js} +1 -1
  33. package/.next/static/chunks/{main-f63f5bd5e9581250.js → main-ca07941c8a544c9a.js} +1 -1
  34. package/.next/static/chunks/pages/_app-a3061a79f91b3796.js +7 -0
  35. package/.next/static/chunks/pages/index-6c5b582e7afc063c.js +1 -0
  36. package/.next/static/chunks/webpack-1a7a166b34ed8d24.js +1 -0
  37. package/.next/static/{8q_9JlEMMcIoT2TpStZYx → zFnoNsMXUha-MKjuZn6AN}/_buildManifest.js +1 -1
  38. package/.next/trace +21 -21
  39. package/.next/trace-build +1 -1
  40. package/package.json +20 -20
  41. package/src/app-layout/AppLayoutContext.tsx +25 -0
  42. package/src/app-layout/index.ts +1 -0
  43. package/src/{hooks/useAppLayout.ts → app-layout/useAppLayoutValue.ts} +4 -6
  44. package/src/components/Board/Board.tsx +1 -1
  45. package/src/components/Board/components/InputPrompt/InputPrompt.tsx +9 -2
  46. package/src/components/Board/hooks/useBackgroundImage.tsx +228 -165
  47. package/src/components/Board/hooks/useBoardStyle.ts +1 -1
  48. package/src/components/Board/hooks/useFloatingActions.ts +1 -1
  49. package/src/components/Dictionary/Dictionary.tsx +1 -1
  50. package/src/components/DictionaryInput/DictionaryInput.tsx +2 -2
  51. package/src/components/Logo/Logo.tsx +1 -1
  52. package/src/components/NavButtons/NavButtons.tsx +1 -1
  53. package/src/components/Rack/Rack.tsx +1 -1
  54. package/src/components/Rack/components/InputPrompt/InputPrompt.tsx +4 -4
  55. package/src/components/Results/Header.tsx +2 -1
  56. package/src/components/Results/Result.tsx +2 -1
  57. package/src/components/ResultsInput/ResultsInput.tsx +2 -2
  58. package/src/components/Solver/Solver.tsx +2 -1
  59. package/src/components/Tile/Tile.tsx +1 -1
  60. package/src/hooks/index.ts +0 -1
  61. package/src/lib/isMac.ts +0 -1
  62. package/src/modals/ResultsModal/ResultsModal.tsx +1 -1
  63. package/src/pages/_app.tsx +7 -4
  64. package/src/pages/index.tsx +42 -21
  65. package/tsconfig.json +3 -2
  66. package/tsconfig.tsbuildinfo +1 -1
  67. package/.next/static/chunks/pages/_app-ac2ab0ef8f59ba92.js +0 -3
  68. package/.next/static/chunks/pages/index-3be9ce84ae513eaa.js +0 -1
  69. package/.next/static/chunks/webpack-49a83516933d4cf2.js +0 -1
  70. /package/.next/static/{8q_9JlEMMcIoT2TpStZYx → zFnoNsMXUha-MKjuZn6AN}/_ssgManifest.js +0 -0
package/.next/trace-build CHANGED
@@ -1 +1 @@
1
- [{"name":"run-typescript","duration":1730400,"timestamp":2793256903,"id":13,"parentId":1,"tags":{},"startTime":1775242438301,"traceId":"e97b27e6c0eb60ec"},{"name":"run-webpack","duration":5983835,"timestamp":2794989610,"id":15,"parentId":1,"tags":{},"startTime":1775242440034,"traceId":"e97b27e6c0eb60ec"},{"name":"static-check","duration":166818,"timestamp":2801042702,"id":2011,"parentId":1,"tags":{},"startTime":1775242446087,"traceId":"e97b27e6c0eb60ec"},{"name":"static-generation","duration":817182,"timestamp":2801217087,"id":2029,"parentId":1,"tags":{},"startTime":1775242446261,"traceId":"e97b27e6c0eb60ec"},{"name":"collect-build-traces","duration":3570908,"timestamp":2801209841,"id":2026,"parentId":1,"tags":{},"startTime":1775242446254,"traceId":"e97b27e6c0eb60ec"},{"name":"telemetry-flush","duration":20,"timestamp":2804781801,"id":2041,"parentId":1,"tags":{},"startTime":1775242449826,"traceId":"e97b27e6c0eb60ec"},{"name":"next-build","duration":11769581,"timestamp":2793012244,"id":1,"tags":{"buildMode":"default","version":"16.2.2","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1775242438057,"traceId":"e97b27e6c0eb60ec"}]
1
+ [{"name":"run-typescript","duration":1926354,"timestamp":430470878,"id":13,"parentId":1,"tags":{},"startTime":1775678234571,"traceId":"dfda121f630d27e7"},{"name":"run-webpack","duration":6504061,"timestamp":432399576,"id":15,"parentId":1,"tags":{},"startTime":1775678236500,"traceId":"dfda121f630d27e7"},{"name":"static-check","duration":195373,"timestamp":438980589,"id":2016,"parentId":1,"tags":{},"startTime":1775678243081,"traceId":"dfda121f630d27e7"},{"name":"static-generation","duration":873316,"timestamp":439183871,"id":2034,"parentId":1,"tags":{},"startTime":1775678243284,"traceId":"dfda121f630d27e7"},{"name":"collect-build-traces","duration":3881714,"timestamp":439176421,"id":2031,"parentId":1,"tags":{},"startTime":1775678243277,"traceId":"dfda121f630d27e7"},{"name":"telemetry-flush","duration":20,"timestamp":443059296,"id":2046,"parentId":1,"tags":{},"startTime":1775678247160,"traceId":"dfda121f630d27e7"},{"name":"next-build","duration":12832915,"timestamp":430226405,"id":1,"tags":{"buildMode":"default","version":"16.2.3","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1775678234327,"traceId":"dfda121f630d27e7"}]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/scrabble-solver",
3
- "version": "2.15.21",
3
+ "version": "2.15.23",
4
4
  "description": "Scrabble Solver 2 - App",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,33 +25,33 @@
25
25
  "type-check": "tsc --noEmit"
26
26
  },
27
27
  "dependencies": {
28
- "@floating-ui/react": "^0.27.16",
28
+ "@floating-ui/react": "^0.27.19",
29
29
  "@kamilmielnik/trie": "^4.0.0",
30
- "@reduxjs/toolkit": "^2.11.1",
31
- "@scrabble-solver/configs": "^2.15.21",
32
- "@scrabble-solver/constants": "^2.15.21",
33
- "@scrabble-solver/dictionaries": "^2.15.21",
34
- "@scrabble-solver/logger": "^2.15.21",
35
- "@scrabble-solver/solver": "^2.15.21",
36
- "@scrabble-solver/types": "^2.15.21",
37
- "@scrabble-solver/word-definitions": "^2.15.21",
30
+ "@reduxjs/toolkit": "^2.11.2",
31
+ "@scrabble-solver/configs": "^2.15.23",
32
+ "@scrabble-solver/constants": "^2.15.23",
33
+ "@scrabble-solver/dictionaries": "^2.15.23",
34
+ "@scrabble-solver/logger": "^2.15.23",
35
+ "@scrabble-solver/solver": "^2.15.23",
36
+ "@scrabble-solver/types": "^2.15.23",
37
+ "@scrabble-solver/word-definitions": "^2.15.23",
38
38
  "classnames": "^2.5.1",
39
39
  "env-cmd": "^11.0.0",
40
40
  "include-media": "^2.0.0",
41
41
  "include-media-query-builder": "^1.1.0",
42
- "next": "^16.0.10",
42
+ "next": "^16.2.3",
43
43
  "normalize.css": "^8.0.1",
44
- "react": "^19.2.3",
44
+ "react": "^19.2.5",
45
45
  "react-cool-onclickoutside": "^1.7.0",
46
- "react-dom": "^19.2.3",
46
+ "react-dom": "^19.2.5",
47
47
  "react-highlight-words": "^0.21.0",
48
48
  "react-modal": "^3.16.3",
49
49
  "react-redux": "^9.2.0",
50
- "react-window": "^2.2.3",
50
+ "react-window": "^2.2.7",
51
51
  "redux-saga": "^1.4.2",
52
52
  "store2": "^2.14.4",
53
- "transliteration": "^2.3.5",
54
- "use-debounce": "^10.0.6",
53
+ "transliteration": "^2.6.1",
54
+ "use-debounce": "^10.1.1",
55
55
  "workbox-expiration": "^7.4.0",
56
56
  "workbox-precaching": "^7.4.0",
57
57
  "workbox-routing": "^7.4.0",
@@ -60,14 +60,14 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "@svgr/webpack": "^8.1.0",
63
- "@types/react": "^19.2.7",
63
+ "@types/react": "^19.2.14",
64
64
  "@types/react-dom": "^19.2.3",
65
- "@types/react-highlight-words": "^0.20.0",
65
+ "@types/react-highlight-words": "^0.20.1",
66
66
  "@types/react-modal": "^3.16.3",
67
67
  "@types/react-portal": "^4.0.7",
68
68
  "@types/react-redux": "^7.1.34",
69
69
  "@types/redux": "^3.6.31",
70
- "sass": "^1.96.0"
70
+ "sass": "^1.99.0"
71
71
  },
72
- "gitHead": "0bac5e768652a92a24dcfdb650aa43d02559f687"
72
+ "gitHead": "1414493526a57ebb4fd93fdb006badcc9d6afb6a"
73
73
  }
@@ -0,0 +1,25 @@
1
+ import { createContext, type FunctionComponent, type ReactNode, useContext } from 'react';
2
+
3
+ import { type AppLayout, useAppLayoutValue } from './useAppLayoutValue';
4
+
5
+ const AppLayoutContext = createContext<AppLayout | null>(null);
6
+
7
+ export const useAppLayout = () => {
8
+ const context = useContext(AppLayoutContext);
9
+
10
+ if (context === null) {
11
+ throw new Error('useAppLayout must be used within an <AppLayoutProvider />');
12
+ }
13
+
14
+ return context;
15
+ };
16
+
17
+ interface Props {
18
+ children: ReactNode;
19
+ }
20
+
21
+ export const AppLayoutProvider: FunctionComponent<Props> = ({ children }) => {
22
+ const appLayout = useAppLayoutValue();
23
+
24
+ return <AppLayoutContext.Provider value={appLayout}>{children}</AppLayoutContext.Provider>;
25
+ };
@@ -0,0 +1 @@
1
+ export { AppLayoutProvider, useAppLayout } from './AppLayoutContext';
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable max-statements */
2
2
 
3
+ import { useColumns, useIsTouchDevice, useMediaQueries, useViewportSize } from 'hooks';
3
4
  import {
4
5
  BOARD_TILE_SIZE_MAX,
5
6
  BORDER_WIDTH,
@@ -22,12 +23,7 @@ import {
22
23
  import { selectConfig, selectShowCoordinates, useTypedSelector } from 'state';
23
24
  import { type ResultColumnId } from 'types';
24
25
 
25
- import { useColumns } from './useColumns';
26
- import { useIsTouchDevice } from './useIsTouchDevice';
27
- import { useMediaQueries } from './useMediaQueries';
28
- import { useViewportSize } from './useViewportSize';
29
-
30
- export const useAppLayout = () => {
26
+ export const useAppLayoutValue = () => {
31
27
  const { viewportHeight, viewportWidth } = useViewportSize();
32
28
  const config = useTypedSelector(selectConfig);
33
29
  const showCoordinates = useTypedSelector(selectShowCoordinates);
@@ -112,3 +108,5 @@ export const useAppLayout = () => {
112
108
  tileSize,
113
109
  };
114
110
  };
111
+
112
+ export type AppLayout = ReturnType<typeof useAppLayoutValue>;
@@ -7,7 +7,7 @@ import { type CSSProperties, type FocusEventHandler, type FunctionComponent, use
7
7
  import useOnclickOutside from 'react-cool-onclickoutside';
8
8
  import { useDispatch } from 'react-redux';
9
9
 
10
- import { useAppLayout } from 'hooks';
10
+ import { useAppLayout } from 'app-layout';
11
11
  import { LOCALE_FEATURES } from 'i18n';
12
12
  import { TRANSITION } from 'parameters';
13
13
  import {
@@ -1,5 +1,12 @@
1
1
  import classNames from 'classnames';
2
- import { type FormEventHandler, forwardRef, type HTMLProps, type MouseEventHandler, useEffect, useState } from 'react';
2
+ import {
3
+ forwardRef,
4
+ type HTMLProps,
5
+ type MouseEventHandler,
6
+ type SubmitEventHandler,
7
+ useEffect,
8
+ useState,
9
+ } from 'react';
3
10
 
4
11
  import { Check } from 'icons';
5
12
  import { useTranslate } from 'state';
@@ -27,7 +34,7 @@ export const InputPrompt = forwardRef<HTMLFormElement, Props>(
27
34
  // On iOS it helps with losing focus too early which makes Actions disappear
28
35
  const handleMouseDown: MouseEventHandler = (event) => event.preventDefault();
29
36
 
30
- const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
37
+ const handleSubmit: SubmitEventHandler<HTMLFormElement> = (event) => {
31
38
  event.preventDefault();
32
39
  event.stopPropagation();
33
40
  onSubmit(input);
@@ -1,11 +1,12 @@
1
1
  /* eslint-disable max-lines, max-statements */
2
2
 
3
3
  import { BONUS_CHARACTER, BONUS_WORD } from '@scrabble-solver/constants';
4
- import { useMemo } from 'react';
4
+ import { useCallback, useMemo } from 'react';
5
5
  import { renderToString } from 'react-dom/server';
6
6
  import { Provider } from 'react-redux';
7
7
 
8
- import { useAppLayout, useMediaQueries } from 'hooks';
8
+ import { useAppLayout } from 'app-layout';
9
+ import { useMediaQueries } from 'hooks';
9
10
  import { LOCALE_FEATURES } from 'i18n';
10
11
  import { Star } from 'icons';
11
12
  import { getTileSizes } from 'lib';
@@ -32,7 +33,10 @@ export const useBackgroundImage = () => {
32
33
  const { isLessThanXs } = useMediaQueries();
33
34
  const borderRadius = isLessThanXs ? BORDER_RADIUS_XS : BORDER_RADIUS;
34
35
  const config = useTypedSelector(selectConfig);
35
- const center = { x: Math.floor(config.boardWidth / 2), y: Math.floor(config.boardHeight / 2) };
36
+ const center = useMemo(
37
+ () => ({ x: Math.floor(config.boardWidth / 2), y: Math.floor(config.boardHeight / 2) }),
38
+ [config.boardHeight, config.boardWidth],
39
+ );
36
40
  const viewBoxHeight = boardHeight;
37
41
  const viewBoxWidth = boardWidth;
38
42
  const bonusSize = cellSize * 0.8;
@@ -42,174 +46,233 @@ export const useBackgroundImage = () => {
42
46
  const { tileFontSize } = getTileSizes(cellSize);
43
47
  const fontSize = tileFontSize * 0.6;
44
48
  const fontOffset = cellSize / 2;
45
- const characterBonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_CHARACTER);
46
- const word2Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 2);
47
- const word3Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 3);
48
- const word4Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 4);
49
-
50
- const getX = (point: Point): number =>
51
- (direction === 'ltr' ? coordinatesSize : 0) + point.x * (cellSize + BORDER_WIDTH);
52
-
53
- const getY = (point: Point): number => coordinatesSize + point.y * (cellSize + BORDER_WIDTH);
54
-
55
- const backgroundSvg = renderToString(
56
- <Provider store={store}>
57
- <svg
58
- height={viewBoxHeight}
59
- viewBox={`0 0 ${viewBoxWidth} ${viewBoxHeight}`}
60
- width={viewBoxWidth}
61
- xmlns="http://www.w3.org/2000/svg"
62
- >
63
- <defs>
64
- <symbol id={HORIZONTAL_LINE}>
65
- <rect fill={BORDER_COLOR_LIGHT} height={GRID_LINE_SIZE} width={viewBoxWidth} />
66
- </symbol>
67
-
68
- <symbol id={VERTICAL_LINE}>
69
- <rect fill={BORDER_COLOR_LIGHT} height={viewBoxHeight} width={GRID_LINE_SIZE} />
70
- </symbol>
71
-
72
- <symbol id={BONUS}>
73
- <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
74
- </symbol>
75
-
76
- <symbol id={BONUS_WORD_2}>
77
- <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
78
-
79
- <text
80
- dominantBaseline="central"
81
- fill="white"
82
- fontFamily="system-ui, sans-serif"
83
- fontSize={fontSize}
84
- fontWeight="bold"
85
- textAnchor="middle"
86
- x={fontOffset}
87
- y={fontOffset}
88
- >
89
- x2
90
- </text>
91
- </symbol>
92
-
93
- <symbol id={BONUS_WORD_3}>
94
- <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
95
-
96
- <text
97
- dominantBaseline="central"
98
- fill="white"
99
- fontFamily="system-ui, sans-serif"
100
- fontSize={fontSize}
101
- fontWeight="bold"
102
- textAnchor="middle"
103
- x={fontOffset}
104
- y={fontOffset}
105
- >
106
- x3
107
- </text>
108
- </symbol>
109
-
110
- <symbol id={BONUS_WORD_4}>
111
- <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
112
-
113
- <text
114
- dominantBaseline="central"
115
- fill="white"
116
- fontFamily="system-ui, sans-serif"
117
- fontSize={fontSize}
118
- fontWeight="bold"
119
- textAnchor="middle"
120
- x={fontOffset}
121
- y={fontOffset}
122
- >
123
- x4
124
- </text>
125
- </symbol>
126
- </defs>
127
-
128
- {showCoordinates === 'hidden' && (
129
- <rect fill="white" height={viewBoxHeight} rx={borderRadius} width={viewBoxWidth} x="0" y="0" />
130
- )}
131
-
132
- {showCoordinates !== 'hidden' && (
133
- <>
134
- <rect fill={COLOR_BACKGROUND} height={viewBoxHeight} rx={borderRadius} width={viewBoxWidth} x="0" y="0" />
135
- <rect
136
- fill="white"
137
- height={viewBoxHeight - coordinatesSize}
138
- width={viewBoxWidth - coordinatesSize}
139
- x={direction === 'ltr' ? coordinatesSize : 0}
140
- y={coordinatesSize}
141
- />
49
+ const characterBonuses = useMemo(
50
+ () => config.bonuses.filter((bonus) => bonus.type === BONUS_CHARACTER),
51
+ [config.bonuses],
52
+ );
53
+ const word2Bonuses = useMemo(
54
+ () => config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 2),
55
+ [config.bonuses],
56
+ );
57
+ const word3Bonuses = useMemo(
58
+ () => config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 3),
59
+ [config.bonuses],
60
+ );
61
+ const word4Bonuses = useMemo(
62
+ () => config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 4),
63
+ [config.bonuses],
64
+ );
65
+
66
+ const getX = useCallback(
67
+ (point: Point): number => (direction === 'ltr' ? coordinatesSize : 0) + point.x * (cellSize + BORDER_WIDTH),
68
+ [coordinatesSize, cellSize, direction],
69
+ );
70
+
71
+ const getY = useCallback(
72
+ (point: Point): number => coordinatesSize + point.y * (cellSize + BORDER_WIDTH),
73
+ [coordinatesSize, cellSize],
74
+ );
75
+
76
+ const backgroundSvg = useMemo(
77
+ () =>
78
+ renderToString(
79
+ <Provider store={store}>
80
+ <svg
81
+ height={viewBoxHeight}
82
+ viewBox={`0 0 ${viewBoxWidth} ${viewBoxHeight}`}
83
+ width={viewBoxWidth}
84
+ xmlns="http://www.w3.org/2000/svg"
85
+ >
86
+ <defs>
87
+ <symbol id={HORIZONTAL_LINE}>
88
+ <rect fill={BORDER_COLOR_LIGHT} height={GRID_LINE_SIZE} width={viewBoxWidth} />
89
+ </symbol>
90
+
91
+ <symbol id={VERTICAL_LINE}>
92
+ <rect fill={BORDER_COLOR_LIGHT} height={viewBoxHeight} width={GRID_LINE_SIZE} />
93
+ </symbol>
94
+
95
+ <symbol id={BONUS}>
96
+ <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
97
+ </symbol>
98
+
99
+ <symbol id={BONUS_WORD_2}>
100
+ <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
101
+
102
+ <text
103
+ dominantBaseline="central"
104
+ fill="white"
105
+ fontFamily="system-ui, sans-serif"
106
+ fontSize={fontSize}
107
+ fontWeight="bold"
108
+ textAnchor="middle"
109
+ x={fontOffset}
110
+ y={fontOffset}
111
+ >
112
+ x2
113
+ </text>
114
+ </symbol>
115
+
116
+ <symbol id={BONUS_WORD_3}>
117
+ <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
118
+
119
+ <text
120
+ dominantBaseline="central"
121
+ fill="white"
122
+ fontFamily="system-ui, sans-serif"
123
+ fontSize={fontSize}
124
+ fontWeight="bold"
125
+ textAnchor="middle"
126
+ x={fontOffset}
127
+ y={fontOffset}
128
+ >
129
+ x3
130
+ </text>
131
+ </symbol>
132
+
133
+ <symbol id={BONUS_WORD_4}>
134
+ <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
135
+
136
+ <text
137
+ dominantBaseline="central"
138
+ fill="white"
139
+ fontFamily="system-ui, sans-serif"
140
+ fontSize={fontSize}
141
+ fontWeight="bold"
142
+ textAnchor="middle"
143
+ x={fontOffset}
144
+ y={fontOffset}
145
+ >
146
+ x4
147
+ </text>
148
+ </symbol>
149
+ </defs>
150
+
151
+ {showCoordinates === 'hidden' && (
152
+ <rect fill="white" height={viewBoxHeight} rx={borderRadius} width={viewBoxWidth} x="0" y="0" />
153
+ )}
154
+
155
+ {showCoordinates !== 'hidden' && (
156
+ <>
157
+ <rect
158
+ fill={COLOR_BACKGROUND}
159
+ height={viewBoxHeight}
160
+ rx={borderRadius}
161
+ width={viewBoxWidth}
162
+ x="0"
163
+ y="0"
164
+ />
165
+ <rect
166
+ fill="white"
167
+ height={viewBoxHeight - coordinatesSize}
168
+ width={viewBoxWidth - coordinatesSize}
169
+ x={direction === 'ltr' ? coordinatesSize : 0}
170
+ y={coordinatesSize}
171
+ />
172
+
173
+ <rect
174
+ fill={COLOR_BACKGROUND}
175
+ height={coordinatesSize}
176
+ rx={borderRadius}
177
+ width={viewBoxWidth}
178
+ x="0"
179
+ y="0"
180
+ />
181
+ <rect
182
+ fill={COLOR_BACKGROUND}
183
+ height={viewBoxHeight}
184
+ rx={borderRadius}
185
+ x={direction === 'ltr' ? 0 : viewBoxWidth - coordinatesSize}
186
+ y="0"
187
+ width={coordinatesSize}
188
+ />
189
+ <use href={`#${HORIZONTAL_LINE}`} y={coordinatesSize - BORDER_WIDTH} />
190
+ <use
191
+ href={`#${VERTICAL_LINE}`}
192
+ x={
193
+ direction === 'ltr' ? coordinatesSize - BORDER_WIDTH : viewBoxWidth - coordinatesSize - BORDER_WIDTH
194
+ }
195
+ />
196
+ </>
197
+ )}
198
+
199
+ {Array.from({ length: config.boardHeight - 1 }).map((_value, index) => (
200
+ <use
201
+ key={index}
202
+ href={`#${HORIZONTAL_LINE}`}
203
+ y={coordinatesSize + (index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH}
204
+ />
205
+ ))}
206
+
207
+ {Array.from({ length: config.boardWidth - 1 }).map((_value, index) => (
208
+ <use
209
+ key={index}
210
+ href={`#${VERTICAL_LINE}`}
211
+ x={(direction === 'ltr' ? coordinatesSize : 0) + (index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH}
212
+ />
213
+ ))}
214
+
215
+ {characterBonuses.map((bonus, index) => (
216
+ <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS}`} x={getX(bonus)} y={getY(bonus)} />
217
+ ))}
218
+
219
+ {word2Bonuses.map((bonus, index) => (
220
+ <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_2}`} x={getX(bonus)} y={getY(bonus)} />
221
+ ))}
222
+
223
+ {word3Bonuses.map((bonus, index) => (
224
+ <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_3}`} x={getX(bonus)} y={getY(bonus)} />
225
+ ))}
226
+
227
+ {word4Bonuses.map((bonus, index) => (
228
+ <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_4}`} x={getX(bonus)} y={getY(bonus)} />
229
+ ))}
142
230
 
143
- <rect fill={COLOR_BACKGROUND} height={coordinatesSize} rx={borderRadius} width={viewBoxWidth} x="0" y="0" />
144
231
  <rect
145
- fill={COLOR_BACKGROUND}
146
- height={viewBoxHeight}
232
+ fill={COLOR_BONUS_START}
233
+ height={bonusSize}
147
234
  rx={borderRadius}
148
- x={direction === 'ltr' ? 0 : viewBoxWidth - coordinatesSize}
149
- y="0"
150
- width={coordinatesSize}
235
+ width={bonusSize}
236
+ x={getX(center) + bonusOffset}
237
+ y={getY(center) + bonusOffset}
151
238
  />
152
- <use href={`#${HORIZONTAL_LINE}`} y={coordinatesSize - BORDER_WIDTH} />
153
- <use
154
- href={`#${VERTICAL_LINE}`}
155
- x={direction === 'ltr' ? coordinatesSize - BORDER_WIDTH : viewBoxWidth - coordinatesSize - BORDER_WIDTH}
239
+
240
+ <Star
241
+ color="white"
242
+ height={iconSize}
243
+ width={iconSize}
244
+ x={getX(center) + iconOffset}
245
+ y={getY(center) + iconOffset}
156
246
  />
157
- </>
158
- )}
159
-
160
- {Array.from({ length: config.boardHeight - 1 }).map((_value, index) => (
161
- <use
162
- key={index}
163
- href={`#${HORIZONTAL_LINE}`}
164
- y={coordinatesSize + (index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH}
165
- />
166
- ))}
167
-
168
- {Array.from({ length: config.boardWidth - 1 }).map((_value, index) => (
169
- <use
170
- key={index}
171
- href={`#${VERTICAL_LINE}`}
172
- x={(direction === 'ltr' ? coordinatesSize : 0) + (index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH}
173
- />
174
- ))}
175
-
176
- {characterBonuses.map((bonus, index) => (
177
- <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS}`} x={getX(bonus)} y={getY(bonus)} />
178
- ))}
179
-
180
- {word2Bonuses.map((bonus, index) => (
181
- <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_2}`} x={getX(bonus)} y={getY(bonus)} />
182
- ))}
183
-
184
- {word3Bonuses.map((bonus, index) => (
185
- <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_3}`} x={getX(bonus)} y={getY(bonus)} />
186
- ))}
187
-
188
- {word4Bonuses.map((bonus, index) => (
189
- <use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_4}`} x={getX(bonus)} y={getY(bonus)} />
190
- ))}
191
-
192
- <rect
193
- fill={COLOR_BONUS_START}
194
- height={bonusSize}
195
- rx={borderRadius}
196
- width={bonusSize}
197
- x={getX(center) + bonusOffset}
198
- y={getY(center) + bonusOffset}
199
- />
200
-
201
- <Star
202
- color="white"
203
- height={iconSize}
204
- width={iconSize}
205
- x={getX(center) + iconOffset}
206
- y={getY(center) + iconOffset}
207
- />
208
- </svg>
209
- </Provider>,
247
+ </svg>
248
+ </Provider>,
249
+ ),
250
+ [
251
+ bonusOffset,
252
+ bonusSize,
253
+ borderRadius,
254
+ cellSize,
255
+ center,
256
+ characterBonuses,
257
+ config.boardHeight,
258
+ config.boardWidth,
259
+ coordinatesSize,
260
+ direction,
261
+ fontOffset,
262
+ fontSize,
263
+ getX,
264
+ getY,
265
+ iconOffset,
266
+ iconSize,
267
+ showCoordinates,
268
+ viewBoxHeight,
269
+ viewBoxWidth,
270
+ word2Bonuses,
271
+ word3Bonuses,
272
+ word4Bonuses,
273
+ ],
210
274
  );
211
275
 
212
- const encodedSvg = useMemo(() => globalThis.btoa(backgroundSvg), [backgroundSvg]);
213
- const dataUrl = `data:image/svg+xml;base64,${encodedSvg}`;
276
+ const dataUrl = useMemo(() => `data:image/svg+xml;base64,${globalThis.btoa(backgroundSvg)}`, [backgroundSvg]);
214
277
  return dataUrl;
215
278
  };
@@ -1,6 +1,6 @@
1
1
  import { type CSSProperties, useMemo } from 'react';
2
2
 
3
- import { useAppLayout } from 'hooks';
3
+ import { useAppLayout } from 'app-layout';
4
4
  import { getTileSizes } from 'lib';
5
5
  import { BORDER_WIDTH } from 'parameters';
6
6
  import { selectConfig, selectShowCoordinates, useTypedSelector } from 'state';
@@ -1,6 +1,6 @@
1
1
  import { autoUpdate, offset, shift, useFloating } from '@floating-ui/react';
2
2
 
3
- import { useAppLayout } from 'hooks';
3
+ import { useAppLayout } from 'app-layout';
4
4
  import { BOARD_CELL_ACTIONS_OFFSET } from 'parameters';
5
5
 
6
6
  export const useFloatingActions = () => {
@@ -1,7 +1,7 @@
1
1
  import classNames from 'classnames';
2
2
  import { type FunctionComponent } from 'react';
3
3
 
4
- import { useAppLayout } from 'hooks';
4
+ import { useAppLayout } from 'app-layout';
5
5
  import {
6
6
  selectDictionaryError,
7
7
  selectDictionaryIsLoading,
@@ -1,5 +1,5 @@
1
1
  import classNames from 'classnames';
2
- import { type ChangeEvent, type FormEvent, type FunctionComponent } from 'react';
2
+ import { type ChangeEvent, type FunctionComponent, type SubmitEvent } from 'react';
3
3
  import { useDispatch } from 'react-redux';
4
4
 
5
5
  import { LOCALE_FEATURES } from 'i18n';
@@ -22,7 +22,7 @@ export const DictionaryInput: FunctionComponent<Props> = ({ className }) => {
22
22
  dispatch(dictionarySlice.actions.changeInput(event.target.value));
23
23
  };
24
24
 
25
- const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
25
+ const handleSubmit = (event: SubmitEvent<HTMLFormElement>) => {
26
26
  event.preventDefault();
27
27
  dispatch(dictionarySlice.actions.submit());
28
28
  };