scrabble-solver 2.8.7 → 2.8.8
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/README.md +8 -8
- package/lerna.json +1 -1
- package/package.json +1 -1
- package/packages/configs/package.json +3 -3
- package/packages/constants/package.json +1 -1
- package/packages/dictionaries/package-lock.json +2 -2
- package/packages/dictionaries/package.json +4 -4
- package/packages/logger/package-lock.json +2 -2
- package/packages/logger/package.json +1 -1
- package/packages/scrabble-solver/.next/BUILD_ID +1 -1
- package/packages/scrabble-solver/.next/build-manifest.json +9 -9
- package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
- package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
- package/packages/scrabble-solver/.next/cache/next-server.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/prerender-manifest.json +1 -1
- package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
- package/packages/scrabble-solver/.next/server/chunks/413.js +89 -22
- package/packages/scrabble-solver/.next/server/chunks/515.js +105 -21
- package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/404.html +2 -2
- package/packages/scrabble-solver/.next/server/pages/404.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/500.html +2 -2
- package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/_document.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/_error.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.html +2 -2
- package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
- package/packages/scrabble-solver/.next/static/chunks/615-d258f6c528c18622.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/{404-30c06e61d256c5b2.js → 404-8eb3ba4f0ba17e08.js} +1 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-4a663fd3d5ca4524.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/{index-fcb69802550afb81.js → index-1a9826d740cc8830.js} +1 -1
- package/packages/scrabble-solver/.next/static/css/180c6c26317ac90f.css +1 -0
- package/packages/scrabble-solver/.next/static/z3J3qmq1nazbDv_ENIkCo/_buildManifest.js +1 -0
- package/packages/scrabble-solver/.next/static/{z_0_lqfmiI_ISokr6NNRq → z3J3qmq1nazbDv_ENIkCo}/_ssgManifest.js +0 -0
- package/packages/scrabble-solver/.next/trace +41 -40
- package/packages/scrabble-solver/package-lock.json +2 -2
- package/packages/scrabble-solver/package.json +8 -8
- package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +34 -9
- package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.tsx +23 -4
- package/packages/scrabble-solver/src/components/Board/components/Cell/CellPure.tsx +29 -1
- package/packages/scrabble-solver/src/components/Board/hooks/useGrid.ts +1 -0
- package/packages/scrabble-solver/src/i18n/de.json +1 -0
- package/packages/scrabble-solver/src/i18n/en.json +1 -0
- package/packages/scrabble-solver/src/i18n/es.json +1 -0
- package/packages/scrabble-solver/src/i18n/fr.json +1 -0
- package/packages/scrabble-solver/src/i18n/pl.json +1 -0
- package/packages/scrabble-solver/src/icons/Flag.svg +4 -0
- package/packages/scrabble-solver/src/icons/Star.svg +4 -0
- package/packages/scrabble-solver/src/icons/index.ts +2 -0
- package/packages/scrabble-solver/src/state/rootReducer.ts +10 -1
- package/packages/scrabble-solver/src/state/sagas.ts +21 -1
- package/packages/scrabble-solver/src/state/selectors.ts +41 -11
- package/packages/scrabble-solver/src/state/slices/cellFilterInitialState.ts +7 -0
- package/packages/scrabble-solver/src/state/slices/cellFilterSlice.ts +24 -0
- package/packages/scrabble-solver/src/state/slices/index.ts +2 -0
- package/packages/scrabble-solver/src/types/index.ts +1 -0
- package/packages/solver/package-lock.json +2 -2
- package/packages/solver/package.json +5 -5
- package/packages/types/package.json +2 -2
- package/packages/word-definitions/package-lock.json +8 -8
- package/packages/word-definitions/package.json +2 -2
- package/packages/word-lists/package-lock.json +2 -2
- package/packages/word-lists/package.json +2 -2
- package/packages/scrabble-solver/.next/static/chunks/56-cf37c430261bbea5.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-0b12a65bea70a0df.js +0 -1
- package/packages/scrabble-solver/.next/static/css/1f39b55d50f5b30b.css +0 -1
- package/packages/scrabble-solver/.next/static/z_0_lqfmiI_ISokr6NNRq/_buildManifest.js +0 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.8",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@scrabble-solver/scrabble-solver",
|
|
9
|
-
"version": "2.8.
|
|
9
|
+
"version": "2.8.8",
|
|
10
10
|
"license": "CC BY-NC-ND 4.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@popperjs/core": "^2.11.6",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.8",
|
|
4
4
|
"description": "Scrabble Solver 2 - App",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16"
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@popperjs/core": "^2.11.6",
|
|
32
32
|
"@reduxjs/toolkit": "^1.8.5",
|
|
33
|
-
"@scrabble-solver/configs": "^2.8.
|
|
34
|
-
"@scrabble-solver/constants": "^2.8.
|
|
35
|
-
"@scrabble-solver/dictionaries": "^2.8.
|
|
36
|
-
"@scrabble-solver/logger": "^2.8.
|
|
37
|
-
"@scrabble-solver/solver": "^2.8.
|
|
38
|
-
"@scrabble-solver/types": "^2.8.
|
|
39
|
-
"@scrabble-solver/word-definitions": "^2.8.
|
|
33
|
+
"@scrabble-solver/configs": "^2.8.8",
|
|
34
|
+
"@scrabble-solver/constants": "^2.8.8",
|
|
35
|
+
"@scrabble-solver/dictionaries": "^2.8.8",
|
|
36
|
+
"@scrabble-solver/logger": "^2.8.8",
|
|
37
|
+
"@scrabble-solver/solver": "^2.8.8",
|
|
38
|
+
"@scrabble-solver/types": "^2.8.8",
|
|
39
|
+
"@scrabble-solver/word-definitions": "^2.8.8",
|
|
40
40
|
"classnames": "^2.3.2",
|
|
41
41
|
"next": "^12.3.1",
|
|
42
42
|
"normalize.css": "^8.0.1",
|
|
@@ -13,7 +13,6 @@ $icon-size: 16px;
|
|
|
13
13
|
transition: var(--transition);
|
|
14
14
|
background-clip: padding-box;
|
|
15
15
|
|
|
16
|
-
&.bonusStart,
|
|
17
16
|
&.bonusWord2,
|
|
18
17
|
&.bonusWord3 {
|
|
19
18
|
position: relative;
|
|
@@ -33,12 +32,7 @@ $icon-size: 16px;
|
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
&.bonusStart {
|
|
36
|
-
background-color: var(--color--
|
|
37
|
-
|
|
38
|
-
&:before {
|
|
39
|
-
content: '★';
|
|
40
|
-
color: var(--color--foreground--secondary);
|
|
41
|
-
}
|
|
35
|
+
background-color: var(--color--violet--light);
|
|
42
36
|
}
|
|
43
37
|
|
|
44
38
|
&.bonusCharacter1 {
|
|
@@ -168,6 +162,7 @@ $icon-size: 16px;
|
|
|
168
162
|
}
|
|
169
163
|
|
|
170
164
|
&,
|
|
165
|
+
.filterCell,
|
|
171
166
|
.toggleDirection {
|
|
172
167
|
width: $icon-size;
|
|
173
168
|
height: $icon-size;
|
|
@@ -180,8 +175,6 @@ $icon-size: 16px;
|
|
|
180
175
|
}
|
|
181
176
|
|
|
182
177
|
.toggleDirection {
|
|
183
|
-
transition: var(--transition);
|
|
184
|
-
|
|
185
178
|
&.right {
|
|
186
179
|
transform: rotate(-90deg);
|
|
187
180
|
}
|
|
@@ -194,3 +187,35 @@ $icon-size: 16px;
|
|
|
194
187
|
font-weight: bold;
|
|
195
188
|
}
|
|
196
189
|
}
|
|
190
|
+
|
|
191
|
+
.filterCell {
|
|
192
|
+
color: var(--color--inactive);
|
|
193
|
+
|
|
194
|
+
&.filtered {
|
|
195
|
+
color: var(--color--foreground--secondary);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.iconContainer {
|
|
200
|
+
position: absolute;
|
|
201
|
+
top: 0;
|
|
202
|
+
right: 0;
|
|
203
|
+
bottom: 0;
|
|
204
|
+
left: 0;
|
|
205
|
+
display: flex;
|
|
206
|
+
align-items: center;
|
|
207
|
+
justify-content: center;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.flagContainer {
|
|
211
|
+
background-color: var(--color--primary);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.flag,
|
|
215
|
+
.star {
|
|
216
|
+
$size: 24px;
|
|
217
|
+
|
|
218
|
+
width: $size;
|
|
219
|
+
height: $size;
|
|
220
|
+
color: white;
|
|
221
|
+
}
|
|
@@ -4,7 +4,15 @@ import { FunctionComponent, RefObject, useCallback, useMemo } from 'react';
|
|
|
4
4
|
import { useDispatch } from 'react-redux';
|
|
5
5
|
|
|
6
6
|
import { getTileSizes } from 'lib';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
boardSlice,
|
|
9
|
+
cellFilterSlice,
|
|
10
|
+
selectCellBonus,
|
|
11
|
+
selectCellIsFiltered,
|
|
12
|
+
selectTilePoints,
|
|
13
|
+
useTranslate,
|
|
14
|
+
useTypedSelector,
|
|
15
|
+
} from 'state';
|
|
8
16
|
|
|
9
17
|
import CellPure from './CellPure';
|
|
10
18
|
|
|
@@ -34,10 +42,19 @@ const Cell: FunctionComponent<Props> = ({
|
|
|
34
42
|
const translate = useTranslate();
|
|
35
43
|
const bonus = useTypedSelector((state) => selectCellBonus(state, cell));
|
|
36
44
|
const points = useTypedSelector((state) => selectTilePoints(state, cell.tile));
|
|
45
|
+
const isFiltered = useTypedSelector((state) => selectCellIsFiltered(state, cell));
|
|
37
46
|
const { tileFontSize } = getTileSizes(size);
|
|
38
47
|
const isEmpty = tile.character === EMPTY_CELL;
|
|
39
48
|
const style = useMemo(() => ({ fontSize: tileFontSize }), [tileFontSize]);
|
|
40
49
|
|
|
50
|
+
const handleDirectionToggleClick = useCallback(() => {
|
|
51
|
+
if (inputRef.current) {
|
|
52
|
+
inputRef.current.focus();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
onDirectionToggle();
|
|
56
|
+
}, [onDirectionToggle]);
|
|
57
|
+
|
|
41
58
|
const handleFocus = useCallback(() => onFocus(x, y), [x, y, onFocus]);
|
|
42
59
|
|
|
43
60
|
const handleToggleBlankClick = useCallback(() => {
|
|
@@ -48,13 +65,13 @@ const Cell: FunctionComponent<Props> = ({
|
|
|
48
65
|
dispatch(boardSlice.actions.toggleCellIsBlank({ x, y }));
|
|
49
66
|
}, [dispatch, x, y]);
|
|
50
67
|
|
|
51
|
-
const
|
|
68
|
+
const handleToggleFilterCellClick = useCallback(() => {
|
|
52
69
|
if (inputRef.current) {
|
|
53
70
|
inputRef.current.focus();
|
|
54
71
|
}
|
|
55
72
|
|
|
56
|
-
|
|
57
|
-
}, [
|
|
73
|
+
dispatch(cellFilterSlice.actions.toggle({ x, y }));
|
|
74
|
+
}, [dispatch, x, y]);
|
|
58
75
|
|
|
59
76
|
return (
|
|
60
77
|
<CellPure
|
|
@@ -65,6 +82,7 @@ const Cell: FunctionComponent<Props> = ({
|
|
|
65
82
|
inputRef={inputRef}
|
|
66
83
|
isCenter={isCenter}
|
|
67
84
|
isEmpty={isEmpty}
|
|
85
|
+
isFiltered={isFiltered}
|
|
68
86
|
points={points}
|
|
69
87
|
size={size}
|
|
70
88
|
style={style}
|
|
@@ -73,6 +91,7 @@ const Cell: FunctionComponent<Props> = ({
|
|
|
73
91
|
onDirectionToggleClick={handleDirectionToggleClick}
|
|
74
92
|
onFocus={handleFocus}
|
|
75
93
|
onToggleBlankClick={handleToggleBlankClick}
|
|
94
|
+
onToggleFilterCellClick={handleToggleFilterCellClick}
|
|
76
95
|
/>
|
|
77
96
|
);
|
|
78
97
|
};
|
|
@@ -2,7 +2,7 @@ import { Bonus, Cell, Tile as TileModel } from '@scrabble-solver/types';
|
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { CSSProperties, FocusEventHandler, FunctionComponent, memo, MouseEventHandler, RefObject } from 'react';
|
|
4
4
|
|
|
5
|
-
import { ArrowDown } from 'icons';
|
|
5
|
+
import { ArrowDown, Flag, Star } from 'icons';
|
|
6
6
|
import { Translate } from 'types';
|
|
7
7
|
|
|
8
8
|
import Tile from '../../../Tile';
|
|
@@ -19,6 +19,7 @@ interface Props {
|
|
|
19
19
|
inputRef: RefObject<HTMLInputElement>;
|
|
20
20
|
isCenter: boolean;
|
|
21
21
|
isEmpty: boolean;
|
|
22
|
+
isFiltered: boolean;
|
|
22
23
|
points?: number;
|
|
23
24
|
size: number;
|
|
24
25
|
style?: CSSProperties;
|
|
@@ -27,6 +28,7 @@ interface Props {
|
|
|
27
28
|
onDirectionToggleClick: MouseEventHandler<HTMLButtonElement>;
|
|
28
29
|
onFocus: FocusEventHandler<HTMLInputElement>;
|
|
29
30
|
onToggleBlankClick: MouseEventHandler<HTMLButtonElement>;
|
|
31
|
+
onToggleFilterCellClick: MouseEventHandler<HTMLButtonElement>;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
const CellPure: FunctionComponent<Props> = ({
|
|
@@ -37,6 +39,7 @@ const CellPure: FunctionComponent<Props> = ({
|
|
|
37
39
|
inputRef,
|
|
38
40
|
isCenter,
|
|
39
41
|
isEmpty,
|
|
42
|
+
isFiltered,
|
|
40
43
|
points,
|
|
41
44
|
size,
|
|
42
45
|
style,
|
|
@@ -45,6 +48,7 @@ const CellPure: FunctionComponent<Props> = ({
|
|
|
45
48
|
onDirectionToggleClick,
|
|
46
49
|
onFocus,
|
|
47
50
|
onToggleBlankClick,
|
|
51
|
+
onToggleFilterCellClick,
|
|
48
52
|
}) => (
|
|
49
53
|
<div
|
|
50
54
|
className={classNames(styles.cell, getBonusClassname(cell, bonus, isCenter), className, {
|
|
@@ -52,6 +56,18 @@ const CellPure: FunctionComponent<Props> = ({
|
|
|
52
56
|
})}
|
|
53
57
|
style={style}
|
|
54
58
|
>
|
|
59
|
+
{isCenter && (
|
|
60
|
+
<div className={classNames(styles.iconContainer)}>
|
|
61
|
+
<Star className={styles.star} />
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
|
|
65
|
+
{isFiltered && (
|
|
66
|
+
<div className={classNames(styles.iconContainer, styles.flagContainer)}>
|
|
67
|
+
<Flag className={styles.flag} />
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
|
|
55
71
|
<Tile
|
|
56
72
|
className={styles.tile}
|
|
57
73
|
character={isEmpty ? undefined : tile.character}
|
|
@@ -74,6 +90,18 @@ const CellPure: FunctionComponent<Props> = ({
|
|
|
74
90
|
/>
|
|
75
91
|
</Button>
|
|
76
92
|
|
|
93
|
+
{isEmpty && (
|
|
94
|
+
<Button
|
|
95
|
+
className={classNames(styles.filterCell, {
|
|
96
|
+
[styles.filtered]: isFiltered,
|
|
97
|
+
})}
|
|
98
|
+
tooltip={translate('cell.filter-cell')}
|
|
99
|
+
onClick={onToggleFilterCellClick}
|
|
100
|
+
>
|
|
101
|
+
<Flag />
|
|
102
|
+
</Button>
|
|
103
|
+
)}
|
|
104
|
+
|
|
77
105
|
{!isEmpty && (
|
|
78
106
|
<Button
|
|
79
107
|
className={classNames(styles.blank, {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/flag-fill/ -->
|
|
2
|
+
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M14.778.085A.5.5 0 0 1 15 .5V8a.5.5 0 0 1-.314.464L14.5 8l.186.464-.003.001-.006.003-.023.009a12.435 12.435 0 0 1-.397.15c-.264.095-.631.223-1.047.35-.816.252-1.879.523-2.71.523-.847 0-1.548-.28-2.158-.525l-.028-.01C7.68 8.71 7.14 8.5 6.5 8.5c-.7 0-1.638.23-2.437.477A19.626 19.626 0 0 0 3 9.342V15.5a.5.5 0 0 1-1 0V.5a.5.5 0 0 1 1 0v.282c.226-.079.496-.17.79-.26C4.606.272 5.67 0 6.5 0c.84 0 1.524.277 2.121.519l.043.018C9.286.788 9.828 1 10.5 1c.7 0 1.638-.23 2.437-.477a19.587 19.587 0 0 0 1.349-.476l.019-.007.004-.002h.001" fill="currentColor" />
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/star-fill/ -->
|
|
2
|
+
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z" fill="currentColor" />
|
|
4
|
+
</svg>
|
|
@@ -8,6 +8,7 @@ export { default as Cog } from './Cog.svg';
|
|
|
8
8
|
export { default as Cross } from './Cross.svg';
|
|
9
9
|
export { default as DashCircleFill } from './DashCircleFill.svg';
|
|
10
10
|
export { default as Eraser } from './Eraser.svg';
|
|
11
|
+
export { default as Flag } from './Flag.svg';
|
|
11
12
|
export { default as FlagEs } from './FlagEs.svg';
|
|
12
13
|
export { default as FlagFr } from './FlagFr.svg';
|
|
13
14
|
export { default as FlagGb } from './FlagGb.svg';
|
|
@@ -20,3 +21,4 @@ export { default as Play } from './Play.svg';
|
|
|
20
21
|
export { default as Sack } from './Sack.svg';
|
|
21
22
|
export { default as SortDown } from './SortDown.svg';
|
|
22
23
|
export { default as SortUp } from './SortUp.svg';
|
|
24
|
+
export { default as Star } from './Star.svg';
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { combineReducers } from 'redux';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
boardSlice,
|
|
5
|
+
dictionarySlice,
|
|
6
|
+
cellFilterSlice,
|
|
7
|
+
rackSlice,
|
|
8
|
+
resultsSlice,
|
|
9
|
+
settingsSlice,
|
|
10
|
+
solveSlice,
|
|
11
|
+
} from './slices';
|
|
4
12
|
|
|
5
13
|
const rootReducer = combineReducers({
|
|
6
14
|
board: boardSlice.reducer,
|
|
15
|
+
cellFilter: cellFilterSlice.reducer,
|
|
7
16
|
dictionary: dictionarySlice.reducer,
|
|
8
17
|
rack: rackSlice.reducer,
|
|
9
18
|
results: resultsSlice.reducer,
|
|
@@ -9,12 +9,21 @@ import { initialize, reset } from './actions';
|
|
|
9
9
|
import {
|
|
10
10
|
selectAutoGroupTiles,
|
|
11
11
|
selectBoard,
|
|
12
|
+
selectCellIsFiltered,
|
|
12
13
|
selectCharacters,
|
|
13
14
|
selectConfig,
|
|
14
15
|
selectDictionary,
|
|
15
16
|
selectLocale,
|
|
16
17
|
} from './selectors';
|
|
17
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
boardSlice,
|
|
20
|
+
cellFilterSlice,
|
|
21
|
+
dictionarySlice,
|
|
22
|
+
rackSlice,
|
|
23
|
+
resultsSlice,
|
|
24
|
+
settingsSlice,
|
|
25
|
+
solveSlice,
|
|
26
|
+
} from './slices';
|
|
18
27
|
|
|
19
28
|
const SUBMIT_DELAY = 150;
|
|
20
29
|
|
|
@@ -26,6 +35,7 @@ const memoizedFindWordDefinitions = memoize(findWordDefinitions);
|
|
|
26
35
|
type AnyGenerator = Generator<any, any, any>;
|
|
27
36
|
|
|
28
37
|
export function* rootSaga(): AnyGenerator {
|
|
38
|
+
yield takeEvery(boardSlice.actions.changeCellValue.type, onCellValueChange);
|
|
29
39
|
yield takeEvery(resultsSlice.actions.applyResult.type, onApplyResult);
|
|
30
40
|
yield takeEvery(resultsSlice.actions.changeResultCandidate.type, onResultCandidateChange);
|
|
31
41
|
yield takeEvery(settingsSlice.actions.changeConfigId.type, onConfigIdChange);
|
|
@@ -36,9 +46,18 @@ export function* rootSaga(): AnyGenerator {
|
|
|
36
46
|
yield takeLatest(solveSlice.actions.submit.type, onSubmit);
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
function* onCellValueChange({ payload }: PayloadAction<{ value: string; x: number; y: number }>): AnyGenerator {
|
|
50
|
+
const isFiltered = yield select((state) => selectCellIsFiltered(state, payload));
|
|
51
|
+
|
|
52
|
+
if (isFiltered) {
|
|
53
|
+
yield put(cellFilterSlice.actions.toggle(payload));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
function* onApplyResult({ payload: result }: PayloadAction<Result>): AnyGenerator {
|
|
40
58
|
const autoGroupTiles = yield select(selectAutoGroupTiles);
|
|
41
59
|
yield put(boardSlice.actions.applyResult(result));
|
|
60
|
+
yield put(cellFilterSlice.actions.reset());
|
|
42
61
|
yield put(rackSlice.actions.removeTiles(result.tiles));
|
|
43
62
|
yield put(rackSlice.actions.groupTiles(autoGroupTiles));
|
|
44
63
|
}
|
|
@@ -72,6 +91,7 @@ function* onInitialize(): AnyGenerator {
|
|
|
72
91
|
|
|
73
92
|
function* onReset(): AnyGenerator {
|
|
74
93
|
yield put(boardSlice.actions.reset());
|
|
94
|
+
yield put(cellFilterSlice.actions.reset());
|
|
75
95
|
yield put(dictionarySlice.actions.reset());
|
|
76
96
|
yield put(rackSlice.actions.reset());
|
|
77
97
|
yield put(resultsSlice.actions.reset());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import { getLocaleConfig } from '@scrabble-solver/configs';
|
|
3
|
-
import { Cell, Config, Tile } from '@scrabble-solver/types';
|
|
3
|
+
import { Cell, Config, Result, Tile } from '@scrabble-solver/types';
|
|
4
4
|
|
|
5
5
|
import i18n from 'i18n';
|
|
6
6
|
import { findCell, getRemainingTiles, getRemainingTilesGroups, sortResults, unorderedArraysEqual } from 'lib';
|
|
@@ -10,6 +10,8 @@ import { RootState } from './types';
|
|
|
10
10
|
|
|
11
11
|
const selectCell = (_: unknown, cell: Cell): Cell => cell;
|
|
12
12
|
|
|
13
|
+
const selectPoint = (_: unknown, point: { x: number; y: number }): { x: number; y: number } => point;
|
|
14
|
+
|
|
13
15
|
const selectCharacter = (_: unknown, character: string | null): string | null => character;
|
|
14
16
|
|
|
15
17
|
const selectTile = (_: unknown, tile: Tile | null): Tile | null => tile;
|
|
@@ -18,6 +20,8 @@ const selectBoardRoot = (state: RootState): RootState['board'] => state.board;
|
|
|
18
20
|
|
|
19
21
|
const selectDictionaryRoot = (state: RootState): RootState['dictionary'] => state.dictionary;
|
|
20
22
|
|
|
23
|
+
const selectCellFilterRoot = (state: RootState): RootState['cellFilter'] => state.cellFilter;
|
|
24
|
+
|
|
21
25
|
const selectRackRoot = (state: RootState): RootState['rack'] => state.rack;
|
|
22
26
|
|
|
23
27
|
const selectResultsRoot = (state: RootState): RootState['results'] => state.results;
|
|
@@ -38,6 +42,12 @@ export const selectConfigId = createSelector([selectSettingsRoot], (settings) =>
|
|
|
38
42
|
|
|
39
43
|
export const selectConfig = createSelector([selectConfigId, selectLocale], getLocaleConfig);
|
|
40
44
|
|
|
45
|
+
export const selectCellFilter = selectCellFilterRoot;
|
|
46
|
+
|
|
47
|
+
export const selectCellIsFiltered = createSelector([selectCellFilter, selectPoint], (cellFilter, { x, y }) => {
|
|
48
|
+
return cellFilter.some((cell) => cell.x === x && cell.y === y);
|
|
49
|
+
});
|
|
50
|
+
|
|
41
51
|
export const selectResults = createSelector([selectResultsRoot], (results) => results.results);
|
|
42
52
|
|
|
43
53
|
export const selectResultsQuery = createSelector([selectResultsRoot], (results) => results.query);
|
|
@@ -51,20 +61,40 @@ export const selectSortedResults = createSelector(
|
|
|
51
61
|
sortResults,
|
|
52
62
|
);
|
|
53
63
|
|
|
64
|
+
const filterResultsByQuery = (results: Result[], query: string): Result[] => {
|
|
65
|
+
if (query.trim().length === 0) {
|
|
66
|
+
return results;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let regExp: RegExp | undefined;
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
regExp = new RegExp(query, 'gi');
|
|
73
|
+
} catch {
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return results.filter((result) => {
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
79
|
+
return regExp!.test(result.word);
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
54
83
|
export const selectSortedFilteredResults = createSelector(
|
|
55
|
-
[selectSortedResults, selectResultsQuery],
|
|
56
|
-
(results, query) => {
|
|
57
|
-
if (!results
|
|
84
|
+
[selectSortedResults, selectResultsQuery, selectCellFilter],
|
|
85
|
+
(results, query, cellFilter) => {
|
|
86
|
+
if (!results) {
|
|
58
87
|
return results;
|
|
59
88
|
}
|
|
60
89
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
const filteredByQuery = filterResultsByQuery(results, query);
|
|
91
|
+
|
|
92
|
+
if (!cellFilter) {
|
|
93
|
+
return filteredByQuery;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return filteredByQuery.filter((result) => {
|
|
97
|
+
return cellFilter.every(({ x, y }) => result.cells.some((cell) => cell.x === x && cell.y === y));
|
|
68
98
|
});
|
|
69
99
|
},
|
|
70
100
|
);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
2
|
+
|
|
3
|
+
import cellFilterInitialState, { Point } from './cellFilterInitialState';
|
|
4
|
+
|
|
5
|
+
const cellFilterSlice = createSlice({
|
|
6
|
+
initialState: cellFilterInitialState,
|
|
7
|
+
name: 'cellFilter',
|
|
8
|
+
reducers: {
|
|
9
|
+
toggle: (state, action: PayloadAction<Point>) => {
|
|
10
|
+
const { x, y } = action.payload;
|
|
11
|
+
const has = state.some((point) => point.x === x && point.y === y);
|
|
12
|
+
|
|
13
|
+
if (has) {
|
|
14
|
+
return state.filter((point) => point.x !== x || point.y !== y);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return [...state, action.payload];
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
reset: () => cellFilterInitialState,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export default cellFilterSlice;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { default as boardInitialState } from './boardInitialState';
|
|
2
2
|
export { default as boardSlice } from './boardSlice';
|
|
3
|
+
export { default as cellFilterInitialState } from './cellFilterInitialState';
|
|
4
|
+
export { default as cellFilterSlice } from './cellFilterSlice';
|
|
3
5
|
export { default as dictionaryInitialState } from './dictionaryInitialState';
|
|
4
6
|
export { default as dictionarySlice } from './dictionarySlice';
|
|
5
7
|
export { default as rackInitialState } from './rackInitialState';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/solver",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.8",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@scrabble-solver/solver",
|
|
9
|
-
"version": "2.8.
|
|
9
|
+
"version": "2.8.8",
|
|
10
10
|
"license": "CC-BY-NC-ND-4.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@kamilmielnik/trie": "^2.0.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/solver",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.8",
|
|
4
4
|
"description": "Scrabble Solver 2 - Solver",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@kamilmielnik/trie": "^2.0.0",
|
|
28
|
-
"@scrabble-solver/types": "^2.8.
|
|
28
|
+
"@scrabble-solver/types": "^2.8.8"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@scrabble-solver/configs": "^2.8.
|
|
32
|
-
"@scrabble-solver/constants": "^2.8.
|
|
33
|
-
"@scrabble-solver/dictionaries": "^2.8.
|
|
31
|
+
"@scrabble-solver/configs": "^2.8.8",
|
|
32
|
+
"@scrabble-solver/constants": "^2.8.8",
|
|
33
|
+
"@scrabble-solver/dictionaries": "^2.8.8"
|
|
34
34
|
},
|
|
35
35
|
"gitHead": "1e2d1ad49a288d4a44196d8e3336740da132c637"
|
|
36
36
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/types",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.8",
|
|
4
4
|
"description": "Scrabble Solver 2 - Types",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"clean:force": "npm run clean && rimraf package-lock.json"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@scrabble-solver/constants": "^2.8.
|
|
27
|
+
"@scrabble-solver/constants": "^2.8.8"
|
|
28
28
|
},
|
|
29
29
|
"gitHead": "1e2d1ad49a288d4a44196d8e3336740da132c637"
|
|
30
30
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/word-definitions",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.8",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@scrabble-solver/word-definitions",
|
|
9
|
-
"version": "2.8.
|
|
9
|
+
"version": "2.8.8",
|
|
10
10
|
"license": "CC-BY-NC-ND-4.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"cheerio": "^1.0.0-rc.12",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"node_modules/@types/node": {
|
|
30
|
-
"version": "18.8.
|
|
31
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.
|
|
32
|
-
"integrity": "sha512-
|
|
30
|
+
"version": "18.8.4",
|
|
31
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.4.tgz",
|
|
32
|
+
"integrity": "sha512-WdlVphvfR/GJCLEMbNA8lJ0lhFNBj4SW3O+O5/cEGw9oYrv0al9zTwuQsq+myDUXgNx2jgBynoVgZ2MMJ6pbow==",
|
|
33
33
|
"dev": true
|
|
34
34
|
},
|
|
35
35
|
"node_modules/boolbase": {
|
|
@@ -249,9 +249,9 @@
|
|
|
249
249
|
}
|
|
250
250
|
},
|
|
251
251
|
"@types/node": {
|
|
252
|
-
"version": "18.8.
|
|
253
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.
|
|
254
|
-
"integrity": "sha512-
|
|
252
|
+
"version": "18.8.4",
|
|
253
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.4.tgz",
|
|
254
|
+
"integrity": "sha512-WdlVphvfR/GJCLEMbNA8lJ0lhFNBj4SW3O+O5/cEGw9oYrv0al9zTwuQsq+myDUXgNx2jgBynoVgZ2MMJ6pbow==",
|
|
255
255
|
"dev": true
|
|
256
256
|
},
|
|
257
257
|
"boolbase": {
|