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 (107) hide show
  1. package/lerna.json +1 -1
  2. package/package.json +1 -1
  3. package/packages/configs/package.json +3 -3
  4. package/packages/constants/package.json +1 -1
  5. package/packages/dictionaries/package-lock.json +2 -2
  6. package/packages/dictionaries/package.json +4 -4
  7. package/packages/logger/package-lock.json +2 -2
  8. package/packages/logger/package.json +1 -1
  9. package/packages/scrabble-solver/.next/BUILD_ID +1 -1
  10. package/packages/scrabble-solver/.next/build-manifest.json +7 -7
  11. package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
  12. package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
  13. package/packages/scrabble-solver/.next/cache/next-server.js.nft.json +1 -1
  14. package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
  15. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
  16. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/0.pack +0 -0
  17. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack +0 -0
  18. package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
  19. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
  20. package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
  21. package/packages/scrabble-solver/.next/prerender-manifest.json +1 -1
  22. package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
  23. package/packages/scrabble-solver/.next/server/chunks/277.js +341 -459
  24. package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
  25. package/packages/scrabble-solver/.next/server/pages/404.html +2 -2
  26. package/packages/scrabble-solver/.next/server/pages/404.js.nft.json +1 -1
  27. package/packages/scrabble-solver/.next/server/pages/500.html +1 -1
  28. package/packages/scrabble-solver/.next/server/pages/_app.js +0 -24
  29. package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
  30. package/packages/scrabble-solver/.next/server/pages/_document.js.nft.json +1 -1
  31. package/packages/scrabble-solver/.next/server/pages/api/solve.js +0 -1
  32. package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
  33. package/packages/scrabble-solver/.next/server/pages/index.js +4 -52
  34. package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
  35. package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
  36. package/packages/scrabble-solver/.next/static/chunks/pages/{404-e0f30450e9920dc3.js → 404-448ba28510855455.js} +1 -1
  37. package/packages/scrabble-solver/.next/static/chunks/pages/_app-270526803bc274eb.js +28 -0
  38. package/packages/scrabble-solver/.next/static/chunks/pages/index-c6e7754ccf3532df.js +1 -0
  39. package/packages/scrabble-solver/.next/static/css/ad39b36eab07e613.css +1 -0
  40. package/packages/scrabble-solver/.next/static/css/e5803e581e4c0451.css +2 -0
  41. package/packages/scrabble-solver/.next/static/esK8DG-6aS5V7QFRtR3YE/_buildManifest.js +1 -0
  42. package/packages/scrabble-solver/.next/trace +53 -55
  43. package/packages/scrabble-solver/package-lock.json +2 -79
  44. package/packages/scrabble-solver/package.json +8 -13
  45. package/packages/scrabble-solver/src/components/{Solver/components/EmptyState/EmptyState.module.scss → Alert/Alert.module.scss} +11 -7
  46. package/packages/scrabble-solver/src/components/{Solver/components/EmptyState/EmptyState.tsx → Alert/Alert.tsx} +8 -6
  47. package/packages/scrabble-solver/src/components/Alert/index.ts +1 -0
  48. package/packages/scrabble-solver/src/components/Board/Board.module.scss +55 -0
  49. package/packages/scrabble-solver/src/components/Board/BoardPure.tsx +4 -0
  50. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +41 -0
  51. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.tsx +12 -0
  52. package/packages/scrabble-solver/src/components/Board/components/Cell/CellPure.tsx +12 -0
  53. package/packages/scrabble-solver/src/components/Board/hooks/useGrid.ts +8 -24
  54. package/packages/scrabble-solver/src/components/Dictionary/Dictionary.module.scss +4 -0
  55. package/packages/scrabble-solver/src/components/DictionaryInput/DictionaryInput.module.scss +1 -0
  56. package/packages/scrabble-solver/src/components/EmptyState/EmptyState.module.scss +2 -1
  57. package/packages/scrabble-solver/src/components/EmptyState/EmptyState.tsx +1 -2
  58. package/packages/scrabble-solver/src/components/Loading/Loading.module.scss +1 -1
  59. package/packages/scrabble-solver/src/components/Logo/Logo.tsx +5 -0
  60. package/packages/scrabble-solver/src/components/Modal/Modal.module.scss +2 -0
  61. package/packages/scrabble-solver/src/components/PlainTiles/PlainTiles.module.scss +1 -1
  62. package/packages/scrabble-solver/src/components/PlainTiles/Tile.tsx +3 -3
  63. package/packages/scrabble-solver/src/components/Rack/Rack.module.scss +25 -0
  64. package/packages/scrabble-solver/src/components/Rack/Rack.tsx +5 -4
  65. package/packages/scrabble-solver/src/components/Rack/RackTile.tsx +6 -13
  66. package/packages/scrabble-solver/src/components/Results/Results.module.scss +28 -0
  67. package/packages/scrabble-solver/src/components/ResultsInput/ResultsInput.module.scss +1 -0
  68. package/packages/scrabble-solver/src/components/Solver/Solver.module.scss +5 -4
  69. package/packages/scrabble-solver/src/components/Solver/Solver.tsx +8 -7
  70. package/packages/scrabble-solver/src/components/Solver/components/index.ts +0 -1
  71. package/packages/scrabble-solver/src/components/Tile/Tile.module.scss +14 -2
  72. package/packages/scrabble-solver/src/components/Tooltip/Tooltip.module.scss +1 -72
  73. package/packages/scrabble-solver/src/components/Tooltip/useTooltip.tsx +25 -35
  74. package/packages/scrabble-solver/src/components/index.ts +1 -0
  75. package/packages/scrabble-solver/src/hooks/index.ts +0 -1
  76. package/packages/scrabble-solver/src/lib/index.ts +0 -1
  77. package/packages/scrabble-solver/src/modals/DictionaryModal/DictionaryModal.module.scss +6 -1
  78. package/packages/scrabble-solver/src/modals/KeyMapModal/KeyMapModal.tsx +5 -21
  79. package/packages/scrabble-solver/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +4 -0
  80. package/packages/scrabble-solver/src/modals/RemainingTilesModal/components/Character/Character.module.scss +1 -0
  81. package/packages/scrabble-solver/src/modals/ResultsModal/ResultsModal.module.scss +1 -0
  82. package/packages/scrabble-solver/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +2 -0
  83. package/packages/scrabble-solver/src/modals/WordsModal/WordsModal.module.scss +8 -1
  84. package/packages/scrabble-solver/src/state/createAppStore.ts +26 -10
  85. package/packages/scrabble-solver/src/state/types.ts +20 -2
  86. package/packages/scrabble-solver/src/styles/mixins.scss +22 -0
  87. package/packages/scrabble-solver/src/styles/variables.scss +16 -1
  88. package/packages/solver/package-lock.json +2 -2
  89. package/packages/solver/package.json +5 -5
  90. package/packages/types/build/Bonus.d.ts +2 -1
  91. package/packages/types/package.json +2 -2
  92. package/packages/types/src/Bonus.ts +3 -1
  93. package/packages/word-definitions/package-lock.json +2 -2
  94. package/packages/word-definitions/package.json +2 -2
  95. package/packages/word-lists/package-lock.json +2 -2
  96. package/packages/word-lists/package.json +2 -2
  97. package/packages/scrabble-solver/.next/static/45ye7793DY705HOcuK9lJ/_buildManifest.js +0 -1
  98. package/packages/scrabble-solver/.next/static/chunks/pages/_app-d7acee5e526752d9.js +0 -28
  99. package/packages/scrabble-solver/.next/static/chunks/pages/index-35d2c1c79a201ae2.js +0 -1
  100. package/packages/scrabble-solver/.next/static/css/a48caa6f57de6e98.css +0 -1
  101. package/packages/scrabble-solver/.next/static/css/c49bbe944ddd1b39.css +0 -2
  102. package/packages/scrabble-solver/src/components/Solver/components/EmptyState/index.ts +0 -1
  103. package/packages/scrabble-solver/src/components/Tooltip/constants.ts +0 -28
  104. package/packages/scrabble-solver/src/hooks/useUniqueId.ts +0 -9
  105. package/packages/scrabble-solver/src/lib/isCtrl.ts +0 -7
  106. package/packages/scrabble-solver/src/state/rootReducer.ts +0 -25
  107. /package/packages/scrabble-solver/.next/static/{45ye7793DY705HOcuK9lJ → esK8DG-6aS5V7QFRtR3YE}/_ssgManifest.js +0 -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}
@@ -9,6 +9,7 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
9
9
  height: 100%;
10
10
  background: var(--color--background--element);
11
11
  border: var(--border);
12
+ border-radius: var(--border--radius);
12
13
  box-shadow: var(--box-shadow);
13
14
  font-family: var(--font--family--title);
14
15
  }
@@ -29,6 +30,9 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
29
30
  }
30
31
 
31
32
  .list {
33
+ @include scrollbars;
34
+
35
+ scrollbar-gutter: stable;
32
36
  transition: var(--transition);
33
37
 
34
38
  &.outdated {
@@ -44,6 +48,8 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
44
48
  justify-content: space-between;
45
49
  font-weight: 700;
46
50
  border-bottom: var(--border);
51
+ border-top-left-radius: inherit;
52
+ border-top-right-radius: inherit;
47
53
  }
48
54
 
49
55
  .headerButton {
@@ -60,6 +66,26 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
60
66
  background-color: var(--color--primary);
61
67
  color: var(--color--primary--opposite);
62
68
  }
69
+
70
+ &:first-child {
71
+ [dir='ltr'] & {
72
+ border-top-left-radius: inherit;
73
+ }
74
+
75
+ [dir='rtl'] & {
76
+ border-top-right-radius: inherit;
77
+ }
78
+ }
79
+
80
+ &:last-child {
81
+ [dir='ltr'] & {
82
+ border-top-right-radius: inherit;
83
+ }
84
+
85
+ [dir='rtl'] & {
86
+ border-top-left-radius: inherit;
87
+ }
88
+ }
63
89
  }
64
90
 
65
91
  .headerButtonLabel {
@@ -175,4 +201,6 @@ $row-padding-horizontal: calc(var(--spacing--m) + var(--spacing--s));
175
201
 
176
202
  .input {
177
203
  border-top: var(--border);
204
+ border-bottom-left-radius: var(--border--radius);
205
+ border-bottom-right-radius: var(--border--radius);
178
206
  }
@@ -8,4 +8,5 @@
8
8
  @include text-input;
9
9
 
10
10
  border: none;
11
+ border-radius: inherit;
11
12
  }
@@ -70,16 +70,21 @@
70
70
  flex-direction: column;
71
71
  background-color: var(--color--background--element);
72
72
  border: var(--border);
73
+ border-radius: var(--border--radius);
73
74
  box-shadow: var(--box-shadow);
74
75
  }
75
76
 
76
77
  .dictionary {
77
78
  flex: 1;
78
79
  border-bottom: var(--border);
80
+ border-top-left-radius: var(--border--radius);
81
+ border-top-right-radius: var(--border--radius);
79
82
  }
80
83
 
81
84
  .dictionaryInput {
82
85
  flex: 0 0 auto;
86
+ border-bottom-left-radius: var(--border--radius);
87
+ border-bottom-right-radius: var(--border--radius);
83
88
  }
84
89
 
85
90
  .bottomContainer {
@@ -97,10 +102,6 @@
97
102
  min-width: 0;
98
103
  }
99
104
 
100
- .rack {
101
- border: var(--border);
102
- }
103
-
104
105
  .controls {
105
106
  width: 100%;
106
107
  }
@@ -19,13 +19,14 @@ import {
19
19
  useTypedSelector,
20
20
  } from 'state';
21
21
 
22
+ import Alert from '../Alert';
22
23
  import Board from '../Board';
23
24
  import Dictionary from '../Dictionary';
24
25
  import DictionaryInput from '../DictionaryInput';
25
26
  import Rack from '../Rack';
26
27
  import Results from '../Results';
27
28
 
28
- import { EmptyState, FloatingSolveButton, ResultCandidatePicker } from './components';
29
+ import { FloatingSolveButton, ResultCandidatePicker } from './components';
29
30
  import styles from './Solver.module.scss';
30
31
 
31
32
  interface Props {
@@ -111,7 +112,7 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
111
112
  <div className={classNames(styles.solver, className)}>
112
113
  <div className={styles.container}>
113
114
  <div className={styles.content}>
114
- <form id="a" className={styles.boardContainer} onSubmit={handleSubmit}>
115
+ <form className={styles.boardContainer} onSubmit={handleSubmit}>
115
116
  <Board cellSize={cellSizeSafe} className={styles.board} />
116
117
  <input className={styles.submitInput} tabIndex={-1} type="submit" />
117
118
  </form>
@@ -129,7 +130,7 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
129
130
 
130
131
  <div className={styles.bottomContainer} ref={bottomContainerRef}>
131
132
  <div className={styles.bottomContent}>
132
- <form id="b" onSubmit={handleSubmit}>
133
+ <form onSubmit={handleSubmit}>
133
134
  <Rack className={styles.rack} tileSize={tileSize} />
134
135
  <input className={styles.submitInput} tabIndex={-1} type="submit" />
135
136
  </form>
@@ -139,15 +140,15 @@ const Solver: FunctionComponent<Props> = ({ className, height, width, onShowResu
139
140
  <ResultCandidatePicker onResultClick={onShowResults} />
140
141
 
141
142
  {error && (
142
- <EmptyState className={styles.emptyState} variant="error">
143
+ <Alert className={styles.emptyState} variant="error">
143
144
  {error.message}
144
- </EmptyState>
145
+ </Alert>
145
146
  )}
146
147
 
147
148
  {allResults && allResults.length === 0 && !isOutdated && (
148
- <EmptyState className={styles.emptyState} variant="warning">
149
+ <Alert className={styles.emptyState} variant="warning">
149
150
  {translate('results.empty-state.no-results')}
150
- </EmptyState>
151
+ </Alert>
151
152
  )}
152
153
  </div>
153
154
  )}
@@ -1,4 +1,3 @@
1
- export { default as EmptyState } from './EmptyState';
2
1
  export { default as FloatingSolveButton } from './FloatingSolveButton';
3
2
  export { default as InsertButton } from './InsertButton';
4
3
  export { default as ResultCandidatePicker } from './ResultCandidatePicker';
@@ -5,6 +5,7 @@
5
5
 
6
6
  position: relative;
7
7
  transition: var(--transition);
8
+ border-radius: var(--border--radius);
8
9
 
9
10
  &.points1 {
10
11
  --background-color: var(--color--yellow);
@@ -85,6 +86,7 @@
85
86
  align-items: center;
86
87
  justify-content: center;
87
88
  background-color: var(--background-color);
89
+ border-radius: inherit;
88
90
  transition: var(--transition);
89
91
  pointer-events: none;
90
92
  user-select: none;
@@ -94,10 +96,18 @@
94
96
  }
95
97
 
96
98
  .raised & {
97
- box-shadow: inset -2px -2px 2px -1px rgba(34, 34, 34, 0.8);
99
+ --shadow--size: 2px;
100
+ --shadow--blur: 2px;
101
+ --shadow--spread: -1px;
102
+ --shadow--color: rgba(34, 34, 34, 0.8);
103
+
104
+ box-shadow: inset calc(-1 * var(--shadow--size)) calc(-1 * var(--shadow--size)) var(--shadow--blur)
105
+ var(--shadow--spread) var(--shadow--color);
98
106
 
99
107
  @include media('<xs') {
100
- box-shadow: inset -2px -2px 1px -1px rgba(34, 34, 34, 0.8);
108
+ --shadow--size: 1px;
109
+ --shadow--spread: 0;
110
+ --shadow--blur: 1px;
101
111
  }
102
112
  }
103
113
  }
@@ -146,10 +156,12 @@
146
156
  [dir='ltr'] & {
147
157
  top: 0;
148
158
  right: 0;
159
+ border-top-right-radius: inherit;
149
160
  }
150
161
 
151
162
  [dir='rtl'] & {
152
163
  bottom: 0;
153
164
  right: 0;
165
+ border-bottom-right-radius: inherit;
154
166
  }
155
167
  }
@@ -1,11 +1,3 @@
1
- $arrow-size: 4px;
2
-
3
- :export {
4
- ARROW_SIZE: $arrow-size;
5
- PREVENT_OVERFLOW: 10px;
6
- OFFSET: 2px;
7
- }
8
-
9
1
  .tooltip {
10
2
  padding: var(--spacing--s) var(--spacing--m);
11
3
  box-shadow: var(--box-shadow);
@@ -13,71 +5,8 @@ $arrow-size: 4px;
13
5
  background-color: var(--color--tooltip--background);
14
6
  color: var(--color--tooltip--foreground);
15
7
  z-index: var(--z-index--tooltip);
16
-
17
- &.top {
18
- .arrow {
19
- bottom: -$arrow-size;
20
-
21
- &::after {
22
- left: 0;
23
- bottom: 0;
24
- border-top-color: var(--color--tooltip--background);
25
- border-bottom: none;
26
- }
27
- }
28
- }
29
-
30
- &.right {
31
- .arrow {
32
- left: -$arrow-size;
33
-
34
- &::after {
35
- left: 0;
36
- top: 0;
37
- border-right-color: var(--color--tooltip--background);
38
- border-left: none;
39
- }
40
- }
41
- }
42
-
43
- &.bottom {
44
- .arrow {
45
- top: -$arrow-size;
46
-
47
- &::after {
48
- top: 0;
49
- left: 0;
50
- border-bottom-color: var(--color--tooltip--background);
51
- border-top: none;
52
- }
53
- }
54
- }
55
-
56
- &.left {
57
- .arrow {
58
- right: -$arrow-size;
59
-
60
- &::after {
61
- right: 0;
62
- top: 0;
63
- border-left-color: var(--color--tooltip--background);
64
- border-right: none;
65
- }
66
- }
67
- }
68
8
  }
69
9
 
70
10
  .arrow {
71
- position: absolute;
72
- width: 2 * $arrow-size;
73
- height: 2 * $arrow-size;
74
-
75
- &::after {
76
- content: ' ';
77
- position: absolute;
78
- height: 0;
79
- width: 0;
80
- pointer-events: none;
81
- border: $arrow-size solid transparent;
82
- }
11
+ fill: var(--color--tooltip--background);
83
12
  }
@@ -1,3 +1,4 @@
1
+ import { arrow, autoUpdate, flip, FloatingArrow, offset, shift, useFloating } from '@floating-ui/react';
1
2
  import classNames from 'classnames';
2
3
  import {
3
4
  FocusEvent,
@@ -6,16 +7,15 @@ import {
6
7
  MouseEventHandler,
7
8
  ReactNode,
8
9
  useCallback,
10
+ useId,
9
11
  useMemo,
12
+ useRef,
10
13
  useState,
11
14
  } from 'react';
12
- import { usePopper } from 'react-popper';
13
- import { useMountedState, useRafLoop } from 'react-use';
14
15
 
15
- import { useIsTouchDevice, usePortal, useUniqueId } from 'hooks';
16
+ import { useIsTouchDevice, usePortal } from 'hooks';
16
17
  import { noop } from 'lib';
17
18
 
18
- import { MODIFIERS } from './constants';
19
19
  import styles from './Tooltip.module.scss';
20
20
 
21
21
  interface Props {
@@ -36,29 +36,27 @@ interface TriggerProps {
36
36
  onMouseOver?: MouseEventHandler;
37
37
  }
38
38
 
39
+ const ARROW_SIZE = 7;
40
+ const ARROW_GAP = 3;
41
+ const PADDING = 10;
42
+
39
43
  // eslint-disable-next-line max-statements
40
44
  const useTooltip = (
41
45
  tooltip: ReactNode,
42
46
  { className, placement = 'top', onBlur = noop, onFocus = noop, onMouseOut = noop, onMouseOver = noop }: Props = {},
43
47
  ): TriggerProps => {
44
- const id = useUniqueId();
48
+ const id = useId();
45
49
  const isTouchDevice = useIsTouchDevice();
46
50
  const isEnabled = Boolean(tooltip) || tooltip === 0;
47
- const isMounted = useMountedState();
48
51
  const [isShown, setIsShown] = useState<boolean>(false);
49
- const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(null);
50
- const [popperElement, setPopperElement] = useState<HTMLElement | null>(null);
51
- const [arrowElement, setArrowElement] = useState<HTMLElement | null>(null);
52
- const {
53
- attributes,
54
- styles: popperStyles,
55
- update,
56
- } = usePopper(referenceElement, popperElement, {
57
- modifiers: [{ name: 'arrow', options: { element: arrowElement } }, ...MODIFIERS],
52
+ const arrowRef = useRef(null);
53
+ const ariaAttributes = useMemo(() => (isShown ? { 'aria-describedby': id } : {}), [id, isShown]);
54
+
55
+ const { x, y, context, refs, strategy } = useFloating({
56
+ middleware: [flip(), shift({ padding: PADDING }), offset(ARROW_SIZE + ARROW_GAP), arrow({ element: arrowRef })],
58
57
  placement,
58
+ whileElementsMounted: autoUpdate,
59
59
  });
60
- const computedPlacement = attributes.popper ? attributes.popper['data-popper-placement'] : placement;
61
- const ariaAttributes = useMemo(() => (isShown ? { 'aria-describedby': id } : {}), [id, isShown]);
62
60
 
63
61
  const handleBlur = useCallback(
64
62
  (event: FocusEvent) => {
@@ -95,7 +93,7 @@ const useTooltip = (
95
93
  const mouseTriggerProps = useMemo(
96
94
  () => ({
97
95
  ...ariaAttributes,
98
- ref: setReferenceElement,
96
+ ref: refs.setReference,
99
97
  onBlur: handleBlur,
100
98
  onFocus: handleFocus,
101
99
  onMouseOut: handleMouseOut,
@@ -107,33 +105,25 @@ const useTooltip = (
107
105
  const touchTriggerProps = useMemo(
108
106
  () => ({
109
107
  ...ariaAttributes,
110
- ref: setReferenceElement,
108
+ ref: refs.setReference,
111
109
  }),
112
110
  [ariaAttributes],
113
111
  );
114
112
 
115
113
  const triggerProps = isTouchDevice ? touchTriggerProps : mouseTriggerProps;
116
114
 
117
- useRafLoop(() => {
118
- if (isMounted() && update) {
119
- update();
120
- }
121
- });
122
-
123
115
  usePortal(
124
116
  <div
125
- className={classNames(styles.tooltip, className, {
126
- [styles.top]: computedPlacement === 'top',
127
- [styles.right]: computedPlacement === 'right',
128
- [styles.bottom]: computedPlacement === 'bottom',
129
- [styles.left]: computedPlacement === 'left',
130
- })}
131
- ref={setPopperElement}
132
- style={popperStyles.popper}
133
- {...attributes.popper}
117
+ className={classNames(styles.tooltip, className)}
118
+ ref={refs.setFloating}
119
+ style={{
120
+ position: strategy,
121
+ top: y ?? 0,
122
+ left: x ?? 0,
123
+ }}
134
124
  >
135
125
  <div>{tooltip}</div>
136
- <div className={styles.arrow} ref={setArrowElement} style={popperStyles.arrow} />
126
+ <FloatingArrow className={styles.arrow} context={context} ref={arrowRef} />
137
127
  </div>,
138
128
  { disabled: !isEnabled || !isShown },
139
129
  );
@@ -1,3 +1,4 @@
1
+ export { default as Alert } from './Alert';
1
2
  export { default as Badge } from './Badge';
2
3
  export { default as Board } from './Board';
3
4
  export { default as Button } from './Button';
@@ -5,4 +5,3 @@ export { default as useLanguage } from './useLanguage';
5
5
  export { default as useLocalStorage } from './useLocalStorage';
6
6
  export { default as useMediaQuery } from './useMediaQuery';
7
7
  export { default as usePortal } from './usePortal';
8
- export { default as useUniqueId } from './useUniqueId';
@@ -17,7 +17,6 @@ export { default as getRemainingTilesGroups } from './getRemainingTilesGroups';
17
17
  export { default as getTotalRemainingTilesCount } from './getTotalRemainingTilesCount';
18
18
  export { default as getTileSizes } from './getTileSizes';
19
19
  export { default as inverseDirection } from './inverseDirection';
20
- export { default as isCtrl } from './isCtrl';
21
20
  export { default as isMac } from './isMac';
22
21
  export { default as isRegExp } from './isRegExp';
23
22
  export { default as isStringArray } from './isStringArray';
@@ -4,15 +4,20 @@
4
4
  flex-direction: column;
5
5
  background-color: var(--color--background--element);
6
6
  border: var(--border);
7
+ border-radius: var(--border--radius);
7
8
  box-shadow: var(--box-shadow);
8
9
  }
9
10
 
10
11
  .dictionary {
11
12
  flex: 1;
12
- border-bottom: var(--border);
13
13
  max-height: initial;
14
+ border-bottom: var(--border);
15
+ border-top-left-radius: var(--border--radius);
16
+ border-top-right-radius: var(--border--radius);
14
17
  }
15
18
 
16
19
  .dictionaryInput {
17
20
  flex: 0 0 auto;
21
+ border-bottom-left-radius: var(--border--radius);
22
+ border-bottom-right-radius: var(--border--radius);
18
23
  }
@@ -1,10 +1,10 @@
1
1
  import { FunctionComponent } from 'react';
2
2
 
3
- import { Key, Modal } from 'components';
4
- import { selectConfig, useTranslate, useTypedSelector } from 'state';
3
+ import { Modal } from 'components';
4
+ import { useTranslate } from 'state';
5
5
 
6
6
  import { Mapping } from './components';
7
- import { ARROWS, BACKSPACE, CTRL, DEL, ENTER, SPACE } from './keys';
7
+ import { ARROWS, BACKSPACE, DEL, ENTER, SPACE } from './keys';
8
8
 
9
9
  interface Props {
10
10
  className?: string;
@@ -14,7 +14,6 @@ interface Props {
14
14
 
15
15
  const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) => {
16
16
  const translate = useTranslate();
17
- const config = useTypedSelector(selectConfig);
18
17
 
19
18
  return (
20
19
  <Modal className={className} isOpen={isOpen} title={translate('keyMap')} onClose={onClose}>
@@ -22,26 +21,11 @@ const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) =
22
21
  <Mapping description={translate('keyMap.board-and-rack.navigate')} mapping={[ARROWS]} />
23
22
  <Mapping description={translate('keyMap.board-and-rack.remove-tile')} mapping={[DEL, BACKSPACE]} />
24
23
  <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
- )}
40
24
  </Modal.Section>
41
25
 
42
26
  <Modal.Section title={translate('keyMap.board')}>
43
- <Mapping description={translate('keyMap.board.toggle-blank')} mapping={[SPACE, [CTRL, <Key key="b">B</Key>]]} />
44
- <Mapping description={translate('keyMap.board.toggle-direction')} mapping={[[CTRL, ARROWS]]} />
27
+ <Mapping description={translate('keyMap.board.toggle-blank')} mapping={[SPACE]} />
28
+ <Mapping description={translate('keyMap.board.toggle-direction')} mapping={[ARROWS]} />
45
29
  </Modal.Section>
46
30
 
47
31
  <Modal.Section title={translate('keyMap.rack')}>
@@ -29,6 +29,10 @@
29
29
  }
30
30
 
31
31
  .group {
32
+ [dir='rtl'] & {
33
+ flex-direction: row-reverse;
34
+ }
35
+
32
36
  &:first-child {
33
37
  margin-left: 0;
34
38
  }