@scrabble-solver/scrabble-solver 2.11.7 → 2.11.8

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 (49) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +6 -6
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/eslint/.cache_8dgz12 +1 -1
  5. package/.next/cache/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 +73 -30
  16. package/.next/server/chunks/865.js +153 -115
  17. package/.next/server/middleware-build-manifest.js +1 -1
  18. package/.next/server/pages/404.html +1 -1
  19. package/.next/server/pages/404.js.nft.json +1 -1
  20. package/.next/server/pages/500.html +1 -1
  21. package/.next/server/pages/_app.js.nft.json +1 -1
  22. package/.next/server/pages/api/solve.js +1 -0
  23. package/.next/server/pages/index.html +1 -1
  24. package/.next/server/pages/index.js +27 -6
  25. package/.next/server/pages/index.js.nft.json +1 -1
  26. package/.next/server/pages/index.json +1 -1
  27. package/.next/server/pages-manifest.json +2 -2
  28. package/.next/static/{uhB6d-q63uRC6RubwepLq → 5ttGCAW8jcIKxpR8om9fK}/_buildManifest.js +1 -1
  29. package/.next/static/chunks/pages/_app-76a8840b6244d5a2.js +28 -0
  30. package/.next/static/chunks/pages/index-6894f40e6cac9243.js +1 -0
  31. package/.next/static/css/{edaeaa48321b4cf2.css → af871fef886ef5b7.css} +2 -2
  32. package/.next/static/css/c6e0e01f44fc0425.css +1 -0
  33. package/.next/trace +50 -50
  34. package/package.json +9 -9
  35. package/src/components/Board/hooks/useBackgroundImage.tsx +13 -9
  36. package/src/components/Board/hooks/useGrid.ts +17 -1
  37. package/src/components/Key/Key.module.scss +7 -11
  38. package/src/components/Rack/Rack.tsx +4 -1
  39. package/src/components/Rack/RackTile.tsx +12 -2
  40. package/src/components/Tile/Tile.module.scss +4 -0
  41. package/src/lib/index.ts +1 -0
  42. package/src/lib/isCtrl.ts +7 -0
  43. package/src/modals/KeyMapModal/KeyMapModal.tsx +20 -4
  44. package/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +10 -4
  45. package/src/styles/variables.scss +2 -0
  46. package/.next/static/chunks/pages/_app-e89a3c225b87516a.js +0 -28
  47. package/.next/static/chunks/pages/index-58744f49bf6b891f.js +0 -1
  48. package/.next/static/css/34adfcf12a7d9bb6.css +0 -1
  49. /package/.next/static/{uhB6d-q63uRC6RubwepLq → 5ttGCAW8jcIKxpR8om9fK}/_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.7",
3
+ "version": "2.11.8",
4
4
  "description": "Scrabble Solver 2 - App",
5
5
  "engines": {
6
6
  "node": ">=16"
@@ -31,13 +31,13 @@
31
31
  "@floating-ui/react": "^0.21.1",
32
32
  "@kamilmielnik/trie": "^2.0.1",
33
33
  "@reduxjs/toolkit": "^1.9.3",
34
- "@scrabble-solver/configs": "^2.11.7",
35
- "@scrabble-solver/constants": "^2.11.7",
36
- "@scrabble-solver/dictionaries": "^2.11.7",
37
- "@scrabble-solver/logger": "^2.11.7",
38
- "@scrabble-solver/solver": "^2.11.7",
39
- "@scrabble-solver/types": "^2.11.7",
40
- "@scrabble-solver/word-definitions": "^2.11.7",
34
+ "@scrabble-solver/configs": "^2.11.8",
35
+ "@scrabble-solver/constants": "^2.11.8",
36
+ "@scrabble-solver/dictionaries": "^2.11.8",
37
+ "@scrabble-solver/logger": "^2.11.8",
38
+ "@scrabble-solver/solver": "^2.11.8",
39
+ "@scrabble-solver/types": "^2.11.8",
40
+ "@scrabble-solver/word-definitions": "^2.11.8",
41
41
  "classnames": "^2.3.2",
42
42
  "include-media": "^2.0.0",
43
43
  "include-media-query-builder": "^1.1.0",
@@ -71,5 +71,5 @@
71
71
  "sass": "^1.59.3",
72
72
  "workbox-webpack-plugin": "^6.5.4"
73
73
  },
74
- "gitHead": "2d485c8578e634628fa627d0c140ff9656a7375b"
74
+ "gitHead": "fa0c31d85a98ce5704ff8e57741204a50071bdc6"
75
75
  }
@@ -5,7 +5,7 @@ import { useMemo } from 'react';
5
5
  import { renderToString } from 'react-dom/server';
6
6
  import { Provider } from 'react-redux';
7
7
 
8
- import { useAppLayout } from 'hooks';
8
+ import { useAppLayout, useMediaQueries } from 'hooks';
9
9
  import { FlagFill, Star } from 'icons';
10
10
  import { dataUrlToBlob, getTileSizes } from 'lib';
11
11
  import { BORDER_COLOR_LIGHT, BORDER_RADIUS, BORDER_WIDTH, COLOR_BONUS_START, COLOR_FILTERED } from 'parameters';
@@ -14,6 +14,8 @@ import { Point } from 'types';
14
14
 
15
15
  import { getBonusColor } from '../lib';
16
16
 
17
+ const BORDER_RADIUS_XS = 3;
18
+ const GRID_LINE_SIZE = 1;
17
19
  const HORIZONTAL_LINE = 'h';
18
20
  const VERTICAL_LINE = 'v';
19
21
  const BONUS = 'b';
@@ -23,6 +25,8 @@ const CELL_FILTER = 'c';
23
25
 
24
26
  const useBackgroundImage = () => {
25
27
  const { boardSize, cellSize } = useAppLayout();
28
+ const { isLessThanXs } = useMediaQueries();
29
+ const borderRadius = isLessThanXs ? BORDER_RADIUS_XS : BORDER_RADIUS;
26
30
  const config = useTypedSelector(selectConfig);
27
31
  const center = { x: Math.floor(config.boardWidth / 2), y: Math.floor(config.boardHeight / 2) };
28
32
  const cellFilter = useTypedSelector(selectCellFilter);
@@ -53,19 +57,19 @@ const useBackgroundImage = () => {
53
57
  >
54
58
  <defs>
55
59
  <symbol id={HORIZONTAL_LINE}>
56
- <rect fill={BORDER_COLOR_LIGHT} height={1} width={viewBoxWidth} />
60
+ <rect fill={BORDER_COLOR_LIGHT} height={GRID_LINE_SIZE} width={viewBoxWidth} />
57
61
  </symbol>
58
62
 
59
63
  <symbol id={VERTICAL_LINE}>
60
- <rect fill={BORDER_COLOR_LIGHT} height={viewBoxHeight} width={1} />
64
+ <rect fill={BORDER_COLOR_LIGHT} height={viewBoxHeight} width={GRID_LINE_SIZE} />
61
65
  </symbol>
62
66
 
63
67
  <symbol id={BONUS}>
64
- <rect height={bonusSize} rx={BORDER_RADIUS} width={bonusSize} x={bonusOffset} y={bonusOffset} />
68
+ <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
65
69
  </symbol>
66
70
 
67
71
  <symbol id={BONUS_WORD_2}>
68
- <rect height={bonusSize} rx={BORDER_RADIUS} width={bonusSize} x={bonusOffset} y={bonusOffset} />
72
+ <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
69
73
 
70
74
  <text
71
75
  dominantBaseline="central"
@@ -82,7 +86,7 @@ const useBackgroundImage = () => {
82
86
  </symbol>
83
87
 
84
88
  <symbol id={BONUS_WORD_3}>
85
- <rect height={bonusSize} rx={BORDER_RADIUS} width={bonusSize} x={bonusOffset} y={bonusOffset} />
89
+ <rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
86
90
 
87
91
  <text
88
92
  dominantBaseline="central"
@@ -102,7 +106,7 @@ const useBackgroundImage = () => {
102
106
  <rect
103
107
  fill={COLOR_FILTERED}
104
108
  height={bonusSize}
105
- rx={BORDER_RADIUS}
109
+ rx={borderRadius}
106
110
  width={bonusSize}
107
111
  x={bonusOffset}
108
112
  y={bonusOffset}
@@ -112,7 +116,7 @@ const useBackgroundImage = () => {
112
116
  </symbol>
113
117
  </defs>
114
118
 
115
- <rect fill="white" height={viewBoxHeight} rx={BORDER_RADIUS} width={viewBoxWidth} x="0" y="0" />
119
+ <rect fill="white" height={viewBoxHeight} rx={borderRadius} width={viewBoxWidth} x="0" y="0" />
116
120
 
117
121
  {Array.from({ length: config.boardHeight - 1 }).map((_value, index) => (
118
122
  <use key={index} href={`#${HORIZONTAL_LINE}`} y={(index + 1) * (cellSize + BORDER_WIDTH) - BORDER_WIDTH} />
@@ -137,7 +141,7 @@ const useBackgroundImage = () => {
137
141
  <rect
138
142
  fill={COLOR_BONUS_START}
139
143
  height={bonusSize}
140
- rx={BORDER_RADIUS}
144
+ rx={borderRadius}
141
145
  width={bonusSize}
142
146
  x={getX(center) + bonusOffset}
143
147
  y={getY(center) + bonusOffset}
@@ -17,7 +17,7 @@ import { useDispatch } from 'react-redux';
17
17
 
18
18
  import { useLatest } from 'hooks';
19
19
  import { LOCALE_FEATURES } from 'i18n';
20
- import { createGridOf, createKeyboardNavigation, extractCharacters, extractInputValue } from 'lib';
20
+ import { createGridOf, createKeyboardNavigation, extractCharacters, extractInputValue, isCtrl } from 'lib';
21
21
  import { boardSlice, selectConfig, selectLocale, useTypedSelector } from 'state';
22
22
  import { Direction, Point } from 'types';
23
23
 
@@ -290,6 +290,22 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
290
290
 
291
291
  const { x, y } = position;
292
292
  const character = event.key.toLowerCase();
293
+ const isTogglingBlank = isCtrl(event) && character === 'b';
294
+ const twoCharacterTile = config.getTwoCharacterTileByPrefix(character);
295
+
296
+ if (isTogglingBlank) {
297
+ event.preventDefault();
298
+ dispatch(boardSlice.actions.toggleCellIsBlank(position));
299
+ return;
300
+ }
301
+
302
+ if (isCtrl(event) && twoCharacterTile) {
303
+ event.preventDefault();
304
+ dispatch(boardSlice.actions.changeCellValue({ x, y, value: twoCharacterTile }));
305
+ moveFocus(1);
306
+ return;
307
+ }
308
+
293
309
  const cell = rows[y][x];
294
310
  const twoCharacterCandidate = cell.tile.character + character;
295
311
 
@@ -1,19 +1,15 @@
1
- $key-size: 36px;
2
- $icon-size: 15px;
3
-
4
1
  .key {
5
2
  position: relative;
6
- top: calc(0px - 2 * var(--border--width));
7
3
  display: inline-block;
8
- min-width: $key-size;
9
- height: $key-size;
4
+ min-width: var(--key--height);
5
+ height: var(--key--height);
10
6
  padding: var(--spacing--s) var(--spacing--m);
11
7
  background-color: var(--color--white);
12
8
  border: var(--border);
13
9
  border-radius: var(--border--radius);
14
10
  box-shadow: var(--box-shadow);
15
11
  font-family: var(--font--family--monospace);
16
- line-height: calc(#{$key-size} - 2 * (var(--spacing--s) + var(--border--width)));
12
+ line-height: calc(var(--key--height) - 2 * (var(--spacing--s) + var(--border--width)));
17
13
  vertical-align: middle;
18
14
  text-align: center;
19
15
  white-space: nowrap;
@@ -24,9 +20,9 @@ $icon-size: 15px;
24
20
 
25
21
  :global(svg) {
26
22
  position: absolute;
27
- top: calc(#{($key-size - $icon-size) / 2} - var(--border--width));
28
- left: calc(#{($key-size - $icon-size) / 2} - var(--border--width));
29
- width: $icon-size;
30
- height: $icon-size;
23
+ top: calc((var(--key--height) - var(--key--icon--size)) / 2 - var(--border--width));
24
+ left: calc((var(--key--height) - var(--key--icon--size)) / 2 - var(--border--width));
25
+ width: var(--key--icon--size);
26
+ height: var(--key--icon--size);
31
27
  }
32
28
  }
@@ -9,6 +9,7 @@ import {
9
9
  extractCharacters,
10
10
  extractInputValue,
11
11
  getTileSizes,
12
+ isCtrl,
12
13
  zipCharactersAndTiles,
13
14
  } from 'lib';
14
15
  import { rackSlice, selectConfig, selectLocale, selectRack, selectResultCandidateTiles, useTypedSelector } from 'state';
@@ -100,7 +101,9 @@ const Rack: FunctionComponent<Props> = ({ className, tileSize }) => {
100
101
  changeActiveIndex(1);
101
102
  },
102
103
  onKeyDown: (event) => {
103
- if (event.currentTarget.value === event.key) {
104
+ if (isCtrl(event) && config.isTwoCharacterTilePrefix(event.key)) {
105
+ changeActiveIndex(1);
106
+ } else if (event.currentTarget.value === event.key) {
104
107
  // change event did not fire because the same character was typed over the current one
105
108
  // but we still want to move the caret
106
109
  event.preventDefault();
@@ -13,7 +13,7 @@ import {
13
13
  } from 'react';
14
14
  import { useDispatch } from 'react-redux';
15
15
 
16
- import { createKeyboardNavigation, extractCharacters, extractInputValue } from 'lib';
16
+ import { createKeyboardNavigation, extractCharacters, extractInputValue, isCtrl } from 'lib';
17
17
  import {
18
18
  rackSlice,
19
19
  selectCharacterIsValid,
@@ -80,7 +80,17 @@ const RackTile: FunctionComponent<Props> = ({
80
80
  event.preventDefault();
81
81
  dispatch(rackSlice.actions.changeCharacter({ character: null, index }));
82
82
  },
83
- onKeyDown,
83
+ onKeyDown: (event) => {
84
+ if (isCtrl(event) && config.isTwoCharacterTilePrefix(event.key)) {
85
+ event.preventDefault();
86
+ event.stopPropagation();
87
+ const twoTilesCharacter = config.getTwoCharacterTileByPrefix(event.key);
88
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
89
+ dispatch(rackSlice.actions.changeCharacter({ character: twoTilesCharacter!, index }));
90
+ }
91
+
92
+ onKeyDown(event);
93
+ },
84
94
  });
85
95
  }, [index, onKeyDown]);
86
96
 
@@ -17,6 +17,10 @@
17
17
  transition-property: background-color, color, box-shadow;
18
18
  user-select: none;
19
19
 
20
+ @include media('<xs') {
21
+ --border--radius: 3px;
22
+ }
23
+
20
24
  &.points1 {
21
25
  --background-color: var(--color--yellow);
22
26
  }
package/src/lib/index.ts CHANGED
@@ -22,6 +22,7 @@ export { default as getTotalRemainingTilesCount } from './getTotalRemainingTiles
22
22
  export { default as groupResults } from './groupResults';
23
23
  export { default as guessLocale } from './guessLocale';
24
24
  export { default as inverseDirection } from './inverseDirection';
25
+ export { default as isCtrl } from './isCtrl';
25
26
  export { default as isMac } from './isMac';
26
27
  export { default as isRegExp } from './isRegExp';
27
28
  export { default as isStringArray } from './isStringArray';
@@ -0,0 +1,7 @@
1
+ import { KeyboardEvent } from 'react';
2
+
3
+ const isCtrl = <T>(event: KeyboardEvent<T> | globalThis.KeyboardEvent): boolean => {
4
+ return event.ctrlKey || event.metaKey;
5
+ };
6
+
7
+ export default isCtrl;
@@ -1,10 +1,10 @@
1
1
  import { FunctionComponent, memo } from 'react';
2
2
 
3
- import { Modal } from 'components';
4
- import { useTranslate } from 'state';
3
+ import { Key, Modal } from 'components';
4
+ import { selectConfig, useTranslate, useTypedSelector } from 'state';
5
5
 
6
6
  import { Mapping } from './components';
7
- import { ARROWS, BACKSPACE, DEL, ENTER, SPACE } from './keys';
7
+ import { ARROWS, BACKSPACE, CTRL, DEL, ENTER, SPACE } from './keys';
8
8
 
9
9
  interface Props {
10
10
  className?: string;
@@ -14,6 +14,7 @@ interface Props {
14
14
 
15
15
  const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) => {
16
16
  const translate = useTranslate();
17
+ const config = useTypedSelector(selectConfig);
17
18
 
18
19
  return (
19
20
  <Modal className={className} isOpen={isOpen} title={translate('keyMap')} onClose={onClose}>
@@ -21,10 +22,25 @@ const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) =
21
22
  <Mapping description={translate('keyMap.board-and-rack.navigate')} mapping={[ARROWS]} />
22
23
  <Mapping description={translate('keyMap.board-and-rack.remove-tile')} mapping={[DEL, BACKSPACE]} />
23
24
  <Mapping description={translate('keyMap.board-and-rack.submit')} mapping={[ENTER]} />
25
+ {config.twoCharacterTiles.length > 0 && (
26
+ <Mapping
27
+ description={translate('keyMap.board-and-rack.insert-two-letter-tile')}
28
+ mapping={[
29
+ [
30
+ CTRL,
31
+ <>
32
+ {config.twoCharacterTiles.map(([firstLetter]) => (
33
+ <Key key={firstLetter}>{firstLetter.toUpperCase()}</Key>
34
+ ))}
35
+ </>,
36
+ ],
37
+ ]}
38
+ />
39
+ )}
24
40
  </Modal.Section>
25
41
 
26
42
  <Modal.Section title={translate('keyMap.board')}>
27
- <Mapping description={translate('keyMap.board.toggle-blank')} mapping={[SPACE]} />
43
+ <Mapping description={translate('keyMap.board.toggle-blank')} mapping={[SPACE, [CTRL, <Key key="b">B</Key>]]} />
28
44
  <Mapping description={translate('keyMap.board.toggle-direction')} mapping={[ARROWS]} />
29
45
  </Modal.Section>
30
46
 
@@ -18,13 +18,19 @@
18
18
  flex-wrap: wrap;
19
19
  }
20
20
 
21
- .group,
22
- .plus {
21
+ .group {
23
22
  margin: 0 var(--spacing--s);
24
23
  }
25
24
 
26
- .slash {
27
- margin: 0 var(--spacing--m);
25
+ .slash,
26
+ .plus {
27
+ min-width: var(--key--height);
28
+ height: var(--key--height);
29
+ padding: var(--spacing--s) var(--spacing--m);
30
+ font-family: var(--font--family--monospace);
31
+ line-height: calc(var(--key--height) - 2 * (var(--spacing--s) + var(--border--width)));
32
+ vertical-align: middle;
33
+ text-align: center;
28
34
  }
29
35
 
30
36
  .group {
@@ -107,6 +107,8 @@ $easeOutSine: cubic-bezier(0.61, 1, 0.88, 1);
107
107
  --button--icon--size: 24px;
108
108
  --dictionary--height: 260px;
109
109
  --dictionary--height--mobile: 110px;
110
+ --key--height: 36px;
111
+ --key--icon--size: 16px;
110
112
  --logo--aspect-ratio: 682 / 166;
111
113
  --logo--height: 60px;
112
114
  --modal--width: 370px;