scrabble-solver 2.10.4 → 2.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lerna.json +1 -1
- package/package.json +11 -11
- package/packages/configs/package-lock.json +4 -4
- package/packages/configs/package.json +3 -3
- package/packages/constants/package-lock.json +2 -2
- package/packages/constants/package.json +1 -1
- package/packages/dictionaries/package-lock.json +2 -2
- package/packages/dictionaries/package.json +4 -4
- package/packages/logger/package-lock.json +2 -2
- package/packages/logger/package.json +1 -1
- package/packages/scrabble-solver/.next/BUILD_ID +1 -1
- package/packages/scrabble-solver/.next/build-manifest.json +9 -9
- package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
- package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
- package/packages/scrabble-solver/.next/cache/next-server.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/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/routes-manifest.json +1 -1
- package/packages/scrabble-solver/.next/server/chunks/176.js +433 -228
- package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/404.html +2 -2
- package/packages/scrabble-solver/.next/server/pages/404.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/500.html +2 -2
- package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.html +2 -2
- package/packages/scrabble-solver/.next/server/pages/index.js +20 -50
- 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 +3 -3
- package/packages/scrabble-solver/.next/static/chunks/791-93aa8b8c22e488ac.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/{404-6c99a0c91257c60b.js → 404-ff35a4cf7f1ec85a.js} +1 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/index-ded620fd5df96be0.js +1 -0
- package/packages/scrabble-solver/.next/static/css/4482c4a0064d3807.css +1 -0
- package/packages/scrabble-solver/.next/static/css/a943dd97164732d4.css +1 -0
- package/packages/scrabble-solver/.next/static/iL0av55MV28b0MXfhKKt2/_buildManifest.js +1 -0
- package/packages/scrabble-solver/.next/trace +55 -55
- package/packages/scrabble-solver/package-lock.json +37 -37
- package/packages/scrabble-solver/package.json +13 -13
- package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +11 -11
- package/packages/scrabble-solver/src/components/Button/Button.module.scss +29 -10
- package/packages/scrabble-solver/src/components/Button/Button.tsx +24 -5
- package/packages/scrabble-solver/src/components/Button/Link.tsx +44 -0
- package/packages/scrabble-solver/src/components/Radio/Radio.module.scss +2 -2
- package/packages/scrabble-solver/src/components/Results/SolveButton.tsx +1 -0
- package/packages/scrabble-solver/src/components/Solver/Solver.module.scss +9 -21
- package/packages/scrabble-solver/src/components/Solver/Solver.tsx +20 -56
- package/packages/scrabble-solver/src/components/Solver/components/ApplyButton/ApplyButton.tsx +1 -0
- package/packages/scrabble-solver/src/components/Solver/components/MobileControls/MobileControls.tsx +62 -0
- package/packages/scrabble-solver/src/components/Solver/components/MobileControls/index.ts +1 -0
- package/packages/scrabble-solver/src/components/{ResultCandidatePicker → Solver/components/ResultCandidatePicker}/ResultCandidatePicker.module.scss +47 -3
- package/packages/scrabble-solver/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +75 -0
- package/packages/scrabble-solver/src/components/Solver/components/SolveButton/SolveButton.tsx +1 -0
- package/packages/scrabble-solver/src/components/Solver/components/index.ts +2 -0
- package/packages/scrabble-solver/src/components/SquareButton/SquareButton.module.scss +1 -2
- package/packages/scrabble-solver/src/components/SquareButton/SquareButton.tsx +2 -2
- package/packages/scrabble-solver/src/components/Tile/Tile.module.scss +35 -18
- package/packages/scrabble-solver/src/components/Tile/Tile.tsx +2 -2
- package/packages/scrabble-solver/src/components/Tile/TilePure.tsx +9 -5
- package/packages/scrabble-solver/src/components/Tooltip/Tooltip.module.scss +5 -5
- package/packages/scrabble-solver/src/components/index.ts +0 -1
- package/packages/scrabble-solver/src/icons/ChevronLeft.svg +4 -0
- package/packages/scrabble-solver/src/icons/ChevronRight.svg +4 -0
- package/packages/scrabble-solver/src/icons/index.ts +2 -0
- package/packages/scrabble-solver/src/modals/MenuModal/MenuModal.module.scss +3 -39
- package/packages/scrabble-solver/src/modals/MenuModal/MenuModal.tsx +22 -20
- package/packages/scrabble-solver/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -1
- package/packages/scrabble-solver/src/modals/ResultsModal/ResultsModal.tsx +3 -3
- package/packages/scrabble-solver/src/styles/mixins.scss +8 -2
- package/packages/solver/package-lock.json +2 -2
- package/packages/solver/package.json +5 -5
- package/packages/types/package-lock.json +3 -3
- package/packages/types/package.json +2 -2
- package/packages/word-definitions/package-lock.json +2 -2
- package/packages/word-definitions/package.json +2 -2
- package/packages/word-lists/package-lock.json +2 -2
- package/packages/word-lists/package.json +2 -2
- package/packages/scrabble-solver/.next/static/P7XhuDLmwJJqC8kgPjX42/_buildManifest.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/528-9942ddad0031ff79.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/index-d761f0af070273d2.js +0 -1
- package/packages/scrabble-solver/.next/static/css/97eb6ee0c4300c83.css +0 -1
- package/packages/scrabble-solver/.next/static/css/dcca0c1a39cf5ef5.css +0 -1
- package/packages/scrabble-solver/src/components/ResultCandidatePicker/ResultCandidatePicker.tsx +0 -38
- /package/packages/scrabble-solver/.next/static/{P7XhuDLmwJJqC8kgPjX42 → iL0av55MV28b0MXfhKKt2}/_ssgManifest.js +0 -0
- /package/packages/scrabble-solver/src/components/{ResultCandidatePicker → Solver/components/ResultCandidatePicker}/index.ts +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Result } from '@scrabble-solver/types';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
|
-
import {
|
|
3
|
+
import { FunctionComponent, SyntheticEvent, useEffect, useMemo } from 'react';
|
|
4
4
|
import { useDispatch } from 'react-redux';
|
|
5
5
|
import { useMeasure } from 'react-use';
|
|
6
6
|
|
|
@@ -17,14 +17,10 @@ import {
|
|
|
17
17
|
} from 'parameters';
|
|
18
18
|
import {
|
|
19
19
|
resultsSlice,
|
|
20
|
-
selectAreResultsOutdated,
|
|
21
20
|
selectConfig,
|
|
22
21
|
selectResultCandidate,
|
|
23
|
-
selectSolveError,
|
|
24
22
|
selectSortedFilteredResults,
|
|
25
|
-
selectSortedResults,
|
|
26
23
|
solveSlice,
|
|
27
|
-
useTranslate,
|
|
28
24
|
useTypedSelector,
|
|
29
25
|
} from 'state';
|
|
30
26
|
|
|
@@ -32,11 +28,10 @@ import Board from '../Board';
|
|
|
32
28
|
import Dictionary from '../Dictionary';
|
|
33
29
|
import DictionaryInput from '../DictionaryInput';
|
|
34
30
|
import Rack from '../Rack';
|
|
35
|
-
import ResultCandidatePicker from '../ResultCandidatePicker';
|
|
36
31
|
import Results from '../Results';
|
|
37
32
|
import Well from '../Well';
|
|
38
33
|
|
|
39
|
-
import {
|
|
34
|
+
import { MobileControls, SolveButton } from './components';
|
|
40
35
|
import styles from './Solver.module.scss';
|
|
41
36
|
|
|
42
37
|
interface Props {
|
|
@@ -49,20 +44,21 @@ interface Props {
|
|
|
49
44
|
// eslint-disable-next-line max-statements
|
|
50
45
|
const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResults }) => {
|
|
51
46
|
const dispatch = useDispatch();
|
|
52
|
-
const translate = useTranslate();
|
|
53
47
|
const isTouchDevice = useIsTouchDevice();
|
|
54
48
|
const [bottomContainerRef, { height: bottomContainerHeight }] = useMeasure<HTMLDivElement>();
|
|
55
49
|
const [resultsContainerRef, { width: resultsContainerWidth }] = useMeasure<HTMLDivElement>();
|
|
56
50
|
const isLessThanXl = useMediaQuery('<xl');
|
|
57
51
|
const isLessThanL = useMediaQuery('<l');
|
|
52
|
+
const isLessThanM = useMediaQuery('<m');
|
|
58
53
|
const componentsSpacing = isLessThanXl ? COMPONENTS_SPACING_SMALL : COMPONENTS_SPACING;
|
|
59
54
|
const maxBoardWidth = width - resultsContainerWidth - (isLessThanL ? 0 : componentsSpacing) - 2 * componentsSpacing;
|
|
60
|
-
const maxBoardHeight = Math.max(
|
|
55
|
+
const maxBoardHeight = Math.max(
|
|
56
|
+
height - bottomContainerHeight,
|
|
57
|
+
isLessThanL ? 0 : COLUMN_MIN_HEIGHT,
|
|
58
|
+
isLessThanM ? Number.POSITIVE_INFINITY : 0,
|
|
59
|
+
);
|
|
61
60
|
const config = useTypedSelector(selectConfig);
|
|
62
61
|
const resultCandidate = useTypedSelector(selectResultCandidate);
|
|
63
|
-
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
64
|
-
const allResults = useTypedSelector(selectSortedResults);
|
|
65
|
-
const error = useTypedSelector(selectSolveError);
|
|
66
62
|
const results = useTypedSelector(selectSortedFilteredResults);
|
|
67
63
|
const [bestResult] = results || [];
|
|
68
64
|
const cellWidth = (maxBoardWidth - (config.boardWidth + 1) * BORDER_WIDTH) / config.boardWidth;
|
|
@@ -109,9 +105,13 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
109
105
|
);
|
|
110
106
|
const callbacks = isTouchDevice ? touchCallbacks : mouseCallbacks;
|
|
111
107
|
|
|
112
|
-
const handleSubmit = (event:
|
|
108
|
+
const handleSubmit = (event: SyntheticEvent) => {
|
|
113
109
|
event.preventDefault();
|
|
114
|
-
|
|
110
|
+
|
|
111
|
+
if (isLessThanL) {
|
|
112
|
+
onShowResults();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
115
|
dispatch(solveSlice.actions.submit());
|
|
116
116
|
};
|
|
117
117
|
|
|
@@ -155,52 +155,16 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
|
|
|
155
155
|
</form>
|
|
156
156
|
|
|
157
157
|
{isLessThanL && (
|
|
158
|
-
<
|
|
159
|
-
{
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
)}
|
|
164
|
-
|
|
165
|
-
{typeof error === 'undefined' && typeof results === 'undefined' && (
|
|
166
|
-
<EmptyState variant="info" onClick={onShowResults}>
|
|
167
|
-
{translate('results.empty-state.uninitialized')}
|
|
168
|
-
</EmptyState>
|
|
169
|
-
)}
|
|
170
|
-
|
|
171
|
-
{typeof error === 'undefined' && typeof results !== 'undefined' && typeof allResults !== 'undefined' && (
|
|
172
|
-
<>
|
|
173
|
-
{isOutdated && (
|
|
174
|
-
<EmptyState variant="info" onClick={onShowResults}>
|
|
175
|
-
{translate('results.empty-state.outdated')}
|
|
176
|
-
</EmptyState>
|
|
177
|
-
)}
|
|
178
|
-
|
|
179
|
-
{!isOutdated && (
|
|
180
|
-
<>
|
|
181
|
-
{allResults.length === 0 && (
|
|
182
|
-
<EmptyState variant="warning" onClick={onShowResults}>
|
|
183
|
-
{translate('results.empty-state.no-results')}
|
|
184
|
-
</EmptyState>
|
|
185
|
-
)}
|
|
186
|
-
|
|
187
|
-
{allResults.length > 0 && resultCandidate && (
|
|
188
|
-
<ResultCandidatePicker className={styles.resultCandidatePicker} onClick={onShowResults} />
|
|
189
|
-
)}
|
|
190
|
-
</>
|
|
191
|
-
)}
|
|
192
|
-
</>
|
|
193
|
-
)}
|
|
194
|
-
|
|
195
|
-
{allResults && allResults.length > 0 && !isOutdated && (
|
|
196
|
-
<ApplyButton className={classNames(styles.submit, styles.apply)} />
|
|
197
|
-
)}
|
|
198
|
-
</div>
|
|
158
|
+
<MobileControls
|
|
159
|
+
className={styles.controls}
|
|
160
|
+
style={{ maxWidth: maxControlsWidth }}
|
|
161
|
+
onShowResults={onShowResults}
|
|
162
|
+
/>
|
|
199
163
|
)}
|
|
200
164
|
</div>
|
|
201
165
|
</div>
|
|
202
166
|
|
|
203
|
-
{isTouchDevice && <SolveButton className={styles.solve} onClick={
|
|
167
|
+
{isTouchDevice && <SolveButton className={styles.solve} onClick={handleSubmit} />}
|
|
204
168
|
</div>
|
|
205
169
|
);
|
|
206
170
|
};
|
package/packages/scrabble-solver/src/components/Solver/components/MobileControls/MobileControls.tsx
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { FunctionComponent, HTMLProps } from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
selectAreResultsOutdated,
|
|
5
|
+
selectResultCandidate,
|
|
6
|
+
selectSolveError,
|
|
7
|
+
selectSortedResults,
|
|
8
|
+
useTranslate,
|
|
9
|
+
useTypedSelector,
|
|
10
|
+
} from 'state';
|
|
11
|
+
|
|
12
|
+
import EmptyState from '../EmptyState';
|
|
13
|
+
import ResultCandidatePicker from '../ResultCandidatePicker';
|
|
14
|
+
|
|
15
|
+
interface Props extends HTMLProps<HTMLDivElement> {
|
|
16
|
+
onShowResults: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line max-statements
|
|
20
|
+
const MobileControls: FunctionComponent<Props> = ({ onShowResults, ...props }) => {
|
|
21
|
+
const translate = useTranslate();
|
|
22
|
+
const resultCandidate = useTypedSelector(selectResultCandidate);
|
|
23
|
+
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
24
|
+
const allResults = useTypedSelector(selectSortedResults);
|
|
25
|
+
const error = useTypedSelector(selectSolveError);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div {...props}>
|
|
29
|
+
{typeof error !== 'undefined' && (
|
|
30
|
+
<EmptyState variant="error" onClick={onShowResults}>
|
|
31
|
+
{error.message}
|
|
32
|
+
</EmptyState>
|
|
33
|
+
)}
|
|
34
|
+
|
|
35
|
+
{typeof error === 'undefined' && typeof allResults === 'undefined' && (
|
|
36
|
+
<EmptyState variant="info" onClick={onShowResults}>
|
|
37
|
+
{translate('results.empty-state.uninitialized')}
|
|
38
|
+
</EmptyState>
|
|
39
|
+
)}
|
|
40
|
+
|
|
41
|
+
{typeof error === 'undefined' && typeof allResults !== 'undefined' && (
|
|
42
|
+
<>
|
|
43
|
+
{(isOutdated || !resultCandidate) && (
|
|
44
|
+
<EmptyState variant="info" onClick={onShowResults}>
|
|
45
|
+
{translate('results.empty-state.outdated')}
|
|
46
|
+
</EmptyState>
|
|
47
|
+
)}
|
|
48
|
+
|
|
49
|
+
{!isOutdated && allResults.length === 0 && (
|
|
50
|
+
<EmptyState variant="warning" onClick={onShowResults}>
|
|
51
|
+
{translate('results.empty-state.no-results')}
|
|
52
|
+
</EmptyState>
|
|
53
|
+
)}
|
|
54
|
+
|
|
55
|
+
{!isOutdated && allResults.length > 0 && resultCandidate && <ResultCandidatePicker onClick={onShowResults} />}
|
|
56
|
+
</>
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default MobileControls;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './MobileControls';
|
|
@@ -1,9 +1,51 @@
|
|
|
1
1
|
@import 'styles/mixins';
|
|
2
2
|
|
|
3
3
|
.resultCandidatePicker {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: var(--spacing--l);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.buttons {
|
|
10
|
+
flex: 0 0 auto;
|
|
11
|
+
display: flex;
|
|
12
|
+
border-radius: var(--border--radius);
|
|
13
|
+
box-shadow: var(--box-shadow);
|
|
14
|
+
|
|
15
|
+
@include media('<xs') {
|
|
16
|
+
display: none;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.button {
|
|
21
|
+
padding: var(--spacing--m);
|
|
22
|
+
box-shadow: none;
|
|
23
|
+
|
|
24
|
+
&:focus-within {
|
|
25
|
+
z-index: 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:first-child {
|
|
29
|
+
border-right: none;
|
|
30
|
+
border-top-right-radius: 0;
|
|
31
|
+
border-bottom-right-radius: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&:last-child {
|
|
35
|
+
border-top-left-radius: 0;
|
|
36
|
+
border-bottom-left-radius: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
[dir='rtl'] & {
|
|
40
|
+
transform: rotate(180deg);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.resultCandidate {
|
|
4
45
|
@include button-reset;
|
|
5
46
|
@include focus-effect;
|
|
6
47
|
|
|
48
|
+
flex: 1;
|
|
7
49
|
display: flex;
|
|
8
50
|
align-items: center;
|
|
9
51
|
min-width: 0;
|
|
@@ -15,9 +57,7 @@
|
|
|
15
57
|
cursor: pointer;
|
|
16
58
|
|
|
17
59
|
&[disabled] {
|
|
18
|
-
|
|
19
|
-
box-shadow: none;
|
|
20
|
-
cursor: not-allowed;
|
|
60
|
+
@include disabled;
|
|
21
61
|
}
|
|
22
62
|
}
|
|
23
63
|
|
|
@@ -74,3 +114,7 @@
|
|
|
74
114
|
height: $size;
|
|
75
115
|
color: var(--color--inactive);
|
|
76
116
|
}
|
|
117
|
+
|
|
118
|
+
.apply {
|
|
119
|
+
flex: 0 0 auto;
|
|
120
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { FunctionComponent, HTMLProps } 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
|
+
useTypedSelector,
|
|
13
|
+
} from 'state';
|
|
14
|
+
|
|
15
|
+
import Button from '../../../Button';
|
|
16
|
+
import ApplyButton from '../ApplyButton';
|
|
17
|
+
|
|
18
|
+
import styles from './ResultCandidatePicker.module.scss';
|
|
19
|
+
|
|
20
|
+
const ResultCandidatePicker: FunctionComponent<HTMLProps<HTMLDivElement>> = ({ className, ...props }) => {
|
|
21
|
+
const dispatch = useDispatch();
|
|
22
|
+
const locale = useTypedSelector(selectLocale);
|
|
23
|
+
const isOutdated = useTypedSelector(selectAreResultsOutdated);
|
|
24
|
+
const sortedResults = useTypedSelector(selectSortedResults);
|
|
25
|
+
const results = sortedResults || [];
|
|
26
|
+
const resultCandidate = useTypedSelector(selectResultCandidate);
|
|
27
|
+
|
|
28
|
+
if (!resultCandidate) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const index = results.findIndex((result) => result.id === resultCandidate.id);
|
|
33
|
+
const isPreviousDisabled = index <= 0;
|
|
34
|
+
const isNextDisabled = index >= results.length - 1;
|
|
35
|
+
|
|
36
|
+
const handleNextClick = () => {
|
|
37
|
+
if (!isNextDisabled) {
|
|
38
|
+
const nextResult = results[index + 1];
|
|
39
|
+
dispatch(resultsSlice.actions.changeResultCandidate(nextResult));
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handlePreviousClick = () => {
|
|
44
|
+
if (!isPreviousDisabled) {
|
|
45
|
+
const previousResult = results[index - 1];
|
|
46
|
+
dispatch(resultsSlice.actions.changeResultCandidate(previousResult));
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className={classNames(styles.resultCandidatePicker, className)} {...props}>
|
|
52
|
+
<div className={styles.buttons}>
|
|
53
|
+
<Button
|
|
54
|
+
className={styles.button}
|
|
55
|
+
disabled={isPreviousDisabled}
|
|
56
|
+
Icon={ChevronLeft}
|
|
57
|
+
onClick={handlePreviousClick}
|
|
58
|
+
/>
|
|
59
|
+
<Button className={styles.button} disabled={isNextDisabled} Icon={ChevronRight} onClick={handleNextClick} />
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<button className={styles.resultCandidate} disabled={isOutdated} type="button">
|
|
63
|
+
<div className={styles.points}>{resultCandidate.points.toLocaleString(locale)}</div>
|
|
64
|
+
<div className={styles.word}>{resultCandidate.word}</div>
|
|
65
|
+
<div className={styles.iconContainer}>
|
|
66
|
+
<ChevronDown className={styles.icon} />
|
|
67
|
+
</div>
|
|
68
|
+
</button>
|
|
69
|
+
|
|
70
|
+
<ApplyButton className={styles.apply} />
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default ResultCandidatePicker;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { default as ApplyButton } from './ApplyButton';
|
|
2
2
|
export { default as EmptyState } from './EmptyState';
|
|
3
|
+
export { default as MobileControls } from './MobileControls';
|
|
4
|
+
export { default as ResultCandidatePicker } from './ResultCandidatePicker';
|
|
3
5
|
export { default as SolveButton } from './SolveButton';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { ButtonHTMLAttributes, FunctionComponent, MouseEventHandler, SVGAttributes } from 'react';
|
|
2
|
+
import { ButtonHTMLAttributes, FunctionComponent, MouseEventHandler, ReactNode, SVGAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
import { useTooltip } from '../Tooltip';
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ import styles from './SquareButton.module.scss';
|
|
|
9
9
|
interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
10
10
|
children?: never;
|
|
11
11
|
Icon: FunctionComponent<SVGAttributes<SVGElement>>;
|
|
12
|
-
tooltip:
|
|
12
|
+
tooltip: ReactNode;
|
|
13
13
|
onClick: MouseEventHandler<HTMLButtonElement>;
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -4,13 +4,8 @@
|
|
|
4
4
|
--background-color: transparent;
|
|
5
5
|
|
|
6
6
|
position: relative;
|
|
7
|
-
background-color: var(--background-color);
|
|
8
7
|
transition: var(--transition);
|
|
9
8
|
|
|
10
|
-
&.raised {
|
|
11
|
-
box-shadow: inset -2px -2px 2px -1px rgba(34, 34, 34, 0.8);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
9
|
&.points1 {
|
|
15
10
|
--background-color: var(--color--yellow);
|
|
16
11
|
}
|
|
@@ -55,31 +50,53 @@
|
|
|
55
50
|
|
|
56
51
|
color: var(--color--error);
|
|
57
52
|
}
|
|
53
|
+
|
|
54
|
+
&:not(.disabled) {
|
|
55
|
+
.input::selection {
|
|
56
|
+
--background--color: transparent;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
.input,
|
|
60
62
|
.character {
|
|
61
|
-
width: 100%;
|
|
62
|
-
height: 100%;
|
|
63
63
|
padding: 0;
|
|
64
|
-
box-sizing: border-box;
|
|
65
|
-
background-color: transparent;
|
|
66
|
-
color: inherit;
|
|
67
|
-
border: none;
|
|
68
64
|
font-weight: bold;
|
|
69
65
|
text-transform: uppercase;
|
|
70
66
|
text-align: center;
|
|
71
67
|
caret-color: transparent;
|
|
68
|
+
box-sizing: border-box;
|
|
69
|
+
}
|
|
72
70
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
.input {
|
|
72
|
+
width: 100%;
|
|
73
|
+
height: 100%;
|
|
74
|
+
background-color: transparent;
|
|
75
|
+
color: transparent;
|
|
76
|
+
border: none;
|
|
77
|
+
font-size: 16px; // prevent iOS from automatically zooming in on focus
|
|
78
|
+
}
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
.character {
|
|
81
|
+
position: absolute;
|
|
82
|
+
top: 0;
|
|
83
|
+
right: 0;
|
|
84
|
+
bottom: 0;
|
|
85
|
+
left: 0;
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
justify-content: center;
|
|
89
|
+
background-color: var(--background-color);
|
|
90
|
+
transition: var(--transition);
|
|
91
|
+
pointer-events: none;
|
|
92
|
+
user-select: none;
|
|
93
|
+
|
|
94
|
+
.empty & {
|
|
95
|
+
color: var(--color--inactive);
|
|
79
96
|
}
|
|
80
97
|
|
|
81
|
-
&
|
|
82
|
-
|
|
98
|
+
.raised & {
|
|
99
|
+
box-shadow: inset -2px -2px 2px -1px rgba(34, 34, 34, 0.8);
|
|
83
100
|
}
|
|
84
101
|
}
|
|
85
102
|
|
|
@@ -55,7 +55,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
55
55
|
const locale = useTypedSelector(selectLocale);
|
|
56
56
|
const { pointsFontSize, tileFontSize, tileSize } = getTileSizes(size);
|
|
57
57
|
const style = useMemo(() => ({ height: tileSize, width: tileSize }), [tileSize]);
|
|
58
|
-
const
|
|
58
|
+
const characterStyle = useMemo(() => ({ fontSize: tileFontSize }), [tileFontSize]);
|
|
59
59
|
const pointsStyle = useMemo(() => ({ fontSize: pointsFontSize }), [pointsFontSize]);
|
|
60
60
|
const inputRef = useMemo<RefObject<HTMLInputElement>>(() => ref || createRef(), [ref]);
|
|
61
61
|
const isEmpty = !character || character === EMPTY_CELL;
|
|
@@ -78,11 +78,11 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
78
78
|
autoFocus={autoFocus}
|
|
79
79
|
canShowPoints={canShowPoints}
|
|
80
80
|
character={character}
|
|
81
|
+
characterStyle={characterStyle}
|
|
81
82
|
className={className}
|
|
82
83
|
disabled={disabled}
|
|
83
84
|
highlighted={highlighted}
|
|
84
85
|
inputRef={inputRef}
|
|
85
|
-
inputStyle={inputStyle}
|
|
86
86
|
isBlank={isBlank}
|
|
87
87
|
isValid={isValid}
|
|
88
88
|
placeholder={placeholder}
|
|
@@ -17,11 +17,11 @@ interface Props {
|
|
|
17
17
|
autoFocus?: boolean;
|
|
18
18
|
canShowPoints?: boolean;
|
|
19
19
|
character?: string;
|
|
20
|
+
characterStyle?: CSSProperties;
|
|
20
21
|
className?: string;
|
|
21
22
|
disabled?: boolean;
|
|
22
23
|
highlighted?: boolean;
|
|
23
24
|
inputRef: RefObject<HTMLInputElement>;
|
|
24
|
-
inputStyle?: CSSProperties;
|
|
25
25
|
isBlank?: boolean;
|
|
26
26
|
isValid?: boolean;
|
|
27
27
|
placeholder?: string;
|
|
@@ -40,11 +40,11 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
40
40
|
autoFocus,
|
|
41
41
|
canShowPoints,
|
|
42
42
|
character,
|
|
43
|
+
characterStyle,
|
|
43
44
|
className,
|
|
44
45
|
disabled,
|
|
45
46
|
highlighted,
|
|
46
47
|
inputRef,
|
|
47
|
-
inputStyle,
|
|
48
48
|
isBlank,
|
|
49
49
|
isValid,
|
|
50
50
|
placeholder,
|
|
@@ -61,6 +61,8 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
61
61
|
<div
|
|
62
62
|
className={classNames(styles.tile, className, {
|
|
63
63
|
[styles.blank]: isBlank,
|
|
64
|
+
[styles.disabled]: disabled,
|
|
65
|
+
[styles.empty]: !character,
|
|
64
66
|
[styles.highlighted]: highlighted,
|
|
65
67
|
[styles.invalid]: !isValid,
|
|
66
68
|
[styles.raised]: raised,
|
|
@@ -77,12 +79,10 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
77
79
|
autoComplete="off"
|
|
78
80
|
autoCorrect="off"
|
|
79
81
|
autoFocus={autoFocus}
|
|
80
|
-
className={styles.
|
|
82
|
+
className={styles.input}
|
|
81
83
|
disabled={disabled}
|
|
82
|
-
placeholder={placeholder}
|
|
83
84
|
ref={inputRef}
|
|
84
85
|
spellCheck={false}
|
|
85
|
-
style={inputStyle}
|
|
86
86
|
tabIndex={tabIndex}
|
|
87
87
|
value={character || ''}
|
|
88
88
|
onChange={onChange}
|
|
@@ -90,6 +90,10 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
90
90
|
onKeyDown={onKeyDown}
|
|
91
91
|
/>
|
|
92
92
|
|
|
93
|
+
<div className={styles.character} style={characterStyle} tabIndex={-1}>
|
|
94
|
+
{character || placeholder}
|
|
95
|
+
</div>
|
|
96
|
+
|
|
93
97
|
{canShowPoints && (
|
|
94
98
|
<span className={styles.points} style={pointsStyle}>
|
|
95
99
|
{pointsFormatted}
|
|
@@ -18,7 +18,7 @@ $arrow-size: 4px;
|
|
|
18
18
|
.arrow {
|
|
19
19
|
bottom: -$arrow-size;
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
&::after {
|
|
22
22
|
left: 0;
|
|
23
23
|
bottom: 0;
|
|
24
24
|
border-top-color: var(--tooltip--background);
|
|
@@ -31,7 +31,7 @@ $arrow-size: 4px;
|
|
|
31
31
|
.arrow {
|
|
32
32
|
left: -$arrow-size;
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
&::after {
|
|
35
35
|
left: 0;
|
|
36
36
|
top: 0;
|
|
37
37
|
border-right-color: var(--tooltip--background);
|
|
@@ -44,7 +44,7 @@ $arrow-size: 4px;
|
|
|
44
44
|
.arrow {
|
|
45
45
|
top: -$arrow-size;
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
&::after {
|
|
48
48
|
top: 0;
|
|
49
49
|
left: 0;
|
|
50
50
|
border-bottom-color: var(--tooltip--background);
|
|
@@ -57,7 +57,7 @@ $arrow-size: 4px;
|
|
|
57
57
|
.arrow {
|
|
58
58
|
right: -$arrow-size;
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
&::after {
|
|
61
61
|
right: 0;
|
|
62
62
|
top: 0;
|
|
63
63
|
border-left-color: var(--tooltip--background);
|
|
@@ -72,7 +72,7 @@ $arrow-size: 4px;
|
|
|
72
72
|
width: 2 * $arrow-size;
|
|
73
73
|
height: 2 * $arrow-size;
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
&::after {
|
|
76
76
|
content: ' ';
|
|
77
77
|
position: absolute;
|
|
78
78
|
height: 0;
|
|
@@ -16,7 +16,6 @@ export { default as PlainTiles } from './PlainTiles';
|
|
|
16
16
|
export { default as Progress } from './Progress';
|
|
17
17
|
export { default as Rack } from './Rack';
|
|
18
18
|
export { default as Radio } from './Radio';
|
|
19
|
-
export { default as ResultCandidatePicker } from './ResultCandidatePicker';
|
|
20
19
|
export { default as Results } from './Results';
|
|
21
20
|
export { default as ResultsInput } from './ResultsInput';
|
|
22
21
|
export { default as Sizer } from './Sizer';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/chevron-left/ -->
|
|
2
|
+
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z" fill="currentColor" fill-rule="evenodd" />
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/chevron-right/ -->
|
|
2
|
+
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z" fill="currentColor" fill-rule="evenodd" />
|
|
4
|
+
</svg>
|
|
@@ -8,6 +8,8 @@ export { default as Check } from './Check.svg';
|
|
|
8
8
|
export { default as CheckboxChecked } from './CheckboxChecked.svg';
|
|
9
9
|
export { default as CheckboxEmpty } from './CheckboxEmpty.svg';
|
|
10
10
|
export { default as ChevronDown } from './ChevronDown.svg';
|
|
11
|
+
export { default as ChevronLeft } from './ChevronLeft.svg';
|
|
12
|
+
export { default as ChevronRight } from './ChevronRight.svg';
|
|
11
13
|
export { default as Cog } from './Cog.svg';
|
|
12
14
|
export { default as Cross } from './Cross.svg';
|
|
13
15
|
export { default as CrossCircleFill } from './CrossCircleFill.svg';
|