@scrabble-solver/scrabble-solver 2.13.6 → 2.13.7
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/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/_app.js +1 -1
- package/.next/server/pages/_error.js +1 -1
- package/.next/server/pages/api/solve.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/server/pages-manifest.json +1 -1
- package/.next/static/{4GWIKe7khKxREyq3ZamDK → QbBIK_mEs1LhYSpQwv6rZ}/_buildManifest.js +1 -1
- package/.next/static/chunks/pages/{404-129e0943628b6fab.js → 404-9d5621b1ca024a45.js} +1 -1
- package/.next/static/chunks/pages/_app-7bc3bf713c40526a.js +17 -0
- package/.next/static/chunks/pages/index-710b5c27542027be.js +1 -0
- package/.next/static/css/{f549d7823f599b8d.css → fe70298e6f597553.css} +1 -1
- package/.next/trace +44 -44
- package/package.json +9 -10
- package/src/components/Board/BoardPure.tsx +1 -1
- package/src/components/Board/hooks/useGrid.ts +13 -5
- package/src/components/Board/lib/index.ts +0 -1
- package/src/components/Button/Button.tsx +16 -17
- package/src/components/Button/Link.tsx +15 -16
- package/src/components/IconButton/IconButton.tsx +8 -8
- package/src/components/IconButton/Link.tsx +8 -8
- package/src/components/Results/Cell.tsx +4 -5
- package/src/components/Results/HeaderButton.tsx +25 -22
- package/src/components/Results/Result.tsx +15 -3
- package/src/components/Results/Results.module.scss +25 -10
- package/src/components/Results/Results.tsx +1 -1
- package/src/components/Results/getCoordinatesColumn.ts +15 -0
- package/src/components/Results/getLocaleColumns.ts +7 -0
- package/src/components/Results/types.ts +1 -0
- package/src/components/Results/useColumns.ts +9 -6
- package/src/components/Tooltip/Tooltip.tsx +28 -0
- package/src/components/Tooltip/TooltipContent.tsx +53 -0
- package/src/components/Tooltip/TooltipTrigger.tsx +26 -0
- package/src/components/Tooltip/context.ts +17 -0
- package/src/components/Tooltip/index.ts +1 -1
- package/src/components/Tooltip/useTooltip.ts +54 -0
- package/src/components/index.ts +1 -1
- package/src/hooks/index.ts +0 -1
- package/src/lib/getCoordinates.ts +18 -0
- package/src/lib/groupResults.ts +16 -11
- package/src/lib/index.ts +2 -1
- package/src/lib/sortResults.ts +1 -0
- package/src/pages/_app.tsx +5 -1
- package/src/parameters/index.ts +1 -0
- package/src/types/index.ts +1 -0
- package/.next/static/chunks/pages/_app-cccda36d00fa2328.js +0 -17
- package/.next/static/chunks/pages/index-caaf20b2488cb10e.js +0 -1
- package/src/components/Tooltip/useTooltip.tsx +0 -134
- package/src/hooks/usePortal.tsx +0 -47
- package/src/lib/canUseDom.ts +0 -3
- /package/.next/static/{4GWIKe7khKxREyq3ZamDK → QbBIK_mEs1LhYSpQwv6rZ}/_ssgManifest.js +0 -0
- /package/src/{components/Board/lib → lib}/getCoordinate.ts +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.7",
|
|
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.16",
|
|
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.7",
|
|
34
|
+
"@scrabble-solver/constants": "^2.13.7",
|
|
35
|
+
"@scrabble-solver/dictionaries": "^2.13.7",
|
|
36
|
+
"@scrabble-solver/logger": "^2.13.7",
|
|
37
|
+
"@scrabble-solver/solver": "^2.13.7",
|
|
38
|
+
"@scrabble-solver/types": "^2.13.7",
|
|
39
|
+
"@scrabble-solver/word-definitions": "^2.13.7",
|
|
40
40
|
"classnames": "^2.5.1",
|
|
41
41
|
"env-cmd": "^10.1.0",
|
|
42
42
|
"include-media": "^2.0.0",
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
"react-dom": "^18.3.1",
|
|
49
49
|
"react-highlight-words": "^0.20.0",
|
|
50
50
|
"react-modal": "^3.16.1",
|
|
51
|
-
"react-portal": "^4.2.2",
|
|
52
51
|
"react-redux": "^9.1.2",
|
|
53
52
|
"react-window": "^1.8.10",
|
|
54
53
|
"redux-saga": "^1.3.0",
|
|
@@ -73,5 +72,5 @@
|
|
|
73
72
|
"@types/redux-saga": "^0.10.5",
|
|
74
73
|
"sass": "^1.77.4"
|
|
75
74
|
},
|
|
76
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "6aec8ec770488560a1db1ccc535ebf3886bdd4ec"
|
|
77
76
|
}
|
|
@@ -13,12 +13,12 @@ import {
|
|
|
13
13
|
} from 'react';
|
|
14
14
|
|
|
15
15
|
import { FlagFill } from 'icons';
|
|
16
|
+
import { getCoordinate } from 'lib';
|
|
16
17
|
import { BORDER_WIDTH } from 'parameters';
|
|
17
18
|
import { Point } from 'types';
|
|
18
19
|
|
|
19
20
|
import styles from './Board.module.scss';
|
|
20
21
|
import { Cell } from './components';
|
|
21
|
-
import { getCoordinate } from './lib';
|
|
22
22
|
|
|
23
23
|
interface Props {
|
|
24
24
|
className?: string;
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
ChangeEvent,
|
|
7
7
|
ChangeEventHandler,
|
|
8
8
|
ClipboardEventHandler,
|
|
9
|
-
createRef,
|
|
10
9
|
KeyboardEventHandler,
|
|
11
10
|
RefObject,
|
|
11
|
+
createRef,
|
|
12
12
|
useCallback,
|
|
13
13
|
useMemo,
|
|
14
14
|
useState,
|
|
@@ -54,14 +54,22 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
|
|
|
54
54
|
const [direction, setLastDirection] = useState<Direction>('horizontal');
|
|
55
55
|
const directionRef = useLatest(direction);
|
|
56
56
|
|
|
57
|
+
const safeActiveIndex = useMemo(
|
|
58
|
+
() => ({
|
|
59
|
+
x: Math.min(activeIndex.x, width - 1),
|
|
60
|
+
y: Math.min(activeIndex.y, height - 1),
|
|
61
|
+
}),
|
|
62
|
+
[activeIndex, width, height],
|
|
63
|
+
);
|
|
64
|
+
|
|
57
65
|
const changeActiveIndex = useCallback(
|
|
58
66
|
(offsetX: number, offsetY: number) => {
|
|
59
|
-
const x = Math.min(Math.max(
|
|
60
|
-
const y = Math.min(Math.max(
|
|
67
|
+
const x = Math.min(Math.max(safeActiveIndex.x + offsetX, 0), width - 1);
|
|
68
|
+
const y = Math.min(Math.max(safeActiveIndex.y + offsetY, 0), height - 1);
|
|
61
69
|
setActiveIndex({ x, y });
|
|
62
70
|
inputRefs[y][x].current?.focus();
|
|
63
71
|
},
|
|
64
|
-
[
|
|
72
|
+
[safeActiveIndex, inputRefs],
|
|
65
73
|
);
|
|
66
74
|
|
|
67
75
|
const getInputRefPosition = useCallback(
|
|
@@ -358,7 +366,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
|
|
|
358
366
|
);
|
|
359
367
|
|
|
360
368
|
return [
|
|
361
|
-
{ activeIndex, direction, inputRefs },
|
|
369
|
+
{ activeIndex: safeActiveIndex, direction, inputRefs },
|
|
362
370
|
{ insertValue, onChange, onDirectionToggle, onFocus, onKeyDown, onPaste },
|
|
363
371
|
];
|
|
364
372
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { ButtonHTMLAttributes, FunctionComponent, ReactNode, SVGAttributes } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { Tooltip } from '../Tooltip';
|
|
5
5
|
|
|
6
6
|
import styles from './Button.module.scss';
|
|
7
7
|
import Link from './Link';
|
|
@@ -23,23 +23,22 @@ const Button: FunctionComponent<Props> = ({
|
|
|
23
23
|
variant = 'default',
|
|
24
24
|
...props
|
|
25
25
|
}) => {
|
|
26
|
-
const triggerProps = useTooltip(tooltip, props);
|
|
27
|
-
|
|
28
26
|
return (
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
<Tooltip tooltip={tooltip}>
|
|
28
|
+
<button
|
|
29
|
+
className={classNames(styles.button, className, {
|
|
30
|
+
[styles.default]: variant === 'default',
|
|
31
|
+
[styles.primary]: variant === 'primary',
|
|
32
|
+
})}
|
|
33
|
+
type="button"
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
36
|
+
<span className={styles.content}>
|
|
37
|
+
{Icon && <Icon className={classNames(styles.icon, iconClassName)} />}
|
|
38
|
+
{children && <span className={styles.label}>{children}</span>}
|
|
39
|
+
</span>
|
|
40
|
+
</button>
|
|
41
|
+
</Tooltip>
|
|
43
42
|
);
|
|
44
43
|
};
|
|
45
44
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { AnchorHTMLAttributes, FunctionComponent, ReactNode, SVGAttributes } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { Tooltip } from '../Tooltip';
|
|
5
5
|
|
|
6
6
|
import styles from './Button.module.scss';
|
|
7
7
|
|
|
@@ -23,22 +23,21 @@ const Link: FunctionComponent<Props> = ({
|
|
|
23
23
|
variant = 'default',
|
|
24
24
|
...props
|
|
25
25
|
}) => {
|
|
26
|
-
const triggerProps = useTooltip(tooltip, props);
|
|
27
|
-
|
|
28
26
|
return (
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
<Tooltip tooltip={tooltip}>
|
|
28
|
+
<a
|
|
29
|
+
className={classNames(styles.button, className, {
|
|
30
|
+
[styles.default]: variant === 'default',
|
|
31
|
+
[styles.primary]: variant === 'primary',
|
|
32
|
+
})}
|
|
33
|
+
{...props}
|
|
34
|
+
>
|
|
35
|
+
<span className={styles.content}>
|
|
36
|
+
{Icon && <Icon className={classNames(styles.icon, iconClassName)} />}
|
|
37
|
+
{children && <span className={styles.label}>{children}</span>}
|
|
38
|
+
</span>
|
|
39
|
+
</a>
|
|
40
|
+
</Tooltip>
|
|
42
41
|
);
|
|
43
42
|
};
|
|
44
43
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { ButtonHTMLAttributes, FunctionComponent, MouseEventHandler, SVGAttributes } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { Tooltip } from '../Tooltip';
|
|
5
5
|
|
|
6
6
|
import styles from './IconButton.module.scss';
|
|
7
7
|
import Link from './Link';
|
|
@@ -15,14 +15,14 @@ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const IconButton: FunctionComponent<Props> = ({ className, Icon, tooltip, ...props }) => {
|
|
18
|
-
const triggerProps = useTooltip(tooltip, props);
|
|
19
|
-
|
|
20
18
|
return (
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
<Tooltip tooltip={tooltip}>
|
|
20
|
+
<button className={classNames(styles.iconButton, className)} type="button" {...props}>
|
|
21
|
+
<span className={styles.content}>
|
|
22
|
+
<Icon className={styles.icon} />
|
|
23
|
+
</span>
|
|
24
|
+
</button>
|
|
25
|
+
</Tooltip>
|
|
26
26
|
);
|
|
27
27
|
};
|
|
28
28
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
2
|
import { AnchorHTMLAttributes, FunctionComponent, SVGAttributes } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { Tooltip } from '../Tooltip';
|
|
5
5
|
|
|
6
6
|
import styles from './IconButton.module.scss';
|
|
7
7
|
|
|
@@ -14,14 +14,14 @@ interface Props extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const Link: FunctionComponent<Props> = ({ className, Icon, tooltip, ...props }) => {
|
|
17
|
-
const triggerProps = useTooltip(tooltip, props);
|
|
18
|
-
|
|
19
17
|
return (
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
<Tooltip tooltip={tooltip}>
|
|
19
|
+
<a className={classNames(styles.iconButton, className)} {...props}>
|
|
20
|
+
<span className={styles.content}>
|
|
21
|
+
<Icon className={styles.icon} />
|
|
22
|
+
</span>
|
|
23
|
+
</a>
|
|
24
|
+
</Tooltip>
|
|
25
25
|
);
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -4,7 +4,7 @@ import { FunctionComponent, ReactNode } from 'react';
|
|
|
4
4
|
import { selectLocale, useTranslate, useTypedSelector } from 'state';
|
|
5
5
|
import { TranslationKey } from 'types';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Tooltip } from '../Tooltip';
|
|
8
8
|
|
|
9
9
|
import styles from './Results.module.scss';
|
|
10
10
|
|
|
@@ -20,12 +20,11 @@ const Cell: FunctionComponent<Props> = ({ children, className, translationKey, t
|
|
|
20
20
|
const translate = useTranslate();
|
|
21
21
|
const locale = useTypedSelector(selectLocale);
|
|
22
22
|
const formattedValue = value.toLocaleString(locale);
|
|
23
|
-
const triggerProps = useTooltip(`${translate(translationKey)}: ${tooltip || formattedValue}`);
|
|
24
23
|
|
|
25
24
|
return (
|
|
26
|
-
<
|
|
27
|
-
{children || formattedValue}
|
|
28
|
-
</
|
|
25
|
+
<Tooltip tooltip={`${translate(translationKey)}: ${tooltip || formattedValue}`}>
|
|
26
|
+
<div className={classNames(styles.cell, className)}>{children || formattedValue}</div>
|
|
27
|
+
</Tooltip>
|
|
29
28
|
);
|
|
30
29
|
};
|
|
31
30
|
|
|
@@ -6,45 +6,48 @@ import { SortDown, SortUp } from 'icons';
|
|
|
6
6
|
import { resultsSlice, selectResultsSort, useTranslate, useTypedSelector } from 'state';
|
|
7
7
|
import { SortDirection } from 'types';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { Tooltip } from '../Tooltip';
|
|
10
10
|
|
|
11
11
|
import styles from './Results.module.scss';
|
|
12
12
|
import { Column } from './types';
|
|
13
13
|
|
|
14
14
|
interface Props {
|
|
15
15
|
column: Column;
|
|
16
|
+
sortable?: boolean;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const HeaderButton = ({ column }: Props): ReactElement => {
|
|
19
|
+
const HeaderButton = ({ column, sortable }: Props): ReactElement => {
|
|
19
20
|
const dispatch = useDispatch();
|
|
20
21
|
const translate = useTranslate();
|
|
21
22
|
const sort = useTypedSelector(selectResultsSort);
|
|
22
|
-
const triggerProps = useTooltip(translate(column.translationKey));
|
|
23
23
|
|
|
24
24
|
const handleClick = useCallback(() => {
|
|
25
25
|
dispatch(resultsSlice.actions.sort(column.id));
|
|
26
26
|
}, [column.id, dispatch]);
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
<Tooltip tooltip={translate(column.translationKey)}>
|
|
30
|
+
<button
|
|
31
|
+
aria-label={translate(column.translationKey)}
|
|
32
|
+
className={classNames(styles.headerButton, column.className, {
|
|
33
|
+
[styles.sortable]: sortable,
|
|
34
|
+
})}
|
|
35
|
+
key={column.id}
|
|
36
|
+
type="button"
|
|
37
|
+
onClick={sortable ? handleClick : undefined}
|
|
38
|
+
>
|
|
39
|
+
<span className={styles.cell}>
|
|
40
|
+
<span className={styles.headerButtonLabel}>{translate(column.translationKey)}</span>
|
|
41
|
+
|
|
42
|
+
{sort.column === column.id && (
|
|
43
|
+
<>
|
|
44
|
+
{sort.direction === SortDirection.Ascending && <SortUp className={styles.sortIcon} />}
|
|
45
|
+
{sort.direction === SortDirection.Descending && <SortDown className={styles.sortIcon} />}
|
|
46
|
+
</>
|
|
47
|
+
)}
|
|
48
|
+
</span>
|
|
49
|
+
</button>
|
|
50
|
+
</Tooltip>
|
|
48
51
|
);
|
|
49
52
|
};
|
|
50
53
|
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { CSSProperties, FocusEventHandler, MouseEventHandler, ReactElement, useRef } from 'react';
|
|
2
|
+
import { CSSProperties, FocusEventHandler, MouseEventHandler, ReactElement, useMemo, useRef } from 'react';
|
|
3
3
|
import Highlighter from 'react-highlight-words';
|
|
4
4
|
|
|
5
5
|
import { LOCALE_FEATURES } from 'i18n';
|
|
6
|
-
import { noop } from 'lib';
|
|
7
|
-
import {
|
|
6
|
+
import { getCoordinates, noop } from 'lib';
|
|
7
|
+
import {
|
|
8
|
+
selectIsResultMatching,
|
|
9
|
+
selectLocale,
|
|
10
|
+
selectResultsQuery,
|
|
11
|
+
selectShowCoordinates,
|
|
12
|
+
useTypedSelector,
|
|
13
|
+
} from 'state';
|
|
8
14
|
import { ResultColumn } from 'types';
|
|
9
15
|
|
|
10
16
|
import Cell from './Cell';
|
|
@@ -31,12 +37,14 @@ const Result = ({ data, index, style }: Props): ReactElement => {
|
|
|
31
37
|
const ref = useRef<HTMLButtonElement>(null);
|
|
32
38
|
const columns = useColumns();
|
|
33
39
|
const locale = useTypedSelector(selectLocale);
|
|
40
|
+
const showCoordinates = useTypedSelector(selectShowCoordinates);
|
|
34
41
|
const query = useTypedSelector(selectResultsQuery);
|
|
35
42
|
const { consonants, direction, separator, vowels } = LOCALE_FEATURES[locale];
|
|
36
43
|
const result = results[index];
|
|
37
44
|
const isMatching = useTypedSelector((state) => selectIsResultMatching(state, index));
|
|
38
45
|
const words = direction === 'rtl' ? [...result.words].reverse() : result.words;
|
|
39
46
|
const enabledColumns = Object.fromEntries(columns.map((column) => [column.id, true]));
|
|
47
|
+
const coordinates = useMemo(() => getCoordinates(result, showCoordinates), [result, showCoordinates]);
|
|
40
48
|
|
|
41
49
|
const handleClick: MouseEventHandler = (event) => onClick(result, event);
|
|
42
50
|
const handleMouseEnter: MouseEventHandler = (event) => onMouseEnter(result, event);
|
|
@@ -61,6 +69,10 @@ const Result = ({ data, index, style }: Props): ReactElement => {
|
|
|
61
69
|
onMouseLeave={handleMouseLeave}
|
|
62
70
|
>
|
|
63
71
|
<span className={styles.resultContent}>
|
|
72
|
+
{enabledColumns[ResultColumn.Coordinates] && (
|
|
73
|
+
<Cell className={styles.coordinates} translationKey="settings.showCoordinates" value={coordinates} />
|
|
74
|
+
)}
|
|
75
|
+
|
|
64
76
|
{enabledColumns[ResultColumn.Word] && (
|
|
65
77
|
<Cell className={styles.word} translationKey="common.word" value={result.word}>
|
|
66
78
|
<Highlighter highlightClassName={styles.highlight} searchWords={[query]} textToHighlight={result.word} />
|
|
@@ -55,19 +55,11 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
55
55
|
|
|
56
56
|
.headerButton {
|
|
57
57
|
@include button-reset;
|
|
58
|
-
@include focus-effect;
|
|
59
58
|
|
|
60
|
-
cursor: pointer;
|
|
61
59
|
text-transform: uppercase;
|
|
62
60
|
transition: var(--transition);
|
|
63
61
|
background-color: var(--color--background);
|
|
64
62
|
|
|
65
|
-
&:focus,
|
|
66
|
-
&:hover {
|
|
67
|
-
background-color: var(--color--primary);
|
|
68
|
-
color: var(--color--primary--opposite);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
63
|
&:first-child {
|
|
72
64
|
[dir='ltr'] & {
|
|
73
65
|
border-top-left-radius: inherit;
|
|
@@ -89,6 +81,18 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
89
81
|
}
|
|
90
82
|
}
|
|
91
83
|
|
|
84
|
+
.sortable {
|
|
85
|
+
@include focus-effect;
|
|
86
|
+
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
|
|
89
|
+
&:focus,
|
|
90
|
+
&:hover {
|
|
91
|
+
background-color: var(--color--primary);
|
|
92
|
+
color: var(--color--primary--opposite);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
92
96
|
.headerButtonLabel {
|
|
93
97
|
@include ellipsis;
|
|
94
98
|
|
|
@@ -139,6 +143,7 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
139
143
|
|
|
140
144
|
.word {
|
|
141
145
|
@include ellipsis;
|
|
146
|
+
display: flex;
|
|
142
147
|
}
|
|
143
148
|
}
|
|
144
149
|
|
|
@@ -151,10 +156,13 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
151
156
|
gap: var(--spacing--s);
|
|
152
157
|
line-height: var(--results--item--height);
|
|
153
158
|
|
|
154
|
-
|
|
155
|
-
.headerButton
|
|
159
|
+
&.word,
|
|
160
|
+
.headerButton.word & {
|
|
156
161
|
justify-content: flex-start;
|
|
162
|
+
}
|
|
157
163
|
|
|
164
|
+
.result &:first-child,
|
|
165
|
+
.headerButton:first-child & {
|
|
158
166
|
[dir='ltr'] & {
|
|
159
167
|
padding-left: $row-padding-horizontal;
|
|
160
168
|
text-align: left;
|
|
@@ -203,6 +211,13 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
|
|
|
203
211
|
font-weight: bold;
|
|
204
212
|
}
|
|
205
213
|
|
|
214
|
+
.coordinates {
|
|
215
|
+
$width: 50px;
|
|
216
|
+
|
|
217
|
+
flex: 0 0 $width;
|
|
218
|
+
max-width: $width;
|
|
219
|
+
}
|
|
220
|
+
|
|
206
221
|
.solveButton {
|
|
207
222
|
display: block;
|
|
208
223
|
margin: var(--spacing--xl) auto 0;
|
|
@@ -69,7 +69,7 @@ const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIn
|
|
|
69
69
|
<div className={classNames(styles.results, className)}>
|
|
70
70
|
<div className={styles.header}>
|
|
71
71
|
{columns.map((column) => (
|
|
72
|
-
<HeaderButton column={column} key={column.id} />
|
|
72
|
+
<HeaderButton column={column} key={column.id} sortable={column.sortable} />
|
|
73
73
|
))}
|
|
74
74
|
</div>
|
|
75
75
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ResultColumn } from 'types';
|
|
2
|
+
|
|
3
|
+
import styles from './Results.module.scss';
|
|
4
|
+
import { Column } from './types';
|
|
5
|
+
|
|
6
|
+
const getCoordinatesColumn = (): Column => {
|
|
7
|
+
return {
|
|
8
|
+
className: styles.coordinates,
|
|
9
|
+
id: ResultColumn.Coordinates,
|
|
10
|
+
translationKey: 'settings.showCoordinates',
|
|
11
|
+
sortable: false,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default getCoordinatesColumn;
|
|
@@ -10,11 +10,13 @@ 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,
|
|
13
14
|
},
|
|
14
15
|
{
|
|
15
16
|
className: styles.stat,
|
|
16
17
|
id: ResultColumn.TilesCount,
|
|
17
18
|
translationKey: 'common.tiles',
|
|
19
|
+
sortable: true,
|
|
18
20
|
},
|
|
19
21
|
];
|
|
20
22
|
|
|
@@ -23,6 +25,7 @@ const getLocaleColumns = (options: { consonants: boolean; vowels: boolean }): Co
|
|
|
23
25
|
className: styles.stat,
|
|
24
26
|
id: ResultColumn.ConsonantsCount,
|
|
25
27
|
translationKey: 'common.consonants',
|
|
28
|
+
sortable: true,
|
|
26
29
|
});
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -31,6 +34,7 @@ const getLocaleColumns = (options: { consonants: boolean; vowels: boolean }): Co
|
|
|
31
34
|
className: styles.stat,
|
|
32
35
|
id: ResultColumn.VowelsCount,
|
|
33
36
|
translationKey: 'common.vowels',
|
|
37
|
+
sortable: true,
|
|
34
38
|
});
|
|
35
39
|
}
|
|
36
40
|
|
|
@@ -39,16 +43,19 @@ const getLocaleColumns = (options: { consonants: boolean; vowels: boolean }): Co
|
|
|
39
43
|
className: styles.stat,
|
|
40
44
|
id: ResultColumn.BlanksCount,
|
|
41
45
|
translationKey: 'common.blanks',
|
|
46
|
+
sortable: true,
|
|
42
47
|
},
|
|
43
48
|
{
|
|
44
49
|
className: styles.stat,
|
|
45
50
|
id: ResultColumn.WordsCount,
|
|
46
51
|
translationKey: 'common.words',
|
|
52
|
+
sortable: true,
|
|
47
53
|
},
|
|
48
54
|
{
|
|
49
55
|
className: styles.points,
|
|
50
56
|
id: ResultColumn.Points,
|
|
51
57
|
translationKey: 'common.points',
|
|
58
|
+
sortable: true,
|
|
52
59
|
},
|
|
53
60
|
);
|
|
54
61
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { useMediaQueries } from 'hooks';
|
|
2
2
|
import { LOCALE_FEATURES } from 'i18n';
|
|
3
|
-
import { selectLocale, useTypedSelector } from 'state';
|
|
3
|
+
import { selectLocale, selectShowCoordinates, useTypedSelector } from 'state';
|
|
4
4
|
import { ResultColumn } from 'types';
|
|
5
5
|
|
|
6
|
+
import getCoordinatesColumn from './getCoordinatesColumn';
|
|
6
7
|
import getLocaleColumns from './getLocaleColumns';
|
|
7
8
|
import { Column } from './types';
|
|
8
9
|
|
|
@@ -17,25 +18,27 @@ const COLUMNS_L = [...COLUMNS_XS];
|
|
|
17
18
|
const useColumns = (): Column[] => {
|
|
18
19
|
const locale = useTypedSelector(selectLocale);
|
|
19
20
|
const localeColumns = getLocaleColumns(LOCALE_FEATURES[locale]);
|
|
21
|
+
const showCoordinates = useTypedSelector(selectShowCoordinates);
|
|
22
|
+
const columns = showCoordinates === 'hidden' ? localeColumns : [getCoordinatesColumn(), ...localeColumns];
|
|
20
23
|
const { isLessThanXs, isLessThanS, isLessThanM, isLessThanL } = useMediaQueries();
|
|
21
24
|
|
|
22
25
|
if (isLessThanXs) {
|
|
23
|
-
return
|
|
26
|
+
return columns.filter((column) => COLUMNS_XS.includes(column.id));
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
if (isLessThanS) {
|
|
27
|
-
return
|
|
30
|
+
return columns.filter((column) => COLUMNS_S.includes(column.id));
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
if (isLessThanM) {
|
|
31
|
-
return
|
|
34
|
+
return columns.filter((column) => COLUMNS_M.includes(column.id));
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
if (isLessThanL) {
|
|
35
|
-
return
|
|
38
|
+
return columns.filter((column) => COLUMNS_L.includes(column.id));
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
return
|
|
41
|
+
return columns;
|
|
39
42
|
};
|
|
40
43
|
|
|
41
44
|
export default useColumns;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Placement } from '@floating-ui/react';
|
|
2
|
+
import { FunctionComponent, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
import { TooltipContext } from './context';
|
|
5
|
+
import { TooltipContent } from './TooltipContent';
|
|
6
|
+
import { TooltipTrigger } from './TooltipTrigger';
|
|
7
|
+
import { useTooltip } from './useTooltip';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
placement?: Placement;
|
|
12
|
+
tooltip?: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Tooltip: FunctionComponent<Props> = ({ children, placement, tooltip }) => {
|
|
16
|
+
const state = useTooltip({ placement });
|
|
17
|
+
|
|
18
|
+
if (!tooltip) {
|
|
19
|
+
return children;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<TooltipContext.Provider value={state}>
|
|
24
|
+
<TooltipTrigger>{children}</TooltipTrigger>
|
|
25
|
+
<TooltipContent>{tooltip}</TooltipContent>
|
|
26
|
+
</TooltipContext.Provider>
|
|
27
|
+
);
|
|
28
|
+
};
|