@scrabble-solver/scrabble-solver 2.10.5 → 2.10.7
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 +10 -10
- 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/176.js +551 -447
- package/.next/server/chunks/290.js +3 -1
- package/.next/server/chunks/579.js +24 -16
- 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 +2 -2
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale]/[word].js +18 -16
- package/.next/server/pages/index.html +2 -2
- package/.next/server/pages/index.js +18 -11
- 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/6RggBFm8kHrh-k1-CG3um/_buildManifest.js +1 -0
- package/.next/static/chunks/509-6ad4482d4351452c.js +1 -0
- package/.next/static/chunks/pages/{404-7619583a9e7188b1.js → 404-67383848027ec49b.js} +1 -1
- package/.next/static/chunks/pages/_app-c58cfa832b76cc87.js +24 -0
- package/.next/static/chunks/pages/index-146039f501e49c08.js +1 -0
- package/.next/static/css/4482c4a0064d3807.css +1 -0
- package/.next/static/css/9d1013ec684361b9.css +1 -0
- package/.next/trace +55 -55
- package/package.json +14 -14
- package/src/components/Board/components/Cell/Button.tsx +1 -0
- package/src/components/Board/components/Cell/Cell.tsx +6 -0
- package/src/components/Board/components/Cell/CellPure.tsx +10 -1
- package/src/components/Button/Button.module.scss +2 -5
- package/src/components/Button/Button.tsx +1 -0
- package/src/components/Button/Link.tsx +1 -0
- package/src/components/{SquareButton/SquareButton.module.scss → IconButton/IconButton.module.scss} +2 -3
- package/src/components/{SquareButton/SquareButton.tsx → IconButton/IconButton.tsx} +7 -6
- package/src/components/{SquareButton → IconButton}/Link.tsx +3 -2
- package/src/components/IconButton/index.ts +1 -0
- package/src/components/Logo/Logo.svg +44 -15
- package/src/components/Modal/Modal.tsx +3 -2
- package/src/components/NavButtons/NavButtons.tsx +37 -9
- package/src/components/Rack/RackTile.tsx +5 -0
- package/src/components/Results/HeaderButton.tsx +1 -0
- package/src/components/Results/Result.tsx +1 -0
- package/src/components/Results/Results.module.scss +0 -1
- package/src/components/Results/SolveButton.tsx +1 -0
- package/src/components/Solver/Solver.module.scss +11 -19
- package/src/components/Solver/Solver.tsx +18 -36
- package/src/components/Solver/components/EmptyState/EmptyState.module.scss +0 -2
- package/src/components/Solver/components/InsertButton/InsertButton.module.scss +15 -0
- package/src/components/Solver/components/{ApplyButton/ApplyButton.tsx → InsertButton/InsertButton.tsx} +10 -6
- package/src/components/Solver/components/InsertButton/index.ts +1 -0
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss +143 -0
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +98 -0
- package/src/components/Solver/components/SolveButton/SolveButton.tsx +11 -1
- package/src/components/Solver/components/index.ts +2 -1
- package/src/components/Tile/Tile.tsx +3 -0
- package/src/components/Tile/TilePure.tsx +3 -0
- package/src/components/Tooltip/useTooltip.tsx +14 -2
- package/src/components/index.ts +1 -2
- package/src/i18n/de.json +5 -0
- package/src/i18n/en.json +5 -0
- package/src/i18n/es.json +5 -0
- package/src/i18n/fa.json +5 -0
- package/src/i18n/fr.json +5 -0
- package/src/i18n/pl.json +5 -0
- package/src/icons/ArrowDown.svg +1 -1
- package/src/icons/ArrowLeft.svg +1 -1
- package/src/icons/ArrowRight.svg +1 -1
- package/src/icons/ArrowUp.svg +1 -1
- package/src/icons/ChevronDown.svg +1 -1
- package/src/icons/ChevronLeft.svg +4 -0
- package/src/icons/ChevronRight.svg +4 -0
- package/src/icons/List.svg +1 -1
- package/src/icons/index.ts +2 -0
- package/src/modals/MenuModal/MenuModal.module.scss +2 -3
- package/src/modals/MenuModal/MenuModal.tsx +9 -3
- package/src/modals/RemainingTilesModal/components/Character/Character.tsx +1 -0
- package/src/modals/SettingsModal/components/AutoGroupTilesSetting/AutoGroupTilesSetting.tsx +2 -2
- package/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.tsx +2 -2
- package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.tsx +2 -2
- package/src/pages/api/dictionary/[locale]/[word].ts +3 -1
- package/src/pages/index.tsx +10 -6
- package/src/state/sagas.ts +5 -2
- package/src/state/useTranslate.ts +5 -2
- package/src/styles/mixins.scss +6 -0
- package/src/types/index.ts +6 -1
- package/.next/static/WILX-RgqlLTd4ZoPs8C_E/_buildManifest.js +0 -1
- package/.next/static/chunks/144-6768fe9a92865ec8.js +0 -1
- package/.next/static/chunks/pages/_app-fa0661b072fc6af9.js +0 -24
- package/.next/static/chunks/pages/index-1a6bbb880f28606a.js +0 -1
- package/.next/static/css/d80ffccf2315791a.css +0 -1
- package/.next/static/css/e737d5d7fbed2434.css +0 -1
- package/src/components/ResultCandidatePicker/ResultCandidatePicker.module.scss +0 -76
- package/src/components/ResultCandidatePicker/ResultCandidatePicker.tsx +0 -38
- package/src/components/Solver/components/ApplyButton/ApplyButton.module.scss +0 -5
- package/src/components/Solver/components/ApplyButton/index.ts +0 -1
- package/src/components/SquareButton/index.ts +0 -1
- /package/.next/static/{WILX-RgqlLTd4ZoPs8C_E → 6RggBFm8kHrh-k1-CG3um}/_ssgManifest.js +0 -0
- /package/src/components/{ResultCandidatePicker → Solver/components/ResultCandidatePicker}/index.ts +0 -0
|
@@ -32,11 +32,10 @@ import Board from '../Board';
|
|
|
32
32
|
import Dictionary from '../Dictionary';
|
|
33
33
|
import DictionaryInput from '../DictionaryInput';
|
|
34
34
|
import Rack from '../Rack';
|
|
35
|
-
import ResultCandidatePicker from '../ResultCandidatePicker';
|
|
36
35
|
import Results from '../Results';
|
|
37
36
|
import Well from '../Well';
|
|
38
37
|
|
|
39
|
-
import {
|
|
38
|
+
import { EmptyState, ResultCandidatePicker, SolveButton } from './components';
|
|
40
39
|
import styles from './Solver.module.scss';
|
|
41
40
|
|
|
42
41
|
interface Props {
|
|
@@ -55,14 +54,19 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
55
54
|
const [resultsContainerRef, { width: resultsContainerWidth }] = useMeasure<HTMLDivElement>();
|
|
56
55
|
const isLessThanXl = useMediaQuery('<xl');
|
|
57
56
|
const isLessThanL = useMediaQuery('<l');
|
|
57
|
+
const isLessThanM = useMediaQuery('<m');
|
|
58
58
|
const componentsSpacing = isLessThanXl ? COMPONENTS_SPACING_SMALL : COMPONENTS_SPACING;
|
|
59
59
|
const maxBoardWidth = width - resultsContainerWidth - (isLessThanL ? 0 : componentsSpacing) - 2 * componentsSpacing;
|
|
60
|
-
const maxBoardHeight = Math.max(
|
|
60
|
+
const maxBoardHeight = Math.max(
|
|
61
|
+
height - bottomContainerHeight,
|
|
62
|
+
isLessThanL ? 0 : COLUMN_MIN_HEIGHT,
|
|
63
|
+
isLessThanM ? Number.POSITIVE_INFINITY : 0,
|
|
64
|
+
);
|
|
61
65
|
const config = useTypedSelector(selectConfig);
|
|
62
|
-
const
|
|
66
|
+
const error = useTypedSelector(selectSolveError);
|
|
63
67
|
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
68
|
+
const resultCandidate = useTypedSelector(selectResultCandidate);
|
|
64
69
|
const allResults = useTypedSelector(selectSortedResults);
|
|
65
|
-
const error = useTypedSelector(selectSolveError);
|
|
66
70
|
const results = useTypedSelector(selectSortedFilteredResults);
|
|
67
71
|
const [bestResult] = results || [];
|
|
68
72
|
const cellWidth = (maxBoardWidth - (config.boardWidth + 1) * BORDER_WIDTH) / config.boardWidth;
|
|
@@ -120,10 +124,10 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
120
124
|
};
|
|
121
125
|
|
|
122
126
|
useEffect(() => {
|
|
123
|
-
if (bestResult) {
|
|
127
|
+
if (isLessThanL && bestResult && !isOutdated) {
|
|
124
128
|
dispatch(resultsSlice.actions.changeResultCandidate(bestResult));
|
|
125
129
|
}
|
|
126
|
-
}, [bestResult, dispatch]);
|
|
130
|
+
}, [bestResult, dispatch, isLessThanL, isOutdated]);
|
|
127
131
|
|
|
128
132
|
return (
|
|
129
133
|
<div className={classNames(styles.solver, className)}>
|
|
@@ -160,41 +164,19 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
160
164
|
|
|
161
165
|
{isLessThanL && (
|
|
162
166
|
<div className={styles.controls} style={{ maxWidth: maxControlsWidth }}>
|
|
163
|
-
{
|
|
164
|
-
|
|
167
|
+
<ResultCandidatePicker onResultClick={onShowResults} />
|
|
168
|
+
|
|
169
|
+
{error && (
|
|
170
|
+
<EmptyState className={styles.emptyState} variant="error">
|
|
165
171
|
{error.message}
|
|
166
172
|
</EmptyState>
|
|
167
173
|
)}
|
|
168
174
|
|
|
169
|
-
{
|
|
170
|
-
<EmptyState variant="
|
|
171
|
-
{translate('results.empty-state.
|
|
175
|
+
{allResults && allResults.length === 0 && !isOutdated && (
|
|
176
|
+
<EmptyState className={styles.emptyState} variant="warning">
|
|
177
|
+
{translate('results.empty-state.no-results')}
|
|
172
178
|
</EmptyState>
|
|
173
179
|
)}
|
|
174
|
-
|
|
175
|
-
{typeof error === 'undefined' && typeof results !== 'undefined' && typeof allResults !== 'undefined' && (
|
|
176
|
-
<>
|
|
177
|
-
{(isOutdated || !resultCandidate) && (
|
|
178
|
-
<EmptyState variant="info" onClick={onShowResults}>
|
|
179
|
-
{translate('results.empty-state.outdated')}
|
|
180
|
-
</EmptyState>
|
|
181
|
-
)}
|
|
182
|
-
|
|
183
|
-
{!isOutdated && allResults.length === 0 && (
|
|
184
|
-
<EmptyState variant="warning" onClick={onShowResults}>
|
|
185
|
-
{translate('results.empty-state.no-results')}
|
|
186
|
-
</EmptyState>
|
|
187
|
-
)}
|
|
188
|
-
|
|
189
|
-
{!isOutdated && allResults.length > 0 && resultCandidate && (
|
|
190
|
-
<ResultCandidatePicker className={styles.resultCandidatePicker} onClick={onShowResults} />
|
|
191
|
-
)}
|
|
192
|
-
</>
|
|
193
|
-
)}
|
|
194
|
-
|
|
195
|
-
{allResults && allResults.length > 0 && !isOutdated && resultCandidate && (
|
|
196
|
-
<ApplyButton className={classNames(styles.submit, styles.apply)} />
|
|
197
|
-
)}
|
|
198
180
|
</div>
|
|
199
181
|
)}
|
|
200
182
|
</div>
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
1
2
|
import { FunctionComponent } from 'react';
|
|
2
3
|
import { useDispatch } from 'react-redux';
|
|
3
4
|
|
|
4
5
|
import { Check } from 'icons';
|
|
5
|
-
import { resultsSlice, selectResultCandidate, useTypedSelector } from 'state';
|
|
6
|
+
import { resultsSlice, selectAreResultsOutdated, selectResultCandidate, useTranslate, useTypedSelector } from 'state';
|
|
6
7
|
|
|
7
8
|
import Button from '../../../Button';
|
|
8
9
|
|
|
9
|
-
import styles from './
|
|
10
|
+
import styles from './InsertButton.module.scss';
|
|
10
11
|
|
|
11
12
|
interface Props {
|
|
12
13
|
className?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
+
const InsertButton: FunctionComponent<Props> = ({ className }) => {
|
|
16
17
|
const dispatch = useDispatch();
|
|
18
|
+
const translate = useTranslate();
|
|
17
19
|
const resultCandidate = useTypedSelector(selectResultCandidate);
|
|
20
|
+
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
18
21
|
|
|
19
22
|
const handleClick = () => {
|
|
20
23
|
if (resultCandidate) {
|
|
@@ -24,8 +27,9 @@ const ApplyButton: FunctionComponent<Props> = ({ className }) => {
|
|
|
24
27
|
|
|
25
28
|
return (
|
|
26
29
|
<Button
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
aria-label={translate('results.insert')}
|
|
31
|
+
className={classNames(styles.insertButton, className)}
|
|
32
|
+
disabled={isOutdated || !resultCandidate}
|
|
29
33
|
Icon={Check}
|
|
30
34
|
iconClassName={styles.icon}
|
|
31
35
|
type="submit"
|
|
@@ -35,4 +39,4 @@ const ApplyButton: FunctionComponent<Props> = ({ className }) => {
|
|
|
35
39
|
);
|
|
36
40
|
};
|
|
37
41
|
|
|
38
|
-
export default
|
|
42
|
+
export default InsertButton;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './InsertButton';
|
package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
@import 'styles/mixins';
|
|
2
|
+
|
|
3
|
+
.resultCandidatePicker {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: var(--spacing--l);
|
|
7
|
+
|
|
8
|
+
@include media('<xs') {
|
|
9
|
+
gap: var(--spacing--m);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.buttons {
|
|
14
|
+
flex: 0 0 auto;
|
|
15
|
+
display: flex;
|
|
16
|
+
border-radius: var(--border--radius);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.button {
|
|
20
|
+
padding: var(--spacing--m);
|
|
21
|
+
|
|
22
|
+
&:focus-within {
|
|
23
|
+
z-index: 1;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&:first-child {
|
|
27
|
+
border-right: none;
|
|
28
|
+
border-top-right-radius: 0;
|
|
29
|
+
border-bottom-right-radius: 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:last-child {
|
|
33
|
+
border-top-left-radius: 0;
|
|
34
|
+
border-bottom-left-radius: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[dir='rtl'] & {
|
|
38
|
+
transform: rotate(180deg);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.resultCandidate {
|
|
43
|
+
@include button-reset;
|
|
44
|
+
@include focus-effect;
|
|
45
|
+
|
|
46
|
+
flex: 1;
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
min-width: 0;
|
|
50
|
+
width: 100%;
|
|
51
|
+
border: var(--border);
|
|
52
|
+
border-radius: var(--border--radius);
|
|
53
|
+
background-color: white;
|
|
54
|
+
box-shadow: var(--box-shadow);
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
|
|
57
|
+
&[disabled] {
|
|
58
|
+
@include disabled;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.content {
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
width: 100%;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.points,
|
|
70
|
+
.word {
|
|
71
|
+
padding: var(--spacing--m) var(--spacing--l);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.points {
|
|
75
|
+
flex: 0 0 auto;
|
|
76
|
+
font-weight: bold;
|
|
77
|
+
|
|
78
|
+
[dir='ltr'] & {
|
|
79
|
+
border-right: var(--border);
|
|
80
|
+
|
|
81
|
+
@include media('<xs') {
|
|
82
|
+
border-right: none;
|
|
83
|
+
padding-right: var(--spacing--xs);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
[dir='rtl'] & {
|
|
88
|
+
border-left: var(--border);
|
|
89
|
+
|
|
90
|
+
@include media('<xs') {
|
|
91
|
+
border-left: none;
|
|
92
|
+
padding-left: var(--spacing--xs);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.word {
|
|
98
|
+
@include ellipsis;
|
|
99
|
+
|
|
100
|
+
flex: 1;
|
|
101
|
+
text-transform: uppercase;
|
|
102
|
+
text-align: center;
|
|
103
|
+
white-space: pre-wrap;
|
|
104
|
+
|
|
105
|
+
[dir='ltr'] & {
|
|
106
|
+
@include media('<xs') {
|
|
107
|
+
padding: var(--spacing--m);
|
|
108
|
+
text-align: left;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
[dir='rtl'] & {
|
|
113
|
+
@include media('<xs') {
|
|
114
|
+
padding: var(--spacing--m);
|
|
115
|
+
text-align: right;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.iconContainer {
|
|
121
|
+
flex: 0 0 auto;
|
|
122
|
+
display: flex;
|
|
123
|
+
|
|
124
|
+
[dir='ltr'] & {
|
|
125
|
+
padding-right: var(--spacing--l);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
[dir='rtl'] & {
|
|
129
|
+
padding-left: var(--spacing--l);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.icon {
|
|
134
|
+
$size: 20px;
|
|
135
|
+
|
|
136
|
+
width: $size;
|
|
137
|
+
height: $size;
|
|
138
|
+
color: var(--color--inactive);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.insert {
|
|
142
|
+
flex: 0 0 auto;
|
|
143
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { FunctionComponent, HTMLProps, MouseEventHandler } from 'react';
|
|
3
|
+
import { useDispatch } from 'react-redux';
|
|
4
|
+
|
|
5
|
+
import { ChevronDown, ChevronLeft, ChevronRight } from 'icons';
|
|
6
|
+
import {
|
|
7
|
+
resultsSlice,
|
|
8
|
+
selectAreResultsOutdated,
|
|
9
|
+
selectLocale,
|
|
10
|
+
selectResultCandidate,
|
|
11
|
+
selectSortedResults,
|
|
12
|
+
useTranslate,
|
|
13
|
+
useTypedSelector,
|
|
14
|
+
} from 'state';
|
|
15
|
+
|
|
16
|
+
import Button from '../../../Button';
|
|
17
|
+
import InsertButton from '../InsertButton';
|
|
18
|
+
|
|
19
|
+
import styles from './ResultCandidatePicker.module.scss';
|
|
20
|
+
|
|
21
|
+
interface Props extends HTMLProps<HTMLDivElement> {
|
|
22
|
+
onResultClick: MouseEventHandler<HTMLButtonElement>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ResultCandidatePicker: FunctionComponent<Props> = ({ className, onResultClick, ...props }) => {
|
|
26
|
+
const dispatch = useDispatch();
|
|
27
|
+
const translate = useTranslate();
|
|
28
|
+
const locale = useTypedSelector(selectLocale);
|
|
29
|
+
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
30
|
+
const sortedResults = useTypedSelector(selectSortedResults);
|
|
31
|
+
const results = sortedResults || [];
|
|
32
|
+
const resultCandidate = useTypedSelector(selectResultCandidate);
|
|
33
|
+
const index = resultCandidate ? results.findIndex((result) => result.id === resultCandidate.id) : -1;
|
|
34
|
+
const disabled = isOutdated || !resultCandidate;
|
|
35
|
+
const isPreviousDisabled = index <= 0 || disabled;
|
|
36
|
+
const isNextDisabled = index >= results.length - 1 || disabled;
|
|
37
|
+
|
|
38
|
+
const handleNextClick = () => {
|
|
39
|
+
if (!isNextDisabled) {
|
|
40
|
+
const nextResult = results[index + 1];
|
|
41
|
+
dispatch(resultsSlice.actions.changeResultCandidate(nextResult));
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const handlePreviousClick = () => {
|
|
46
|
+
if (!isPreviousDisabled) {
|
|
47
|
+
const previousResult = results[index - 1];
|
|
48
|
+
dispatch(resultsSlice.actions.changeResultCandidate(previousResult));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div className={classNames(styles.resultCandidatePicker, className)} {...props}>
|
|
54
|
+
<div className={styles.buttons}>
|
|
55
|
+
<Button
|
|
56
|
+
aria-label={translate('common.previous')}
|
|
57
|
+
className={styles.button}
|
|
58
|
+
disabled={isPreviousDisabled}
|
|
59
|
+
Icon={ChevronLeft}
|
|
60
|
+
onClick={handlePreviousClick}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
<Button
|
|
64
|
+
aria-label={translate('common.next')}
|
|
65
|
+
className={styles.button}
|
|
66
|
+
disabled={isNextDisabled}
|
|
67
|
+
Icon={ChevronRight}
|
|
68
|
+
onClick={handleNextClick}
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<button
|
|
73
|
+
aria-label={translate('results')}
|
|
74
|
+
className={styles.resultCandidate}
|
|
75
|
+
disabled={disabled}
|
|
76
|
+
type="button"
|
|
77
|
+
onClick={onResultClick}
|
|
78
|
+
>
|
|
79
|
+
{resultCandidate && (
|
|
80
|
+
<>
|
|
81
|
+
<div className={styles.points}>{resultCandidate.points.toLocaleString(locale)}</div>
|
|
82
|
+
<div className={styles.word}>{resultCandidate.word}</div>
|
|
83
|
+
</>
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
{!resultCandidate && <div className={styles.word}> </div>}
|
|
87
|
+
|
|
88
|
+
<div className={styles.iconContainer}>
|
|
89
|
+
<ChevronDown className={styles.icon} />
|
|
90
|
+
</div>
|
|
91
|
+
</button>
|
|
92
|
+
|
|
93
|
+
<InsertButton className={styles.insert} />
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export default ResultCandidatePicker;
|
|
@@ -4,7 +4,14 @@ import { useDispatch } from 'react-redux';
|
|
|
4
4
|
|
|
5
5
|
import { Search } from 'icons';
|
|
6
6
|
import { noop } from 'lib';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
selectAreResultsOutdated,
|
|
9
|
+
selectIsLoading,
|
|
10
|
+
selectRack,
|
|
11
|
+
solveSlice,
|
|
12
|
+
useTranslate,
|
|
13
|
+
useTypedSelector,
|
|
14
|
+
} from 'state';
|
|
8
15
|
|
|
9
16
|
import Button from '../../../Button';
|
|
10
17
|
|
|
@@ -17,6 +24,7 @@ interface Props {
|
|
|
17
24
|
|
|
18
25
|
const SolveButton: FunctionComponent<Props> = ({ className, onClick = noop }) => {
|
|
19
26
|
const dispatch = useDispatch();
|
|
27
|
+
const translate = useTranslate();
|
|
20
28
|
const isLoading = useTypedSelector(selectIsLoading);
|
|
21
29
|
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
22
30
|
const rack = useTypedSelector(selectRack);
|
|
@@ -29,10 +37,12 @@ const SolveButton: FunctionComponent<Props> = ({ className, onClick = noop }) =>
|
|
|
29
37
|
|
|
30
38
|
return (
|
|
31
39
|
<Button
|
|
40
|
+
aria-label={translate('results.solve')}
|
|
32
41
|
className={classNames(styles.solveButton, className)}
|
|
33
42
|
disabled={isLoading || !isOutdated || !hasTiles}
|
|
34
43
|
Icon={Search}
|
|
35
44
|
iconClassName={styles.icon}
|
|
45
|
+
tooltip={translate('results.solve')}
|
|
36
46
|
type="submit"
|
|
37
47
|
variant="primary"
|
|
38
48
|
onClick={handleClick}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { default as ApplyButton } from './ApplyButton';
|
|
2
1
|
export { default as EmptyState } from './EmptyState';
|
|
2
|
+
export { default as InsertButton } from './InsertButton';
|
|
3
|
+
export { default as ResultCandidatePicker } from './ResultCandidatePicker';
|
|
3
4
|
export { default as SolveButton } from './SolveButton';
|
|
@@ -16,6 +16,7 @@ import { selectLocale, useTypedSelector } from 'state';
|
|
|
16
16
|
import TilePure from './TilePure';
|
|
17
17
|
|
|
18
18
|
interface Props {
|
|
19
|
+
'aria-label': string;
|
|
19
20
|
autoFocus?: boolean;
|
|
20
21
|
character?: string;
|
|
21
22
|
className?: string;
|
|
@@ -35,6 +36,7 @@ interface Props {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const Tile: FunctionComponent<Props> = ({
|
|
39
|
+
'aria-label': ariaLabel,
|
|
38
40
|
autoFocus,
|
|
39
41
|
className,
|
|
40
42
|
character = '',
|
|
@@ -75,6 +77,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
75
77
|
|
|
76
78
|
return (
|
|
77
79
|
<TilePure
|
|
80
|
+
aria-label={ariaLabel}
|
|
78
81
|
autoFocus={autoFocus}
|
|
79
82
|
canShowPoints={canShowPoints}
|
|
80
83
|
character={character}
|
|
@@ -14,6 +14,7 @@ import { ExclamationSquareFill } from 'icons';
|
|
|
14
14
|
import styles from './Tile.module.scss';
|
|
15
15
|
|
|
16
16
|
interface Props {
|
|
17
|
+
'aria-label': string;
|
|
17
18
|
autoFocus?: boolean;
|
|
18
19
|
canShowPoints?: boolean;
|
|
19
20
|
character?: string;
|
|
@@ -37,6 +38,7 @@ interface Props {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
const TilePure: FunctionComponent<Props> = ({
|
|
41
|
+
'aria-label': ariaLabel,
|
|
40
42
|
autoFocus,
|
|
41
43
|
canShowPoints,
|
|
42
44
|
character,
|
|
@@ -75,6 +77,7 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
75
77
|
style={style}
|
|
76
78
|
>
|
|
77
79
|
<input
|
|
80
|
+
aria-label={ariaLabel}
|
|
78
81
|
autoCapitalize="none"
|
|
79
82
|
autoComplete="off"
|
|
80
83
|
autoCorrect="off"
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import { usePopper } from 'react-popper';
|
|
13
13
|
import { useMountedState, useRafLoop } from 'react-use';
|
|
14
14
|
|
|
15
|
-
import { usePortal, useUniqueId } from 'hooks';
|
|
15
|
+
import { useIsTouchDevice, usePortal, useUniqueId } from 'hooks';
|
|
16
16
|
import { noop } from 'lib';
|
|
17
17
|
|
|
18
18
|
import { MODIFIERS } from './constants';
|
|
@@ -36,11 +36,13 @@ interface TriggerProps {
|
|
|
36
36
|
onMouseOver?: MouseEventHandler;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// eslint-disable-next-line max-statements
|
|
39
40
|
const useTooltip = (
|
|
40
41
|
tooltip: ReactNode,
|
|
41
42
|
{ className, placement = 'top', onBlur = noop, onFocus = noop, onMouseOut = noop, onMouseOver = noop }: Props = {},
|
|
42
43
|
): TriggerProps => {
|
|
43
44
|
const id = useUniqueId();
|
|
45
|
+
const isTouchDevice = useIsTouchDevice();
|
|
44
46
|
const isEnabled = Boolean(tooltip) || tooltip === 0;
|
|
45
47
|
const isMounted = useMountedState();
|
|
46
48
|
const [isShown, setIsShown] = useState<boolean>(false);
|
|
@@ -90,7 +92,7 @@ const useTooltip = (
|
|
|
90
92
|
[onMouseOver],
|
|
91
93
|
);
|
|
92
94
|
|
|
93
|
-
const
|
|
95
|
+
const mouseTriggerProps = useMemo(
|
|
94
96
|
() => ({
|
|
95
97
|
...ariaAttributes,
|
|
96
98
|
ref: setReferenceElement,
|
|
@@ -102,6 +104,16 @@ const useTooltip = (
|
|
|
102
104
|
[ariaAttributes, handleBlur, handleFocus, handleMouseOut, handleMouseOver],
|
|
103
105
|
);
|
|
104
106
|
|
|
107
|
+
const touchTriggerProps = useMemo(
|
|
108
|
+
() => ({
|
|
109
|
+
...ariaAttributes,
|
|
110
|
+
ref: setReferenceElement,
|
|
111
|
+
}),
|
|
112
|
+
[ariaAttributes],
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const triggerProps = isTouchDevice ? touchTriggerProps : mouseTriggerProps;
|
|
116
|
+
|
|
105
117
|
useRafLoop(() => {
|
|
106
118
|
if (isMounted() && update) {
|
|
107
119
|
update();
|
package/src/components/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { default as Checkbox } from './Checkbox';
|
|
|
5
5
|
export { default as Dictionary } from './Dictionary';
|
|
6
6
|
export { default as DictionaryInput } from './DictionaryInput';
|
|
7
7
|
export { default as EmptyState } from './EmptyState';
|
|
8
|
+
export { default as IconButton } from './IconButton';
|
|
8
9
|
export { default as Key } from './Key';
|
|
9
10
|
export { default as Loading } from './Loading';
|
|
10
11
|
export { default as Logo } from './Logo';
|
|
@@ -16,13 +17,11 @@ export { default as PlainTiles } from './PlainTiles';
|
|
|
16
17
|
export { default as Progress } from './Progress';
|
|
17
18
|
export { default as Rack } from './Rack';
|
|
18
19
|
export { default as Radio } from './Radio';
|
|
19
|
-
export { default as ResultCandidatePicker } from './ResultCandidatePicker';
|
|
20
20
|
export { default as Results } from './Results';
|
|
21
21
|
export { default as ResultsInput } from './ResultsInput';
|
|
22
22
|
export { default as Sizer } from './Sizer';
|
|
23
23
|
export { default as Solver } from './Solver';
|
|
24
24
|
export { default as SplashScreen } from './SplashScreen';
|
|
25
|
-
export { default as SquareButton } from './SquareButton';
|
|
26
25
|
export { default as SvgFontCss } from './SvgFontCss';
|
|
27
26
|
export { default as SvgFontFix } from './SvgFontFix';
|
|
28
27
|
export { default as Tile } from './Tile';
|
package/src/i18n/de.json
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
"cell.filter-cell": "Zielort - klicken zum Wechseln",
|
|
3
3
|
"cell.set-blank": "Als Blanko markieren",
|
|
4
4
|
"cell.set-not-blank": "Nicht als Blanko markieren",
|
|
5
|
+
"cell.tile.location": "Brett: Stein ({{x}}, {{y}})",
|
|
5
6
|
"cell.toggle-direction": "Schreibrichtung - klicken zum Wechseln",
|
|
6
7
|
"common.blanks": "Blankos",
|
|
7
8
|
"common.clear": "Löschen",
|
|
8
9
|
"common.close": "Schließen",
|
|
9
10
|
"common.consonants": "Konsonanten",
|
|
10
11
|
"common.loading": "Laden",
|
|
12
|
+
"common.next": "Weiter",
|
|
11
13
|
"common.points": "Punkte",
|
|
14
|
+
"common.previous": "Zurück",
|
|
12
15
|
"common.tiles": "Steine",
|
|
13
16
|
"common.two-letter-tiles": "Zwei-Buchstaben",
|
|
14
17
|
"common.vowels": "Vokale",
|
|
@@ -37,6 +40,7 @@
|
|
|
37
40
|
"keyMap.rack.insert-blank": "Blanko hinzufügen (Leertaste)",
|
|
38
41
|
"menu": "Menü",
|
|
39
42
|
"rack.placeholder": "Steine…",
|
|
43
|
+
"rack.tile.location": "Ablage: Stein ({{index}})",
|
|
40
44
|
"remaining-tiles": "Restliche Steine",
|
|
41
45
|
"results": "Ergebnisse",
|
|
42
46
|
"results.empty-state.no-filtered-results": "Keine Ergebnisse für diese Anfrage.",
|
|
@@ -44,6 +48,7 @@
|
|
|
44
48
|
"results.empty-state.outdated": "Ergebnisse sind alt. Klicken zum Aktualisieren.",
|
|
45
49
|
"results.empty-state.uninitialized": "Wörter die aus deinen Buchstaben generiert wurden erscheinen hier.",
|
|
46
50
|
"results.input.placeholder": "Suchergebnisse... (RegExp)",
|
|
51
|
+
"results.insert": "Hinzufügen",
|
|
47
52
|
"results.solve": "Lösen",
|
|
48
53
|
"settings": "Einstellungen",
|
|
49
54
|
"settings.autoGroupTiles": "Restliche Steine gruppieren",
|
package/src/i18n/en.json
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
"cell.filter-cell": "Target destination - click to toggle",
|
|
3
3
|
"cell.set-blank": "Mark it a blank",
|
|
4
4
|
"cell.set-not-blank": "Mark it not a blank",
|
|
5
|
+
"cell.tile.location": "Board: tile ({{x}}, {{y}})",
|
|
5
6
|
"cell.toggle-direction": "Typing direction - click to toggle",
|
|
6
7
|
"common.blanks": "Blanks",
|
|
7
8
|
"common.clear": "Clear",
|
|
8
9
|
"common.close": "Close",
|
|
9
10
|
"common.consonants": "Consonants",
|
|
10
11
|
"common.loading": "Loading",
|
|
12
|
+
"common.next": "Next",
|
|
11
13
|
"common.points": "Points",
|
|
14
|
+
"common.previous": "Previous",
|
|
12
15
|
"common.tiles": "Tiles",
|
|
13
16
|
"common.two-letter-tiles": "Two-letter",
|
|
14
17
|
"common.vowels": "Vowels",
|
|
@@ -37,6 +40,7 @@
|
|
|
37
40
|
"keyMap.rack.insert-blank": "Insert blank (spacebar)",
|
|
38
41
|
"menu": "Menu",
|
|
39
42
|
"rack.placeholder": "Letters",
|
|
43
|
+
"rack.tile.location": "Rack: tile ({{index}})",
|
|
40
44
|
"remaining-tiles": "Remaining tiles",
|
|
41
45
|
"results": "Results",
|
|
42
46
|
"results.empty-state.no-filtered-results": "No result matches this query.",
|
|
@@ -44,6 +48,7 @@
|
|
|
44
48
|
"results.empty-state.outdated": "Results are outdated. Click below to update.",
|
|
45
49
|
"results.empty-state.uninitialized": "Words generated from your letters will be shown here.",
|
|
46
50
|
"results.input.placeholder": "Search results... (RegExp)",
|
|
51
|
+
"results.insert": "Insert",
|
|
47
52
|
"results.solve": "Solve",
|
|
48
53
|
"settings": "Settings",
|
|
49
54
|
"settings.autoGroupTiles": "Group remaining tiles",
|
package/src/i18n/es.json
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
"cell.filter-cell": "Destino objetivo: haga clic para alternar",
|
|
3
3
|
"cell.set-blank": "Marcar como en blanco",
|
|
4
4
|
"cell.set-not-blank": "Marcar como no en blanco",
|
|
5
|
+
"cell.tile.location": "Tablero: espacio ({{x}}, {{y}})",
|
|
5
6
|
"cell.toggle-direction": "Dirección de escritura: haga clic para alternar",
|
|
6
7
|
"common.blanks": "Blancos",
|
|
7
8
|
"common.clear": "Borrar",
|
|
8
9
|
"common.close": "Cerrar",
|
|
9
10
|
"common.consonants": "Consonantes",
|
|
10
11
|
"common.loading": "Cargando",
|
|
12
|
+
"common.next": "Siguiente",
|
|
11
13
|
"common.points": "Puntos",
|
|
14
|
+
"common.previous": "Anterior",
|
|
12
15
|
"common.tiles": "Longitud",
|
|
13
16
|
"common.two-letter-tiles": "Dos letras",
|
|
14
17
|
"common.vowels": "Vocales",
|
|
@@ -37,6 +40,7 @@
|
|
|
37
40
|
"keyMap.rack.insert-blank": "Insertar espacio en blanco (barra espaciadora)",
|
|
38
41
|
"menu": "Menú",
|
|
39
42
|
"rack.placeholder": "Letras…",
|
|
43
|
+
"rack.tile.location": "Estante: espacio ({{index}})",
|
|
40
44
|
"remaining-tiles": "Casillas restantes",
|
|
41
45
|
"results": "Resultados",
|
|
42
46
|
"results.empty-state.no-filtered-results": "Ningún resultado coincide con esta consulta.",
|
|
@@ -44,6 +48,7 @@
|
|
|
44
48
|
"results.empty-state.outdated": "Los resultados están desactualizados. Haga clic a continuación para actualizar.",
|
|
45
49
|
"results.empty-state.uninitialized": "Aquí se mostrarán las palabras generadas a partir de sus letras.",
|
|
46
50
|
"results.input.placeholder": "Busque una solución... (RegExp)",
|
|
51
|
+
"results.insert": "Insertar",
|
|
47
52
|
"results.solve": "Resolver",
|
|
48
53
|
"settings": "Configuración",
|
|
49
54
|
"settings.autoGroupTiles": "Agrupar casillas restantes",
|