@scrabble-solver/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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +12 -12
- 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-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-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- 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/{176.js → 277.js} +860 -767
- package/.next/server/chunks/{290.js → 417.js} +3 -3
- package/.next/server/chunks/50.js +371 -343
- package/.next/server/chunks/664.js +15 -15
- package/.next/server/chunks/859.js +17 -10
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/404.js.nft.json +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages/_app.js +4 -12
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/_document.js +2 -2
- package/.next/server/pages/_document.js.nft.json +1 -1
- package/.next/server/pages/_error.js +4 -4
- package/.next/server/pages/api/dictionary/[locale]/[word].js +4 -4
- package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale].js +3 -3
- package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
- package/.next/server/pages/api/solve.js +9 -14
- package/.next/server/pages/api/solve.js.nft.json +1 -1
- package/.next/server/pages/api/verify.js +3 -3
- 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 +1 -1
- package/.next/server/pages/index.js +250 -175
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/index.json +1 -1
- package/.next/server/pages-manifest.json +1 -1
- package/.next/static/45ye7793DY705HOcuK9lJ/_buildManifest.js +1 -0
- package/.next/static/chunks/main-0ecb9ccfcb6c9b24.js +1 -0
- package/.next/static/chunks/pages/404-e0f30450e9920dc3.js +1 -0
- package/.next/static/chunks/pages/_app-d7acee5e526752d9.js +28 -0
- package/.next/static/chunks/pages/{_error-8353112a01355ec2.js → _error-54de1933a164a1ff.js} +1 -1
- package/.next/static/chunks/pages/index-35d2c1c79a201ae2.js +1 -0
- package/.next/static/css/a48caa6f57de6e98.css +1 -0
- package/.next/static/css/c49bbe944ddd1b39.css +2 -0
- package/.next/trace +55 -55
- package/package.json +12 -13
- package/src/components/Board/components/Cell/Cell.module.scss +1 -0
- package/src/components/Board/hooks/useGrid.ts +1 -1
- package/src/components/Dictionary/Dictionary.module.scss +13 -8
- package/src/components/Dictionary/Dictionary.tsx +5 -5
- package/src/components/Loading/Loading.tsx +1 -1
- package/src/components/Modal/Modal.module.scss +0 -1
- package/src/components/NavButtons/NavButtons.tsx +4 -5
- package/src/components/Results/Results.module.scss +15 -2
- package/src/components/Results/Results.tsx +78 -67
- package/src/components/ResultsInput/ResultsInput.tsx +6 -2
- package/src/components/Solver/Solver.module.scss +9 -7
- package/src/components/Solver/Solver.tsx +21 -49
- package/src/components/Solver/components/FloatingSolveButton/FloatingSolveButton.module.scss +7 -0
- package/src/components/Solver/components/{SolveButton/SolveButton.tsx → FloatingSolveButton/FloatingSolveButton.tsx} +6 -6
- package/src/components/Solver/components/FloatingSolveButton/index.ts +1 -0
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss +19 -4
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +13 -1
- package/src/components/Solver/components/index.ts +1 -1
- package/src/components/Spinner/Spinner.module.scss +11 -0
- package/src/components/Spinner/Spinner.tsx +19 -0
- package/src/components/Spinner/index.ts +1 -0
- package/src/components/Tile/Tile.tsx +9 -9
- package/src/components/index.ts +1 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAppLayout.ts +29 -0
- package/src/i18n/de.json +1 -0
- package/src/i18n/en.json +1 -0
- package/src/i18n/es.json +1 -0
- package/src/i18n/fa.json +1 -0
- package/src/i18n/fr.json +1 -0
- package/src/i18n/index.ts +1 -1
- package/src/i18n/pl.json +1 -0
- package/src/modals/DictionaryModal/DictionaryModal.module.scss +18 -0
- package/src/modals/DictionaryModal/DictionaryModal.tsx +27 -0
- package/src/modals/DictionaryModal/index.ts +1 -0
- package/src/modals/MenuModal/MenuModal.tsx +7 -1
- package/src/modals/ResultsModal/ResultsModal.module.scss +18 -1
- package/src/modals/ResultsModal/ResultsModal.tsx +10 -13
- package/src/modals/index.ts +1 -0
- package/src/pages/api/solve.ts +9 -10
- package/src/pages/index.tsx +20 -15
- package/src/parameters/index.ts +0 -6
- package/src/service-worker/routeSolveRequests.ts +4 -1
- package/src/state/localStorage.ts +0 -9
- package/src/styles/animations.scss +10 -0
- package/src/styles/global.scss +1 -1
- package/src/styles/variables.scss +1 -1
- package/src/types/index.ts +1 -0
- package/.next/server/font-loader-manifest.js +0 -1
- package/.next/static/chunks/main-74c4d6b2b5c362f3.js +0 -1
- package/.next/static/chunks/pages/404-d5ff00df1c687977.js +0 -1
- package/.next/static/chunks/pages/_app-3272e798504c40d8.js +0 -28
- package/.next/static/chunks/pages/index-5c2544930e46c5ce.js +0 -1
- package/.next/static/css/336e75db2b74b157.css +0 -2
- package/.next/static/css/ec4e47a6b1866fe5.css +0 -1
- package/.next/static/warzWo25tDxo_Eiv9T6f2/_buildManifest.js +0 -1
- package/src/components/Solver/components/SolveButton/SolveButton.module.scss +0 -4
- package/src/components/Solver/components/SolveButton/index.ts +0 -1
- package/src/components/Well/Well.module.scss +0 -6
- package/src/components/Well/Well.tsx +0 -17
- package/src/components/Well/index.ts +0 -1
- /package/.next/server/{font-loader-manifest.json → next-font-manifest.json} +0 -0
- /package/.next/static/{warzWo25tDxo_Eiv9T6f2 → 45ye7793DY705HOcuK9lJ}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -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,8 +74,8 @@
|
|
|
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
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "493dd931e7f16c34c425295a0f048756d6c192c8"
|
|
82
81
|
}
|
|
@@ -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
|
};
|