@scrabble-solver/scrabble-solver 2.11.4 → 2.11.6

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 (148) 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/export-marker.json +1 -1
  13. package/.next/next-server.js.nft.json +1 -1
  14. package/.next/prerender-manifest.json +1 -1
  15. package/.next/routes-manifest.json +1 -1
  16. package/.next/server/chunks/131.js +1 -1
  17. package/.next/server/chunks/277.js +851 -1179
  18. package/.next/server/chunks/636.js +286 -0
  19. package/.next/server/chunks/675.js +550 -0
  20. package/.next/server/middleware-build-manifest.js +1 -1
  21. package/.next/server/pages/404.html +1 -5
  22. package/.next/server/pages/404.js.nft.json +1 -1
  23. package/.next/server/pages/500.html +1 -1
  24. package/.next/server/pages/_app.js +73 -9
  25. package/.next/server/pages/_app.js.nft.json +1 -1
  26. package/.next/server/pages/_document.js.nft.json +1 -1
  27. package/.next/server/pages/_error.js +1 -280
  28. package/.next/server/pages/_error.js.nft.json +1 -1
  29. package/.next/server/pages/api/solve.js +22 -2
  30. package/.next/server/pages/index.html +1 -1
  31. package/.next/server/pages/index.js +381 -314
  32. package/.next/server/pages/index.js.nft.json +1 -1
  33. package/.next/server/pages/index.json +1 -1
  34. package/.next/server/pages-manifest.json +1 -1
  35. package/.next/static/Jmk00rVXCbdjFgP77tKXQ/_buildManifest.js +1 -0
  36. package/.next/static/chunks/pages/{404-448ba28510855455.js → 404-8176f4acd0cfeb42.js} +1 -1
  37. package/.next/static/chunks/pages/_app-b4fa92112b8f0385.js +28 -0
  38. package/.next/static/chunks/pages/index-ccd762f8f5028729.js +1 -0
  39. package/.next/static/css/1cd302e7648d209c.css +2 -0
  40. package/.next/static/css/34adfcf12a7d9bb6.css +1 -0
  41. package/.next/trace +50 -53
  42. package/next.config.js +1 -0
  43. package/package.json +12 -13
  44. package/src/@types/svg.d.ts +1 -1
  45. package/src/components/Board/Board.tsx +48 -44
  46. package/src/components/Board/components/Actions/Actions.tsx +4 -2
  47. package/src/components/Board/components/Cell/Cell.module.scss +59 -5
  48. package/src/components/Board/hooks/useGrid.ts +5 -3
  49. package/src/components/Button/Button.module.scss +1 -1
  50. package/src/components/Dictionary/Dictionary.module.scss +0 -1
  51. package/src/components/EmptyState/EmptyState.module.scss +0 -1
  52. package/src/components/Key/Key.module.scss +1 -1
  53. package/src/components/Loading/Loading.module.scss +1 -1
  54. package/src/components/Loading/Loading.tsx +1 -1
  55. package/src/components/Logo/Logo.tsx +10 -12
  56. package/src/components/Logo/LogoBlueprint.tsx +21 -0
  57. package/src/components/Logo/index.ts +1 -1
  58. package/src/components/Modal/Modal.module.scss +1 -6
  59. package/src/components/Modal/Modal.tsx +15 -8
  60. package/src/components/NavButtons/NavButtons.tsx +2 -2
  61. package/src/components/PlainTiles/PlainTiles.tsx +0 -10
  62. package/src/components/PlainTiles/Tile.tsx +1 -4
  63. package/src/components/Rack/Rack.module.scss +59 -0
  64. package/src/components/Results/HeaderButton.tsx +6 -6
  65. package/src/components/Results/Results.module.scss +3 -1
  66. package/src/components/Results/Results.tsx +7 -7
  67. package/src/components/Results/useColumns.ts +2 -5
  68. package/src/components/Solver/Solver.tsx +6 -23
  69. package/src/components/Tile/Tile.module.scss +2 -1
  70. package/src/components/Tile/Tile.tsx +8 -4
  71. package/src/components/index.ts +0 -5
  72. package/src/hooks/index.ts +6 -0
  73. package/src/hooks/useAppLayout.ts +62 -12
  74. package/src/hooks/useDirection.ts +2 -2
  75. package/src/hooks/useEffectOnce.ts +5 -0
  76. package/src/hooks/useIsTouchDevice.ts +1 -1
  77. package/src/hooks/useLanguage.ts +2 -2
  78. package/src/hooks/useLatest.ts +13 -0
  79. package/src/hooks/useLocalStorage.ts +51 -0
  80. package/src/hooks/useMedia.ts +36 -0
  81. package/src/hooks/useMediaQueries.ts +13 -0
  82. package/src/hooks/useMediaQuery.ts +2 -1
  83. package/src/hooks/useOnWindowResize.ts +13 -0
  84. package/src/hooks/useViewportSize.ts +19 -0
  85. package/src/i18n/constants.ts +14 -22
  86. package/src/lib/arrayEquals.ts +5 -0
  87. package/src/lib/index.ts +1 -0
  88. package/src/lib/zipCharactersAndTiles.ts +3 -1
  89. package/src/modals/DictionaryModal/DictionaryModal.tsx +2 -2
  90. package/src/modals/KeyMapModal/KeyMapModal.tsx +2 -2
  91. package/src/modals/KeyMapModal/components/Mapping/Mapping.module.scss +0 -1
  92. package/src/modals/KeyMapModal/keys.tsx +0 -2
  93. package/src/modals/MenuModal/MenuModal.module.scss +28 -4
  94. package/src/modals/MenuModal/MenuModal.tsx +4 -4
  95. package/src/modals/RemainingTilesModal/RemainingTilesModal.tsx +2 -2
  96. package/src/modals/ResultsModal/ResultsModal.module.scss +1 -5
  97. package/src/modals/ResultsModal/ResultsModal.tsx +10 -2
  98. package/src/modals/SettingsModal/SettingsModal.tsx +2 -2
  99. package/src/modals/SettingsModal/components/AutoGroupTilesSetting/lib.ts +3 -1
  100. package/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.module.scss +0 -1
  101. package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +1 -6
  102. package/src/modals/WordsModal/WordsModal.tsx +2 -2
  103. package/src/pages/index.module.scss +3 -21
  104. package/src/pages/index.tsx +51 -69
  105. package/src/parameters/index.ts +29 -2
  106. package/src/state/localStorage.ts +13 -2
  107. package/src/state/sagas.ts +16 -8
  108. package/src/state/slices/boardInitialState.ts +5 -1
  109. package/src/state/slices/boardSlice.ts +2 -2
  110. package/src/state/slices/rackInitialState.ts +8 -2
  111. package/src/state/slices/rackSlice.ts +16 -13
  112. package/src/state/slices/settingsInitialState.ts +9 -4
  113. package/src/state/slices/settingsSlice.ts +3 -1
  114. package/src/styles/animations.scss +0 -20
  115. package/src/styles/global.scss +4 -15
  116. package/src/styles/mixins.scss +0 -60
  117. package/src/styles/variables.scss +14 -5
  118. package/src/types/index.ts +4 -0
  119. package/.next/static/MvHZRF4XuJ7g8LLLRkf8U/_buildManifest.js +0 -1
  120. package/.next/static/chunks/pages/_app-66d80a5594aab8dc.js +0 -28
  121. package/.next/static/chunks/pages/index-0858deea02b2a417.js +0 -1
  122. package/.next/static/css/885da289cec275b3.css +0 -1
  123. package/.next/static/css/ea1c8134fe9a143e.css +0 -2
  124. package/src/components/LogoSplashScreen/LogoSplashScreen.module.scss +0 -65
  125. package/src/components/LogoSplashScreen/LogoSplashScreen.tsx +0 -31
  126. package/src/components/LogoSplashScreen/index.ts +0 -1
  127. package/src/components/Sizer/Sizer.module.scss +0 -10
  128. package/src/components/Sizer/Sizer.tsx +0 -10
  129. package/src/components/Sizer/index.ts +0 -1
  130. package/src/components/SplashScreen/SplashScreen.module.scss +0 -14
  131. package/src/components/SplashScreen/SplashScreen.tsx +0 -19
  132. package/src/components/SplashScreen/index.ts +0 -1
  133. package/src/components/SvgFontCss/SvgFontCss.tsx +0 -14
  134. package/src/components/SvgFontCss/createCss.ts +0 -11
  135. package/src/components/SvgFontCss/createStyle.ts +0 -9
  136. package/src/components/SvgFontCss/createSvg.ts +0 -10
  137. package/src/components/SvgFontCss/index.ts +0 -1
  138. package/src/components/SvgFontFix/SvgFontFix.module.scss +0 -5
  139. package/src/components/SvgFontFix/SvgFontFix.tsx +0 -21
  140. package/src/components/SvgFontFix/index.ts +0 -1
  141. package/src/hooks/useLocalStorage/index.ts +0 -1
  142. package/src/hooks/useLocalStorage/useLocalStorage.ts +0 -13
  143. package/src/hooks/useLocalStorage/useLocalStorageBoard.ts +0 -29
  144. package/src/hooks/useLocalStorage/useLocalStorageConfigId.ts +0 -29
  145. package/src/hooks/useLocalStorage/useLocalStorageLocale.ts +0 -32
  146. package/src/hooks/useLocalStorage/useLocalStorageRack.ts +0 -29
  147. /package/.next/static/{MvHZRF4XuJ7g8LLLRkf8U → Jmk00rVXCbdjFgP77tKXQ}/_ssgManifest.js +0 -0
  148. /package/{src/components/Logo/Logo.svg → public/logo.svg} +0 -0
@@ -7,7 +7,6 @@ import styles from './i18n.module.scss';
7
7
 
8
8
  interface LocaleFeatures {
9
9
  direction: 'ltr' | 'rtl';
10
- fontFamily: string;
11
10
  consonants: boolean;
12
11
  vowels: boolean;
13
12
  }
@@ -15,43 +14,36 @@ interface LocaleFeatures {
15
14
  export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
16
15
  [Locale.DE_DE]: {
17
16
  direction: 'ltr',
18
- fontFamily: 'Open Sans',
19
17
  consonants: true,
20
18
  vowels: true,
21
19
  },
22
20
  [Locale.EN_GB]: {
23
21
  direction: 'ltr',
24
- fontFamily: 'Open Sans',
25
22
  consonants: true,
26
23
  vowels: true,
27
24
  },
28
25
  [Locale.EN_US]: {
29
26
  direction: 'ltr',
30
- fontFamily: 'Open Sans',
31
27
  consonants: true,
32
28
  vowels: true,
33
29
  },
34
30
  [Locale.ES_ES]: {
35
31
  direction: 'ltr',
36
- fontFamily: 'Open Sans',
37
32
  consonants: true,
38
33
  vowels: true,
39
34
  },
40
35
  [Locale.FA_IR]: {
41
36
  direction: 'rtl',
42
- fontFamily: 'Vazirmatn',
43
37
  consonants: false,
44
38
  vowels: false,
45
39
  },
46
40
  [Locale.FR_FR]: {
47
41
  direction: 'ltr',
48
- fontFamily: 'Open Sans',
49
42
  consonants: true,
50
43
  vowels: true,
51
44
  },
52
45
  [Locale.PL_PL]: {
53
46
  direction: 'ltr',
54
- fontFamily: 'Open Sans',
55
47
  consonants: true,
56
48
  vowels: true,
57
49
  },
@@ -66,6 +58,13 @@ interface Flag {
66
58
  }
67
59
 
68
60
  export const LOCALE_FLAGS: Record<Locale, Flag> = {
61
+ [Locale.DE_DE]: {
62
+ className: styles.de,
63
+ Icon: FlagDe,
64
+ label: 'Deutsch',
65
+ name: 'German',
66
+ value: Locale.DE_DE,
67
+ },
69
68
  [Locale.EN_GB]: {
70
69
  className: styles.gb,
71
70
  Icon: FlagGb,
@@ -80,6 +79,13 @@ export const LOCALE_FLAGS: Record<Locale, Flag> = {
80
79
  name: 'English (US)',
81
80
  value: Locale.EN_US,
82
81
  },
82
+ [Locale.ES_ES]: {
83
+ className: styles.es,
84
+ Icon: FlagEs,
85
+ label: 'Español',
86
+ name: 'Spanish',
87
+ value: Locale.ES_ES,
88
+ },
83
89
  [Locale.FA_IR]: {
84
90
  className: styles.fa,
85
91
  Icon: FlagFa,
@@ -94,13 +100,6 @@ export const LOCALE_FLAGS: Record<Locale, Flag> = {
94
100
  name: 'French',
95
101
  value: Locale.FR_FR,
96
102
  },
97
- [Locale.DE_DE]: {
98
- className: styles.de,
99
- Icon: FlagDe,
100
- label: 'Deutsch',
101
- name: 'German',
102
- value: Locale.DE_DE,
103
- },
104
103
  [Locale.PL_PL]: {
105
104
  className: styles.pl,
106
105
  Icon: FlagPl,
@@ -108,11 +107,4 @@ export const LOCALE_FLAGS: Record<Locale, Flag> = {
108
107
  name: 'Polish',
109
108
  value: Locale.PL_PL,
110
109
  },
111
- [Locale.ES_ES]: {
112
- className: styles.es,
113
- Icon: FlagEs,
114
- label: 'Español',
115
- name: 'Spanish',
116
- value: Locale.ES_ES,
117
- },
118
110
  };
@@ -0,0 +1,5 @@
1
+ const arrayEquals = <T>(array1: T[], array2: T[]): boolean => {
2
+ return array1.length === array2.length && array1.every((value, index) => value === array2[index]);
3
+ };
4
+
5
+ export default arrayEquals;
package/src/lib/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { default as arrayEquals } from './arrayEquals';
1
2
  export { default as canUseDom } from './canUseDom';
2
3
  export { default as createArray } from './createArray';
3
4
  export { default as createComparator } from './createComparator';
@@ -1,12 +1,14 @@
1
1
  import { BLANK } from '@scrabble-solver/constants';
2
2
  import { Tile } from '@scrabble-solver/types';
3
3
 
4
+ import { Rack } from 'types';
5
+
4
6
  interface CharacterTilePair {
5
7
  character: string | null;
6
8
  tile: Tile | null;
7
9
  }
8
10
 
9
- const zipCharactersAndTiles = (characters: (string | null)[], tiles: Tile[]): CharacterTilePair[] => {
11
+ const zipCharactersAndTiles = (characters: Rack, tiles: Tile[]): CharacterTilePair[] => {
10
12
  let remainingTiles = [...tiles];
11
13
 
12
14
  return characters.map((character) => {
@@ -1,4 +1,4 @@
1
- import { FunctionComponent } from 'react';
1
+ import { FunctionComponent, memo } from 'react';
2
2
 
3
3
  import { Dictionary, DictionaryInput, Modal } from 'components';
4
4
  import { useTranslate } from 'state';
@@ -24,4 +24,4 @@ const DictionaryModal: FunctionComponent<Props> = ({ className, isOpen, onClose
24
24
  );
25
25
  };
26
26
 
27
- export default DictionaryModal;
27
+ export default memo(DictionaryModal);
@@ -1,4 +1,4 @@
1
- import { FunctionComponent } from 'react';
1
+ import { FunctionComponent, memo } from 'react';
2
2
 
3
3
  import { Modal } from 'components';
4
4
  import { useTranslate } from 'state';
@@ -35,4 +35,4 @@ const KeyMapModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) =
35
35
  );
36
36
  };
37
37
 
38
- export default KeyMapModal;
38
+ export default memo(KeyMapModal);
@@ -9,7 +9,6 @@
9
9
  .description {
10
10
  margin-bottom: var(--spacing--m);
11
11
  font-size: var(--font--size--h3);
12
- font-family: var(--font--family--title);
13
12
  font-weight: 300;
14
13
  }
15
14
 
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { Key } from 'components';
4
2
  import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp } from 'icons';
5
3
  import { isMac } from 'lib';
@@ -23,11 +23,35 @@
23
23
  justify-content: space-between;
24
24
  }
25
25
 
26
- .flag {
27
- --height: var(--button--icon--size);
26
+ .settingsLabel {
27
+ @include ellipsis;
28
+
29
+ --quatar-flag-aspect-ratio: 2.545;
30
+ --highest-flag-aspect-ratio: var(--quatar-flag-aspect-ratio);
31
+ --aspect-ratio: var(--highest-flag-aspect-ratio);
32
+ --padding: calc(var(--spacing--l) + var(--button--icon--size) * var(--aspect-ratio));
33
+
34
+ [dir='ltr'] & {
35
+ padding-right: var(--padding);
36
+ }
37
+
38
+ [dir='rtl'] & {
39
+ padding-left: var(--padding);
40
+ }
41
+ }
28
42
 
29
- width: calc(var(--height) * var(--aspect--ratio));
30
- height: var(--height);
43
+ .flag {
44
+ position: fixed;
45
+ width: calc(var(--button--icon--size) * var(--aspect-ratio));
46
+ height: var(--button--icon--size);
31
47
  border-radius: var(--border--radius);
32
48
  box-shadow: 0 0 0 1px var(--box-shadow--color);
49
+
50
+ [dir='ltr'] & {
51
+ right: calc(2 * var(--spacing--l));
52
+ }
53
+
54
+ [dir='rtl'] & {
55
+ left: calc(2 * var(--spacing--l));
56
+ }
33
57
  }
@@ -1,4 +1,4 @@
1
- import { FunctionComponent } from 'react';
1
+ import { FunctionComponent, memo } from 'react';
2
2
 
3
3
  import { Button, Modal } from 'components';
4
4
  import { LOCALE_FLAGS } from 'i18n';
@@ -18,7 +18,7 @@ interface Props {
18
18
  onShowWords: () => void;
19
19
  }
20
20
 
21
- const Menu: FunctionComponent<Props> = ({
21
+ const MenuModal: FunctionComponent<Props> = ({
22
22
  className,
23
23
  isOpen,
24
24
  onClose,
@@ -63,7 +63,7 @@ const Menu: FunctionComponent<Props> = ({
63
63
 
64
64
  <Button aria-label={translate('settings')} className={styles.button} Icon={Cog} onClick={onShowSettings}>
65
65
  <div className={styles.settings}>
66
- <span>{translate('settings')}</span>
66
+ <div className={styles.settingsLabel}>{translate('settings')}</div>
67
67
  <Flag.Icon className={styles.flag} />
68
68
  </div>
69
69
  </Button>
@@ -71,4 +71,4 @@ const Menu: FunctionComponent<Props> = ({
71
71
  );
72
72
  };
73
73
 
74
- export default Menu;
74
+ export default memo(MenuModal);
@@ -1,4 +1,4 @@
1
- import { FunctionComponent } from 'react';
1
+ import { FunctionComponent, memo } from 'react';
2
2
 
3
3
  import { Badge, Modal } from 'components';
4
4
  import { LOCALE_FEATURES } from 'i18n';
@@ -56,4 +56,4 @@ const RemainingTilesModal: FunctionComponent<Props> = ({ className, isOpen, onCl
56
56
  );
57
57
  };
58
58
 
59
- export default RemainingTilesModal;
59
+ export default memo(RemainingTilesModal);
@@ -13,12 +13,8 @@
13
13
  }
14
14
 
15
15
  .dictionary {
16
- flex: 0 1 calc(var(--dictionary--height) - var(--text-input--height));
16
+ flex: 0 0 var(--dictionary--height--mobile);
17
17
  background-color: var(--color--background--element);
18
18
  border: var(--border);
19
19
  border-radius: var(--border--radius);
20
-
21
- @media (max-height: 600px) {
22
- display: none;
23
- }
24
20
  }
@@ -1,8 +1,9 @@
1
1
  import { Result } from '@scrabble-solver/types';
2
- import { FunctionComponent, useMemo } from 'react';
2
+ import { FunctionComponent, memo, useEffect, useMemo } from 'react';
3
3
  import { useDispatch } from 'react-redux';
4
4
 
5
5
  import { Button, Dictionary, Modal, Results } from 'components';
6
+ import { useAppLayout } from 'hooks';
6
7
  import { Check, EyeFill } from 'icons';
7
8
  import { resultsSlice, selectResultCandidate, selectResults, useTranslate, useTypedSelector } from 'state';
8
9
 
@@ -17,6 +18,7 @@ interface Props {
17
18
  const ResultsModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) => {
18
19
  const dispatch = useDispatch();
19
20
  const translate = useTranslate();
21
+ const { showResultsInModal } = useAppLayout();
20
22
  const results = useTypedSelector(selectResults);
21
23
  const resultCandidate = useTypedSelector(selectResultCandidate);
22
24
  const index = results ? results.findIndex((result) => result.id === resultCandidate?.id) : -1;
@@ -49,6 +51,12 @@ const ResultsModal: FunctionComponent<Props> = ({ className, isOpen, onClose })
49
51
  onClose();
50
52
  };
51
53
 
54
+ useEffect(() => {
55
+ if (isOpen && !showResultsInModal) {
56
+ onClose();
57
+ }
58
+ }, [isOpen, onClose, showResultsInModal]);
59
+
52
60
  return (
53
61
  <Modal
54
62
  className={className}
@@ -88,4 +96,4 @@ const ResultsModal: FunctionComponent<Props> = ({ className, isOpen, onClose })
88
96
  );
89
97
  };
90
98
 
91
- export default ResultsModal;
99
+ export default memo(ResultsModal);
@@ -1,4 +1,4 @@
1
- import { FunctionComponent } from 'react';
1
+ import { FunctionComponent, memo } from 'react';
2
2
 
3
3
  import { Modal } from 'components';
4
4
  import { useTranslate } from 'state';
@@ -31,4 +31,4 @@ const SettingsModal: FunctionComponent<Props> = ({ className, isOpen, onClose })
31
31
  );
32
32
  };
33
33
 
34
- export default SettingsModal;
34
+ export default memo(SettingsModal);
@@ -1,6 +1,8 @@
1
+ import { AutoGroupTiles } from 'types';
2
+
1
3
  import { NULL_VALUE } from './constants';
2
4
 
3
- export const parseValue = (value: string): 'left' | 'right' | null => {
5
+ export const parseValue = (value: string): AutoGroupTiles => {
4
6
  if (value === 'left' || value === 'right') {
5
7
  return value;
6
8
  }
@@ -9,5 +9,4 @@
9
9
  .label {
10
10
  display: flex;
11
11
  align-items: center;
12
- font-family: var(--font--family--latin);
13
12
  }
@@ -10,17 +10,12 @@
10
10
  display: flex;
11
11
  align-items: center;
12
12
  gap: var(--spacing--l);
13
- font-family: var(--font--family--latin);
14
-
15
- &.fa {
16
- font-family: var(--font--family--arabic);
17
- }
18
13
  }
19
14
 
20
15
  .flag {
21
16
  --height: 32px;
22
17
 
23
- width: calc(var(--height) * var(--aspect--ratio));
18
+ width: calc(var(--height) * var(--aspect-ratio));
24
19
  height: var(--height);
25
20
  border-radius: var(--border--radius);
26
21
  box-shadow: var(--box-shadow);
@@ -1,5 +1,5 @@
1
1
  import classNames from 'classnames';
2
- import { FunctionComponent } from 'react';
2
+ import { FunctionComponent, memo } from 'react';
3
3
 
4
4
  import { Badge, Modal } from 'components';
5
5
  import { Check, Cross } from 'icons';
@@ -53,4 +53,4 @@ const WordsModal: FunctionComponent<Props> = ({ className, isOpen, onClose }) =>
53
53
  );
54
54
  };
55
55
 
56
- export default WordsModal;
56
+ export default memo(WordsModal);
@@ -1,18 +1,3 @@
1
- @import 'styles/mixins';
2
-
3
- .index {
4
- display: flex;
5
- flex-direction: column;
6
- height: 100%;
7
- opacity: 0;
8
- overflow: auto;
9
- transition: var(--transition--long);
10
-
11
- &.initialized {
12
- opacity: 1;
13
- }
14
- }
15
-
16
1
  .nav {
17
2
  position: relative;
18
3
  z-index: 1;
@@ -23,7 +8,7 @@
23
8
  width: 100%;
24
9
  display: flex;
25
10
  align-items: flex-start;
26
- padding: var(--spacing--l);
11
+ padding: var(--nav--padding);
27
12
  }
28
13
 
29
14
  .navLogo {
@@ -36,12 +21,9 @@
36
21
  }
37
22
 
38
23
  .logo {
39
- height: 60px;
24
+ width: calc(var(--logo--aspect-ratio) * var(--logo--height));
25
+ height: var(--logo--height);
40
26
  user-select: none;
41
-
42
- @include media('<l') {
43
- height: 48px;
44
- }
45
27
  }
46
28
 
47
29
  .solver {
@@ -1,13 +1,11 @@
1
- import classNames from 'classnames';
2
1
  import fs from 'fs';
3
2
  import path from 'path';
4
- import { FunctionComponent, useEffect, useState } from 'react';
3
+ import { FunctionComponent, useCallback, useState } from 'react';
5
4
  import ReactModal from 'react-modal';
6
5
  import { useDispatch } from 'react-redux';
7
- import { useEffectOnce, useMeasure } from 'react-use';
8
6
 
9
- import { Logo, LogoSplashScreen, NavButtons, Solver, SvgFontFix } from 'components';
10
- import { useAppLayout, useDirection, useLanguage, useLocalStorage } from 'hooks';
7
+ import { Logo, NavButtons, Solver } from 'components';
8
+ import { useDirection, useEffectOnce, useLanguage, useLocalStorage } from 'hooks';
11
9
  import { LOCALE_FEATURES } from 'i18n';
12
10
  import {
13
11
  DictionaryModal,
@@ -33,7 +31,6 @@ interface Props {
33
31
  const Index: FunctionComponent<Props> = ({ version }) => {
34
32
  const dispatch = useDispatch();
35
33
  const locale = useTypedSelector(selectLocale);
36
- const { showResultsInModal } = useAppLayout();
37
34
  const [showDictionary, setShowDictionary] = useState(false);
38
35
  const [showKeyMap, setShowKeyMap] = useState(false);
39
36
  const [showMenu, setShowMenu] = useState(false);
@@ -41,38 +38,36 @@ const Index: FunctionComponent<Props> = ({ version }) => {
41
38
  const [showResults, setShowResults] = useState(false);
42
39
  const [showSettings, setShowSettings] = useState(false);
43
40
  const [showWords, setShowWords] = useState(false);
44
- const [isInitialized, setIsInitialized] = useState(false);
45
- const [indexRef, { height: indexHeight, width: indexWidth }] = useMeasure<HTMLDivElement>();
46
- const [navRef, { height: navHeight }] = useMeasure<HTMLElement>();
47
- const solverHeight = indexHeight - navHeight;
48
- const solverWidth = indexWidth;
49
41
  const [isClient, setIsClient] = useState(false);
50
42
 
51
- const handleClear = () => {
52
- dispatch(reset());
53
- };
43
+ const handleShowResults = useCallback(() => setShowResults(true), []);
44
+ const handleClear = useCallback(() => dispatch(reset()), [dispatch]);
45
+ const handleHideDictionary = useCallback(() => setShowDictionary(false), []);
46
+ const handleHideKeyMap = useCallback(() => setShowKeyMap(false), []);
47
+ const handleHideMenu = useCallback(() => setShowMenu(false), []);
48
+ const handleHideRemainingTiles = useCallback(() => setShowRemainingTiles(false), []);
49
+ const handleHideResults = useCallback(() => setShowResults(false), []);
50
+ const handleHideSettings = useCallback(() => setShowSettings(false), []);
51
+ const handleHideWords = useCallback(() => setShowWords(false), []);
52
+ const handleShowDictionary = useCallback(() => setShowDictionary(true), []);
53
+ const handleShowKeyMap = useCallback(() => setShowKeyMap(true), []);
54
+ const handleShowMenu = useCallback(() => setShowMenu(true), []);
55
+ const handleShowRemainingTiles = useCallback(() => setShowRemainingTiles(true), []);
56
+ const handleShowSettings = useCallback(() => setShowSettings(true), []);
57
+ const handleShowWords = useCallback(() => setShowWords(true), []);
54
58
 
55
59
  useDirection(LOCALE_FEATURES[locale].direction);
56
60
  useLanguage(locale);
57
61
  useLocalStorage();
58
62
 
59
63
  useEffectOnce(() => {
60
- setIsClient(true);
61
- dispatch(initialize());
62
- setIsInitialized(true);
63
- });
64
-
65
- useEffect(() => {
66
- if (!showResultsInModal) {
67
- setShowResults(false);
68
- }
69
- }, [showResultsInModal]);
70
-
71
- useEffect(() => {
72
64
  if (process.env.NODE_ENV === 'production') {
73
65
  registerServiceWorker();
74
66
  }
75
- }, []);
67
+
68
+ setIsClient(true);
69
+ dispatch(initialize());
70
+ });
76
71
 
77
72
  if (!isClient) {
78
73
  return null;
@@ -80,60 +75,47 @@ const Index: FunctionComponent<Props> = ({ version }) => {
80
75
 
81
76
  return (
82
77
  <>
83
- <SvgFontFix />
84
-
85
- <div className={classNames(styles.index, { [styles.initialized]: isInitialized })} ref={indexRef}>
86
- <nav className={styles.nav} ref={navRef}>
87
- <div className={styles.navContent}>
88
- <div className={styles.navLogo}>
89
- <a className={styles.logoContainer} href="/" title={version}>
90
- <Logo className={styles.logo} />
91
- </a>
92
- </div>
93
-
94
- <NavButtons
95
- onClear={handleClear}
96
- onShowKeyMap={() => setShowKeyMap(true)}
97
- onShowMenu={() => setShowMenu(true)}
98
- onShowRemainingTiles={() => setShowRemainingTiles(true)}
99
- onShowSettings={() => setShowSettings(true)}
100
- onShowWords={() => setShowWords(true)}
101
- />
78
+ <nav className={styles.nav}>
79
+ <div className={styles.navContent}>
80
+ <div className={styles.navLogo}>
81
+ <a className={styles.logoContainer} href="/" title={version}>
82
+ <Logo className={styles.logo} />
83
+ </a>
102
84
  </div>
103
- </nav>
104
-
105
- {solverHeight > 0 && solverWidth > 0 && (
106
- <Solver
107
- className={styles.solver}
108
- height={solverHeight}
109
- width={solverWidth}
110
- onShowResults={() => setShowResults(true)}
85
+
86
+ <NavButtons
87
+ onClear={handleClear}
88
+ onShowKeyMap={handleShowKeyMap}
89
+ onShowMenu={handleShowMenu}
90
+ onShowRemainingTiles={handleShowRemainingTiles}
91
+ onShowSettings={handleShowSettings}
92
+ onShowWords={handleShowWords}
111
93
  />
112
- )}
113
- </div>
94
+ </div>
95
+ </nav>
96
+
97
+ <Solver className={styles.solver} onShowResults={handleShowResults} />
114
98
 
115
99
  <MenuModal
116
100
  isOpen={showMenu}
117
- onClose={() => setShowMenu(false)}
118
- onShowDictionary={() => setShowDictionary(true)}
119
- onShowRemainingTiles={() => setShowRemainingTiles(true)}
120
- onShowSettings={() => setShowSettings(true)}
121
- onShowWords={() => setShowWords(true)}
101
+ onClose={handleHideMenu}
102
+ onShowDictionary={handleShowDictionary}
103
+ onShowRemainingTiles={handleShowRemainingTiles}
104
+ onShowSettings={handleShowSettings}
105
+ onShowWords={handleShowWords}
122
106
  />
123
107
 
124
- <SettingsModal isOpen={showSettings} onClose={() => setShowSettings(false)} />
125
-
126
- <KeyMapModal isOpen={showKeyMap} onClose={() => setShowKeyMap(false)} />
108
+ <SettingsModal isOpen={showSettings} onClose={handleHideSettings} />
127
109
 
128
- <WordsModal isOpen={showWords} onClose={() => setShowWords(false)} />
110
+ <KeyMapModal isOpen={showKeyMap} onClose={handleHideKeyMap} />
129
111
 
130
- <RemainingTilesModal isOpen={showRemainingTiles} onClose={() => setShowRemainingTiles(false)} />
112
+ <WordsModal isOpen={showWords} onClose={handleHideWords} />
131
113
 
132
- <ResultsModal isOpen={showResults} onClose={() => setShowResults(false)} />
114
+ <RemainingTilesModal isOpen={showRemainingTiles} onClose={handleHideRemainingTiles} />
133
115
 
134
- <DictionaryModal isOpen={showDictionary} onClose={() => setShowDictionary(false)} />
116
+ <ResultsModal isOpen={showResults} onClose={handleHideResults} />
135
117
 
136
- <LogoSplashScreen forceShow={!isInitialized} />
118
+ <DictionaryModal isOpen={showDictionary} onClose={handleHideDictionary} />
137
119
  </>
138
120
  );
139
121
  };
@@ -19,8 +19,14 @@ export const COLOR_GREEN = '#bae3ba';
19
19
  export const COLOR_RED = '#f7c2aa';
20
20
  export const COLOR_YELLOW = '#efe3ae';
21
21
 
22
- export const COMPONENTS_SPACING = 40;
23
- export const COMPONENTS_SPACING_SMALL = 20;
22
+ export const SPACING_XS = 2;
23
+ export const SPACING_S = 5;
24
+ export const SPACING_M = 10;
25
+ export const SPACING_L = 20;
26
+ export const SPACING_XL = 40;
27
+
28
+ export const COMPONENTS_SPACING = SPACING_XL;
29
+ export const COMPONENTS_SPACING_SMALL = SPACING_L;
24
30
 
25
31
  export const BOARD_CELL_ACTIONS_OFFSET = 3;
26
32
  export const BOARD_CELL_BORDER_WIDTH = 1;
@@ -36,6 +42,23 @@ export const BOARD_TILE_SIZE_MIN = 20;
36
42
 
37
43
  export const BORDER_WIDTH = 1;
38
44
 
45
+ export const BUTTON_ICON_SIZE = 24;
46
+ export const BUTTON_PADDING_VERTICAL = SPACING_M;
47
+ export const BUTTON_HEIGHT = BUTTON_ICON_SIZE + 2 * BUTTON_PADDING_VERTICAL + 2 * BORDER_WIDTH;
48
+
49
+ export const DICTIONARY_HEIGHT = 260;
50
+ export const DICTIONARY_HEIGHT_MOBILE = 110;
51
+
52
+ export const LOGO_ASPECT_RATIO = 682 / 166;
53
+ export const LOGO_HEIGHT = 60;
54
+ export const LOGO_HEIGHT_SMALL = 48;
55
+ export const LOGO_SRC = '/logo.svg';
56
+
57
+ export const MODAL_WIDTH = 370;
58
+ export const MODAL_HEADER_HEIGHT = 45;
59
+
60
+ export const NAV_PADDING = SPACING_L;
61
+
39
62
  export const TILE_SIZE = 80;
40
63
 
41
64
  export const PLAIN_TILES_COLOR_DEFAULT = COLOR_GREEN;
@@ -67,10 +90,14 @@ export const RACK_TILE_SIZE_MAX = 80;
67
90
 
68
91
  export const REMAINING_TILES_TILE_SIZE = 50;
69
92
 
93
+ export const RESULTS_HEADER_HEIGHT = 35;
94
+
70
95
  export const RESULTS_ITEM_HEIGHT = 40;
71
96
 
72
97
  export const SOLVER_COLUMN_WIDTH = 580;
73
98
 
99
+ export const TEXT_INPUT_HEIGHT = 40;
100
+
74
101
  export const TILE_APPEAR_DURATION = 200;
75
102
 
76
103
  export const TILE_APPEAR_KEYFRAMES = [