@scrabble-solver/scrabble-solver 2.9.2 → 2.10.0

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 (120) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +11 -11
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/eslint/.cache_8dgz12 +1 -1
  5. package/.next/cache/next-server.js.nft.json +1 -1
  6. package/.next/cache/webpack/client-production/0.pack +0 -0
  7. package/.next/cache/webpack/client-production/index.pack +0 -0
  8. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  9. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  10. package/.next/cache/webpack/server-production/0.pack +0 -0
  11. package/.next/cache/webpack/server-production/index.pack +0 -0
  12. package/.next/next-server.js.nft.json +1 -1
  13. package/.next/prerender-manifest.json +1 -1
  14. package/.next/routes-manifest.json +1 -1
  15. package/.next/server/chunks/{429.js → 131.js} +2 -3652
  16. package/.next/server/chunks/413.js +367 -185
  17. package/.next/server/chunks/44.js +71 -45
  18. package/.next/server/chunks/515.js +1135 -504
  19. package/.next/server/chunks/911.js +96 -2
  20. package/.next/server/middleware-build-manifest.js +1 -1
  21. package/.next/server/pages/404.html +2 -2
  22. package/.next/server/pages/404.js.nft.json +1 -1
  23. package/.next/server/pages/500.html +2 -2
  24. package/.next/server/pages/_app.js.nft.json +1 -1
  25. package/.next/server/pages/_error.js.nft.json +1 -1
  26. package/.next/server/pages/api/dictionary/[locale]/[word].js +48 -10
  27. package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
  28. package/.next/server/pages/api/dictionary/[locale].js +1 -8
  29. package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
  30. package/.next/server/pages/api/solve.js +62 -49
  31. package/.next/server/pages/api/solve.js.nft.json +1 -1
  32. package/.next/server/pages/api/verify.js +2 -9
  33. package/.next/server/pages/api/verify.js.nft.json +1 -1
  34. package/.next/server/pages/index.html +4 -8
  35. package/.next/server/pages/index.js +43 -36
  36. package/.next/server/pages/index.js.nft.json +1 -1
  37. package/.next/server/pages/index.json +1 -1
  38. package/.next/server/pages-manifest.json +2 -2
  39. package/.next/static/chunks/368-d423e70be6c0c473.js +1 -0
  40. package/.next/static/chunks/pages/{404-7082923654d5996f.js → 404-932294135c3206dd.js} +1 -1
  41. package/.next/static/chunks/pages/_app-3f5508a5f544d9eb.js +1 -0
  42. package/.next/static/chunks/pages/index-8af7a9d7a2cd98a7.js +1 -0
  43. package/.next/static/css/6b1833fd19d3a74a.css +1 -0
  44. package/.next/static/css/a6154e4ca046ca13.css +1 -0
  45. package/.next/static/css/bad53af6f8616677.css +1 -0
  46. package/.next/static/vscqn7BEtAxJteWSwNnas/_buildManifest.js +1 -0
  47. package/.next/static/{Ntg-ilwD7GqTIFwRpSaTQ → vscqn7BEtAxJteWSwNnas}/_ssgManifest.js +0 -0
  48. package/.next/trace +52 -52
  49. package/package.json +9 -9
  50. package/src/components/Board/components/Cell/Cell.module.scss +45 -9
  51. package/src/components/Board/hooks/useGrid.ts +4 -2
  52. package/src/components/Dictionary/Dictionary.module.scss +8 -1
  53. package/src/components/EmptyState/EmptyState.tsx +6 -2
  54. package/src/components/Loading/Loading.tsx +13 -2
  55. package/src/components/Logo/Logo.svg +62 -0
  56. package/src/components/Logo/index.ts +1 -1
  57. package/src/components/NavButtons/NavButtons.module.scss +8 -7
  58. package/src/components/NavButtons/NavButtons.tsx +35 -28
  59. package/src/components/PlainTiles/PlainTiles.tsx +7 -1
  60. package/src/components/PlainTiles/Tile.tsx +4 -3
  61. package/src/components/Rack/Rack.tsx +4 -2
  62. package/src/components/Radio/Radio.module.scss +1 -1
  63. package/src/components/RemainingTiles/Character.tsx +7 -2
  64. package/src/components/RemainingTiles/RemainingTiles.tsx +28 -20
  65. package/src/components/Results/Cell.tsx +5 -3
  66. package/src/components/Results/Result.tsx +8 -3
  67. package/src/components/Results/Results.module.scss +31 -9
  68. package/src/components/Results/Results.tsx +6 -2
  69. package/src/components/Results/getColumns.ts +58 -0
  70. package/src/components/Settings/components/ConfigSetting/ConfigSetting.module.scss +1 -0
  71. package/src/components/Settings/components/LocaleSetting/LocaleSetting.module.scss +12 -1
  72. package/src/components/Settings/components/LocaleSetting/LocaleSetting.tsx +1 -1
  73. package/src/components/Settings/components/LocaleSetting/options.ts +7 -1
  74. package/src/components/Sidebar/Sidebar.module.scss +42 -8
  75. package/src/components/SvgFontCss/SvgFontCss.tsx +8 -7
  76. package/src/components/SvgFontCss/createCss.ts +11 -0
  77. package/src/components/SvgFontCss/createStyle.ts +9 -0
  78. package/src/components/SvgFontCss/createSvg.ts +10 -0
  79. package/src/components/SvgFontFix/SvgFontFix.module.scss +5 -0
  80. package/src/components/SvgFontFix/SvgFontFix.tsx +21 -0
  81. package/src/components/SvgFontFix/index.ts +1 -0
  82. package/src/components/Tile/Tile.module.scss +10 -2
  83. package/src/components/Tile/Tile.tsx +4 -0
  84. package/src/components/Tile/TilePure.tsx +3 -1
  85. package/src/components/Words/Words.tsx +4 -3
  86. package/src/components/index.ts +1 -0
  87. package/src/hooks/index.ts +2 -0
  88. package/src/hooks/useDirection.ts +22 -0
  89. package/src/hooks/useLanguage.ts +22 -0
  90. package/src/i18n/constants.ts +53 -0
  91. package/src/i18n/fa.json +56 -0
  92. package/src/i18n/i18n.ts +22 -0
  93. package/src/i18n/index.ts +2 -20
  94. package/src/icons/FlagFa.svg +95 -0
  95. package/src/icons/index.ts +1 -0
  96. package/src/lib/createComparator.ts +22 -0
  97. package/src/lib/createKeyComparator.ts +4 -2
  98. package/src/lib/createStringComparator.ts +5 -0
  99. package/src/lib/detectLocale.ts +4 -0
  100. package/src/lib/getRemainingTiles.ts +4 -2
  101. package/src/lib/getRemainingTilesGroups.ts +48 -23
  102. package/src/lib/index.ts +2 -2
  103. package/src/lib/sortResults.ts +11 -9
  104. package/src/lib/unorderedArraysEqual.ts +3 -2
  105. package/src/pages/api/verify.ts +1 -1
  106. package/src/pages/index.module.scss +7 -18
  107. package/src/pages/index.tsx +10 -2
  108. package/src/service-worker/routeVerifyRequests.ts +1 -1
  109. package/src/state/selectors.ts +5 -5
  110. package/src/styles/global.scss +6 -1
  111. package/.next/static/Ntg-ilwD7GqTIFwRpSaTQ/_buildManifest.js +0 -1
  112. package/.next/static/chunks/317-5e5334962dd7c681.js +0 -1
  113. package/.next/static/chunks/pages/_app-183f598b1d4d480b.js +0 -1
  114. package/.next/static/chunks/pages/index-b1ffeaddd9fb64b5.js +0 -1
  115. package/.next/static/css/751e8a14776d05d8.css +0 -1
  116. package/.next/static/css/ad2a08918868cad8.css +0 -1
  117. package/.next/static/css/e8de67ad5ea35427.css +0 -1
  118. package/src/components/Results/constants.ts +0 -42
  119. package/src/lib/comparator.ts +0 -16
  120. package/src/lib/stringComparator.ts +0 -5
@@ -0,0 +1,53 @@
1
+ import { Locale } from '@scrabble-solver/types';
2
+
3
+ interface LocaleFeatures {
4
+ direction: 'ltr' | 'rtl';
5
+ fontFamily: string;
6
+ consonants: boolean;
7
+ vowels: boolean;
8
+ }
9
+
10
+ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
11
+ [Locale.DE_DE]: {
12
+ direction: 'ltr',
13
+ fontFamily: 'Open Sans',
14
+ consonants: true,
15
+ vowels: true,
16
+ },
17
+ [Locale.EN_GB]: {
18
+ direction: 'ltr',
19
+ fontFamily: 'Open Sans',
20
+ consonants: true,
21
+ vowels: true,
22
+ },
23
+ [Locale.EN_US]: {
24
+ direction: 'ltr',
25
+ fontFamily: 'Open Sans',
26
+ consonants: true,
27
+ vowels: true,
28
+ },
29
+ [Locale.ES_ES]: {
30
+ direction: 'ltr',
31
+ fontFamily: 'Open Sans',
32
+ consonants: true,
33
+ vowels: true,
34
+ },
35
+ [Locale.FA_IR]: {
36
+ direction: 'rtl',
37
+ fontFamily: 'Vazirmatn',
38
+ consonants: false,
39
+ vowels: false,
40
+ },
41
+ [Locale.FR_FR]: {
42
+ direction: 'ltr',
43
+ fontFamily: 'Open Sans',
44
+ consonants: true,
45
+ vowels: true,
46
+ },
47
+ [Locale.PL_PL]: {
48
+ direction: 'ltr',
49
+ fontFamily: 'Open Sans',
50
+ consonants: true,
51
+ vowels: true,
52
+ },
53
+ };
@@ -0,0 +1,56 @@
1
+ {
2
+ "cell.filter-cell": "مقصد - کلیک برای تغییر",
3
+ "cell.set-blank": "علامت گذاری به عنوان خالی",
4
+ "cell.set-not-blank": "علامت گذاری به عنوان غیر خالی",
5
+ "cell.toggle-direction": "جهت تایپ - کلیک برای تغییر",
6
+ "common.blanks": "خالی",
7
+ "common.clear": "پاک کردن",
8
+ "common.close": "بستن",
9
+ "common.consonants": "حروف صامت",
10
+ "common.loading": "در حال بارگزاری",
11
+ "common.points": "امتیازات",
12
+ "common.tiles": "کاشی ها",
13
+ "common.two-letter-tiles": "دو حرفی",
14
+ "common.vowels": "حروف مصوت",
15
+ "common.word": "کلمه",
16
+ "common.words": "کلمات",
17
+ "dictionary.empty-state.no-definitions": "کلمه در فرهنگ لغت وجود دارد، ولی معنایی برای آن ثبت نشده است.",
18
+ "dictionary.empty-state.no-results": "کلمه در فرهنگ لغت یافت نشد.",
19
+ "dictionary.empty-state.not-allowed": "این کلمه مجاز نیست.",
20
+ "dictionary.empty-state.uninitialized": "معنی لغت اینجا نمایش داده خواهد شد.",
21
+ "dictionary.input.placeholder": "جستجو در فرهنگ لغت ...",
22
+ "empty-state.error": "خطا",
23
+ "empty-state.info": "اطلاعات",
24
+ "empty-state.success": "حله!",
25
+ "empty-state.warning": "اوووخ!",
26
+ "github": "به این پروژه در گیتهاب سر بزنید",
27
+ "keyMap": "میانبر های کیبورد",
28
+ "keyMap.board": "صفحه",
29
+ "keyMap.board.toggle-blank": "علامت/عدم علامت گذاری کاشی به عنوان خالی",
30
+ "keyMap.board.toggle-direction": "تغییر جهت تایپ",
31
+ "keyMap.board-and-rack": "صفحه و طاقچه",
32
+ "keyMap.board-and-rack.insert-two-letter-tile": "وارد کردن کاشی دو حرفی",
33
+ "keyMap.board-and-rack.navigate": "حرکت",
34
+ "keyMap.board-and-rack.remove-tile": "حذف کاشی",
35
+ "keyMap.board-and-rack.submit": "حل کردن",
36
+ "keyMap.rack": "طاقچه",
37
+ "keyMap.rack.insert-blank": "وارد کردن کاشی خالی (دکمه اسپیس)",
38
+ "rack.placeholder": "لیستحرف",
39
+ "remaining-tiles": "کاشی های باقی مانده",
40
+ "results.empty-state.no-filtered-results": "پاسخی یافت نشد.",
41
+ "results.empty-state.no-results": "کلمه قابل استفاده پیدا نشد.",
42
+ "results.empty-state.outdated": "نتایج به روز نیستند، برای بروز رسانی کلیک کنید.",
43
+ "results.empty-state.uninitialized": "کلمات تولید شده از حروف شما اینجا نمایش داده خواهد شد.",
44
+ "results.input.placeholder": "جستجو در نتایج (RegExp)",
45
+ "results.solve": "حل کن",
46
+ "settings": "تنظیمات",
47
+ "settings.autoGroupTiles": "کاشی های باقی مانده ی طاقچه را کنار هم قرار بده",
48
+ "settings.autoGroupTiles.left": "در سمت چپ",
49
+ "settings.autoGroupTiles.right": "در سمت راست",
50
+ "settings.autoGroupTiles.null": "کنار هم قرار نده",
51
+ "settings.game": "بازی",
52
+ "settings.language": "زبان",
53
+ "words": "کلمات ساخته شده",
54
+ "words.invalid": "نا معتبر",
55
+ "words.valid": "معتبر"
56
+ }
@@ -0,0 +1,22 @@
1
+ import { Locale } from '@scrabble-solver/types';
2
+
3
+ import { Translations } from 'types';
4
+
5
+ import de from './de.json';
6
+ import en from './en.json';
7
+ import es from './es.json';
8
+ import fa from './fa.json';
9
+ import fr from './fr.json';
10
+ import pl from './pl.json';
11
+
12
+ const i18n: Record<Locale, Translations> = {
13
+ [Locale.DE_DE]: de,
14
+ [Locale.EN_GB]: en,
15
+ [Locale.EN_US]: en,
16
+ [Locale.ES_ES]: es,
17
+ [Locale.FA_IR]: fa,
18
+ [Locale.FR_FR]: fr,
19
+ [Locale.PL_PL]: pl,
20
+ };
21
+
22
+ export default i18n;
package/src/i18n/index.ts CHANGED
@@ -1,20 +1,2 @@
1
- import { Locale } from '@scrabble-solver/types';
2
-
3
- import { Translations } from 'types';
4
-
5
- import de from './de.json';
6
- import en from './en.json';
7
- import es from './es.json';
8
- import fr from './fr.json';
9
- import pl from './pl.json';
10
-
11
- const i18n: Record<Locale, Translations> = {
12
- [Locale.DE_DE]: de,
13
- [Locale.EN_GB]: en,
14
- [Locale.EN_US]: en,
15
- [Locale.ES_ES]: es,
16
- [Locale.FR_FR]: fr,
17
- [Locale.PL_PL]: pl,
18
- };
19
-
20
- export default i18n;
1
+ export { default } from './i18n';
2
+ export * from './constants';
@@ -0,0 +1,95 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 630 360">
2
+ <path style="fill:#da0000" d="M 0,0 H 630 V 360 H 0 Z" />
3
+ <path style="fill:#ffffff" d="M 0,0 H 630 V 240 H 0 Z" />
4
+ <path style="fill:#239f40" d="M 0,0 H 630 V 120 H 0 Z" />
5
+ <g transform="translate(8.4,100.4)">
6
+ <g fill="none" stroke="#fff" stroke-width="2">
7
+ <path d="M0,1H26M1,10V5H9V9H17V5H12M4,9H6M26,9H21V5H29M29,0V9H37V0M33,0V9" transform="scale(1.4)" />
8
+ <path d="M0,7H9M10,7H19" transform="scale(2.8)" />
9
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
10
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
11
+ </g>
12
+ <g>
13
+ <g fill="none" stroke="#ffffff" stroke-width="2" transform="translate(56)">
14
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
15
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
16
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
17
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
18
+ </g>
19
+ <g fill="none" stroke="#ffffff" stroke-width="2" transform="translate(112)">
20
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
21
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
22
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
23
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
24
+ </g>
25
+ <g fill="none" stroke="#ffffff" stroke-width="2" transform="translate(168)">
26
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
27
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
28
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
29
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
30
+ </g>
31
+ </g>
32
+ <g transform="translate(168)">
33
+ <g fill="none" stroke="#ffffff" stroke-width="2" transform="translate(56)">
34
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
35
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
36
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
37
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
38
+ </g>
39
+ <g fill="none" stroke="#ffffff" stroke-width="2" transform="translate(112)">
40
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
41
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
42
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
43
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
44
+ </g>
45
+ <g fill="none" stroke="#ffffff" stroke-width="2" transform="translate(168)">
46
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
47
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
48
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
49
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
50
+ </g>
51
+ </g>
52
+ <g transform="translate(392)">
53
+ <g fill="none" stroke="#ffffff" stroke-width="2">
54
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
55
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
56
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
57
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
58
+ </g>
59
+ <g>
60
+ <g fill="none" stroke="#ffffff" stroke-width="2" transform="translate(56)">
61
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
62
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
63
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
64
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
65
+ </g>
66
+ <g fill="none" stroke="#ffffff" stroke-width="2" transform="translate(112)">
67
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
68
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
69
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
70
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
71
+ </g>
72
+ <g fill="none" stroke="#ffffff" stroke-width="2" transform="translate(168)">
73
+ <path d="M 0,1 H 26 M 1,10 V 5 h 8 v 4 h 8 V 5 H 12 M 4,9 H 6 M 26,9 H 21 V 5 h 8 m 0,-5 v 9 h 8 V 0 m -4,0 v 9" transform="scale(1.4)" />
74
+ <path d="m 0,7 h 9 m 1,0 h 9" transform="scale(2.8)" />
75
+ <path d="m 0,139.6 h 25.2 m 2.8,0 h 25.2" style="stroke-width:5.6" />
76
+ <path d="m 0,146.6 h 36.4 m -35,12.6 v -7 h 11.2 v 5.6 h 11.2 v -5.6 h -7 m -11.2,5.6 h 2.8 m 28,0 h -7 v -5.6 h 11.2 m 0,-7 v 12.6 h 11.2 v -12.6 m -5.6,0 v 12.6" style="stroke-width:2.8" />
77
+ </g>
78
+ </g>
79
+ </g>
80
+ </g>
81
+ <g fill="#da0000" transform="matrix(45,0,0,45,315,180)">
82
+ <g>
83
+ <path d="M-0.54815,0.83638A0.912046,0.912046 0 0,0 0.328544,-0.722384A1,1 0 0,1 -0.54815,0.83638" />
84
+ <path d="M0.618339,0.661409A0.763932,0.763932 0 0,0 0.421644,-0.741049A1,1 0 0,1 0.618339,0.661409" />
85
+ <path d="M0,1 -0.05,0 0,-0.787278A0.309995,0.309995 0 0,0 0.118034,-0.688191V-0.100406L0.077809,0.892905z" />
86
+ <path d="M-0.02,-0.85 0,-0.831217A0.14431,0.14431 0 0,0 0.252075,-0.967708A0.136408,0.136408 0 0,1 0,-0.924634" />
87
+ </g>
88
+ <g transform="scale(-1,1)">
89
+ <path d="M -0.54815,0.83638 A 0.912046,0.912046 0 0 0 0.328544,-0.722384 1,1 0 0 1 -0.54815,0.83638" />
90
+ <path d="M 0.618339,0.661409 A 0.763932,0.763932 0 0 0 0.421644,-0.741049 1,1 0 0 1 0.618339,0.661409" />
91
+ <path d="M 0,1 -0.05,0 0,-0.787278 a 0.309995,0.309995 0 0 0 0.118034,0.099087 v 0.587785 L 0.077809,0.892905 Z" />
92
+ <path d="M -0.02,-0.85 0,-0.831217 A 0.14431,0.14431 0 0 0 0.252075,-0.967708 0.136408,0.136408 0 0 1 0,-0.924634" />
93
+ </g>
94
+ </g>
95
+ </svg>
@@ -13,6 +13,7 @@ export { default as DashCircleFill } from './DashCircleFill.svg';
13
13
  export { default as Eraser } from './Eraser.svg';
14
14
  export { default as Flag } from './Flag.svg';
15
15
  export { default as FlagEs } from './FlagEs.svg';
16
+ export { default as FlagFa } from './FlagFa.svg';
16
17
  export { default as FlagFr } from './FlagFr.svg';
17
18
  export { default as FlagGb } from './FlagGb.svg';
18
19
  export { default as FlagPl } from './FlagPl.svg';
@@ -0,0 +1,22 @@
1
+ import { Comparator } from 'types';
2
+
3
+ import createStringComparator from './createStringComparator';
4
+ import numberComparator from './numberComparator';
5
+
6
+ const createComparator = <T>(locale: string): Comparator<T> => {
7
+ const stringComparator = createStringComparator(locale);
8
+
9
+ return (a: T, b: T): number => {
10
+ if (typeof a === 'string' && typeof b === 'string') {
11
+ return stringComparator(a, b);
12
+ }
13
+
14
+ if (typeof a === 'number' && typeof b === 'number') {
15
+ return numberComparator(a, b);
16
+ }
17
+
18
+ return 0;
19
+ };
20
+ };
21
+
22
+ export default createComparator;
@@ -1,9 +1,11 @@
1
1
  import { Comparator } from 'types';
2
2
 
3
+ import createStringComparator from './createStringComparator';
3
4
  import numberComparator from './numberComparator';
4
- import stringComparator from './stringComparator';
5
5
 
6
- const createKeyComparator = <T extends Record<keyof T, unknown>>(key: keyof T): Comparator<T> => {
6
+ const createKeyComparator = <T extends Record<keyof T, unknown>>(key: keyof T, locale: string): Comparator<T> => {
7
+ const stringComparator = createStringComparator(locale);
8
+
7
9
  return (a: T, b: T): number => {
8
10
  const aValue = a[key];
9
11
  const bValue = b[key];
@@ -0,0 +1,5 @@
1
+ import { Comparator } from 'types';
2
+
3
+ const createStringComparator: (locale: string) => Comparator<string> = (locale) => (a, b) => a.localeCompare(b, locale);
4
+
5
+ export default createStringComparator;
@@ -9,6 +9,10 @@ const detectLocale = (): Locale => {
9
9
  return Locale.EN_GB;
10
10
  }
11
11
 
12
+ if (window.navigator.languages.includes('fa') || window.navigator.languages.includes('fa-IR')) {
13
+ return Locale.FA_IR;
14
+ }
15
+
12
16
  if (window.navigator.languages.includes('fr-FR')) {
13
17
  return Locale.FR_FR;
14
18
  }
@@ -5,7 +5,7 @@ import { RemainingTile } from 'types';
5
5
 
6
6
  import createKeyComparator from './createKeyComparator';
7
7
 
8
- const getRemainingTiles = (config: Config, board: Board, characters: string[]): RemainingTile[] => {
8
+ const getRemainingTiles = (config: Config, board: Board, characters: string[], locale: string): RemainingTile[] => {
9
9
  const nonEmptyCells = board.rows.flat().filter((cell) => !cell.isEmpty);
10
10
  const letterCells = nonEmptyCells.filter((cell) => !cell.tile.isBlank);
11
11
  const remainingTiles = Object.fromEntries(config.tiles.map((tile) => [tile.character, { ...tile, usedCount: 0 }]));
@@ -36,7 +36,9 @@ const getRemainingTiles = (config: Config, board: Board, characters: string[]):
36
36
  ++remainingTiles[letter].usedCount;
37
37
  }
38
38
 
39
- return [...Object.values(remainingTiles).sort(createKeyComparator('character')), blank];
39
+ const comparator = createKeyComparator('character', locale);
40
+
41
+ return [...Object.values(remainingTiles).sort(comparator), blank];
40
42
  };
41
43
 
42
44
  export default getRemainingTiles;
@@ -5,39 +5,64 @@ import { RemainingTile, RemainingTilesGroup } from 'types';
5
5
  import getRemainingTilesCount from './getRemainingTilesCount';
6
6
  import getTotalRemainingTilesCount from './getTotalRemainingTilesCount';
7
7
 
8
- export const getRemainingTilesGroups = (remainingTiles: RemainingTile[]): RemainingTilesGroup[] => {
9
- const consonants = remainingTiles.filter(({ character }) => CONSONANTS.includes(character));
10
- const vowels = remainingTiles.filter(({ character }) => VOWELS.includes(character));
11
- const twoCharacterTiles = remainingTiles.filter(({ character }) => character.length === 2);
12
- const blanks = remainingTiles.filter(({ character }) => character === BLANK);
13
- const groups: RemainingTilesGroup[] = [
14
- {
8
+ const getRemainingTilesGroups = (remainingTiles: RemainingTile[]): RemainingTilesGroup[] => {
9
+ const consonants = remainingTiles.filter(isConsonant);
10
+ const vowels = remainingTiles.filter(isVowel);
11
+ const groups: RemainingTilesGroup[] = [];
12
+
13
+ if (consonants.length + vowels.length > 0) {
14
+ groups.push({
15
15
  remainingCount: getRemainingTilesCount(vowels),
16
16
  tiles: vowels,
17
17
  translationKey: 'common.vowels',
18
18
  totalCount: getTotalRemainingTilesCount(vowels),
19
- },
20
- {
19
+ });
20
+
21
+ groups.push({
21
22
  remainingCount: getRemainingTilesCount(consonants),
22
23
  tiles: consonants,
23
24
  translationKey: 'common.consonants',
24
25
  totalCount: getTotalRemainingTilesCount(consonants),
25
- },
26
- {
27
- remainingCount: getRemainingTilesCount(twoCharacterTiles),
28
- tiles: twoCharacterTiles,
29
- translationKey: 'common.two-letter-tiles',
30
- totalCount: getTotalRemainingTilesCount(twoCharacterTiles),
31
- },
32
- {
33
- remainingCount: getRemainingTilesCount(blanks),
34
- tiles: blanks,
35
- translationKey: 'common.blanks',
36
- totalCount: getTotalRemainingTilesCount(blanks),
37
- },
38
- ];
26
+ });
27
+ } else {
28
+ const tiles = remainingTiles.filter(isLetter);
29
+
30
+ groups.push({
31
+ remainingCount: getRemainingTilesCount(tiles),
32
+ tiles,
33
+ translationKey: 'common.tiles',
34
+ totalCount: getTotalRemainingTilesCount(tiles),
35
+ });
36
+ }
37
+
38
+ const twoCharacterTiles = remainingTiles.filter(isTwoCharacter);
39
+ const blanks = remainingTiles.filter(isBlank);
40
+
41
+ groups.push({
42
+ remainingCount: getRemainingTilesCount(twoCharacterTiles),
43
+ tiles: twoCharacterTiles,
44
+ translationKey: 'common.two-letter-tiles',
45
+ totalCount: getTotalRemainingTilesCount(twoCharacterTiles),
46
+ });
47
+
48
+ groups.push({
49
+ remainingCount: getRemainingTilesCount(blanks),
50
+ tiles: blanks,
51
+ translationKey: 'common.blanks',
52
+ totalCount: getTotalRemainingTilesCount(blanks),
53
+ });
39
54
 
40
55
  return groups.filter(({ totalCount }) => totalCount > 0);
41
56
  };
42
57
 
58
+ const isConsonant = (tile: RemainingTile): boolean => CONSONANTS.includes(tile.character);
59
+
60
+ const isVowel = (tile: RemainingTile): boolean => VOWELS.includes(tile.character);
61
+
62
+ const isLetter = (tile: RemainingTile): boolean => !isBlank(tile) && !isTwoCharacter(tile);
63
+
64
+ const isTwoCharacter = (tile: RemainingTile): boolean => tile.character.length === 2;
65
+
66
+ const isBlank = (tile: RemainingTile): boolean => tile.character === BLANK;
67
+
43
68
  export default getRemainingTilesGroups;
package/src/lib/index.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  export { default as canUseDom } from './canUseDom';
2
- export { default as comparator } from './comparator';
3
2
  export { default as createArray } from './createArray';
3
+ export { default as createComparator } from './createComparator';
4
4
  export { default as createGridOf } from './createGridOf';
5
5
  export { default as createKeyboardNavigation } from './createKeyboardNavigation';
6
6
  export { default as createKeyComparator } from './createKeyComparator';
7
7
  export { default as createNullMovingComparator } from './createNullMovingComparator';
8
+ export { default as createStringComparator } from './createStringComparator';
8
9
  export { default as detectLocale } from './detectLocale';
9
10
  export { default as extractCharacters } from './extractCharacters';
10
11
  export { default as extractInputValue } from './extractInputValue';
@@ -25,6 +26,5 @@ export { default as noop } from './noop';
25
26
  export { default as numberComparator } from './numberComparator';
26
27
  export { default as reverseComparator } from './reverseComparator';
27
28
  export { default as sortResults } from './sortResults';
28
- export { default as stringComparator } from './stringComparator';
29
29
  export { default as unorderedArraysEqual } from './unorderedArraysEqual';
30
30
  export { default as zipCharactersAndTiles } from './zipCharactersAndTiles';
@@ -5,26 +5,28 @@ import { Comparator, ResultColumn, SortDirection } from 'types';
5
5
  import createKeyComparator from './createKeyComparator';
6
6
  import reverseComparator from './reverseComparator';
7
7
 
8
- const comparators: Record<ResultColumn, Comparator<Result>> = {
9
- [ResultColumn.BlanksCount]: createKeyComparator('blanksCount'),
10
- [ResultColumn.ConsonantsCount]: createKeyComparator('consonantsCount'),
11
- [ResultColumn.Points]: createKeyComparator('points'),
12
- [ResultColumn.TilesCount]: createKeyComparator('tilesCount'),
13
- [ResultColumn.VowelsCount]: createKeyComparator('vowelsCount'),
14
- [ResultColumn.Word]: createKeyComparator('word'),
15
- [ResultColumn.WordsCount]: createKeyComparator('wordsCount'),
8
+ const comparators: Record<ResultColumn, (locale: string) => Comparator<Result>> = {
9
+ [ResultColumn.BlanksCount]: (locale: string) => createKeyComparator('blanksCount', locale),
10
+ [ResultColumn.ConsonantsCount]: (locale: string) => createKeyComparator('consonantsCount', locale),
11
+ [ResultColumn.Points]: (locale: string) => createKeyComparator('points', locale),
12
+ [ResultColumn.TilesCount]: (locale: string) => createKeyComparator('tilesCount', locale),
13
+ [ResultColumn.VowelsCount]: (locale: string) => createKeyComparator('vowelsCount', locale),
14
+ [ResultColumn.Word]: (locale: string) => createKeyComparator('word', locale),
15
+ [ResultColumn.WordsCount]: (locale: string) => createKeyComparator('wordsCount', locale),
16
16
  };
17
17
 
18
18
  const sortResults = (
19
19
  results: Result[] | undefined,
20
20
  column: ResultColumn,
21
21
  sortDirection: SortDirection,
22
+ locale: string,
22
23
  ): Result[] | undefined => {
23
24
  if (typeof results === 'undefined') {
24
25
  return results;
25
26
  }
26
27
 
27
- const comparator = comparators[column];
28
+ const createComparator = comparators[column];
29
+ const comparator = createComparator(locale);
28
30
  const finalComparator = sortDirection === SortDirection.Descending ? reverseComparator(comparator) : comparator;
29
31
  return [...results].sort(finalComparator);
30
32
  };
@@ -1,10 +1,11 @@
1
- import comparator from './comparator';
1
+ import createComparator from './createComparator';
2
2
 
3
- const unorderedArraysEqual = <T>(a: T[], b: T[]): boolean => {
3
+ const unorderedArraysEqual = <T>(a: T[], b: T[], locale: string): boolean => {
4
4
  if (a.length !== b.length) {
5
5
  return false;
6
6
  }
7
7
 
8
+ const comparator = createComparator(locale);
8
9
  const aSorted = [...a].sort(comparator);
9
10
  const bSorted = [...b].sort(comparator);
10
11
  return aSorted.every((character, index) => character === bSorted[index]);
@@ -30,7 +30,7 @@ const verify = async (request: NextApiRequest, response: NextApiResponse): Promi
30
30
  });
31
31
 
32
32
  const trie = await dictionaries.get(locale);
33
- const words = board.getWords().sort((a, b) => a.localeCompare(b));
33
+ const words = board.getWords().sort((a, b) => a.localeCompare(b, locale));
34
34
  const invalidWords = words.filter((word) => !trie.has(word));
35
35
  const validWords = words.filter((word) => trie.has(word));
36
36
  response.status(200).send({ invalidWords, validWords });
@@ -58,6 +58,11 @@
58
58
  align-items: center;
59
59
  justify-content: center;
60
60
  height: 100%;
61
+ gap: var(--spacing--xl);
62
+
63
+ @include tablet {
64
+ gap: var(--spacing--l);
65
+ }
61
66
  }
62
67
 
63
68
  .boardContainer {
@@ -68,27 +73,11 @@
68
73
  .sidebar {
69
74
  display: flex;
70
75
  flex-direction: column;
71
- padding-left: var(--spacing--xl);
72
76
  flex: 0 0 var(--sidebar--width);
73
-
74
- > * {
75
- margin-bottom: var(--spacing--xl);
76
-
77
- &:last-child {
78
- margin-bottom: 0;
79
- }
80
- }
77
+ gap: var(--spacing--xl);
81
78
 
82
79
  @include tablet {
83
- padding-left: var(--spacing--l);
84
-
85
- > * {
86
- margin-bottom: var(--spacing--l);
87
-
88
- &:last-child {
89
- margin-bottom: 0;
90
- }
91
- }
80
+ gap: var(--spacing--l);
92
81
  }
93
82
  }
94
83
 
@@ -18,14 +18,16 @@ import {
18
18
  Results,
19
19
  Settings,
20
20
  Splash,
21
+ SvgFontFix,
21
22
  Well,
22
23
  Words,
23
24
  } from 'components';
24
- import { useIsTablet, useLocalStorage } from 'hooks';
25
+ import { useDirection, useIsTablet, useLanguage, useLocalStorage } from 'hooks';
26
+ import { LOCALE_FEATURES } from 'i18n';
25
27
  import { getCellSize } from 'lib';
26
28
  import { COMPONENTS_SPACING, COMPONENTS_SPACING_MOBILE, DICTIONARY_HEIGHT } from 'parameters';
27
29
  import { registerServiceWorker } from 'serviceWorkerManager';
28
- import { initialize, localStorage, reset, selectConfig, solveSlice, useTypedSelector } from 'state';
30
+ import { initialize, localStorage, reset, selectConfig, selectLocale, solveSlice, useTypedSelector } from 'state';
29
31
 
30
32
  import styles from './index.module.scss';
31
33
 
@@ -37,6 +39,7 @@ interface Props {
37
39
 
38
40
  const Index: FunctionComponent<Props> = ({ version }) => {
39
41
  const dispatch = useDispatch();
42
+ const locale = useTypedSelector(selectLocale);
40
43
  const isTablet = useIsTablet();
41
44
  const [showKeyMap, setShowKeyMap] = useState(false);
42
45
  const [showRemainingTiles, setShowRemainingTiles] = useState(false);
@@ -68,6 +71,9 @@ const Index: FunctionComponent<Props> = ({ version }) => {
68
71
  }
69
72
  };
70
73
 
74
+ const { direction } = LOCALE_FEATURES[locale];
75
+ useDirection(direction);
76
+ useLanguage(locale);
71
77
  useLocalStorage();
72
78
 
73
79
  useEffectOnce(() => {
@@ -86,6 +92,8 @@ const Index: FunctionComponent<Props> = ({ version }) => {
86
92
 
87
93
  return (
88
94
  <>
95
+ <SvgFontFix />
96
+
89
97
  <div className={classNames(styles.index, { [styles.initialized]: isInitialized })}>
90
98
  <div className={styles.nav}>
91
99
  <div className={styles.navLogo}>
@@ -22,7 +22,7 @@ const routeVerifyRequests = () => {
22
22
  }
23
23
 
24
24
  const board = Board.fromJson(boardJson);
25
- const words = board.getWords().sort((a, b) => a.localeCompare(b));
25
+ const words = board.getWords().sort((a, b) => a.localeCompare(b, locale));
26
26
  const invalidWords = words.filter((word) => !trie.has(word));
27
27
  const validWords = words.filter((word) => trie.has(word));
28
28
  const json = JSON.stringify({ invalidWords, validWords });
@@ -63,7 +63,7 @@ export const selectResultsSortColumn = createSelector([selectResultsRoot], (resu
63
63
  export const selectResultsSortDirection = createSelector([selectResultsRoot], (results) => results.sort.direction);
64
64
 
65
65
  export const selectSortedResults = createSelector(
66
- [selectResults, selectResultsSortColumn, selectResultsSortDirection],
66
+ [selectResults, selectResultsSortColumn, selectResultsSortDirection, selectLocale],
67
67
  sortResults,
68
68
  );
69
69
 
@@ -167,9 +167,9 @@ export const selectSolveError = createSelector([selectSolveRoot], (solve) => {
167
167
  });
168
168
 
169
169
  export const selectHaveCharactersChanged = createSelector(
170
- [selectLastSolvedParameters, selectCharacters],
171
- (lastSolvedParameters, characters) => {
172
- return !unorderedArraysEqual(lastSolvedParameters.characters, characters);
170
+ [selectLastSolvedParameters, selectCharacters, selectLocale],
171
+ (lastSolvedParameters, characters, locale) => {
172
+ return !unorderedArraysEqual(lastSolvedParameters.characters, characters, locale);
173
173
  },
174
174
  );
175
175
 
@@ -184,7 +184,7 @@ export const selectAreResultsOutdated = createSelector(
184
184
  );
185
185
 
186
186
  export const selectRemainingTiles = createSelector(
187
- [selectConfig, selectBoardRoot, selectCharacters],
187
+ [selectConfig, selectBoardRoot, selectCharacters, selectLocale],
188
188
  getRemainingTiles,
189
189
  );
190
190