@scrabble-solver/scrabble-solver 2.13.12 → 2.13.13

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 (89) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +14 -14
  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/client-production/index.pack.old +0 -0
  8. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  9. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  10. package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  11. package/.next/cache/webpack/server-production/0.pack +0 -0
  12. package/.next/cache/webpack/server-production/index.pack +0 -0
  13. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  14. package/.next/prerender-manifest.js +1 -1
  15. package/.next/prerender-manifest.json +1 -1
  16. package/.next/routes-manifest.json +1 -1
  17. package/.next/server/chunks/807.js +1 -1
  18. package/.next/server/middleware-build-manifest.js +1 -1
  19. package/.next/server/pages/404.html +1 -1
  20. package/.next/server/pages/500.html +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/chunks/{main-b5b360c6afb66b05.js → main-8b0b4e610892a916.js} +1 -1
  27. package/.next/static/chunks/pages/{404-0c9f3e0f8b15f487.js → 404-b0c2ccded2455be0.js} +1 -1
  28. package/.next/static/chunks/pages/_app-42ce6b4032e931ff.js +17 -0
  29. package/.next/static/chunks/pages/index-3718075f2ba2220c.js +1 -0
  30. package/.next/static/chunks/webpack-c4acd79e87956a0e.js +1 -0
  31. package/.next/static/css/2adc9736d823979b.css +2 -0
  32. package/.next/static/{N8hSsS6Ppzlj3ebHMSZvI → qwJjm2FeDHHGY92CY5oQQ}/_buildManifest.js +1 -1
  33. package/.next/trace +45 -45
  34. package/package.json +13 -13
  35. package/src/components/Board/Board.module.scss +3 -9
  36. package/src/components/Board/Board.tsx +2 -2
  37. package/src/components/Board/BoardPure.tsx +3 -2
  38. package/src/components/Board/components/ToggleDirectionButton/ToggleDirectionButton.tsx +2 -0
  39. package/src/components/Board/hooks/useBackgroundImage.tsx +2 -6
  40. package/src/components/Board/hooks/useBoardStyle.ts +10 -4
  41. package/src/components/Dictionary/Dictionary.module.scss +10 -7
  42. package/src/components/Dictionary/Dictionary.tsx +38 -36
  43. package/src/components/Loading/Loading.module.scss +1 -0
  44. package/src/components/Loading/Loading.tsx +1 -1
  45. package/src/components/Modal/components/Section/Section.tsx +3 -2
  46. package/src/components/NavButtons/NavButtons.tsx +1 -0
  47. package/src/components/Rack/Rack.tsx +1 -0
  48. package/src/components/Results/Cell.tsx +7 -3
  49. package/src/components/Results/Header.tsx +99 -0
  50. package/src/components/Results/HeaderButton.tsx +18 -13
  51. package/src/components/Results/Result.tsx +23 -16
  52. package/src/components/Results/Results.module.scss +24 -12
  53. package/src/components/Results/Results.tsx +3 -9
  54. package/src/components/Results/types.ts +0 -8
  55. package/src/components/Solver/Solver.tsx +1 -1
  56. package/src/components/Tile/TilePure.tsx +1 -0
  57. package/src/hooks/index.ts +1 -0
  58. package/src/hooks/useAppLayout.ts +12 -1
  59. package/src/hooks/useColumns.ts +47 -0
  60. package/src/icons/GeoAlt.svg +5 -0
  61. package/src/icons/OneTwoThree.svg +4 -0
  62. package/src/icons/SquareA.svg +6 -0
  63. package/src/icons/SquareB.svg +6 -0
  64. package/src/icons/Squares.svg +34 -0
  65. package/src/icons/Words.svg +22 -0
  66. package/src/icons/index.ts +6 -0
  67. package/src/lib/groupResults.ts +1 -1
  68. package/src/lib/index.ts +0 -1
  69. package/src/lib/sortResults.ts +10 -10
  70. package/src/modals/KeyMapModal/KeyMapModal.tsx +8 -9
  71. package/src/modals/RemainingTilesModal/RemainingTilesModal.tsx +1 -0
  72. package/src/modals/SettingsModal/SettingsModal.tsx +5 -5
  73. package/src/modals/WordsModal/WordsModal.tsx +2 -0
  74. package/src/parameters/index.ts +12 -0
  75. package/src/state/selectors.ts +26 -1
  76. package/src/state/slices/resultsInitialState.ts +2 -2
  77. package/src/state/slices/resultsSlice.ts +2 -2
  78. package/src/state/useTranslate.ts +5 -1
  79. package/src/styles/variables.scss +1 -0
  80. package/src/types/index.ts +11 -2
  81. package/.next/static/chunks/pages/_app-8246f5b39b6a5e59.js +0 -17
  82. package/.next/static/chunks/pages/index-65bfe83d121535ab.js +0 -1
  83. package/.next/static/chunks/webpack-6ef43a8d4a395f49.js +0 -1
  84. package/.next/static/css/2f727b21d1331ea5.css +0 -2
  85. package/src/components/Results/getCoordinatesColumn.ts +0 -14
  86. package/src/components/Results/getLocaleColumns.ts +0 -58
  87. package/src/components/Results/useColumns.ts +0 -44
  88. package/src/lib/dataUrlToBlob.ts +0 -20
  89. /package/.next/static/{N8hSsS6Ppzlj3ebHMSZvI → qwJjm2FeDHHGY92CY5oQQ}/_ssgManifest.js +0 -0
@@ -2,6 +2,7 @@ import classNames from 'classnames';
2
2
  import { CSSProperties, FocusEventHandler, MouseEventHandler, ReactElement, useMemo, useRef } from 'react';
3
3
  import Highlighter from 'react-highlight-words';
4
4
 
5
+ import { useAppLayout, useColumns } from 'hooks';
5
6
  import { LOCALE_FEATURES } from 'i18n';
6
7
  import { getCoordinates, noop } from 'lib';
7
8
  import {
@@ -11,12 +12,11 @@ import {
11
12
  selectShowCoordinates,
12
13
  useTypedSelector,
13
14
  } from 'state';
14
- import { ResultColumn } from 'types';
15
+ import { ResultColumnId } from 'types';
15
16
 
16
17
  import Cell from './Cell';
17
18
  import styles from './Results.module.scss';
18
19
  import { ResultData } from './types';
19
- import useColumns from './useColumns';
20
20
 
21
21
  interface Props {
22
22
  data: ResultData;
@@ -34,16 +34,16 @@ const Result = ({ data, index, style }: Props): ReactElement => {
34
34
  onMouseEnter = noop,
35
35
  onMouseLeave = noop,
36
36
  } = data;
37
+ const { resultWordWidth } = useAppLayout();
37
38
  const ref = useRef<HTMLButtonElement>(null);
38
39
  const columns = useColumns();
39
40
  const locale = useTypedSelector(selectLocale);
40
41
  const showCoordinates = useTypedSelector(selectShowCoordinates);
41
42
  const query = useTypedSelector(selectResultsQuery);
42
- const { consonants, direction, separator, vowels } = LOCALE_FEATURES[locale];
43
+ const { direction, separator } = LOCALE_FEATURES[locale];
43
44
  const result = results[index];
44
45
  const isMatching = useTypedSelector((state) => selectIsResultMatching(state, index));
45
46
  const words = direction === 'rtl' ? [...result.words].reverse() : result.words;
46
- const enabledColumns = Object.fromEntries(columns.map((column) => [column.id, true]));
47
47
  const coordinates = useMemo(() => getCoordinates(result, showCoordinates), [result, showCoordinates]);
48
48
 
49
49
  const handleClick: MouseEventHandler = (event) => onClick(result, event);
@@ -54,11 +54,13 @@ const Result = ({ data, index, style }: Props): ReactElement => {
54
54
 
55
55
  return (
56
56
  <button
57
+ aria-hidden={isMatching ? undefined : 'true'}
57
58
  aria-label={result.word}
58
59
  className={classNames(styles.result, {
59
60
  [styles.highlighted]: index === highlightedIndex,
60
61
  [styles.notMatching]: !isMatching,
61
62
  })}
63
+ data-testid="result"
62
64
  ref={ref}
63
65
  style={style}
64
66
  type="button"
@@ -69,33 +71,38 @@ const Result = ({ data, index, style }: Props): ReactElement => {
69
71
  onMouseLeave={handleMouseLeave}
70
72
  >
71
73
  <span className={styles.resultContent}>
72
- {enabledColumns[ResultColumn.Coordinates] && (
74
+ {columns[ResultColumnId.Coordinates] && (
73
75
  <Cell className={styles.coordinates} translationKey="settings.showCoordinates" value={coordinates} />
74
76
  )}
75
77
 
76
- {enabledColumns[ResultColumn.Word] && (
77
- <Cell className={styles.word} translationKey="common.word" value={result.word}>
78
+ {columns[ResultColumnId.Word] && (
79
+ <Cell
80
+ className={styles.word}
81
+ style={{ flexBasis: resultWordWidth }}
82
+ translationKey="common.word"
83
+ value={result.word}
84
+ >
78
85
  <Highlighter highlightClassName={styles.highlight} searchWords={[query]} textToHighlight={result.word} />
79
86
  </Cell>
80
87
  )}
81
88
 
82
- {enabledColumns[ResultColumn.TilesCount] && (
89
+ {columns[ResultColumnId.TilesCount] && (
83
90
  <Cell className={styles.stat} translationKey="common.tiles" value={result.tilesCount} />
84
91
  )}
85
92
 
86
- {enabledColumns[ResultColumn.ConsonantsCount] && consonants && (
87
- <Cell className={styles.stat} translationKey="common.consonants" value={result.consonantsCount} />
93
+ {columns[ResultColumnId.VowelsCount] && (
94
+ <Cell className={styles.stat} translationKey="common.vowels" value={result.vowelsCount} />
88
95
  )}
89
96
 
90
- {enabledColumns[ResultColumn.VowelsCount] && vowels && (
91
- <Cell className={styles.stat} translationKey="common.vowels" value={result.vowelsCount} />
97
+ {columns[ResultColumnId.ConsonantsCount] && (
98
+ <Cell className={styles.stat} translationKey="common.consonants" value={result.consonantsCount} />
92
99
  )}
93
100
 
94
- {enabledColumns[ResultColumn.BlanksCount] && (
101
+ {columns[ResultColumnId.BlanksCount] && (
95
102
  <Cell className={styles.stat} translationKey="common.blanks" value={result.blanksCount} />
96
103
  )}
97
104
 
98
- {enabledColumns[ResultColumn.WordsCount] && (
105
+ {columns[ResultColumnId.WordsCount] && (
99
106
  <Cell
100
107
  className={styles.stat}
101
108
  translationKey="common.words"
@@ -104,8 +111,8 @@ const Result = ({ data, index, style }: Props): ReactElement => {
104
111
  />
105
112
  )}
106
113
 
107
- {enabledColumns[ResultColumn.Points] && (
108
- <Cell className={styles.points} translationKey="common.points" value={result.points} />
114
+ {columns[ResultColumnId.Points] && (
115
+ <Cell className={styles.points} dataTestId="points" translationKey="common.points" value={result.points} />
109
116
  )}
110
117
  </span>
111
118
  </button>
@@ -44,7 +44,6 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
44
44
  display: flex;
45
45
  align-items: center;
46
46
  justify-content: space-between;
47
- font-weight: 700;
48
47
  border-bottom: var(--border);
49
48
  border-top-left-radius: inherit;
50
49
  border-top-right-radius: inherit;
@@ -53,11 +52,13 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
53
52
  .headerButton {
54
53
  @include button-reset;
55
54
  @include focus-effect;
55
+
56
56
  cursor: pointer;
57
57
 
58
58
  text-transform: uppercase;
59
59
  transition: var(--transition);
60
60
  background-color: var(--color--background);
61
+ height: 100%;
61
62
 
62
63
  &:first-child {
63
64
  border-start-start-radius: inherit;
@@ -72,6 +73,17 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
72
73
  background-color: var(--color--primary);
73
74
  color: var(--color--primary--opposite);
74
75
  }
76
+
77
+ &.points {
78
+ @include scrollbars;
79
+
80
+ overflow-y: scroll;
81
+
82
+ &,
83
+ &:hover {
84
+ scrollbar-color: transparent transparent;
85
+ }
86
+ }
75
87
  }
76
88
 
77
89
  .headerButtonLabel {
@@ -81,6 +93,12 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
81
93
  text-align: start;
82
94
  }
83
95
 
96
+ .headerButtonIcon {
97
+ flex: 0 0 auto;
98
+ width: var(--results--icon--size);
99
+ height: var(--results--icon--size);
100
+ }
101
+
84
102
  .result {
85
103
  @include button-reset;
86
104
 
@@ -98,7 +116,7 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
98
116
  }
99
117
  }
100
118
 
101
- &.notMatching {
119
+ &[aria-hidden='true'] {
102
120
  color: var(--color--inactive);
103
121
 
104
122
  &:focus,
@@ -138,24 +156,18 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
138
156
  justify-content: flex-start;
139
157
  }
140
158
 
141
- .result &:first-child,
142
- .headerButton:first-child & {
143
- text-align: start;
159
+ .result &.word:first-child,
160
+ .headerButton.word:first-child & {
144
161
  padding-inline-start: $row-padding-horizontal;
145
162
  }
146
163
 
147
- .result &:last-child,
148
- .headerButton:last-child & {
149
- padding-inline-end: $row-padding-horizontal;
150
- }
151
-
152
164
  &:last-child {
153
165
  flex: 1;
154
166
  }
155
167
  }
156
168
 
157
169
  .word {
158
- flex: 1 0;
170
+ flex: 0 0;
159
171
  text-transform: uppercase;
160
172
  }
161
173
 
@@ -176,7 +188,7 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
176
188
  }
177
189
 
178
190
  .coordinates {
179
- $width: 50px;
191
+ $width: 55px;
180
192
 
181
193
  flex: 0 0 $width;
182
194
  max-width: $width;
@@ -20,12 +20,11 @@ import EmptyState from '../EmptyState';
20
20
  import Loading from '../Loading';
21
21
  import ResultsInput from '../ResultsInput';
22
22
 
23
- import HeaderButton from './HeaderButton';
23
+ import Header from './Header';
24
24
  import Result from './Result';
25
25
  import styles from './Results.module.scss';
26
26
  import SolveButton from './SolveButton';
27
27
  import { ResultCallbacks, ResultData } from './types';
28
- import useColumns from './useColumns';
29
28
 
30
29
  interface Props {
31
30
  callbacks: ResultCallbacks;
@@ -47,7 +46,6 @@ const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIn
47
46
  const error = useTypedSelector(selectSolveError);
48
47
  const itemData = useMemo(() => ({ ...callbacks, highlightedIndex, results }), [callbacks, highlightedIndex, results]);
49
48
  const [listRef, setListRef] = useState<FixedSizeList<ResultData> | null>(null);
50
- const columns = useColumns();
51
49
  const scrollToIndex = typeof highlightedIndex === 'number' ? highlightedIndex : 0;
52
50
  const scrollToIndexRef = useLatest(scrollToIndex);
53
51
  const hasResults = typeof error === 'undefined' && typeof results !== 'undefined';
@@ -70,12 +68,8 @@ const Results: FunctionComponent<Props> = ({ callbacks, className, highlightedIn
70
68
  }, [results, listRef, scrollToIndexRef]);
71
69
 
72
70
  return (
73
- <div className={classNames(styles.results, className)}>
74
- <div className={styles.header}>
75
- {columns.map((column) => (
76
- <HeaderButton column={column} key={column.id} />
77
- ))}
78
- </div>
71
+ <div className={classNames(styles.results, className)} data-testid="results">
72
+ <Header />
79
73
 
80
74
  <div className={styles.content}>
81
75
  {typeof error !== 'undefined' && (
@@ -1,14 +1,6 @@
1
1
  import { Result } from '@scrabble-solver/types';
2
2
  import { FocusEvent, MouseEvent } from 'react';
3
3
 
4
- import { ResultColumn, TranslationKey } from 'types';
5
-
6
- export interface Column {
7
- className: string;
8
- id: ResultColumn;
9
- translationKey: TranslationKey;
10
- }
11
-
12
4
  export interface ResultCallbacks {
13
5
  onBlur?: (result: Result, event: FocusEvent) => void;
14
6
  onClick?: (result: Result, event: MouseEvent) => void;
@@ -100,7 +100,7 @@ const Solver: FunctionComponent<Props> = ({ className, onShowResults }) => {
100
100
  <div className={styles.column}>
101
101
  <Results callbacks={callbacks} className={styles.results} />
102
102
 
103
- <div className={styles.dictionaryContainer}>
103
+ <div data-testid="dictionary" className={styles.dictionaryContainer}>
104
104
  <Dictionary className={styles.dictionary} />
105
105
  <DictionaryInput className={styles.dictionaryInput} />
106
106
  </div>
@@ -77,6 +77,7 @@ const TilePure: FunctionComponent<Props> = ({
77
77
  [styles.points5]: typeof points === 'number' && points >= 5,
78
78
  [styles.raised]: raised,
79
79
  })}
80
+ role={highlighted ? 'mark' : undefined}
80
81
  style={style}
81
82
  >
82
83
  {character || placeholder}
@@ -1,4 +1,5 @@
1
1
  export { default as useAppLayout } from './useAppLayout';
2
+ export { default as useColumns } from './useColumns';
2
3
  export { default as useDirection } from './useDirection';
3
4
  export { default as useEffectOnce } from './useEffectOnce';
4
5
  export { default as useIsTouchDevice } from './useIsTouchDevice';
@@ -15,10 +15,13 @@ import {
15
15
  MODAL_WIDTH,
16
16
  NAV_PADDING,
17
17
  RACK_TILE_SIZE_MAX,
18
+ RESULTS_COLUMN_WIDTH,
18
19
  SOLVER_COLUMN_WIDTH,
19
20
  } from 'parameters';
20
21
  import { selectConfig, selectShowCoordinates, useTypedSelector } from 'state';
22
+ import { ResultColumnId } from 'types';
21
23
 
24
+ import useColumns from './useColumns';
22
25
  import useIsTouchDevice from './useIsTouchDevice';
23
26
  import useMediaQueries from './useMediaQueries';
24
27
  import useViewportSize from './useViewportSize';
@@ -29,6 +32,7 @@ const useAppLayout = () => {
29
32
  const showCoordinates = useTypedSelector(selectShowCoordinates);
30
33
  const isTouchDevice = useIsTouchDevice();
31
34
  const { isLessThanXs, isLessThanS, isLessThanM, isLessThanL, isLessThanXl } = useMediaQueries();
35
+ const columns = useColumns();
32
36
  const isBoardFullWidth = isLessThanM;
33
37
  const showResultCandidatePicker = isLessThanL;
34
38
  const componentsSpacing = isLessThanXl ? COMPONENTS_SPACING_SMALL : COMPONENTS_SPACING;
@@ -68,6 +72,12 @@ const useAppLayout = () => {
68
72
  ? viewportHeight - dictionaryHeight - BUTTON_HEIGHT - MODAL_HEADER_HEIGHT - 5 * componentsSpacing
69
73
  : boardSize - componentsSpacing - dictionaryHeight;
70
74
  const rackWidth = tileSize * config.rackSize;
75
+ const resultsWidth = isLessThanL ? modalWidth - 2 * componentsSpacing : SOLVER_COLUMN_WIDTH;
76
+ const columnsWidth = Object.keys(columns).reduce(
77
+ (sum, column) => sum + (RESULTS_COLUMN_WIDTH[column as ResultColumnId] ?? 0),
78
+ 0,
79
+ );
80
+ const resultWordWidth = resultsWidth - 2 * BORDER_WIDTH - columnsWidth;
71
81
 
72
82
  return {
73
83
  actionsWidth: 2 * BUTTON_HEIGHT - BORDER_WIDTH,
@@ -83,7 +93,8 @@ const useAppLayout = () => {
83
93
  rackHeight: tileSize,
84
94
  rackWidth,
85
95
  resultsHeight,
86
- resultsWidth: isLessThanL ? modalWidth - 2 * componentsSpacing : SOLVER_COLUMN_WIDTH,
96
+ resultsWidth,
97
+ resultWordWidth,
87
98
  showCompactControls: !showColumn,
88
99
  showKeyMap: !isTouchDevice,
89
100
  showResultsInModal,
@@ -0,0 +1,47 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { selectColumns, useTypedSelector } from 'state';
4
+ import { ResultColumnId } from 'types';
5
+
6
+ import useMediaQueries from './useMediaQueries';
7
+
8
+ const COLUMNS_XS = [ResultColumnId.Coordinates, ResultColumnId.Word, ResultColumnId.Points];
9
+
10
+ const COLUMNS_S = [...COLUMNS_XS, ResultColumnId.BlanksCount, ResultColumnId.WordsCount];
11
+
12
+ const COLUMNS_M = [...COLUMNS_XS];
13
+
14
+ const COLUMNS_L = [...COLUMNS_XS];
15
+
16
+ const useColumns = (): Partial<Record<ResultColumnId, boolean>> => {
17
+ const columns = useTypedSelector(selectColumns);
18
+ const { isLessThanXs, isLessThanS, isLessThanM, isLessThanL } = useMediaQueries();
19
+
20
+ const filteredColumns = useMemo(() => {
21
+ if (isLessThanXs) {
22
+ return columns.filter((columnId) => COLUMNS_XS.includes(columnId));
23
+ }
24
+
25
+ if (isLessThanS) {
26
+ return columns.filter((columnId) => COLUMNS_S.includes(columnId));
27
+ }
28
+
29
+ if (isLessThanM) {
30
+ return columns.filter((columnId) => COLUMNS_M.includes(columnId));
31
+ }
32
+
33
+ if (isLessThanL) {
34
+ return columns.filter((columnId) => COLUMNS_L.includes(columnId));
35
+ }
36
+
37
+ return columns;
38
+ }, [columns, isLessThanXs, isLessThanS, isLessThanM, isLessThanL]);
39
+
40
+ const columnsMap = useMemo(() => {
41
+ return Object.fromEntries(filteredColumns.map((column) => [column, true]));
42
+ }, [filteredColumns]);
43
+
44
+ return columnsMap;
45
+ };
46
+
47
+ export default useColumns;
@@ -0,0 +1,5 @@
1
+ <!-- https://icons.getbootstrap.com/icons/geo-alt/ -->
2
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
3
+ <path d="M12.166 8.94c-.524 1.062-1.234 2.12-1.96 3.07A32 32 0 0 1 8 14.58a32 32 0 0 1-2.206-2.57c-.726-.95-1.436-2.008-1.96-3.07C3.304 7.867 3 6.862 3 6a5 5 0 0 1 10 0c0 .862-.305 1.867-.834 2.94M8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10" />
4
+ <path d="M8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4m0 1a3 3 0 1 0 0-6 3 3 0 0 0 0 6" />
5
+ </svg>
@@ -0,0 +1,4 @@
1
+ <!-- https://icons.getbootstrap.com/icons/123/ -->
2
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
3
+ <path d="M2.873 11.297V4.142H1.699L0 5.379v1.137l1.64-1.18h.06v5.961zm3.213-5.09v-.063c0-.618.44-1.169 1.196-1.169.676 0 1.174.44 1.174 1.106 0 .624-.42 1.101-.807 1.526L4.99 10.553v.744h4.78v-.99H6.643v-.069L8.41 8.252c.65-.724 1.237-1.332 1.237-2.27C9.646 4.849 8.723 4 7.308 4c-1.573 0-2.36 1.064-2.36 2.15v.057zm6.559 1.883h.786c.823 0 1.374.481 1.379 1.179.01.707-.55 1.216-1.421 1.21-.77-.005-1.326-.419-1.379-.953h-1.095c.042 1.053.938 1.918 2.464 1.918 1.478 0 2.642-.839 2.62-2.144-.02-1.143-.922-1.651-1.551-1.714v-.063c.535-.09 1.347-.66 1.326-1.678-.026-1.053-.933-1.855-2.359-1.845-1.5.005-2.317.88-2.348 1.898h1.116c.032-.498.498-.944 1.206-.944.703 0 1.206.435 1.206 1.07.005.64-.504 1.106-1.2 1.106h-.75z" />
4
+ </svg>
@@ -0,0 +1,6 @@
1
+ <!-- https://icons.getbootstrap.com/icons/square/ -->
2
+ <!-- https://icons.getbootstrap.com/icons/alphabet-uppercase/ -->
3
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
4
+ <path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z" />
5
+ <path d="M 7.2949219,4.8691409 5.2382813,11.130859 H 6.4648438 L 6.9453125,9.5195319 h 2.046875 L 9.4707032,11.130859 H 10.761719 L 8.7148438,4.8691409 Z m 0.6484375,1.199219 h 0.054687 l 0.75,2.53125 h -1.554687 z" />
6
+ </svg>
@@ -0,0 +1,6 @@
1
+ <!-- https://icons.getbootstrap.com/icons/square/ -->
2
+ <!-- https://icons.getbootstrap.com/icons/alphabet-uppercase/ -->
3
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
4
+ <path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z" />
5
+ <path d="m 5.658203,4.8691408 v 6.2617182 h 2.5507813 c 1.2429974,0 2.1328127,-0.646627 2.1328127,-1.7656244 0,-0.9409982 -0.629236,-1.4533438 -1.3652346,-1.5273438 V 7.7812502 C 9.5825612,7.6292506 10.027344,7.172155 10.027344,6.4101564 c 0,-0.957998 -0.7185963,-1.5410156 -1.8085941,-1.5410156 z m 1.203125,0.9316406 h 1.0742187 c 0.569999,1e-7 0.8964844,0.3103136 0.8964844,0.8203126 0,0.5449988 -0.3579236,0.8535156 -1.1699218,0.8535156 H 6.861328 Z m 0,2.5371094 h 1.1503906 c 0.7129986,0 1.0957031,0.3621262 1.0957031,0.953125 10e-8,0.5969988 -0.3927518,0.9082032 -1.34375,0.9082032 H 6.861328 Z" />
6
+ </svg>
@@ -0,0 +1,34 @@
1
+ <!-- https://icons.getbootstrap.com/icons/square/ -->
2
+ <!-- https://icons.getbootstrap.com/icons/alphabet-uppercase/ -->
3
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
4
+ <path
5
+ d="m 2.75,4 v 0.75 h 7.75 a 0.75,0.75 0 0 1 0.75,0.75 v 7.75 H 12 V 5.5 A 1.5,1.5 0 0 0 10.5,4 Z"
6
+ stroke-width="0.75" />
7
+ <path
8
+ d="m 12,14 h -0.75 v 0.5 a 0.75,0.75 0 0 1 -0.75,0.75 h -9 A 0.75,0.75 0 0 1 0.75,14.5 v -9 A 0.75,0.75 0 0 1 1.5,4.75 H 2 V 4 H 1.5 A 1.5,1.5 0 0 0 0,5.5 v 9 A 1.5,1.5 0 0 0 1.5,16 h 9 A 1.5,1.5 0 0 0 12,14.5 Z"
9
+ stroke-width="0.75" />
10
+ <path
11
+ d="M 2,4.75 H 2.75 V 4 H 2 Z"
12
+ stroke-width="0.75" />
13
+ <path
14
+ d="M 12,14 V 13.25 H 11.25 V 14 Z"
15
+ stroke-width="0.75" />
16
+ <path
17
+ d="m 4.75,2 v 0.75 h 7.75 a 0.75,0.75 0 0 1 0.75,0.75 v 7.75 H 14 V 3.5 A 1.5,1.5 0 0 0 12.5,2 Z"
18
+ stroke-width="0.75" />
19
+ <path
20
+ d="M 4,2.75 V 2 H 3.5 A 1.5,1.5 0 0 0 2,3.5 V 4 H 2.75 V 3.5 A 0.75,0.75 0 0 1 3.5,2.75 Z"
21
+ stroke-width="0.75" />
22
+ <path
23
+ d="m 14,12 h -0.75 v 0.5 A 0.75,0.75 0 0 1 12.5,13.25 H 12 V 14 h 0.5 A 1.5,1.5 0 0 0 14,12.5 Z"
24
+ stroke-width="0.75" />
25
+ <path
26
+ d="M 4,2.75 H 4.75 V 2 H 4 Z"
27
+ stroke-width="0.75" />
28
+ <path
29
+ d="M 14,12 V 11.25 H 13.25 V 12 Z"
30
+ stroke-width="0.75" />
31
+ <path
32
+ d="M 5.5,0 A 1.5,1.5 0 0 0 4,1.5 V 2 H 4.75 V 1.5 A 0.75,0.75 0 0 1 5.5,0.75 h 9 a 0.75,0.75 0 0 1 0.75,0.75 v 9 A 0.75,0.75 0 0 1 14.5,11.25 H 14 V 12 h 0.5 A 1.5,1.5 0 0 0 16,10.5 v -9 A 1.5,1.5 0 0 0 14.5,0 Z"
33
+ stroke-width="0.75" />
34
+ </svg>
@@ -0,0 +1,22 @@
1
+ <!-- https://icons.getbootstrap.com/icons/square/ -->
2
+ <!-- https://icons.getbootstrap.com/icons/alphabet-uppercase/ -->
3
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
4
+ <path
5
+ d="M 7.21875,0.51562499 A 0.51562499,0.51562499 0 0 1 7.734375,1.03125 v 6.1875 A 0.51562499,0.51562499 0 0 1 7.21875,7.734375 H 1.03125 A 0.51562499,0.51562499 0 0 1 0.51562499,7.21875 V 1.03125 A 0.51562499,0.51562499 0 0 1 1.03125,0.51562499 Z M 1.03125,0 A 1.03125,1.03125 0 0 0 0,1.03125 v 6.1875 A 1.03125,1.03125 0 0 0 1.03125,8.25 h 6.1875 A 1.03125,1.03125 0 0 0 8.25,7.21875 V 1.03125 A 1.03125,1.03125 0 0 0 7.21875,0 Z"
6
+ stroke-width="0.5" />
7
+ <path
8
+ d="m 14.96875,8.265625 a 0.51562499,0.51562499 0 0 1 0.515625,0.515625 v 6.1875 A 0.51562499,0.51562499 0 0 1 14.96875,15.484375 H 8.7812501 A 0.51562499,0.51562499 0 0 1 8.265625,14.96875 V 8.78125 A 0.51562499,0.51562499 0 0 1 8.7812501,8.265625 Z M 8.7812501,7.75 A 1.03125,1.03125 0 0 0 7.75,8.78125 v 6.1875 A 1.03125,1.03125 0 0 0 8.7812501,16 H 14.96875 A 1.03125,1.03125 0 0 0 16,14.96875 V 8.78125 A 1.03125,1.03125 0 0 0 14.96875,7.75 Z"
9
+ stroke-width="0.5" />
10
+ <path
11
+ d="M 3.7614277,2.5 2.7009247,5.75 H 3.3333995 L 3.5811523,4.9136776 H 4.6366198 L 4.8833654,5.75 H 5.5490753 L 4.493608,2.5 Z m 0.3343657,0.6224265 h 0.0282 L 4.5107288,4.4362133 H 3.7090572 Z"
12
+ stroke-width="0.5" />
13
+ <path
14
+ d="m 2.954049,10.250001 v 3.25 h 1.275448 c 0.621526,0 1.066454,-0.335617 1.066454,-0.916407 0,-0.488403 -0.314632,-0.754324 -0.682648,-0.792732 v -0.0294 c 0.303013,-0.07889 0.525414,-0.316137 0.525414,-0.711635 0,-0.497226 -0.359314,-0.799828 -0.904337,-0.799828 z m 0.60159,0.483546 h 0.537133 c 0.285012,0 0.448262,0.161062 0.448262,0.425765 0,0.282869 -0.17897,0.442997 -0.584987,0.442997 H 3.555639 Z m 0,1.316828 h 0.57522 c 0.356516,0 0.547877,0.187953 0.547877,0.494698 0,0.309859 -0.196385,0.471382 -0.671906,0.471382 H 3.555639 Z"
15
+ stroke-width="0.5" />
16
+ <path
17
+ d="M 7.21875,8.265625 A 0.51562499,0.51562499 0 0 1 7.734375,8.78125 v 6.1875 A 0.51562499,0.51562499 0 0 1 7.21875,15.484375 H 1.03125 A 0.51562499,0.51562499 0 0 1 0.515625,14.96875 V 8.78125 A 0.51562499,0.51562499 0 0 1 1.03125,8.265625 Z M 1.03125,7.75 A 1.03125,1.03125 0 0 0 0,8.78125 v 6.1875 A 1.03125,1.03125 0 0 0 1.03125,16 h 6.1875 A 1.03125,1.03125 0 0 0 8.25,14.96875 V 8.78125 A 1.03125,1.03125 0 0 0 7.21875,7.75 Z"
18
+ stroke-width="0.5" />
19
+ <path
20
+ d="m 11.92041,10.250001 c -0.819499,0 -1.287109,0.548047 -1.287109,1.435547 v 0.389648 c 0,0.887999 0.46311,1.424805 1.287109,1.424805 0.6715,0 1.167289,-0.413243 1.196289,-1.010742 v -0.06055 h -0.585937 c -0.0245,0.320999 -0.253946,0.547851 -0.606446,0.547851 -0.428499,0 -0.672851,-0.323367 -0.672851,-0.901367 v -0.386719 c 0,-0.579999 0.248352,-0.915039 0.672851,-0.915039 0.348,0 0.581446,0.244649 0.606446,0.577149 h 0.585937 v -0.05664 c -0.0265,-0.618999 -0.522789,-1.043945 -1.196289,-1.043945 z"
21
+ stroke-width="0.5" />
22
+ </svg>
@@ -28,15 +28,21 @@ export { default as FlagGb } from './FlagGb.svg';
28
28
  export { default as FlagPl } from './FlagPl.svg';
29
29
  export { default as FlagRo } from './FlagRo.svg';
30
30
  export { default as FlagUs } from './FlagUs.svg';
31
+ export { default as GeoAlt } from './GeoAlt.svg';
31
32
  export { default as Github } from './Github.svg';
32
33
  export { default as InfoCircleFill } from './InfoCircleFill.svg';
33
34
  export { default as Keyboard } from './Keyboard.svg';
34
35
  export { default as KeyboardFill } from './KeyboardFill.svg';
35
36
  export { default as List } from './List.svg';
37
+ export { default as OneTwoThree } from './OneTwoThree.svg';
36
38
  export { default as Sack } from './Sack.svg';
37
39
  export { default as Search } from './Search.svg';
38
40
  export { default as SortDown } from './SortDown.svg';
39
41
  export { default as SortUp } from './SortUp.svg';
40
42
  export { default as Square } from './Square.svg';
43
+ export { default as SquareA } from './SquareA.svg';
44
+ export { default as SquareB } from './SquareB.svg';
41
45
  export { default as SquareFill } from './SquareFill.svg';
46
+ export { default as Squares } from './Squares.svg';
42
47
  export { default as Star } from './Star.svg';
48
+ export { default as Words } from './Words.svg';
@@ -18,7 +18,7 @@ const groupResults = (
18
18
 
19
19
  const { matching, other } = results.reduce<GroupedResults>(
20
20
  (groupedResults, result) => {
21
- const matchesQuery = () => regExp.test(result.word);
21
+ const matchesQuery = () => Boolean(result.word.match(regExp));
22
22
 
23
23
  if (resultMatchesCellFilter(result, cellFilter) && matchesQuery()) {
24
24
  groupedResults.matching.push(result);
package/src/lib/index.ts CHANGED
@@ -7,7 +7,6 @@ export { default as createKeyboardNavigation } from './createKeyboardNavigation'
7
7
  export { default as createNullMovingComparator } from './createNullMovingComparator';
8
8
  export { default as createRegExp } from './createRegExp';
9
9
  export { default as createStringComparator } from './createStringComparator';
10
- export { default as dataUrlToBlob } from './dataUrlToBlob';
11
10
  export { default as detectLocale } from './detectLocale';
12
11
  export { default as extractCharacters } from './extractCharacters';
13
12
  export { default as extractCharactersByCase } from './extractCharactersByCase';
@@ -1,26 +1,26 @@
1
1
  import { Result, ShowCoordinates } from '@scrabble-solver/types';
2
2
 
3
- import { Comparator, ResultColumn, Sort, SortDirection } from 'types';
3
+ import { Comparator, ResultColumnId, Sort, SortDirection } from 'types';
4
4
 
5
5
  import createKeyComparator from './createKeyComparator';
6
6
  import createStringComparator from './createStringComparator';
7
7
  import getCoordinates from './getCoordinates';
8
8
  import reverseComparator from './reverseComparator';
9
9
 
10
- const comparators: Record<ResultColumn, (locale: string, showCoordinates: ShowCoordinates) => Comparator<Result>> = {
11
- [ResultColumn.BlanksCount]: (locale: string) => createKeyComparator('blanksCount', locale),
12
- [ResultColumn.ConsonantsCount]: (locale: string) => createKeyComparator('consonantsCount', locale),
13
- [ResultColumn.Coordinates]: (locale: string, showCoordinates: ShowCoordinates) => (a, b) => {
10
+ const comparators: Record<ResultColumnId, (locale: string, showCoordinates: ShowCoordinates) => Comparator<Result>> = {
11
+ [ResultColumnId.BlanksCount]: (locale: string) => createKeyComparator('blanksCount', locale),
12
+ [ResultColumnId.ConsonantsCount]: (locale: string) => createKeyComparator('consonantsCount', locale),
13
+ [ResultColumnId.Coordinates]: (locale: string, showCoordinates: ShowCoordinates) => (a, b) => {
14
14
  const stringComparator = createStringComparator(locale);
15
15
  const aValue = getCoordinates(a, showCoordinates);
16
16
  const bValue = getCoordinates(b, showCoordinates);
17
17
  return stringComparator(aValue, bValue);
18
18
  },
19
- [ResultColumn.Points]: (locale: string) => createKeyComparator('points', locale),
20
- [ResultColumn.TilesCount]: (locale: string) => createKeyComparator('tilesCount', locale),
21
- [ResultColumn.VowelsCount]: (locale: string) => createKeyComparator('vowelsCount', locale),
22
- [ResultColumn.Word]: (locale: string) => createKeyComparator('word', locale),
23
- [ResultColumn.WordsCount]: (locale: string) => createKeyComparator('wordsCount', locale),
19
+ [ResultColumnId.Points]: (locale: string) => createKeyComparator('points', locale),
20
+ [ResultColumnId.TilesCount]: (locale: string) => createKeyComparator('tilesCount', locale),
21
+ [ResultColumnId.VowelsCount]: (locale: string) => createKeyComparator('vowelsCount', locale),
22
+ [ResultColumnId.Word]: (locale: string) => createKeyComparator('word', locale),
23
+ [ResultColumnId.WordsCount]: (locale: string) => createKeyComparator('wordsCount', locale),
24
24
  };
25
25
 
26
26
  const sortResults = (
@@ -17,7 +17,7 @@ const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) =
17
17
 
18
18
  return (
19
19
  <Modal className={className} isOpen={isOpen} title={translate('keyMap')} onClose={onClose}>
20
- <Modal.Section title={translate('keyMap.board-and-rack')}>
20
+ <Modal.Section label={translate('keyMap.board-and-rack')} title={translate('keyMap.board-and-rack')}>
21
21
  <Mapping description={translate('keyMap.board-and-rack.navigate')} mapping={[<Arrows key="arrows" />]} />
22
22
 
23
23
  <Mapping
@@ -27,11 +27,6 @@ const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) =
27
27
 
28
28
  <Mapping description={translate('keyMap.board-and-rack.submit')} mapping={[<Enter key="del" />]} />
29
29
 
30
- <Mapping
31
- description={translate('keyMap.board.toggle-cell-filter')}
32
- mapping={[[<Ctrl key="ctrl" />, <Key key="g">G</Key>]]}
33
- />
34
-
35
30
  {config.twoCharacterTiles.length > 0 && (
36
31
  <Mapping
37
32
  description={translate('keyMap.board-and-rack.insert-two-letter-tile')}
@@ -49,12 +44,16 @@ const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) =
49
44
  )}
50
45
  </Modal.Section>
51
46
 
52
- <Modal.Section title={translate('keyMap.board')}>
53
- <Mapping description={translate('keyMap.board.toggle-blank')} mapping={[<Space key="space" />]} />
47
+ <Modal.Section label={translate('keyMap.board')} title={translate('keyMap.board')}>
54
48
  <Mapping description={translate('keyMap.board.toggle-direction')} mapping={[<Arrows key="arrows" />]} />
49
+ <Mapping description={translate('keyMap.board.toggle-blank')} mapping={[<Space key="space" />]} />
50
+ <Mapping
51
+ description={translate('keyMap.board.toggle-cell-filter')}
52
+ mapping={[[<Ctrl key="ctrl" />, <Key key="g">G</Key>]]}
53
+ />
55
54
  </Modal.Section>
56
55
 
57
- <Modal.Section title={translate('keyMap.rack')}>
56
+ <Modal.Section label={translate('keyMap.rack')} title={translate('keyMap.rack')}>
58
57
  <Mapping description={translate('keyMap.rack.insert-blank')} mapping={[<Space key="space" />]} />
59
58
  </Modal.Section>
60
59
  </Modal>
@@ -31,6 +31,7 @@ const RemainingTilesModal: FunctionComponent<Props> = ({ className, isOpen, onCl
31
31
  return (
32
32
  <Modal.Section
33
33
  key={translationKey}
34
+ label={translate(translationKey)}
34
35
  title={
35
36
  <span className={styles.title}>
36
37
  <span>{translate(translationKey)}</span>
@@ -24,25 +24,25 @@ const SettingsModal: FunctionComponent<Props> = ({ className, isOpen, onClose })
24
24
 
25
25
  return (
26
26
  <Modal className={className} isOpen={isOpen} title={translate('settings')} onClose={onClose}>
27
- <Modal.Section title={translate('settings.game')}>
27
+ <Modal.Section label={translate('settings.game')} title={translate('settings.game')}>
28
28
  <ConfigSetting disabled={!isOpen} />
29
29
  </Modal.Section>
30
30
 
31
- <Modal.Section title={translate('settings.language')}>
31
+ <Modal.Section label={translate('settings.language')} title={translate('settings.language')}>
32
32
  <LocaleSetting disabled={!isOpen} />
33
33
  </Modal.Section>
34
34
 
35
- <Modal.Section title={translate('settings.showCoordinates')}>
35
+ <Modal.Section label={translate('settings.showCoordinates')} title={translate('settings.showCoordinates')}>
36
36
  <ShowCoordinatesSetting disabled={!isOpen} />
37
37
  </Modal.Section>
38
38
 
39
39
  {!isTouchDevice && (
40
- <Modal.Section title={translate('settings.inputMode')}>
40
+ <Modal.Section label={translate('settings.inputMode')} title={translate('settings.inputMode')}>
41
41
  <InputModeSetting disabled={!isOpen} />
42
42
  </Modal.Section>
43
43
  )}
44
44
 
45
- <Modal.Section title={translate('settings.autoGroupTiles')}>
45
+ <Modal.Section label={translate('settings.autoGroupTiles')} title={translate('settings.autoGroupTiles')}>
46
46
  <AutoGroupTilesSetting disabled={!isOpen} />
47
47
  </Modal.Section>
48
48
  </Modal>