@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.
Files changed (146) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +12 -12
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/eslint/.cache_8dgz12 +1 -1
  5. package/.next/cache/next-server.js.nft.json +1 -1
  6. package/.next/cache/webpack/client-production/0.pack +0 -0
  7. package/.next/cache/webpack/client-production/index.pack +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/server-production/0.pack +0 -0
  11. package/.next/cache/webpack/server-production/index.pack +0 -0
  12. package/.next/next-server.js.nft.json +1 -1
  13. package/.next/prerender-manifest.json +1 -1
  14. package/.next/required-server-files.json +1 -1
  15. package/.next/routes-manifest.json +1 -1
  16. package/.next/server/chunks/{176.js → 277.js} +956 -930
  17. package/.next/server/chunks/{290.js → 417.js} +3 -3
  18. package/.next/server/chunks/50.js +371 -343
  19. package/.next/server/chunks/664.js +15 -15
  20. package/.next/server/chunks/859.js +17 -10
  21. package/.next/server/middleware-build-manifest.js +1 -1
  22. package/.next/server/next-font-manifest.js +1 -0
  23. package/.next/server/pages/404.html +2 -2
  24. package/.next/server/pages/404.js.nft.json +1 -1
  25. package/.next/server/pages/500.html +1 -1
  26. package/.next/server/pages/_app.js +4 -28
  27. package/.next/server/pages/_app.js.nft.json +1 -1
  28. package/.next/server/pages/_document.js +2 -2
  29. package/.next/server/pages/_document.js.nft.json +1 -1
  30. package/.next/server/pages/_error.js +4 -4
  31. package/.next/server/pages/api/dictionary/[locale]/[word].js +4 -4
  32. package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
  33. package/.next/server/pages/api/dictionary/[locale].js +3 -3
  34. package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
  35. package/.next/server/pages/api/solve.js +9 -11
  36. package/.next/server/pages/api/solve.js.nft.json +1 -1
  37. package/.next/server/pages/api/verify.js +3 -3
  38. package/.next/server/pages/api/verify.js.nft.json +1 -1
  39. package/.next/server/pages/api/visit.js +3 -3
  40. package/.next/server/pages/api/visit.js.nft.json +1 -1
  41. package/.next/server/pages/index.html +1 -1
  42. package/.next/server/pages/index.js +256 -210
  43. package/.next/server/pages/index.js.nft.json +1 -1
  44. package/.next/server/pages/index.json +1 -1
  45. package/.next/static/chunks/main-0ecb9ccfcb6c9b24.js +1 -0
  46. package/.next/static/chunks/pages/404-448ba28510855455.js +1 -0
  47. package/.next/static/chunks/pages/_app-270526803bc274eb.js +28 -0
  48. package/.next/static/chunks/pages/{_error-8353112a01355ec2.js → _error-54de1933a164a1ff.js} +1 -1
  49. package/.next/static/chunks/pages/index-c6e7754ccf3532df.js +1 -0
  50. package/.next/static/css/ad39b36eab07e613.css +1 -0
  51. package/.next/static/css/e5803e581e4c0451.css +2 -0
  52. package/.next/static/esK8DG-6aS5V7QFRtR3YE/_buildManifest.js +1 -0
  53. package/.next/trace +53 -55
  54. package/package.json +12 -17
  55. package/src/components/{Solver/components/EmptyState/EmptyState.module.scss → Alert/Alert.module.scss} +11 -7
  56. package/src/components/{Solver/components/EmptyState/EmptyState.tsx → Alert/Alert.tsx} +8 -6
  57. package/src/components/Alert/index.ts +1 -0
  58. package/src/components/Board/Board.module.scss +55 -0
  59. package/src/components/Board/BoardPure.tsx +4 -0
  60. package/src/components/Board/components/Cell/Cell.module.scss +42 -0
  61. package/src/components/Board/components/Cell/Cell.tsx +12 -0
  62. package/src/components/Board/components/Cell/CellPure.tsx +12 -0
  63. package/src/components/Board/hooks/useGrid.ts +8 -24
  64. package/src/components/Dictionary/Dictionary.module.scss +17 -8
  65. package/src/components/Dictionary/Dictionary.tsx +5 -5
  66. package/src/components/DictionaryInput/DictionaryInput.module.scss +1 -0
  67. package/src/components/EmptyState/EmptyState.module.scss +2 -1
  68. package/src/components/EmptyState/EmptyState.tsx +1 -2
  69. package/src/components/Loading/Loading.module.scss +1 -1
  70. package/src/components/Loading/Loading.tsx +1 -1
  71. package/src/components/Logo/Logo.tsx +5 -0
  72. package/src/components/Modal/Modal.module.scss +2 -1
  73. package/src/components/NavButtons/NavButtons.tsx +4 -5
  74. package/src/components/PlainTiles/PlainTiles.module.scss +1 -1
  75. package/src/components/PlainTiles/Tile.tsx +3 -3
  76. package/src/components/Rack/Rack.module.scss +25 -0
  77. package/src/components/Rack/Rack.tsx +5 -4
  78. package/src/components/Rack/RackTile.tsx +6 -13
  79. package/src/components/Results/Results.module.scss +33 -2
  80. package/src/components/Results/Results.tsx +11 -11
  81. package/src/components/ResultsInput/ResultsInput.module.scss +1 -0
  82. package/src/components/Solver/Solver.module.scss +6 -4
  83. package/src/components/Solver/Solver.tsx +16 -28
  84. package/src/components/Solver/components/FloatingSolveButton/FloatingSolveButton.module.scss +7 -0
  85. package/src/components/Solver/components/{SolveButton/SolveButton.tsx → FloatingSolveButton/FloatingSolveButton.tsx} +6 -6
  86. package/src/components/Solver/components/FloatingSolveButton/index.ts +1 -0
  87. package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss +19 -4
  88. package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +13 -1
  89. package/src/components/Solver/components/index.ts +1 -2
  90. package/src/components/Spinner/Spinner.module.scss +11 -0
  91. package/src/components/Spinner/Spinner.tsx +19 -0
  92. package/src/components/Spinner/index.ts +1 -0
  93. package/src/components/Tile/Tile.module.scss +14 -2
  94. package/src/components/Tile/Tile.tsx +5 -5
  95. package/src/components/Tooltip/Tooltip.module.scss +1 -72
  96. package/src/components/Tooltip/useTooltip.tsx +25 -35
  97. package/src/components/index.ts +2 -0
  98. package/src/hooks/index.ts +1 -1
  99. package/src/hooks/useAppLayout.ts +29 -0
  100. package/src/i18n/de.json +1 -0
  101. package/src/i18n/en.json +1 -0
  102. package/src/i18n/es.json +1 -0
  103. package/src/i18n/fa.json +1 -0
  104. package/src/i18n/fr.json +1 -0
  105. package/src/i18n/index.ts +1 -1
  106. package/src/i18n/pl.json +1 -0
  107. package/src/lib/index.ts +0 -1
  108. package/src/modals/DictionaryModal/DictionaryModal.module.scss +23 -0
  109. package/src/modals/DictionaryModal/DictionaryModal.tsx +27 -0
  110. package/src/modals/DictionaryModal/index.ts +1 -0
  111. package/src/modals/KeyMapModal/KeyMapModal.tsx +5 -21
  112. package/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +4 -0
  113. package/src/modals/MenuModal/MenuModal.tsx +7 -1
  114. package/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -0
  115. package/src/modals/ResultsModal/ResultsModal.module.scss +19 -0
  116. package/src/modals/ResultsModal/ResultsModal.tsx +8 -7
  117. package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +2 -0
  118. package/src/modals/WordsModal/WordsModal.module.scss +8 -1
  119. package/src/modals/index.ts +1 -0
  120. package/src/pages/api/solve.ts +9 -10
  121. package/src/pages/index.tsx +20 -15
  122. package/src/state/createAppStore.ts +26 -10
  123. package/src/state/localStorage.ts +0 -9
  124. package/src/state/types.ts +20 -2
  125. package/src/styles/animations.scss +10 -0
  126. package/src/styles/global.scss +1 -1
  127. package/src/styles/mixins.scss +22 -0
  128. package/src/styles/variables.scss +17 -2
  129. package/src/types/index.ts +1 -0
  130. package/.next/server/font-loader-manifest.js +0 -1
  131. package/.next/static/chunks/main-74c4d6b2b5c362f3.js +0 -1
  132. package/.next/static/chunks/pages/404-6c1a6e3251710371.js +0 -1
  133. package/.next/static/chunks/pages/_app-d98e480ff8c583de.js +0 -28
  134. package/.next/static/chunks/pages/index-bd1c7d3872c37456.js +0 -1
  135. package/.next/static/css/a9b55372a26cf77d.css +0 -1
  136. package/.next/static/css/b8954b85e2fa5b63.css +0 -2
  137. package/.next/static/msKI0ZURgJImoGBJvCBiF/_buildManifest.js +0 -1
  138. package/src/components/Solver/components/EmptyState/index.ts +0 -1
  139. package/src/components/Solver/components/SolveButton/SolveButton.module.scss +0 -4
  140. package/src/components/Solver/components/SolveButton/index.ts +0 -1
  141. package/src/components/Tooltip/constants.ts +0 -28
  142. package/src/hooks/useUniqueId.ts +0 -9
  143. package/src/lib/isCtrl.ts +0 -7
  144. package/src/state/rootReducer.ts +0 -25
  145. /package/.next/server/{font-loader-manifest.json → next-font-manifest.json} +0 -0
  146. /package/.next/static/{msKI0ZURgJImoGBJvCBiF → esK8DG-6aS5V7QFRtR3YE}/_ssgManifest.js +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/scrabble-solver",
3
- "version": "2.10.11",
3
+ "version": "2.11.1",
4
4
  "description": "Scrabble Solver 2 - App",
5
5
  "engines": {
6
6
  "node": ">=16"
@@ -28,34 +28,30 @@
28
28
  "start": "env-cmd next start -p 3333"
29
29
  },
30
30
  "dependencies": {
31
- "@floating-ui/react": "^0.19.2",
31
+ "@floating-ui/react": "^0.20.1",
32
32
  "@kamilmielnik/trie": "^2.0.1",
33
- "@popperjs/core": "^2.11.6",
34
33
  "@reduxjs/toolkit": "^1.9.3",
35
- "@scrabble-solver/configs": "^2.10.11",
36
- "@scrabble-solver/constants": "^2.10.11",
37
- "@scrabble-solver/dictionaries": "^2.10.11",
38
- "@scrabble-solver/logger": "^2.10.11",
39
- "@scrabble-solver/solver": "^2.10.11",
40
- "@scrabble-solver/types": "^2.10.11",
41
- "@scrabble-solver/word-definitions": "^2.10.11",
34
+ "@scrabble-solver/configs": "^2.11.1",
35
+ "@scrabble-solver/constants": "^2.11.1",
36
+ "@scrabble-solver/dictionaries": "^2.11.1",
37
+ "@scrabble-solver/logger": "^2.11.1",
38
+ "@scrabble-solver/solver": "^2.11.1",
39
+ "@scrabble-solver/types": "^2.11.1",
40
+ "@scrabble-solver/word-definitions": "^2.11.1",
42
41
  "classnames": "^2.3.2",
43
42
  "include-media": "^2.0.0",
44
43
  "include-media-query-builder": "^1.1.0",
45
- "next": "^13.2.1",
44
+ "next": "^13.2.4",
46
45
  "normalize.css": "^8.0.1",
47
46
  "react": "^18.2.0",
48
47
  "react-dom": "^18.2.0",
49
48
  "react-modal": "^3.16.1",
50
- "react-popper": "^2.3.0",
51
49
  "react-portal": "^4.2.2",
52
50
  "react-redux": "^8.0.5",
53
51
  "react-use": "^17.4.0",
54
52
  "react-window": "^1.8.8",
55
- "redux": "^4.2.1",
56
53
  "redux-saga": "^1.2.2",
57
54
  "store2": "^2.14.2",
58
- "uuid": "^9.0.0",
59
55
  "workbox-expiration": "^6.5.4",
60
56
  "workbox-precaching": "^6.5.4",
61
57
  "workbox-routing": "^6.5.4",
@@ -72,10 +68,9 @@
72
68
  "@types/react-window": "^1.8.5",
73
69
  "@types/redux": "^3.6.31",
74
70
  "@types/redux-saga": "^0.10.5",
75
- "@types/uuid": "^9.0.1",
76
71
  "env-cmd": "^10.1.0",
77
- "sass": "^1.58.3",
72
+ "sass": "^1.59.2",
78
73
  "workbox-webpack-plugin": "^6.5.4"
79
74
  },
80
- "gitHead": "b070bc7baa92d64a480fd457ad8cc757af51efc0"
75
+ "gitHead": "8dea3cee4e6f902ab8d1ae2339dd1e48b11db39d"
81
76
  }
@@ -1,4 +1,4 @@
1
- .emptyState {
1
+ .alert {
2
2
  display: flex;
3
3
  min-width: 0;
4
4
  width: 100%;
@@ -11,7 +11,6 @@
11
11
  .iconContainer {
12
12
  flex: 0 0 auto;
13
13
  display: flex;
14
- align-items: center;
15
14
  justify-content: center;
16
15
  padding: var(--spacing--m);
17
16
  color: var(--color--foreground--secondary);
@@ -33,15 +32,20 @@
33
32
  color: var(--color--error);
34
33
  }
35
34
 
36
- .warning & {
37
- background-color: var(--color--yellow--light);
38
- color: var(--color--warning);
39
- }
40
-
41
35
  .info & {
42
36
  background-color: var(--color--blue--light);
43
37
  color: var(--color--info);
44
38
  }
39
+
40
+ .success & {
41
+ background-color: var(--color--green--light);
42
+ color: var(--color--success);
43
+ }
44
+
45
+ .warning & {
46
+ background-color: var(--color--yellow--light);
47
+ color: var(--color--warning);
48
+ }
45
49
  }
46
50
 
47
51
  .icon {
@@ -1,30 +1,32 @@
1
1
  import classNames from 'classnames';
2
2
  import { FunctionComponent, HTMLProps, ReactNode, SVGAttributes } from 'react';
3
3
 
4
- import { CrossCircleFill, ExclamationTriangleFill, InfoCircleFill } from 'icons';
4
+ import { Check, CrossCircleFill, ExclamationTriangleFill, InfoCircleFill } from 'icons';
5
5
 
6
- import styles from './EmptyState.module.scss';
6
+ import styles from './Alert.module.scss';
7
7
 
8
8
  interface Props extends HTMLProps<HTMLDivElement> {
9
9
  children: ReactNode;
10
10
  className?: string;
11
- variant: 'error' | 'info' | 'warning';
11
+ variant: 'error' | 'info' | 'success' | 'warning';
12
12
  }
13
13
 
14
14
  const ICON_PER_TYPE: Record<Props['variant'], FunctionComponent<SVGAttributes<SVGElement>>> = {
15
15
  error: CrossCircleFill,
16
16
  info: InfoCircleFill,
17
+ success: Check,
17
18
  warning: ExclamationTriangleFill,
18
19
  };
19
20
 
20
- const EmptyState: FunctionComponent<Props> = ({ children, className, variant, ...props }) => {
21
+ const Alert: FunctionComponent<Props> = ({ children, className, variant, ...props }) => {
21
22
  const Icon = ICON_PER_TYPE[variant];
22
23
 
23
24
  return (
24
25
  <div
25
- className={classNames(styles.emptyState, className, {
26
+ className={classNames(styles.alert, className, {
26
27
  [styles.error]: variant === 'error',
27
28
  [styles.info]: variant === 'info',
29
+ [styles.success]: variant === 'success',
28
30
  [styles.warning]: variant === 'warning',
29
31
  })}
30
32
  {...props}
@@ -38,4 +40,4 @@ const EmptyState: FunctionComponent<Props> = ({ children, className, variant, ..
38
40
  );
39
41
  };
40
42
 
41
- export default EmptyState;
43
+ export default Alert;
@@ -0,0 +1 @@
1
+ export { default } from './Alert';
@@ -4,10 +4,65 @@
4
4
  display: table;
5
5
  box-shadow: var(--box-shadow);
6
6
  border: var(--border);
7
+ border-radius: var(--border--radius);
7
8
  }
8
9
 
9
10
  .row {
10
11
  display: table-row;
12
+
13
+ &:first-child {
14
+ border-top-left-radius: var(--border--radius);
15
+ border-top-right-radius: var(--border--radius);
16
+
17
+ .cell {
18
+ &:first-child {
19
+ [dir='ltr'] & {
20
+ border-top-left-radius: var(--border--radius);
21
+ }
22
+
23
+ [dir='rtl'] & {
24
+ border-top-right-radius: var(--border--radius);
25
+ }
26
+ }
27
+
28
+ &:last-child {
29
+ [dir='ltr'] & {
30
+ border-top-right-radius: var(--border--radius);
31
+ }
32
+
33
+ [dir='rtl'] & {
34
+ border-top-left-radius: var(--border--radius);
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ &:last-child {
41
+ border-bottom-left-radius: var(--border--radius);
42
+ border-bottom-right-radius: var(--border--radius);
43
+
44
+ .cell {
45
+ &:first-child {
46
+ [dir='ltr'] & {
47
+ border-bottom-left-radius: var(--border--radius);
48
+ }
49
+
50
+ [dir='rtl'] & {
51
+ border-bottom-right-radius: var(--border--radius);
52
+ }
53
+ }
54
+
55
+ &:last-child {
56
+ [dir='ltr'] & {
57
+ border-bottom-right-radius: var(--border--radius);
58
+ }
59
+
60
+ [dir='rtl'] & {
61
+ border-bottom-left-radius: var(--border--radius);
62
+ }
63
+ }
64
+ }
65
+ }
11
66
  }
12
67
 
13
68
  .actions {
@@ -45,6 +45,10 @@ const BoardPure: FunctionComponent<Props> = ({
45
45
  <Cell
46
46
  className={styles.cell}
47
47
  cell={cell}
48
+ cellBottom={y < rows.length - 1 ? rows[y + 1][x] : undefined}
49
+ cellLeft={x > 0 ? rows[y][x - 1] : undefined}
50
+ cellRight={x < rows.length - 1 ? rows[y][x + 1] : undefined}
51
+ cellTop={y > 0 ? rows[y - 1][x] : undefined}
48
52
  inputRef={inputRefs[y][x]}
49
53
  isBottom={y === rows.length - 1}
50
54
  isCenter={center.x === x && center.y === y}
@@ -112,6 +112,7 @@
112
112
  font-size: 60%;
113
113
  font-weight: bold;
114
114
  color: var(--color--white);
115
+ transition: var(--transition);
115
116
  content: ' ';
116
117
 
117
118
  [lang='fa-IR'] & {
@@ -124,6 +125,47 @@
124
125
  }
125
126
  }
126
127
 
128
+ .tile {
129
+ .sharpTopLeft & {
130
+ [dir='ltr'] & {
131
+ border-top-left-radius: 0;
132
+ }
133
+
134
+ [dir='rtl'] & {
135
+ border-top-right-radius: 0;
136
+ }
137
+ }
138
+
139
+ .sharpTopRight & {
140
+ [dir='ltr'] & {
141
+ border-top-right-radius: 0;
142
+ }
143
+
144
+ [dir='rtl'] & {
145
+ border-top-left-radius: 0;
146
+ }
147
+ }
148
+
149
+ .sharpBottomLeft & {
150
+ [dir='ltr'] & {
151
+ border-bottom-left-radius: 0;
152
+ }
153
+
154
+ [dir='rtl'] & {
155
+ border-bottom-right-radius: 0;
156
+ }
157
+ }
158
+
159
+ .sharpBottomRight & {
160
+ [dir='ltr'] & {
161
+ border-bottom-right-radius: 0;
162
+ }
163
+
164
+ [dir='rtl'] & {
165
+ border-bottom-left-radius: 0;
166
+ }
167
+ }
168
+ }
127
169
  .icon {
128
170
  $size: 40%;
129
171
 
@@ -17,6 +17,10 @@ import CellPure from './CellPure';
17
17
 
18
18
  interface Props {
19
19
  cell: CellModel;
20
+ cellBottom?: CellModel;
21
+ cellLeft?: CellModel;
22
+ cellRight?: CellModel;
23
+ cellTop?: CellModel;
20
24
  className?: string;
21
25
  inputRef: RefObject<HTMLInputElement>;
22
26
  isBottom: boolean;
@@ -29,6 +33,10 @@ interface Props {
29
33
 
30
34
  const Cell: FunctionComponent<Props> = ({
31
35
  cell,
36
+ cellBottom,
37
+ cellLeft,
38
+ cellRight,
39
+ cellTop,
32
40
  className,
33
41
  inputRef,
34
42
  isBottom,
@@ -59,6 +67,10 @@ const Cell: FunctionComponent<Props> = ({
59
67
  })}
60
68
  bonus={bonus}
61
69
  cell={cell}
70
+ cellBottom={cellBottom}
71
+ cellLeft={cellLeft}
72
+ cellRight={cellRight}
73
+ cellTop={cellTop}
62
74
  className={className}
63
75
  inputRef={inputRef}
64
76
  isBottom={isBottom}
@@ -13,6 +13,10 @@ interface Props {
13
13
  'aria-label': string;
14
14
  bonus: Bonus | undefined;
15
15
  cell: Cell;
16
+ cellBottom?: Cell;
17
+ cellLeft?: Cell;
18
+ cellRight?: Cell;
19
+ cellTop?: Cell;
16
20
  className?: string;
17
21
  inputRef: RefObject<HTMLInputElement>;
18
22
  isBottom: boolean;
@@ -33,6 +37,10 @@ const CellPure: FunctionComponent<Props> = ({
33
37
  'aria-label': ariaLabel,
34
38
  bonus,
35
39
  cell,
40
+ cellBottom,
41
+ cellLeft,
42
+ cellRight,
43
+ cellTop,
36
44
  className,
37
45
  inputRef,
38
46
  isBottom,
@@ -53,6 +61,10 @@ const CellPure: FunctionComponent<Props> = ({
53
61
  [styles.bottom]: isBottom,
54
62
  [styles.filtered]: isFiltered,
55
63
  [styles.right]: isRight,
64
+ [styles.sharpTopLeft]: cellTop?.hasTile() || cellLeft?.hasTile(),
65
+ [styles.sharpTopRight]: cellTop?.hasTile() || cellRight?.hasTile(),
66
+ [styles.sharpBottomLeft]: cellBottom?.hasTile() || cellLeft?.hasTile(),
67
+ [styles.sharpBottomRight]: cellBottom?.hasTile() || cellRight?.hasTile(),
56
68
  })}
57
69
  style={style}
58
70
  >
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable max-lines, max-statements */
2
+ import { PayloadAction } from '@reduxjs/toolkit';
2
3
  import { BLANK, EMPTY_CELL } from '@scrabble-solver/constants';
3
4
  import { Board, Cell } from '@scrabble-solver/types';
4
5
  import {
@@ -14,10 +15,9 @@ import {
14
15
  } from 'react';
15
16
  import { useDispatch } from 'react-redux';
16
17
  import { useLatest } from 'react-use';
17
- import { AnyAction } from 'redux';
18
18
 
19
19
  import { LOCALE_FEATURES } from 'i18n';
20
- import { createGridOf, createKeyboardNavigation, extractCharacters, extractInputValue, isCtrl } from 'lib';
20
+ import { createGridOf, createKeyboardNavigation, extractCharacters, extractInputValue } from 'lib';
21
21
  import { boardSlice, selectConfig, selectLocale, useTypedSelector } from 'state';
22
22
  import { Direction } from 'types';
23
23
 
@@ -85,7 +85,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
85
85
  const insertValue = useCallback(
86
86
  (position: Point, value: string) => {
87
87
  const characters = value ? extractCharacters(config, value).filter((character) => character !== BLANK) : [BLANK];
88
- const actions: AnyAction[] = [];
88
+ const actions: PayloadAction<unknown>[] = [];
89
89
  let board = new Board({ rows: rows.map((row) => row.map((cell) => cell.clone())) });
90
90
  let { x, y } = position;
91
91
 
@@ -225,7 +225,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
225
225
  onArrowDown: (event) => {
226
226
  event.preventDefault();
227
227
 
228
- if (isCtrl(event)) {
228
+ if (direction === 'horizontal') {
229
229
  onDirectionToggle();
230
230
  } else {
231
231
  changeActiveIndex(0, 1);
@@ -234,7 +234,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
234
234
  onArrowLeft: (event) => {
235
235
  event.preventDefault();
236
236
 
237
- if (isCtrl(event)) {
237
+ if (direction === 'vertical') {
238
238
  onDirectionToggle();
239
239
  } else {
240
240
  changeActiveIndex(LOCALE_FEATURES[locale].direction === 'ltr' ? -1 : 1, 0);
@@ -243,7 +243,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
243
243
  onArrowRight: (event) => {
244
244
  event.preventDefault();
245
245
 
246
- if (isCtrl(event)) {
246
+ if (direction === 'vertical') {
247
247
  onDirectionToggle();
248
248
  } else {
249
249
  changeActiveIndex(LOCALE_FEATURES[locale].direction === 'ltr' ? 1 : -1, 0);
@@ -252,7 +252,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
252
252
  onArrowUp: (event) => {
253
253
  event.preventDefault();
254
254
 
255
- if (isCtrl(event)) {
255
+ if (direction === 'horizontal') {
256
256
  onDirectionToggle();
257
257
  } else {
258
258
  changeActiveIndex(0, -1);
@@ -289,22 +289,6 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
289
289
 
290
290
  const { x, y } = position;
291
291
  const character = event.key.toLowerCase();
292
- const isTogglingBlank = isCtrl(event) && character === 'b';
293
- const twoCharacterTile = config.getTwoCharacterTileByPrefix(character);
294
-
295
- if (isTogglingBlank) {
296
- event.preventDefault();
297
- dispatch(boardSlice.actions.toggleCellIsBlank(position));
298
- return;
299
- }
300
-
301
- if (isCtrl(event) && twoCharacterTile) {
302
- event.preventDefault();
303
- dispatch(boardSlice.actions.changeCellValue({ x, y, value: twoCharacterTile }));
304
- moveFocus(1);
305
- return;
306
- }
307
-
308
292
  const cell = rows[y][x];
309
293
  const twoCharacterCandidate = cell.tile.character + character;
310
294
 
@@ -333,7 +317,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
333
317
  dispatch(boardSlice.actions.toggleCellIsBlank(position));
334
318
  },
335
319
  });
336
- }, [changeActiveIndex, config, dispatch, locale, onDirectionToggle, rows]);
320
+ }, [changeActiveIndex, config, direction, dispatch, locale, moveFocus, onDirectionToggle, rows]);
337
321
 
338
322
  const onPaste = useCallback<ClipboardEventHandler>(
339
323
  (event) => {
@@ -1,15 +1,30 @@
1
+ @import 'styles/mixins';
2
+
1
3
  .dictionary {
4
+ @include scrollbars;
5
+
2
6
  position: relative;
7
+ max-height: var(--dictionary--height);
3
8
  height: var(--dictionary--height);
4
9
  overflow-y: auto;
5
10
  word-break: break-word;
6
11
  transition: var(--transition);
12
+
13
+ &.isAllowed {
14
+ background-color: var(--color--green--light);
15
+ }
16
+
17
+ &.isNotAllowed {
18
+ background-color: var(--color--red--light);
19
+ }
7
20
  }
8
21
 
9
22
  .result {
10
23
  transition: var(--transition);
11
24
 
12
25
  &.isAllowed {
26
+ background-color: var(--color--green--light);
27
+
13
28
  & + & {
14
29
  .content {
15
30
  padding-top: 0;
@@ -18,6 +33,8 @@
18
33
  }
19
34
 
20
35
  &.isNotAllowed {
36
+ background-color: var(--color--red--light);
37
+
21
38
  & + & {
22
39
  .content {
23
40
  padding-top: 0;
@@ -37,14 +54,6 @@
37
54
  text-transform: uppercase;
38
55
  }
39
56
 
40
- .isAllowed {
41
- background-color: var(--color--green--light);
42
- }
43
-
44
- .isNotAllowed {
45
- background-color: var(--color--red--light);
46
- }
47
-
48
57
  .definitions {
49
58
  margin: 0;
50
59
 
@@ -16,18 +16,18 @@ const Dictionary: FunctionComponent<Props> = ({ className }) => {
16
16
  const translate = useTranslate();
17
17
  const { results, isLoading } = useTypedSelector(selectDictionary);
18
18
  const error = useTypedSelector(selectDictionaryError);
19
- const isFirstAllowed = results.length > 0 ? results[0].isAllowed : undefined;
19
+ const isLastAllowed = results.length > 0 ? results[results.length - 1].isAllowed : undefined;
20
20
 
21
21
  return (
22
22
  <div
23
23
  className={classNames(styles.dictionary, className, {
24
- [styles.isAllowed]: isFirstAllowed === true,
25
- [styles.isNotAllowed]: isFirstAllowed === false,
24
+ [styles.isAllowed]: isLastAllowed === true,
25
+ [styles.isNotAllowed]: isLastAllowed === false,
26
26
  })}
27
27
  >
28
- {typeof error !== 'undefined' && <EmptyState variant="error">{error.message}</EmptyState>}
28
+ {typeof error !== 'undefined' && !isLoading && <EmptyState variant="error">{error.message}</EmptyState>}
29
29
 
30
- {typeof error === 'undefined' && results.length === 0 && (
30
+ {typeof error === 'undefined' && !isLoading && results.length === 0 && (
31
31
  <EmptyState variant="info">{translate('dictionary.empty-state.uninitialized')}</EmptyState>
32
32
  )}
33
33
 
@@ -8,4 +8,5 @@
8
8
  @include text-input;
9
9
 
10
10
  border: none;
11
+ border-radius: inherit;
11
12
  }
@@ -8,7 +8,8 @@
8
8
 
9
9
  .tiles {
10
10
  display: inline-block;
11
+ max-width: 100%;
11
12
  margin-bottom: var(--spacing--m);
12
- height: 40px;
13
+ height: 48px;
13
14
  overflow: visible;
14
15
  }
@@ -41,8 +41,7 @@ const EmptyState: FunctionComponent<Props> = ({ children, className, variant })
41
41
  return (
42
42
  <div className={classNames(styles.emptyState, className)}>
43
43
  <PlainTiles className={styles.tiles} color={COLORS_PER_TYPE[variant]} content={content} />
44
-
45
- <div className={styles.content}>{children}</div>
44
+ <div>{children}</div>
46
45
  </div>
47
46
  );
48
47
  };
@@ -30,6 +30,6 @@
30
30
  }
31
31
 
32
32
  .tiles {
33
- width: 298px;
33
+ width: 100% * 2 / 3;
34
34
  height: 100%;
35
35
  }
@@ -29,7 +29,7 @@ const Loading: FunctionComponent<Props> = ({ className, wave = true }) => {
29
29
  const content = useMemo(() => prepareContent(message), [message]);
30
30
 
31
31
  return (
32
- <div className={classNames(styles.loading, className)}>
32
+ <div aria-label={translation} className={classNames(styles.loading, className)} role="status">
33
33
  <div className={styles.dim} />
34
34
  <div className={styles.logo}>
35
35
  <PlainTiles className={classNames(styles.tiles)} content={content} dropShadow wave={wave} />
@@ -1,3 +1,8 @@
1
+ /**
2
+ * This component is unused, but it serves as a blueprint for the Logo.svg.
3
+ * Logo.svg is what this component generates with all the text nodes transformed
4
+ * into paths (manually with Inkscape), and corner radius removed from tiles.
5
+ */
1
6
  import { CSSProperties, FunctionComponent } from 'react';
2
7
 
3
8
  import PlainTiles from '../PlainTiles';
@@ -112,10 +112,11 @@
112
112
  }
113
113
 
114
114
  .content {
115
+ @include scrollbars;
116
+
115
117
  position: relative;
116
118
  flex: 1;
117
119
  min-height: 0;
118
- margin-top: calc(-1 * var(--spacing--l));
119
120
  padding: var(--spacing--l);
120
121
  overflow: auto;
121
122
  }
@@ -1,7 +1,7 @@
1
1
  import classNames from 'classnames';
2
2
  import { FunctionComponent } from 'react';
3
3
 
4
- import { useIsTouchDevice, useMediaQuery } from 'hooks';
4
+ import { useAppLayout } from 'hooks';
5
5
  import { CardChecklist, Cog, Eraser, Github, Keyboard, List, Sack } from 'icons';
6
6
  import { GITHUB_PROJECT_URL } from 'parameters';
7
7
  import { selectHasInvalidWords, selectHasOverusedTiles, useTranslate, useTypedSelector } from 'state';
@@ -30,10 +30,9 @@ const NavButtons: FunctionComponent<Props> = ({
30
30
  const translate = useTranslate();
31
31
  const hasOverusedTiles = useTypedSelector(selectHasOverusedTiles);
32
32
  const hasInvalidWords = useTypedSelector(selectHasInvalidWords);
33
- const isTouchDevice = useIsTouchDevice();
34
- const isLessThanS = useMediaQuery('<s');
33
+ const { showKeyMap, showShortNav } = useAppLayout();
35
34
 
36
- if (isLessThanS) {
35
+ if (showShortNav) {
37
36
  return (
38
37
  <div className={styles.navButtons}>
39
38
  <div className={styles.group}>
@@ -114,7 +113,7 @@ const NavButtons: FunctionComponent<Props> = ({
114
113
  <div className={styles.separator} />
115
114
 
116
115
  <div className={styles.group}>
117
- {!isTouchDevice && (
116
+ {showKeyMap && (
118
117
  <IconButton
119
118
  aria-label={translate('keyMap')}
120
119
  className={styles.button}
@@ -20,5 +20,5 @@ $duration: 750ms;
20
20
  }
21
21
 
22
22
  .dropShadow {
23
- filter: drop-shadow(0 0 4px rgba(#000, 0.4));
23
+ filter: drop-shadow(0 3px 3px rgba(0, 0, 0, 0.4));
24
24
  }
@@ -14,12 +14,12 @@ interface Props {
14
14
 
15
15
  const Tile: FunctionComponent<Props> = ({ character, className, color, fontFamily, points, size, transform, x, y }) => (
16
16
  <g className={className} transform={transform}>
17
- <rect fill={color} height={size} width={size} x={x} y={y} />
17
+ <rect fill={color} height={size} rx={size * 0.15} width={size} x={x} y={y} />
18
18
 
19
19
  <text
20
20
  dominantBaseline="central"
21
21
  fontFamily={fontFamily}
22
- fontSize={0.6 * size}
22
+ fontSize={size * 0.6}
23
23
  fontWeight="bold"
24
24
  textAnchor="middle"
25
25
  x={x + size / 2}
@@ -32,7 +32,7 @@ const Tile: FunctionComponent<Props> = ({ character, className, color, fontFamil
32
32
  <text
33
33
  dominantBaseline="text-after-edge"
34
34
  fontFamily={fontFamily}
35
- fontSize={0.25 * size}
35
+ fontSize={size * 0.25}
36
36
  fontWeight="bold"
37
37
  textAnchor="end"
38
38
  x={x + size * 0.9}