scrabble-solver 2.11.7 → 2.11.9

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 (149) hide show
  1. package/lerna.json +1 -1
  2. package/package.json +11 -11
  3. package/packages/configs/build/literaki/literaki.js +7 -7
  4. package/packages/configs/build/scrabble/scrabble.js +7 -7
  5. package/packages/configs/package.json +3 -3
  6. package/packages/configs/src/literaki/literaki.ts +7 -7
  7. package/packages/configs/src/scrabble/scrabble.ts +8 -8
  8. package/packages/constants/package.json +1 -1
  9. package/packages/dictionaries/package-lock.json +10 -3
  10. package/packages/dictionaries/package.json +4 -4
  11. package/packages/logger/package-lock.json +227 -6
  12. package/packages/logger/package.json +1 -1
  13. package/packages/scrabble-solver/.next/BUILD_ID +1 -1
  14. package/packages/scrabble-solver/.next/build-manifest.json +7 -7
  15. package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
  16. package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
  17. package/packages/scrabble-solver/.next/cache/next-server.js.nft.json +1 -1
  18. package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
  19. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
  20. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/0.pack +0 -0
  21. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack +0 -0
  22. package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
  23. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
  24. package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
  25. package/packages/scrabble-solver/.next/prerender-manifest.json +1 -1
  26. package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
  27. package/packages/scrabble-solver/.next/server/chunks/131.js +153 -115
  28. package/packages/scrabble-solver/.next/server/chunks/277.js +1430 -691
  29. package/packages/scrabble-solver/.next/server/chunks/44.js +3 -0
  30. package/packages/scrabble-solver/.next/server/chunks/50.js +20 -78
  31. package/packages/scrabble-solver/.next/server/chunks/865.js +153 -115
  32. package/packages/scrabble-solver/.next/server/chunks/911.js +14 -14
  33. package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
  34. package/packages/scrabble-solver/.next/server/pages/404.html +1 -1
  35. package/packages/scrabble-solver/.next/server/pages/404.js.nft.json +1 -1
  36. package/packages/scrabble-solver/.next/server/pages/500.html +1 -1
  37. package/packages/scrabble-solver/.next/server/pages/_app.js +8 -0
  38. package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
  39. package/packages/scrabble-solver/.next/server/pages/api/solve.js +44 -11
  40. package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
  41. package/packages/scrabble-solver/.next/server/pages/index.js +169 -15
  42. package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
  43. package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
  44. package/packages/scrabble-solver/.next/static/9oRWxnZ1xFLSs55FJtiYi/_buildManifest.js +1 -0
  45. package/packages/scrabble-solver/.next/static/chunks/pages/{404-ca203fa27afc37d8.js → 404-b4b5ce15153d4825.js} +1 -1
  46. package/packages/scrabble-solver/.next/static/chunks/pages/_app-b0231bed954dd413.js +28 -0
  47. package/packages/scrabble-solver/.next/static/chunks/pages/index-4e8566409753e1c3.js +1 -0
  48. package/packages/scrabble-solver/.next/static/css/60e8258da7362a1a.css +1 -0
  49. package/packages/scrabble-solver/.next/static/css/fcc46fec97b11afc.css +2 -0
  50. package/packages/scrabble-solver/.next/trace +52 -50
  51. package/packages/scrabble-solver/package-lock.json +4991 -214
  52. package/packages/scrabble-solver/package.json +13 -12
  53. package/packages/scrabble-solver/src/components/Board/Board.module.scss +18 -4
  54. package/packages/scrabble-solver/src/components/Board/Board.tsx +145 -76
  55. package/packages/scrabble-solver/src/components/Board/BoardPure.tsx +32 -40
  56. package/packages/scrabble-solver/src/components/Board/components/Actions/Actions.module.scss +6 -17
  57. package/packages/scrabble-solver/src/components/Board/components/Actions/Actions.tsx +36 -18
  58. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +12 -13
  59. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.tsx +53 -3
  60. package/packages/scrabble-solver/src/components/Board/components/InputPrompt/InputPrompt.module.scss +47 -0
  61. package/packages/scrabble-solver/src/components/Board/components/InputPrompt/InputPrompt.tsx +81 -0
  62. package/packages/scrabble-solver/src/components/Board/components/InputPrompt/index.ts +1 -0
  63. package/packages/scrabble-solver/src/components/Board/components/ToggleDirectionButton/ToggleDirectionButton.module.scss +21 -0
  64. package/packages/scrabble-solver/src/components/Board/components/ToggleDirectionButton/ToggleDirectionButton.tsx +34 -0
  65. package/packages/scrabble-solver/src/components/Board/components/ToggleDirectionButton/index.ts +1 -0
  66. package/packages/scrabble-solver/src/components/Board/components/index.ts +2 -0
  67. package/packages/scrabble-solver/src/components/Board/hooks/index.ts +4 -0
  68. package/packages/scrabble-solver/src/components/Board/hooks/useBackgroundImage.tsx +13 -9
  69. package/packages/scrabble-solver/src/components/Board/hooks/useBoardStyle.ts +27 -0
  70. package/packages/scrabble-solver/src/components/Board/hooks/useFloatingActions.ts +22 -0
  71. package/packages/scrabble-solver/src/components/Board/hooks/useFloatingFocus.ts +10 -0
  72. package/packages/scrabble-solver/src/components/Board/hooks/useFloatingInputPrompt.ts +19 -0
  73. package/packages/scrabble-solver/src/components/Board/hooks/useGrid.ts +19 -2
  74. package/packages/scrabble-solver/src/components/Key/Key.module.scss +7 -11
  75. package/packages/scrabble-solver/src/components/NavButtons/NavButtons.tsx +2 -2
  76. package/packages/scrabble-solver/src/components/Rack/Rack.module.scss +6 -6
  77. package/packages/scrabble-solver/src/components/Rack/Rack.tsx +102 -24
  78. package/packages/scrabble-solver/src/components/Rack/components/InputPrompt/InputPrompt.module.scss +22 -0
  79. package/packages/scrabble-solver/src/components/Rack/components/InputPrompt/InputPrompt.tsx +89 -0
  80. package/packages/scrabble-solver/src/components/Rack/components/InputPrompt/index.ts +1 -0
  81. package/packages/scrabble-solver/src/components/Rack/components/RackTile/RackTile.module.scss +11 -0
  82. package/packages/scrabble-solver/src/components/Rack/{RackTile.tsx → components/RackTile/RackTile.tsx} +59 -9
  83. package/packages/scrabble-solver/src/components/Rack/components/RackTile/index.ts +1 -0
  84. package/packages/scrabble-solver/src/components/Rack/components/index.ts +2 -0
  85. package/packages/scrabble-solver/src/components/Radio/Radio.module.scss +0 -8
  86. package/packages/scrabble-solver/src/components/Solver/Solver.module.scss +0 -20
  87. package/packages/scrabble-solver/src/components/Solver/Solver.tsx +2 -4
  88. package/packages/scrabble-solver/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +2 -10
  89. package/packages/scrabble-solver/src/components/Solver/components/index.ts +0 -1
  90. package/packages/scrabble-solver/src/components/Tile/Tile.module.scss +5 -0
  91. package/packages/scrabble-solver/src/components/Tile/Tile.tsx +8 -6
  92. package/packages/scrabble-solver/src/components/Tile/TilePure.tsx +8 -0
  93. package/packages/scrabble-solver/src/hooks/useAppLayout.ts +3 -1
  94. package/packages/scrabble-solver/src/hooks/useLocalStorage.ts +8 -0
  95. package/packages/scrabble-solver/src/i18n/de.json +6 -1
  96. package/packages/scrabble-solver/src/i18n/en.json +6 -1
  97. package/packages/scrabble-solver/src/i18n/es.json +6 -1
  98. package/packages/scrabble-solver/src/i18n/fa.json +6 -1
  99. package/packages/scrabble-solver/src/i18n/fr.json +6 -1
  100. package/packages/scrabble-solver/src/i18n/pl.json +6 -1
  101. package/packages/scrabble-solver/src/icons/Keyboard.svg +4 -3
  102. package/packages/scrabble-solver/src/icons/KeyboardFill.svg +4 -0
  103. package/packages/scrabble-solver/src/icons/index.ts +1 -0
  104. package/packages/scrabble-solver/src/lib/extractCharacters.test.ts +26 -0
  105. package/packages/scrabble-solver/src/lib/extractCharacters.ts +11 -9
  106. package/packages/scrabble-solver/src/lib/extractCharactersByCase.test.ts +31 -0
  107. package/packages/scrabble-solver/src/lib/extractCharactersByCase.ts +31 -0
  108. package/packages/scrabble-solver/src/lib/index.ts +4 -1
  109. package/packages/scrabble-solver/src/lib/isCtrl.ts +7 -0
  110. package/packages/scrabble-solver/src/lib/isUpperCase.ts +7 -0
  111. package/packages/scrabble-solver/src/modals/KeyMapModal/KeyMapModal.tsx +20 -4
  112. package/packages/scrabble-solver/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +10 -4
  113. package/packages/scrabble-solver/src/modals/SettingsModal/SettingsModal.tsx +5 -1
  114. package/packages/scrabble-solver/src/modals/SettingsModal/components/InputModeSetting/InputModeSetting.module.scss +12 -0
  115. package/packages/scrabble-solver/src/modals/SettingsModal/components/InputModeSetting/InputModeSetting.tsx +55 -0
  116. package/packages/scrabble-solver/src/modals/SettingsModal/components/InputModeSetting/index.ts +1 -0
  117. package/packages/scrabble-solver/src/modals/SettingsModal/components/InputModeSetting/lib.ts +13 -0
  118. package/packages/scrabble-solver/src/modals/SettingsModal/components/InputModeSetting/types.ts +7 -0
  119. package/packages/scrabble-solver/src/modals/SettingsModal/components/index.ts +1 -0
  120. package/packages/scrabble-solver/src/state/localStorage.ts +10 -1
  121. package/packages/scrabble-solver/src/state/selectors.ts +2 -0
  122. package/packages/scrabble-solver/src/state/slices/settingsInitialState.ts +4 -1
  123. package/packages/scrabble-solver/src/state/slices/settingsSlice.ts +6 -1
  124. package/packages/scrabble-solver/src/styles/mixins.scss +1 -0
  125. package/packages/scrabble-solver/src/styles/variables.scss +2 -0
  126. package/packages/scrabble-solver/src/types/index.ts +7 -0
  127. package/packages/solver/build/getCellsScore.js +3 -2
  128. package/packages/solver/package-lock.json +10 -3
  129. package/packages/solver/package.json +5 -5
  130. package/packages/solver/src/getCellsScore.ts +3 -2
  131. package/packages/types/build/Config.d.ts +2 -0
  132. package/packages/types/build/Config.js +3 -0
  133. package/packages/types/build/ConfigJson.d.ts +2 -0
  134. package/packages/types/package.json +2 -2
  135. package/packages/types/src/Config.ts +5 -0
  136. package/packages/types/src/ConfigJson.ts +2 -0
  137. package/packages/word-definitions/package-lock.json +18 -18
  138. package/packages/word-definitions/package.json +2 -2
  139. package/packages/word-lists/package-lock.json +403 -11
  140. package/packages/word-lists/package.json +2 -2
  141. package/packages/scrabble-solver/.next/static/chunks/pages/_app-e89a3c225b87516a.js +0 -28
  142. package/packages/scrabble-solver/.next/static/chunks/pages/index-58744f49bf6b891f.js +0 -1
  143. package/packages/scrabble-solver/.next/static/css/34adfcf12a7d9bb6.css +0 -1
  144. package/packages/scrabble-solver/.next/static/css/edaeaa48321b4cf2.css +0 -2
  145. package/packages/scrabble-solver/.next/static/uhB6d-q63uRC6RubwepLq/_buildManifest.js +0 -1
  146. package/packages/scrabble-solver/src/components/Solver/components/FloatingSolveButton/FloatingSolveButton.module.scss +0 -7
  147. package/packages/scrabble-solver/src/components/Solver/components/FloatingSolveButton/FloatingSolveButton.tsx +0 -53
  148. package/packages/scrabble-solver/src/components/Solver/components/FloatingSolveButton/index.ts +0 -1
  149. /package/packages/scrabble-solver/.next/static/{uhB6d-q63uRC6RubwepLq → 9oRWxnZ1xFLSs55FJtiYi}/_ssgManifest.js +0 -0
@@ -1,9 +1,24 @@
1
1
  import { EMPTY_CELL } from '@scrabble-solver/constants';
2
2
  import { Cell as CellModel } from '@scrabble-solver/types';
3
3
  import classNames from 'classnames';
4
- import { ChangeEventHandler, FunctionComponent, RefObject, useCallback } from 'react';
4
+ import {
5
+ ChangeEventHandler,
6
+ FocusEventHandler,
7
+ FunctionComponent,
8
+ MouseEventHandler,
9
+ RefObject,
10
+ TouchEventHandler,
11
+ useCallback,
12
+ } from 'react';
5
13
 
6
- import { selectCellIsValid, selectLocale, selectTilePoints, useTranslate, useTypedSelector } from 'state';
14
+ import {
15
+ selectCellIsValid,
16
+ selectInputMode,
17
+ selectLocale,
18
+ selectTilePoints,
19
+ useTranslate,
20
+ useTypedSelector,
21
+ } from 'state';
7
22
 
8
23
  import Tile from '../../../Tile';
9
24
 
@@ -37,11 +52,44 @@ const Cell: FunctionComponent<Props> = ({
37
52
  const { tile, x, y } = cell;
38
53
  const translate = useTranslate();
39
54
  const locale = useTypedSelector(selectLocale);
55
+ const inputMode = useTypedSelector(selectInputMode);
40
56
  const points = useTypedSelector((state) => selectTilePoints(state, cell.tile));
41
57
  const isValid = useTypedSelector((state) => selectCellIsValid(state, cell));
42
58
  const isEmpty = tile.character === EMPTY_CELL;
43
59
 
44
- const handleFocus = useCallback(() => onFocus(x, y), [x, y, onFocus]);
60
+ const handleFocus: FocusEventHandler<HTMLInputElement> = useCallback(
61
+ (event) => {
62
+ if (inputMode === 'touchscreen') {
63
+ event.preventDefault();
64
+ event.target.blur();
65
+ }
66
+
67
+ onFocus(x, y);
68
+ },
69
+ [inputMode, onFocus, x, y],
70
+ );
71
+
72
+ const handleMouseDown: MouseEventHandler<HTMLInputElement> = useCallback(
73
+ (event) => {
74
+ if (inputMode === 'touchscreen') {
75
+ event.preventDefault();
76
+ }
77
+
78
+ onFocus(x, y);
79
+ },
80
+ [inputMode, onFocus, x, y],
81
+ );
82
+
83
+ const handleTouchStart: TouchEventHandler<HTMLInputElement> = useCallback(
84
+ (event) => {
85
+ if (inputMode === 'touchscreen') {
86
+ event.preventDefault();
87
+ }
88
+
89
+ onFocus(x, y);
90
+ },
91
+ [inputMode, onFocus, x, y],
92
+ );
45
93
 
46
94
  return (
47
95
  <Tile
@@ -66,6 +114,8 @@ const Cell: FunctionComponent<Props> = ({
66
114
  tabIndex={cell.x === 0 && cell.y === 0 ? undefined : -1}
67
115
  onChange={onChange}
68
116
  onFocus={handleFocus}
117
+ onMouseDown={handleMouseDown}
118
+ onTouchStart={handleTouchStart}
69
119
  />
70
120
  );
71
121
  };
@@ -0,0 +1,47 @@
1
+ @import 'styles/mixins';
2
+
3
+ .inputPrompt {
4
+ display: flex;
5
+ box-shadow: var(--box-shadow);
6
+ border-radius: var(--border--radius);
7
+ transition: var(--transition);
8
+ transition-property: opacity;
9
+ }
10
+
11
+ .toggleDirection {
12
+ [dir='ltr'] & {
13
+ border-top-right-radius: 0;
14
+ border-bottom-right-radius: 0;
15
+ }
16
+
17
+ [dir='rtl'] & {
18
+ border-top-left-radius: 0;
19
+ border-bottom-left-radius: 0;
20
+ }
21
+ }
22
+
23
+ .input {
24
+ @include text-input;
25
+
26
+ height: 100%;
27
+ max-width: 200px;
28
+ border: none;
29
+ border-top: var(--border);
30
+ border-bottom: var(--border);
31
+ }
32
+
33
+ .insert {
34
+ [dir='ltr'] & {
35
+ border-top-left-radius: 0;
36
+ border-bottom-left-radius: 0;
37
+ }
38
+
39
+ [dir='rtl'] & {
40
+ border-top-right-radius: 0;
41
+ border-bottom-right-radius: 0;
42
+ }
43
+ }
44
+
45
+ .insertIcon {
46
+ transform: scale(1.5);
47
+ }
@@ -0,0 +1,81 @@
1
+ import classNames from 'classnames';
2
+ import { FormEventHandler, forwardRef, HTMLProps, MouseEventHandler, useEffect, useState } from 'react';
3
+
4
+ import { Check } from 'icons';
5
+ import { useTranslate } from 'state';
6
+ import { Direction } from 'types';
7
+
8
+ import Button from '../../../Button';
9
+ import ToggleDirectionButton from '../ToggleDirectionButton';
10
+
11
+ import styles from './InputPrompt.module.scss';
12
+
13
+ interface Props extends Omit<HTMLProps<HTMLFormElement>, 'onSubmit'> {
14
+ className?: string;
15
+ direction: Direction;
16
+ initialValue: string;
17
+ onDirectionToggle: MouseEventHandler<HTMLButtonElement>;
18
+ onSubmit: (input: string) => void;
19
+ }
20
+
21
+ const InputPrompt = forwardRef<HTMLFormElement, Props>(
22
+ ({ className, direction, initialValue, onDirectionToggle, onSubmit, ...props }, ref) => {
23
+ const translate = useTranslate();
24
+ const [inputRef, setInputRef] = useState<HTMLInputElement | null>(null);
25
+ const [input, setInput] = useState(initialValue.trim());
26
+
27
+ // On iOS it helps with losing focus too early which makes Actions disappear
28
+ const handleMouseDown: MouseEventHandler = (event) => event.preventDefault();
29
+
30
+ const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
31
+ event.preventDefault();
32
+ event.stopPropagation();
33
+ onSubmit(input);
34
+ };
35
+
36
+ useEffect(() => {
37
+ if (inputRef) {
38
+ inputRef.focus();
39
+ inputRef.select();
40
+ }
41
+ }, [inputRef]);
42
+
43
+ return (
44
+ <form className={classNames(styles.inputPrompt, className)} ref={ref} onSubmit={handleSubmit} {...props}>
45
+ <ToggleDirectionButton
46
+ className={styles.toggleDirection}
47
+ direction={direction}
48
+ onClick={onDirectionToggle}
49
+ onMouseDown={handleMouseDown}
50
+ />
51
+
52
+ <div>
53
+ <input
54
+ autoCapitalize="none"
55
+ autoComplete="off"
56
+ autoCorrect="off"
57
+ className={styles.input}
58
+ placeholder={translate('rack.placeholder')}
59
+ spellCheck={false}
60
+ ref={setInputRef}
61
+ value={input}
62
+ onChange={(event) => setInput(event.target.value)}
63
+ />
64
+ </div>
65
+
66
+ <Button
67
+ aria-label={translate('results.insert')}
68
+ className={styles.insert}
69
+ Icon={Check}
70
+ iconClassName={styles.insertIcon}
71
+ tooltip={translate('results.insert')}
72
+ type="submit"
73
+ variant="primary"
74
+ onMouseDown={handleMouseDown}
75
+ />
76
+ </form>
77
+ );
78
+ },
79
+ );
80
+
81
+ export default InputPrompt;
@@ -0,0 +1 @@
1
+ export { default } from './InputPrompt';
@@ -0,0 +1,21 @@
1
+ .button {
2
+ padding: var(--spacing--m);
3
+ box-shadow: none !important;
4
+
5
+ &:active,
6
+ &:hover {
7
+ color: var(--color--foreground);
8
+ }
9
+ }
10
+
11
+ .icon {
12
+ transition: var(--transition);
13
+
14
+ &.right {
15
+ transform: rotate(-90deg);
16
+
17
+ [dir='rtl'] & {
18
+ transform: rotate(90deg);
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,34 @@
1
+ import classNames from 'classnames';
2
+ import { ButtonHTMLAttributes, FunctionComponent } from 'react';
3
+
4
+ import { ArrowDown } from 'icons';
5
+ import { useTranslate } from 'state';
6
+ import { Direction } from 'types';
7
+
8
+ import Button from '../../../Button';
9
+
10
+ import styles from './ToggleDirectionButton.module.scss';
11
+
12
+ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
13
+ className?: string;
14
+ direction: Direction;
15
+ }
16
+
17
+ const ToggleDirectionButton: FunctionComponent<Props> = ({ className, direction, ...props }) => {
18
+ const translate = useTranslate();
19
+
20
+ return (
21
+ <Button
22
+ aria-label={translate('cell.toggle-direction')}
23
+ className={classNames(styles.button, className)}
24
+ Icon={ArrowDown}
25
+ iconClassName={classNames(styles.icon, {
26
+ [styles.right]: direction === 'horizontal',
27
+ })}
28
+ tooltip={translate('cell.toggle-direction')}
29
+ {...props}
30
+ />
31
+ );
32
+ };
33
+
34
+ export default ToggleDirectionButton;
@@ -0,0 +1 @@
1
+ export { default } from './ToggleDirectionButton';
@@ -1,2 +1,4 @@
1
1
  export { default as Actions } from './Actions';
2
2
  export { default as Cell } from './Cell';
3
+ export { default as InputPrompt } from './InputPrompt';
4
+ export { default as ToggleDirectionButton } from './ToggleDirectionButton';
@@ -1,2 +1,6 @@
1
1
  export { default as useBackgroundImage } from './useBackgroundImage';
2
+ export { default as useBoardStyle } from './useBoardStyle';
3
+ export { default as useFloatingActions } from './useFloatingActions';
4
+ export { default as useFloatingFocus } from './useFloatingFocus';
5
+ export { default as useFloatingInputPrompt } from './useFloatingInputPrompt';
2
6
  export { default as useGrid } from './useGrid';
@@ -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}
@@ -0,0 +1,27 @@
1
+ import { CSSProperties, useMemo } from 'react';
2
+
3
+ import { useAppLayout } from 'hooks';
4
+ import { getTileSizes } from 'lib';
5
+ import { selectConfig, useTypedSelector } from 'state';
6
+
7
+ import useBackgroundImage from './useBackgroundImage';
8
+
9
+ const useBoardStyle = () => {
10
+ const config = useTypedSelector(selectConfig);
11
+ const { cellSize } = useAppLayout();
12
+ const { tileFontSize } = getTileSizes(cellSize);
13
+ const backgroundImage = useBackgroundImage();
14
+ const boardStyle = useMemo<CSSProperties>(
15
+ () => ({
16
+ backgroundImage,
17
+ fontSize: tileFontSize,
18
+ gridTemplateColumns: `repeat(${config.boardWidth}, 1fr)`,
19
+ gridTemplateRows: `repeat(${config.boardHeight}, 1fr)`,
20
+ }),
21
+ [backgroundImage, config.boardHeight, config.boardWidth, tileFontSize],
22
+ );
23
+
24
+ return boardStyle;
25
+ };
26
+
27
+ export default useBoardStyle;
@@ -0,0 +1,22 @@
1
+ import { autoUpdate, offset, shift, useFloating } from '@floating-ui/react';
2
+
3
+ import { useAppLayout } from 'hooks';
4
+ import { BOARD_CELL_ACTIONS_OFFSET } from 'parameters';
5
+
6
+ const useFloatingActions = () => {
7
+ const { actionsWidth } = useAppLayout();
8
+
9
+ return useFloating({
10
+ middleware: [
11
+ offset({
12
+ mainAxis: -BOARD_CELL_ACTIONS_OFFSET,
13
+ alignmentAxis: BOARD_CELL_ACTIONS_OFFSET - actionsWidth,
14
+ }),
15
+ shift(),
16
+ ],
17
+ placement: 'top-end',
18
+ whileElementsMounted: autoUpdate,
19
+ });
20
+ };
21
+
22
+ export default useFloatingActions;
@@ -0,0 +1,10 @@
1
+ import { autoUpdate, useFloating } from '@floating-ui/react';
2
+
3
+ const useFloatingFocus = () => {
4
+ return useFloating({
5
+ placement: 'top-start',
6
+ whileElementsMounted: autoUpdate,
7
+ });
8
+ };
9
+
10
+ export default useFloatingFocus;
@@ -0,0 +1,19 @@
1
+ import { autoUpdate, offset, shift, useFloating } from '@floating-ui/react';
2
+
3
+ import { BOARD_CELL_ACTIONS_OFFSET } from 'parameters';
4
+
5
+ const useFloatingInputPrompt = () => {
6
+ return useFloating({
7
+ middleware: [
8
+ offset({
9
+ mainAxis: -BOARD_CELL_ACTIONS_OFFSET,
10
+ alignmentAxis: BOARD_CELL_ACTIONS_OFFSET,
11
+ }),
12
+ shift(),
13
+ ],
14
+ placement: 'top',
15
+ whileElementsMounted: autoUpdate,
16
+ });
17
+ };
18
+
19
+ export default useFloatingInputPrompt;
@@ -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
 
@@ -32,6 +32,7 @@ interface State {
32
32
  }
33
33
 
34
34
  interface Actions {
35
+ insertValue: (position: Point, value: string) => void;
35
36
  onChange: ChangeEventHandler<HTMLInputElement>;
36
37
  onDirectionToggle: () => void;
37
38
  onFocus: (x: number, y: number) => void;
@@ -290,6 +291,22 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
290
291
 
291
292
  const { x, y } = position;
292
293
  const character = event.key.toLowerCase();
294
+ const isTogglingBlank = isCtrl(event) && character === 'b';
295
+ const twoCharacterTile = config.getTwoCharacterTileByPrefix(character);
296
+
297
+ if (isTogglingBlank) {
298
+ event.preventDefault();
299
+ dispatch(boardSlice.actions.toggleCellIsBlank(position));
300
+ return;
301
+ }
302
+
303
+ if (isCtrl(event) && twoCharacterTile) {
304
+ event.preventDefault();
305
+ dispatch(boardSlice.actions.changeCellValue({ x, y, value: twoCharacterTile }));
306
+ moveFocus(1);
307
+ return;
308
+ }
309
+
293
310
  const cell = rows[y][x];
294
311
  const twoCharacterCandidate = cell.tile.character + character;
295
312
 
@@ -342,7 +359,7 @@ const useGrid = (rows: Cell[][]): [State, Actions] => {
342
359
 
343
360
  return [
344
361
  { activeIndex, direction, inputRefs },
345
- { onChange, onDirectionToggle, onFocus, onKeyDown, onPaste },
362
+ { insertValue, onChange, onDirectionToggle, onFocus, onKeyDown, onPaste },
346
363
  ];
347
364
  };
348
365
 
@@ -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
  }
@@ -2,7 +2,7 @@ import classNames from 'classnames';
2
2
  import { FunctionComponent, memo } from 'react';
3
3
 
4
4
  import { useAppLayout } from 'hooks';
5
- import { CardChecklist, Cog, Eraser, Github, Keyboard, List, Sack } from 'icons';
5
+ import { CardChecklist, Cog, Eraser, Github, KeyboardFill, List, Sack } from 'icons';
6
6
  import { GITHUB_PROJECT_URL } from 'parameters';
7
7
  import { selectHasInvalidWords, selectHasOverusedTiles, useTranslate, useTypedSelector } from 'state';
8
8
 
@@ -117,7 +117,7 @@ const NavButtons: FunctionComponent<Props> = ({
117
117
  <IconButton
118
118
  aria-label={translate('keyMap')}
119
119
  className={styles.button}
120
- Icon={Keyboard}
120
+ Icon={KeyboardFill}
121
121
  tooltip={translate('keyMap')}
122
122
  onClick={onShowKeyMap}
123
123
  />
@@ -60,20 +60,20 @@
60
60
  }
61
61
 
62
62
  .rack {
63
+ position: relative;
63
64
  display: flex;
64
65
  box-shadow: var(--box-shadow);
65
66
  border-radius: var(--border--radius);
67
+ transition: var(--transition);
68
+ opacity: 1;
66
69
  }
67
70
 
68
71
  .tile {
69
- @include focus-effect;
70
72
  @include lighthouse-input-size-hack;
73
+ }
71
74
 
72
- --background-color: var(--color--background);
73
-
74
- &:focus-within {
75
- z-index: 2;
76
- }
75
+ .hidden {
76
+ opacity: 0;
77
77
  }
78
78
 
79
79
  .sharpLeft {