@scrabble-solver/scrabble-solver 2.16.1 → 2.16.3

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 (81) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +6 -6
  3. package/.next/cache/.previewinfo +1 -1
  4. package/.next/cache/.rscinfo +1 -1
  5. package/.next/cache/.tsbuildinfo +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/client-production/index.pack.old +0 -0
  9. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  10. package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  11. package/.next/cache/webpack/server-production/0.pack +0 -0
  12. package/.next/cache/webpack/server-production/index.pack +0 -0
  13. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  14. package/.next/prerender-manifest.json +4 -4
  15. package/.next/routes-manifest.json +1 -1
  16. package/.next/server/chunks/712.js +1 -1
  17. package/.next/server/middleware-build-manifest.js +1 -1
  18. package/.next/server/pages/404.html +1 -1
  19. package/.next/server/pages/500.html +1 -1
  20. package/.next/server/pages/index.html +1 -1
  21. package/.next/server/pages/index.js +1 -1
  22. package/.next/server/pages/index.json +1 -1
  23. package/.next/server/pages/not-found.html +1 -1
  24. package/.next/static/{-2mw8IrOEpCcsQa6oPYhR → 2uHbiXWVmb-guNjYk2sfF}/_buildManifest.js +1 -1
  25. package/.next/static/chunks/pages/_app-b74c31c912e414e4.js +7 -0
  26. package/.next/static/chunks/pages/index-35f70f1627836521.js +1 -0
  27. package/.next/static/css/0af35adb4bab56f0.css +2 -0
  28. package/.next/static/css/a78e04fe95452380.css +1 -0
  29. package/.next/trace +21 -21
  30. package/.next/trace-build +1 -1
  31. package/package.json +9 -9
  32. package/src/components/Board/Board.tsx +3 -1
  33. package/src/components/Board/BoardPure.tsx +3 -0
  34. package/src/components/Board/components/Cell/Cell.module.scss +6 -0
  35. package/src/components/Board/components/Cell/Cell.tsx +18 -6
  36. package/src/components/Board/lib.ts +83 -2
  37. package/src/components/Board/selectors.ts +20 -1
  38. package/src/components/DictionaryInput/DictionaryInput.tsx +1 -0
  39. package/src/components/Modal/Modal.tsx +2 -0
  40. package/src/components/Progress/Progress.tsx +5 -0
  41. package/src/components/Rack/components/RackTile/RackTile.tsx +4 -1
  42. package/src/components/Radio/Radio.tsx +1 -1
  43. package/src/components/Results/Result.tsx +1 -0
  44. package/src/components/Results/Results.tsx +8 -2
  45. package/src/components/Tile/TilePure.tsx +20 -18
  46. package/src/i18n/languages/english.json +4 -1
  47. package/src/i18n/languages/french.json +4 -1
  48. package/src/i18n/languages/german.json +4 -1
  49. package/src/i18n/languages/persian.json +4 -1
  50. package/src/i18n/languages/polish.json +4 -1
  51. package/src/i18n/languages/romanian.json +4 -1
  52. package/src/i18n/languages/spanish.json +4 -1
  53. package/src/i18n/languages/turkish.json +4 -1
  54. package/src/modals/RemainingTilesModal/components/Character/Character.module.scss +4 -0
  55. package/src/modals/RemainingTilesModal/components/Character/Character.tsx +35 -2
  56. package/src/modals/SettingsModal/SettingsModal.tsx +8 -0
  57. package/src/modals/SettingsModal/components/AutoGroupTilesSetting/AutoGroupTilesSetting.tsx +4 -4
  58. package/src/modals/SettingsModal/components/HighlightUnreachableCellsSetting/HighlightUnreachableCellsSetting.module.scss +12 -0
  59. package/src/modals/SettingsModal/components/HighlightUnreachableCellsSetting/HighlightUnreachableCellsSetting.tsx +51 -0
  60. package/src/modals/SettingsModal/components/HighlightUnreachableCellsSetting/index.ts +1 -0
  61. package/src/modals/SettingsModal/components/index.ts +1 -0
  62. package/src/state/hoveredTile/index.ts +4 -0
  63. package/src/state/hoveredTile/initialState.ts +5 -0
  64. package/src/state/hoveredTile/selectors.ts +3 -0
  65. package/src/state/hoveredTile/slice.ts +16 -0
  66. package/src/state/hoveredTile/types.ts +3 -0
  67. package/src/state/index.ts +1 -0
  68. package/src/state/localStorage.ts +1 -0
  69. package/src/state/settings/initialState.ts +1 -0
  70. package/src/state/settings/selectors.ts +5 -0
  71. package/src/state/settings/slice.ts +5 -0
  72. package/src/state/settings/types.ts +1 -0
  73. package/src/state/store.ts +2 -0
  74. package/src/state/types.ts +2 -0
  75. package/src/types/index.ts +3 -0
  76. package/tsconfig.tsbuildinfo +1 -1
  77. package/.next/static/chunks/pages/_app-9c9aaea7b6881765.js +0 -7
  78. package/.next/static/chunks/pages/index-e9ea63e97865edcf.js +0 -1
  79. package/.next/static/css/78726ae7cf7a5497.css +0 -2
  80. package/.next/static/css/addf6fd4f75742d0.css +0 -1
  81. /package/.next/static/{-2mw8IrOEpCcsQa6oPYhR → 2uHbiXWVmb-guNjYk2sfF}/_ssgManifest.js +0 -0
@@ -4,7 +4,7 @@
4
4
  "cell.filter-cell.include": "Hedef varış noktası",
5
5
  "cell.set-blank": "Boş olarak işaretle",
6
6
  "cell.set-not-blank": "Boş değil olarak işaretle",
7
- "cell.tile.location": "Tahta: harfler ({{x}}, {{y}})",
7
+ "cell.tile.location": "Tahta: harfler {{coordinates}}",
8
8
  "cell.toggle-direction": "Yazma yönü - geçiş yapmak için tıklayın",
9
9
  "common.arrows": "Ok tuşları",
10
10
  "common.blanks": "Jokerler",
@@ -13,6 +13,8 @@
13
13
  "common.consonants": "Ünlü harfler",
14
14
  "common.loading": "Yükleniyor",
15
15
  "common.next": "Sonraki",
16
+ "common.off": "Kapalı",
17
+ "common.on": "Açık",
16
18
  "common.points": "Puanlar",
17
19
  "common.previous": "Öncesi",
18
20
  "common.space": "Boşluk çubuğu",
@@ -64,6 +66,7 @@
64
66
  "settings.autoGroupTiles.right": "Sağdaki",
65
67
  "settings.autoGroupTiles.null": "Gruplamayın",
66
68
  "settings.game": "Oyun",
69
+ "settings.highlightUnreachableCells": "Ulaşılamayan kareleri vurgula",
67
70
  "settings.inputMode": "Giriş Methodu",
68
71
  "settings.inputMode.keyboard": "Klavye",
69
72
  "settings.inputMode.touchscreen": "Dokunmatik ekran",
@@ -43,3 +43,7 @@
43
43
  font-size: var(--font--size--m);
44
44
  white-space: nowrap;
45
45
  }
46
+
47
+ .unused {
48
+ cursor: not-allowed;
49
+ }
@@ -1,11 +1,18 @@
1
1
  import { BLANK } from '@scrabble-solver/constants';
2
2
  import classNames from 'classnames';
3
- import { type FunctionComponent } from 'react';
3
+ import { type FunctionComponent, useCallback, useEffect } from 'react';
4
+ import { useDispatch } from 'react-redux';
4
5
 
5
6
  import { Progress, Tile } from '@/components';
6
7
  import { LOCALE_FEATURES } from '@/i18n';
7
8
  import { REMAINING_TILES_TILE_SIZE } from '@/parameters';
8
- import { selectCharacterPoints, selectLocale, useTypedSelector } from '@/state';
9
+ import {
10
+ hoveredTileSlice,
11
+ selectCharacterPoints,
12
+ selectHoveredCharacter,
13
+ selectLocale,
14
+ useTypedSelector,
15
+ } from '@/state';
9
16
  import { type RemainingTile } from '@/types';
10
17
 
11
18
  import styles from './Character.module.scss';
@@ -15,7 +22,9 @@ interface Props {
15
22
  }
16
23
 
17
24
  export const Character: FunctionComponent<Props> = ({ tile }) => {
25
+ const dispatch = useDispatch();
18
26
  const locale = useTypedSelector(selectLocale);
27
+ const hoveredCharacter = useTypedSelector(selectHoveredCharacter);
19
28
  const { direction } = LOCALE_FEATURES[locale];
20
29
  const { character, count, usedCount } = tile;
21
30
 
@@ -27,19 +36,43 @@ export const Character: FunctionComponent<Props> = ({ tile }) => {
27
36
  const points = useTypedSelector((state) => selectCharacterPoints(state, character));
28
37
  const current = direction === 'ltr' ? remainingCount : count;
29
38
  const total = direction === 'ltr' ? count : remainingCount;
39
+ const isUsed = usedCount > 0;
40
+ const isHovered = isUsed && hoveredCharacter === character;
41
+
42
+ const handleMouseEnter = useCallback(() => {
43
+ if (!isUsed) {
44
+ return;
45
+ }
46
+ dispatch(hoveredTileSlice.actions.set(character));
47
+ }, [character, dispatch, isUsed]);
48
+
49
+ const handleMouseLeave = useCallback(() => {
50
+ dispatch(hoveredTileSlice.actions.clear());
51
+ }, [dispatch]);
52
+
53
+ useEffect(() => {
54
+ return () => {
55
+ dispatch(hoveredTileSlice.actions.clear());
56
+ };
57
+ }, [dispatch]);
30
58
 
31
59
  return (
32
60
  <div
33
61
  className={classNames(styles.character, {
34
62
  [styles.finished]: remainingCount <= 0,
35
63
  [styles.overused]: remainingCount < 0,
64
+ [styles.unused]: !isUsed,
36
65
  })}
66
+ data-testid={character === BLANK ? 'remaining-tile-blank' : `remaining-tile-${character}`}
67
+ onMouseEnter={handleMouseEnter}
68
+ onMouseLeave={handleMouseLeave}
37
69
  >
38
70
  <Tile
39
71
  aria-label={character}
40
72
  character={character}
41
73
  className={styles.tile}
42
74
  disabled
75
+ highlighted={isHovered}
43
76
  isBlank={character === BLANK}
44
77
  isValid={remainingCount >= 0}
45
78
  points={points}
@@ -7,6 +7,7 @@ import { useTranslate } from '@/state';
7
7
  import {
8
8
  AutoGroupTilesSetting,
9
9
  ConfigSetting,
10
+ HighlightUnreachableCellsSetting,
10
11
  InputModeSetting,
11
12
  LocaleSetting,
12
13
  ShowCoordinatesSetting,
@@ -50,6 +51,13 @@ const SettingsModalBase: FunctionComponent<Props> = ({ className, isOpen, onClos
50
51
  <Modal.Section label={translate('settings.removeCellFilters')} title={translate('settings.removeCellFilters')}>
51
52
  <RemoveCellFiltersSetting disabled={!isOpen} />
52
53
  </Modal.Section>
54
+
55
+ <Modal.Section
56
+ label={translate('settings.highlightUnreachableCells')}
57
+ title={translate('settings.highlightUnreachableCells')}
58
+ >
59
+ <HighlightUnreachableCellsSetting disabled={!isOpen} />
60
+ </Modal.Section>
53
61
  </Modal>
54
62
  );
55
63
  };
@@ -19,6 +19,10 @@ export const AutoGroupTilesSetting: FunctionComponent<Props> = ({ className, dis
19
19
  const value = useTypedSelector(selectAutoGroupTiles);
20
20
 
21
21
  const options = [
22
+ {
23
+ label: translate('settings.autoGroupTiles.null'),
24
+ value: NULL_VALUE,
25
+ },
22
26
  {
23
27
  label: translate('settings.autoGroupTiles.left'),
24
28
  value: 'left',
@@ -27,10 +31,6 @@ export const AutoGroupTilesSetting: FunctionComponent<Props> = ({ className, dis
27
31
  label: translate('settings.autoGroupTiles.right'),
28
32
  value: 'right',
29
33
  },
30
- {
31
- label: translate('settings.autoGroupTiles.null'),
32
- value: NULL_VALUE,
33
- },
34
34
  ];
35
35
 
36
36
  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
@@ -0,0 +1,12 @@
1
+ .option {
2
+ margin-bottom: var(--spacing--m);
3
+
4
+ &:last-child {
5
+ margin-bottom: 0;
6
+ }
7
+ }
8
+
9
+ .label {
10
+ display: flex;
11
+ align-items: center;
12
+ }
@@ -0,0 +1,51 @@
1
+ import { type ChangeEvent, type FunctionComponent } from 'react';
2
+ import { useDispatch } from 'react-redux';
3
+
4
+ import { Radio } from '@/components';
5
+ import { selectHighlightUnreachableCells, settingsSlice, useTranslate, useTypedSelector } from '@/state';
6
+
7
+ import styles from './HighlightUnreachableCellsSetting.module.scss';
8
+
9
+ interface Props {
10
+ className?: string;
11
+ disabled?: boolean;
12
+ }
13
+
14
+ export const HighlightUnreachableCellsSetting: FunctionComponent<Props> = ({ className, disabled }) => {
15
+ const dispatch = useDispatch();
16
+ const translate = useTranslate();
17
+ const value = useTypedSelector(selectHighlightUnreachableCells);
18
+
19
+ const options = [
20
+ {
21
+ label: translate('common.off'),
22
+ value: 'off',
23
+ },
24
+ {
25
+ label: translate('common.on'),
26
+ value: 'on',
27
+ },
28
+ ];
29
+
30
+ const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
31
+ dispatch(settingsSlice.actions.changeHighlightUnreachableCells(event.target.value === 'on'));
32
+ };
33
+
34
+ return (
35
+ <div className={className}>
36
+ {options.map((option) => (
37
+ <Radio
38
+ checked={(value ? 'on' : 'off') === option.value}
39
+ className={styles.option}
40
+ disabled={disabled}
41
+ key={option.value}
42
+ name="highlightUnreachableCells"
43
+ value={option.value}
44
+ onChange={handleChange}
45
+ >
46
+ <div className={styles.label}>{option.label}</div>
47
+ </Radio>
48
+ ))}
49
+ </div>
50
+ );
51
+ };
@@ -0,0 +1 @@
1
+ export { HighlightUnreachableCellsSetting } from './HighlightUnreachableCellsSetting';
@@ -1,5 +1,6 @@
1
1
  export { AutoGroupTilesSetting } from './AutoGroupTilesSetting';
2
2
  export { ConfigSetting } from './ConfigSetting';
3
+ export { HighlightUnreachableCellsSetting } from './HighlightUnreachableCellsSetting';
3
4
  export { InputModeSetting } from './InputModeSetting';
4
5
  export { LocaleSetting } from './LocaleSetting';
5
6
  export { ShowCoordinatesSetting } from './ShowCoordinatesSetting';
@@ -0,0 +1,4 @@
1
+ export * from './initialState';
2
+ export * from './selectors';
3
+ export * from './slice';
4
+ export * from './types';
@@ -0,0 +1,5 @@
1
+ import type { HoveredTileState } from './types';
2
+
3
+ export const hoveredTileInitialState: HoveredTileState = {
4
+ character: null,
5
+ };
@@ -0,0 +1,3 @@
1
+ import type { RootState } from '../types';
2
+
3
+ export const selectHoveredCharacter = (state: RootState): string | null => state.hoveredTile.character;
@@ -0,0 +1,16 @@
1
+ import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
2
+
3
+ import { hoveredTileInitialState } from './initialState';
4
+
5
+ export const hoveredTileSlice = createSlice({
6
+ initialState: hoveredTileInitialState,
7
+ name: 'hoveredTile',
8
+ reducers: {
9
+ set: (state, action: PayloadAction<string>) => {
10
+ state.character = action.payload;
11
+ },
12
+ clear: (state) => {
13
+ state.character = null;
14
+ },
15
+ },
16
+ });
@@ -0,0 +1,3 @@
1
+ export interface HoveredTileState {
2
+ character: string | null;
3
+ }
@@ -2,6 +2,7 @@ export * from './actions';
2
2
  export * from './board';
3
3
  export * from './cellFilters';
4
4
  export * from './dictionary';
5
+ export * from './hoveredTile';
5
6
  export * from './localStorage';
6
7
  export * from './rack';
7
8
  export * from './results';
@@ -12,6 +12,7 @@ const SETTINGS = 'settings';
12
12
  const LEGACY_KEYS: Record<keyof SettingsState, string> = {
13
13
  autoGroupTiles: 'auto-group-tiles',
14
14
  game: 'config-id',
15
+ highlightUnreachableCells: 'highlight-unreachable-cells',
15
16
  inputMode: 'input-mode',
16
17
  locale: 'locale',
17
18
  removeCellFilters: 'remove-cell-filters',
@@ -10,6 +10,7 @@ const isTouchScreen = typeof globalThis.matchMedia !== 'undefined' && globalThis
10
10
  export const settingsInitialState: SettingsState = {
11
11
  autoGroupTiles: null,
12
12
  game: Game.Scrabble,
13
+ highlightUnreachableCells: false,
13
14
  inputMode: isTouchScreen ? 'touchscreen' : 'keyboard',
14
15
  locale: guessLocale(),
15
16
  removeCellFilters: 'always',
@@ -19,6 +19,11 @@ export const selectAutoGroupTiles = createSelector([selectSettings], (settings)
19
19
 
20
20
  export const selectGame = createSelector([selectSettings], (settings) => settings.game);
21
21
 
22
+ export const selectHighlightUnreachableCells = createSelector(
23
+ [selectSettings],
24
+ (settings) => settings.highlightUnreachableCells,
25
+ );
26
+
22
27
  export const selectInputMode = createSelector([selectSettings], (settings) => settings.inputMode);
23
28
 
24
29
  export const selectLocale = createSelector([selectSettings], (settings) => settings.locale);
@@ -19,6 +19,11 @@ export const settingsSlice = createSlice({
19
19
  return { ...state, game };
20
20
  },
21
21
 
22
+ changeHighlightUnreachableCells: (state, action: PayloadAction<boolean>) => {
23
+ const highlightUnreachableCells = action.payload;
24
+ return { ...state, highlightUnreachableCells };
25
+ },
26
+
22
27
  changeInputMode: (state, action: PayloadAction<InputMode>) => {
23
28
  const inputMode = action.payload;
24
29
  return { ...state, inputMode };
@@ -5,6 +5,7 @@ import type { AutoGroupTiles, InputMode, RemoveCellFilters } from '@/types';
5
5
  export interface SettingsState {
6
6
  autoGroupTiles: AutoGroupTiles;
7
7
  game: Game;
8
+ highlightUnreachableCells: boolean;
8
9
  inputMode: InputMode;
9
10
  locale: Locale;
10
11
  showCoordinates: ShowCoordinates;
@@ -4,6 +4,7 @@ import reduxSaga from 'redux-saga';
4
4
  import { boardSlice } from './board';
5
5
  import { cellFiltersSlice } from './cellFilters';
6
6
  import { dictionarySlice } from './dictionary';
7
+ import { hoveredTileSlice } from './hoveredTile';
7
8
  import { rackSlice } from './rack';
8
9
  import { resultsSlice } from './results';
9
10
  import { rootSaga } from './sagas';
@@ -18,6 +19,7 @@ export const store = configureStore({
18
19
  board: boardSlice.reducer,
19
20
  cellFilters: cellFiltersSlice.reducer,
20
21
  dictionary: dictionarySlice.reducer,
22
+ hoveredTile: hoveredTileSlice.reducer,
21
23
  rack: rackSlice.reducer,
22
24
  results: resultsSlice.reducer,
23
25
  settings: settingsSlice.reducer,
@@ -1,6 +1,7 @@
1
1
  import type { BoardState } from './board';
2
2
  import type { CellFiltersState } from './cellFilters';
3
3
  import type { DictionaryState } from './dictionary';
4
+ import type { HoveredTileState } from './hoveredTile';
4
5
  import type { RackState } from './rack';
5
6
  import type { ResultsState } from './results';
6
7
  import type { SettingsState } from './settings';
@@ -11,6 +12,7 @@ export type RootState = {
11
12
  board: BoardState;
12
13
  cellFilters: CellFiltersState;
13
14
  dictionary: DictionaryState;
15
+ hoveredTile: HoveredTileState;
14
16
  rack: RackState;
15
17
  results: ResultsState;
16
18
  settings: SettingsState;
@@ -89,6 +89,8 @@ export type TranslationKey =
89
89
  | 'common.blanks'
90
90
  | 'common.consonants'
91
91
  | 'common.next'
92
+ | 'common.off'
93
+ | 'common.on'
92
94
  | 'common.points'
93
95
  | 'common.previous'
94
96
  | 'common.space'
@@ -140,6 +142,7 @@ export type TranslationKey =
140
142
  | 'settings.autoGroupTiles.right'
141
143
  | 'settings.autoGroupTiles.null'
142
144
  | 'settings.game'
145
+ | 'settings.highlightUnreachableCells'
143
146
  | 'settings.inputMode'
144
147
  | 'settings.inputMode.keyboard'
145
148
  | 'settings.inputMode.touchscreen'