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/lerna.json +1 -1
- package/package.json +1 -1
- package/packages/configs/package.json +3 -3
- 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 +6 -6
- 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/210.js +109 -0
- package/packages/scrabble-solver/.next/server/chunks/277.js +187 -95
- package/packages/scrabble-solver/.next/server/chunks/44.js +47 -0
- package/packages/scrabble-solver/.next/server/chunks/987.js +91 -0
- 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 +1 -1
- package/packages/scrabble-solver/.next/server/pages/_app.js +1 -73
- package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/_document.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js +3 -3
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js +3 -3
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/solve.js +10 -6
- package/packages/scrabble-solver/.next/server/pages/api/solve.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/verify.js +5 -4
- package/packages/scrabble-solver/.next/server/pages/api/verify.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/visit.js +3 -3
- package/packages/scrabble-solver/.next/server/pages/api/visit.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.js +104 -207
- 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/static/{Mdvi3FY0PqkILKLbPlVBU → USLkKOoHbITebIEHkMGX_}/_buildManifest.js +1 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-21c83ddb81fc09d0.js +28 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/index-0858deea02b2a417.js +1 -0
- package/packages/scrabble-solver/.next/static/css/885da289cec275b3.css +1 -0
- package/packages/scrabble-solver/.next/static/css/ea1c8134fe9a143e.css +2 -0
- package/packages/scrabble-solver/.next/trace +53 -54
- package/packages/scrabble-solver/package-lock.json +2 -2
- package/packages/scrabble-solver/package.json +8 -8
- package/packages/scrabble-solver/src/api/index.ts +1 -0
- package/packages/scrabble-solver/src/api/isCellValid.ts +3 -2
- package/packages/scrabble-solver/src/api/isCharacterValid.ts +13 -0
- package/packages/scrabble-solver/src/components/Button/Button.module.scss +14 -1
- package/packages/scrabble-solver/src/components/Modal/Modal.module.scss +11 -3
- package/packages/scrabble-solver/src/components/NotFound/NotFound.module.scss +13 -4
- package/packages/scrabble-solver/src/components/NotFound/NotFound.tsx +4 -7
- package/packages/scrabble-solver/src/components/Rack/Rack.tsx +3 -1
- package/packages/scrabble-solver/src/components/Tile/Tile.module.scss +23 -18
- package/packages/scrabble-solver/src/components/Tile/Tile.tsx +2 -5
- package/packages/scrabble-solver/src/components/Tile/TilePure.tsx +1 -2
- package/packages/scrabble-solver/src/i18n/constants.ts +65 -0
- package/packages/scrabble-solver/src/i18n/de.json +1 -0
- package/packages/scrabble-solver/src/i18n/en.json +1 -0
- package/packages/scrabble-solver/src/i18n/es.json +1 -0
- package/packages/scrabble-solver/src/i18n/fa.json +1 -0
- package/packages/scrabble-solver/src/i18n/fr.json +1 -0
- package/packages/scrabble-solver/src/i18n/i18n.module.scss +27 -0
- package/packages/scrabble-solver/src/i18n/pl.json +1 -0
- package/packages/scrabble-solver/src/icons/DashCircleFill.svg +1 -0
- package/packages/scrabble-solver/src/icons/EyeFill.svg +5 -0
- package/packages/scrabble-solver/src/icons/index.ts +3 -2
- package/packages/scrabble-solver/src/modals/MenuModal/MenuModal.module.scss +17 -1
- package/packages/scrabble-solver/src/modals/MenuModal/MenuModal.tsx +8 -2
- package/packages/scrabble-solver/src/modals/RemainingTilesModal/RemainingTilesModal.tsx +4 -1
- package/packages/scrabble-solver/src/modals/RemainingTilesModal/components/Character/Character.module.scss +8 -0
- package/packages/scrabble-solver/src/modals/ResultsModal/ResultsModal.tsx +35 -11
- package/packages/scrabble-solver/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +3 -44
- package/packages/scrabble-solver/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.tsx +4 -2
- package/packages/scrabble-solver/src/pages/api/solve.ts +3 -2
- package/packages/scrabble-solver/src/styles/mixins.scss +2 -0
- package/packages/scrabble-solver/src/styles/variables.scss +12 -0
- package/packages/scrabble-solver/src/types/index.ts +1 -0
- package/packages/solver/package-lock.json +2 -2
- package/packages/solver/package.json +5 -5
- 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/server/chunks/417.js +0 -221
- package/packages/scrabble-solver/.next/server/chunks/664.js +0 -621
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-495e6f4ccc278bb2.js +0 -28
- package/packages/scrabble-solver/.next/static/chunks/pages/index-5ecc51900ca29685.js +0 -1
- package/packages/scrabble-solver/.next/static/css/17b0a2db8742105f.css +0 -1
- package/packages/scrabble-solver/.next/static/css/e1ffeb2558330c55.css +0 -2
- package/packages/scrabble-solver/src/modals/SettingsModal/components/LocaleSetting/options.ts +0 -68
- /package/packages/scrabble-solver/.next/static/{Mdvi3FY0PqkILKLbPlVBU → USLkKOoHbITebIEHkMGX_}/_ssgManifest.js +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.3",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@scrabble-solver/scrabble-solver",
|
|
9
|
-
"version": "2.11.
|
|
9
|
+
"version": "2.11.3",
|
|
10
10
|
"license": "CC BY-NC-ND 4.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@floating-ui/react": "^0.20.1",
|
|
@@ -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",
|
|
@@ -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';
|
|
@@ -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,
|
|
@@ -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
|
+
};
|
|
@@ -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",
|
|
@@ -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",
|
|
@@ -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",
|
|
@@ -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": "کاشی های باقی مانده ی طاقچه را کنار هم قرار بده",
|
|
@@ -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
|
+
}
|
|
@@ -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>
|
|
@@ -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
|
}
|