@scrabble-solver/scrabble-solver 2.10.11 → 2.11.1
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 +12 -12
- 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/required-server-files.json +1 -1
- package/.next/routes-manifest.json +1 -1
- package/.next/server/chunks/{176.js → 277.js} +956 -930
- package/.next/server/chunks/{290.js → 417.js} +3 -3
- package/.next/server/chunks/50.js +371 -343
- package/.next/server/chunks/664.js +15 -15
- package/.next/server/chunks/859.js +17 -10
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/next-font-manifest.js +1 -0
- 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 +4 -28
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/_document.js +2 -2
- package/.next/server/pages/_document.js.nft.json +1 -1
- package/.next/server/pages/_error.js +4 -4
- package/.next/server/pages/api/dictionary/[locale]/[word].js +4 -4
- 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 +9 -11
- package/.next/server/pages/api/solve.js.nft.json +1 -1
- package/.next/server/pages/api/verify.js +3 -3
- 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 +256 -210
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/index.json +1 -1
- package/.next/static/chunks/main-0ecb9ccfcb6c9b24.js +1 -0
- package/.next/static/chunks/pages/404-448ba28510855455.js +1 -0
- package/.next/static/chunks/pages/_app-270526803bc274eb.js +28 -0
- package/.next/static/chunks/pages/{_error-8353112a01355ec2.js → _error-54de1933a164a1ff.js} +1 -1
- package/.next/static/chunks/pages/index-c6e7754ccf3532df.js +1 -0
- package/.next/static/css/ad39b36eab07e613.css +1 -0
- package/.next/static/css/e5803e581e4c0451.css +2 -0
- package/.next/static/esK8DG-6aS5V7QFRtR3YE/_buildManifest.js +1 -0
- package/.next/trace +53 -55
- package/package.json +12 -17
- package/src/components/{Solver/components/EmptyState/EmptyState.module.scss → Alert/Alert.module.scss} +11 -7
- package/src/components/{Solver/components/EmptyState/EmptyState.tsx → Alert/Alert.tsx} +8 -6
- package/src/components/Alert/index.ts +1 -0
- package/src/components/Board/Board.module.scss +55 -0
- package/src/components/Board/BoardPure.tsx +4 -0
- package/src/components/Board/components/Cell/Cell.module.scss +42 -0
- package/src/components/Board/components/Cell/Cell.tsx +12 -0
- package/src/components/Board/components/Cell/CellPure.tsx +12 -0
- package/src/components/Board/hooks/useGrid.ts +8 -24
- package/src/components/Dictionary/Dictionary.module.scss +17 -8
- package/src/components/Dictionary/Dictionary.tsx +5 -5
- package/src/components/DictionaryInput/DictionaryInput.module.scss +1 -0
- package/src/components/EmptyState/EmptyState.module.scss +2 -1
- package/src/components/EmptyState/EmptyState.tsx +1 -2
- package/src/components/Loading/Loading.module.scss +1 -1
- package/src/components/Loading/Loading.tsx +1 -1
- package/src/components/Logo/Logo.tsx +5 -0
- package/src/components/Modal/Modal.module.scss +2 -1
- package/src/components/NavButtons/NavButtons.tsx +4 -5
- package/src/components/PlainTiles/PlainTiles.module.scss +1 -1
- package/src/components/PlainTiles/Tile.tsx +3 -3
- package/src/components/Rack/Rack.module.scss +25 -0
- package/src/components/Rack/Rack.tsx +5 -4
- package/src/components/Rack/RackTile.tsx +6 -13
- package/src/components/Results/Results.module.scss +33 -2
- package/src/components/Results/Results.tsx +11 -11
- package/src/components/ResultsInput/ResultsInput.module.scss +1 -0
- package/src/components/Solver/Solver.module.scss +6 -4
- package/src/components/Solver/Solver.tsx +16 -28
- package/src/components/Solver/components/FloatingSolveButton/FloatingSolveButton.module.scss +7 -0
- package/src/components/Solver/components/{SolveButton/SolveButton.tsx → FloatingSolveButton/FloatingSolveButton.tsx} +6 -6
- package/src/components/Solver/components/FloatingSolveButton/index.ts +1 -0
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss +19 -4
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +13 -1
- package/src/components/Solver/components/index.ts +1 -2
- package/src/components/Spinner/Spinner.module.scss +11 -0
- package/src/components/Spinner/Spinner.tsx +19 -0
- package/src/components/Spinner/index.ts +1 -0
- package/src/components/Tile/Tile.module.scss +14 -2
- package/src/components/Tile/Tile.tsx +5 -5
- package/src/components/Tooltip/Tooltip.module.scss +1 -72
- package/src/components/Tooltip/useTooltip.tsx +25 -35
- package/src/components/index.ts +2 -0
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useAppLayout.ts +29 -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/index.ts +1 -1
- package/src/i18n/pl.json +1 -0
- package/src/lib/index.ts +0 -1
- package/src/modals/DictionaryModal/DictionaryModal.module.scss +23 -0
- package/src/modals/DictionaryModal/DictionaryModal.tsx +27 -0
- package/src/modals/DictionaryModal/index.ts +1 -0
- package/src/modals/KeyMapModal/KeyMapModal.tsx +5 -21
- package/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +4 -0
- package/src/modals/MenuModal/MenuModal.tsx +7 -1
- package/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -0
- package/src/modals/ResultsModal/ResultsModal.module.scss +19 -0
- package/src/modals/ResultsModal/ResultsModal.tsx +8 -7
- package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +2 -0
- package/src/modals/WordsModal/WordsModal.module.scss +8 -1
- package/src/modals/index.ts +1 -0
- package/src/pages/api/solve.ts +9 -10
- package/src/pages/index.tsx +20 -15
- package/src/state/createAppStore.ts +26 -10
- package/src/state/localStorage.ts +0 -9
- package/src/state/types.ts +20 -2
- package/src/styles/animations.scss +10 -0
- package/src/styles/global.scss +1 -1
- package/src/styles/mixins.scss +22 -0
- package/src/styles/variables.scss +17 -2
- package/src/types/index.ts +1 -0
- package/.next/server/font-loader-manifest.js +0 -1
- package/.next/static/chunks/main-74c4d6b2b5c362f3.js +0 -1
- package/.next/static/chunks/pages/404-6c1a6e3251710371.js +0 -1
- package/.next/static/chunks/pages/_app-d98e480ff8c583de.js +0 -28
- package/.next/static/chunks/pages/index-bd1c7d3872c37456.js +0 -1
- package/.next/static/css/a9b55372a26cf77d.css +0 -1
- package/.next/static/css/b8954b85e2fa5b63.css +0 -2
- package/.next/static/msKI0ZURgJImoGBJvCBiF/_buildManifest.js +0 -1
- package/src/components/Solver/components/EmptyState/index.ts +0 -1
- package/src/components/Solver/components/SolveButton/SolveButton.module.scss +0 -4
- package/src/components/Solver/components/SolveButton/index.ts +0 -1
- package/src/components/Tooltip/constants.ts +0 -28
- package/src/hooks/useUniqueId.ts +0 -9
- package/src/lib/isCtrl.ts +0 -7
- package/src/state/rootReducer.ts +0 -25
- /package/.next/server/{font-loader-manifest.json → next-font-manifest.json} +0 -0
- /package/.next/static/{msKI0ZURgJImoGBJvCBiF → esK8DG-6aS5V7QFRtR3YE}/_ssgManifest.js +0 -0
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useRef,
|
|
12
12
|
} from 'react';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { useAppLayout } from 'hooks';
|
|
15
15
|
import { getTileSizes, noop } from 'lib';
|
|
16
16
|
import { EASE_OUT_CUBIC, TILE_APPEAR_DURATION, TILE_APPEAR_KEYFRAMES } from 'parameters';
|
|
17
17
|
import { selectLocale, useTypedSelector } from 'state';
|
|
@@ -58,6 +58,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
58
58
|
onKeyDown = noop,
|
|
59
59
|
}) => {
|
|
60
60
|
const locale = useTypedSelector(selectLocale);
|
|
61
|
+
const { animateTile, showTilePoints } = useAppLayout();
|
|
61
62
|
const { pointsFontSize, tileFontSize, tileSize } = getTileSizes(size);
|
|
62
63
|
const style = useMemo(() => ({ height: tileSize, width: tileSize }), [tileSize]);
|
|
63
64
|
const characterStyle = useMemo(() => ({ fontSize: tileFontSize }), [tileFontSize]);
|
|
@@ -65,8 +66,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
65
66
|
const ref = useRef<HTMLInputElement>(null);
|
|
66
67
|
const mergedRef = useMergeRefs(inputRef ? [ref, inputRef] : [ref]);
|
|
67
68
|
const isEmpty = !character || character === EMPTY_CELL;
|
|
68
|
-
const
|
|
69
|
-
const canShowPoints = (isBlank || !isEmpty) && typeof points !== 'undefined' && !isLessThanXs;
|
|
69
|
+
const canShowPoints = showTilePoints && (!isEmpty || isBlank) && typeof points !== 'undefined';
|
|
70
70
|
const pointsFormatted = typeof points === 'number' ? points.toLocaleString(locale) : '';
|
|
71
71
|
|
|
72
72
|
const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (event) => {
|
|
@@ -81,7 +81,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
81
81
|
}, [autoFocus, ref]);
|
|
82
82
|
|
|
83
83
|
useEffect(() => {
|
|
84
|
-
if (!ref.current?.parentElement || !character ||
|
|
84
|
+
if (!ref.current?.parentElement || !character || !animateTile) {
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -90,7 +90,7 @@ const Tile: FunctionComponent<Props> = ({
|
|
|
90
90
|
easing: EASE_OUT_CUBIC,
|
|
91
91
|
fill: 'forwards',
|
|
92
92
|
});
|
|
93
|
-
}, [character,
|
|
93
|
+
}, [character, animateTile]);
|
|
94
94
|
|
|
95
95
|
return (
|
|
96
96
|
<TilePure
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
$arrow-size: 4px;
|
|
2
|
-
|
|
3
|
-
:export {
|
|
4
|
-
ARROW_SIZE: $arrow-size;
|
|
5
|
-
PREVENT_OVERFLOW: 10px;
|
|
6
|
-
OFFSET: 2px;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
1
|
.tooltip {
|
|
10
2
|
padding: var(--spacing--s) var(--spacing--m);
|
|
11
3
|
box-shadow: var(--box-shadow);
|
|
@@ -13,71 +5,8 @@ $arrow-size: 4px;
|
|
|
13
5
|
background-color: var(--color--tooltip--background);
|
|
14
6
|
color: var(--color--tooltip--foreground);
|
|
15
7
|
z-index: var(--z-index--tooltip);
|
|
16
|
-
|
|
17
|
-
&.top {
|
|
18
|
-
.arrow {
|
|
19
|
-
bottom: -$arrow-size;
|
|
20
|
-
|
|
21
|
-
&::after {
|
|
22
|
-
left: 0;
|
|
23
|
-
bottom: 0;
|
|
24
|
-
border-top-color: var(--color--tooltip--background);
|
|
25
|
-
border-bottom: none;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
&.right {
|
|
31
|
-
.arrow {
|
|
32
|
-
left: -$arrow-size;
|
|
33
|
-
|
|
34
|
-
&::after {
|
|
35
|
-
left: 0;
|
|
36
|
-
top: 0;
|
|
37
|
-
border-right-color: var(--color--tooltip--background);
|
|
38
|
-
border-left: none;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
&.bottom {
|
|
44
|
-
.arrow {
|
|
45
|
-
top: -$arrow-size;
|
|
46
|
-
|
|
47
|
-
&::after {
|
|
48
|
-
top: 0;
|
|
49
|
-
left: 0;
|
|
50
|
-
border-bottom-color: var(--color--tooltip--background);
|
|
51
|
-
border-top: none;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
&.left {
|
|
57
|
-
.arrow {
|
|
58
|
-
right: -$arrow-size;
|
|
59
|
-
|
|
60
|
-
&::after {
|
|
61
|
-
right: 0;
|
|
62
|
-
top: 0;
|
|
63
|
-
border-left-color: var(--color--tooltip--background);
|
|
64
|
-
border-right: none;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
8
|
}
|
|
69
9
|
|
|
70
10
|
.arrow {
|
|
71
|
-
|
|
72
|
-
width: 2 * $arrow-size;
|
|
73
|
-
height: 2 * $arrow-size;
|
|
74
|
-
|
|
75
|
-
&::after {
|
|
76
|
-
content: ' ';
|
|
77
|
-
position: absolute;
|
|
78
|
-
height: 0;
|
|
79
|
-
width: 0;
|
|
80
|
-
pointer-events: none;
|
|
81
|
-
border: $arrow-size solid transparent;
|
|
82
|
-
}
|
|
11
|
+
fill: var(--color--tooltip--background);
|
|
83
12
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arrow, autoUpdate, flip, FloatingArrow, offset, shift, useFloating } from '@floating-ui/react';
|
|
1
2
|
import classNames from 'classnames';
|
|
2
3
|
import {
|
|
3
4
|
FocusEvent,
|
|
@@ -6,16 +7,15 @@ import {
|
|
|
6
7
|
MouseEventHandler,
|
|
7
8
|
ReactNode,
|
|
8
9
|
useCallback,
|
|
10
|
+
useId,
|
|
9
11
|
useMemo,
|
|
12
|
+
useRef,
|
|
10
13
|
useState,
|
|
11
14
|
} from 'react';
|
|
12
|
-
import { usePopper } from 'react-popper';
|
|
13
|
-
import { useMountedState, useRafLoop } from 'react-use';
|
|
14
15
|
|
|
15
|
-
import { useIsTouchDevice, usePortal
|
|
16
|
+
import { useIsTouchDevice, usePortal } from 'hooks';
|
|
16
17
|
import { noop } from 'lib';
|
|
17
18
|
|
|
18
|
-
import { MODIFIERS } from './constants';
|
|
19
19
|
import styles from './Tooltip.module.scss';
|
|
20
20
|
|
|
21
21
|
interface Props {
|
|
@@ -36,29 +36,27 @@ interface TriggerProps {
|
|
|
36
36
|
onMouseOver?: MouseEventHandler;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
const ARROW_SIZE = 7;
|
|
40
|
+
const ARROW_GAP = 3;
|
|
41
|
+
const PADDING = 10;
|
|
42
|
+
|
|
39
43
|
// eslint-disable-next-line max-statements
|
|
40
44
|
const useTooltip = (
|
|
41
45
|
tooltip: ReactNode,
|
|
42
46
|
{ className, placement = 'top', onBlur = noop, onFocus = noop, onMouseOut = noop, onMouseOver = noop }: Props = {},
|
|
43
47
|
): TriggerProps => {
|
|
44
|
-
const id =
|
|
48
|
+
const id = useId();
|
|
45
49
|
const isTouchDevice = useIsTouchDevice();
|
|
46
50
|
const isEnabled = Boolean(tooltip) || tooltip === 0;
|
|
47
|
-
const isMounted = useMountedState();
|
|
48
51
|
const [isShown, setIsShown] = useState<boolean>(false);
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
const {
|
|
53
|
-
|
|
54
|
-
styles: popperStyles,
|
|
55
|
-
update,
|
|
56
|
-
} = usePopper(referenceElement, popperElement, {
|
|
57
|
-
modifiers: [{ name: 'arrow', options: { element: arrowElement } }, ...MODIFIERS],
|
|
52
|
+
const arrowRef = useRef(null);
|
|
53
|
+
const ariaAttributes = useMemo(() => (isShown ? { 'aria-describedby': id } : {}), [id, isShown]);
|
|
54
|
+
|
|
55
|
+
const { x, y, context, refs, strategy } = useFloating({
|
|
56
|
+
middleware: [flip(), shift({ padding: PADDING }), offset(ARROW_SIZE + ARROW_GAP), arrow({ element: arrowRef })],
|
|
58
57
|
placement,
|
|
58
|
+
whileElementsMounted: autoUpdate,
|
|
59
59
|
});
|
|
60
|
-
const computedPlacement = attributes.popper ? attributes.popper['data-popper-placement'] : placement;
|
|
61
|
-
const ariaAttributes = useMemo(() => (isShown ? { 'aria-describedby': id } : {}), [id, isShown]);
|
|
62
60
|
|
|
63
61
|
const handleBlur = useCallback(
|
|
64
62
|
(event: FocusEvent) => {
|
|
@@ -95,7 +93,7 @@ const useTooltip = (
|
|
|
95
93
|
const mouseTriggerProps = useMemo(
|
|
96
94
|
() => ({
|
|
97
95
|
...ariaAttributes,
|
|
98
|
-
ref:
|
|
96
|
+
ref: refs.setReference,
|
|
99
97
|
onBlur: handleBlur,
|
|
100
98
|
onFocus: handleFocus,
|
|
101
99
|
onMouseOut: handleMouseOut,
|
|
@@ -107,33 +105,25 @@ const useTooltip = (
|
|
|
107
105
|
const touchTriggerProps = useMemo(
|
|
108
106
|
() => ({
|
|
109
107
|
...ariaAttributes,
|
|
110
|
-
ref:
|
|
108
|
+
ref: refs.setReference,
|
|
111
109
|
}),
|
|
112
110
|
[ariaAttributes],
|
|
113
111
|
);
|
|
114
112
|
|
|
115
113
|
const triggerProps = isTouchDevice ? touchTriggerProps : mouseTriggerProps;
|
|
116
114
|
|
|
117
|
-
useRafLoop(() => {
|
|
118
|
-
if (isMounted() && update) {
|
|
119
|
-
update();
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
115
|
usePortal(
|
|
124
116
|
<div
|
|
125
|
-
className={classNames(styles.tooltip, className
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
style={popperStyles.popper}
|
|
133
|
-
{...attributes.popper}
|
|
117
|
+
className={classNames(styles.tooltip, className)}
|
|
118
|
+
ref={refs.setFloating}
|
|
119
|
+
style={{
|
|
120
|
+
position: strategy,
|
|
121
|
+
top: y ?? 0,
|
|
122
|
+
left: x ?? 0,
|
|
123
|
+
}}
|
|
134
124
|
>
|
|
135
125
|
<div>{tooltip}</div>
|
|
136
|
-
<
|
|
126
|
+
<FloatingArrow className={styles.arrow} context={context} ref={arrowRef} />
|
|
137
127
|
</div>,
|
|
138
128
|
{ disabled: !isEnabled || !isShown },
|
|
139
129
|
);
|
package/src/components/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { default as Alert } from './Alert';
|
|
1
2
|
export { default as Badge } from './Badge';
|
|
2
3
|
export { default as Board } from './Board';
|
|
3
4
|
export { default as Button } from './Button';
|
|
@@ -22,6 +23,7 @@ export { default as ResultsInput } from './ResultsInput';
|
|
|
22
23
|
export { default as SeoMessage } from './SeoMessage';
|
|
23
24
|
export { default as Sizer } from './Sizer';
|
|
24
25
|
export { default as Solver } from './Solver';
|
|
26
|
+
export { default as Spinner } from './Spinner';
|
|
25
27
|
export { default as SplashScreen } from './SplashScreen';
|
|
26
28
|
export { default as SvgFontCss } from './SvgFontCss';
|
|
27
29
|
export { default as SvgFontFix } from './SvgFontFix';
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
export { default as useAppLayout } from './useAppLayout';
|
|
1
2
|
export { default as useDirection } from './useDirection';
|
|
2
3
|
export { default as useIsTouchDevice } from './useIsTouchDevice';
|
|
3
4
|
export { default as useLanguage } from './useLanguage';
|
|
4
5
|
export { default as useLocalStorage } from './useLocalStorage';
|
|
5
6
|
export { default as useMediaQuery } from './useMediaQuery';
|
|
6
7
|
export { default as usePortal } from './usePortal';
|
|
7
|
-
export { default as useUniqueId } from './useUniqueId';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { COMPONENTS_SPACING, COMPONENTS_SPACING_SMALL } from 'parameters';
|
|
2
|
+
|
|
3
|
+
import useIsTouchDevice from './useIsTouchDevice';
|
|
4
|
+
import useMediaQuery from './useMediaQuery';
|
|
5
|
+
|
|
6
|
+
const useAppLayout = () => {
|
|
7
|
+
const isTouchDevice = useIsTouchDevice();
|
|
8
|
+
const isLessThanXs = useMediaQuery('<xs');
|
|
9
|
+
const isLessThanS = useMediaQuery('<s');
|
|
10
|
+
const isLessThanM = useMediaQuery('<m');
|
|
11
|
+
const isLessThanL = useMediaQuery('<l');
|
|
12
|
+
const isLessThanXl = useMediaQuery('<xl');
|
|
13
|
+
const showColumn = !isLessThanL;
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
animateTile: !isLessThanXs,
|
|
17
|
+
componentsSpacing: isLessThanXl ? COMPONENTS_SPACING_SMALL : COMPONENTS_SPACING,
|
|
18
|
+
isBoardFullWidth: isLessThanM,
|
|
19
|
+
showColumn,
|
|
20
|
+
showCompactControls: !showColumn,
|
|
21
|
+
showFloatingSolveButton: isTouchDevice,
|
|
22
|
+
showKeyMap: !isTouchDevice,
|
|
23
|
+
showResultsInModal: isLessThanL,
|
|
24
|
+
showShortNav: isLessThanS,
|
|
25
|
+
showTilePoints: !isLessThanXs,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default useAppLayout;
|
package/src/i18n/de.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"common.vowels": "Vokale",
|
|
18
18
|
"common.word": "Wort",
|
|
19
19
|
"common.words": "Wörter",
|
|
20
|
+
"dictionary": "Wörterbuch",
|
|
20
21
|
"dictionary.empty-state.no-definitions": "Wort existiert im Wörterbuch aber hat keine Definition.",
|
|
21
22
|
"dictionary.empty-state.no-results": "Wort kann nicht im Wörterbuch gefunden werden.",
|
|
22
23
|
"dictionary.empty-state.not-allowed": "Dieses Wort ist nicht erlaubt.",
|
package/src/i18n/en.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"common.vowels": "Vowels",
|
|
18
18
|
"common.word": "Word",
|
|
19
19
|
"common.words": "Words",
|
|
20
|
+
"dictionary": "Dictionary",
|
|
20
21
|
"dictionary.empty-state.no-definitions": "Word exists in the dictionary but it does not have a definition.",
|
|
21
22
|
"dictionary.empty-state.no-results": "Unable to find word definition in the dictionary.",
|
|
22
23
|
"dictionary.empty-state.not-allowed": "This word is not allowed.",
|
package/src/i18n/es.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"common.vowels": "Vocales",
|
|
18
18
|
"common.word": "Palabra",
|
|
19
19
|
"common.words": "Palabras",
|
|
20
|
+
"dictionary": "Diccionario",
|
|
20
21
|
"dictionary.empty-state.no-definitions": "La palabra existe en el diccionario pero no tiene una definición.",
|
|
21
22
|
"dictionary.empty-state.no-results": "No se puede encontrar la definición de palabra en el diccionario.",
|
|
22
23
|
"dictionary.empty-state.not-allowed": "Esta palabra no es aceptable.",
|
package/src/i18n/fa.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"common.vowels": "حروف مصوت",
|
|
18
18
|
"common.word": "کلمه",
|
|
19
19
|
"common.words": "کلمات",
|
|
20
|
+
"dictionary": "فرهنگ لغت",
|
|
20
21
|
"dictionary.empty-state.no-definitions": "کلمه در فرهنگ لغت وجود دارد، ولی معنایی برای آن ثبت نشده است.",
|
|
21
22
|
"dictionary.empty-state.no-results": "کلمه در فرهنگ لغت یافت نشد.",
|
|
22
23
|
"dictionary.empty-state.not-allowed": "این کلمه مجاز نیست.",
|
package/src/i18n/fr.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"common.vowels": "Voyelles",
|
|
18
18
|
"common.word": "Mot",
|
|
19
19
|
"common.words": "Mots",
|
|
20
|
+
"dictionary": "Dictionnaire",
|
|
20
21
|
"dictionary.empty-state.no-definitions": "Le mot existe dans le dictionary mais n'a pas de définition.",
|
|
21
22
|
"dictionary.empty-state.no-results": "Impossible de trouver une définition pour ce mot dans le dictionaire.",
|
|
22
23
|
"dictionary.empty-state.not-allowed": "Ce mot n'est pas pas acceptable.",
|
package/src/i18n/index.ts
CHANGED
package/src/i18n/pl.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"common.vowels": "Samogłoski",
|
|
18
18
|
"common.word": "Słowo",
|
|
19
19
|
"common.words": "Słowa",
|
|
20
|
+
"dictionary": "Słownik",
|
|
20
21
|
"dictionary.empty-state.no-definitions": "Słowo istnieje w słowniku ale nie posiada definicji.",
|
|
21
22
|
"dictionary.empty-state.no-results": "Nie udało się znaleźć definicji słowa w słowniku.",
|
|
22
23
|
"dictionary.empty-state.not-allowed": "To słowo nie jest dopuszczalne w grach.",
|
package/src/lib/index.ts
CHANGED
|
@@ -17,7 +17,6 @@ export { default as getRemainingTilesGroups } from './getRemainingTilesGroups';
|
|
|
17
17
|
export { default as getTotalRemainingTilesCount } from './getTotalRemainingTilesCount';
|
|
18
18
|
export { default as getTileSizes } from './getTileSizes';
|
|
19
19
|
export { default as inverseDirection } from './inverseDirection';
|
|
20
|
-
export { default as isCtrl } from './isCtrl';
|
|
21
20
|
export { default as isMac } from './isMac';
|
|
22
21
|
export { default as isRegExp } from './isRegExp';
|
|
23
22
|
export { default as isStringArray } from './isStringArray';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.content {
|
|
2
|
+
height: 100%;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
background-color: var(--color--background--element);
|
|
6
|
+
border: var(--border);
|
|
7
|
+
border-radius: var(--border--radius);
|
|
8
|
+
box-shadow: var(--box-shadow);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.dictionary {
|
|
12
|
+
flex: 1;
|
|
13
|
+
max-height: initial;
|
|
14
|
+
border-bottom: var(--border);
|
|
15
|
+
border-top-left-radius: var(--border--radius);
|
|
16
|
+
border-top-right-radius: var(--border--radius);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.dictionaryInput {
|
|
20
|
+
flex: 0 0 auto;
|
|
21
|
+
border-bottom-left-radius: var(--border--radius);
|
|
22
|
+
border-bottom-right-radius: var(--border--radius);
|
|
23
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FunctionComponent } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Dictionary, DictionaryInput, Modal } from 'components';
|
|
4
|
+
import { useTranslate } from 'state';
|
|
5
|
+
|
|
6
|
+
import styles from './DictionaryModal.module.scss';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
className?: string;
|
|
10
|
+
isOpen: boolean;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const DictionaryModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) => {
|
|
15
|
+
const translate = useTranslate();
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Modal className={className} isOpen={isOpen} title={translate('dictionary')} onClose={onClose}>
|
|
19
|
+
<div className={styles.content}>
|
|
20
|
+
<Dictionary className={styles.dictionary} />
|
|
21
|
+
<DictionaryInput className={styles.dictionaryInput} />
|
|
22
|
+
</div>
|
|
23
|
+
</Modal>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default DictionaryModal;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './DictionaryModal';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { FunctionComponent } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { Modal } from 'components';
|
|
4
|
+
import { useTranslate } from 'state';
|
|
5
5
|
|
|
6
6
|
import { Mapping } from './components';
|
|
7
|
-
import { ARROWS, BACKSPACE,
|
|
7
|
+
import { ARROWS, BACKSPACE, DEL, ENTER, SPACE } from './keys';
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
className?: string;
|
|
@@ -14,7 +14,6 @@ interface Props {
|
|
|
14
14
|
|
|
15
15
|
const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) => {
|
|
16
16
|
const translate = useTranslate();
|
|
17
|
-
const config = useTypedSelector(selectConfig);
|
|
18
17
|
|
|
19
18
|
return (
|
|
20
19
|
<Modal className={className} isOpen={isOpen} title={translate('keyMap')} onClose={onClose}>
|
|
@@ -22,26 +21,11 @@ const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) =
|
|
|
22
21
|
<Mapping description={translate('keyMap.board-and-rack.navigate')} mapping={[ARROWS]} />
|
|
23
22
|
<Mapping description={translate('keyMap.board-and-rack.remove-tile')} mapping={[DEL, BACKSPACE]} />
|
|
24
23
|
<Mapping description={translate('keyMap.board-and-rack.submit')} mapping={[ENTER]} />
|
|
25
|
-
{config.twoCharacterTiles.length > 0 && (
|
|
26
|
-
<Mapping
|
|
27
|
-
description={translate('keyMap.board-and-rack.insert-two-letter-tile')}
|
|
28
|
-
mapping={[
|
|
29
|
-
[
|
|
30
|
-
CTRL,
|
|
31
|
-
<>
|
|
32
|
-
{config.twoCharacterTiles.map(([firstLetter]) => (
|
|
33
|
-
<Key key={firstLetter}>{firstLetter.toUpperCase()}</Key>
|
|
34
|
-
))}
|
|
35
|
-
</>,
|
|
36
|
-
],
|
|
37
|
-
]}
|
|
38
|
-
/>
|
|
39
|
-
)}
|
|
40
24
|
</Modal.Section>
|
|
41
25
|
|
|
42
26
|
<Modal.Section title={translate('keyMap.board')}>
|
|
43
|
-
<Mapping description={translate('keyMap.board.toggle-blank')} mapping={[SPACE
|
|
44
|
-
<Mapping description={translate('keyMap.board.toggle-direction')} mapping={[
|
|
27
|
+
<Mapping description={translate('keyMap.board.toggle-blank')} mapping={[SPACE]} />
|
|
28
|
+
<Mapping description={translate('keyMap.board.toggle-direction')} mapping={[ARROWS]} />
|
|
45
29
|
</Modal.Section>
|
|
46
30
|
|
|
47
31
|
<Modal.Section title={translate('keyMap.rack')}>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FunctionComponent } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Button, Modal } from 'components';
|
|
4
|
-
import { CardChecklist, Cog, Github, Sack } from 'icons';
|
|
4
|
+
import { BookHalf, CardChecklist, Cog, Github, Sack } from 'icons';
|
|
5
5
|
import { GITHUB_PROJECT_URL } from 'parameters';
|
|
6
6
|
import { useTranslate } from 'state';
|
|
7
7
|
|
|
@@ -11,6 +11,7 @@ interface Props {
|
|
|
11
11
|
className?: string;
|
|
12
12
|
isOpen: boolean;
|
|
13
13
|
onClose: () => void;
|
|
14
|
+
onShowDictionary: () => void;
|
|
14
15
|
onShowRemainingTiles: () => void;
|
|
15
16
|
onShowSettings: () => void;
|
|
16
17
|
onShowWords: () => void;
|
|
@@ -20,6 +21,7 @@ const Menu: FunctionComponent<Props> = ({
|
|
|
20
21
|
className,
|
|
21
22
|
isOpen,
|
|
22
23
|
onClose,
|
|
24
|
+
onShowDictionary,
|
|
23
25
|
onShowRemainingTiles,
|
|
24
26
|
onShowSettings,
|
|
25
27
|
onShowWords,
|
|
@@ -41,6 +43,10 @@ const Menu: FunctionComponent<Props> = ({
|
|
|
41
43
|
{translate('words')}
|
|
42
44
|
</Button>
|
|
43
45
|
|
|
46
|
+
<Button aria-label={translate('dictionary')} className={styles.button} Icon={BookHalf} onClick={onShowDictionary}>
|
|
47
|
+
{translate('dictionary')}
|
|
48
|
+
</Button>
|
|
49
|
+
|
|
44
50
|
<Button.Link
|
|
45
51
|
aria-label={translate('github')}
|
|
46
52
|
className={styles.button}
|
|
@@ -1,6 +1,25 @@
|
|
|
1
|
+
@import 'styles/mixins';
|
|
2
|
+
|
|
1
3
|
.content {
|
|
2
4
|
position: relative;
|
|
3
5
|
display: flex;
|
|
4
6
|
flex-direction: column;
|
|
5
7
|
height: 100%;
|
|
8
|
+
gap: var(--spacing--l);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.results {
|
|
12
|
+
flex: 1;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.dictionary {
|
|
16
|
+
flex: 0 0 calc(var(--dictionary--height) - var(--text-input--height));
|
|
17
|
+
background-color: var(--color--background--element);
|
|
18
|
+
border: var(--border);
|
|
19
|
+
border-radius: var(--border--radius);
|
|
20
|
+
box-shadow: var(--box-shadow);
|
|
21
|
+
|
|
22
|
+
@media (max-height: 600px) {
|
|
23
|
+
display: none;
|
|
24
|
+
}
|
|
6
25
|
}
|
|
@@ -2,8 +2,7 @@ import { Result } from '@scrabble-solver/types';
|
|
|
2
2
|
import { FunctionComponent, useMemo } from 'react';
|
|
3
3
|
import { useDispatch } from 'react-redux';
|
|
4
4
|
|
|
5
|
-
import { Modal, Results } from 'components';
|
|
6
|
-
import { useMediaQuery } from 'hooks';
|
|
5
|
+
import { Dictionary, Modal, Results } from 'components';
|
|
7
6
|
import {
|
|
8
7
|
resultsSlice,
|
|
9
8
|
selectResultCandidate,
|
|
@@ -23,7 +22,6 @@ interface Props {
|
|
|
23
22
|
const ResultsModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) => {
|
|
24
23
|
const dispatch = useDispatch();
|
|
25
24
|
const translate = useTranslate();
|
|
26
|
-
const isLessThanS = useMediaQuery('<s');
|
|
27
25
|
const results = useTypedSelector(selectSortedFilteredResults);
|
|
28
26
|
const resultCandidate = useTypedSelector(selectResultCandidate);
|
|
29
27
|
const index = (results || []).findIndex((result) => result.id === resultCandidate?.id);
|
|
@@ -32,20 +30,23 @@ const ResultsModal: FunctionComponent<Props> = ({ className, isOpen, onClose })
|
|
|
32
30
|
const callbacks = useMemo(
|
|
33
31
|
() => ({
|
|
34
32
|
onClick: (result: Result) => {
|
|
35
|
-
|
|
33
|
+
const isSelected = result === resultCandidate;
|
|
36
34
|
|
|
37
|
-
if (
|
|
35
|
+
if (isSelected) {
|
|
38
36
|
onClose();
|
|
37
|
+
} else {
|
|
38
|
+
dispatch(resultsSlice.actions.changeResultCandidate(result));
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
41
|
}),
|
|
42
|
-
[dispatch,
|
|
42
|
+
[dispatch, onClose, resultCandidate],
|
|
43
43
|
);
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<Modal className={className} isOpen={isOpen} title={translate('results')} onClose={onClose}>
|
|
47
47
|
<div className={styles.content}>
|
|
48
|
-
<Results callbacks={callbacks} highlightedIndex={highlightedIndex} />
|
|
48
|
+
<Results callbacks={callbacks} className={styles.results} highlightedIndex={highlightedIndex} />
|
|
49
|
+
<Dictionary className={styles.dictionary} />
|
|
49
50
|
</div>
|
|
50
51
|
</Modal>
|
|
51
52
|
);
|
package/src/modals/index.ts
CHANGED