@scrabble-solver/scrabble-solver 2.11.0 → 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 (89) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +7 -7
  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/routes-manifest.json +1 -1
  15. package/.next/server/chunks/277.js +341 -459
  16. package/.next/server/middleware-build-manifest.js +1 -1
  17. package/.next/server/pages/404.html +2 -2
  18. package/.next/server/pages/404.js.nft.json +1 -1
  19. package/.next/server/pages/500.html +1 -1
  20. package/.next/server/pages/_app.js +0 -24
  21. package/.next/server/pages/_app.js.nft.json +1 -1
  22. package/.next/server/pages/_document.js.nft.json +1 -1
  23. package/.next/server/pages/api/solve.js +0 -1
  24. package/.next/server/pages/index.html +1 -1
  25. package/.next/server/pages/index.js +4 -52
  26. package/.next/server/pages/index.js.nft.json +1 -1
  27. package/.next/server/pages/index.json +1 -1
  28. package/.next/static/chunks/pages/{404-e0f30450e9920dc3.js → 404-448ba28510855455.js} +1 -1
  29. package/.next/static/chunks/pages/_app-270526803bc274eb.js +28 -0
  30. package/.next/static/chunks/pages/index-c6e7754ccf3532df.js +1 -0
  31. package/.next/static/css/ad39b36eab07e613.css +1 -0
  32. package/.next/static/css/e5803e581e4c0451.css +2 -0
  33. package/.next/static/esK8DG-6aS5V7QFRtR3YE/_buildManifest.js +1 -0
  34. package/.next/trace +53 -55
  35. package/package.json +9 -14
  36. package/src/components/{Solver/components/EmptyState/EmptyState.module.scss → Alert/Alert.module.scss} +11 -7
  37. package/src/components/{Solver/components/EmptyState/EmptyState.tsx → Alert/Alert.tsx} +8 -6
  38. package/src/components/Alert/index.ts +1 -0
  39. package/src/components/Board/Board.module.scss +55 -0
  40. package/src/components/Board/BoardPure.tsx +4 -0
  41. package/src/components/Board/components/Cell/Cell.module.scss +41 -0
  42. package/src/components/Board/components/Cell/Cell.tsx +12 -0
  43. package/src/components/Board/components/Cell/CellPure.tsx +12 -0
  44. package/src/components/Board/hooks/useGrid.ts +8 -24
  45. package/src/components/Dictionary/Dictionary.module.scss +4 -0
  46. package/src/components/DictionaryInput/DictionaryInput.module.scss +1 -0
  47. package/src/components/EmptyState/EmptyState.module.scss +2 -1
  48. package/src/components/EmptyState/EmptyState.tsx +1 -2
  49. package/src/components/Loading/Loading.module.scss +1 -1
  50. package/src/components/Logo/Logo.tsx +5 -0
  51. package/src/components/Modal/Modal.module.scss +2 -0
  52. package/src/components/PlainTiles/PlainTiles.module.scss +1 -1
  53. package/src/components/PlainTiles/Tile.tsx +3 -3
  54. package/src/components/Rack/Rack.module.scss +25 -0
  55. package/src/components/Rack/Rack.tsx +5 -4
  56. package/src/components/Rack/RackTile.tsx +6 -13
  57. package/src/components/Results/Results.module.scss +28 -0
  58. package/src/components/ResultsInput/ResultsInput.module.scss +1 -0
  59. package/src/components/Solver/Solver.module.scss +5 -4
  60. package/src/components/Solver/Solver.tsx +8 -7
  61. package/src/components/Solver/components/index.ts +0 -1
  62. package/src/components/Tile/Tile.module.scss +14 -2
  63. package/src/components/Tooltip/Tooltip.module.scss +1 -72
  64. package/src/components/Tooltip/useTooltip.tsx +25 -35
  65. package/src/components/index.ts +1 -0
  66. package/src/hooks/index.ts +0 -1
  67. package/src/lib/index.ts +0 -1
  68. package/src/modals/DictionaryModal/DictionaryModal.module.scss +6 -1
  69. package/src/modals/KeyMapModal/KeyMapModal.tsx +5 -21
  70. package/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +4 -0
  71. package/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -0
  72. package/src/modals/ResultsModal/ResultsModal.module.scss +1 -0
  73. package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +2 -0
  74. package/src/modals/WordsModal/WordsModal.module.scss +8 -1
  75. package/src/state/createAppStore.ts +26 -10
  76. package/src/state/types.ts +20 -2
  77. package/src/styles/mixins.scss +22 -0
  78. package/src/styles/variables.scss +16 -1
  79. package/.next/static/45ye7793DY705HOcuK9lJ/_buildManifest.js +0 -1
  80. package/.next/static/chunks/pages/_app-d7acee5e526752d9.js +0 -28
  81. package/.next/static/chunks/pages/index-35d2c1c79a201ae2.js +0 -1
  82. package/.next/static/css/a48caa6f57de6e98.css +0 -1
  83. package/.next/static/css/c49bbe944ddd1b39.css +0 -2
  84. package/src/components/Solver/components/EmptyState/index.ts +0 -1
  85. package/src/components/Tooltip/constants.ts +0 -28
  86. package/src/hooks/useUniqueId.ts +0 -9
  87. package/src/lib/isCtrl.ts +0 -7
  88. package/src/state/rootReducer.ts +0 -25
  89. /package/.next/static/{45ye7793DY705HOcuK9lJ → 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.11.0",
3
+ "version": "2.11.1",
4
4
  "description": "Scrabble Solver 2 - App",
5
5
  "engines": {
6
6
  "node": ">=16"
@@ -30,15 +30,14 @@
30
30
  "dependencies": {
31
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.11.0",
36
- "@scrabble-solver/constants": "^2.11.0",
37
- "@scrabble-solver/dictionaries": "^2.11.0",
38
- "@scrabble-solver/logger": "^2.11.0",
39
- "@scrabble-solver/solver": "^2.11.0",
40
- "@scrabble-solver/types": "^2.11.0",
41
- "@scrabble-solver/word-definitions": "^2.11.0",
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",
@@ -47,15 +46,12 @@
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
72
  "sass": "^1.59.2",
78
73
  "workbox-webpack-plugin": "^6.5.4"
79
74
  },
80
- "gitHead": "493dd931e7f16c34c425295a0f048756d6c192c8"
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}
@@ -125,6 +125,47 @@
125
125
  }
126
126
  }
127
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
+ }
128
169
  .icon {
129
170
  $size: 40%;
130
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, moveFocus, onDirectionToggle, rows]);
320
+ }, [changeActiveIndex, config, direction, dispatch, locale, moveFocus, onDirectionToggle, rows]);
337
321
 
338
322
  const onPaste = useCallback<ClipboardEventHandler>(
339
323
  (event) => {
@@ -1,4 +1,8 @@
1
+ @import 'styles/mixins';
2
+
1
3
  .dictionary {
4
+ @include scrollbars;
5
+
2
6
  position: relative;
3
7
  max-height: var(--dictionary--height);
4
8
  height: var(--dictionary--height);
@@ -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
  }
@@ -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,6 +112,8 @@
112
112
  }
113
113
 
114
114
  .content {
115
+ @include scrollbars;
116
+
115
117
  position: relative;
116
118
  flex: 1;
117
119
  min-height: 0;
@@ -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}
@@ -3,6 +3,7 @@
3
3
  .rack {
4
4
  display: flex;
5
5
  box-shadow: var(--box-shadow);
6
+ border-radius: var(--border--radius);
6
7
  }
7
8
 
8
9
  .tile {
@@ -15,3 +16,27 @@
15
16
  z-index: 2;
16
17
  }
17
18
  }
19
+
20
+ .sharpLeft {
21
+ [dir='ltr'] & {
22
+ border-top-left-radius: 0;
23
+ border-bottom-left-radius: 0;
24
+ }
25
+
26
+ [dir='rtl'] & {
27
+ border-top-right-radius: 0;
28
+ border-bottom-right-radius: 0;
29
+ }
30
+ }
31
+
32
+ .sharpRight {
33
+ [dir='ltr'] & {
34
+ border-top-right-radius: 0;
35
+ border-bottom-right-radius: 0;
36
+ }
37
+
38
+ [dir='rtl'] & {
39
+ border-top-left-radius: 0;
40
+ border-bottom-left-radius: 0;
41
+ }
42
+ }
@@ -8,7 +8,6 @@ import {
8
8
  createKeyboardNavigation,
9
9
  extractCharacters,
10
10
  extractInputValue,
11
- isCtrl,
12
11
  zipCharactersAndTiles,
13
12
  } from 'lib';
14
13
  import { rackSlice, selectConfig, selectLocale, selectRack, selectResultCandidateTiles, useTypedSelector } from 'state';
@@ -99,9 +98,7 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
99
98
  changeActiveIndex(1);
100
99
  },
101
100
  onKeyDown: (event) => {
102
- if (isCtrl(event) && config.isTwoCharacterTilePrefix(event.key)) {
103
- changeActiveIndex(1);
104
- } else if (event.currentTarget.value === event.key) {
101
+ if (event.currentTarget.value === event.key) {
105
102
  // change event did not fire because the same character was typed over the current one
106
103
  // but we still want to move the caret
107
104
  event.preventDefault();
@@ -118,6 +115,10 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
118
115
  <RackTile
119
116
  activeIndexRef={activeIndexRef}
120
117
  character={character}
118
+ className={classNames({
119
+ [styles.sharpLeft]: index !== 0,
120
+ [styles.sharpRight]: index !== tiles.length - 1,
121
+ })}
121
122
  index={index}
122
123
  inputRef={tilesRefs[index]}
123
124
  key={index}
@@ -1,5 +1,6 @@
1
1
  import { BLANK } from '@scrabble-solver/constants';
2
2
  import { Tile as TileModel } from '@scrabble-solver/types';
3
+ import classNames from 'classnames';
3
4
  import {
4
5
  ChangeEvent,
5
6
  ChangeEventHandler,
@@ -12,7 +13,7 @@ import {
12
13
  } from 'react';
13
14
  import { useDispatch } from 'react-redux';
14
15
 
15
- import { createKeyboardNavigation, extractCharacters, extractInputValue, isCtrl } from 'lib';
16
+ import { createKeyboardNavigation, extractCharacters, extractInputValue } from 'lib';
16
17
  import {
17
18
  rackSlice,
18
19
  selectCharacterIsValid,
@@ -30,6 +31,7 @@ import styles from './Rack.module.scss';
30
31
  interface Props {
31
32
  activeIndexRef: MutableRefObject<number | undefined>;
32
33
  character: string | null;
34
+ className?: string;
33
35
  index: number;
34
36
  inputRef: RefObject<HTMLInputElement>;
35
37
  size: number;
@@ -41,6 +43,7 @@ interface Props {
41
43
  const RackTile: FunctionComponent<Props> = ({
42
44
  activeIndexRef,
43
45
  character,
46
+ className,
44
47
  index,
45
48
  inputRef,
46
49
  size,
@@ -77,17 +80,7 @@ const RackTile: FunctionComponent<Props> = ({
77
80
  event.preventDefault();
78
81
  dispatch(rackSlice.actions.changeCharacter({ character: null, index }));
79
82
  },
80
- onKeyDown: (event) => {
81
- if (isCtrl(event) && config.isTwoCharacterTilePrefix(event.key)) {
82
- event.preventDefault();
83
- event.stopPropagation();
84
- const twoTilesCharacter = config.getTwoCharacterTileByPrefix(event.key);
85
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
86
- dispatch(rackSlice.actions.changeCharacter({ character: twoTilesCharacter!, index }));
87
- }
88
-
89
- onKeyDown(event);
90
- },
83
+ onKeyDown,
91
84
  });
92
85
  }, [index, onKeyDown]);
93
86
 
@@ -97,7 +90,7 @@ const RackTile: FunctionComponent<Props> = ({
97
90
  index: (index + 1).toLocaleString(locale),
98
91
  })}
99
92
  autoFocus={index === 0}
100
- className={styles.tile}
93
+ className={classNames(styles.tile, className)}
101
94
  character={character === null ? undefined : character}
102
95
  highlighted={tile !== null}
103
96
  inputRef={inputRef}