@scrabble-solver/scrabble-solver 2.11.2 → 2.11.3
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 +187 -95
- package/.next/server/chunks/44.js +47 -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/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 → USLkKOoHbITebIEHkMGX_}/_buildManifest.js +1 -1
- package/.next/static/chunks/pages/_app-21c83ddb81fc09d0.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 +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/i18n.module.scss +27 -0
- package/src/i18n/pl.json +1 -0
- 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 → USLkKOoHbITebIEHkMGX_}/_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.3",
|
|
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.3",
|
|
35
|
+
"@scrabble-solver/constants": "^2.11.3",
|
|
36
|
+
"@scrabble-solver/dictionaries": "^2.11.3",
|
|
37
|
+
"@scrabble-solver/logger": "^2.11.3",
|
|
38
|
+
"@scrabble-solver/solver": "^2.11.3",
|
|
39
|
+
"@scrabble-solver/types": "^2.11.3",
|
|
40
|
+
"@scrabble-solver/word-definitions": "^2.11.3",
|
|
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": "693e06a0f1375268d31d12c1a24924bf5ca3c267"
|
|
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
|
@@ -50,6 +50,7 @@
|
|
|
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
|
@@ -50,6 +50,7 @@
|
|
|
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
|
@@ -50,6 +50,7 @@
|
|
|
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
|
@@ -50,6 +50,7 @@
|
|
|
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
|
@@ -50,6 +50,7 @@
|
|
|
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
|
@@ -50,6 +50,7 @@
|
|
|
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
|
);
|
|
@@ -2,6 +2,8 @@ import { FunctionComponent } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { Badge, Modal } from 'components';
|
|
4
4
|
import { LOCALE_FEATURES } from 'i18n';
|
|
5
|
+
import { getTileSizes } from 'lib';
|
|
6
|
+
import { REMAINING_TILES_TILE_SIZE } from 'parameters';
|
|
5
7
|
import { selectLocale, selectRemainingTilesGroups, useTranslate, useTypedSelector } from 'state';
|
|
6
8
|
|
|
7
9
|
import { Character } from './components';
|
|
@@ -17,6 +19,7 @@ const RemainingTilesModal: FunctionComponent<Props> = ({ className, isOpen, onCl
|
|
|
17
19
|
const translate = useTranslate();
|
|
18
20
|
const locale = useTypedSelector(selectLocale);
|
|
19
21
|
const groups = useTypedSelector(selectRemainingTilesGroups);
|
|
22
|
+
const { tileFontSize } = getTileSizes(REMAINING_TILES_TILE_SIZE);
|
|
20
23
|
const { direction } = LOCALE_FEATURES[locale];
|
|
21
24
|
|
|
22
25
|
return (
|
|
@@ -37,7 +40,7 @@ const RemainingTilesModal: FunctionComponent<Props> = ({ className, isOpen, onCl
|
|
|
37
40
|
</span>
|
|
38
41
|
}
|
|
39
42
|
>
|
|
40
|
-
<div className={styles.content}>
|
|
43
|
+
<div className={styles.content} style={{ fontSize: tileFontSize }}>
|
|
41
44
|
{tiles.map((tile) => {
|
|
42
45
|
return (
|
|
43
46
|
<div className={styles.character} key={tile.character}>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@import 'styles/mixins';
|
|
2
|
+
|
|
1
3
|
.character {
|
|
2
4
|
display: flex;
|
|
3
5
|
flex-direction: column;
|
|
@@ -29,9 +31,15 @@
|
|
|
29
31
|
.remaining {
|
|
30
32
|
height: 6px;
|
|
31
33
|
margin-top: var(--spacing--m);
|
|
34
|
+
box-shadow: var(--box-shadow--raised);
|
|
35
|
+
|
|
36
|
+
@include media('<xs') {
|
|
37
|
+
box-shadow: var(--box-shadow--raised--subtle);
|
|
38
|
+
}
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
.count {
|
|
35
42
|
padding: var(--spacing--xs) 0;
|
|
43
|
+
font-size: var(--font--size--m);
|
|
36
44
|
white-space: nowrap;
|
|
37
45
|
}
|