@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.
Files changed (70) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +6 -6
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/eslint/.cache_8dgz12 +1 -1
  5. package/.next/cache/webpack/client-production/0.pack +0 -0
  6. package/.next/cache/webpack/client-production/index.pack +0 -0
  7. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  8. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  9. package/.next/cache/webpack/server-production/0.pack +0 -0
  10. package/.next/cache/webpack/server-production/index.pack +0 -0
  11. package/.next/prerender-manifest.js +1 -1
  12. package/.next/prerender-manifest.json +1 -1
  13. package/.next/routes-manifest.json +1 -1
  14. package/.next/server/chunks/807.js +1 -1
  15. package/.next/server/chunks/977.js +1 -1
  16. package/.next/server/middleware-build-manifest.js +1 -1
  17. package/.next/server/pages/404.html +1 -1
  18. package/.next/server/pages/500.html +1 -1
  19. package/.next/server/pages/_app.js +1 -1
  20. package/.next/server/pages/_error.js +1 -1
  21. package/.next/server/pages/api/solve.js +1 -1
  22. package/.next/server/pages/index.html +1 -1
  23. package/.next/server/pages/index.js +1 -1
  24. package/.next/server/pages/index.json +1 -1
  25. package/.next/server/pages-manifest.json +1 -1
  26. package/.next/static/{4GWIKe7khKxREyq3ZamDK → QbBIK_mEs1LhYSpQwv6rZ}/_buildManifest.js +1 -1
  27. package/.next/static/chunks/pages/{404-129e0943628b6fab.js → 404-9d5621b1ca024a45.js} +1 -1
  28. package/.next/static/chunks/pages/_app-7bc3bf713c40526a.js +17 -0
  29. package/.next/static/chunks/pages/index-710b5c27542027be.js +1 -0
  30. package/.next/static/css/{f549d7823f599b8d.css → fe70298e6f597553.css} +1 -1
  31. package/.next/trace +44 -44
  32. package/package.json +9 -10
  33. package/src/components/Board/BoardPure.tsx +1 -1
  34. package/src/components/Board/hooks/useGrid.ts +13 -5
  35. package/src/components/Board/lib/index.ts +0 -1
  36. package/src/components/Button/Button.tsx +16 -17
  37. package/src/components/Button/Link.tsx +15 -16
  38. package/src/components/IconButton/IconButton.tsx +8 -8
  39. package/src/components/IconButton/Link.tsx +8 -8
  40. package/src/components/Results/Cell.tsx +4 -5
  41. package/src/components/Results/HeaderButton.tsx +25 -22
  42. package/src/components/Results/Result.tsx +15 -3
  43. package/src/components/Results/Results.module.scss +25 -10
  44. package/src/components/Results/Results.tsx +1 -1
  45. package/src/components/Results/getCoordinatesColumn.ts +15 -0
  46. package/src/components/Results/getLocaleColumns.ts +7 -0
  47. package/src/components/Results/types.ts +1 -0
  48. package/src/components/Results/useColumns.ts +9 -6
  49. package/src/components/Tooltip/Tooltip.tsx +28 -0
  50. package/src/components/Tooltip/TooltipContent.tsx +53 -0
  51. package/src/components/Tooltip/TooltipTrigger.tsx +26 -0
  52. package/src/components/Tooltip/context.ts +17 -0
  53. package/src/components/Tooltip/index.ts +1 -1
  54. package/src/components/Tooltip/useTooltip.ts +54 -0
  55. package/src/components/index.ts +1 -1
  56. package/src/hooks/index.ts +0 -1
  57. package/src/lib/getCoordinates.ts +18 -0
  58. package/src/lib/groupResults.ts +16 -11
  59. package/src/lib/index.ts +2 -1
  60. package/src/lib/sortResults.ts +1 -0
  61. package/src/pages/_app.tsx +5 -1
  62. package/src/parameters/index.ts +1 -0
  63. package/src/types/index.ts +1 -0
  64. package/.next/static/chunks/pages/_app-cccda36d00fa2328.js +0 -17
  65. package/.next/static/chunks/pages/index-caaf20b2488cb10e.js +0 -1
  66. package/src/components/Tooltip/useTooltip.tsx +0 -134
  67. package/src/hooks/usePortal.tsx +0 -47
  68. package/src/lib/canUseDom.ts +0 -3
  69. /package/.next/static/{4GWIKe7khKxREyq3ZamDK → QbBIK_mEs1LhYSpQwv6rZ}/_ssgManifest.js +0 -0
  70. /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.6",
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.6",
34
- "@scrabble-solver/constants": "^2.13.6",
35
- "@scrabble-solver/dictionaries": "^2.13.6",
36
- "@scrabble-solver/logger": "^2.13.6",
37
- "@scrabble-solver/solver": "^2.13.6",
38
- "@scrabble-solver/types": "^2.13.6",
39
- "@scrabble-solver/word-definitions": "^2.13.6",
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": "940c0be24a464f8db603e4f6e7cb727486e9b154"
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(activeIndex.x + offsetX, 0), width - 1);
60
- const y = Math.min(Math.max(activeIndex.y + offsetY, 0), height - 1);
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
- [activeIndex, inputRefs],
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,3 +1,2 @@
1
1
  export { default as getBonusColor } from './getBonusColor';
2
- export { default as getCoordinate } from './getCoordinate';
3
2
  export { default as getPositionInGrid } from './getPositionInGrid';
@@ -1,7 +1,7 @@
1
1
  import classNames from 'classnames';
2
2
  import { ButtonHTMLAttributes, FunctionComponent, ReactNode, SVGAttributes } from 'react';
3
3
 
4
- import { useTooltip } from '../Tooltip';
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
- <button
30
- className={classNames(styles.button, className, {
31
- [styles.default]: variant === 'default',
32
- [styles.primary]: variant === 'primary',
33
- })}
34
- type="button"
35
- {...props}
36
- {...triggerProps}
37
- >
38
- <span className={styles.content}>
39
- {Icon && <Icon className={classNames(styles.icon, iconClassName)} />}
40
- {children && <span className={styles.label}>{children}</span>}
41
- </span>
42
- </button>
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 { useTooltip } from '../Tooltip';
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
- <a
30
- className={classNames(styles.button, className, {
31
- [styles.default]: variant === 'default',
32
- [styles.primary]: variant === 'primary',
33
- })}
34
- {...props}
35
- {...triggerProps}
36
- >
37
- <span className={styles.content}>
38
- {Icon && <Icon className={classNames(styles.icon, iconClassName)} />}
39
- {children && <span className={styles.label}>{children}</span>}
40
- </span>
41
- </a>
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 { useTooltip } from '../Tooltip';
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
- <button className={classNames(styles.iconButton, className)} type="button" {...props} {...triggerProps}>
22
- <span className={styles.content}>
23
- <Icon className={styles.icon} />
24
- </span>
25
- </button>
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 { useTooltip } from '../Tooltip';
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
- <a className={classNames(styles.iconButton, className)} {...props} {...triggerProps}>
21
- <span className={styles.content}>
22
- <Icon className={styles.icon} />
23
- </span>
24
- </a>
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 { useTooltip } from '../Tooltip';
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
- <div className={classNames(styles.cell, className)} {...triggerProps}>
27
- {children || formattedValue}
28
- </div>
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 { useTooltip } from '../Tooltip';
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
- <button
30
- aria-label={translate(column.translationKey)}
31
- className={classNames(styles.headerButton, column.className)}
32
- key={column.id}
33
- type="button"
34
- onClick={handleClick}
35
- {...triggerProps}
36
- >
37
- <span className={styles.cell}>
38
- <span className={styles.headerButtonLabel}>{translate(column.translationKey)}</span>
39
-
40
- {sort.column === column.id && (
41
- <>
42
- {sort.direction === SortDirection.Ascending && <SortUp className={styles.sortIcon} />}
43
- {sort.direction === SortDirection.Descending && <SortDown className={styles.sortIcon} />}
44
- </>
45
- )}
46
- </span>
47
- </button>
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 { selectIsResultMatching, selectLocale, selectResultsQuery, useTypedSelector } from 'state';
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
- .result &:first-child,
155
- .headerButton:first-child & {
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
 
@@ -7,6 +7,7 @@ export interface Column {
7
7
  className: string;
8
8
  id: ResultColumn;
9
9
  translationKey: TranslationKey;
10
+ sortable?: boolean;
10
11
  }
11
12
 
12
13
  export interface ResultCallbacks {
@@ -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 localeColumns.filter((column) => COLUMNS_XS.includes(column.id));
26
+ return columns.filter((column) => COLUMNS_XS.includes(column.id));
24
27
  }
25
28
 
26
29
  if (isLessThanS) {
27
- return localeColumns.filter((column) => COLUMNS_S.includes(column.id));
30
+ return columns.filter((column) => COLUMNS_S.includes(column.id));
28
31
  }
29
32
 
30
33
  if (isLessThanM) {
31
- return localeColumns.filter((column) => COLUMNS_M.includes(column.id));
34
+ return columns.filter((column) => COLUMNS_M.includes(column.id));
32
35
  }
33
36
 
34
37
  if (isLessThanL) {
35
- return localeColumns.filter((column) => COLUMNS_L.includes(column.id));
38
+ return columns.filter((column) => COLUMNS_L.includes(column.id));
36
39
  }
37
40
 
38
- return localeColumns;
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
+ };