@scrabble-solver/scrabble-solver 2.10.7 → 2.10.8
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 -13
- 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 +4273 -214
- 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 +296 -13
- 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 +17 -0
- package/.next/server/pages/index.html +1 -9
- package/.next/server/pages/index.js +23 -16
- 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/Cs23uxWG6AxS72F2yrjHu/_buildManifest.js +1 -0
- package/.next/static/chunks/pages/{404-67383848027ec49b.js → 404-8cab6d62fe4ead73.js} +1 -1
- package/.next/static/chunks/pages/_app-dcbbb823dc93a031.js +28 -0
- package/.next/static/chunks/pages/index-df1ff01aa82d2d4d.js +1 -0
- package/.next/static/css/bf2e969b88c4e3dd.css +2 -0
- package/.next/static/css/d1cc6b79b211b7b8.css +1 -0
- package/.next/trace +55 -55
- package/package.json +10 -9
- package/src/components/Badge/Badge.module.scss +1 -1
- package/src/components/Board/components/Cell/Cell.module.scss +32 -64
- package/src/components/Board/components/Cell/CellPure.tsx +15 -22
- package/src/components/Button/Button.module.scss +2 -2
- package/src/components/Checkbox/Checkbox.tsx +1 -4
- package/src/components/Dictionary/Dictionary.tsx +28 -30
- package/src/components/DictionaryInput/DictionaryInput.tsx +3 -0
- package/src/components/Key/Key.module.scss +1 -1
- package/src/components/LogoSplashScreen/LogoSplashScreen.module.scss +1 -1
- package/src/components/Modal/Modal.module.scss +4 -2
- package/src/components/Rack/Rack.module.scss +4 -0
- package/src/components/Radio/Radio.tsx +1 -4
- package/src/components/Results/Results.module.scss +2 -2
- package/src/components/SeoMessage/SeoMessage.tsx +19 -0
- package/src/components/SeoMessage/index.ts +1 -0
- package/src/components/Solver/components/EmptyState/EmptyState.module.scss +1 -1
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss +9 -2
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +2 -1
- package/src/components/Tile/Tile.module.scss +49 -11
- package/src/components/Tile/Tile.tsx +23 -8
- package/src/components/Tile/TilePure.tsx +27 -20
- package/src/components/Tooltip/Tooltip.module.scss +7 -7
- package/src/components/index.ts +1 -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/pl.json +1 -0
- package/src/icons/Flag.svg +2 -2
- package/src/icons/FlagFill.svg +4 -0
- package/src/icons/Square.svg +4 -0
- package/src/icons/SquareFill.svg +4 -0
- package/src/icons/index.ts +3 -0
- package/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -1
- package/src/modals/ResultsModal/ResultsModal.module.scss +1 -1
- package/src/modals/SettingsModal/components/AutoGroupTilesSetting/AutoGroupTilesSetting.tsx +1 -2
- package/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.tsx +1 -2
- package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +14 -24
- package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.tsx +1 -2
- package/src/pages/_app.tsx +9 -5
- package/src/pages/index.module.scss +1 -2
- package/src/pages/index.tsx +10 -8
- package/src/parameters/index.ts +10 -0
- package/src/state/slices/boardSlice.ts +5 -5
- package/src/styles/mixins.scss +4 -2
- package/src/styles/variables.scss +39 -32
- package/src/types/index.ts +1 -0
- package/.next/server/chunks/579.js +0 -3925
- package/.next/static/6RggBFm8kHrh-k1-CG3um/_buildManifest.js +0 -1
- package/.next/static/chunks/490-d29992f1c264d70e.js +0 -5
- package/.next/static/chunks/509-6ad4482d4351452c.js +0 -1
- package/.next/static/chunks/pages/_app-c58cfa832b76cc87.js +0 -24
- package/.next/static/chunks/pages/index-146039f501e49c08.js +0 -1
- package/.next/static/css/4482c4a0064d3807.css +0 -1
- package/.next/static/css/78e42ad01f580f64.css +0 -1
- package/.next/static/css/9d1013ec684361b9.css +0 -1
- package/src/components/Board/components/Cell/Button.tsx +0 -32
- /package/.next/static/{6RggBFm8kHrh-k1-CG3um → Cs23uxWG6AxS72F2yrjHu}/_ssgManifest.js +0 -0
|
@@ -24,18 +24,20 @@
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
&.blank {
|
|
27
|
-
--background-color: white;
|
|
27
|
+
--background-color: var(--color--white);
|
|
28
28
|
|
|
29
|
+
.placeholder,
|
|
29
30
|
.character {
|
|
30
|
-
color:
|
|
31
|
+
color: var(--color--foreground);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
&.highlighted {
|
|
35
36
|
--background-color: var(--color--primary);
|
|
36
37
|
|
|
37
|
-
color:
|
|
38
|
+
color: var(--color--primary--opposite);
|
|
38
39
|
|
|
40
|
+
.placeholder,
|
|
39
41
|
.character {
|
|
40
42
|
color: inherit;
|
|
41
43
|
}
|
|
@@ -45,12 +47,6 @@
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
&.invalid {
|
|
49
|
-
--background-color: var(--color--red--light);
|
|
50
|
-
|
|
51
|
-
color: var(--color--error);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
50
|
&:not(.disabled) {
|
|
55
51
|
.input::selection {
|
|
56
52
|
--background--color: transparent;
|
|
@@ -58,8 +54,19 @@
|
|
|
58
54
|
}
|
|
59
55
|
}
|
|
60
56
|
|
|
57
|
+
.inputContainer {
|
|
58
|
+
position: absolute;
|
|
59
|
+
top: 0;
|
|
60
|
+
right: 0;
|
|
61
|
+
bottom: 0;
|
|
62
|
+
left: 0;
|
|
63
|
+
display: flex;
|
|
64
|
+
overflow: hidden;
|
|
65
|
+
}
|
|
66
|
+
|
|
61
67
|
.input,
|
|
62
|
-
.character
|
|
68
|
+
.character,
|
|
69
|
+
.placeholder {
|
|
63
70
|
padding: 0;
|
|
64
71
|
font-weight: bold;
|
|
65
72
|
text-transform: uppercase;
|
|
@@ -75,9 +82,26 @@
|
|
|
75
82
|
color: transparent;
|
|
76
83
|
border: none;
|
|
77
84
|
font-size: 16px; // prevent iOS from automatically zooming in on focus
|
|
85
|
+
|
|
86
|
+
// Hack for this Lighthouse warning:
|
|
87
|
+
// > Interactive elements like buttons and links should be large enough (48x48px), and have
|
|
88
|
+
// > enough space around them, to be easy enough to tap without overlapping onto other elements.
|
|
89
|
+
//
|
|
90
|
+
// .inputContainer exists only for the purpose of this hack
|
|
91
|
+
$min-tile-width: 20px;
|
|
92
|
+
$min-lighthouse-size: 48px;
|
|
93
|
+
|
|
94
|
+
position: absolute;
|
|
95
|
+
top: calc((100% - $min-lighthouse-size) / 2);
|
|
96
|
+
right: calc((100% - $min-lighthouse-size) / 2);
|
|
97
|
+
bottom: calc((100% - $min-lighthouse-size) / 2);
|
|
98
|
+
left: calc((100% - $min-lighthouse-size) / 2);
|
|
99
|
+
width: $min-lighthouse-size;
|
|
100
|
+
height: $min-lighthouse-size;
|
|
78
101
|
}
|
|
79
102
|
|
|
80
|
-
.character
|
|
103
|
+
.character,
|
|
104
|
+
.placeholder {
|
|
81
105
|
position: absolute;
|
|
82
106
|
top: 0;
|
|
83
107
|
right: 0;
|
|
@@ -97,6 +121,18 @@
|
|
|
97
121
|
|
|
98
122
|
.raised & {
|
|
99
123
|
box-shadow: inset -2px -2px 2px -1px rgba(34, 34, 34, 0.8);
|
|
124
|
+
|
|
125
|
+
@include media('<xs') {
|
|
126
|
+
box-shadow: inset -2px -2px 1px -1px rgba(34, 34, 34, 0.8);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.character {
|
|
132
|
+
opacity: 1;
|
|
133
|
+
|
|
134
|
+
.empty & {
|
|
135
|
+
opacity: 0;
|
|
100
136
|
}
|
|
101
137
|
}
|
|
102
138
|
|
|
@@ -130,6 +166,8 @@
|
|
|
130
166
|
position: absolute;
|
|
131
167
|
width: $size;
|
|
132
168
|
height: $size;
|
|
169
|
+
background-color: var(--color--error--opposite);
|
|
170
|
+
color: var(--color--error);
|
|
133
171
|
|
|
134
172
|
[dir='ltr'] & {
|
|
135
173
|
top: 0;
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { EMPTY_CELL } from '@scrabble-solver/constants';
|
|
2
|
+
import mergeRefs from 'merge-refs';
|
|
2
3
|
import {
|
|
3
4
|
ChangeEventHandler,
|
|
4
|
-
createRef,
|
|
5
5
|
FocusEventHandler,
|
|
6
6
|
FunctionComponent,
|
|
7
7
|
KeyboardEventHandler,
|
|
8
8
|
RefObject,
|
|
9
9
|
useEffect,
|
|
10
10
|
useMemo,
|
|
11
|
+
useRef,
|
|
11
12
|
} from 'react';
|
|
12
13
|
|
|
13
14
|
import { getTileSizes, noop } from 'lib';
|
|
15
|
+
import { EASE_OUT_CUBIC, TILE_APPEAR_DURATION, TILE_APPEAR_KEYFRAMES } from 'parameters';
|
|
14
16
|
import { selectLocale, useTypedSelector } from 'state';
|
|
15
17
|
|
|
16
18
|
import TilePure from './TilePure';
|
|
@@ -42,7 +44,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
42
44
|
character = '',
|
|
43
45
|
disabled,
|
|
44
46
|
highlighted,
|
|
45
|
-
inputRef
|
|
47
|
+
inputRef,
|
|
46
48
|
isBlank,
|
|
47
49
|
isValid,
|
|
48
50
|
placeholder,
|
|
@@ -59,21 +61,34 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
59
61
|
const style = useMemo(() => ({ height: tileSize, width: tileSize }), [tileSize]);
|
|
60
62
|
const characterStyle = useMemo(() => ({ fontSize: tileFontSize }), [tileFontSize]);
|
|
61
63
|
const pointsStyle = useMemo(() => ({ fontSize: pointsFontSize }), [pointsFontSize]);
|
|
62
|
-
const
|
|
64
|
+
const ref = useRef<HTMLInputElement>(null);
|
|
65
|
+
const mergedRef = inputRef ? mergeRefs(ref, inputRef) : ref;
|
|
63
66
|
const isEmpty = !character || character === EMPTY_CELL;
|
|
64
67
|
const canShowPoints = (isBlank || !isEmpty) && typeof points !== 'undefined';
|
|
65
68
|
const pointsFormatted = typeof points === 'number' ? points.toLocaleString(locale) : '';
|
|
66
69
|
|
|
67
70
|
const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (event) => {
|
|
68
|
-
|
|
71
|
+
ref.current?.select();
|
|
69
72
|
onKeyDown(event);
|
|
70
73
|
};
|
|
71
74
|
|
|
72
75
|
useEffect(() => {
|
|
73
|
-
if (autoFocus &&
|
|
74
|
-
|
|
76
|
+
if (autoFocus && ref.current) {
|
|
77
|
+
ref.current.focus();
|
|
75
78
|
}
|
|
76
|
-
}, [autoFocus,
|
|
79
|
+
}, [autoFocus, ref]);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!ref.current?.parentElement || !character) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
ref.current.parentElement.animate(TILE_APPEAR_KEYFRAMES, {
|
|
87
|
+
duration: TILE_APPEAR_DURATION,
|
|
88
|
+
easing: EASE_OUT_CUBIC,
|
|
89
|
+
fill: 'forwards',
|
|
90
|
+
});
|
|
91
|
+
}, [character]);
|
|
77
92
|
|
|
78
93
|
return (
|
|
79
94
|
<TilePure
|
|
@@ -85,7 +100,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
85
100
|
className={className}
|
|
86
101
|
disabled={disabled}
|
|
87
102
|
highlighted={highlighted}
|
|
88
|
-
inputRef={
|
|
103
|
+
inputRef={mergedRef}
|
|
89
104
|
isBlank={isBlank}
|
|
90
105
|
isValid={isValid}
|
|
91
106
|
placeholder={placeholder}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
FunctionComponent,
|
|
7
7
|
KeyboardEventHandler,
|
|
8
8
|
memo,
|
|
9
|
-
|
|
9
|
+
Ref,
|
|
10
10
|
} from 'react';
|
|
11
11
|
|
|
12
12
|
import { ExclamationSquareFill } from 'icons';
|
|
@@ -22,7 +22,7 @@ interface Props {
|
|
|
22
22
|
className?: string;
|
|
23
23
|
disabled?: boolean;
|
|
24
24
|
highlighted?: boolean;
|
|
25
|
-
inputRef:
|
|
25
|
+
inputRef: Ref<HTMLInputElement>;
|
|
26
26
|
isBlank?: boolean;
|
|
27
27
|
isValid?: boolean;
|
|
28
28
|
placeholder?: string;
|
|
@@ -66,7 +66,6 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
66
66
|
[styles.disabled]: disabled,
|
|
67
67
|
[styles.empty]: !character,
|
|
68
68
|
[styles.highlighted]: highlighted,
|
|
69
|
-
[styles.invalid]: !isValid,
|
|
70
69
|
[styles.raised]: raised,
|
|
71
70
|
[styles.points1]: points === 1,
|
|
72
71
|
[styles.points2]: points === 2,
|
|
@@ -76,25 +75,33 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
76
75
|
})}
|
|
77
76
|
style={style}
|
|
78
77
|
>
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
78
|
+
<div className={styles.inputContainer}>
|
|
79
|
+
<input
|
|
80
|
+
aria-label={ariaLabel}
|
|
81
|
+
autoCapitalize="none"
|
|
82
|
+
autoComplete="off"
|
|
83
|
+
autoCorrect="off"
|
|
84
|
+
autoFocus={autoFocus}
|
|
85
|
+
className={styles.input}
|
|
86
|
+
disabled={disabled}
|
|
87
|
+
ref={inputRef}
|
|
88
|
+
spellCheck={false}
|
|
89
|
+
tabIndex={tabIndex}
|
|
90
|
+
value={character || ''}
|
|
91
|
+
onChange={onChange}
|
|
92
|
+
onFocus={onFocus}
|
|
93
|
+
onKeyDown={onKeyDown}
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
{placeholder && (
|
|
98
|
+
<div className={styles.placeholder} style={characterStyle} tabIndex={-1}>
|
|
99
|
+
{placeholder}
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
95
102
|
|
|
96
103
|
<div className={styles.character} style={characterStyle} tabIndex={-1}>
|
|
97
|
-
{character
|
|
104
|
+
{character}
|
|
98
105
|
</div>
|
|
99
106
|
|
|
100
107
|
{canShowPoints && (
|
|
@@ -10,9 +10,9 @@ $arrow-size: 4px;
|
|
|
10
10
|
padding: var(--spacing--s) var(--spacing--m);
|
|
11
11
|
box-shadow: var(--box-shadow);
|
|
12
12
|
border-radius: var(--border--radius);
|
|
13
|
-
background-color: var(--tooltip--background);
|
|
14
|
-
color: var(--tooltip--foreground);
|
|
15
|
-
z-index:
|
|
13
|
+
background-color: var(--color--tooltip--background);
|
|
14
|
+
color: var(--color--tooltip--foreground);
|
|
15
|
+
z-index: var(--z-index--tooltip);
|
|
16
16
|
|
|
17
17
|
&.top {
|
|
18
18
|
.arrow {
|
|
@@ -21,7 +21,7 @@ $arrow-size: 4px;
|
|
|
21
21
|
&::after {
|
|
22
22
|
left: 0;
|
|
23
23
|
bottom: 0;
|
|
24
|
-
border-top-color: var(--tooltip--background);
|
|
24
|
+
border-top-color: var(--color--tooltip--background);
|
|
25
25
|
border-bottom: none;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -34,7 +34,7 @@ $arrow-size: 4px;
|
|
|
34
34
|
&::after {
|
|
35
35
|
left: 0;
|
|
36
36
|
top: 0;
|
|
37
|
-
border-right-color: var(--tooltip--background);
|
|
37
|
+
border-right-color: var(--color--tooltip--background);
|
|
38
38
|
border-left: none;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -47,7 +47,7 @@ $arrow-size: 4px;
|
|
|
47
47
|
&::after {
|
|
48
48
|
top: 0;
|
|
49
49
|
left: 0;
|
|
50
|
-
border-bottom-color: var(--tooltip--background);
|
|
50
|
+
border-bottom-color: var(--color--tooltip--background);
|
|
51
51
|
border-top: none;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -60,7 +60,7 @@ $arrow-size: 4px;
|
|
|
60
60
|
&::after {
|
|
61
61
|
right: 0;
|
|
62
62
|
top: 0;
|
|
63
|
-
border-left-color: var(--tooltip--background);
|
|
63
|
+
border-left-color: var(--color--tooltip--background);
|
|
64
64
|
border-right: none;
|
|
65
65
|
}
|
|
66
66
|
}
|
package/src/components/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export { default as Rack } from './Rack';
|
|
|
19
19
|
export { default as Radio } from './Radio';
|
|
20
20
|
export { default as Results } from './Results';
|
|
21
21
|
export { default as ResultsInput } from './ResultsInput';
|
|
22
|
+
export { default as SeoMessage } from './SeoMessage';
|
|
22
23
|
export { default as Sizer } from './Sizer';
|
|
23
24
|
export { default as Solver } from './Solver';
|
|
24
25
|
export { default as SplashScreen } from './SplashScreen';
|
package/src/i18n/de.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dictionary.empty-state.not-allowed": "Dieses Wort ist nicht erlaubt.",
|
|
23
23
|
"dictionary.empty-state.uninitialized": "Die Wörterbuchdefinition des letzten markierten Wortes wird hier angezeigt.",
|
|
24
24
|
"dictionary.input.placeholder": "Durchsuche Wörterbuch...",
|
|
25
|
+
"dictionary.input.title": "Durch Kommas getrennte Wörter",
|
|
25
26
|
"empty-state.error": "Fehler",
|
|
26
27
|
"empty-state.info": "Info",
|
|
27
28
|
"empty-state.success": "Juhuu!",
|
package/src/i18n/en.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dictionary.empty-state.not-allowed": "This word is not allowed.",
|
|
23
23
|
"dictionary.empty-state.uninitialized": "Dictionary definition of the most recently highlighted word will be shown here.",
|
|
24
24
|
"dictionary.input.placeholder": "Search dictionary...",
|
|
25
|
+
"dictionary.input.title": "Comma-separated words",
|
|
25
26
|
"empty-state.error": "Error",
|
|
26
27
|
"empty-state.info": "Info",
|
|
27
28
|
"empty-state.success": "Yeah!",
|
package/src/i18n/es.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dictionary.empty-state.not-allowed": "Esta palabra no es aceptable.",
|
|
23
23
|
"dictionary.empty-state.uninitialized": "Aquí se mostrará la definición del diccionario de la última palabra resaltada.",
|
|
24
24
|
"dictionary.input.placeholder": "Busca el diccionario...",
|
|
25
|
+
"dictionary.input.title": "Palabras separadas por comas",
|
|
25
26
|
"empty-state.error": "Error",
|
|
26
27
|
"empty-state.info": "Info",
|
|
27
28
|
"empty-state.success": "Sí!",
|
package/src/i18n/fa.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dictionary.empty-state.not-allowed": "این کلمه مجاز نیست.",
|
|
23
23
|
"dictionary.empty-state.uninitialized": "معنی لغت اینجا نمایش داده خواهد شد.",
|
|
24
24
|
"dictionary.input.placeholder": "جستجو در فرهنگ لغت ...",
|
|
25
|
+
"dictionary.input.title": "کلمات جدا شده با کاما",
|
|
25
26
|
"empty-state.error": "خطا",
|
|
26
27
|
"empty-state.info": "اطلاعات",
|
|
27
28
|
"empty-state.success": "حله!",
|
package/src/i18n/fr.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dictionary.empty-state.not-allowed": "Ce mot n'est pas pas acceptable.",
|
|
23
23
|
"dictionary.empty-state.uninitialized": "La définition dictionaire du dernier mot surligné sera affichée ici.",
|
|
24
24
|
"dictionary.input.placeholder": "Rechercher dans le dictionnaire...",
|
|
25
|
+
"dictionary.input.title": "Mots séparées par des virgules",
|
|
25
26
|
"empty-state.error": "Erreur",
|
|
26
27
|
"empty-state.info": "Info",
|
|
27
28
|
"empty-state.success": "Ouais!",
|
package/src/i18n/pl.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dictionary.empty-state.not-allowed": "To słowo nie jest dopuszczalne w grach.",
|
|
23
23
|
"dictionary.empty-state.uninitialized": "Tu zostanie wyświetlona słownikowa definicja ostatnio podświetlonego słowa.",
|
|
24
24
|
"dictionary.input.placeholder": "Szukaj w słowniku...",
|
|
25
|
+
"dictionary.input.title": "Słowa rozdzielone przecinkiem",
|
|
25
26
|
"empty-state.error": "Błąd",
|
|
26
27
|
"empty-state.info": "Info",
|
|
27
28
|
"empty-state.success": "Hurra!",
|
package/src/icons/Flag.svg
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- https://icons.getbootstrap.com/icons/flag
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/flag/ -->
|
|
2
2
|
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
-
<path d="M14.778.085A.5.5 0 0 1 15 .5V8a.5.5 0 0 1-.314.464L14.5 8l.186.464-.003.001-.006.003-.023.009a12.435 12.435 0 0 1-.397.15c-.264.095-.631.223-1.047.35-.816.252-1.879.523-2.71.523-.847 0-1.548-.28-2.158-.525l-.028-.01C7.68 8.71 7.14 8.5 6.5 8.5c-.7 0-1.638.23-2.437.477A19.626 19.626 0 0 0 3 9.342V15.5a.5.5 0 0 1-1 0V.5a.5.5 0 0 1 1 0v.282c.226-.079.496-.17.79-.26C4.606.272 5.67 0 6.5 0c.84 0 1.524.277 2.121.519l.043.018C9.286.788 9.828 1 10.5 1c.7 0 1.638-.23 2.437-.477a19.587 19.587 0 0 0 1.349-.476l.019-.007.004-.002h.
|
|
3
|
+
<path d="M14.778.085A.5.5 0 0 1 15 .5V8a.5.5 0 0 1-.314.464L14.5 8l.186.464-.003.001-.006.003-.023.009a12.435 12.435 0 0 1-.397.15c-.264.095-.631.223-1.047.35-.816.252-1.879.523-2.71.523-.847 0-1.548-.28-2.158-.525l-.028-.01C7.68 8.71 7.14 8.5 6.5 8.5c-.7 0-1.638.23-2.437.477A19.626 19.626 0 0 0 3 9.342V15.5a.5.5 0 0 1-1 0V.5a.5.5 0 0 1 1 0v.282c.226-.079.496-.17.79-.26C4.606.272 5.67 0 6.5 0c.84 0 1.524.277 2.121.519l.043.018C9.286.788 9.828 1 10.5 1c.7 0 1.638-.23 2.437-.477a19.587 19.587 0 0 0 1.349-.476l.019-.007.004-.002h.001M14 1.221c-.22.078-.48.167-.766.255-.81.252-1.872.523-2.734.523-.886 0-1.592-.286-2.203-.534l-.008-.003C7.662 1.21 7.139 1 6.5 1c-.669 0-1.606.229-2.415.478A21.294 21.294 0 0 0 3 1.845v6.433c.22-.078.48-.167.766-.255C4.576 7.77 5.638 7.5 6.5 7.5c.847 0 1.548.28 2.158.525l.028.01C9.32 8.29 9.86 8.5 10.5 8.5c.668 0 1.606-.229 2.415-.478A21.317 21.317 0 0 0 14 7.655V1.222z" fill="currentColor" />
|
|
4
4
|
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/flag-fill/ -->
|
|
2
|
+
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M14.778.085A.5.5 0 0 1 15 .5V8a.5.5 0 0 1-.314.464L14.5 8l.186.464-.003.001-.006.003-.023.009a12.435 12.435 0 0 1-.397.15c-.264.095-.631.223-1.047.35-.816.252-1.879.523-2.71.523-.847 0-1.548-.28-2.158-.525l-.028-.01C7.68 8.71 7.14 8.5 6.5 8.5c-.7 0-1.638.23-2.437.477A19.626 19.626 0 0 0 3 9.342V15.5a.5.5 0 0 1-1 0V.5a.5.5 0 0 1 1 0v.282c.226-.079.496-.17.79-.26C4.606.272 5.67 0 6.5 0c.84 0 1.524.277 2.121.519l.043.018C9.286.788 9.828 1 10.5 1c.7 0 1.638-.23 2.437-.477a19.587 19.587 0 0 0 1.349-.476l.019-.007.004-.002h.001" fill="currentColor" />
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/square/ -->
|
|
2
|
+
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z" fill="currentColor" />
|
|
4
|
+
</svg>
|
package/src/icons/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export { default as Eraser } from './Eraser.svg';
|
|
|
19
19
|
export { default as ExclamationSquareFill } from './ExclamationSquareFill.svg';
|
|
20
20
|
export { default as ExclamationTriangleFill } from './ExclamationTriangleFill.svg';
|
|
21
21
|
export { default as Flag } from './Flag.svg';
|
|
22
|
+
export { default as FlagFill } from './FlagFill.svg';
|
|
22
23
|
export { default as FlagEs } from './FlagEs.svg';
|
|
23
24
|
export { default as FlagFa } from './FlagFa.svg';
|
|
24
25
|
export { default as FlagFr } from './FlagFr.svg';
|
|
@@ -34,4 +35,6 @@ export { default as Sack } from './Sack.svg';
|
|
|
34
35
|
export { default as Search } from './Search.svg';
|
|
35
36
|
export { default as SortDown } from './SortDown.svg';
|
|
36
37
|
export { default as SortUp } from './SortUp.svg';
|
|
38
|
+
export { default as Square } from './Square.svg';
|
|
39
|
+
export { default as SquareFill } from './SquareFill.svg';
|
|
37
40
|
export { default as Star } from './Star.svg';
|
|
@@ -40,12 +40,11 @@ const AutoGroupTilesSetting: FunctionComponent<Props> = ({ className, disabled }
|
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<div className={className}>
|
|
43
|
-
{options.map((option
|
|
43
|
+
{options.map((option) => (
|
|
44
44
|
<Radio
|
|
45
45
|
checked={value === parseValue(option.value)}
|
|
46
46
|
className={styles.option}
|
|
47
47
|
disabled={disabled}
|
|
48
|
-
id={`autoGroupTiles-${index}`}
|
|
49
48
|
key={option.value}
|
|
50
49
|
name="autoGroupTiles"
|
|
51
50
|
value={option.value}
|
|
@@ -22,12 +22,11 @@ const ConfigSetting: FunctionComponent<Props> = ({ className, disabled }) => {
|
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
24
|
<div className={className}>
|
|
25
|
-
{options.map((option
|
|
25
|
+
{options.map((option) => (
|
|
26
26
|
<Radio
|
|
27
27
|
checked={configId === option.value}
|
|
28
28
|
className={styles.option}
|
|
29
29
|
disabled={disabled}
|
|
30
|
-
id={`configId-${index}`}
|
|
31
30
|
key={option.value}
|
|
32
31
|
name="configId"
|
|
33
32
|
value={option.value}
|
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
@import 'styles/mixins';
|
|
2
2
|
|
|
3
3
|
.option {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
&:last-child {
|
|
7
|
-
margin-bottom: 0;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
&:hover,
|
|
11
|
-
&.checked {
|
|
12
|
-
.flag {
|
|
13
|
-
box-shadow: var(--box-shadow);
|
|
14
|
-
}
|
|
4
|
+
& + & {
|
|
5
|
+
margin-top: var(--spacing--m);
|
|
15
6
|
}
|
|
16
7
|
}
|
|
17
8
|
|
|
@@ -19,10 +10,10 @@
|
|
|
19
10
|
display: flex;
|
|
20
11
|
align-items: center;
|
|
21
12
|
gap: var(--spacing--l);
|
|
22
|
-
font-family:
|
|
13
|
+
font-family: var(--font--family), sans-serif;
|
|
23
14
|
|
|
24
15
|
&.fa {
|
|
25
|
-
font-family:
|
|
16
|
+
font-family: var(--font--family--arabic), var(--font--family), sans-serif;
|
|
26
17
|
}
|
|
27
18
|
}
|
|
28
19
|
|
|
@@ -30,17 +21,16 @@
|
|
|
30
21
|
$height: 32px;
|
|
31
22
|
|
|
32
23
|
height: $height;
|
|
33
|
-
box-shadow: var(--box-shadow--null);
|
|
34
24
|
transition: var(--transition);
|
|
35
25
|
|
|
36
|
-
&.
|
|
37
|
-
$aspect-ratio: 1.
|
|
26
|
+
&.de {
|
|
27
|
+
$aspect-ratio: 1.6;
|
|
38
28
|
|
|
39
29
|
width: $height * $aspect-ratio;
|
|
40
30
|
}
|
|
41
31
|
|
|
42
|
-
&.
|
|
43
|
-
$aspect-ratio: 1.
|
|
32
|
+
&.es {
|
|
33
|
+
$aspect-ratio: 1.5;
|
|
44
34
|
|
|
45
35
|
width: $height * $aspect-ratio;
|
|
46
36
|
}
|
|
@@ -51,6 +41,12 @@
|
|
|
51
41
|
width: $height * $aspect-ratio;
|
|
52
42
|
}
|
|
53
43
|
|
|
44
|
+
&.fr {
|
|
45
|
+
$aspect-ratio: 1.6;
|
|
46
|
+
|
|
47
|
+
width: $height * $aspect-ratio;
|
|
48
|
+
}
|
|
49
|
+
|
|
54
50
|
&.gb {
|
|
55
51
|
$aspect-ratio: 2;
|
|
56
52
|
|
|
@@ -68,10 +64,4 @@
|
|
|
68
64
|
|
|
69
65
|
width: $height * $aspect-ratio;
|
|
70
66
|
}
|
|
71
|
-
|
|
72
|
-
&.de {
|
|
73
|
-
$aspect-ratio: 1.6;
|
|
74
|
-
|
|
75
|
-
width: $height * $aspect-ratio;
|
|
76
|
-
}
|
|
77
67
|
}
|
|
@@ -25,14 +25,13 @@ const LocaleSetting: FunctionComponent<Props> = ({ className, disabled }) => {
|
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
27
|
<div className={className}>
|
|
28
|
-
{options.map(({ Icon, ...option }
|
|
28
|
+
{options.map(({ Icon, ...option }) => (
|
|
29
29
|
<Radio
|
|
30
30
|
checked={locale === option.value}
|
|
31
31
|
className={classNames(styles.option, className, {
|
|
32
32
|
[styles.checked]: locale === option.value,
|
|
33
33
|
})}
|
|
34
34
|
disabled={disabled}
|
|
35
|
-
id={`locale-${index}`}
|
|
36
35
|
key={option.value}
|
|
37
36
|
name="locale"
|
|
38
37
|
value={option.value}
|
package/src/pages/_app.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import Head from 'next/head';
|
|
|
3
3
|
import { FunctionComponent } from 'react';
|
|
4
4
|
import { Provider } from 'react-redux';
|
|
5
5
|
|
|
6
|
+
import { SeoMessage } from 'components';
|
|
6
7
|
import { createAppStore } from 'state';
|
|
7
8
|
|
|
8
9
|
import 'styles/global.scss';
|
|
@@ -10,10 +11,16 @@ import 'styles/global.scss';
|
|
|
10
11
|
const DESCRIPTION =
|
|
11
12
|
// eslint-disable-next-line max-len
|
|
12
13
|
'Scrabble Solver 2 - Free and open-source analysis tool for Scrabble and Literaki. Quickly find top scoring words using given letters and board state. Available in English, French, German, Polish & Spanish.';
|
|
14
|
+
|
|
13
15
|
const KEYWORDS = [
|
|
16
|
+
'Scrabble Solver',
|
|
14
17
|
'Scrabble',
|
|
15
18
|
'Solver',
|
|
19
|
+
'Board',
|
|
16
20
|
'Open-source',
|
|
21
|
+
'Open',
|
|
22
|
+
'Source',
|
|
23
|
+
'Word',
|
|
17
24
|
'Finder',
|
|
18
25
|
'Cheating',
|
|
19
26
|
'Literaki',
|
|
@@ -22,6 +29,7 @@ const KEYWORDS = [
|
|
|
22
29
|
'Français',
|
|
23
30
|
'Deutsch',
|
|
24
31
|
'Polski',
|
|
32
|
+
'فارسی',
|
|
25
33
|
'Español',
|
|
26
34
|
'SOWPODS',
|
|
27
35
|
'TWL06',
|
|
@@ -55,11 +63,7 @@ const App: FunctionComponent<AppProps> = ({ Component, pageProps }) => (
|
|
|
55
63
|
</Head>
|
|
56
64
|
|
|
57
65
|
<Provider store={store}>
|
|
58
|
-
<
|
|
59
|
-
Scrabble Solver 2 is a free and open-source analysis tool for Scrabble and Literaki. Quickly find top scoring
|
|
60
|
-
words using given letters and board state. Available in English, French, German, Polish & Spanish. Source code
|
|
61
|
-
is available on GitHub - contributions are welcome!
|
|
62
|
-
</p>
|
|
66
|
+
<SeoMessage />
|
|
63
67
|
<Component {...pageProps} />
|
|
64
68
|
</Provider>
|
|
65
69
|
</>
|
package/src/pages/index.tsx
CHANGED
|
@@ -76,6 +76,10 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
76
76
|
}
|
|
77
77
|
}, []);
|
|
78
78
|
|
|
79
|
+
if (!isClient) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
79
83
|
return (
|
|
80
84
|
<>
|
|
81
85
|
<SvgFontFix />
|
|
@@ -100,14 +104,12 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
100
104
|
</div>
|
|
101
105
|
</div>
|
|
102
106
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/>
|
|
110
|
-
)}
|
|
107
|
+
<Solver
|
|
108
|
+
className={styles.solver}
|
|
109
|
+
height={solverHeight}
|
|
110
|
+
width={solverWidth}
|
|
111
|
+
onShowResults={() => setShowResults(true)}
|
|
112
|
+
/>
|
|
111
113
|
</div>
|
|
112
114
|
|
|
113
115
|
<MenuModal
|