@scrabble-solver/scrabble-solver 2.10.9 → 2.10.11
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 +7 -7
- 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/routes-manifest.json +1 -1
- package/.next/server/chunks/131.js +24 -17
- package/.next/server/chunks/176.js +164 -209
- package/.next/server/chunks/50.js +712 -1025
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/404.js.nft.json +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages/_app.js +0 -8
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/_document.js.nft.json +1 -1
- package/.next/server/pages/api/solve.js +0 -4
- package/.next/server/pages/index.html +1 -1
- package/.next/server/pages/index.js +19 -38
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/index.json +1 -1
- package/.next/static/chunks/pages/{404-d5ff00df1c687977.js → 404-6c1a6e3251710371.js} +1 -1
- package/.next/static/chunks/pages/_app-d98e480ff8c583de.js +28 -0
- package/.next/static/chunks/pages/index-bd1c7d3872c37456.js +1 -0
- package/.next/static/css/a9b55372a26cf77d.css +1 -0
- package/.next/static/css/b8954b85e2fa5b63.css +2 -0
- package/.next/static/msKI0ZURgJImoGBJvCBiF/_buildManifest.js +1 -0
- package/.next/trace +55 -55
- package/package.json +10 -11
- package/src/components/Board/components/Actions/Actions.tsx +6 -0
- package/src/components/Button/Button.module.scss +1 -1
- package/src/components/Dictionary/Dictionary.tsx +1 -1
- package/src/components/Modal/Modal.module.scss +3 -1
- package/src/components/Results/Results.module.scss +16 -6
- package/src/components/Results/Results.tsx +72 -61
- package/src/components/ResultsInput/ResultsInput.tsx +6 -2
- package/src/components/Solver/Solver.module.scss +8 -7
- package/src/components/Solver/Solver.tsx +10 -25
- package/src/components/Tile/Tile.tsx +6 -6
- package/src/components/index.ts +0 -1
- package/src/modals/ResultsModal/ResultsModal.module.scss +0 -1
- package/src/modals/ResultsModal/ResultsModal.tsx +4 -8
- package/src/parameters/index.ts +0 -6
- package/src/service-worker/routeSolveRequests.ts +4 -1
- package/.next/static/chunks/pages/_app-52cb288dc680bdfe.js +0 -28
- package/.next/static/chunks/pages/index-5c2544930e46c5ce.js +0 -1
- package/.next/static/css/ec4e47a6b1866fe5.css +0 -1
- package/.next/static/css/f65b7b2a74f57c1c.css +0 -2
- package/.next/static/fZRsz4P0gQ8Wgb9jP8eap/_buildManifest.js +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/static/{fZRsz4P0gQ8Wgb9jP8eap → msKI0ZURgJImoGBJvCBiF}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.11",
|
|
4
4
|
"description": "Scrabble Solver 2 - App",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"homepage": "https://github.com/kamilmielnik/scrabble-solver#readme",
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "env-cmd next build",
|
|
24
|
-
"clean": "rimraf .next/ node_modules/
|
|
24
|
+
"clean": "rimraf .next/ node_modules/",
|
|
25
25
|
"clean:force": "npm run clean && rimraf package-lock.json",
|
|
26
26
|
"debug": "NODE_OPTIONS='--inspect' next dev",
|
|
27
27
|
"dev": "env-cmd next dev",
|
|
@@ -32,17 +32,16 @@
|
|
|
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.10.
|
|
36
|
-
"@scrabble-solver/constants": "^2.10.
|
|
37
|
-
"@scrabble-solver/dictionaries": "^2.10.
|
|
38
|
-
"@scrabble-solver/logger": "^2.10.
|
|
39
|
-
"@scrabble-solver/solver": "^2.10.
|
|
40
|
-
"@scrabble-solver/types": "^2.10.
|
|
41
|
-
"@scrabble-solver/word-definitions": "^2.10.
|
|
35
|
+
"@scrabble-solver/configs": "^2.10.11",
|
|
36
|
+
"@scrabble-solver/constants": "^2.10.11",
|
|
37
|
+
"@scrabble-solver/dictionaries": "^2.10.11",
|
|
38
|
+
"@scrabble-solver/logger": "^2.10.11",
|
|
39
|
+
"@scrabble-solver/solver": "^2.10.11",
|
|
40
|
+
"@scrabble-solver/types": "^2.10.11",
|
|
41
|
+
"@scrabble-solver/word-definitions": "^2.10.11",
|
|
42
42
|
"classnames": "^2.3.2",
|
|
43
43
|
"include-media": "^2.0.0",
|
|
44
44
|
"include-media-query-builder": "^1.1.0",
|
|
45
|
-
"merge-refs": "^1.1.2",
|
|
46
45
|
"next": "^13.2.1",
|
|
47
46
|
"normalize.css": "^8.0.1",
|
|
48
47
|
"react": "^18.2.0",
|
|
@@ -78,5 +77,5 @@
|
|
|
78
77
|
"sass": "^1.58.3",
|
|
79
78
|
"workbox-webpack-plugin": "^6.5.4"
|
|
80
79
|
},
|
|
81
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "b070bc7baa92d64a480fd457ad8cc757af51efc0"
|
|
82
81
|
}
|
|
@@ -25,6 +25,9 @@ const Actions = forwardRef<HTMLDivElement, Props>(
|
|
|
25
25
|
const isBlank = cell.tile.isBlank;
|
|
26
26
|
const isEmpty = cell.tile.character === EMPTY_CELL;
|
|
27
27
|
|
|
28
|
+
// On iOS it helps with losing focus too early which makes Actions disappear
|
|
29
|
+
const handleMouseDown: MouseEventHandler = (event) => event.preventDefault();
|
|
30
|
+
|
|
28
31
|
return (
|
|
29
32
|
<div className={classNames(styles.actions, className)} ref={ref} {...props}>
|
|
30
33
|
<Button
|
|
@@ -37,6 +40,7 @@ const Actions = forwardRef<HTMLDivElement, Props>(
|
|
|
37
40
|
tabIndex={disabled ? -1 : undefined}
|
|
38
41
|
tooltip={translate('cell.toggle-direction')}
|
|
39
42
|
onClick={onDirectionToggle}
|
|
43
|
+
onMouseDown={handleMouseDown}
|
|
40
44
|
/>
|
|
41
45
|
|
|
42
46
|
{isEmpty && (
|
|
@@ -47,6 +51,7 @@ const Actions = forwardRef<HTMLDivElement, Props>(
|
|
|
47
51
|
tabIndex={disabled ? -1 : undefined}
|
|
48
52
|
tooltip={translate('cell.filter-cell')}
|
|
49
53
|
onClick={onToggleFilterCell}
|
|
54
|
+
onMouseDown={handleMouseDown}
|
|
50
55
|
/>
|
|
51
56
|
)}
|
|
52
57
|
|
|
@@ -58,6 +63,7 @@ const Actions = forwardRef<HTMLDivElement, Props>(
|
|
|
58
63
|
tabIndex={disabled ? -1 : undefined}
|
|
59
64
|
tooltip={isBlank ? translate('cell.set-not-blank') : translate('cell.set-blank')}
|
|
60
65
|
onClick={onToggleBlank}
|
|
66
|
+
onMouseDown={handleMouseDown}
|
|
61
67
|
/>
|
|
62
68
|
)}
|
|
63
69
|
</div>
|
|
@@ -27,7 +27,7 @@ const Dictionary: FunctionComponent<Props> = ({ className }) => {
|
|
|
27
27
|
>
|
|
28
28
|
{typeof error !== 'undefined' && <EmptyState variant="error">{error.message}</EmptyState>}
|
|
29
29
|
|
|
30
|
-
{results.length === 0 && (
|
|
30
|
+
{typeof error === 'undefined' && results.length === 0 && (
|
|
31
31
|
<EmptyState variant="info">{translate('dictionary.empty-state.uninitialized')}</EmptyState>
|
|
32
32
|
)}
|
|
33
33
|
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
.modal {
|
|
17
17
|
position: fixed;
|
|
18
|
+
bottom: 0;
|
|
18
19
|
z-index: var(--z-index--modal);
|
|
19
|
-
height: 100vh;
|
|
20
20
|
width: var(--modal--width);
|
|
21
21
|
padding: 0;
|
|
22
22
|
border-radius: 0;
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
background-color: var(--color--background);
|
|
70
70
|
transition: var(--transition--long);
|
|
71
71
|
transform: translateX(var(--modal--width));
|
|
72
|
+
overflow: hidden;
|
|
72
73
|
opacity: 0;
|
|
73
74
|
|
|
74
75
|
[dir='ltr'] & {
|
|
@@ -113,6 +114,7 @@
|
|
|
113
114
|
.content {
|
|
114
115
|
position: relative;
|
|
115
116
|
flex: 1;
|
|
117
|
+
min-height: 0;
|
|
116
118
|
margin-top: calc(-1 * var(--spacing--l));
|
|
117
119
|
padding: var(--spacing--l);
|
|
118
120
|
overflow: auto;
|
|
@@ -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,20 @@ $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
|
+
border-top: var(--border);
|
|
25
|
+
border-bottom: var(--border);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.listContainer {
|
|
29
|
+
position: absolute;
|
|
30
|
+
top: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
17
33
|
.list {
|
|
18
|
-
flex: 1 1 auto;
|
|
19
34
|
transition: var(--transition);
|
|
20
35
|
|
|
21
36
|
&.outdated {
|
|
@@ -29,7 +44,6 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
29
44
|
display: flex;
|
|
30
45
|
align-items: center;
|
|
31
46
|
justify-content: space-between;
|
|
32
|
-
border-bottom: var(--border);
|
|
33
47
|
font-weight: 700;
|
|
34
48
|
}
|
|
35
49
|
|
|
@@ -159,7 +173,3 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
159
173
|
width: $size;
|
|
160
174
|
height: $size;
|
|
161
175
|
}
|
|
162
|
-
|
|
163
|
-
.input {
|
|
164
|
-
border-top: var(--border);
|
|
165
|
-
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { FunctionComponent, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { 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,10 @@ import useColumns from './useColumns';
|
|
|
28
30
|
|
|
29
31
|
interface Props {
|
|
30
32
|
callbacks: ResultCallbacks;
|
|
31
|
-
height: number;
|
|
32
33
|
highlightedIndex?: number;
|
|
33
|
-
width: number;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const Results: FunctionComponent<Props> = ({ callbacks,
|
|
36
|
+
const Results: FunctionComponent<Props> = ({ callbacks, highlightedIndex }) => {
|
|
37
37
|
const translate = useTranslate();
|
|
38
38
|
const locale = useTypedSelector(selectLocale);
|
|
39
39
|
const { direction } = LOCALE_FEATURES[locale];
|
|
@@ -44,6 +44,7 @@ const Results: FunctionComponent<Props> = ({ callbacks, height, highlightedIndex
|
|
|
44
44
|
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
45
45
|
const error = useTypedSelector(selectSolveError);
|
|
46
46
|
const itemData = useMemo(() => ({ ...callbacks, highlightedIndex, results }), [callbacks, highlightedIndex, results]);
|
|
47
|
+
const [sizerRef, { height, width }] = useMeasure<HTMLDivElement>();
|
|
47
48
|
const [listRef, setListRef] = useState<FixedSizeList<ResultData> | null>(null);
|
|
48
49
|
const columns = useColumns();
|
|
49
50
|
const scrollToIndex = typeof highlightedIndex === 'number' ? highlightedIndex : 0;
|
|
@@ -70,65 +71,75 @@ const Results: FunctionComponent<Props> = ({ callbacks, height, highlightedIndex
|
|
|
70
71
|
))}
|
|
71
72
|
</div>
|
|
72
73
|
|
|
73
|
-
{
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
<div className={styles.content}>
|
|
75
|
+
<Sizer ref={sizerRef} />
|
|
76
|
+
|
|
77
|
+
{typeof error !== 'undefined' && (
|
|
78
|
+
<EmptyState className={styles.emptyState} variant="error">
|
|
79
|
+
{error.message}
|
|
80
|
+
</EmptyState>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{typeof error === 'undefined' && typeof filteredResults === 'undefined' && (
|
|
84
|
+
<EmptyState className={styles.emptyState} variant="info">
|
|
85
|
+
{translate('results.empty-state.uninitialized')}
|
|
86
|
+
|
|
87
|
+
<SolveButton className={styles.solveButton} />
|
|
88
|
+
</EmptyState>
|
|
89
|
+
)}
|
|
90
|
+
|
|
91
|
+
{typeof error === 'undefined' &&
|
|
92
|
+
typeof filteredResults !== 'undefined' &&
|
|
93
|
+
typeof allResults !== 'undefined' && (
|
|
94
|
+
<>
|
|
95
|
+
{isOutdated && (
|
|
96
|
+
<EmptyState className={styles.emptyState} variant="info">
|
|
97
|
+
{translate('results.empty-state.outdated')}
|
|
98
|
+
|
|
99
|
+
<SolveButton className={styles.solveButton} />
|
|
100
|
+
</EmptyState>
|
|
101
|
+
)}
|
|
102
|
+
|
|
103
|
+
{!isOutdated && (
|
|
104
|
+
<>
|
|
105
|
+
{allResults.length === 0 && (
|
|
106
|
+
<EmptyState className={styles.emptyState} variant="warning">
|
|
107
|
+
{translate('results.empty-state.no-results')}
|
|
108
|
+
</EmptyState>
|
|
109
|
+
)}
|
|
110
|
+
|
|
111
|
+
{allResults.length > 0 && filteredResults.length === 0 && (
|
|
112
|
+
<EmptyState className={styles.emptyState} variant="info">
|
|
113
|
+
{translate('results.empty-state.no-filtered-results')}
|
|
114
|
+
</EmptyState>
|
|
115
|
+
)}
|
|
116
|
+
|
|
117
|
+
{allResults.length > 0 && filteredResults.length > 0 && (
|
|
118
|
+
<div className={styles.listContainer}>
|
|
119
|
+
<FixedSizeList
|
|
120
|
+
className={classNames(styles.list, {
|
|
121
|
+
[styles.outdated]: isOutdated,
|
|
122
|
+
})}
|
|
123
|
+
direction={direction}
|
|
124
|
+
height={height}
|
|
125
|
+
itemCount={filteredResults.length}
|
|
126
|
+
itemData={itemData}
|
|
127
|
+
itemSize={RESULTS_ITEM_HEIGHT}
|
|
128
|
+
ref={setListRef}
|
|
129
|
+
width={width}
|
|
130
|
+
>
|
|
131
|
+
{Result}
|
|
132
|
+
</FixedSizeList>
|
|
133
|
+
</div>
|
|
134
|
+
)}
|
|
135
|
+
</>
|
|
136
|
+
)}
|
|
137
|
+
</>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
86
140
|
|
|
87
141
|
{typeof error === 'undefined' && typeof filteredResults !== 'undefined' && typeof allResults !== 'undefined' && (
|
|
88
|
-
<>
|
|
89
|
-
{isOutdated && (
|
|
90
|
-
<EmptyState className={styles.emptyState} variant="info">
|
|
91
|
-
{translate('results.empty-state.outdated')}
|
|
92
|
-
|
|
93
|
-
<SolveButton className={styles.solveButton} />
|
|
94
|
-
</EmptyState>
|
|
95
|
-
)}
|
|
96
|
-
|
|
97
|
-
{!isOutdated && (
|
|
98
|
-
<>
|
|
99
|
-
{allResults.length === 0 && (
|
|
100
|
-
<EmptyState className={styles.emptyState} variant="warning">
|
|
101
|
-
{translate('results.empty-state.no-results')}
|
|
102
|
-
</EmptyState>
|
|
103
|
-
)}
|
|
104
|
-
|
|
105
|
-
{allResults.length > 0 && filteredResults.length === 0 && (
|
|
106
|
-
<EmptyState className={styles.emptyState} variant="info">
|
|
107
|
-
{translate('results.empty-state.no-filtered-results')}
|
|
108
|
-
</EmptyState>
|
|
109
|
-
)}
|
|
110
|
-
|
|
111
|
-
{allResults.length > 0 && filteredResults.length > 0 && (
|
|
112
|
-
<FixedSizeList
|
|
113
|
-
className={classNames(styles.list, {
|
|
114
|
-
[styles.outdated]: isOutdated,
|
|
115
|
-
})}
|
|
116
|
-
direction={direction}
|
|
117
|
-
height={height - RESULTS_HEADER_HEIGHT - RESULTS_INPUT_HEIGHT - 2 * BORDER_WIDTH}
|
|
118
|
-
itemCount={filteredResults.length}
|
|
119
|
-
itemData={itemData}
|
|
120
|
-
itemSize={RESULTS_ITEM_HEIGHT}
|
|
121
|
-
ref={setListRef}
|
|
122
|
-
width={width}
|
|
123
|
-
>
|
|
124
|
-
{Result}
|
|
125
|
-
</FixedSizeList>
|
|
126
|
-
)}
|
|
127
|
-
|
|
128
|
-
{allResults.length > 0 && <ResultsInput className={styles.input} />}
|
|
129
|
-
</>
|
|
130
|
-
)}
|
|
131
|
-
</>
|
|
142
|
+
<>{allResults.length > 0 && !isOutdated && <ResultsInput />}</>
|
|
132
143
|
)}
|
|
133
144
|
|
|
134
145
|
{isLoading && <Loading />}
|
|
@@ -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
|
}
|
|
@@ -9,10 +9,8 @@ import {
|
|
|
9
9
|
BOARD_TILE_SIZE_MAX,
|
|
10
10
|
BOARD_TILE_SIZE_MIN,
|
|
11
11
|
BORDER_WIDTH,
|
|
12
|
-
COLUMN_MIN_HEIGHT,
|
|
13
12
|
COMPONENTS_SPACING,
|
|
14
13
|
COMPONENTS_SPACING_SMALL,
|
|
15
|
-
DICTIONARY_HEIGHT,
|
|
16
14
|
RACK_TILE_SIZE_MAX,
|
|
17
15
|
} from 'parameters';
|
|
18
16
|
import {
|
|
@@ -33,7 +31,6 @@ import Dictionary from '../Dictionary';
|
|
|
33
31
|
import DictionaryInput from '../DictionaryInput';
|
|
34
32
|
import Rack from '../Rack';
|
|
35
33
|
import Results from '../Results';
|
|
36
|
-
import Well from '../Well';
|
|
37
34
|
|
|
38
35
|
import { EmptyState, ResultCandidatePicker, SolveButton } from './components';
|
|
39
36
|
import styles from './Solver.module.scss';
|
|
@@ -51,17 +48,13 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
51
48
|
const translate = useTranslate();
|
|
52
49
|
const isTouchDevice = useIsTouchDevice();
|
|
53
50
|
const [bottomContainerRef, { height: bottomContainerHeight }] = useMeasure<HTMLDivElement>();
|
|
54
|
-
const [
|
|
51
|
+
const [columnRef, { width: columnWidth }] = useMeasure<HTMLDivElement>();
|
|
55
52
|
const isLessThanXl = useMediaQuery('<xl');
|
|
56
53
|
const isLessThanL = useMediaQuery('<l');
|
|
57
54
|
const isLessThanM = useMediaQuery('<m');
|
|
58
55
|
const componentsSpacing = isLessThanXl ? COMPONENTS_SPACING_SMALL : COMPONENTS_SPACING;
|
|
59
|
-
const maxBoardWidth = width -
|
|
60
|
-
const maxBoardHeight = Math.max(
|
|
61
|
-
height - bottomContainerHeight,
|
|
62
|
-
isLessThanL ? 0 : COLUMN_MIN_HEIGHT,
|
|
63
|
-
isLessThanM ? Number.POSITIVE_INFINITY : 0,
|
|
64
|
-
);
|
|
56
|
+
const maxBoardWidth = width - columnWidth - (isLessThanL ? 0 : componentsSpacing) - 2 * componentsSpacing;
|
|
57
|
+
const maxBoardHeight = Math.max(height - bottomContainerHeight, isLessThanM ? Number.POSITIVE_INFINITY : 0);
|
|
65
58
|
const config = useTypedSelector(selectConfig);
|
|
66
59
|
const error = useTypedSelector(selectSolveError);
|
|
67
60
|
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
@@ -74,8 +67,6 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
74
67
|
const cellSize = Math.min(cellWidth, cellHeight);
|
|
75
68
|
const cellSizeSafe = Math.min(Math.max(cellSize, BOARD_TILE_SIZE_MIN), BOARD_TILE_SIZE_MAX);
|
|
76
69
|
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
70
|
const maxControlsWidth = tileSize * config.maximumCharactersCount + 2 * BORDER_WIDTH;
|
|
80
71
|
const touchCallbacks = useMemo(
|
|
81
72
|
() => ({
|
|
@@ -138,19 +129,13 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
138
129
|
<input className={styles.submitInput} tabIndex={-1} type="submit" />
|
|
139
130
|
</form>
|
|
140
131
|
|
|
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>
|
|
132
|
+
<div className={styles.column} ref={columnRef}>
|
|
133
|
+
<Results callbacks={callbacks} />
|
|
134
|
+
|
|
135
|
+
<div className={styles.dictionaryContainer}>
|
|
136
|
+
<Dictionary className={styles.dictionary} />
|
|
137
|
+
<DictionaryInput className={styles.dictionaryInput} />
|
|
138
|
+
</div>
|
|
154
139
|
</div>
|
|
155
140
|
</div>
|
|
156
141
|
</div>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { useMergeRefs } from '@floating-ui/react';
|
|
1
2
|
import { EMPTY_CELL } from '@scrabble-solver/constants';
|
|
2
|
-
import mergeRefs from 'merge-refs';
|
|
3
3
|
import {
|
|
4
4
|
ChangeEventHandler,
|
|
5
5
|
FocusEventHandler,
|
|
6
6
|
FunctionComponent,
|
|
7
7
|
KeyboardEventHandler,
|
|
8
|
-
|
|
8
|
+
Ref,
|
|
9
9
|
useEffect,
|
|
10
10
|
useMemo,
|
|
11
11
|
useRef,
|
|
@@ -25,7 +25,7 @@ interface Props {
|
|
|
25
25
|
className?: string;
|
|
26
26
|
disabled?: boolean;
|
|
27
27
|
highlighted?: boolean;
|
|
28
|
-
inputRef?:
|
|
28
|
+
inputRef?: Ref<HTMLInputElement>;
|
|
29
29
|
isBlank?: boolean;
|
|
30
30
|
isValid?: boolean;
|
|
31
31
|
placeholder?: string;
|
|
@@ -63,7 +63,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
63
63
|
const characterStyle = useMemo(() => ({ fontSize: tileFontSize }), [tileFontSize]);
|
|
64
64
|
const pointsStyle = useMemo(() => ({ fontSize: pointsFontSize }), [pointsFontSize]);
|
|
65
65
|
const ref = useRef<HTMLInputElement>(null);
|
|
66
|
-
const mergedRef = inputRef ?
|
|
66
|
+
const mergedRef = useMergeRefs(inputRef ? [ref, inputRef] : [ref]);
|
|
67
67
|
const isEmpty = !character || character === EMPTY_CELL;
|
|
68
68
|
const isLessThanXs = useMediaQuery('<xs');
|
|
69
69
|
const canShowPoints = (isBlank || !isEmpty) && typeof points !== 'undefined' && !isLessThanXs;
|
|
@@ -81,7 +81,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
81
81
|
}, [autoFocus, ref]);
|
|
82
82
|
|
|
83
83
|
useEffect(() => {
|
|
84
|
-
if (!ref.current?.parentElement || !character) {
|
|
84
|
+
if (!ref.current?.parentElement || !character || isLessThanXs) {
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -90,7 +90,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
90
90
|
easing: EASE_OUT_CUBIC,
|
|
91
91
|
fill: 'forwards',
|
|
92
92
|
});
|
|
93
|
-
}, [character]);
|
|
93
|
+
}, [character, isLessThanXs]);
|
|
94
94
|
|
|
95
95
|
return (
|
|
96
96
|
<TilePure
|
package/src/components/index.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Result } from '@scrabble-solver/types';
|
|
2
2
|
import { FunctionComponent, useMemo } from 'react';
|
|
3
3
|
import { useDispatch } from 'react-redux';
|
|
4
|
-
import { useMeasure } from 'react-use';
|
|
5
4
|
|
|
6
|
-
import { Modal, Results
|
|
5
|
+
import { Modal, Results } from 'components';
|
|
7
6
|
import { useMediaQuery } from 'hooks';
|
|
8
7
|
import {
|
|
9
8
|
resultsSlice,
|
|
@@ -24,7 +23,6 @@ interface Props {
|
|
|
24
23
|
const ResultsModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) => {
|
|
25
24
|
const dispatch = useDispatch();
|
|
26
25
|
const translate = useTranslate();
|
|
27
|
-
const [sizerRef, { height, width }] = useMeasure<HTMLDivElement>();
|
|
28
26
|
const isLessThanS = useMediaQuery('<s');
|
|
29
27
|
const results = useTypedSelector(selectSortedFilteredResults);
|
|
30
28
|
const resultCandidate = useTypedSelector(selectResultCandidate);
|
|
@@ -46,11 +44,9 @@ const ResultsModal: FunctionComponent<Props> = ({ className, isOpen, onClose })
|
|
|
46
44
|
|
|
47
45
|
return (
|
|
48
46
|
<Modal className={className} isOpen={isOpen} title={translate('results')} onClose={onClose}>
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
<Results callbacks={callbacks} height={height} highlightedIndex={highlightedIndex} width={width} />
|
|
53
|
-
</Well>
|
|
47
|
+
<div className={styles.content}>
|
|
48
|
+
<Results callbacks={callbacks} highlightedIndex={highlightedIndex} />
|
|
49
|
+
</div>
|
|
54
50
|
</Modal>
|
|
55
51
|
);
|
|
56
52
|
};
|
package/src/parameters/index.ts
CHANGED
|
@@ -38,10 +38,6 @@ export const BOARD_TILE_SIZE_MIN = 20;
|
|
|
38
38
|
|
|
39
39
|
export const BORDER_WIDTH = 1;
|
|
40
40
|
|
|
41
|
-
export const COLUMN_MIN_HEIGHT = 588.5;
|
|
42
|
-
|
|
43
|
-
export const DICTIONARY_HEIGHT = 260;
|
|
44
|
-
|
|
45
41
|
export const TILE_SIZE = 80;
|
|
46
42
|
|
|
47
43
|
export const PLAIN_TILES_COLOR_DEFAULT = COLOR_GREEN;
|
|
@@ -73,9 +69,7 @@ export const RACK_TILE_SIZE_MAX = 80;
|
|
|
73
69
|
|
|
74
70
|
export const REMAINING_TILES_TILE_SIZE = 50;
|
|
75
71
|
|
|
76
|
-
export const RESULTS_HEADER_HEIGHT = 34;
|
|
77
72
|
export const RESULTS_ITEM_HEIGHT = 40;
|
|
78
|
-
export const RESULTS_INPUT_HEIGHT = 40;
|
|
79
73
|
|
|
80
74
|
export const TILE_APPEAR_DURATION = 200;
|
|
81
75
|
|
|
@@ -23,7 +23,10 @@ const isSlowDevice = (): boolean | undefined => {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const count = Math.min(localDurations.length, serverDurations.length);
|
|
26
|
-
|
|
26
|
+
const local = localDurations.slice(0, count).sort().slice(1, -1);
|
|
27
|
+
const server = serverDurations.slice(0, count).sort().slice(1, -1);
|
|
28
|
+
|
|
29
|
+
return average(local) > average(server);
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
const routeSolveRequests = () => {
|