@scrabble-solver/scrabble-solver 2.11.2 → 2.11.4
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 +6 -6
- 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/210.js +109 -0
- package/.next/server/chunks/277.js +189 -125
- package/.next/server/chunks/44.js +19 -0
- package/.next/server/chunks/987.js +91 -0
- 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 +1 -73
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/_document.js.nft.json +1 -1
- package/.next/server/pages/_error.js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale]/[word].js +3 -3
- package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale].js +3 -3
- package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
- package/.next/server/pages/api/solve.js +10 -6
- package/.next/server/pages/api/solve.js.nft.json +1 -1
- package/.next/server/pages/api/verify.js +5 -4
- package/.next/server/pages/api/verify.js.nft.json +1 -1
- package/.next/server/pages/api/visit.js +3 -3
- package/.next/server/pages/api/visit.js.nft.json +1 -1
- package/.next/server/pages/index.html +1 -1
- package/.next/server/pages/index.js +104 -207
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/index.json +1 -1
- package/.next/static/{Mdvi3FY0PqkILKLbPlVBU → MvHZRF4XuJ7g8LLLRkf8U}/_buildManifest.js +1 -1
- package/.next/static/chunks/pages/_app-66d80a5594aab8dc.js +28 -0
- package/.next/static/chunks/pages/index-0858deea02b2a417.js +1 -0
- package/.next/static/css/885da289cec275b3.css +1 -0
- package/.next/static/css/ea1c8134fe9a143e.css +2 -0
- package/.next/trace +53 -54
- package/package.json +9 -9
- package/src/api/index.ts +1 -0
- package/src/api/isCellValid.ts +3 -2
- package/src/api/isCharacterValid.ts +13 -0
- package/src/components/Button/Button.module.scss +14 -1
- package/src/components/Modal/Modal.module.scss +11 -3
- package/src/components/NotFound/NotFound.module.scss +13 -4
- package/src/components/NotFound/NotFound.tsx +4 -7
- package/src/components/Rack/Rack.tsx +3 -1
- package/src/components/Tile/Tile.module.scss +23 -18
- package/src/components/Tile/Tile.tsx +2 -5
- package/src/components/Tile/TilePure.tsx +1 -2
- package/src/i18n/constants.ts +65 -0
- package/src/i18n/de.json +3 -2
- package/src/i18n/en.json +3 -2
- package/src/i18n/es.json +3 -2
- package/src/i18n/fa.json +2 -1
- package/src/i18n/fr.json +3 -2
- package/src/i18n/i18n.module.scss +27 -0
- package/src/i18n/pl.json +3 -2
- package/src/icons/DashCircleFill.svg +1 -0
- package/src/icons/EyeFill.svg +5 -0
- package/src/icons/index.ts +3 -2
- package/src/modals/MenuModal/MenuModal.module.scss +17 -1
- package/src/modals/MenuModal/MenuModal.tsx +8 -2
- package/src/modals/RemainingTilesModal/RemainingTilesModal.tsx +4 -1
- package/src/modals/RemainingTilesModal/components/Character/Character.module.scss +8 -0
- package/src/modals/ResultsModal/ResultsModal.tsx +35 -11
- package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +3 -44
- package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.tsx +4 -2
- package/src/pages/api/solve.ts +3 -2
- package/src/styles/mixins.scss +2 -0
- package/src/styles/variables.scss +12 -0
- package/src/types/index.ts +1 -0
- package/.next/server/chunks/417.js +0 -221
- package/.next/server/chunks/664.js +0 -621
- package/.next/static/chunks/pages/_app-495e6f4ccc278bb2.js +0 -28
- package/.next/static/chunks/pages/index-5ecc51900ca29685.js +0 -1
- package/.next/static/css/17b0a2db8742105f.css +0 -1
- package/.next/static/css/e1ffeb2558330c55.css +0 -2
- package/src/modals/SettingsModal/components/LocaleSetting/options.ts +0 -68
- /package/.next/static/{Mdvi3FY0PqkILKLbPlVBU → MvHZRF4XuJ7g8LLLRkf8U}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.4",
|
|
4
4
|
"description": "Scrabble Solver 2 - App",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16"
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
"@floating-ui/react": "^0.20.1",
|
|
32
32
|
"@kamilmielnik/trie": "^2.0.1",
|
|
33
33
|
"@reduxjs/toolkit": "^1.9.3",
|
|
34
|
-
"@scrabble-solver/configs": "^2.11.
|
|
35
|
-
"@scrabble-solver/constants": "^2.11.
|
|
36
|
-
"@scrabble-solver/dictionaries": "^2.11.
|
|
37
|
-
"@scrabble-solver/logger": "^2.11.
|
|
38
|
-
"@scrabble-solver/solver": "^2.11.
|
|
39
|
-
"@scrabble-solver/types": "^2.11.
|
|
40
|
-
"@scrabble-solver/word-definitions": "^2.11.
|
|
34
|
+
"@scrabble-solver/configs": "^2.11.4",
|
|
35
|
+
"@scrabble-solver/constants": "^2.11.4",
|
|
36
|
+
"@scrabble-solver/dictionaries": "^2.11.4",
|
|
37
|
+
"@scrabble-solver/logger": "^2.11.4",
|
|
38
|
+
"@scrabble-solver/solver": "^2.11.4",
|
|
39
|
+
"@scrabble-solver/types": "^2.11.4",
|
|
40
|
+
"@scrabble-solver/word-definitions": "^2.11.4",
|
|
41
41
|
"classnames": "^2.3.2",
|
|
42
42
|
"include-media": "^2.0.0",
|
|
43
43
|
"include-media-query-builder": "^1.1.0",
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"sass": "^1.59.2",
|
|
73
73
|
"workbox-webpack-plugin": "^6.5.4"
|
|
74
74
|
},
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "41fd187eab48d417aafc30f8a1d407178323c415"
|
|
76
76
|
}
|
package/src/api/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as getServerLoggingData } from './getServerLoggingData';
|
|
2
2
|
export { default as isBoardValid } from './isBoardValid';
|
|
3
3
|
export { default as isCellValid } from './isCellValid';
|
|
4
|
+
export { default as isCharacterValid } from './isCharacterValid';
|
|
4
5
|
export { default as isRowValid } from './isRowValid';
|
package/src/api/isCellValid.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { BLANK } from '@scrabble-solver/constants';
|
|
2
1
|
import { CellJson, Config } from '@scrabble-solver/types';
|
|
3
2
|
|
|
3
|
+
import isCharacterValid from './isCharacterValid';
|
|
4
|
+
|
|
4
5
|
const isCellValid = (cell: CellJson, config: Config): boolean => {
|
|
5
6
|
const { isEmpty, tile, x, y } = cell;
|
|
6
7
|
|
|
@@ -16,7 +17,7 @@ const isCellValid = (cell: CellJson, config: Config): boolean => {
|
|
|
16
17
|
return false;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
if (tile !== null && !
|
|
20
|
+
if (tile !== null && !isCharacterValid(tile.character)) {
|
|
20
21
|
return false;
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const TWO_TILE_CHARACTER_LENGTH = 2;
|
|
2
|
+
const MAX_CHARACTER_LENGTH = TWO_TILE_CHARACTER_LENGTH;
|
|
3
|
+
|
|
4
|
+
const isCharacterValid = (character: string): boolean => {
|
|
5
|
+
/*
|
|
6
|
+
* We could be strict here and check whether config.hasCharacter(character) || character === BLANK
|
|
7
|
+
* but since this case won't really affect/break solving, we don't need to worry about it.
|
|
8
|
+
* It's better to display an empty state than error state in UI, so this is a sanity check only.
|
|
9
|
+
*/
|
|
10
|
+
return character.length !== 0 && character.length <= MAX_CHARACTER_LENGTH;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default isCharacterValid;
|
|
@@ -60,16 +60,29 @@
|
|
|
60
60
|
.content {
|
|
61
61
|
display: flex;
|
|
62
62
|
align-items: center;
|
|
63
|
-
gap:
|
|
63
|
+
gap: var(--spacing--l);
|
|
64
|
+
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
.icon {
|
|
68
|
+
flex: 0 0 auto;
|
|
67
69
|
width: var(--button--icon--size);
|
|
68
70
|
height: var(--button--icon--size);
|
|
69
71
|
transition: var(--transition);
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
.label {
|
|
75
|
+
@include ellipsis;
|
|
76
|
+
|
|
77
|
+
flex: 1;
|
|
73
78
|
font-size: var(--font--size--h3);
|
|
74
79
|
transition: var(--transition);
|
|
80
|
+
|
|
81
|
+
[dir='ltr'] & {
|
|
82
|
+
text-align: left;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
[dir='rtl'] & {
|
|
86
|
+
text-align: right;
|
|
87
|
+
}
|
|
75
88
|
}
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
z-index: 1;
|
|
90
90
|
flex: 0 0 auto;
|
|
91
91
|
padding: var(--spacing--l);
|
|
92
|
+
padding-bottom: 0;
|
|
92
93
|
background-color: var(--color--background);
|
|
93
94
|
}
|
|
94
95
|
|
|
@@ -117,18 +118,25 @@
|
|
|
117
118
|
position: relative;
|
|
118
119
|
flex: 1;
|
|
119
120
|
min-height: 0;
|
|
120
|
-
padding: var(--spacing--l);
|
|
121
121
|
overflow: auto;
|
|
122
|
+
padding: var(--spacing--l);
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
.footer {
|
|
125
126
|
flex: 0 0 auto;
|
|
126
127
|
display: flex;
|
|
127
|
-
|
|
128
|
+
flex-direction: row-reverse;
|
|
129
|
+
justify-content: space-between;
|
|
130
|
+
gap: var(--spacing--l);
|
|
128
131
|
padding: var(--spacing--l);
|
|
129
|
-
|
|
132
|
+
padding-top: 0;
|
|
130
133
|
|
|
131
134
|
@include media('>s') {
|
|
132
135
|
display: none;
|
|
133
136
|
}
|
|
137
|
+
|
|
138
|
+
> * {
|
|
139
|
+
min-width: 0;
|
|
140
|
+
flex: 1;
|
|
141
|
+
}
|
|
134
142
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@import 'styles/mixins';
|
|
2
|
+
|
|
1
3
|
.notFound {
|
|
2
4
|
display: flex;
|
|
3
5
|
align-items: center;
|
|
@@ -11,18 +13,25 @@
|
|
|
11
13
|
align-items: center;
|
|
12
14
|
justify-content: center;
|
|
13
15
|
flex-direction: column;
|
|
14
|
-
padding: var(--spacing--
|
|
16
|
+
padding: var(--spacing--l);
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
.icon {
|
|
18
|
-
|
|
20
|
+
--size: 200px;
|
|
19
21
|
|
|
20
|
-
height:
|
|
21
|
-
width:
|
|
22
|
+
height: var(--size);
|
|
23
|
+
width: var(--size);
|
|
22
24
|
margin-bottom: var(--spacing--xxl);
|
|
23
25
|
color: var(--color--red);
|
|
26
|
+
|
|
27
|
+
@include media('<xs') {
|
|
28
|
+
--size: 50%;
|
|
29
|
+
|
|
30
|
+
margin-bottom: var(--spacing--xl);
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
.tiles {
|
|
35
|
+
max-width: 100%;
|
|
27
36
|
height: 60px;
|
|
28
37
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import Link from 'next/link';
|
|
2
1
|
import { FunctionComponent } from 'react';
|
|
3
2
|
|
|
4
3
|
import { DashCircleFill } from 'icons';
|
|
@@ -11,12 +10,10 @@ const CONTENT = [['HTTP', '404']];
|
|
|
11
10
|
|
|
12
11
|
const NotFound: FunctionComponent = () => (
|
|
13
12
|
<div className={styles.notFound}>
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
</a>
|
|
19
|
-
</Link>
|
|
13
|
+
<a className={styles.link} href="/">
|
|
14
|
+
<DashCircleFill className={styles.icon} />
|
|
15
|
+
<PlainTiles className={styles.tiles} content={CONTENT} />
|
|
16
|
+
</a>
|
|
20
17
|
</div>
|
|
21
18
|
);
|
|
22
19
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
createKeyboardNavigation,
|
|
9
9
|
extractCharacters,
|
|
10
10
|
extractInputValue,
|
|
11
|
+
getTileSizes,
|
|
11
12
|
zipCharactersAndTiles,
|
|
12
13
|
} from 'lib';
|
|
13
14
|
import { rackSlice, selectConfig, selectLocale, selectRack, selectResultCandidateTiles, useTypedSelector } from 'state';
|
|
@@ -31,6 +32,7 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
|
|
|
31
32
|
const tilesRefs = useMemo(() => createArray(tilesCount).map(() => createRef<HTMLInputElement>()), [tilesCount]);
|
|
32
33
|
const activeIndexRef = useRef<number>();
|
|
33
34
|
const { direction } = LOCALE_FEATURES[locale];
|
|
35
|
+
const { tileFontSize } = getTileSizes(tileSize);
|
|
34
36
|
|
|
35
37
|
const changeActiveIndex = useCallback(
|
|
36
38
|
(offset: number) => {
|
|
@@ -110,7 +112,7 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
|
|
|
110
112
|
}, [changeActiveIndex, config, direction]);
|
|
111
113
|
|
|
112
114
|
return (
|
|
113
|
-
<div className={classNames(styles.rack, className)} onPaste={handlePaste}>
|
|
115
|
+
<div className={classNames(styles.rack, className)} style={{ fontSize: tileFontSize }} onPaste={handlePaste}>
|
|
114
116
|
{tiles.map(({ character, tile }, index) => (
|
|
115
117
|
<RackTile
|
|
116
118
|
activeIndexRef={activeIndexRef}
|
|
@@ -34,18 +34,10 @@
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
&.raised {
|
|
37
|
-
--shadow--
|
|
38
|
-
--shadow--blur: 2px;
|
|
39
|
-
--shadow--spread: -1px;
|
|
40
|
-
--shadow--color: rgba(34, 34, 34, 0.8);
|
|
41
|
-
|
|
42
|
-
box-shadow: inset calc(-1 * var(--shadow--size)) calc(-1 * var(--shadow--size)) var(--shadow--blur)
|
|
43
|
-
var(--shadow--spread) var(--shadow--color);
|
|
37
|
+
box-shadow: var(--box-shadow--raised);
|
|
44
38
|
|
|
45
39
|
@include media('<xs') {
|
|
46
|
-
--shadow--
|
|
47
|
-
--shadow--spread: 0;
|
|
48
|
-
--shadow--blur: 1px;
|
|
40
|
+
box-shadow: var(--box-shadow--raised--subtle);
|
|
49
41
|
}
|
|
50
42
|
}
|
|
51
43
|
|
|
@@ -59,6 +51,13 @@
|
|
|
59
51
|
color: var(--color--inactive);
|
|
60
52
|
}
|
|
61
53
|
|
|
54
|
+
&.invalid {
|
|
55
|
+
--background-color: var(--color--foreground);
|
|
56
|
+
--shadow--color: var(--box-shadow--color--inverse);
|
|
57
|
+
|
|
58
|
+
color: var(--color--white);
|
|
59
|
+
}
|
|
60
|
+
|
|
62
61
|
&.highlighted {
|
|
63
62
|
--background-color: var(--color--primary);
|
|
64
63
|
|
|
@@ -114,23 +113,29 @@
|
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
.alert {
|
|
117
|
-
|
|
116
|
+
--size: 30%;
|
|
118
117
|
|
|
119
118
|
position: absolute;
|
|
120
|
-
width:
|
|
121
|
-
height:
|
|
122
|
-
background
|
|
119
|
+
width: var(--size);
|
|
120
|
+
height: var(--size);
|
|
121
|
+
background: radial-gradient(
|
|
122
|
+
var(--color--error--opposite),
|
|
123
|
+
var(--color--error--opposite) 85%,
|
|
124
|
+
transparent 85%,
|
|
125
|
+
transparent
|
|
126
|
+
);
|
|
123
127
|
color: var(--color--error);
|
|
128
|
+
pointer-events: none;
|
|
124
129
|
|
|
125
130
|
[dir='ltr'] & {
|
|
126
131
|
top: 0;
|
|
127
132
|
right: 0;
|
|
128
|
-
border-
|
|
133
|
+
border-bottom-right-radius: inherit;
|
|
129
134
|
}
|
|
130
135
|
|
|
131
136
|
[dir='rtl'] & {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
border-
|
|
137
|
+
top: 0;
|
|
138
|
+
left: 0;
|
|
139
|
+
border-top-left-radius: inherit;
|
|
135
140
|
}
|
|
136
141
|
}
|
|
@@ -59,11 +59,8 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
59
59
|
}) => {
|
|
60
60
|
const locale = useTypedSelector(selectLocale);
|
|
61
61
|
const { animateTile, showTilePoints } = useAppLayout();
|
|
62
|
-
const { pointsFontSize,
|
|
63
|
-
const style = useMemo(
|
|
64
|
-
() => ({ fontSize: tileFontSize, height: tileSize, width: tileSize }),
|
|
65
|
-
[tileSize, tileFontSize],
|
|
66
|
-
);
|
|
62
|
+
const { pointsFontSize, tileSize } = getTileSizes(size);
|
|
63
|
+
const style = useMemo(() => ({ height: tileSize, width: tileSize }), [tileSize]);
|
|
67
64
|
const pointsStyle = useMemo(() => ({ fontSize: pointsFontSize }), [pointsFontSize]);
|
|
68
65
|
const ref = useRef<HTMLInputElement>(null);
|
|
69
66
|
const mergedRef = useMergeRefs(inputRef ? [ref, inputRef] : [ref]);
|
|
@@ -61,10 +61,9 @@ const TilePure: FunctionComponent<Props> = ({
|
|
|
61
61
|
<div
|
|
62
62
|
className={classNames(styles.tile, className, {
|
|
63
63
|
[styles.blank]: isBlank,
|
|
64
|
-
[styles.disabled]: disabled,
|
|
65
64
|
[styles.empty]: !character,
|
|
65
|
+
[styles.invalid]: !isValid,
|
|
66
66
|
[styles.highlighted]: highlighted,
|
|
67
|
-
[styles.placeholder]: !character,
|
|
68
67
|
[styles.points1]: points === 1,
|
|
69
68
|
[styles.points2]: points === 2,
|
|
70
69
|
[styles.points3]: points === 3,
|
package/src/i18n/constants.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { Locale } from '@scrabble-solver/types';
|
|
2
|
+
import { FunctionComponent, SVGAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
import { FlagDe, FlagEs, FlagFa, FlagFr, FlagGb, FlagPl, FlagUs } from 'icons';
|
|
5
|
+
|
|
6
|
+
import styles from './i18n.module.scss';
|
|
2
7
|
|
|
3
8
|
interface LocaleFeatures {
|
|
4
9
|
direction: 'ltr' | 'rtl';
|
|
@@ -51,3 +56,63 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
|
|
|
51
56
|
vowels: true,
|
|
52
57
|
},
|
|
53
58
|
};
|
|
59
|
+
|
|
60
|
+
interface Flag {
|
|
61
|
+
className: string;
|
|
62
|
+
Icon: FunctionComponent<SVGAttributes<SVGElement>>;
|
|
63
|
+
label: string;
|
|
64
|
+
name: string;
|
|
65
|
+
value: Locale;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const LOCALE_FLAGS: Record<Locale, Flag> = {
|
|
69
|
+
[Locale.EN_GB]: {
|
|
70
|
+
className: styles.gb,
|
|
71
|
+
Icon: FlagGb,
|
|
72
|
+
label: 'English (GB)',
|
|
73
|
+
name: 'English (GB)',
|
|
74
|
+
value: Locale.EN_GB,
|
|
75
|
+
},
|
|
76
|
+
[Locale.EN_US]: {
|
|
77
|
+
className: styles.us,
|
|
78
|
+
Icon: FlagUs,
|
|
79
|
+
label: 'English (US)',
|
|
80
|
+
name: 'English (US)',
|
|
81
|
+
value: Locale.EN_US,
|
|
82
|
+
},
|
|
83
|
+
[Locale.FA_IR]: {
|
|
84
|
+
className: styles.fa,
|
|
85
|
+
Icon: FlagFa,
|
|
86
|
+
label: 'فارسی',
|
|
87
|
+
name: 'Persian',
|
|
88
|
+
value: Locale.FA_IR,
|
|
89
|
+
},
|
|
90
|
+
[Locale.FR_FR]: {
|
|
91
|
+
className: styles.fr,
|
|
92
|
+
Icon: FlagFr,
|
|
93
|
+
label: 'Français',
|
|
94
|
+
name: 'French',
|
|
95
|
+
value: Locale.FR_FR,
|
|
96
|
+
},
|
|
97
|
+
[Locale.DE_DE]: {
|
|
98
|
+
className: styles.de,
|
|
99
|
+
Icon: FlagDe,
|
|
100
|
+
label: 'Deutsch',
|
|
101
|
+
name: 'German',
|
|
102
|
+
value: Locale.DE_DE,
|
|
103
|
+
},
|
|
104
|
+
[Locale.PL_PL]: {
|
|
105
|
+
className: styles.pl,
|
|
106
|
+
Icon: FlagPl,
|
|
107
|
+
label: 'Polski',
|
|
108
|
+
name: 'Polish',
|
|
109
|
+
value: Locale.PL_PL,
|
|
110
|
+
},
|
|
111
|
+
[Locale.ES_ES]: {
|
|
112
|
+
className: styles.es,
|
|
113
|
+
Icon: FlagEs,
|
|
114
|
+
label: 'Español',
|
|
115
|
+
name: 'Spanish',
|
|
116
|
+
value: Locale.ES_ES,
|
|
117
|
+
},
|
|
118
|
+
};
|
package/src/i18n/de.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dictionary.empty-state.no-definitions": "Wort existiert im Wörterbuch aber hat keine Definition.",
|
|
22
22
|
"dictionary.empty-state.no-results": "Wort kann nicht im Wörterbuch gefunden werden.",
|
|
23
23
|
"dictionary.empty-state.not-allowed": "Dieses Wort ist nicht erlaubt.",
|
|
24
|
-
"dictionary.empty-state.uninitialized": "Die
|
|
24
|
+
"dictionary.empty-state.uninitialized": "Die Wörterbuchdéfinition wird hier angezeigt.",
|
|
25
25
|
"dictionary.input.placeholder": "Durchsuche Wörterbuch...",
|
|
26
26
|
"dictionary.input.title": "Durch Kommas getrennte Wörter",
|
|
27
27
|
"empty-state.error": "Fehler",
|
|
@@ -46,10 +46,11 @@
|
|
|
46
46
|
"remaining-tiles": "Restliche Steine",
|
|
47
47
|
"results": "Ergebnisse",
|
|
48
48
|
"results.empty-state.no-results": "Keine Ergebnisse - kein Wort konnte generiert werden.",
|
|
49
|
-
"results.empty-state.outdated": "Ergebnisse sind alt.
|
|
49
|
+
"results.empty-state.outdated": "Ergebnisse sind alt.",
|
|
50
50
|
"results.empty-state.uninitialized": "Wörter die aus deinen Buchstaben generiert wurden erscheinen hier.",
|
|
51
51
|
"results.input.placeholder": "Suchergebnisse... (RegExp)",
|
|
52
52
|
"results.insert": "Hinzufügen",
|
|
53
|
+
"results.preview": "Vorschau",
|
|
53
54
|
"results.solve": "Lösen",
|
|
54
55
|
"settings": "Einstellungen",
|
|
55
56
|
"settings.autoGroupTiles": "Restliche Steine gruppieren",
|
package/src/i18n/en.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dictionary.empty-state.no-definitions": "Word exists in the dictionary but it does not have a definition.",
|
|
22
22
|
"dictionary.empty-state.no-results": "Unable to find word definition in the dictionary.",
|
|
23
23
|
"dictionary.empty-state.not-allowed": "This word is not allowed.",
|
|
24
|
-
"dictionary.empty-state.uninitialized": "
|
|
24
|
+
"dictionary.empty-state.uninitialized": "Word definition will be shown here.",
|
|
25
25
|
"dictionary.input.placeholder": "Search dictionary...",
|
|
26
26
|
"dictionary.input.title": "Comma-separated words",
|
|
27
27
|
"empty-state.error": "Error",
|
|
@@ -46,10 +46,11 @@
|
|
|
46
46
|
"remaining-tiles": "Remaining tiles",
|
|
47
47
|
"results": "Results",
|
|
48
48
|
"results.empty-state.no-results": "No results - unable to generate any words.",
|
|
49
|
-
"results.empty-state.outdated": "Results are outdated.
|
|
49
|
+
"results.empty-state.outdated": "Results are outdated.",
|
|
50
50
|
"results.empty-state.uninitialized": "Words generated from your letters will be shown here.",
|
|
51
51
|
"results.input.placeholder": "Search results... (RegExp)",
|
|
52
52
|
"results.insert": "Insert",
|
|
53
|
+
"results.preview": "Preview",
|
|
53
54
|
"results.solve": "Solve",
|
|
54
55
|
"settings": "Settings",
|
|
55
56
|
"settings.autoGroupTiles": "Group remaining tiles",
|
package/src/i18n/es.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dictionary.empty-state.no-definitions": "La palabra existe en el diccionario pero no tiene una definición.",
|
|
22
22
|
"dictionary.empty-state.no-results": "No se puede encontrar la definición de palabra en el diccionario.",
|
|
23
23
|
"dictionary.empty-state.not-allowed": "Esta palabra no es aceptable.",
|
|
24
|
-
"dictionary.empty-state.uninitialized": "Aquí se mostrará la definición del diccionario
|
|
24
|
+
"dictionary.empty-state.uninitialized": "Aquí se mostrará la definición del diccionario.",
|
|
25
25
|
"dictionary.input.placeholder": "Busca el diccionario...",
|
|
26
26
|
"dictionary.input.title": "Palabras separadas por comas",
|
|
27
27
|
"empty-state.error": "Error",
|
|
@@ -46,10 +46,11 @@
|
|
|
46
46
|
"remaining-tiles": "Casillas restantes",
|
|
47
47
|
"results": "Resultados",
|
|
48
48
|
"results.empty-state.no-results": "No hay resultados; no se pueden generar palabras",
|
|
49
|
-
"results.empty-state.outdated": "Los resultados están desactualizados.
|
|
49
|
+
"results.empty-state.outdated": "Los resultados están desactualizados.",
|
|
50
50
|
"results.empty-state.uninitialized": "Aquí se mostrarán las palabras generadas a partir de sus letras.",
|
|
51
51
|
"results.input.placeholder": "Busque una solución... (RegExp)",
|
|
52
52
|
"results.insert": "Insertar",
|
|
53
|
+
"results.preview": "Vista previa",
|
|
53
54
|
"results.solve": "Resolver",
|
|
54
55
|
"settings": "Configuración",
|
|
55
56
|
"settings.autoGroupTiles": "Agrupar casillas restantes",
|
package/src/i18n/fa.json
CHANGED
|
@@ -46,10 +46,11 @@
|
|
|
46
46
|
"remaining-tiles": "کاشی های باقی مانده",
|
|
47
47
|
"results": "نتایج",
|
|
48
48
|
"results.empty-state.no-results": "کلمه قابل استفاده پیدا نشد.",
|
|
49
|
-
"results.empty-state.outdated": "نتایج به روز نیستند، برای
|
|
49
|
+
"results.empty-state.outdated": "نتایج به روز نیستند، برای بروز.",
|
|
50
50
|
"results.empty-state.uninitialized": "کلمات تولید شده از حروف شما اینجا نمایش داده خواهد شد.",
|
|
51
51
|
"results.input.placeholder": "جستجو در نتایج (RegExp)",
|
|
52
52
|
"results.insert": "وارد کردن",
|
|
53
|
+
"results.preview": "پیش نمایش",
|
|
53
54
|
"results.solve": "حل کن",
|
|
54
55
|
"settings": "تنظیمات",
|
|
55
56
|
"settings.autoGroupTiles": "کاشی های باقی مانده ی طاقچه را کنار هم قرار بده",
|
package/src/i18n/fr.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dictionary.empty-state.no-definitions": "Le mot existe dans le dictionary mais n'a pas de définition.",
|
|
22
22
|
"dictionary.empty-state.no-results": "Impossible de trouver une définition pour ce mot dans le dictionaire.",
|
|
23
23
|
"dictionary.empty-state.not-allowed": "Ce mot n'est pas pas acceptable.",
|
|
24
|
-
"dictionary.empty-state.uninitialized": "La définition dictionaire
|
|
24
|
+
"dictionary.empty-state.uninitialized": "La définition dictionaire sera affichée ici.",
|
|
25
25
|
"dictionary.input.placeholder": "Rechercher dans le dictionnaire...",
|
|
26
26
|
"dictionary.input.title": "Mots séparées par des virgules",
|
|
27
27
|
"empty-state.error": "Erreur",
|
|
@@ -46,10 +46,11 @@
|
|
|
46
46
|
"remaining-tiles": "Cases restantes",
|
|
47
47
|
"results": "Résultats",
|
|
48
48
|
"results.empty-state.no-results": "Pas de résultats - impossible de générer des mots.",
|
|
49
|
-
"results.empty-state.outdated": "Les résultats sont dépassé.
|
|
49
|
+
"results.empty-state.outdated": "Les résultats sont dépassé.",
|
|
50
50
|
"results.empty-state.uninitialized": "Les mots générés à partir de vos lettres seront affichés ici.",
|
|
51
51
|
"results.input.placeholder": "Rechercher les résultats... (RegExp)",
|
|
52
52
|
"results.insert": "Inserer",
|
|
53
|
+
"results.preview": "Prévisualisation",
|
|
53
54
|
"results.solve": "Résoudre",
|
|
54
55
|
"settings": "Options",
|
|
55
56
|
"settings.autoGroupTiles": "Grouper les cases restantes",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.de {
|
|
2
|
+
--aspect-ratio: var(--flag--de--aspect-ratio);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.es {
|
|
6
|
+
--aspect-ratio: var(--flag--es--aspect-ratio);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.fa {
|
|
10
|
+
--aspect-ratio: var(--flag--fa--aspect-ratio);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.fr {
|
|
14
|
+
--aspect-ratio: var(--flag--fr--aspect-ratio);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.gb {
|
|
18
|
+
--aspect-ratio: var(--flag--gb--aspect-ratio);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.pl {
|
|
22
|
+
--aspect-ratio: var(--flag--pl--aspect-ratio);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.us {
|
|
26
|
+
--aspect-ratio: var(--flag--us--aspect-ratio);
|
|
27
|
+
}
|
package/src/i18n/pl.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dictionary.empty-state.no-definitions": "Słowo istnieje w słowniku ale nie posiada definicji.",
|
|
22
22
|
"dictionary.empty-state.no-results": "Nie udało się znaleźć definicji słowa w słowniku.",
|
|
23
23
|
"dictionary.empty-state.not-allowed": "To słowo nie jest dopuszczalne w grach.",
|
|
24
|
-
"dictionary.empty-state.uninitialized": "Tu zostanie wyświetlona
|
|
24
|
+
"dictionary.empty-state.uninitialized": "Tu zostanie wyświetlona definicja słowa.",
|
|
25
25
|
"dictionary.input.placeholder": "Szukaj w słowniku...",
|
|
26
26
|
"dictionary.input.title": "Słowa rozdzielone przecinkiem",
|
|
27
27
|
"empty-state.error": "Błąd",
|
|
@@ -46,10 +46,11 @@
|
|
|
46
46
|
"remaining-tiles": "Pozostałe płytki",
|
|
47
47
|
"results": "Wyniki",
|
|
48
48
|
"results.empty-state.no-results": "Brak wyników - nie można wygenerować żadnego słowa.",
|
|
49
|
-
"results.empty-state.outdated": "Wyniki są nieaktualne.
|
|
49
|
+
"results.empty-state.outdated": "Wyniki są nieaktualne.",
|
|
50
50
|
"results.empty-state.uninitialized": "Tu zostaną wyświetlone słowa wygenerowane z Twoich liter.",
|
|
51
51
|
"results.input.placeholder": "Szukaj rozwiązania... (RegExp)",
|
|
52
52
|
"results.insert": "Wstaw",
|
|
53
|
+
"results.preview": "Podgląd",
|
|
53
54
|
"results.solve": "Rozwiąż",
|
|
54
55
|
"settings": "Opcje",
|
|
55
56
|
"settings.autoGroupTiles": "Grupuj pozostałe płytki",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- https://icons.getbootstrap.com/icons/eye-fill/ -->
|
|
2
|
+
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z" fill="currentColor" />
|
|
4
|
+
<path d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8zm8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z" fill="currentColor" />
|
|
5
|
+
</svg>
|
package/src/icons/index.ts
CHANGED
|
@@ -18,15 +18,16 @@ export { default as DashCircleFill } from './DashCircleFill.svg';
|
|
|
18
18
|
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
|
+
export { default as EyeFill } from './EyeFill.svg';
|
|
21
22
|
export { default as Flag } from './Flag.svg';
|
|
22
|
-
export { default as
|
|
23
|
+
export { default as FlagDe } from './FlagDe.svg';
|
|
23
24
|
export { default as FlagEs } from './FlagEs.svg';
|
|
24
25
|
export { default as FlagFa } from './FlagFa.svg';
|
|
26
|
+
export { default as FlagFill } from './FlagFill.svg';
|
|
25
27
|
export { default as FlagFr } from './FlagFr.svg';
|
|
26
28
|
export { default as FlagGb } from './FlagGb.svg';
|
|
27
29
|
export { default as FlagPl } from './FlagPl.svg';
|
|
28
30
|
export { default as FlagUs } from './FlagUs.svg';
|
|
29
|
-
export { default as FlagDe } from './FlagDe.svg';
|
|
30
31
|
export { default as Github } from './Github.svg';
|
|
31
32
|
export { default as InfoCircleFill } from './InfoCircleFill.svg';
|
|
32
33
|
export { default as Keyboard } from './Keyboard.svg';
|
|
@@ -8,10 +8,26 @@
|
|
|
8
8
|
&:active,
|
|
9
9
|
&:focus,
|
|
10
10
|
&:hover {
|
|
11
|
-
box-shadow:
|
|
11
|
+
box-shadow: 0 1px 1px 0 var(--box-shadow--color);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
& + & {
|
|
15
15
|
margin-top: var(--spacing--l);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
.settings {
|
|
20
|
+
width: 100%;
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: space-between;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.flag {
|
|
27
|
+
--height: var(--button--icon--size);
|
|
28
|
+
|
|
29
|
+
width: calc(var(--height) * var(--aspect--ratio));
|
|
30
|
+
height: var(--height);
|
|
31
|
+
border-radius: var(--border--radius);
|
|
32
|
+
box-shadow: 0 0 0 1px var(--box-shadow--color);
|
|
33
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { FunctionComponent } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Button, Modal } from 'components';
|
|
4
|
+
import { LOCALE_FLAGS } from 'i18n';
|
|
4
5
|
import { BookHalf, CardChecklist, Cog, Github, Sack } from 'icons';
|
|
5
6
|
import { GITHUB_PROJECT_URL } from 'parameters';
|
|
6
|
-
import { useTranslate } from 'state';
|
|
7
|
+
import { selectLocale, useTranslate, useTypedSelector } from 'state';
|
|
7
8
|
|
|
8
9
|
import styles from './MenuModal.module.scss';
|
|
9
10
|
|
|
@@ -27,6 +28,8 @@ const Menu: FunctionComponent<Props> = ({
|
|
|
27
28
|
onShowWords,
|
|
28
29
|
}) => {
|
|
29
30
|
const translate = useTranslate();
|
|
31
|
+
const locale = useTypedSelector(selectLocale);
|
|
32
|
+
const Flag = LOCALE_FLAGS[locale];
|
|
30
33
|
|
|
31
34
|
return (
|
|
32
35
|
<Modal className={className} isOpen={isOpen} title={translate('menu')} onClose={onClose}>
|
|
@@ -59,7 +62,10 @@ const Menu: FunctionComponent<Props> = ({
|
|
|
59
62
|
</Button.Link>
|
|
60
63
|
|
|
61
64
|
<Button aria-label={translate('settings')} className={styles.button} Icon={Cog} onClick={onShowSettings}>
|
|
62
|
-
{
|
|
65
|
+
<div className={styles.settings}>
|
|
66
|
+
<span>{translate('settings')}</span>
|
|
67
|
+
<Flag.Icon className={styles.flag} />
|
|
68
|
+
</div>
|
|
63
69
|
</Button>
|
|
64
70
|
</Modal>
|
|
65
71
|
);
|