@scrabble-solver/scrabble-solver 2.13.10 → 2.13.12
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/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/prerender-manifest.js +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/routes-manifest.json +1 -1
- package/.next/server/chunks/807.js +1 -1
- package/.next/server/chunks/911.js +1 -1
- package/.next/server/chunks/977.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages/api/dictionary/[locale]/[word].js +1 -1
- package/.next/server/pages/api/solve.js +1 -1
- package/.next/server/pages/api/verify.js +1 -1
- package/.next/server/pages/index.html +1 -1
- package/.next/server/pages/index.js +1 -1
- package/.next/server/pages/index.json +1 -1
- package/.next/static/{0kOqO_aASkcT2xjhiptyo → N8hSsS6Ppzlj3ebHMSZvI}/_buildManifest.js +1 -1
- package/.next/static/chunks/pages/{404-b447c5ca188dd7c1.js → 404-0c9f3e0f8b15f487.js} +1 -1
- package/.next/static/chunks/pages/_app-8246f5b39b6a5e59.js +17 -0
- package/.next/static/chunks/pages/index-65bfe83d121535ab.js +1 -0
- package/.next/static/css/{841a5b5f0b2fb131.css → 2f727b21d1331ea5.css} +2 -2
- package/.next/trace +45 -44
- package/package.json +9 -9
- package/src/api/isBoardValid.ts +1 -1
- package/src/api/isCellValid.ts +2 -2
- package/src/api/isRowValid.ts +1 -1
- package/src/components/Board/components/Actions/Actions.tsx +15 -3
- package/src/components/Board/components/Cell/Cell.tsx +1 -1
- package/src/components/Board/components/ToggleDirectionButton/ToggleDirectionButton.tsx +8 -1
- package/src/components/Board/hooks/useBackgroundImage.tsx +3 -3
- package/src/components/Board/hooks/useBoardStyle.ts +3 -5
- package/src/components/Board/hooks/useGrid.ts +13 -9
- package/src/components/Key/Key.module.scss +3 -2
- package/src/components/Keys/Arrows/Arrows.module.scss +41 -0
- package/src/components/Keys/Arrows/Arrows.tsx +31 -0
- package/src/components/Keys/Arrows/index.ts +1 -0
- package/src/components/Keys/index.ts +1 -0
- package/src/components/Rack/components/InputPrompt/InputPrompt.tsx +1 -1
- package/src/components/Results/HeaderButton.tsx +3 -6
- package/src/components/Results/Results.module.scss +2 -6
- package/src/components/Results/Results.tsx +1 -1
- package/src/components/Results/getCoordinatesColumn.ts +0 -1
- package/src/components/Results/getLocaleColumns.ts +0 -7
- package/src/components/Results/types.ts +0 -1
- package/src/components/index.ts +2 -0
- package/src/components/keys.tsx +26 -0
- package/src/hooks/useAppLayout.ts +8 -8
- package/src/i18n/languages/english.json +4 -1
- package/src/i18n/languages/french.json +4 -1
- package/src/i18n/languages/german.json +4 -1
- package/src/i18n/languages/persian.json +4 -1
- package/src/i18n/languages/polish.json +4 -1
- package/src/i18n/languages/romanian.json +3 -0
- package/src/i18n/languages/spanish.json +4 -1
- package/src/lib/getCellSize.ts +2 -2
- package/src/lib/sortGroupedResults.ts +4 -3
- package/src/lib/sortResults.ts +17 -5
- package/src/modals/KeyMapModal/KeyMapModal.tsx +19 -9
- package/src/modals/SettingsModal/components/ShowCoordinatesSetting/ShowCoordinatesSetting.tsx +1 -1
- package/src/pages/api/dictionary/[locale]/[word].ts +1 -4
- package/src/state/sagas.ts +6 -6
- package/src/state/selectors.ts +1 -1
- package/src/state/slices/boardInitialState.ts +2 -2
- package/src/state/slices/boardSlice.ts +2 -2
- package/src/styles/variables.scss +2 -2
- package/src/types/index.ts +4 -1
- package/.next/static/chunks/pages/_app-0bbddaa93fde16ea.js +0 -17
- package/.next/static/chunks/pages/index-24b84719cf22731c.js +0 -1
- package/src/modals/KeyMapModal/keys.tsx +0 -46
- /package/.next/static/{0kOqO_aASkcT2xjhiptyo → N8hSsS6Ppzlj3ebHMSZvI}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.12",
|
|
4
4
|
"description": "Scrabble Solver 2 - App",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16"
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"@floating-ui/react": "^0.26.17",
|
|
31
31
|
"@kamilmielnik/trie": "^3.0.0",
|
|
32
32
|
"@reduxjs/toolkit": "^2.2.5",
|
|
33
|
-
"@scrabble-solver/configs": "^2.13.
|
|
34
|
-
"@scrabble-solver/constants": "^2.13.
|
|
35
|
-
"@scrabble-solver/dictionaries": "^2.13.
|
|
36
|
-
"@scrabble-solver/logger": "^2.13.
|
|
37
|
-
"@scrabble-solver/solver": "^2.13.
|
|
38
|
-
"@scrabble-solver/types": "^2.13.
|
|
39
|
-
"@scrabble-solver/word-definitions": "^2.13.
|
|
33
|
+
"@scrabble-solver/configs": "^2.13.12",
|
|
34
|
+
"@scrabble-solver/constants": "^2.13.12",
|
|
35
|
+
"@scrabble-solver/dictionaries": "^2.13.12",
|
|
36
|
+
"@scrabble-solver/logger": "^2.13.12",
|
|
37
|
+
"@scrabble-solver/solver": "^2.13.12",
|
|
38
|
+
"@scrabble-solver/types": "^2.13.12",
|
|
39
|
+
"@scrabble-solver/word-definitions": "^2.13.12",
|
|
40
40
|
"classnames": "^2.5.1",
|
|
41
41
|
"env-cmd": "^10.1.0",
|
|
42
42
|
"include-media": "^2.0.0",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"@types/redux-saga": "^0.10.5",
|
|
74
74
|
"sass": "^1.77.5"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "092d0fb2988a32cfd29384d6d075e106e0042b11"
|
|
77
77
|
}
|
package/src/api/isBoardValid.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { BoardJson, CellJson, Config } from '@scrabble-solver/types';
|
|
|
3
3
|
import isRowValid from './isRowValid';
|
|
4
4
|
|
|
5
5
|
const isBoardValid = (board: BoardJson, config: Config): boolean => {
|
|
6
|
-
if (board.length !== config.
|
|
6
|
+
if (board.length !== config.boardSize) {
|
|
7
7
|
return false;
|
|
8
8
|
}
|
|
9
9
|
|
package/src/api/isCellValid.ts
CHANGED
|
@@ -5,11 +5,11 @@ import isCharacterValid from './isCharacterValid';
|
|
|
5
5
|
const isCellValid = (cell: CellJson, config: Config): boolean => {
|
|
6
6
|
const { isEmpty, tile, x, y } = cell;
|
|
7
7
|
|
|
8
|
-
if (x < 0 || x >= config.
|
|
8
|
+
if (x < 0 || x >= config.boardSize) {
|
|
9
9
|
return false;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
if (y < 0 || y >= config.
|
|
12
|
+
if (y < 0 || y >= config.boardSize) {
|
|
13
13
|
return false;
|
|
14
14
|
}
|
|
15
15
|
|
package/src/api/isRowValid.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { CellJson, Config } from '@scrabble-solver/types';
|
|
|
3
3
|
import isCellValid from './isCellValid';
|
|
4
4
|
|
|
5
5
|
const isRowValid = (row: CellJson[], config: Config): boolean => {
|
|
6
|
-
if (row.length !== config.
|
|
6
|
+
if (row.length !== config.boardSize) {
|
|
7
7
|
return false;
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -3,8 +3,9 @@ import { Cell } from '@scrabble-solver/types';
|
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
import { forwardRef, HTMLProps, MouseEventHandler } from 'react';
|
|
5
5
|
|
|
6
|
+
import { useIsTouchDevice } from 'hooks';
|
|
6
7
|
import { Keyboard, Square, SquareFill } from 'icons';
|
|
7
|
-
import { findCell } from 'lib';
|
|
8
|
+
import { findCell, isMac } from 'lib';
|
|
8
9
|
import { selectCellFilter, selectInputMode, selectResultCandidateCells, useTranslate, useTypedSelector } from 'state';
|
|
9
10
|
import { Direction } from 'types';
|
|
10
11
|
|
|
@@ -29,6 +30,7 @@ const Actions = forwardRef<HTMLDivElement, Props>(
|
|
|
29
30
|
ref,
|
|
30
31
|
) => {
|
|
31
32
|
const translate = useTranslate();
|
|
33
|
+
const isTouchDevice = useIsTouchDevice();
|
|
32
34
|
const inputMode = useTypedSelector(selectInputMode);
|
|
33
35
|
const filter = useTypedSelector((state) => selectCellFilter(state, cell));
|
|
34
36
|
const resultCandidateCells = useTypedSelector(selectResultCandidateCells);
|
|
@@ -66,7 +68,12 @@ const Actions = forwardRef<HTMLDivElement, Props>(
|
|
|
66
68
|
aria-label={translate(labelTranslationKey)}
|
|
67
69
|
className={classNames(styles.action)}
|
|
68
70
|
Icon={Icon}
|
|
69
|
-
tooltip={
|
|
71
|
+
tooltip={
|
|
72
|
+
<>
|
|
73
|
+
<span>{translate(labelTranslationKey)}</span>
|
|
74
|
+
{!isTouchDevice && <span> ({isMac() ? '⌘' : 'Ctrl'} + G)</span>}
|
|
75
|
+
</>
|
|
76
|
+
}
|
|
70
77
|
onClick={onToggleFilterCell}
|
|
71
78
|
onMouseDown={handleMouseDown}
|
|
72
79
|
/>
|
|
@@ -77,7 +84,12 @@ const Actions = forwardRef<HTMLDivElement, Props>(
|
|
|
77
84
|
aria-label={isBlank ? translate('cell.set-not-blank') : translate('cell.set-blank')}
|
|
78
85
|
className={styles.action}
|
|
79
86
|
Icon={isBlank ? SquareFill : Square}
|
|
80
|
-
tooltip={
|
|
87
|
+
tooltip={
|
|
88
|
+
<>
|
|
89
|
+
<span>{isBlank ? translate('cell.set-not-blank') : translate('cell.set-blank')}</span>
|
|
90
|
+
{!isTouchDevice && <span> ({translate('common.space')})</span>}
|
|
91
|
+
</>
|
|
92
|
+
}
|
|
81
93
|
onClick={onToggleBlank}
|
|
82
94
|
onMouseDown={handleMouseDown}
|
|
83
95
|
/>
|
|
@@ -101,7 +101,7 @@ const Cell: FunctionComponent<Props> = ({
|
|
|
101
101
|
})}
|
|
102
102
|
className={classNames(styles.tile, className, {
|
|
103
103
|
[styles.first3]: x < 3,
|
|
104
|
-
[styles.last3]: config.
|
|
104
|
+
[styles.last3]: config.boardSize - x - 1 < 3,
|
|
105
105
|
[styles.sharpTopLeft]: cellTop?.hasTile() || cellLeft?.hasTile(),
|
|
106
106
|
[styles.sharpTopRight]: cellTop?.hasTile() || cellRight?.hasTile(),
|
|
107
107
|
[styles.sharpBottomLeft]: cellBottom?.hasTile() || cellLeft?.hasTile(),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { ButtonHTMLAttributes, FunctionComponent } from 'react';
|
|
3
3
|
|
|
4
|
+
import { useIsTouchDevice } from 'hooks';
|
|
4
5
|
import { ArrowDown } from 'icons';
|
|
5
6
|
import { useTranslate } from 'state';
|
|
6
7
|
import { Direction } from 'types';
|
|
@@ -16,6 +17,7 @@ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
16
17
|
|
|
17
18
|
const ToggleDirectionButton: FunctionComponent<Props> = ({ className, direction, ...props }) => {
|
|
18
19
|
const translate = useTranslate();
|
|
20
|
+
const isTouchDevice = useIsTouchDevice();
|
|
19
21
|
|
|
20
22
|
return (
|
|
21
23
|
<Button
|
|
@@ -25,7 +27,12 @@ const ToggleDirectionButton: FunctionComponent<Props> = ({ className, direction,
|
|
|
25
27
|
iconClassName={classNames(styles.icon, {
|
|
26
28
|
[styles.right]: direction === 'horizontal',
|
|
27
29
|
})}
|
|
28
|
-
tooltip={
|
|
30
|
+
tooltip={
|
|
31
|
+
<>
|
|
32
|
+
<span>{translate('cell.toggle-direction')}</span>
|
|
33
|
+
{!isTouchDevice && <span> ({translate('common.arrows')})</span>}
|
|
34
|
+
</>
|
|
35
|
+
}
|
|
29
36
|
{...props}
|
|
30
37
|
/>
|
|
31
38
|
);
|
|
@@ -32,7 +32,7 @@ const useBackgroundImage = () => {
|
|
|
32
32
|
const { isLessThanXs } = useMediaQueries();
|
|
33
33
|
const borderRadius = isLessThanXs ? BORDER_RADIUS_XS : BORDER_RADIUS;
|
|
34
34
|
const config = useTypedSelector(selectConfig);
|
|
35
|
-
const center = { x: Math.floor(config.
|
|
35
|
+
const center = { x: Math.floor(config.boardSize / 2), y: Math.floor(config.boardSize / 2) };
|
|
36
36
|
const viewBoxHeight = boardSize;
|
|
37
37
|
const viewBoxWidth = boardSize;
|
|
38
38
|
const bonusSize = cellSize * 0.8;
|
|
@@ -157,7 +157,7 @@ const useBackgroundImage = () => {
|
|
|
157
157
|
</>
|
|
158
158
|
)}
|
|
159
159
|
|
|
160
|
-
{Array.from({ length: config.
|
|
160
|
+
{Array.from({ length: config.boardSize - 1 }).map((_value, index) => (
|
|
161
161
|
<use
|
|
162
162
|
key={index}
|
|
163
163
|
href={`#${HORIZONTAL_LINE}`}
|
|
@@ -165,7 +165,7 @@ const useBackgroundImage = () => {
|
|
|
165
165
|
/>
|
|
166
166
|
))}
|
|
167
167
|
|
|
168
|
-
{Array.from({ length: config.
|
|
168
|
+
{Array.from({ length: config.boardSize - 1 }).map((_value, index) => (
|
|
169
169
|
<use
|
|
170
170
|
key={index}
|
|
171
171
|
href={`#${VERTICAL_LINE}`}
|
|
@@ -17,13 +17,11 @@ const useBoardStyle = () => {
|
|
|
17
17
|
backgroundImage,
|
|
18
18
|
fontSize: tileFontSize,
|
|
19
19
|
gridTemplateColumns:
|
|
20
|
-
showCoordinates === 'hidden' ? `repeat(${config.
|
|
20
|
+
showCoordinates === 'hidden' ? `repeat(${config.boardSize}, 1fr)` : `0.5fr repeat(${config.boardSize}, 1fr)`,
|
|
21
21
|
gridTemplateRows:
|
|
22
|
-
showCoordinates === 'hidden'
|
|
23
|
-
? `repeat(${config.boardHeight}, 1fr)`
|
|
24
|
-
: `0.5fr repeat(${config.boardHeight}, 1fr)`,
|
|
22
|
+
showCoordinates === 'hidden' ? `repeat(${config.boardSize}, 1fr)` : `0.5fr repeat(${config.boardSize}, 1fr)`,
|
|
25
23
|
}),
|
|
26
|
-
[backgroundImage, config.
|
|
24
|
+
[backgroundImage, config.boardSize, tileFontSize],
|
|
27
25
|
);
|
|
28
26
|
|
|
29
27
|
return boardStyle;
|
|
@@ -18,7 +18,7 @@ import { useDispatch } from 'react-redux';
|
|
|
18
18
|
import { useLatest } from 'hooks';
|
|
19
19
|
import { LOCALE_FEATURES } from 'i18n';
|
|
20
20
|
import { createGridOf, createKeyboardNavigation, extractCharacters, extractInputValue, isCtrl } from 'lib';
|
|
21
|
-
import { boardSlice, selectConfig, selectLocale, useTypedSelector } from 'state';
|
|
21
|
+
import { boardSlice, cellFilterSlice, selectConfig, selectLocale, useTypedSelector } from 'state';
|
|
22
22
|
import { Direction, Point } from 'types';
|
|
23
23
|
|
|
24
24
|
import { getPositionInGrid } from '../lib';
|
|
@@ -106,7 +106,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
|
|
|
106
106
|
};
|
|
107
107
|
|
|
108
108
|
characters.forEach((character) => {
|
|
109
|
-
if (x >= config.
|
|
109
|
+
if (x >= config.boardSize || y >= config.boardSize) {
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -299,15 +299,8 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
|
|
|
299
299
|
|
|
300
300
|
const { x, y } = position;
|
|
301
301
|
const character = event.key.toLowerCase();
|
|
302
|
-
const isTogglingBlank = isCtrl(event) && character === 'b';
|
|
303
302
|
const twoCharacterTile = config.getTwoCharacterTileByPrefix(character);
|
|
304
303
|
|
|
305
|
-
if (isTogglingBlank) {
|
|
306
|
-
event.preventDefault();
|
|
307
|
-
dispatch(boardSlice.actions.toggleCellIsBlank(position));
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
304
|
if (isCtrl(event) && twoCharacterTile) {
|
|
312
305
|
event.preventDefault();
|
|
313
306
|
dispatch(boardSlice.actions.changeCellValue({ x, y, value: twoCharacterTile }));
|
|
@@ -316,6 +309,17 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
|
|
|
316
309
|
}
|
|
317
310
|
|
|
318
311
|
const cell = rows[y][x];
|
|
312
|
+
|
|
313
|
+
if (isCtrl(event) && character === 'g') {
|
|
314
|
+
event.preventDefault();
|
|
315
|
+
|
|
316
|
+
if (!cell.hasTile()) {
|
|
317
|
+
dispatch(cellFilterSlice.actions.toggle(position));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
319
323
|
const twoCharacterCandidate = cell.tile.character + character;
|
|
320
324
|
|
|
321
325
|
if (config.twoCharacterTiles.includes(twoCharacterCandidate)) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
height: var(--key--height);
|
|
6
6
|
padding: var(--spacing--s) var(--spacing--m);
|
|
7
7
|
background-color: var(--color--white);
|
|
8
|
+
color: var(--color--foreground);
|
|
8
9
|
border: var(--border);
|
|
9
10
|
border-radius: var(--border--radius);
|
|
10
11
|
box-shadow: var(--box-shadow);
|
|
@@ -12,10 +13,10 @@
|
|
|
12
13
|
line-height: calc(var(--key--height) - 2 * (var(--spacing--s) + var(--border--width)));
|
|
13
14
|
vertical-align: middle;
|
|
14
15
|
text-align: center;
|
|
15
|
-
white-space:
|
|
16
|
+
white-space: pre-wrap;
|
|
16
17
|
|
|
17
18
|
& + & {
|
|
18
|
-
margin-
|
|
19
|
+
margin-inline-start: var(--spacing--xs);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
:global(svg) {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
.arrows {
|
|
2
|
+
display: inline-grid;
|
|
3
|
+
grid-template-columns: repeat(3, 1fr);
|
|
4
|
+
grid-template-rows: repeat(2, 1fr);
|
|
5
|
+
gap: var(--spacing--xs);
|
|
6
|
+
gap: 1px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.arrow {
|
|
10
|
+
margin-inline-start: 0 !important;
|
|
11
|
+
max-width: var(--key--height--small);
|
|
12
|
+
padding: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.up {
|
|
16
|
+
grid-row: 1;
|
|
17
|
+
grid-column: 2;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.left {
|
|
21
|
+
grid-row: 2;
|
|
22
|
+
grid-column: 1;
|
|
23
|
+
|
|
24
|
+
[dir='rtl'] & {
|
|
25
|
+
grid-column: 3;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.right {
|
|
30
|
+
grid-row: 2;
|
|
31
|
+
grid-column: 3;
|
|
32
|
+
|
|
33
|
+
[dir='rtl'] & {
|
|
34
|
+
grid-column: 1;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.down {
|
|
39
|
+
grid-row: 2;
|
|
40
|
+
grid-column: 2;
|
|
41
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { FunctionComponent } from 'react';
|
|
3
|
+
|
|
4
|
+
import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp } from 'icons';
|
|
5
|
+
|
|
6
|
+
import Key from '../../Key';
|
|
7
|
+
|
|
8
|
+
import styles from './Arrows.module.scss';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const Arrows: FunctionComponent<Props> = ({ className }) => (
|
|
15
|
+
<div className={classNames(styles.arrows, className)}>
|
|
16
|
+
<Key className={classNames(styles.arrow, styles.left)}>
|
|
17
|
+
<ArrowLeft />
|
|
18
|
+
</Key>
|
|
19
|
+
<Key className={classNames(styles.arrow, styles.up)}>
|
|
20
|
+
<ArrowUp />
|
|
21
|
+
</Key>
|
|
22
|
+
<Key className={classNames(styles.arrow, styles.right)}>
|
|
23
|
+
<ArrowRight />
|
|
24
|
+
</Key>
|
|
25
|
+
<Key className={classNames(styles.arrow, styles.down)}>
|
|
26
|
+
<ArrowDown />
|
|
27
|
+
</Key>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export default Arrows;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Arrows';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Arrows } from './Arrows';
|
|
@@ -39,7 +39,7 @@ const InputPrompt = forwardRef<HTMLFormElement, Props>(
|
|
|
39
39
|
(event) => {
|
|
40
40
|
event.preventDefault();
|
|
41
41
|
const charactersByCase = extractCharactersByCase(config, value);
|
|
42
|
-
const characters = Array.from({ length: config.
|
|
42
|
+
const characters = Array.from({ length: config.rackSize }, (_, index) => {
|
|
43
43
|
return typeof charactersByCase[index] === 'string' ? charactersByCase[index] : null;
|
|
44
44
|
});
|
|
45
45
|
dispatch(rackSlice.actions.changeCharacters({ characters, index: 0 }));
|
|
@@ -13,10 +13,9 @@ import { Column } from './types';
|
|
|
13
13
|
|
|
14
14
|
interface Props {
|
|
15
15
|
column: Column;
|
|
16
|
-
sortable?: boolean;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
const HeaderButton = ({ column
|
|
18
|
+
const HeaderButton = ({ column }: Props): ReactElement => {
|
|
20
19
|
const dispatch = useDispatch();
|
|
21
20
|
const translate = useTranslate();
|
|
22
21
|
const sort = useTypedSelector(selectResultsSort);
|
|
@@ -29,12 +28,10 @@ const HeaderButton = ({ column, sortable }: Props): ReactElement => {
|
|
|
29
28
|
<Tooltip tooltip={translate(column.translationKey)}>
|
|
30
29
|
<button
|
|
31
30
|
aria-label={translate(column.translationKey)}
|
|
32
|
-
className={classNames(styles.headerButton, column.className
|
|
33
|
-
[styles.sortable]: sortable,
|
|
34
|
-
})}
|
|
31
|
+
className={classNames(styles.headerButton, column.className)}
|
|
35
32
|
key={column.id}
|
|
36
33
|
type="button"
|
|
37
|
-
onClick={
|
|
34
|
+
onClick={handleClick}
|
|
38
35
|
>
|
|
39
36
|
<span className={styles.cell}>
|
|
40
37
|
<span className={styles.headerButtonLabel}>{translate(column.translationKey)}</span>
|
|
@@ -52,6 +52,8 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
52
52
|
|
|
53
53
|
.headerButton {
|
|
54
54
|
@include button-reset;
|
|
55
|
+
@include focus-effect;
|
|
56
|
+
cursor: pointer;
|
|
55
57
|
|
|
56
58
|
text-transform: uppercase;
|
|
57
59
|
transition: var(--transition);
|
|
@@ -64,12 +66,6 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
64
66
|
&:last-child {
|
|
65
67
|
border-start-end-radius: inherit;
|
|
66
68
|
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.sortable {
|
|
70
|
-
@include focus-effect;
|
|
71
|
-
|
|
72
|
-
cursor: pointer;
|
|
73
69
|
|
|
74
70
|
&:focus,
|
|
75
71
|
&:hover {
|
|
@@ -73,7 +73,7 @@ const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIn
|
|
|
73
73
|
<div className={classNames(styles.results, className)}>
|
|
74
74
|
<div className={styles.header}>
|
|
75
75
|
{columns.map((column) => (
|
|
76
|
-
<HeaderButton column={column} key={column.id}
|
|
76
|
+
<HeaderButton column={column} key={column.id} />
|
|
77
77
|
))}
|
|
78
78
|
</div>
|
|
79
79
|
|
|
@@ -10,13 +10,11 @@ const getLocaleColumns = (options: { consonants: boolean; vowels: boolean }): Co
|
|
|
10
10
|
className: styles.word,
|
|
11
11
|
id: ResultColumn.Word,
|
|
12
12
|
translationKey: 'common.word',
|
|
13
|
-
sortable: true,
|
|
14
13
|
},
|
|
15
14
|
{
|
|
16
15
|
className: styles.stat,
|
|
17
16
|
id: ResultColumn.TilesCount,
|
|
18
17
|
translationKey: 'common.tiles',
|
|
19
|
-
sortable: true,
|
|
20
18
|
},
|
|
21
19
|
];
|
|
22
20
|
|
|
@@ -25,7 +23,6 @@ const getLocaleColumns = (options: { consonants: boolean; vowels: boolean }): Co
|
|
|
25
23
|
className: styles.stat,
|
|
26
24
|
id: ResultColumn.ConsonantsCount,
|
|
27
25
|
translationKey: 'common.consonants',
|
|
28
|
-
sortable: true,
|
|
29
26
|
});
|
|
30
27
|
}
|
|
31
28
|
|
|
@@ -34,7 +31,6 @@ const getLocaleColumns = (options: { consonants: boolean; vowels: boolean }): Co
|
|
|
34
31
|
className: styles.stat,
|
|
35
32
|
id: ResultColumn.VowelsCount,
|
|
36
33
|
translationKey: 'common.vowels',
|
|
37
|
-
sortable: true,
|
|
38
34
|
});
|
|
39
35
|
}
|
|
40
36
|
|
|
@@ -43,19 +39,16 @@ const getLocaleColumns = (options: { consonants: boolean; vowels: boolean }): Co
|
|
|
43
39
|
className: styles.stat,
|
|
44
40
|
id: ResultColumn.BlanksCount,
|
|
45
41
|
translationKey: 'common.blanks',
|
|
46
|
-
sortable: true,
|
|
47
42
|
},
|
|
48
43
|
{
|
|
49
44
|
className: styles.stat,
|
|
50
45
|
id: ResultColumn.WordsCount,
|
|
51
46
|
translationKey: 'common.words',
|
|
52
|
-
sortable: true,
|
|
53
47
|
},
|
|
54
48
|
{
|
|
55
49
|
className: styles.points,
|
|
56
50
|
id: ResultColumn.Points,
|
|
57
51
|
translationKey: 'common.points',
|
|
58
|
-
sortable: true,
|
|
59
52
|
},
|
|
60
53
|
);
|
|
61
54
|
|
package/src/components/index.ts
CHANGED
|
@@ -22,4 +22,6 @@ export { default as SeoMessage } from './SeoMessage';
|
|
|
22
22
|
export { default as Solver } from './Solver';
|
|
23
23
|
export { default as Spinner } from './Spinner';
|
|
24
24
|
export { default as Tile } from './Tile';
|
|
25
|
+
export * from './keys';
|
|
26
|
+
export * from './Keys';
|
|
25
27
|
export * from './Tooltip';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FunctionComponent } from 'react';
|
|
2
|
+
|
|
3
|
+
import { isMac } from 'lib';
|
|
4
|
+
import { useTranslate } from 'state';
|
|
5
|
+
|
|
6
|
+
import Key from './Key';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Backspace: FunctionComponent<Props> = ({ className }) => <Key className={className}>← Backspace</Key>;
|
|
13
|
+
|
|
14
|
+
export const Ctrl: FunctionComponent<Props> = ({ className }) => (
|
|
15
|
+
<Key className={className}>{isMac() ? '⌘' : 'Ctrl'}</Key>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export const Del: FunctionComponent<Props> = ({ className }) => <Key className={className}>Del</Key>;
|
|
19
|
+
|
|
20
|
+
export const Enter: FunctionComponent<Props> = ({ className }) => <Key className={className}>Enter ⏎</Key>;
|
|
21
|
+
|
|
22
|
+
export const Space: FunctionComponent<Props> = ({ className }) => {
|
|
23
|
+
const translate = useTranslate();
|
|
24
|
+
|
|
25
|
+
return <Key className={className}>{` ${translate('common.space')} `}</Key>;
|
|
26
|
+
};
|
|
@@ -39,7 +39,7 @@ const useAppLayout = () => {
|
|
|
39
39
|
const solverHeight = viewportHeight - navHeight;
|
|
40
40
|
const solverWidth = viewportWidth;
|
|
41
41
|
const maxBoardWidth = solverWidth - columnWidth - (showColumn ? componentsSpacing : 0) - 2 * componentsSpacing;
|
|
42
|
-
const tileSize = Math.min((maxBoardWidth - 2 * BORDER_WIDTH) / config.
|
|
42
|
+
const tileSize = Math.min((maxBoardWidth - 2 * BORDER_WIDTH) / config.rackSize, RACK_TILE_SIZE_MAX);
|
|
43
43
|
const candidatePickerHeight = showResultCandidatePicker ? BUTTON_HEIGHT + componentsSpacing : 0;
|
|
44
44
|
const bottomContainerHeight = candidatePickerHeight + tileSize + 2 * componentsSpacing;
|
|
45
45
|
const maxBoardHeight = isBoardFullWidth
|
|
@@ -49,25 +49,25 @@ const useAppLayout = () => {
|
|
|
49
49
|
const coordinatesSizeRatio = showCoordinates === 'hidden' ? 0 : 0.5;
|
|
50
50
|
const coordinatesBorderWidth = showCoordinates === 'hidden' ? 0 : 1;
|
|
51
51
|
const cellWidth =
|
|
52
|
-
(maxBoardWidth - (config.
|
|
53
|
-
(config.
|
|
52
|
+
(maxBoardWidth - (config.boardSize + 1 + coordinatesBorderWidth) * BORDER_WIDTH) /
|
|
53
|
+
(config.boardSize + coordinatesSizeRatio);
|
|
54
54
|
const cellHeight =
|
|
55
|
-
(maxBoardHeight - (config.
|
|
56
|
-
(config.
|
|
55
|
+
(maxBoardHeight - (config.boardSize + 1 + coordinatesBorderWidth) * BORDER_WIDTH) /
|
|
56
|
+
(config.boardSize + coordinatesSizeRatio);
|
|
57
57
|
const cellSize = Math.min(Math.min(cellWidth, cellHeight), BOARD_TILE_SIZE_MAX);
|
|
58
58
|
const coordinatesSize = coordinatesSizeRatio * cellSize;
|
|
59
59
|
const boardSize =
|
|
60
|
-
(cellSize + BORDER_WIDTH) * config.
|
|
60
|
+
(cellSize + BORDER_WIDTH) * config.boardSize +
|
|
61
61
|
BORDER_WIDTH +
|
|
62
62
|
(showCoordinates === 'hidden' ? 0 : coordinatesSize + BORDER_WIDTH);
|
|
63
|
-
const maxControlsWidth = tileSize * config.
|
|
63
|
+
const maxControlsWidth = tileSize * config.rackSize + 2 * BORDER_WIDTH;
|
|
64
64
|
const showResultsInModal = isLessThanL;
|
|
65
65
|
const dictionaryHeight = showResultsInModal ? DICTIONARY_HEIGHT_MOBILE : DICTIONARY_HEIGHT;
|
|
66
66
|
const modalWidth = isLessThanS ? viewportWidth : MODAL_WIDTH;
|
|
67
67
|
const resultsHeight = isLessThanL
|
|
68
68
|
? viewportHeight - dictionaryHeight - BUTTON_HEIGHT - MODAL_HEADER_HEIGHT - 5 * componentsSpacing
|
|
69
69
|
: boardSize - componentsSpacing - dictionaryHeight;
|
|
70
|
-
const rackWidth = tileSize * config.
|
|
70
|
+
const rackWidth = tileSize * config.rackSize;
|
|
71
71
|
|
|
72
72
|
return {
|
|
73
73
|
actionsWidth: 2 * BUTTON_HEIGHT - BORDER_WIDTH,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"cell.set-not-blank": "Mark it not a blank",
|
|
7
7
|
"cell.tile.location": "Board: tile ({{x}}, {{y}})",
|
|
8
8
|
"cell.toggle-direction": "Typing direction",
|
|
9
|
+
"common.arrows": "Arrow keys",
|
|
9
10
|
"common.blanks": "Blanks",
|
|
10
11
|
"common.clear": "Clear",
|
|
11
12
|
"common.close": "Close",
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
"common.next": "Next",
|
|
15
16
|
"common.points": "Points",
|
|
16
17
|
"common.previous": "Previous",
|
|
18
|
+
"common.space": "Spacebar",
|
|
17
19
|
"common.tiles": "Tiles",
|
|
18
20
|
"common.two-letter-tiles": "Two-letter",
|
|
19
21
|
"common.vowels": "Vowels",
|
|
@@ -34,6 +36,7 @@
|
|
|
34
36
|
"keyMap": "Keyboard shortcuts",
|
|
35
37
|
"keyMap.board": "Board",
|
|
36
38
|
"keyMap.board.toggle-blank": "Mark/unmark tile as a blank",
|
|
39
|
+
"keyMap.board.toggle-cell-filter": "Toggle destination filter",
|
|
37
40
|
"keyMap.board.toggle-direction": "Toggle typing direction",
|
|
38
41
|
"keyMap.board-and-rack": "Board & rack",
|
|
39
42
|
"keyMap.board-and-rack.insert-two-letter-tile": "Insert two-letter tile",
|
|
@@ -41,7 +44,7 @@
|
|
|
41
44
|
"keyMap.board-and-rack.remove-tile": "Remove tile",
|
|
42
45
|
"keyMap.board-and-rack.submit": "Start solving",
|
|
43
46
|
"keyMap.rack": "Rack",
|
|
44
|
-
"keyMap.rack.insert-blank": "Insert blank
|
|
47
|
+
"keyMap.rack.insert-blank": "Insert blank",
|
|
45
48
|
"menu": "Menu",
|
|
46
49
|
"rack.placeholder": "Letters",
|
|
47
50
|
"rack.tile.location": "Rack: tile ({{index}})",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"cell.set-not-blank": "Marquer comme non vide",
|
|
7
7
|
"cell.tile.location": "Plateau: la case ({{x}}, {{y}})",
|
|
8
8
|
"cell.toggle-direction": "Direction d'écriture",
|
|
9
|
+
"common.arrows": "Touches fléchées",
|
|
9
10
|
"common.blanks": "Cases vides",
|
|
10
11
|
"common.clear": "Effacer",
|
|
11
12
|
"common.close": "Fermer",
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
"common.next": "Suivant",
|
|
15
16
|
"common.points": "Points",
|
|
16
17
|
"common.previous": "Précédent",
|
|
18
|
+
"common.space": "Barre d'espace",
|
|
17
19
|
"common.tiles": "Cases",
|
|
18
20
|
"common.two-letter-tiles": "Deux lettres",
|
|
19
21
|
"common.vowels": "Voyelles",
|
|
@@ -34,6 +36,7 @@
|
|
|
34
36
|
"keyMap": "Raccourcis clavier",
|
|
35
37
|
"keyMap.board": "Plateau",
|
|
36
38
|
"keyMap.board.toggle-blank": "Marqué/Démarqué la case en tant que vide",
|
|
39
|
+
"keyMap.board.toggle-cell-filter": "Basculer le filtre de destination",
|
|
37
40
|
"keyMap.board.toggle-direction": "Faire basculer la direction d'écriture",
|
|
38
41
|
"keyMap.board-and-rack": "Plateau & chevalet",
|
|
39
42
|
"keyMap.board-and-rack.insert-two-letter-tile": "Insérer une tuile de deux lettres",
|
|
@@ -41,7 +44,7 @@
|
|
|
41
44
|
"keyMap.board-and-rack.remove-tile": "Supprimer une case",
|
|
42
45
|
"keyMap.board-and-rack.submit": "Commencer la résolution",
|
|
43
46
|
"keyMap.rack": "Chevalet",
|
|
44
|
-
"keyMap.rack.insert-blank": "Inserer une case vide
|
|
47
|
+
"keyMap.rack.insert-blank": "Inserer une case vide",
|
|
45
48
|
"menu": "Menu",
|
|
46
49
|
"rack.placeholder": "Lettres",
|
|
47
50
|
"rack.tile.location": "Chevalet: la case ({{index}})",
|