@scrabble-solver/scrabble-solver 2.11.4 → 2.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +7 -7
- 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/export-marker.json +1 -1
- 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/131.js +1 -1
- package/.next/server/chunks/277.js +851 -1179
- package/.next/server/chunks/636.js +286 -0
- package/.next/server/chunks/675.js +550 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -5
- package/.next/server/pages/404.js.nft.json +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages/_app.js +73 -9
- 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 +1 -280
- package/.next/server/pages/_error.js.nft.json +1 -1
- package/.next/server/pages/api/solve.js +22 -2
- package/.next/server/pages/index.html +1 -1
- package/.next/server/pages/index.js +381 -314
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/index.json +1 -1
- package/.next/server/pages-manifest.json +1 -1
- package/.next/static/Jmk00rVXCbdjFgP77tKXQ/_buildManifest.js +1 -0
- package/.next/static/chunks/pages/{404-448ba28510855455.js → 404-8176f4acd0cfeb42.js} +1 -1
- package/.next/static/chunks/pages/_app-b4fa92112b8f0385.js +28 -0
- package/.next/static/chunks/pages/index-ccd762f8f5028729.js +1 -0
- package/.next/static/css/1cd302e7648d209c.css +2 -0
- package/.next/static/css/34adfcf12a7d9bb6.css +1 -0
- package/.next/trace +50 -53
- package/next.config.js +1 -0
- package/package.json +12 -13
- package/src/@types/svg.d.ts +1 -1
- package/src/components/Board/Board.tsx +48 -44
- package/src/components/Board/components/Actions/Actions.tsx +4 -2
- package/src/components/Board/components/Cell/Cell.module.scss +59 -5
- package/src/components/Board/hooks/useGrid.ts +5 -3
- package/src/components/Button/Button.module.scss +1 -1
- package/src/components/Dictionary/Dictionary.module.scss +0 -1
- package/src/components/EmptyState/EmptyState.module.scss +0 -1
- package/src/components/Key/Key.module.scss +1 -1
- package/src/components/Loading/Loading.module.scss +1 -1
- package/src/components/Loading/Loading.tsx +1 -1
- package/src/components/Logo/Logo.tsx +10 -12
- package/src/components/Logo/LogoBlueprint.tsx +21 -0
- package/src/components/Logo/index.ts +1 -1
- package/src/components/Modal/Modal.module.scss +1 -6
- package/src/components/Modal/Modal.tsx +15 -8
- package/src/components/NavButtons/NavButtons.tsx +2 -2
- package/src/components/PlainTiles/PlainTiles.tsx +0 -10
- package/src/components/PlainTiles/Tile.tsx +1 -4
- package/src/components/Rack/Rack.module.scss +59 -0
- package/src/components/Results/HeaderButton.tsx +6 -6
- package/src/components/Results/Results.module.scss +3 -1
- package/src/components/Results/Results.tsx +7 -7
- package/src/components/Results/useColumns.ts +2 -5
- package/src/components/Solver/Solver.tsx +6 -23
- package/src/components/Tile/Tile.module.scss +2 -1
- package/src/components/Tile/Tile.tsx +8 -4
- package/src/components/index.ts +0 -5
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useAppLayout.ts +62 -12
- package/src/hooks/useDirection.ts +2 -2
- package/src/hooks/useEffectOnce.ts +5 -0
- package/src/hooks/useIsTouchDevice.ts +1 -1
- package/src/hooks/useLanguage.ts +2 -2
- package/src/hooks/useLatest.ts +13 -0
- package/src/hooks/useLocalStorage.ts +51 -0
- package/src/hooks/useMedia.ts +36 -0
- package/src/hooks/useMediaQueries.ts +13 -0
- package/src/hooks/useMediaQuery.ts +2 -1
- package/src/hooks/useOnWindowResize.ts +13 -0
- package/src/hooks/useViewportSize.ts +19 -0
- package/src/i18n/constants.ts +14 -22
- package/src/lib/arrayEquals.ts +5 -0
- package/src/lib/index.ts +1 -0
- package/src/lib/zipCharactersAndTiles.ts +3 -1
- package/src/modals/DictionaryModal/DictionaryModal.tsx +2 -2
- package/src/modals/KeyMapModal/KeyMapModal.tsx +2 -2
- package/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +0 -1
- package/src/modals/KeyMapModal/keys.tsx +0 -2
- package/src/modals/MenuModal/MenuModal.module.scss +28 -4
- package/src/modals/MenuModal/MenuModal.tsx +4 -4
- package/src/modals/RemainingTilesModal/RemainingTilesModal.tsx +2 -2
- package/src/modals/ResultsModal/ResultsModal.module.scss +1 -5
- package/src/modals/ResultsModal/ResultsModal.tsx +10 -2
- package/src/modals/SettingsModal/SettingsModal.tsx +2 -2
- package/src/modals/SettingsModal/components/AutoGroupTilesSetting/lib.ts +3 -1
- package/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.module.scss +0 -1
- package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +1 -6
- package/src/modals/WordsModal/WordsModal.tsx +2 -2
- package/src/pages/index.module.scss +3 -21
- package/src/pages/index.tsx +51 -69
- package/src/parameters/index.ts +29 -2
- package/src/state/localStorage.ts +13 -2
- package/src/state/sagas.ts +16 -8
- package/src/state/slices/boardInitialState.ts +5 -1
- package/src/state/slices/boardSlice.ts +2 -2
- package/src/state/slices/rackInitialState.ts +8 -2
- package/src/state/slices/rackSlice.ts +16 -13
- package/src/state/slices/settingsInitialState.ts +9 -4
- package/src/state/slices/settingsSlice.ts +3 -1
- package/src/styles/animations.scss +0 -20
- package/src/styles/global.scss +4 -15
- package/src/styles/mixins.scss +0 -60
- package/src/styles/variables.scss +14 -5
- package/src/types/index.ts +4 -0
- package/.next/static/MvHZRF4XuJ7g8LLLRkf8U/_buildManifest.js +0 -1
- package/.next/static/chunks/pages/_app-66d80a5594aab8dc.js +0 -28
- package/.next/static/chunks/pages/index-0858deea02b2a417.js +0 -1
- package/.next/static/css/885da289cec275b3.css +0 -1
- package/.next/static/css/ea1c8134fe9a143e.css +0 -2
- package/src/components/LogoSplashScreen/LogoSplashScreen.module.scss +0 -65
- package/src/components/LogoSplashScreen/LogoSplashScreen.tsx +0 -31
- package/src/components/LogoSplashScreen/index.ts +0 -1
- package/src/components/Sizer/Sizer.module.scss +0 -10
- package/src/components/Sizer/Sizer.tsx +0 -10
- package/src/components/Sizer/index.ts +0 -1
- package/src/components/SplashScreen/SplashScreen.module.scss +0 -14
- package/src/components/SplashScreen/SplashScreen.tsx +0 -19
- package/src/components/SplashScreen/index.ts +0 -1
- package/src/components/SvgFontCss/SvgFontCss.tsx +0 -14
- package/src/components/SvgFontCss/createCss.ts +0 -11
- package/src/components/SvgFontCss/createStyle.ts +0 -9
- package/src/components/SvgFontCss/createSvg.ts +0 -10
- package/src/components/SvgFontCss/index.ts +0 -1
- package/src/components/SvgFontFix/SvgFontFix.module.scss +0 -5
- package/src/components/SvgFontFix/SvgFontFix.tsx +0 -21
- package/src/components/SvgFontFix/index.ts +0 -1
- package/src/hooks/useLocalStorage/index.ts +0 -1
- package/src/hooks/useLocalStorage/useLocalStorage.ts +0 -13
- package/src/hooks/useLocalStorage/useLocalStorageBoard.ts +0 -29
- package/src/hooks/useLocalStorage/useLocalStorageConfigId.ts +0 -29
- package/src/hooks/useLocalStorage/useLocalStorageLocale.ts +0 -32
- package/src/hooks/useLocalStorage/useLocalStorageRack.ts +0 -29
- /package/.next/static/{MvHZRF4XuJ7g8LLLRkf8U → Jmk00rVXCbdjFgP77tKXQ}/_ssgManifest.js +0 -0
- /package/{src/components/Logo/Logo.svg → public/logo.svg} +0 -0
package/next.config.js
CHANGED
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.6",
|
|
4
4
|
"description": "Scrabble Solver 2 - App",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16"
|
|
@@ -28,16 +28,16 @@
|
|
|
28
28
|
"start": "env-cmd next start -p 3333"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@floating-ui/react": "^0.
|
|
31
|
+
"@floating-ui/react": "^0.21.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.6",
|
|
35
|
+
"@scrabble-solver/constants": "^2.11.6",
|
|
36
|
+
"@scrabble-solver/dictionaries": "^2.11.6",
|
|
37
|
+
"@scrabble-solver/logger": "^2.11.6",
|
|
38
|
+
"@scrabble-solver/solver": "^2.11.6",
|
|
39
|
+
"@scrabble-solver/types": "^2.11.6",
|
|
40
|
+
"@scrabble-solver/word-definitions": "^2.11.6",
|
|
41
41
|
"classnames": "^2.3.2",
|
|
42
42
|
"include-media": "^2.0.0",
|
|
43
43
|
"include-media-query-builder": "^1.1.0",
|
|
@@ -48,9 +48,8 @@
|
|
|
48
48
|
"react-modal": "^3.16.1",
|
|
49
49
|
"react-portal": "^4.2.2",
|
|
50
50
|
"react-redux": "^8.0.5",
|
|
51
|
-
"react-use": "^17.4.0",
|
|
52
51
|
"react-window": "^1.8.8",
|
|
53
|
-
"redux-saga": "^1.2.
|
|
52
|
+
"redux-saga": "^1.2.3",
|
|
54
53
|
"store2": "^2.14.2",
|
|
55
54
|
"workbox-expiration": "^6.5.4",
|
|
56
55
|
"workbox-precaching": "^6.5.4",
|
|
@@ -69,8 +68,8 @@
|
|
|
69
68
|
"@types/redux": "^3.6.31",
|
|
70
69
|
"@types/redux-saga": "^0.10.5",
|
|
71
70
|
"env-cmd": "^10.1.0",
|
|
72
|
-
"sass": "^1.59.
|
|
71
|
+
"sass": "^1.59.3",
|
|
73
72
|
"workbox-webpack-plugin": "^6.5.4"
|
|
74
73
|
},
|
|
75
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "7075b82ef54cfb37e153152040c432639c3b1a99"
|
|
76
75
|
}
|
package/src/@types/svg.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { autoUpdate, FloatingPortal, offset, shift, useFloating
|
|
1
|
+
import { autoUpdate, FloatingPortal, offset, shift, useFloating } from '@floating-ui/react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
|
-
import { CSSProperties, FocusEventHandler, FunctionComponent, useState } from 'react';
|
|
3
|
+
import { CSSProperties, FocusEventHandler, FunctionComponent, useCallback, useState } from 'react';
|
|
4
4
|
import { useDispatch } from 'react-redux';
|
|
5
|
-
import { useMeasure } from 'react-use';
|
|
6
5
|
|
|
6
|
+
import { useAppLayout } from 'hooks';
|
|
7
7
|
import { BOARD_CELL_ACTIONS_OFFSET, TRANSITION } from 'parameters';
|
|
8
8
|
import { boardSlice, cellFilterSlice, selectBoard, selectRowsWithCandidate, useTypedSelector } from 'state';
|
|
9
9
|
|
|
@@ -21,7 +21,7 @@ const Board: FunctionComponent<Props> = ({ cellSize, className }) => {
|
|
|
21
21
|
const dispatch = useDispatch();
|
|
22
22
|
const rows = useTypedSelector(selectRowsWithCandidate);
|
|
23
23
|
const board = useTypedSelector(selectBoard);
|
|
24
|
-
const
|
|
24
|
+
const { actionsWidth } = useAppLayout();
|
|
25
25
|
const [{ activeIndex, direction, inputRefs }, { onChange, onDirectionToggle, onFocus, onKeyDown, onPaste }] =
|
|
26
26
|
useGrid(rows);
|
|
27
27
|
const inputRef = inputRefs[activeIndex.y][activeIndex.x];
|
|
@@ -41,53 +41,57 @@ const Board: FunctionComponent<Props> = ({ cellSize, className }) => {
|
|
|
41
41
|
whileElementsMounted: autoUpdate,
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
44
|
+
const handleBlur: FocusEventHandler = useCallback(
|
|
45
|
+
(event) => {
|
|
46
|
+
const eventComesFromActions = refs.floating.current?.contains(event.relatedTarget);
|
|
47
|
+
const eventComesFromBoard = event.currentTarget.contains(event.relatedTarget);
|
|
48
|
+
const isLocalEvent = eventComesFromActions || eventComesFromBoard;
|
|
49
|
+
|
|
50
|
+
if (!isLocalEvent) {
|
|
51
|
+
setShowActions(false);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[refs.floating],
|
|
55
|
+
);
|
|
55
56
|
|
|
56
|
-
const handleDirectionToggle = () => {
|
|
57
|
+
const handleDirectionToggle = useCallback(() => {
|
|
57
58
|
inputRef.current?.focus();
|
|
58
59
|
onDirectionToggle();
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const handleFocus: typeof onFocus = (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
60
|
+
}, [inputRef, onDirectionToggle]);
|
|
61
|
+
|
|
62
|
+
const handleFocus: typeof onFocus = useCallback(
|
|
63
|
+
(newX, newY) => {
|
|
64
|
+
const isFirstFocus = !showActions;
|
|
65
|
+
const originalTransition = refs.floating.current?.style.transition || '';
|
|
66
|
+
const newInputRef = inputRefs[newY][newX].current;
|
|
67
|
+
const newTileElement = newInputRef?.parentElement || null;
|
|
68
|
+
|
|
69
|
+
if (isFirstFocus) {
|
|
70
|
+
setTransition('none');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
refs.setReference(newTileElement);
|
|
74
|
+
onFocus(newX, newY);
|
|
75
|
+
setShowActions(true);
|
|
76
|
+
|
|
77
|
+
if (isFirstFocus) {
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
setTransition(originalTransition);
|
|
80
|
+
}, 0);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
[inputRefs, onFocus, refs.floating, refs.setReference, showActions],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const handleToggleBlank = useCallback(() => {
|
|
83
87
|
inputRef.current?.focus();
|
|
84
88
|
dispatch(boardSlice.actions.toggleCellIsBlank(cell));
|
|
85
|
-
};
|
|
89
|
+
}, [cell, dispatch, inputRef]);
|
|
86
90
|
|
|
87
|
-
const handleToggleFilterCell = () => {
|
|
91
|
+
const handleToggleFilterCell = useCallback(() => {
|
|
88
92
|
inputRef.current?.focus();
|
|
89
93
|
dispatch(cellFilterSlice.actions.toggle(cell));
|
|
90
|
-
};
|
|
94
|
+
}, [cell, dispatch, inputRef]);
|
|
91
95
|
|
|
92
96
|
return (
|
|
93
97
|
<>
|
|
@@ -112,7 +116,7 @@ const Board: FunctionComponent<Props> = ({ cellSize, className }) => {
|
|
|
112
116
|
})}
|
|
113
117
|
disabled={!showActions}
|
|
114
118
|
direction={direction}
|
|
115
|
-
ref={
|
|
119
|
+
ref={refs.setFloating}
|
|
116
120
|
style={{
|
|
117
121
|
position: strategy,
|
|
118
122
|
top: y ?? 0,
|
|
@@ -4,7 +4,8 @@ import classNames from 'classnames';
|
|
|
4
4
|
import { forwardRef, HTMLProps, MouseEventHandler } from 'react';
|
|
5
5
|
|
|
6
6
|
import { ArrowDown, Flag, FlagFill, Square, SquareFill } from 'icons';
|
|
7
|
-
import {
|
|
7
|
+
import { findCell } from 'lib';
|
|
8
|
+
import { selectCellIsFiltered, selectResultCandidateCells, useTranslate, useTypedSelector } from 'state';
|
|
8
9
|
|
|
9
10
|
import Button from '../../../Button';
|
|
10
11
|
|
|
@@ -22,8 +23,9 @@ const Actions = forwardRef<HTMLDivElement, Props>(
|
|
|
22
23
|
({ cell, className, direction, disabled, onDirectionToggle, onToggleBlank, onToggleFilterCell, ...props }, ref) => {
|
|
23
24
|
const translate = useTranslate();
|
|
24
25
|
const isFiltered = useTypedSelector((state) => selectCellIsFiltered(state, cell));
|
|
26
|
+
const resultCandidateCells = useTypedSelector(selectResultCandidateCells);
|
|
25
27
|
const isBlank = cell.tile.isBlank;
|
|
26
|
-
const isEmpty = cell.tile.character === EMPTY_CELL;
|
|
28
|
+
const isEmpty = cell.tile.character === EMPTY_CELL || Boolean(findCell(resultCandidateCells, cell.x, cell.y));
|
|
27
29
|
|
|
28
30
|
// On iOS it helps with losing focus too early which makes Actions disappear
|
|
29
31
|
const handleMouseDown: MouseEventHandler = (event) => event.preventDefault();
|
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
@import 'styles/mixins';
|
|
2
2
|
|
|
3
|
+
@mixin lighthouse-input-size-hack {
|
|
4
|
+
// Hack for this Lighthouse warning:
|
|
5
|
+
// > Interactive elements like buttons and links should be large enough (48x48px), and have
|
|
6
|
+
// > enough space around them, to be easy enough to tap without overlapping onto other elements.
|
|
7
|
+
|
|
8
|
+
input {
|
|
9
|
+
position: absolute;
|
|
10
|
+
top: -100%;
|
|
11
|
+
right: -100%;
|
|
12
|
+
left: -100%;
|
|
13
|
+
bottom: -100%;
|
|
14
|
+
width: 300%;
|
|
15
|
+
height: 300%;
|
|
16
|
+
clip-path: inset((100% / 3));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
[dir='ltr'] & {
|
|
20
|
+
&:nth-child(1),
|
|
21
|
+
&:nth-child(2),
|
|
22
|
+
&:nth-child(3) {
|
|
23
|
+
input {
|
|
24
|
+
left: 0;
|
|
25
|
+
clip-path: polygon(0 (100% / 3), (100% / 3) (100% / 3), (100% / 3) (200% / 3), 0 (200% / 3));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&:nth-last-child(1),
|
|
30
|
+
&:nth-last-child(2),
|
|
31
|
+
&:nth-last-child(3) {
|
|
32
|
+
input {
|
|
33
|
+
left: -200%;
|
|
34
|
+
clip-path: polygon((200% / 3) (100% / 3), 100% (100% / 3), 100% (200% / 3), (200% / 3) (200% / 3));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
[dir='rtl'] & {
|
|
40
|
+
&:nth-child(1),
|
|
41
|
+
&:nth-child(2),
|
|
42
|
+
&:nth-child(3) {
|
|
43
|
+
input {
|
|
44
|
+
left: -200%;
|
|
45
|
+
right: 0;
|
|
46
|
+
clip-path: polygon((200% / 3) (100% / 3), 100% (100% / 3), 100% (200% / 3), (200% / 3) (200% / 3));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&:nth-last-child(1),
|
|
51
|
+
&:nth-last-child(2),
|
|
52
|
+
&:nth-last-child(3) {
|
|
53
|
+
input {
|
|
54
|
+
left: 0;
|
|
55
|
+
right: -200%;
|
|
56
|
+
clip-path: polygon(0 (100% / 3), (100% / 3) (100% / 3), (100% / 3) (200% / 3), 0 (200% / 3));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
3
62
|
.cell {
|
|
4
63
|
@include focus-effect;
|
|
5
64
|
@include lighthouse-input-size-hack;
|
|
@@ -9,7 +68,6 @@
|
|
|
9
68
|
vertical-align: middle;
|
|
10
69
|
background-color: var(--color--white);
|
|
11
70
|
border-bottom: var(--border--width) dotted var(--border--color--light);
|
|
12
|
-
transition: var(--transition);
|
|
13
71
|
background-clip: padding-box;
|
|
14
72
|
|
|
15
73
|
[dir='ltr'] & {
|
|
@@ -121,10 +179,6 @@
|
|
|
121
179
|
font-weight: bold;
|
|
122
180
|
color: var(--color--white);
|
|
123
181
|
transition: var(--transition);
|
|
124
|
-
|
|
125
|
-
[lang='fa-IR'] & {
|
|
126
|
-
font-family: var(--font--family--latin);
|
|
127
|
-
}
|
|
128
182
|
}
|
|
129
183
|
|
|
130
184
|
&:focus-within {
|
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
useState,
|
|
15
15
|
} from 'react';
|
|
16
16
|
import { useDispatch } from 'react-redux';
|
|
17
|
-
import { useLatest } from 'react-use';
|
|
18
17
|
|
|
18
|
+
import { useLatest } from 'hooks';
|
|
19
19
|
import { LOCALE_FEATURES } from 'i18n';
|
|
20
20
|
import { createGridOf, createKeyboardNavigation, extractCharacters, extractInputValue } from 'lib';
|
|
21
21
|
import { boardSlice, selectConfig, selectLocale, useTypedSelector } from 'state';
|
|
@@ -235,6 +235,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
|
|
|
235
235
|
|
|
236
236
|
if (direction === 'vertical') {
|
|
237
237
|
onDirectionToggle();
|
|
238
|
+
changeActiveIndex(LOCALE_FEATURES[locale].direction === 'ltr' ? -1 : 0, 0);
|
|
238
239
|
} else {
|
|
239
240
|
changeActiveIndex(LOCALE_FEATURES[locale].direction === 'ltr' ? -1 : 1, 0);
|
|
240
241
|
}
|
|
@@ -244,6 +245,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
|
|
|
244
245
|
|
|
245
246
|
if (direction === 'vertical') {
|
|
246
247
|
onDirectionToggle();
|
|
248
|
+
changeActiveIndex(LOCALE_FEATURES[locale].direction === 'ltr' ? 0 : -1, 0);
|
|
247
249
|
} else {
|
|
248
250
|
changeActiveIndex(LOCALE_FEATURES[locale].direction === 'ltr' ? 1 : -1, 0);
|
|
249
251
|
}
|
|
@@ -253,9 +255,9 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
|
|
|
253
255
|
|
|
254
256
|
if (direction === 'horizontal') {
|
|
255
257
|
onDirectionToggle();
|
|
256
|
-
} else {
|
|
257
|
-
changeActiveIndex(0, -1);
|
|
258
258
|
}
|
|
259
|
+
|
|
260
|
+
changeActiveIndex(0, -1);
|
|
259
261
|
},
|
|
260
262
|
onBackspace: (event) => {
|
|
261
263
|
const position = getInputRefPosition(event.target as HTMLInputElement);
|
|
@@ -31,7 +31,7 @@ const Loading: FunctionComponent<Props> = ({ className, wave = true }) => {
|
|
|
31
31
|
return (
|
|
32
32
|
<div aria-label={translation} className={classNames(styles.loading, className)} role="status">
|
|
33
33
|
<div className={styles.dim} />
|
|
34
|
-
<div className={styles.
|
|
34
|
+
<div className={styles.text}>
|
|
35
35
|
<PlainTiles className={classNames(styles.tiles)} content={content} dropShadow wave={wave} />
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Logo.svg is what this component generates with all the text nodes transformed
|
|
4
|
-
* into paths (manually with Inkscape), and corner radius removed from tiles.
|
|
5
|
-
*/
|
|
6
|
-
import { CSSProperties, FunctionComponent } from 'react';
|
|
1
|
+
import Image from 'next/image';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
7
3
|
|
|
8
|
-
import
|
|
4
|
+
import { useAppLayout } from 'hooks';
|
|
5
|
+
import { LOGO_SRC } from 'parameters';
|
|
9
6
|
|
|
10
7
|
interface Props {
|
|
11
8
|
className?: string;
|
|
12
|
-
style?: CSSProperties;
|
|
13
9
|
}
|
|
14
10
|
|
|
15
|
-
const
|
|
11
|
+
const Logo = forwardRef<HTMLImageElement, Props>((props, ref) => {
|
|
12
|
+
const { logoHeight, logoWidth } = useAppLayout();
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
);
|
|
14
|
+
return (
|
|
15
|
+
<Image {...props} alt="Scrabble Solver 2" height={logoHeight} priority ref={ref} src={LOGO_SRC} width={logoWidth} />
|
|
16
|
+
);
|
|
17
|
+
});
|
|
20
18
|
|
|
21
19
|
export default Logo;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This component is unused, but it serves as a blueprint for the Logo.svg.
|
|
3
|
+
* Logo.svg is what this component generates with all the text nodes transformed
|
|
4
|
+
* into paths (manually with Inkscape), and corner radius removed from tiles.
|
|
5
|
+
*/
|
|
6
|
+
import { CSSProperties, FunctionComponent } from 'react';
|
|
7
|
+
|
|
8
|
+
import PlainTiles from '../PlainTiles';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
className?: string;
|
|
12
|
+
style?: CSSProperties;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const CONTENT = [['SCRABBLE'], ['SOLVER', '2']];
|
|
16
|
+
|
|
17
|
+
const LogoBlueprint: FunctionComponent<Props> = ({ className, style }) => (
|
|
18
|
+
<PlainTiles className={className} content={CONTENT} style={style} />
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export default LogoBlueprint;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from './Logo
|
|
1
|
+
export { default } from './Logo';
|
|
@@ -25,10 +25,6 @@
|
|
|
25
25
|
&:focus {
|
|
26
26
|
outline: none;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
@include media('<s') {
|
|
30
|
-
--modal--width: 100%;
|
|
31
|
-
}
|
|
32
28
|
}
|
|
33
29
|
|
|
34
30
|
.afterOpen {
|
|
@@ -126,12 +122,11 @@
|
|
|
126
122
|
flex: 0 0 auto;
|
|
127
123
|
display: flex;
|
|
128
124
|
flex-direction: row-reverse;
|
|
129
|
-
justify-content: space-between;
|
|
130
125
|
gap: var(--spacing--l);
|
|
131
126
|
padding: var(--spacing--l);
|
|
132
127
|
padding-top: 0;
|
|
133
128
|
|
|
134
|
-
@include media('>
|
|
129
|
+
@include media('>l') {
|
|
135
130
|
display: none;
|
|
136
131
|
}
|
|
137
132
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { FunctionComponent, ReactNode, useEffect, useState } from 'react';
|
|
2
|
+
import { FunctionComponent, ReactNode, useCallback, useEffect, useState } from 'react';
|
|
3
3
|
import ReactModal from 'react-modal';
|
|
4
|
-
import { useKey } from 'react-use';
|
|
5
4
|
|
|
6
5
|
import { CrossSquareFill } from 'icons';
|
|
7
6
|
import { TRANSITION_DURATION_LONG } from 'parameters';
|
|
@@ -25,16 +24,24 @@ const Modal: FunctionComponent<Props> = ({ children, className, footer, isOpen,
|
|
|
25
24
|
const translate = useTranslate();
|
|
26
25
|
const [shouldReturnFocusAfterClose, setShouldReturnFocusAfterClose] = useState(true);
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const handleEscape = useCallback(
|
|
28
|
+
(event: KeyboardEvent) => {
|
|
29
|
+
if (event.key === 'Escape') {
|
|
30
|
+
setShouldReturnFocusAfterClose(false);
|
|
31
|
+
onClose();
|
|
32
|
+
}
|
|
33
33
|
},
|
|
34
|
-
undefined,
|
|
35
34
|
[onClose],
|
|
36
35
|
);
|
|
37
36
|
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
document.addEventListener('keydown', handleEscape);
|
|
39
|
+
|
|
40
|
+
return () => {
|
|
41
|
+
document.removeEventListener('keydown', handleEscape);
|
|
42
|
+
};
|
|
43
|
+
}, [handleEscape]);
|
|
44
|
+
|
|
38
45
|
useEffect(() => {
|
|
39
46
|
if (isOpen) {
|
|
40
47
|
setShouldReturnFocusAfterClose(true);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { FunctionComponent } from 'react';
|
|
2
|
+
import { FunctionComponent, memo } from 'react';
|
|
3
3
|
|
|
4
4
|
import { useAppLayout } from 'hooks';
|
|
5
5
|
import { CardChecklist, Cog, Eraser, Github, Keyboard, List, Sack } from 'icons';
|
|
@@ -135,4 +135,4 @@ const NavButtons: FunctionComponent<Props> = ({
|
|
|
135
135
|
);
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
-
export default NavButtons;
|
|
138
|
+
export default memo(NavButtons);
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { CSSProperties, FunctionComponent, useMemo } from 'react';
|
|
3
3
|
|
|
4
|
-
import { LOCALE_FEATURES } from 'i18n';
|
|
5
|
-
import { selectLocale, useTypedSelector } from 'state';
|
|
6
|
-
|
|
7
|
-
import SvgFontCss from '../SvgFontCss';
|
|
8
|
-
|
|
9
4
|
import { createPlainTiles, getViewbox } from './lib';
|
|
10
5
|
import styles from './PlainTiles.module.scss';
|
|
11
6
|
import Tile from './Tile';
|
|
@@ -21,8 +16,6 @@ interface Props {
|
|
|
21
16
|
}
|
|
22
17
|
|
|
23
18
|
const PlainTiles: FunctionComponent<Props> = ({ className, color, content, dropShadow, showPoints, style, wave }) => {
|
|
24
|
-
const locale = useTypedSelector(selectLocale);
|
|
25
|
-
const { fontFamily } = LOCALE_FEATURES[locale];
|
|
26
19
|
const tiles = useMemo(() => createPlainTiles({ color, content, showPoints }), [color, content, showPoints]);
|
|
27
20
|
|
|
28
21
|
return (
|
|
@@ -35,14 +28,11 @@ const PlainTiles: FunctionComponent<Props> = ({ className, color, content, dropS
|
|
|
35
28
|
viewBox={getViewbox(content)}
|
|
36
29
|
xmlns="http://www.w3.org/2000/svg"
|
|
37
30
|
>
|
|
38
|
-
<SvgFontCss fontFamily={fontFamily} />
|
|
39
|
-
|
|
40
31
|
{tiles.map((tile, index) => (
|
|
41
32
|
<Tile
|
|
42
33
|
character={tile.character}
|
|
43
34
|
className={styles.tile}
|
|
44
35
|
color={tile.color}
|
|
45
|
-
fontFamily={fontFamily}
|
|
46
36
|
key={index}
|
|
47
37
|
points={tile.points}
|
|
48
38
|
size={tile.size}
|
|
@@ -4,7 +4,6 @@ interface Props {
|
|
|
4
4
|
character: string;
|
|
5
5
|
className?: string;
|
|
6
6
|
color: string;
|
|
7
|
-
fontFamily: string;
|
|
8
7
|
points?: number;
|
|
9
8
|
size: number;
|
|
10
9
|
transform?: string;
|
|
@@ -12,13 +11,12 @@ interface Props {
|
|
|
12
11
|
y: number;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
const Tile: FunctionComponent<Props> = ({ character, className, color,
|
|
14
|
+
const Tile: FunctionComponent<Props> = ({ character, className, color, points, size, transform, x, y }) => (
|
|
16
15
|
<g className={className} transform={transform}>
|
|
17
16
|
<rect fill={color} height={size} rx={size * 0.15} width={size} x={x} y={y} />
|
|
18
17
|
|
|
19
18
|
<text
|
|
20
19
|
dominantBaseline="central"
|
|
21
|
-
fontFamily={fontFamily}
|
|
22
20
|
fontSize={size * 0.6}
|
|
23
21
|
fontWeight="bold"
|
|
24
22
|
textAnchor="middle"
|
|
@@ -31,7 +29,6 @@ const Tile: FunctionComponent<Props> = ({ character, className, color, fontFamil
|
|
|
31
29
|
{typeof points === 'number' && (
|
|
32
30
|
<text
|
|
33
31
|
dominantBaseline="text-after-edge"
|
|
34
|
-
fontFamily={fontFamily}
|
|
35
32
|
fontSize={size * 0.25}
|
|
36
33
|
fontWeight="bold"
|
|
37
34
|
textAnchor="end"
|
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
@import 'styles/mixins';
|
|
2
2
|
|
|
3
|
+
@mixin lighthouse-input-size-hack {
|
|
4
|
+
// Hack for this Lighthouse warning:
|
|
5
|
+
// > Interactive elements like buttons and links should be large enough (48x48px), and have
|
|
6
|
+
// > enough space around them, to be easy enough to tap without overlapping onto other elements.
|
|
7
|
+
|
|
8
|
+
input {
|
|
9
|
+
position: absolute;
|
|
10
|
+
top: -200%;
|
|
11
|
+
right: -100%;
|
|
12
|
+
left: -100%;
|
|
13
|
+
bottom: 0;
|
|
14
|
+
width: 300%;
|
|
15
|
+
height: 300%;
|
|
16
|
+
clip-path: polygon((100% / 3) (200% / 3), (200% / 3) (200% / 3), (200% / 3) 100%, (100% / 3) 100%);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
[dir='ltr'] & {
|
|
20
|
+
&:nth-child(1),
|
|
21
|
+
&:nth-child(2),
|
|
22
|
+
&:nth-child(3) {
|
|
23
|
+
input {
|
|
24
|
+
left: 0;
|
|
25
|
+
clip-path: polygon(0 (200% / 3), (100% / 3) (200% / 3), (100% / 3) 100%, 0 100%);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&:nth-last-child(1),
|
|
30
|
+
&:nth-last-child(2),
|
|
31
|
+
&:nth-last-child(3) {
|
|
32
|
+
input {
|
|
33
|
+
left: -200%;
|
|
34
|
+
clip-path: polygon((200% / 3) (200% / 3), 100% (200% / 3), 100% 100%, (200% / 3) 100%);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
[dir='rtl'] & {
|
|
40
|
+
&:nth-child(1),
|
|
41
|
+
&:nth-child(2),
|
|
42
|
+
&:nth-child(3) {
|
|
43
|
+
input {
|
|
44
|
+
left: -200%;
|
|
45
|
+
right: 0;
|
|
46
|
+
clip-path: polygon((200% / 3) (200% / 3), 100% (200% / 3), 100% 100%, (200% / 3) 100%);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&:nth-last-child(1),
|
|
51
|
+
&:nth-last-child(2),
|
|
52
|
+
&:nth-last-child(3) {
|
|
53
|
+
input {
|
|
54
|
+
left: 0;
|
|
55
|
+
right: -200%;
|
|
56
|
+
clip-path: polygon(0 (200% / 3), (100% / 3) (200% / 3), (100% / 3) 100%, 0 100%);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
3
62
|
.rack {
|
|
4
63
|
display: flex;
|
|
5
64
|
box-shadow: var(--box-shadow);
|