@scrabble-solver/scrabble-solver 2.10.1 → 2.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +19 -31
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/eslint/.cache_8dgz12 +1 -1
- package/.next/cache/next-server.js.nft.json +1 -1
- package/.next/cache/webpack/client-development/0.pack +0 -0
- package/.next/cache/webpack/client-development/1.pack +0 -0
- package/.next/cache/webpack/client-development/2.pack +0 -0
- package/.next/cache/webpack/client-development/3.pack +0 -0
- package/.next/cache/webpack/client-development/4.pack +0 -0
- package/.next/cache/webpack/client-development/index.pack +0 -0
- package/.next/cache/webpack/client-development/index.pack.old +0 -0
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-development/0.pack +0 -0
- package/.next/cache/webpack/server-development/10.pack_ +0 -0
- package/.next/cache/webpack/server-development/5.pack_ +0 -0
- package/.next/cache/webpack/server-development/8.pack_ +0 -0
- package/.next/cache/webpack/server-development/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/images-manifest.json +1 -1
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +1 -1
- package/.next/server/chunks/131.js +1399 -117
- package/.next/server/chunks/{515.js → 176.js} +771 -679
- package/.next/server/chunks/210.js +122 -0
- package/.next/server/chunks/{939.js → 290.js} +3 -3
- package/.next/server/chunks/44.js +33 -33
- package/.next/server/chunks/50.js +5 -9
- package/.next/server/chunks/{413.js → 579.js} +136 -87
- package/.next/server/chunks/664.js +537 -350
- package/.next/server/chunks/859.js +56 -23
- package/.next/server/chunks/865.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/404.js.nft.json +1 -1
- package/.next/server/pages/500.html +2 -2
- package/.next/server/pages/_app.js +23 -5
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/_document.js +2 -2
- package/.next/server/pages/_error.js +206 -25
- package/.next/server/pages/_error.js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale]/[word].js +10 -10
- package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale].js +10 -10
- package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
- package/.next/server/pages/api/solve.js +14 -13
- package/.next/server/pages/api/solve.js.nft.json +1 -1
- package/.next/server/pages/api/verify.js +10 -10
- package/.next/server/pages/api/verify.js.nft.json +1 -1
- package/.next/server/pages/api/visit.js +3 -3
- package/.next/server/pages/api/visit.js.nft.json +1 -1
- package/.next/server/pages/index.html +2 -2
- package/.next/server/pages/index.js +44 -128
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/index.json +1 -1
- package/.next/server/pages-manifest.json +3 -3
- package/.next/static/FjrbXpI5fkt4lmko-vL_7/_buildManifest.js +1 -0
- package/.next/static/{hf94cues-LcXZRCpAzQ6w → FjrbXpI5fkt4lmko-vL_7}/_ssgManifest.js +0 -0
- package/.next/static/chunks/361-d16f336a9752a55a.js +1 -0
- package/.next/static/chunks/724-eb48df4d1ba3df8b.js +5 -0
- package/.next/static/chunks/amp.js +720 -0
- package/.next/static/chunks/framework-2c79e2a64abdb08b.js +33 -0
- package/.next/static/chunks/main-f11614d8aa7ee555.js +1 -0
- package/.next/static/chunks/main.js +1076 -0
- package/.next/static/chunks/pages/404-24f9617eeb8d6dc1.js +1 -0
- package/.next/static/chunks/pages/_app-959e495f0f221247.js +24 -0
- package/.next/static/chunks/pages/_app.js +2121 -0
- package/.next/static/chunks/pages/_error-8353112a01355ec2.js +1 -0
- package/.next/static/chunks/pages/_error.js +28 -0
- package/.next/static/chunks/pages/index-1e30dafa41bddb80.js +1 -0
- package/.next/static/chunks/pages/index.js +5314 -0
- package/.next/static/chunks/react-refresh.js +62 -0
- package/.next/static/chunks/webpack-59c5c889f52620d6.js +1 -0
- package/.next/static/chunks/webpack.js +1237 -0
- package/.next/static/css/aafd07997120f1e4.css +1 -0
- package/.next/static/css/c8d26240c04079b9.css +1 -0
- package/.next/static/css/eb9d57f7103525ab.css +1 -0
- package/.next/static/development/_buildManifest.js +1 -0
- package/.next/static/development/_ssgManifest.js +1 -0
- package/.next/static/webpack/fb4b50d5e70ee127.webpack.hot-update.json +1 -0
- package/.next/static/webpack/pages/index.fb4b50d5e70ee127.hot-update.js +171 -0
- package/.next/static/webpack/webpack.fb4b50d5e70ee127.hot-update.js +18 -0
- package/.next/trace +52 -52
- package/package.json +21 -21
- package/public/og.png +0 -0
- package/src/components/Board/Board.module.scss +1 -2
- package/src/components/Board/BoardPure.tsx +2 -0
- package/src/components/Board/components/Cell/Cell.module.scss +65 -55
- package/src/components/Board/components/Cell/Cell.tsx +9 -0
- package/src/components/Board/components/Cell/CellPure.tsx +11 -2
- package/src/components/NavButtons/NavButtons.tsx +11 -1
- package/src/components/Rack/RackTile.tsx +10 -1
- package/src/components/RemainingTiles/Character.module.scss +0 -1
- package/src/components/RemainingTiles/Character.tsx +1 -0
- package/src/components/Solver/Solver.module.scss +85 -0
- package/src/components/Solver/Solver.tsx +75 -0
- package/src/components/Solver/index.ts +1 -0
- package/src/components/Tile/Tile.module.scss +51 -7
- package/src/components/Tile/Tile.tsx +3 -0
- package/src/components/Tile/TilePure.tsx +8 -1
- package/src/components/index.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useIsTouchDevice.ts +7 -0
- package/src/icons/ExclamationSquareFill.svg +4 -0
- package/src/icons/index.ts +1 -0
- package/src/pages/index.module.scss +1 -75
- package/src/pages/index.tsx +11 -73
- package/src/parameters/index.ts +2 -0
- package/src/service-worker/average.ts +9 -0
- package/src/service-worker/routeSolveRequests.ts +46 -7
- package/src/state/selectors.ts +20 -0
- package/src/state/slices/boardInitialState.ts +27 -0
- package/src/styles/mixins.scss +6 -1
- package/src/styles/variables.scss +6 -3
- package/.next/InjectManifest.js.nft.json +0 -1
- package/.next/static/chunks/368-8b386c3106556f62.js +0 -1
- package/.next/static/chunks/546-447e243fc9de2c59.js +0 -1
- package/.next/static/chunks/framework-4556c45dd113b893.js +0 -1
- package/.next/static/chunks/main-a75cf611e061d8f8.js +0 -1
- package/.next/static/chunks/pages/404-932294135c3206dd.js +0 -1
- package/.next/static/chunks/pages/_app-8f0df20f771045ed.js +0 -1
- package/.next/static/chunks/pages/_error-a4ba2246ff8fb532.js +0 -1
- package/.next/static/chunks/pages/index-8af7a9d7a2cd98a7.js +0 -1
- package/.next/static/chunks/webpack-5752944655d749a0.js +0 -1
- package/.next/static/css/6b1833fd19d3a74a.css +0 -1
- package/.next/static/css/a6154e4ca046ca13.css +0 -1
- package/.next/static/css/bad53af6f8616677.css +0 -1
- package/.next/static/hf94cues-LcXZRCpAzQ6w/_buildManifest.js +0 -1
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
@import 'styles/mixins';
|
|
2
|
+
|
|
1
3
|
.tile {
|
|
4
|
+
--background-color: transparent;
|
|
5
|
+
|
|
2
6
|
position: relative;
|
|
3
|
-
background-color:
|
|
7
|
+
background-color: var(--background-color);
|
|
4
8
|
transition: var(--transition);
|
|
5
9
|
|
|
6
10
|
&.raised {
|
|
@@ -8,24 +12,34 @@
|
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
&.points1 {
|
|
11
|
-
background-color: var(--color--yellow);
|
|
15
|
+
--background-color: var(--color--yellow);
|
|
16
|
+
|
|
17
|
+
background-color: var(--background-color);
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
&.points2 {
|
|
15
|
-
background-color: var(--color--green);
|
|
21
|
+
--background-color: var(--color--green);
|
|
22
|
+
|
|
23
|
+
background-color: var(--background-color);
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
&.points3,
|
|
19
27
|
&.points4 {
|
|
20
|
-
background-color: var(--color--blue);
|
|
28
|
+
--background-color: var(--color--blue);
|
|
29
|
+
|
|
30
|
+
background-color: var(--background-color);
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
&.points5 {
|
|
24
|
-
background-color: var(--color--red);
|
|
34
|
+
--background-color: var(--color--red);
|
|
35
|
+
|
|
36
|
+
background-color: var(--background-color);
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
&.blank {
|
|
28
|
-
background-color: white;
|
|
40
|
+
--background-color: white;
|
|
41
|
+
|
|
42
|
+
background-color: var(--background-color);
|
|
29
43
|
|
|
30
44
|
.character {
|
|
31
45
|
color: black;
|
|
@@ -33,7 +47,9 @@
|
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
&.highlighted {
|
|
36
|
-
background-color: var(--color--primary);
|
|
50
|
+
--background-color: var(--color--primary);
|
|
51
|
+
|
|
52
|
+
background-color: var(--background-color);
|
|
37
53
|
color: white;
|
|
38
54
|
|
|
39
55
|
.character {
|
|
@@ -44,6 +60,13 @@
|
|
|
44
60
|
color: inherit;
|
|
45
61
|
}
|
|
46
62
|
}
|
|
63
|
+
|
|
64
|
+
&.invalid {
|
|
65
|
+
--background-color: var(--color--red--light);
|
|
66
|
+
|
|
67
|
+
background-color: var(--background-color);
|
|
68
|
+
color: var(--color--error);
|
|
69
|
+
}
|
|
47
70
|
}
|
|
48
71
|
|
|
49
72
|
.character {
|
|
@@ -52,6 +75,7 @@
|
|
|
52
75
|
padding: 0;
|
|
53
76
|
box-sizing: border-box;
|
|
54
77
|
background-color: transparent;
|
|
78
|
+
color: inherit;
|
|
55
79
|
border: none;
|
|
56
80
|
font-weight: bold;
|
|
57
81
|
text-transform: uppercase;
|
|
@@ -72,6 +96,8 @@
|
|
|
72
96
|
}
|
|
73
97
|
|
|
74
98
|
.points {
|
|
99
|
+
@include text-stroke(var(--background-color), 1px);
|
|
100
|
+
|
|
75
101
|
position: absolute;
|
|
76
102
|
font-weight: bold;
|
|
77
103
|
user-select: none;
|
|
@@ -88,3 +114,21 @@
|
|
|
88
114
|
left: 9%;
|
|
89
115
|
}
|
|
90
116
|
}
|
|
117
|
+
|
|
118
|
+
.alert {
|
|
119
|
+
$size: 30%;
|
|
120
|
+
|
|
121
|
+
position: absolute;
|
|
122
|
+
width: $size;
|
|
123
|
+
height: $size;
|
|
124
|
+
|
|
125
|
+
[dir='ltr'] & {
|
|
126
|
+
top: 0;
|
|
127
|
+
right: 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
[dir='rtl'] & {
|
|
131
|
+
bottom: 0;
|
|
132
|
+
right: 0;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -23,6 +23,7 @@ interface Props {
|
|
|
23
23
|
highlighted?: boolean;
|
|
24
24
|
inputRef?: RefObject<HTMLInputElement>;
|
|
25
25
|
isBlank?: boolean;
|
|
26
|
+
isValid?: boolean;
|
|
26
27
|
placeholder?: string;
|
|
27
28
|
points?: number;
|
|
28
29
|
raised?: boolean;
|
|
@@ -41,6 +42,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
41
42
|
highlighted,
|
|
42
43
|
inputRef: ref,
|
|
43
44
|
isBlank,
|
|
45
|
+
isValid,
|
|
44
46
|
placeholder,
|
|
45
47
|
points,
|
|
46
48
|
raised,
|
|
@@ -82,6 +84,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
82
84
|
inputRef={inputRef}
|
|
83
85
|
inputStyle={inputStyle}
|
|
84
86
|
isBlank={isBlank}
|
|
87
|
+
isValid={isValid}
|
|
85
88
|
placeholder={placeholder}
|
|
86
89
|
points={points}
|
|
87
90
|
pointsFormatted={pointsFormatted}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
RefObject,
|
|
10
10
|
} from 'react';
|
|
11
11
|
|
|
12
|
+
import { ExclamationSquareFill } from 'icons';
|
|
13
|
+
|
|
12
14
|
import styles from './Tile.module.scss';
|
|
13
15
|
|
|
14
16
|
interface Props {
|
|
@@ -21,6 +23,7 @@ interface Props {
|
|
|
21
23
|
inputRef: RefObject<HTMLInputElement>;
|
|
22
24
|
inputStyle?: CSSProperties;
|
|
23
25
|
isBlank?: boolean;
|
|
26
|
+
isValid?: boolean;
|
|
24
27
|
placeholder?: string;
|
|
25
28
|
points?: number;
|
|
26
29
|
pointsFormatted?: string;
|
|
@@ -43,6 +46,7 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
43
46
|
inputRef,
|
|
44
47
|
inputStyle,
|
|
45
48
|
isBlank,
|
|
49
|
+
isValid,
|
|
46
50
|
placeholder,
|
|
47
51
|
points,
|
|
48
52
|
pointsFormatted,
|
|
@@ -56,8 +60,9 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
56
60
|
}) => (
|
|
57
61
|
<div
|
|
58
62
|
className={classNames(styles.tile, className, {
|
|
59
|
-
[styles.highlighted]: highlighted,
|
|
60
63
|
[styles.blank]: isBlank,
|
|
64
|
+
[styles.highlighted]: highlighted,
|
|
65
|
+
[styles.invalid]: !isValid,
|
|
61
66
|
[styles.raised]: raised,
|
|
62
67
|
[styles.points1]: points === 1,
|
|
63
68
|
[styles.points2]: points === 2,
|
|
@@ -90,6 +95,8 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
90
95
|
{pointsFormatted}
|
|
91
96
|
</span>
|
|
92
97
|
)}
|
|
98
|
+
|
|
99
|
+
{!isValid && <ExclamationSquareFill className={styles.alert} />}
|
|
93
100
|
</div>
|
|
94
101
|
);
|
|
95
102
|
|
package/src/components/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ export { default as ResultsInput } from './ResultsInput';
|
|
|
20
20
|
export { default as Screen } from './Screen';
|
|
21
21
|
export { default as Settings } from './Settings';
|
|
22
22
|
export { default as Sidebar } from './Sidebar';
|
|
23
|
+
export { default as Solver } from './Solver';
|
|
23
24
|
export { default as Splash } from './Splash';
|
|
24
25
|
export { default as SquareButton } from './SquareButton';
|
|
25
26
|
export { default as SvgFontCss } from './SvgFontCss';
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as useDirection } from './useDirection';
|
|
2
2
|
export { default as useIsTablet } from './useIsTablet';
|
|
3
|
+
export { default as useIsTouchDevice } from './useIsTouchDevice';
|
|
3
4
|
export { default as useLanguage } from './useLanguage';
|
|
4
5
|
export { default as useLocalStorage } from './useLocalStorage';
|
|
5
6
|
export { default as usePortal } from './usePortal';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/exclamation-square-fill/ -->
|
|
2
|
+
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6 4c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995A.905.905 0 0 1 8 4zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" fill="currentColor" />
|
|
4
|
+
</svg>
|
package/src/icons/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { default as Cross } from './Cross.svg';
|
|
|
11
11
|
export { default as CrossFill } from './CrossFill.svg';
|
|
12
12
|
export { default as DashCircleFill } from './DashCircleFill.svg';
|
|
13
13
|
export { default as Eraser } from './Eraser.svg';
|
|
14
|
+
export { default as ExclamationSquareFill } from './ExclamationSquareFill.svg';
|
|
14
15
|
export { default as Flag } from './Flag.svg';
|
|
15
16
|
export { default as FlagEs } from './FlagEs.svg';
|
|
16
17
|
export { default as FlagFa } from './FlagFa.svg';
|
|
@@ -43,80 +43,6 @@
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
.
|
|
47
|
-
$board-size: 15;
|
|
48
|
-
$tile-size-max: 60px;
|
|
49
|
-
$tile-border-size: 1px;
|
|
50
|
-
|
|
51
|
-
flex: 1;
|
|
52
|
-
padding: 0 var(--spacing--xl);
|
|
53
|
-
max-height: $board-size * ($tile-size-max + $tile-border-size) + $tile-border-size;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.content {
|
|
57
|
-
display: flex;
|
|
58
|
-
align-items: center;
|
|
59
|
-
justify-content: center;
|
|
60
|
-
height: 100%;
|
|
61
|
-
gap: var(--spacing--xl);
|
|
62
|
-
|
|
63
|
-
@include tablet {
|
|
64
|
-
gap: var(--spacing--l);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.boardContainer {
|
|
69
|
-
display: flex;
|
|
70
|
-
position: relative;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.sidebar {
|
|
74
|
-
display: flex;
|
|
75
|
-
flex-direction: column;
|
|
76
|
-
flex: 0 0 var(--sidebar--width);
|
|
77
|
-
gap: var(--spacing--xl);
|
|
78
|
-
|
|
79
|
-
@include tablet {
|
|
80
|
-
gap: var(--spacing--l);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.dictionary {
|
|
85
|
-
display: flex;
|
|
86
|
-
flex-direction: column;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.dictionaryOutput {
|
|
90
|
-
flex: 1;
|
|
91
|
-
border-bottom: var(--border);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.dictionaryInput {
|
|
95
|
-
flex: 0 0 auto;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.resultsContainer {
|
|
46
|
+
.solver {
|
|
99
47
|
flex: 1;
|
|
100
|
-
position: relative;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.rackContainer {
|
|
104
|
-
position: relative;
|
|
105
|
-
z-index: 1;
|
|
106
|
-
flex: 0 0 auto;
|
|
107
|
-
display: flex;
|
|
108
|
-
justify-content: center;
|
|
109
|
-
margin: var(--spacing--xl) var(--spacing--l);
|
|
110
|
-
|
|
111
|
-
@include tablet {
|
|
112
|
-
margin: var(--spacing--l);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.rack {
|
|
117
|
-
border: var(--border);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
.submitInput {
|
|
121
|
-
display: none;
|
|
122
48
|
}
|
package/src/pages/index.tsx
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { AnimationEvent,
|
|
4
|
+
import { AnimationEvent, FunctionComponent, useEffect, useState } from 'react';
|
|
5
5
|
import Modal from 'react-modal';
|
|
6
6
|
import { useDispatch } from 'react-redux';
|
|
7
|
-
import { useEffectOnce
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
Dictionary,
|
|
12
|
-
DictionaryInput,
|
|
13
|
-
KeyMap,
|
|
14
|
-
Logo,
|
|
15
|
-
NavButtons,
|
|
16
|
-
Rack,
|
|
17
|
-
RemainingTiles,
|
|
18
|
-
Results,
|
|
19
|
-
Settings,
|
|
20
|
-
Splash,
|
|
21
|
-
SvgFontFix,
|
|
22
|
-
Well,
|
|
23
|
-
Words,
|
|
24
|
-
} from 'components';
|
|
25
|
-
import { useDirection, useIsTablet, useLanguage, useLocalStorage } from 'hooks';
|
|
7
|
+
import { useEffectOnce } from 'react-use';
|
|
8
|
+
|
|
9
|
+
import { KeyMap, Logo, NavButtons, RemainingTiles, Settings, Solver, Splash, SvgFontFix, Words } from 'components';
|
|
10
|
+
import { useDirection, useLanguage, useLocalStorage } from 'hooks';
|
|
26
11
|
import { LOCALE_FEATURES } from 'i18n';
|
|
27
|
-
import {
|
|
28
|
-
import { COMPONENTS_SPACING, COMPONENTS_SPACING_MOBILE, DICTIONARY_HEIGHT } from 'parameters';
|
|
12
|
+
import { INITIALIZATION_DURATION } from 'parameters';
|
|
29
13
|
import { registerServiceWorker } from 'serviceWorkerManager';
|
|
30
|
-
import { initialize, localStorage, reset,
|
|
14
|
+
import { initialize, localStorage, reset, selectLocale, useTypedSelector } from 'state';
|
|
31
15
|
|
|
32
16
|
import styles from './index.module.scss';
|
|
33
17
|
|
|
@@ -40,30 +24,16 @@ interface Props {
|
|
|
40
24
|
const Index: FunctionComponent<Props> = ({ version }) => {
|
|
41
25
|
const dispatch = useDispatch();
|
|
42
26
|
const locale = useTypedSelector(selectLocale);
|
|
43
|
-
const isTablet = useIsTablet();
|
|
44
27
|
const [showKeyMap, setShowKeyMap] = useState(false);
|
|
45
28
|
const [showRemainingTiles, setShowRemainingTiles] = useState(false);
|
|
46
29
|
const [showSettings, setShowSettings] = useState(false);
|
|
47
30
|
const [showWords, setShowWords] = useState(false);
|
|
48
|
-
const [
|
|
49
|
-
const [contentRef, { height: contentHeight, width: contentWidth }] = useMeasure<HTMLDivElement>();
|
|
50
|
-
const [resultsContainerRef, { height: resultsContainerHeight, width: resultsContainerWidth }] =
|
|
51
|
-
useMeasure<HTMLDivElement>();
|
|
52
|
-
const config = useTypedSelector(selectConfig);
|
|
53
|
-
const cellSize = getCellSize(config, contentWidth - resultsContainerWidth, contentHeight);
|
|
54
|
-
const isInitializedInitial = contentWidth > 0 && boardHeight > 0 && resultsContainerWidth > 0;
|
|
55
|
-
const [isInitialized, setIsInitialized] = useState(isInitializedInitial);
|
|
56
|
-
const resultsHeight = boardHeight - DICTIONARY_HEIGHT - (isTablet ? COMPONENTS_SPACING_MOBILE : COMPONENTS_SPACING);
|
|
31
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
57
32
|
|
|
58
33
|
const handleClear = () => {
|
|
59
34
|
dispatch(reset());
|
|
60
35
|
};
|
|
61
36
|
|
|
62
|
-
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
|
63
|
-
event.preventDefault();
|
|
64
|
-
dispatch(solveSlice.actions.submit());
|
|
65
|
-
};
|
|
66
|
-
|
|
67
37
|
const handleSplashAnimationEnd = (event: AnimationEvent<HTMLDivElement>) => {
|
|
68
38
|
if (event.target === event.currentTarget && !localStorage.getHasVisited()) {
|
|
69
39
|
setShowSettings(true);
|
|
@@ -71,8 +41,7 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
71
41
|
}
|
|
72
42
|
};
|
|
73
43
|
|
|
74
|
-
|
|
75
|
-
useDirection(direction);
|
|
44
|
+
useDirection(LOCALE_FEATURES[locale].direction);
|
|
76
45
|
useLanguage(locale);
|
|
77
46
|
useLocalStorage();
|
|
78
47
|
|
|
@@ -81,7 +50,7 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
81
50
|
|
|
82
51
|
setTimeout(() => {
|
|
83
52
|
setIsInitialized(true);
|
|
84
|
-
},
|
|
53
|
+
}, INITIALIZATION_DURATION);
|
|
85
54
|
});
|
|
86
55
|
|
|
87
56
|
useEffect(() => {
|
|
@@ -111,44 +80,13 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
111
80
|
/>
|
|
112
81
|
</div>
|
|
113
82
|
|
|
114
|
-
<
|
|
115
|
-
<div className={styles.content} ref={contentRef}>
|
|
116
|
-
<form className={styles.boardContainer} onSubmit={handleSubmit}>
|
|
117
|
-
{contentWidth > 0 && <Board cellSize={cellSize} innerRef={boardRef} />}
|
|
118
|
-
<input className={styles.submitInput} tabIndex={-1} type="submit" />
|
|
119
|
-
</form>
|
|
120
|
-
|
|
121
|
-
<div className={styles.sidebar} style={{ height: boardHeight + 1 }}>
|
|
122
|
-
<Well className={styles.resultsContainer} ref={resultsContainerRef}>
|
|
123
|
-
{resultsContainerWidth > 0 && resultsContainerHeight > 0 && (
|
|
124
|
-
<Results height={resultsHeight} width={resultsContainerWidth} />
|
|
125
|
-
)}
|
|
126
|
-
</Well>
|
|
127
|
-
|
|
128
|
-
<Well>
|
|
129
|
-
<div className={styles.dictionary} style={{ height: DICTIONARY_HEIGHT }}>
|
|
130
|
-
<Dictionary className={styles.dictionaryOutput} />
|
|
131
|
-
<DictionaryInput className={styles.dictionaryInput} />
|
|
132
|
-
</div>
|
|
133
|
-
</Well>
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
|
|
138
|
-
<form className={styles.rackContainer} onSubmit={handleSubmit}>
|
|
139
|
-
<Rack className={styles.rack} />
|
|
140
|
-
<input className={styles.submitInput} tabIndex={-1} type="submit" />
|
|
141
|
-
</form>
|
|
83
|
+
<Solver className={styles.solver} />
|
|
142
84
|
</div>
|
|
143
85
|
|
|
144
86
|
<Settings isOpen={showSettings} onClose={() => setShowSettings(false)} />
|
|
145
|
-
|
|
146
87
|
<KeyMap isOpen={showKeyMap} onClose={() => setShowKeyMap(false)} />
|
|
147
|
-
|
|
148
88
|
<Words isOpen={showWords} onClose={() => setShowWords(false)} />
|
|
149
|
-
|
|
150
89
|
<RemainingTiles isOpen={showRemainingTiles} onClose={() => setShowRemainingTiles(false)} />
|
|
151
|
-
|
|
152
90
|
<Splash forceShow={!isInitialized} onAnimationEnd={handleSplashAnimationEnd} />
|
|
153
91
|
</>
|
|
154
92
|
);
|
package/src/parameters/index.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { Trie } from '@kamilmielnik/trie';
|
|
1
2
|
import { getConfig } from '@scrabble-solver/configs';
|
|
2
3
|
import { BLANK } from '@scrabble-solver/constants';
|
|
3
4
|
import { solve } from '@scrabble-solver/solver';
|
|
4
5
|
import { Board, Locale, Tile } from '@scrabble-solver/types';
|
|
5
6
|
import { registerRoute } from 'workbox-routing';
|
|
6
7
|
|
|
8
|
+
import average from './average';
|
|
7
9
|
import { revalidateDictionary } from './dictionaries';
|
|
8
10
|
import getTrie from './getTrie';
|
|
9
11
|
|
|
@@ -11,24 +13,61 @@ const headers = {
|
|
|
11
13
|
'Content-Type': 'application/json; charset=utf-8',
|
|
12
14
|
};
|
|
13
15
|
|
|
16
|
+
const MIN_MEASUREMENTS = 5;
|
|
17
|
+
const localDurations: number[] = [];
|
|
18
|
+
const serverDurations: number[] = [];
|
|
19
|
+
|
|
20
|
+
const isSlowDevice = (): boolean | undefined => {
|
|
21
|
+
if (localDurations.length < MIN_MEASUREMENTS || serverDurations.length < MIN_MEASUREMENTS) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const count = Math.min(localDurations.length, serverDurations.length);
|
|
26
|
+
return average(localDurations.slice(0, count)) > average(serverDurations.slice(0, count));
|
|
27
|
+
};
|
|
28
|
+
|
|
14
29
|
const routeSolveRequests = () => {
|
|
15
30
|
registerRoute(
|
|
16
31
|
({ url }) => url.origin === location.origin && url.pathname === '/api/solve',
|
|
17
32
|
async ({ request }) => {
|
|
18
33
|
const { board, characters, configId, locale } = await request.clone().json();
|
|
34
|
+
|
|
35
|
+
const solveLocal = async (trie: Trie) => {
|
|
36
|
+
const config = getConfig(configId)[locale as Locale];
|
|
37
|
+
const tiles = characters.map((character: string) => new Tile({ character, isBlank: character === BLANK }));
|
|
38
|
+
const resultsJson = solve(trie, config, Board.fromJson(board), tiles);
|
|
39
|
+
const json = JSON.stringify(resultsJson);
|
|
40
|
+
return new Response(json, { headers });
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const solveServer = () => fetch(request);
|
|
44
|
+
|
|
19
45
|
const trie = await getTrie(locale);
|
|
20
46
|
|
|
21
|
-
if (
|
|
22
|
-
const response = await
|
|
47
|
+
if (trie && typeof isSlowDevice() === 'undefined') {
|
|
48
|
+
const response = await Promise.race([
|
|
49
|
+
(async () => {
|
|
50
|
+
const start = Date.now();
|
|
51
|
+
const result = await solveLocal(trie);
|
|
52
|
+
localDurations.push(Date.now() - start);
|
|
53
|
+
return result;
|
|
54
|
+
})(),
|
|
55
|
+
(async () => {
|
|
56
|
+
const start = Date.now();
|
|
57
|
+
const result = await solveServer();
|
|
58
|
+
serverDurations.push(Date.now() - start);
|
|
59
|
+
return result;
|
|
60
|
+
})(),
|
|
61
|
+
]);
|
|
62
|
+
|
|
23
63
|
revalidateDictionary(locale);
|
|
24
64
|
return response;
|
|
25
65
|
}
|
|
26
66
|
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return new Response(json, { headers });
|
|
67
|
+
const handleSolve = trie && !isSlowDevice() ? () => solveLocal(trie) : () => solveServer();
|
|
68
|
+
const response = await handleSolve();
|
|
69
|
+
revalidateDictionary(locale);
|
|
70
|
+
return response;
|
|
32
71
|
},
|
|
33
72
|
'POST',
|
|
34
73
|
);
|
package/src/state/selectors.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import { getLocaleConfig } from '@scrabble-solver/configs';
|
|
3
|
+
import { BLANK } from '@scrabble-solver/constants';
|
|
3
4
|
import { Cell, Config, isError, Result, Tile } from '@scrabble-solver/types';
|
|
4
5
|
|
|
5
6
|
import i18n, { LOCALE_FEATURES } from 'i18n';
|
|
@@ -62,6 +63,14 @@ export const selectCellIsFiltered = createSelector([selectCellFilter, selectPoin
|
|
|
62
63
|
return cellFilter.some((cell) => cell.x === x && cell.y === y);
|
|
63
64
|
});
|
|
64
65
|
|
|
66
|
+
export const selectCellIsValid = createSelector([selectConfig, selectCell], (config, cell) => {
|
|
67
|
+
if (!cell.hasTile()) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return config.tiles.some((tile) => tile.character === cell.tile.character);
|
|
72
|
+
});
|
|
73
|
+
|
|
65
74
|
export const selectResults = createSelector([selectResultsRoot], (results) => results.results);
|
|
66
75
|
|
|
67
76
|
export const selectResultsQuery = createSelector([selectResultsRoot], (results) => results.query);
|
|
@@ -140,6 +149,17 @@ export const selectCharacterPoints = createSelector(
|
|
|
140
149
|
},
|
|
141
150
|
);
|
|
142
151
|
|
|
152
|
+
export const selectCharacterIsValid = createSelector(
|
|
153
|
+
[selectConfig, selectCharacter],
|
|
154
|
+
(config: Config, character: string | null) => {
|
|
155
|
+
if (character === null || character === BLANK) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return config.tiles.some((tile) => tile.character === character);
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
|
|
143
163
|
export const selectTilePoints = createSelector([selectConfig, selectTile], (config: Config, tile: Tile | null) => {
|
|
144
164
|
return config.getTilePoints(tile);
|
|
145
165
|
});
|
|
@@ -7,4 +7,31 @@ const { configId, locale } = settingsInitialState;
|
|
|
7
7
|
const { boardHeight, boardWidth } = getLocaleConfig(configId, locale);
|
|
8
8
|
const boardInitialState: Board = Board.create(boardWidth, boardHeight);
|
|
9
9
|
|
|
10
|
+
// const createOxyphenbutazone = () => {
|
|
11
|
+
// // Tiles: oypbaze
|
|
12
|
+
|
|
13
|
+
// const board = Board.fromStringArray([
|
|
14
|
+
// ' x hen ut on ',
|
|
15
|
+
// 'puer or amas j',
|
|
16
|
+
// 'a led a er a',
|
|
17
|
+
// 'c ki i elf c',
|
|
18
|
+
// 'i snot n is u',
|
|
19
|
+
// 'f t o w do l',
|
|
20
|
+
// 'y e moa er a',
|
|
21
|
+
// 'i r solar t',
|
|
22
|
+
// 'n v h t i',
|
|
23
|
+
// 'g i i bitten',
|
|
24
|
+
// ' e n v g',
|
|
25
|
+
// ' w g e ',
|
|
26
|
+
// ' e ',
|
|
27
|
+
// ' d ',
|
|
28
|
+
// ' ',
|
|
29
|
+
// ]);
|
|
30
|
+
|
|
31
|
+
// board.rows[4][3].tile.isBlank = true;
|
|
32
|
+
// board.rows[9][11].tile.isBlank = true;
|
|
33
|
+
|
|
34
|
+
// return board;
|
|
35
|
+
// };
|
|
36
|
+
|
|
10
37
|
export default boardInitialState;
|
package/src/styles/mixins.scss
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
@mixin tablet
|
|
1
|
+
@mixin tablet {
|
|
2
2
|
@media (max-width: 1024px) {
|
|
3
3
|
@content;
|
|
4
4
|
}
|
|
@@ -73,3 +73,8 @@
|
|
|
73
73
|
font-size: var(--font--size--s);
|
|
74
74
|
font-family: var(--font--family);
|
|
75
75
|
}
|
|
76
|
+
|
|
77
|
+
@mixin text-stroke($color, $size: 1px) {
|
|
78
|
+
text-shadow: $size 0 $color, (-$size) 0 $color, 0 $size $color, 0 (-$size) $color, (-$size) (-$size) $color,
|
|
79
|
+
(-$size) $size $color, $size (-$size) $color, $size $size $color;
|
|
80
|
+
}
|
|
@@ -2,6 +2,7 @@ $easeOutSine: cubic-bezier(0.61, 1, 0.88, 1);
|
|
|
2
2
|
|
|
3
3
|
:root {
|
|
4
4
|
--border--color: #cdcdcd;
|
|
5
|
+
--border--color--light: #d9d9d9;
|
|
5
6
|
--border--radius: 5px;
|
|
6
7
|
--border--width: 1px;
|
|
7
8
|
--border: var(--border--width) solid var(--border--color);
|
|
@@ -41,13 +42,15 @@ $easeOutSine: cubic-bezier(0.61, 1, 0.88, 1);
|
|
|
41
42
|
--color--blue: #c7d8f9;
|
|
42
43
|
--color--blue--light: #dde4f6;
|
|
43
44
|
--color--light-blue: #7bb5e5;
|
|
44
|
-
--color--light-blue--light: #
|
|
45
|
+
--color--light-blue--light: #b8d5ed;
|
|
45
46
|
--color--dark-blue: #1868ad;
|
|
46
|
-
--color--dark-blue--light: #
|
|
47
|
+
--color--dark-blue--light: #86aed1;
|
|
47
48
|
--color--red: #f7c2aa;
|
|
48
49
|
--color--red--light: #fbe0d4;
|
|
49
50
|
--color--violet: #78387f;
|
|
50
|
-
--color--violet--light: #
|
|
51
|
+
--color--violet--light: #b284b8;
|
|
52
|
+
--color--orange: #fbc997;
|
|
53
|
+
--color--pink: #f19393;
|
|
51
54
|
|
|
52
55
|
--font--family: 'Open Sans', sans-serif;
|
|
53
56
|
--font--family--title: 'Lato', sans-serif;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":1,"files":["../public/service-worker.js","../public/service-worker.js.map","../package.json"]}
|