@scrabble-solver/scrabble-solver 2.10.0 → 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} +850 -732
- package/.next/server/chunks/210.js +122 -0
- package/.next/server/chunks/{939.js → 290.js} +4 -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} +184 -117
- 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 +11 -11
- 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 +34 -36
- 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 +4 -4
- package/.next/static/FjrbXpI5fkt4lmko-vL_7/_buildManifest.js +1 -0
- package/.next/static/{vscqn7BEtAxJteWSwNnas → 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/Board/hooks/useGrid.ts +6 -6
- package/src/components/NavButtons/NavButtons.tsx +11 -1
- package/src/components/Rack/Rack.tsx +16 -4
- 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/Results/Results.tsx +2 -2
- package/src/components/Settings/components/AutoGroupTilesSetting/AutoGroupTilesSetting.tsx +2 -2
- package/src/components/Settings/components/LocaleSetting/options.ts +9 -1
- 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/i18n/fr.json +1 -1
- package/src/icons/ExclamationSquareFill.svg +4 -0
- package/src/icons/index.ts +1 -0
- package/src/lib/getRemainingTilesGroups.ts +24 -26
- package/src/pages/index.module.scss +1 -75
- package/src/pages/index.tsx +11 -73
- package/src/parameters/index.ts +2 -0
- package/src/sdk/findWordDefinitions.ts +1 -1
- package/src/service-worker/average.ts +9 -0
- package/src/service-worker/routeSolveRequests.ts +46 -7
- package/src/state/sagas.ts +19 -5
- package/src/state/selectors.ts +30 -2
- 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-d423e70be6c0c473.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-3f5508a5f544d9eb.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/vscqn7BEtAxJteWSwNnas/_buildManifest.js +0 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
@import 'styles/mixins';
|
|
2
|
+
|
|
3
|
+
.solver {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
height: 100%;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.contentWrapper {
|
|
10
|
+
$board-size: 15;
|
|
11
|
+
$tile-size-max: 60px;
|
|
12
|
+
$tile-border-size: 1px;
|
|
13
|
+
|
|
14
|
+
flex: 1;
|
|
15
|
+
padding: 0 var(--spacing--xl);
|
|
16
|
+
max-height: $board-size * ($tile-size-max + $tile-border-size) + $tile-border-size;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.content {
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
height: 100%;
|
|
24
|
+
gap: var(--spacing--xl);
|
|
25
|
+
|
|
26
|
+
@include tablet {
|
|
27
|
+
gap: var(--spacing--l);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.boardContainer {
|
|
32
|
+
display: flex;
|
|
33
|
+
position: relative;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.sidebar {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
flex: 0 0 var(--sidebar--width);
|
|
40
|
+
gap: var(--spacing--xl);
|
|
41
|
+
|
|
42
|
+
@include tablet {
|
|
43
|
+
gap: var(--spacing--l);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.dictionary {
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.dictionaryOutput {
|
|
53
|
+
flex: 1;
|
|
54
|
+
border-bottom: var(--border);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.dictionaryInput {
|
|
58
|
+
flex: 0 0 auto;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.resultsContainer {
|
|
62
|
+
flex: 1;
|
|
63
|
+
position: relative;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.rackContainer {
|
|
67
|
+
position: relative;
|
|
68
|
+
z-index: 1;
|
|
69
|
+
flex: 0 0 auto;
|
|
70
|
+
display: flex;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
margin: var(--spacing--xl) var(--spacing--l);
|
|
73
|
+
|
|
74
|
+
@include tablet {
|
|
75
|
+
margin: var(--spacing--l);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.rack {
|
|
80
|
+
border: var(--border);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.submitInput {
|
|
84
|
+
display: none;
|
|
85
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { FormEvent, FunctionComponent } from 'react';
|
|
3
|
+
import { useDispatch } from 'react-redux';
|
|
4
|
+
import { useMeasure } from 'react-use';
|
|
5
|
+
|
|
6
|
+
import { useIsTablet } from 'hooks';
|
|
7
|
+
import { getCellSize } from 'lib';
|
|
8
|
+
import { COMPONENTS_SPACING, COMPONENTS_SPACING_MOBILE, DICTIONARY_HEIGHT } from 'parameters';
|
|
9
|
+
import { selectConfig, solveSlice, useTypedSelector } from 'state';
|
|
10
|
+
|
|
11
|
+
import Board from '../Board';
|
|
12
|
+
import Dictionary from '../Dictionary';
|
|
13
|
+
import DictionaryInput from '../DictionaryInput';
|
|
14
|
+
import Rack from '../Rack';
|
|
15
|
+
import Results from '../Results';
|
|
16
|
+
import Well from '../Well';
|
|
17
|
+
|
|
18
|
+
import styles from './Solver.module.scss';
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const Solver: FunctionComponent<Props> = ({ className }) => {
|
|
25
|
+
const dispatch = useDispatch();
|
|
26
|
+
const isTablet = useIsTablet();
|
|
27
|
+
const [boardRef, { height: boardHeight }] = useMeasure<HTMLDivElement>();
|
|
28
|
+
const [contentRef, { height: contentHeight, width: contentWidth }] = useMeasure<HTMLDivElement>();
|
|
29
|
+
const [resultsContainerRef, { height: resultsContainerHeight, width: resultsContainerWidth }] =
|
|
30
|
+
useMeasure<HTMLDivElement>();
|
|
31
|
+
const config = useTypedSelector(selectConfig);
|
|
32
|
+
const cellSize = getCellSize(config, contentWidth - resultsContainerWidth, contentHeight);
|
|
33
|
+
const componentsSpacing = isTablet ? COMPONENTS_SPACING_MOBILE : COMPONENTS_SPACING;
|
|
34
|
+
const resultsHeight = boardHeight - DICTIONARY_HEIGHT - componentsSpacing;
|
|
35
|
+
|
|
36
|
+
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
dispatch(solveSlice.actions.submit());
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className={classNames(styles.solver, className)}>
|
|
43
|
+
<div className={styles.contentWrapper}>
|
|
44
|
+
<div className={styles.content} ref={contentRef}>
|
|
45
|
+
<form className={styles.boardContainer} onSubmit={handleSubmit}>
|
|
46
|
+
{contentWidth > 0 && <Board cellSize={cellSize} innerRef={boardRef} />}
|
|
47
|
+
<input className={styles.submitInput} tabIndex={-1} type="submit" />
|
|
48
|
+
</form>
|
|
49
|
+
|
|
50
|
+
<div className={styles.sidebar} style={{ height: boardHeight + 1 }}>
|
|
51
|
+
<Well className={styles.resultsContainer} ref={resultsContainerRef}>
|
|
52
|
+
{resultsContainerWidth > 0 && resultsContainerHeight > 0 && (
|
|
53
|
+
<Results height={resultsHeight} width={resultsContainerWidth} />
|
|
54
|
+
)}
|
|
55
|
+
</Well>
|
|
56
|
+
|
|
57
|
+
<Well>
|
|
58
|
+
<div className={styles.dictionary} style={{ height: DICTIONARY_HEIGHT }}>
|
|
59
|
+
<Dictionary className={styles.dictionaryOutput} />
|
|
60
|
+
<DictionaryInput className={styles.dictionaryInput} />
|
|
61
|
+
</div>
|
|
62
|
+
</Well>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<form className={styles.rackContainer} onSubmit={handleSubmit}>
|
|
68
|
+
<Rack className={styles.rack} />
|
|
69
|
+
<input className={styles.submitInput} tabIndex={-1} type="submit" />
|
|
70
|
+
</form>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default Solver;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Solver';
|
|
@@ -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';
|
package/src/i18n/fr.json
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"results.empty-state.no-filtered-results": "Aucun résultat ne correspond à cette requête",
|
|
41
41
|
"results.empty-state.no-results": "Pas de résultats - impossible de générer des mots.",
|
|
42
42
|
"results.empty-state.outdated": "Les résultats sont dépassé. Cliquer ci-dessous pour mettre à jour.",
|
|
43
|
-
"results.empty-state.uninitialized": "
|
|
43
|
+
"results.empty-state.uninitialized": "Les mots générés à partir de vos lettres seront affichés ici.",
|
|
44
44
|
"results.input.placeholder": "Rechercher les résultats... (RegExp)",
|
|
45
45
|
"results.solve": "Résoudre",
|
|
46
46
|
"settings": "Options",
|
|
@@ -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';
|
|
@@ -8,32 +8,29 @@ import getTotalRemainingTilesCount from './getTotalRemainingTilesCount';
|
|
|
8
8
|
const getRemainingTilesGroups = (remainingTiles: RemainingTile[]): RemainingTilesGroup[] => {
|
|
9
9
|
const consonants = remainingTiles.filter(isConsonant);
|
|
10
10
|
const vowels = remainingTiles.filter(isVowel);
|
|
11
|
+
const other = remainingTiles.filter(isOther);
|
|
11
12
|
const groups: RemainingTilesGroup[] = [];
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
translationKey: 'common.tiles',
|
|
34
|
-
totalCount: getTotalRemainingTilesCount(tiles),
|
|
35
|
-
});
|
|
36
|
-
}
|
|
14
|
+
groups.push({
|
|
15
|
+
remainingCount: getRemainingTilesCount(vowels),
|
|
16
|
+
tiles: vowels,
|
|
17
|
+
translationKey: 'common.vowels',
|
|
18
|
+
totalCount: getTotalRemainingTilesCount(vowels),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
groups.push({
|
|
22
|
+
remainingCount: getRemainingTilesCount(consonants),
|
|
23
|
+
tiles: consonants,
|
|
24
|
+
translationKey: 'common.consonants',
|
|
25
|
+
totalCount: getTotalRemainingTilesCount(consonants),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
groups.push({
|
|
29
|
+
remainingCount: getRemainingTilesCount(other),
|
|
30
|
+
tiles: other,
|
|
31
|
+
translationKey: 'common.tiles',
|
|
32
|
+
totalCount: getTotalRemainingTilesCount(other),
|
|
33
|
+
});
|
|
37
34
|
|
|
38
35
|
const twoCharacterTiles = remainingTiles.filter(isTwoCharacter);
|
|
39
36
|
const blanks = remainingTiles.filter(isBlank);
|
|
@@ -59,10 +56,11 @@ const isConsonant = (tile: RemainingTile): boolean => CONSONANTS.includes(tile.c
|
|
|
59
56
|
|
|
60
57
|
const isVowel = (tile: RemainingTile): boolean => VOWELS.includes(tile.character);
|
|
61
58
|
|
|
62
|
-
const isLetter = (tile: RemainingTile): boolean => !isBlank(tile) && !isTwoCharacter(tile);
|
|
63
|
-
|
|
64
59
|
const isTwoCharacter = (tile: RemainingTile): boolean => tile.character.length === 2;
|
|
65
60
|
|
|
66
61
|
const isBlank = (tile: RemainingTile): boolean => tile.character === BLANK;
|
|
67
62
|
|
|
63
|
+
const isOther = (tile: RemainingTile) =>
|
|
64
|
+
!isConsonant(tile) && !isVowel(tile) && !isBlank(tile) && !isTwoCharacter(tile);
|
|
65
|
+
|
|
68
66
|
export default getRemainingTilesGroups;
|
|
@@ -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
|
);
|