scrabble-solver 2.10.10 → 2.11.0
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/lerna.json +1 -1
- package/package.json +8 -8
- 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 +8 -8
- package/packages/logger/package.json +1 -1
- package/packages/scrabble-solver/.next/BUILD_ID +1 -1
- package/packages/scrabble-solver/.next/build-manifest.json +12 -12
- 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/edge-server-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/edge-server-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/required-server-files.json +1 -1
- package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
- package/packages/scrabble-solver/.next/server/chunks/{176.js → 277.js} +860 -767
- package/packages/scrabble-solver/.next/server/chunks/{290.js → 417.js} +3 -3
- package/packages/scrabble-solver/.next/server/chunks/50.js +371 -343
- package/packages/scrabble-solver/.next/server/chunks/664.js +15 -15
- package/packages/scrabble-solver/.next/server/chunks/859.js +17 -10
- package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
- package/packages/scrabble-solver/.next/server/next-font-manifest.js +1 -0
- 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 +1 -1
- package/packages/scrabble-solver/.next/server/pages/_app.js +4 -12
- package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/_document.js +2 -2
- package/packages/scrabble-solver/.next/server/pages/_document.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/_error.js +4 -4
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js +4 -4
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js +3 -3
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/solve.js +9 -14
- package/packages/scrabble-solver/.next/server/pages/api/solve.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/verify.js +3 -3
- package/packages/scrabble-solver/.next/server/pages/api/verify.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/visit.js +3 -3
- package/packages/scrabble-solver/.next/server/pages/api/visit.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.js +250 -175
- 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/server/pages-manifest.json +1 -1
- package/packages/scrabble-solver/.next/static/45ye7793DY705HOcuK9lJ/_buildManifest.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/main-0ecb9ccfcb6c9b24.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/404-e0f30450e9920dc3.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-d7acee5e526752d9.js +28 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/{_error-8353112a01355ec2.js → _error-54de1933a164a1ff.js} +1 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/index-35d2c1c79a201ae2.js +1 -0
- package/packages/scrabble-solver/.next/static/css/a48caa6f57de6e98.css +1 -0
- package/packages/scrabble-solver/.next/static/css/c49bbe944ddd1b39.css +2 -0
- package/packages/scrabble-solver/.next/trace +55 -55
- package/packages/scrabble-solver/package-lock.json +224 -253
- package/packages/scrabble-solver/package.json +11 -12
- package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +1 -0
- package/packages/scrabble-solver/src/components/Board/hooks/useGrid.ts +1 -1
- package/packages/scrabble-solver/src/components/Dictionary/Dictionary.module.scss +13 -8
- package/packages/scrabble-solver/src/components/Dictionary/Dictionary.tsx +5 -5
- package/packages/scrabble-solver/src/components/Loading/Loading.tsx +1 -1
- package/packages/scrabble-solver/src/components/Modal/Modal.module.scss +0 -1
- package/packages/scrabble-solver/src/components/NavButtons/NavButtons.tsx +4 -5
- package/packages/scrabble-solver/src/components/Results/Results.module.scss +15 -2
- package/packages/scrabble-solver/src/components/Results/Results.tsx +78 -67
- package/packages/scrabble-solver/src/components/ResultsInput/ResultsInput.tsx +6 -2
- package/packages/scrabble-solver/src/components/Solver/Solver.module.scss +9 -7
- package/packages/scrabble-solver/src/components/Solver/Solver.tsx +21 -49
- package/packages/scrabble-solver/src/components/Solver/components/FloatingSolveButton/FloatingSolveButton.module.scss +7 -0
- package/packages/scrabble-solver/src/components/Solver/components/{SolveButton/SolveButton.tsx → FloatingSolveButton/FloatingSolveButton.tsx} +6 -6
- package/packages/scrabble-solver/src/components/Solver/components/FloatingSolveButton/index.ts +1 -0
- package/packages/scrabble-solver/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss +19 -4
- package/packages/scrabble-solver/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +13 -1
- package/packages/scrabble-solver/src/components/Solver/components/index.ts +1 -1
- package/packages/scrabble-solver/src/components/Spinner/Spinner.module.scss +11 -0
- package/packages/scrabble-solver/src/components/Spinner/Spinner.tsx +19 -0
- package/packages/scrabble-solver/src/components/Spinner/index.ts +1 -0
- package/packages/scrabble-solver/src/components/Tile/Tile.tsx +9 -9
- package/packages/scrabble-solver/src/components/index.ts +1 -1
- package/packages/scrabble-solver/src/hooks/index.ts +1 -0
- package/packages/scrabble-solver/src/hooks/useAppLayout.ts +29 -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/fa.json +1 -0
- package/packages/scrabble-solver/src/i18n/fr.json +1 -0
- package/packages/scrabble-solver/src/i18n/index.ts +1 -1
- package/packages/scrabble-solver/src/i18n/pl.json +1 -0
- package/packages/scrabble-solver/src/modals/DictionaryModal/DictionaryModal.module.scss +18 -0
- package/packages/scrabble-solver/src/modals/DictionaryModal/DictionaryModal.tsx +27 -0
- package/packages/scrabble-solver/src/modals/DictionaryModal/index.ts +1 -0
- package/packages/scrabble-solver/src/modals/MenuModal/MenuModal.tsx +7 -1
- package/packages/scrabble-solver/src/modals/ResultsModal/ResultsModal.module.scss +18 -1
- package/packages/scrabble-solver/src/modals/ResultsModal/ResultsModal.tsx +10 -13
- package/packages/scrabble-solver/src/modals/index.ts +1 -0
- package/packages/scrabble-solver/src/pages/api/solve.ts +9 -10
- package/packages/scrabble-solver/src/pages/index.tsx +20 -15
- package/packages/scrabble-solver/src/parameters/index.ts +0 -6
- package/packages/scrabble-solver/src/service-worker/routeSolveRequests.ts +4 -1
- package/packages/scrabble-solver/src/state/localStorage.ts +0 -9
- package/packages/scrabble-solver/src/styles/animations.scss +10 -0
- package/packages/scrabble-solver/src/styles/global.scss +1 -1
- package/packages/scrabble-solver/src/styles/variables.scss +1 -1
- 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/build/lib/normalizeDefinition.js +1 -1
- package/packages/word-definitions/package-lock.json +8 -8
- package/packages/word-definitions/package.json +2 -2
- package/packages/word-definitions/src/lib/normalizeDefinition.ts +1 -1
- package/packages/word-definitions/src/parse/__tests__/expected/en-US.oe.json +7 -0
- package/packages/word-definitions/src/parse/__tests__/input/en-US.oe.html +1337 -0
- package/packages/word-definitions/src/parse/parse.test.ts +1 -0
- package/packages/word-lists/package-lock.json +8 -8
- package/packages/word-lists/package.json +2 -2
- package/packages/configs/package-lock.json +0 -58
- package/packages/constants/package-lock.json +0 -13
- package/packages/scrabble-solver/.next/server/font-loader-manifest.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/main-74c4d6b2b5c362f3.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/404-d5ff00df1c687977.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-3272e798504c40d8.js +0 -28
- package/packages/scrabble-solver/.next/static/chunks/pages/index-5c2544930e46c5ce.js +0 -1
- package/packages/scrabble-solver/.next/static/css/336e75db2b74b157.css +0 -2
- package/packages/scrabble-solver/.next/static/css/ec4e47a6b1866fe5.css +0 -1
- package/packages/scrabble-solver/.next/static/warzWo25tDxo_Eiv9T6f2/_buildManifest.js +0 -1
- package/packages/scrabble-solver/src/components/Solver/components/SolveButton/SolveButton.module.scss +0 -4
- package/packages/scrabble-solver/src/components/Solver/components/SolveButton/index.ts +0 -1
- package/packages/scrabble-solver/src/components/Well/Well.module.scss +0 -6
- package/packages/scrabble-solver/src/components/Well/Well.tsx +0 -17
- package/packages/scrabble-solver/src/components/Well/index.ts +0 -1
- package/packages/types/package-lock.json +0 -30
- /package/packages/scrabble-solver/.next/server/{font-loader-manifest.json → next-font-manifest.json} +0 -0
- /package/packages/scrabble-solver/.next/static/{warzWo25tDxo_Eiv9T6f2 → 45ye7793DY705HOcuK9lJ}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Scrabble Solver 2 - App",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16"
|
|
@@ -28,22 +28,21 @@
|
|
|
28
28
|
"start": "env-cmd next start -p 3333"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@floating-ui/react": "^0.
|
|
31
|
+
"@floating-ui/react": "^0.20.1",
|
|
32
32
|
"@kamilmielnik/trie": "^2.0.1",
|
|
33
33
|
"@popperjs/core": "^2.11.6",
|
|
34
34
|
"@reduxjs/toolkit": "^1.9.3",
|
|
35
|
-
"@scrabble-solver/configs": "^2.
|
|
36
|
-
"@scrabble-solver/constants": "^2.
|
|
37
|
-
"@scrabble-solver/dictionaries": "^2.
|
|
38
|
-
"@scrabble-solver/logger": "^2.
|
|
39
|
-
"@scrabble-solver/solver": "^2.
|
|
40
|
-
"@scrabble-solver/types": "^2.
|
|
41
|
-
"@scrabble-solver/word-definitions": "^2.
|
|
35
|
+
"@scrabble-solver/configs": "^2.11.0",
|
|
36
|
+
"@scrabble-solver/constants": "^2.11.0",
|
|
37
|
+
"@scrabble-solver/dictionaries": "^2.11.0",
|
|
38
|
+
"@scrabble-solver/logger": "^2.11.0",
|
|
39
|
+
"@scrabble-solver/solver": "^2.11.0",
|
|
40
|
+
"@scrabble-solver/types": "^2.11.0",
|
|
41
|
+
"@scrabble-solver/word-definitions": "^2.11.0",
|
|
42
42
|
"classnames": "^2.3.2",
|
|
43
43
|
"include-media": "^2.0.0",
|
|
44
44
|
"include-media-query-builder": "^1.1.0",
|
|
45
|
-
"
|
|
46
|
-
"next": "^13.2.1",
|
|
45
|
+
"next": "^13.2.4",
|
|
47
46
|
"normalize.css": "^8.0.1",
|
|
48
47
|
"react": "^18.2.0",
|
|
49
48
|
"react-dom": "^18.2.0",
|
|
@@ -75,7 +74,7 @@
|
|
|
75
74
|
"@types/redux-saga": "^0.10.5",
|
|
76
75
|
"@types/uuid": "^9.0.1",
|
|
77
76
|
"env-cmd": "^10.1.0",
|
|
78
|
-
"sass": "^1.
|
|
77
|
+
"sass": "^1.59.2",
|
|
79
78
|
"workbox-webpack-plugin": "^6.5.4"
|
|
80
79
|
},
|
|
81
80
|
"gitHead": "1e2d1ad49a288d4a44196d8e3336740da132c637"
|
|
@@ -333,7 +333,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
|
|
|
333
333
|
dispatch(boardSlice.actions.toggleCellIsBlank(position));
|
|
334
334
|
},
|
|
335
335
|
});
|
|
336
|
-
}, [changeActiveIndex, config, dispatch, locale, onDirectionToggle, rows]);
|
|
336
|
+
}, [changeActiveIndex, config, dispatch, locale, moveFocus, onDirectionToggle, rows]);
|
|
337
337
|
|
|
338
338
|
const onPaste = useCallback<ClipboardEventHandler>(
|
|
339
339
|
(event) => {
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
.dictionary {
|
|
2
2
|
position: relative;
|
|
3
|
+
max-height: var(--dictionary--height);
|
|
3
4
|
height: var(--dictionary--height);
|
|
4
5
|
overflow-y: auto;
|
|
5
6
|
word-break: break-word;
|
|
6
7
|
transition: var(--transition);
|
|
8
|
+
|
|
9
|
+
&.isAllowed {
|
|
10
|
+
background-color: var(--color--green--light);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&.isNotAllowed {
|
|
14
|
+
background-color: var(--color--red--light);
|
|
15
|
+
}
|
|
7
16
|
}
|
|
8
17
|
|
|
9
18
|
.result {
|
|
10
19
|
transition: var(--transition);
|
|
11
20
|
|
|
12
21
|
&.isAllowed {
|
|
22
|
+
background-color: var(--color--green--light);
|
|
23
|
+
|
|
13
24
|
& + & {
|
|
14
25
|
.content {
|
|
15
26
|
padding-top: 0;
|
|
@@ -18,6 +29,8 @@
|
|
|
18
29
|
}
|
|
19
30
|
|
|
20
31
|
&.isNotAllowed {
|
|
32
|
+
background-color: var(--color--red--light);
|
|
33
|
+
|
|
21
34
|
& + & {
|
|
22
35
|
.content {
|
|
23
36
|
padding-top: 0;
|
|
@@ -37,14 +50,6 @@
|
|
|
37
50
|
text-transform: uppercase;
|
|
38
51
|
}
|
|
39
52
|
|
|
40
|
-
.isAllowed {
|
|
41
|
-
background-color: var(--color--green--light);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.isNotAllowed {
|
|
45
|
-
background-color: var(--color--red--light);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
53
|
.definitions {
|
|
49
54
|
margin: 0;
|
|
50
55
|
|
|
@@ -16,18 +16,18 @@ const Dictionary: FunctionComponent<Props> = ({ className }) => {
|
|
|
16
16
|
const translate = useTranslate();
|
|
17
17
|
const { results, isLoading } = useTypedSelector(selectDictionary);
|
|
18
18
|
const error = useTypedSelector(selectDictionaryError);
|
|
19
|
-
const
|
|
19
|
+
const isLastAllowed = results.length > 0 ? results[results.length - 1].isAllowed : undefined;
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
22
|
<div
|
|
23
23
|
className={classNames(styles.dictionary, className, {
|
|
24
|
-
[styles.isAllowed]:
|
|
25
|
-
[styles.isNotAllowed]:
|
|
24
|
+
[styles.isAllowed]: isLastAllowed === true,
|
|
25
|
+
[styles.isNotAllowed]: isLastAllowed === false,
|
|
26
26
|
})}
|
|
27
27
|
>
|
|
28
|
-
{typeof error !== 'undefined' && <EmptyState variant="error">{error.message}</EmptyState>}
|
|
28
|
+
{typeof error !== 'undefined' && !isLoading && <EmptyState variant="error">{error.message}</EmptyState>}
|
|
29
29
|
|
|
30
|
-
{results.length === 0 && (
|
|
30
|
+
{typeof error === 'undefined' && !isLoading && results.length === 0 && (
|
|
31
31
|
<EmptyState variant="info">{translate('dictionary.empty-state.uninitialized')}</EmptyState>
|
|
32
32
|
)}
|
|
33
33
|
|
|
@@ -29,7 +29,7 @@ const Loading: FunctionComponent<Props> = ({ className, wave = true }) => {
|
|
|
29
29
|
const content = useMemo(() => prepareContent(message), [message]);
|
|
30
30
|
|
|
31
31
|
return (
|
|
32
|
-
<div className={classNames(styles.loading, className)}>
|
|
32
|
+
<div aria-label={translation} className={classNames(styles.loading, className)} role="status">
|
|
33
33
|
<div className={styles.dim} />
|
|
34
34
|
<div className={styles.logo}>
|
|
35
35
|
<PlainTiles className={classNames(styles.tiles)} content={content} dropShadow wave={wave} />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { FunctionComponent } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { useAppLayout } from 'hooks';
|
|
5
5
|
import { CardChecklist, Cog, Eraser, Github, Keyboard, List, Sack } from 'icons';
|
|
6
6
|
import { GITHUB_PROJECT_URL } from 'parameters';
|
|
7
7
|
import { selectHasInvalidWords, selectHasOverusedTiles, useTranslate, useTypedSelector } from 'state';
|
|
@@ -30,10 +30,9 @@ const NavButtons: FunctionComponent<Props> = ({
|
|
|
30
30
|
const translate = useTranslate();
|
|
31
31
|
const hasOverusedTiles = useTypedSelector(selectHasOverusedTiles);
|
|
32
32
|
const hasInvalidWords = useTypedSelector(selectHasInvalidWords);
|
|
33
|
-
const
|
|
34
|
-
const isLessThanS = useMediaQuery('<s');
|
|
33
|
+
const { showKeyMap, showShortNav } = useAppLayout();
|
|
35
34
|
|
|
36
|
-
if (
|
|
35
|
+
if (showShortNav) {
|
|
37
36
|
return (
|
|
38
37
|
<div className={styles.navButtons}>
|
|
39
38
|
<div className={styles.group}>
|
|
@@ -114,7 +113,7 @@ const NavButtons: FunctionComponent<Props> = ({
|
|
|
114
113
|
<div className={styles.separator} />
|
|
115
114
|
|
|
116
115
|
<div className={styles.group}>
|
|
117
|
-
{
|
|
116
|
+
{showKeyMap && (
|
|
118
117
|
<IconButton
|
|
119
118
|
aria-label={translate('keyMap')}
|
|
120
119
|
className={styles.button}
|
|
@@ -7,6 +7,9 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
7
7
|
display: flex;
|
|
8
8
|
flex-direction: column;
|
|
9
9
|
height: 100%;
|
|
10
|
+
background: var(--color--background--element);
|
|
11
|
+
border: var(--border);
|
|
12
|
+
box-shadow: var(--box-shadow);
|
|
10
13
|
font-family: var(--font--family--title);
|
|
11
14
|
}
|
|
12
15
|
|
|
@@ -14,8 +17,18 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
14
17
|
flex: 1;
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
.content {
|
|
21
|
+
flex: 1;
|
|
22
|
+
position: relative;
|
|
23
|
+
height: 100%;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.listContainer {
|
|
27
|
+
position: absolute;
|
|
28
|
+
top: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
17
31
|
.list {
|
|
18
|
-
flex: 1 1 auto;
|
|
19
32
|
transition: var(--transition);
|
|
20
33
|
|
|
21
34
|
&.outdated {
|
|
@@ -29,8 +42,8 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
29
42
|
display: flex;
|
|
30
43
|
align-items: center;
|
|
31
44
|
justify-content: space-between;
|
|
32
|
-
border-bottom: var(--border);
|
|
33
45
|
font-weight: 700;
|
|
46
|
+
border-bottom: var(--border);
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
.headerButton {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { FunctionComponent, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { useLatest, useMeasure } from 'react-use';
|
|
3
4
|
import { FixedSizeList } from 'react-window';
|
|
4
5
|
|
|
5
6
|
import { LOCALE_FEATURES } from 'i18n';
|
|
6
|
-
import {
|
|
7
|
+
import { RESULTS_ITEM_HEIGHT } from 'parameters';
|
|
7
8
|
import {
|
|
8
9
|
selectAreResultsOutdated,
|
|
9
10
|
selectIsLoading,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
import EmptyState from '../EmptyState';
|
|
19
20
|
import Loading from '../Loading';
|
|
20
21
|
import ResultsInput from '../ResultsInput';
|
|
22
|
+
import Sizer from '../Sizer';
|
|
21
23
|
|
|
22
24
|
import HeaderButton from './HeaderButton';
|
|
23
25
|
import Result from './Result';
|
|
@@ -28,12 +30,11 @@ import useColumns from './useColumns';
|
|
|
28
30
|
|
|
29
31
|
interface Props {
|
|
30
32
|
callbacks: ResultCallbacks;
|
|
31
|
-
|
|
33
|
+
className?: string;
|
|
32
34
|
highlightedIndex?: number;
|
|
33
|
-
width: number;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
const Results: FunctionComponent<Props> = ({ callbacks,
|
|
37
|
+
const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIndex }) => {
|
|
37
38
|
const translate = useTranslate();
|
|
38
39
|
const locale = useTypedSelector(selectLocale);
|
|
39
40
|
const { direction } = LOCALE_FEATURES[locale];
|
|
@@ -44,92 +45,102 @@ const Results: FunctionComponent<Props> = ({ callbacks, height, highlightedIndex
|
|
|
44
45
|
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
45
46
|
const error = useTypedSelector(selectSolveError);
|
|
46
47
|
const itemData = useMemo(() => ({ ...callbacks, highlightedIndex, results }), [callbacks, highlightedIndex, results]);
|
|
48
|
+
const [sizerRef, { height, width }] = useMeasure<HTMLDivElement>();
|
|
47
49
|
const [listRef, setListRef] = useState<FixedSizeList<ResultData> | null>(null);
|
|
48
50
|
const columns = useColumns();
|
|
49
51
|
const scrollToIndex = typeof highlightedIndex === 'number' ? highlightedIndex : 0;
|
|
52
|
+
const scrollToIndexRef = useLatest(scrollToIndex);
|
|
53
|
+
const hasResults =
|
|
54
|
+
typeof error === 'undefined' && typeof filteredResults !== 'undefined' && typeof allResults !== 'undefined';
|
|
50
55
|
|
|
51
56
|
useEffect(() => {
|
|
52
57
|
// without setTimeout, the initial scrolling offset is calculated
|
|
53
58
|
// incorrectly, as the list is not fully rendered by the browser yet
|
|
54
59
|
const timeout = globalThis.setTimeout(() => {
|
|
55
60
|
if (listRef) {
|
|
56
|
-
listRef.scrollToItem(
|
|
61
|
+
listRef.scrollToItem(scrollToIndexRef.current, 'center');
|
|
57
62
|
}
|
|
58
63
|
}, 0);
|
|
59
64
|
|
|
60
65
|
return () => {
|
|
61
66
|
globalThis.clearTimeout(timeout);
|
|
62
67
|
};
|
|
63
|
-
}, [listRef,
|
|
68
|
+
}, [allResults, listRef, scrollToIndexRef]);
|
|
64
69
|
|
|
65
70
|
return (
|
|
66
|
-
<div className={styles.results}>
|
|
71
|
+
<div className={classNames(styles.results, className)}>
|
|
67
72
|
<div className={styles.header}>
|
|
68
73
|
{columns.map((column) => (
|
|
69
74
|
<HeaderButton column={column} key={column.id} />
|
|
70
75
|
))}
|
|
71
76
|
</div>
|
|
72
77
|
|
|
73
|
-
{
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
78
|
+
<div className={styles.content}>
|
|
79
|
+
<Sizer ref={sizerRef} />
|
|
80
|
+
|
|
81
|
+
{typeof error !== 'undefined' && (
|
|
82
|
+
<EmptyState className={styles.emptyState} variant="error">
|
|
83
|
+
{error.message}
|
|
84
|
+
</EmptyState>
|
|
85
|
+
)}
|
|
86
|
+
|
|
87
|
+
{typeof error === 'undefined' && typeof filteredResults === 'undefined' && (
|
|
88
|
+
<EmptyState className={styles.emptyState} variant="info">
|
|
89
|
+
{translate('results.empty-state.uninitialized')}
|
|
90
|
+
|
|
91
|
+
<SolveButton className={styles.solveButton} />
|
|
92
|
+
</EmptyState>
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
{hasResults && (
|
|
96
|
+
<>
|
|
97
|
+
{isOutdated && (
|
|
98
|
+
<EmptyState className={styles.emptyState} variant="info">
|
|
99
|
+
{translate('results.empty-state.outdated')}
|
|
100
|
+
|
|
101
|
+
<SolveButton className={styles.solveButton} />
|
|
102
|
+
</EmptyState>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
{!isOutdated && (
|
|
106
|
+
<>
|
|
107
|
+
{allResults.length === 0 && (
|
|
108
|
+
<EmptyState className={styles.emptyState} variant="warning">
|
|
109
|
+
{translate('results.empty-state.no-results')}
|
|
110
|
+
</EmptyState>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
{allResults.length > 0 && filteredResults.length === 0 && (
|
|
114
|
+
<EmptyState className={styles.emptyState} variant="info">
|
|
115
|
+
{translate('results.empty-state.no-filtered-results')}
|
|
116
|
+
</EmptyState>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
{allResults.length > 0 && filteredResults.length > 0 && (
|
|
120
|
+
<div className={styles.listContainer}>
|
|
121
|
+
<FixedSizeList
|
|
122
|
+
className={classNames(styles.list, {
|
|
123
|
+
[styles.outdated]: isOutdated,
|
|
124
|
+
})}
|
|
125
|
+
direction={direction}
|
|
126
|
+
height={height}
|
|
127
|
+
itemCount={filteredResults.length}
|
|
128
|
+
itemData={itemData}
|
|
129
|
+
itemSize={RESULTS_ITEM_HEIGHT}
|
|
130
|
+
ref={setListRef}
|
|
131
|
+
width={width}
|
|
132
|
+
>
|
|
133
|
+
{Result}
|
|
134
|
+
</FixedSizeList>
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
</>
|
|
138
|
+
)}
|
|
139
|
+
</>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{hasResults && allResults.length > 0 && !isOutdated && <ResultsInput className={styles.input} />}
|
|
133
144
|
|
|
134
145
|
{isLoading && <Loading />}
|
|
135
146
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { ChangeEvent, FunctionComponent, useState } from 'react';
|
|
2
|
+
import { ChangeEvent, FormEventHandler, FunctionComponent, useState } from 'react';
|
|
3
3
|
import { useDispatch } from 'react-redux';
|
|
4
4
|
|
|
5
5
|
import { isRegExp } from 'lib';
|
|
@@ -27,8 +27,12 @@ const ResultsInput: FunctionComponent<Props> = ({ className }) => {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
const handleSubmit: FormEventHandler = (event) => {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
};
|
|
33
|
+
|
|
30
34
|
return (
|
|
31
|
-
<form className={classNames(styles.resultsInput, className)}>
|
|
35
|
+
<form className={classNames(styles.resultsInput, className)} onSubmit={handleSubmit}>
|
|
32
36
|
<input
|
|
33
37
|
className={styles.input}
|
|
34
38
|
placeholder={translate('results.input.placeholder')}
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
display: grid;
|
|
33
33
|
grid-template-columns: minmax(0, 1fr) var(--solver-column--width);
|
|
34
34
|
gap: var(--spacing);
|
|
35
|
+
height: 100%;
|
|
35
36
|
margin: 0 auto;
|
|
36
37
|
|
|
37
38
|
@include media('<l') {
|
|
@@ -62,17 +63,17 @@
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
.
|
|
66
|
-
flex:
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.dictionary {
|
|
66
|
+
.dictionaryContainer {
|
|
67
|
+
flex: 0 0 auto;
|
|
68
|
+
height: var(--dictionary--height);
|
|
71
69
|
display: flex;
|
|
72
70
|
flex-direction: column;
|
|
71
|
+
background-color: var(--color--background--element);
|
|
72
|
+
border: var(--border);
|
|
73
|
+
box-shadow: var(--box-shadow);
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
.
|
|
76
|
+
.dictionary {
|
|
76
77
|
flex: 1;
|
|
77
78
|
border-bottom: var(--border);
|
|
78
79
|
}
|
|
@@ -113,6 +114,7 @@
|
|
|
113
114
|
|
|
114
115
|
position: fixed;
|
|
115
116
|
bottom: var(--spacing);
|
|
117
|
+
z-index: 1;
|
|
116
118
|
|
|
117
119
|
@include media('<xs') {
|
|
118
120
|
--spacing: var(--spacing--m);
|
|
@@ -4,17 +4,8 @@ import { FunctionComponent, SyntheticEvent, useEffect, useMemo } from 'react';
|
|
|
4
4
|
import { useDispatch } from 'react-redux';
|
|
5
5
|
import { useMeasure } from 'react-use';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
BOARD_TILE_SIZE_MAX,
|
|
10
|
-
BOARD_TILE_SIZE_MIN,
|
|
11
|
-
BORDER_WIDTH,
|
|
12
|
-
COLUMN_MIN_HEIGHT,
|
|
13
|
-
COMPONENTS_SPACING,
|
|
14
|
-
COMPONENTS_SPACING_SMALL,
|
|
15
|
-
DICTIONARY_HEIGHT,
|
|
16
|
-
RACK_TILE_SIZE_MAX,
|
|
17
|
-
} from 'parameters';
|
|
7
|
+
import { useAppLayout, useIsTouchDevice } from 'hooks';
|
|
8
|
+
import { BOARD_TILE_SIZE_MAX, BOARD_TILE_SIZE_MIN, BORDER_WIDTH, RACK_TILE_SIZE_MAX } from 'parameters';
|
|
18
9
|
import {
|
|
19
10
|
resultsSlice,
|
|
20
11
|
selectAreResultsOutdated,
|
|
@@ -33,9 +24,8 @@ import Dictionary from '../Dictionary';
|
|
|
33
24
|
import DictionaryInput from '../DictionaryInput';
|
|
34
25
|
import Rack from '../Rack';
|
|
35
26
|
import Results from '../Results';
|
|
36
|
-
import Well from '../Well';
|
|
37
27
|
|
|
38
|
-
import { EmptyState,
|
|
28
|
+
import { EmptyState, FloatingSolveButton, ResultCandidatePicker } from './components';
|
|
39
29
|
import styles from './Solver.module.scss';
|
|
40
30
|
|
|
41
31
|
interface Props {
|
|
@@ -50,18 +40,12 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
50
40
|
const dispatch = useDispatch();
|
|
51
41
|
const translate = useTranslate();
|
|
52
42
|
const isTouchDevice = useIsTouchDevice();
|
|
43
|
+
const { componentsSpacing, isBoardFullWidth, showColumn, showCompactControls, showFloatingSolveButton } =
|
|
44
|
+
useAppLayout();
|
|
53
45
|
const [bottomContainerRef, { height: bottomContainerHeight }] = useMeasure<HTMLDivElement>();
|
|
54
|
-
const [
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const isLessThanM = useMediaQuery('<m');
|
|
58
|
-
const componentsSpacing = isLessThanXl ? COMPONENTS_SPACING_SMALL : COMPONENTS_SPACING;
|
|
59
|
-
const maxBoardWidth = width - resultsContainerWidth - (isLessThanL ? 0 : componentsSpacing) - 2 * componentsSpacing;
|
|
60
|
-
const maxBoardHeight = Math.max(
|
|
61
|
-
height - bottomContainerHeight,
|
|
62
|
-
isLessThanL ? 0 : COLUMN_MIN_HEIGHT,
|
|
63
|
-
isLessThanM ? Number.POSITIVE_INFINITY : 0,
|
|
64
|
-
);
|
|
46
|
+
const [columnRef, { width: columnWidth }] = useMeasure<HTMLDivElement>();
|
|
47
|
+
const maxBoardWidth = width - columnWidth - (showColumn ? componentsSpacing : 0) - 2 * componentsSpacing;
|
|
48
|
+
const maxBoardHeight = isBoardFullWidth ? Number.POSITIVE_INFINITY : Math.max(height - bottomContainerHeight, 0);
|
|
65
49
|
const config = useTypedSelector(selectConfig);
|
|
66
50
|
const error = useTypedSelector(selectSolveError);
|
|
67
51
|
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
@@ -74,8 +58,6 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
74
58
|
const cellSize = Math.min(cellWidth, cellHeight);
|
|
75
59
|
const cellSizeSafe = Math.min(Math.max(cellSize, BOARD_TILE_SIZE_MIN), BOARD_TILE_SIZE_MAX);
|
|
76
60
|
const tileSize = Math.min((maxBoardWidth - 2 * BORDER_WIDTH) / config.maximumCharactersCount, RACK_TILE_SIZE_MAX);
|
|
77
|
-
const boardSize = (cellSizeSafe + BORDER_WIDTH) * Math.max(config.boardWidth, config.boardHeight) + BORDER_WIDTH;
|
|
78
|
-
const resultsHeight = boardSize - DICTIONARY_HEIGHT - componentsSpacing - 4 * BORDER_WIDTH;
|
|
79
61
|
const maxControlsWidth = tileSize * config.maximumCharactersCount + 2 * BORDER_WIDTH;
|
|
80
62
|
const touchCallbacks = useMemo(
|
|
81
63
|
() => ({
|
|
@@ -116,53 +98,43 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
116
98
|
const handleSubmit = (event: SyntheticEvent) => {
|
|
117
99
|
event.preventDefault();
|
|
118
100
|
|
|
119
|
-
if (isLessThanL) {
|
|
120
|
-
onShowResults();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
101
|
dispatch(solveSlice.actions.submit());
|
|
124
102
|
};
|
|
125
103
|
|
|
126
104
|
useEffect(() => {
|
|
127
|
-
if (
|
|
105
|
+
if (showCompactControls && bestResult && !isOutdated) {
|
|
128
106
|
dispatch(resultsSlice.actions.changeResultCandidate(bestResult));
|
|
129
107
|
}
|
|
130
|
-
}, [bestResult, dispatch,
|
|
108
|
+
}, [bestResult, dispatch, showCompactControls, isOutdated]);
|
|
131
109
|
|
|
132
110
|
return (
|
|
133
111
|
<div className={classNames(styles.solver, className)}>
|
|
134
112
|
<div className={styles.container}>
|
|
135
113
|
<div className={styles.content}>
|
|
136
|
-
<form className={styles.boardContainer} onSubmit={handleSubmit}>
|
|
114
|
+
<form id="a" className={styles.boardContainer} onSubmit={handleSubmit}>
|
|
137
115
|
<Board cellSize={cellSizeSafe} className={styles.board} />
|
|
138
116
|
<input className={styles.submitInput} tabIndex={-1} type="submit" />
|
|
139
117
|
</form>
|
|
140
118
|
|
|
141
|
-
<div className={styles.column}>
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<Well>
|
|
149
|
-
<div className={styles.dictionary} style={{ height: DICTIONARY_HEIGHT }}>
|
|
150
|
-
<Dictionary className={styles.dictionaryOutput} />
|
|
151
|
-
<DictionaryInput className={styles.dictionaryInput} />
|
|
152
|
-
</div>
|
|
153
|
-
</Well>
|
|
119
|
+
<div className={styles.column} ref={columnRef}>
|
|
120
|
+
<Results callbacks={callbacks} />
|
|
121
|
+
|
|
122
|
+
<div className={styles.dictionaryContainer}>
|
|
123
|
+
<Dictionary className={styles.dictionary} />
|
|
124
|
+
<DictionaryInput className={styles.dictionaryInput} />
|
|
125
|
+
</div>
|
|
154
126
|
</div>
|
|
155
127
|
</div>
|
|
156
128
|
</div>
|
|
157
129
|
|
|
158
130
|
<div className={styles.bottomContainer} ref={bottomContainerRef}>
|
|
159
131
|
<div className={styles.bottomContent}>
|
|
160
|
-
<form onSubmit={handleSubmit}>
|
|
132
|
+
<form id="b" onSubmit={handleSubmit}>
|
|
161
133
|
<Rack className={styles.rack} tileSize={tileSize} />
|
|
162
134
|
<input className={styles.submitInput} tabIndex={-1} type="submit" />
|
|
163
135
|
</form>
|
|
164
136
|
|
|
165
|
-
{
|
|
137
|
+
{showCompactControls && (
|
|
166
138
|
<div className={styles.controls} style={{ maxWidth: maxControlsWidth }}>
|
|
167
139
|
<ResultCandidatePicker onResultClick={onShowResults} />
|
|
168
140
|
|
|
@@ -182,7 +154,7 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
182
154
|
</div>
|
|
183
155
|
</div>
|
|
184
156
|
|
|
185
|
-
{
|
|
157
|
+
{showFloatingSolveButton && <FloatingSolveButton className={styles.solve} onClick={handleSubmit} />}
|
|
186
158
|
</div>
|
|
187
159
|
);
|
|
188
160
|
};
|