@scrabble-solver/scrabble-solver 2.18.0 → 2.18.2
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.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +15 -15
- package/.next/cache/.previewinfo +1 -1
- package/.next/cache/.rscinfo +1 -1
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/.next/diagnostics/framework.json +1 -1
- package/.next/dynamic-css-manifest.json +2 -2
- package/.next/prerender-manifest.json +4 -4
- package/.next/react-loadable-manifest.json +37 -37
- package/.next/routes-manifest.json +1 -1
- package/.next/server/chunks/309.js +1 -1
- package/.next/server/chunks/344.js +1 -1
- package/.next/server/chunks/371.js +1 -1
- package/.next/server/chunks/392.js +1 -1
- package/.next/server/chunks/465.js +1 -1
- package/.next/server/chunks/546.js +1 -1
- package/.next/server/chunks/641.js +1 -1
- package/.next/server/chunks/67.js +1 -0
- package/.next/server/chunks/712.js +1 -1
- package/.next/server/chunks/846.js +1 -1
- package/.next/server/chunks/886.js +1 -1
- package/.next/server/chunks/942.js +1 -0
- package/.next/server/chunks/953.js +1 -1
- package/.next/server/chunks/961.js +1 -1
- package/.next/server/dynamic-css-manifest.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/404.html +3 -2
- package/.next/server/pages/500.html +3 -2
- package/.next/server/pages/_app.js +1 -1
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/_document.js +2 -2
- package/.next/server/pages/_document.js.nft.json +1 -1
- package/.next/server/pages/_error.js +2 -2
- package/.next/server/pages/_error.js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale]/[word].js +1 -1
- package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale].js +1 -1
- package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
- package/.next/server/pages/api/solve.js +1 -1
- package/.next/server/pages/api/solve.js.nft.json +1 -1
- package/.next/server/pages/api/verify.js +1 -1
- package/.next/server/pages/api/verify.js.nft.json +1 -1
- package/.next/server/pages/index.html +3 -2
- package/.next/server/pages/index.js +4 -4
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/index.json +1 -1
- package/.next/server/pages/not-found.html +3 -2
- package/.next/server/pages/not-found.js.nft.json +1 -1
- package/.next/static/chunks/100.305bdf9c75b1758f.js +1 -0
- package/.next/static/chunks/256.66ff9fb6d7db9ec7.js +1 -0
- package/.next/static/chunks/{307.67f52f13e3d60d4e.js → 307.89e6100f58443679.js} +1 -1
- package/.next/static/chunks/{363.01eed3233f92b6a4.js → 363.cec65b1f9668a75e.js} +1 -1
- package/.next/static/chunks/{422.1559ae6756912ca5.js → 422.3e55b4c94e328701.js} +1 -1
- package/.next/static/chunks/{663.d2c9ca36b26695b8.js → 663.bd2c45413fbca518.js} +1 -1
- package/.next/static/chunks/693.677c88e0eb1340b5.js +1 -0
- package/.next/static/chunks/{820.97b82f751ca49579.js → 820.823e9e885ebb66af.js} +1 -1
- package/.next/static/chunks/827.c81b92b383a748c9.js +1 -0
- package/.next/static/chunks/{865.a9b563a45848a83c.js → 865.59baba8296c9b116.js} +1 -1
- package/.next/static/chunks/912.077603e4f0bc6cca.js +1 -0
- package/.next/static/chunks/{939.19b33a3d3c351d73.js → 939.2ad86b50405c7897.js} +1 -1
- package/.next/static/chunks/980.7ce17464fc56648a.js +1 -0
- package/.next/static/chunks/{main-b92e08f3fc96dd8d.js → main-7e52da35a4beea0b.js} +1 -1
- package/.next/static/chunks/pages/_app-f9ec81278ffa7b1a.js +1 -0
- package/.next/static/chunks/pages/index-b5bf018100015277.js +1 -0
- package/.next/static/chunks/pages/not-found-a59f9deeeff63d17.js +1 -0
- package/.next/static/chunks/webpack-9f5c9d275348829b.js +1 -0
- package/.next/static/css/65bcc9440e067113.css +1 -0
- package/.next/static/css/{abc2425d3ba607bc.css → 92f85ce358a0a468.css} +2 -1
- package/.next/static/css/a691348b788f8bc0.css +1 -0
- package/.next/static/css/cd4d7f2458487399.css +1 -0
- package/.next/static/{FL82obwzFHRzvV6z76I-U → nvGZh1gPuX98L07YmxlYD}/_buildManifest.js +1 -1
- package/.next/trace +21 -21
- package/.next/trace-build +1 -1
- package/AGENTS.md +9 -0
- package/CLAUDE.md +1 -0
- package/package.json +23 -14
- package/public/robots.txt +1 -0
- package/public/site.webmanifest +14 -0
- package/src/components/Board/Board.tsx +3 -1
- package/src/components/Board/BoardPure.tsx +26 -20
- package/src/components/Board/components/Cell/Cell.tsx +4 -4
- package/src/components/Board/lib.test.ts +106 -0
- package/src/components/Board/lib.ts +34 -3
- package/src/components/Board/selectors.ts +17 -5
- package/src/components/Button/Button.module.scss +1 -1
- package/src/components/Modal/Modal.module.scss +0 -4
- package/src/components/Results/Header.tsx +30 -5
- package/src/components/Results/Result.tsx +45 -55
- package/src/components/Results/Results.module.scss +0 -126
- package/src/components/Results/Results.module.scss.d.ts +0 -10
- package/src/components/Results/Results.tsx +25 -5
- package/src/components/Solver/Solver.module.scss +4 -4
- package/src/components/Solver/Solver.tsx +5 -3
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss +5 -1
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.module.scss.d.ts +1 -0
- package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +5 -2
- package/src/components/Table/Cell.tsx +42 -0
- package/src/components/Table/Header.tsx +11 -0
- package/src/components/{Results → Table}/HeaderButton.tsx +22 -14
- package/src/components/Table/Row.tsx +59 -0
- package/src/components/{ResultsInput/ResultsInput.tsx → Table/Search.tsx} +9 -11
- package/src/components/Table/Table.module.scss +147 -0
- package/src/components/Table/Table.module.scss.d.ts +19 -0
- package/src/components/Table/index.ts +5 -0
- package/src/components/Tile/Tile.module.scss +2 -1
- package/src/hooks/useColumns.ts +7 -5
- package/src/hooks/useIsCompactLayout.ts +14 -0
- package/src/i18n/languages/english.json +6 -1
- package/src/i18n/languages/french.json +6 -1
- package/src/i18n/languages/german.json +6 -1
- package/src/i18n/languages/persian.json +6 -1
- package/src/i18n/languages/polish.json +6 -1
- package/src/i18n/languages/romanian.json +6 -1
- package/src/i18n/languages/spanish.json +6 -1
- package/src/i18n/languages/turkish.json +6 -1
- package/src/icons/QuestionSquare.svg +5 -0
- package/src/lib/createCoordinatesComparator.ts +23 -0
- package/src/lib/createKeyComparator.ts +1 -4
- package/src/lib/createSortComparator.ts +22 -0
- package/src/lib/getCoordinates.test.ts +21 -0
- package/src/lib/getCoordinates.ts +16 -0
- package/src/lib/getNextSort.ts +12 -0
- package/src/lib/waitForIdleOrFirstIntent.ts +19 -8
- package/src/modals/RemainingTilesModal/RemainingTilesModal.tsx +3 -1
- package/src/modals/RemainingTilesModal/components/Character/Character.tsx +8 -9
- package/src/modals/ResultsModal/ResultsModal.tsx +14 -10
- package/src/modals/WordsModal/WordsModal.module.scss +16 -27
- package/src/modals/WordsModal/WordsModal.module.scss.d.ts +3 -6
- package/src/modals/WordsModal/WordsModal.tsx +56 -42
- package/src/modals/WordsModal/components/WordsTable/WordRow.tsx +92 -0
- package/src/modals/WordsModal/components/WordsTable/WordsTable.module.scss +69 -0
- package/src/modals/WordsModal/components/WordsTable/WordsTable.module.scss.d.ts +16 -0
- package/src/modals/WordsModal/components/WordsTable/WordsTable.tsx +137 -0
- package/src/modals/WordsModal/components/WordsTable/index.ts +1 -0
- package/src/modals/WordsModal/components/index.ts +1 -0
- package/src/pages/_app.tsx +40 -76
- package/src/pages/_document.tsx +21 -19
- package/src/pages/api/verify.ts +7 -6
- package/src/pages/index.tsx +58 -6
- package/src/pages/not-found.tsx +13 -2
- package/src/parameters/index.ts +9 -0
- package/src/sdk/verify.ts +3 -2
- package/src/solver-worker/client.test.ts +10 -3
- package/src/solver-worker/index.worker.ts +7 -7
- package/src/solver-worker/messages.ts +3 -3
- package/src/state/hoveredWord/index.ts +4 -0
- package/src/state/hoveredWord/initialState.ts +5 -0
- package/src/state/hoveredWord/selectors.ts +5 -0
- package/src/state/hoveredWord/slice.ts +18 -0
- package/src/state/hoveredWord/types.ts +5 -0
- package/src/state/index.ts +1 -0
- package/src/state/localStorage.ts +13 -2
- package/src/state/results/lib.ts +9 -26
- package/src/state/results/selectors.ts +9 -1
- package/src/state/results/slice.ts +4 -16
- package/src/state/sagas.ts +30 -5
- package/src/state/selectors.ts +18 -1
- package/src/state/store.ts +2 -0
- package/src/state/types.ts +2 -0
- package/src/state/verify/initialState.ts +7 -0
- package/src/state/verify/lib.test.ts +55 -0
- package/src/state/verify/lib.ts +49 -0
- package/src/state/verify/selectors.ts +37 -0
- package/src/state/verify/slice.ts +13 -2
- package/src/state/verify/types.ts +6 -2
- package/src/styles/_tokens.scss +2 -0
- package/src/styles/mixins.scss +5 -0
- package/src/styles/variables.module.scss +1 -0
- package/src/styles/variables.module.scss.d.ts +1 -0
- package/src/styles/variables.scss +1 -1
- package/src/types/index.ts +24 -11
- package/tsconfig.tsbuildinfo +1 -1
- package/.next/server/chunks/316.js +0 -1
- package/.next/server/chunks/916.js +0 -1
- package/.next/static/chunks/106.e414e88694add27f.js +0 -1
- package/.next/static/chunks/127.125d896a19061d71.js +0 -1
- package/.next/static/chunks/18.8b91e137d239e15c.js +0 -1
- package/.next/static/chunks/583.7a46807be14c377e.js +0 -1
- package/.next/static/chunks/711.ba7b8f900dfd40d0.js +0 -1
- package/.next/static/chunks/912.ff8037ba9d4db5c8.js +0 -1
- package/.next/static/chunks/pages/_app-ba5d628e81a15461.js +0 -1
- package/.next/static/chunks/pages/index-5f9fa6ac3cd3d97b.js +0 -1
- package/.next/static/chunks/pages/not-found-23d61fd3ed791cad.js +0 -1
- package/.next/static/chunks/webpack-e34c3ae12275e978.js +0 -1
- package/.next/static/css/b74cd7de82dabcdd.css +0 -1
- package/.next/static/css/c403ce739a9c0d83.css +0 -1
- package/.next/static/css/fbfdce70731d0d2b.css +0 -1
- package/src/components/Results/Cell.tsx +0 -32
- package/src/components/ResultsInput/ResultsInput.module.scss +0 -12
- package/src/components/ResultsInput/ResultsInput.module.scss.d.ts +0 -7
- package/src/components/ResultsInput/index.ts +0 -1
- package/src/components/SeoMessage/SeoMessage.tsx +0 -18
- package/src/components/SeoMessage/index.ts +0 -1
- /package/.next/static/{FL82obwzFHRzvV6z76I-U → nvGZh1gPuX98L07YmxlYD}/_ssgManifest.js +0 -0
package/src/pages/_app.tsx
CHANGED
|
@@ -4,85 +4,49 @@ import Head from 'next/head';
|
|
|
4
4
|
import { type FunctionComponent } from 'react';
|
|
5
5
|
import { Provider } from 'react-redux';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { SITE_DESCRIPTION, SITE_URL } from '@/parameters';
|
|
8
8
|
import { store } from '@/state';
|
|
9
9
|
|
|
10
10
|
import '../styles/global.scss';
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
'SJP',
|
|
52
|
-
'FISE-2017',
|
|
53
|
-
'FISE-2',
|
|
54
|
-
'CNRTL',
|
|
55
|
-
'Kamil Mielnik',
|
|
56
|
-
].join(',');
|
|
57
|
-
|
|
58
|
-
const App: FunctionComponent<AppProps> = ({ Component, pageProps }) => (
|
|
59
|
-
<>
|
|
60
|
-
<Head>
|
|
61
|
-
<title>Scrabble Solver 2</title>
|
|
62
|
-
<meta charSet="utf-8" />
|
|
63
|
-
<meta name="author" content="Kamil Mielnik" />
|
|
64
|
-
<meta name="description" content={DESCRIPTION} />
|
|
65
|
-
<meta name="keywords" content={KEYWORDS} />
|
|
66
|
-
<meta name="robots" content="index, follow, notranslate, noimageindex" />
|
|
67
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
68
|
-
<meta property="og:title" content={DESCRIPTION} />
|
|
69
|
-
<meta property="og:type" content="website" />
|
|
70
|
-
<meta property="og:url" content="https://scrabble-solver.org" />
|
|
71
|
-
<meta property="og:image" content="https://scrabble-solver.org/og.png" />
|
|
72
|
-
<meta property="og:image:type" content="image/png" />
|
|
73
|
-
<meta property="og:image:width" content="1200" />
|
|
74
|
-
<meta property="og:image:height" content="630" />
|
|
75
|
-
<meta property="og:image:alt" content={DESCRIPTION} />
|
|
76
|
-
</Head>
|
|
77
|
-
|
|
78
|
-
<Provider store={store}>
|
|
79
|
-
<SeoMessage />
|
|
80
|
-
|
|
81
|
-
<FloatingDelayGroup delay={0}>
|
|
82
|
-
<Component {...pageProps} />
|
|
83
|
-
</FloatingDelayGroup>
|
|
84
|
-
</Provider>
|
|
85
|
-
</>
|
|
86
|
-
);
|
|
12
|
+
const TITLE = 'Scrabble Solver 2 - Board Solver & Word Finder';
|
|
13
|
+
|
|
14
|
+
const App: FunctionComponent<AppProps> = ({ Component, pageProps, router }) => {
|
|
15
|
+
const isIndexable = router.pathname === '/';
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<Head>
|
|
20
|
+
<title>{TITLE}</title>
|
|
21
|
+
<meta charSet="utf-8" />
|
|
22
|
+
<meta name="author" content="Kamil Mielnik" />
|
|
23
|
+
<meta name="description" content={SITE_DESCRIPTION} />
|
|
24
|
+
{isIndexable ? <link rel="canonical" href={`${SITE_URL}/`} /> : <meta name="robots" content="noindex" />}
|
|
25
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
26
|
+
<meta property="og:site_name" content="Scrabble Solver" />
|
|
27
|
+
<meta property="og:title" content={TITLE} />
|
|
28
|
+
<meta property="og:description" content={SITE_DESCRIPTION} />
|
|
29
|
+
<meta property="og:type" content="website" />
|
|
30
|
+
<meta property="og:url" content={`${SITE_URL}/`} />
|
|
31
|
+
<meta property="og:locale" content="en_US" />
|
|
32
|
+
<meta property="og:image" content={`${SITE_URL}/og.png`} />
|
|
33
|
+
<meta property="og:image:type" content="image/png" />
|
|
34
|
+
<meta property="og:image:width" content="1200" />
|
|
35
|
+
<meta property="og:image:height" content="630" />
|
|
36
|
+
<meta property="og:image:alt" content="Scrabble tiles spelling out Scrabble Solver 2" />
|
|
37
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
38
|
+
<meta name="twitter:title" content={TITLE} />
|
|
39
|
+
<meta name="twitter:description" content={SITE_DESCRIPTION} />
|
|
40
|
+
<meta name="twitter:image" content={`${SITE_URL}/og.png`} />
|
|
41
|
+
</Head>
|
|
42
|
+
|
|
43
|
+
<Provider store={store}>
|
|
44
|
+
<FloatingDelayGroup delay={0}>
|
|
45
|
+
<Component {...pageProps} />
|
|
46
|
+
</FloatingDelayGroup>
|
|
47
|
+
</Provider>
|
|
48
|
+
</>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
87
51
|
|
|
88
52
|
export default App;
|
package/src/pages/_document.tsx
CHANGED
|
@@ -70,27 +70,29 @@ export default class MyDocument extends Document {
|
|
|
70
70
|
<InlineCssHead>
|
|
71
71
|
{/* eslint-disable-next-line react/no-danger */}
|
|
72
72
|
<script dangerouslySetInnerHTML={{ __html: preHydrationScript }} />
|
|
73
|
-
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="icons/apple-touch-icon-57x57.png" />
|
|
74
|
-
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="icons/apple-touch-icon-114x114.png" />
|
|
75
|
-
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="icons/apple-touch-icon-72x72.png" />
|
|
76
|
-
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="icons/apple-touch-icon-144x144.png" />
|
|
77
|
-
<link rel="apple-touch-icon-precomposed" sizes="60x60" href="icons/apple-touch-icon-60x60.png" />
|
|
78
|
-
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="icons/apple-touch-icon-120x120.png" />
|
|
79
|
-
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="icons/apple-touch-icon-76x76.png" />
|
|
80
|
-
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="icons/apple-touch-icon-152x152.png" />
|
|
81
|
-
<link rel="icon" type="image/png" href="icons/favicon-196x196.png" sizes="196x196" />
|
|
82
|
-
<link rel="icon" type="image/png" href="icons/favicon-96x96.png" sizes="96x96" />
|
|
83
|
-
<link rel="icon" type="image/png" href="icons/favicon-32x32.png" sizes="32x32" />
|
|
84
|
-
<link rel="icon" type="image/png" href="icons/favicon-16x16.png" sizes="16x16" />
|
|
85
|
-
<link rel="icon" type="image/png" href="icons/favicon-128.png" sizes="128x128" />
|
|
86
|
-
<link rel="icon" href="icons/favicon.svg" />
|
|
73
|
+
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="/icons/apple-touch-icon-57x57.png" />
|
|
74
|
+
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/icons/apple-touch-icon-114x114.png" />
|
|
75
|
+
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/icons/apple-touch-icon-72x72.png" />
|
|
76
|
+
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/icons/apple-touch-icon-144x144.png" />
|
|
77
|
+
<link rel="apple-touch-icon-precomposed" sizes="60x60" href="/icons/apple-touch-icon-60x60.png" />
|
|
78
|
+
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="/icons/apple-touch-icon-120x120.png" />
|
|
79
|
+
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="/icons/apple-touch-icon-76x76.png" />
|
|
80
|
+
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="/icons/apple-touch-icon-152x152.png" />
|
|
81
|
+
<link rel="icon" type="image/png" href="/icons/favicon-196x196.png" sizes="196x196" />
|
|
82
|
+
<link rel="icon" type="image/png" href="/icons/favicon-96x96.png" sizes="96x96" />
|
|
83
|
+
<link rel="icon" type="image/png" href="/icons/favicon-32x32.png" sizes="32x32" />
|
|
84
|
+
<link rel="icon" type="image/png" href="/icons/favicon-16x16.png" sizes="16x16" />
|
|
85
|
+
<link rel="icon" type="image/png" href="/icons/favicon-128.png" sizes="128x128" />
|
|
86
|
+
<link rel="icon" href="/icons/favicon.svg" />
|
|
87
|
+
<link rel="manifest" href="/site.webmanifest" />
|
|
87
88
|
<meta name="application-name" content="Scrabble Solver" />
|
|
89
|
+
<meta name="theme-color" content="#EFE3AE" />
|
|
88
90
|
<meta name="msapplication-TileColor" content="#EFE3AE" />
|
|
89
|
-
<meta name="msapplication-TileImage" content="icons/mstile-144x144.png" />
|
|
90
|
-
<meta name="msapplication-square70x70logo" content="icons/mstile-70x70.png" />
|
|
91
|
-
<meta name="msapplication-square150x150logo" content="icons/mstile-150x150.png" />
|
|
92
|
-
<meta name="msapplication-wide310x150logo" content="icons/mstile-310x150.png" />
|
|
93
|
-
<meta name="msapplication-square310x310logo" content="icons/mstile-310x310.png" />
|
|
91
|
+
<meta name="msapplication-TileImage" content="/icons/mstile-144x144.png" />
|
|
92
|
+
<meta name="msapplication-square70x70logo" content="/icons/mstile-70x70.png" />
|
|
93
|
+
<meta name="msapplication-square150x150logo" content="/icons/mstile-150x150.png" />
|
|
94
|
+
<meta name="msapplication-wide310x150logo" content="/icons/mstile-310x150.png" />
|
|
95
|
+
<meta name="msapplication-square310x310logo" content="/icons/mstile-310x310.png" />
|
|
94
96
|
</InlineCssHead>
|
|
95
97
|
|
|
96
98
|
<body>
|
package/src/pages/api/verify.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Board, type Config, type Game, type Locale, isBoardJson, isGame, isLoca
|
|
|
5
5
|
import { type NextApiRequest, type NextApiResponse } from 'next';
|
|
6
6
|
|
|
7
7
|
import { getServerLoggingData, isBoardValid } from '@/api';
|
|
8
|
+
import { type VerifiedWord } from '@/types';
|
|
8
9
|
|
|
9
10
|
interface RequestData {
|
|
10
11
|
board: Board;
|
|
@@ -31,15 +32,15 @@ const verify = async (request: NextApiRequest, response: NextApiResponse): Promi
|
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
const gaddag = await dictionaries.get(locale);
|
|
34
|
-
const words = board.getWords().sort((a, b) => a.localeCompare(b, locale));
|
|
35
|
-
const invalidWords:
|
|
36
|
-
const validWords:
|
|
35
|
+
const words = board.getWords().sort((a, b) => a.word.localeCompare(b.word, locale));
|
|
36
|
+
const invalidWords: VerifiedWord[] = [];
|
|
37
|
+
const validWords: VerifiedWord[] = [];
|
|
37
38
|
|
|
38
39
|
for (const word of words) {
|
|
39
|
-
if (gaddag.has(word)) {
|
|
40
|
-
validWords.push(word);
|
|
40
|
+
if (gaddag.has(word.word)) {
|
|
41
|
+
validWords.push({ ...word, isValid: true });
|
|
41
42
|
} else {
|
|
42
|
-
invalidWords.push(word);
|
|
43
|
+
invalidWords.push({ ...word, isValid: false });
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
package/src/pages/index.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { execSync } from 'child_process';
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import dynamic from 'next/dynamic';
|
|
8
8
|
import path from 'path';
|
|
9
|
-
import { type FunctionComponent, useCallback, useEffect, useState } from 'react';
|
|
9
|
+
import { type FunctionComponent, useCallback, useEffect, useRef, useState } from 'react';
|
|
10
10
|
import { useDispatch } from 'react-redux';
|
|
11
11
|
|
|
12
12
|
import { Logo } from '@/components/Logo';
|
|
@@ -14,13 +14,23 @@ import { NavButtons } from '@/components/NavButtons';
|
|
|
14
14
|
import { Solver } from '@/components/Solver';
|
|
15
15
|
import { useDirection } from '@/hooks/useDirection';
|
|
16
16
|
import { useEffectOnce } from '@/hooks/useEffectOnce';
|
|
17
|
+
import { useIsCompactLayout } from '@/hooks/useIsCompactLayout';
|
|
17
18
|
import { useLanguage } from '@/hooks/useLanguage';
|
|
18
19
|
import { useLocalStorage } from '@/hooks/useLocalStorage';
|
|
19
20
|
import { LOCALE_FEATURES } from '@/i18n/constants';
|
|
20
21
|
import { schedulePreloadModals } from '@/modals/preload';
|
|
21
|
-
import { CONFIG_PENDING_CLASS } from '@/parameters';
|
|
22
|
+
import { CONFIG_PENDING_CLASS, GITHUB_PROJECT_URL, NPM_PACKAGE_URL, SITE_DESCRIPTION, SITE_URL } from '@/parameters';
|
|
22
23
|
import { registerServiceWorker } from '@/serviceWorkerManager';
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
hoveredWordSlice,
|
|
26
|
+
initialize,
|
|
27
|
+
reset,
|
|
28
|
+
resultsSlice,
|
|
29
|
+
selectConfig,
|
|
30
|
+
selectIsHydrated,
|
|
31
|
+
selectLocale,
|
|
32
|
+
useTypedSelector,
|
|
33
|
+
} from '@/state';
|
|
24
34
|
|
|
25
35
|
import styles from './index.module.scss';
|
|
26
36
|
|
|
@@ -49,6 +59,30 @@ const WordsModal = dynamic(() => import('@/modals/WordsModal').then((module) =>
|
|
|
49
59
|
ssr: false,
|
|
50
60
|
});
|
|
51
61
|
|
|
62
|
+
const WEB_APPLICATION_JSON_LD = JSON.stringify({
|
|
63
|
+
'@context': 'https://schema.org',
|
|
64
|
+
'@type': 'WebApplication',
|
|
65
|
+
name: 'Scrabble Solver',
|
|
66
|
+
alternateName: 'Scrabble Solver 2',
|
|
67
|
+
url: `${SITE_URL}/`,
|
|
68
|
+
description: SITE_DESCRIPTION,
|
|
69
|
+
applicationCategory: 'GameApplication',
|
|
70
|
+
operatingSystem: 'Any',
|
|
71
|
+
browserRequirements: 'Requires JavaScript',
|
|
72
|
+
image: `${SITE_URL}/og.png`,
|
|
73
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
74
|
+
inLanguage: ['en-US', 'en-GB', 'de-DE', 'es-ES', 'fa-IR', 'fr-FR', 'pl-PL', 'ro-RO', 'tr-TR'],
|
|
75
|
+
featureList: [
|
|
76
|
+
'Finds the highest-scoring words for a given board and rack',
|
|
77
|
+
'Supports Scrabble, Super Scrabble, Scrabble Duel, Letter League, Crossplay, Literaki, and Kelimelik',
|
|
78
|
+
'Dictionaries and word definitions in 8 languages',
|
|
79
|
+
'Works offline once a dictionary is cached',
|
|
80
|
+
'Free, open source, no ads, no sign-up',
|
|
81
|
+
],
|
|
82
|
+
sameAs: [GITHUB_PROJECT_URL, NPM_PACKAGE_URL],
|
|
83
|
+
author: { '@type': 'Person', name: 'Kamil Mielnik', url: 'https://kamilmielnik.com' },
|
|
84
|
+
});
|
|
85
|
+
|
|
52
86
|
interface Props {
|
|
53
87
|
version: string;
|
|
54
88
|
}
|
|
@@ -60,6 +94,8 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
60
94
|
const config = useTypedSelector(selectConfig);
|
|
61
95
|
const locale = useTypedSelector(selectLocale);
|
|
62
96
|
const isHydrated = useTypedSelector(selectIsHydrated);
|
|
97
|
+
const isCompactLayout = useIsCompactLayout();
|
|
98
|
+
const previousIsCompactLayout = useRef(isCompactLayout);
|
|
63
99
|
const [modals, setModals] = useState<Record<Modal, boolean>>({
|
|
64
100
|
dictionary: false,
|
|
65
101
|
keyMap: false,
|
|
@@ -107,6 +143,7 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
107
143
|
const handleCloseResults = useCallback(() => patchModals({ results: false }), [patchModals]);
|
|
108
144
|
const handleCloseSettings = useCallback(() => patchModals({ settings: false }), [patchModals]);
|
|
109
145
|
const handleCloseWords = useCallback(() => patchModals({ words: false }), [patchModals]);
|
|
146
|
+
const handlePreviewWord = useCallback(() => patchModals({ menu: false, words: false }), [patchModals]);
|
|
110
147
|
|
|
111
148
|
useDirection(LOCALE_FEATURES[locale].direction);
|
|
112
149
|
useLanguage(locale);
|
|
@@ -135,6 +172,16 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
135
172
|
}
|
|
136
173
|
}, [isHydrated]);
|
|
137
174
|
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
if (previousIsCompactLayout.current === isCompactLayout) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
previousIsCompactLayout.current = isCompactLayout;
|
|
181
|
+
dispatch(resultsSlice.actions.changeResultCandidate(null));
|
|
182
|
+
dispatch(hoveredWordSlice.actions.clear());
|
|
183
|
+
}, [dispatch, isCompactLayout]);
|
|
184
|
+
|
|
138
185
|
useEffectOnce(() => {
|
|
139
186
|
if (process.env.NODE_ENV === 'production') {
|
|
140
187
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
@@ -149,11 +196,11 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
149
196
|
<>
|
|
150
197
|
<nav className={styles.nav}>
|
|
151
198
|
<div className={styles.navContent}>
|
|
152
|
-
<
|
|
199
|
+
<h1 className={styles.navLogo}>
|
|
153
200
|
<a className={styles.logoContainer} href="/" title={version}>
|
|
154
201
|
<Logo className={styles.logo} />
|
|
155
202
|
</a>
|
|
156
|
-
</
|
|
203
|
+
</h1>
|
|
157
204
|
|
|
158
205
|
<NavButtons
|
|
159
206
|
onClear={handleClear}
|
|
@@ -170,6 +217,9 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
170
217
|
<Solver className={styles.solver} onShowResults={handleShowResults} />
|
|
171
218
|
</main>
|
|
172
219
|
|
|
220
|
+
{/* eslint-disable-next-line react/no-danger */}
|
|
221
|
+
<script dangerouslySetInnerHTML={{ __html: WEB_APPLICATION_JSON_LD }} type="application/ld+json" />
|
|
222
|
+
|
|
173
223
|
{mountedModals.menu && (
|
|
174
224
|
<MenuModal
|
|
175
225
|
isOpen={modals.menu}
|
|
@@ -185,7 +235,9 @@ const Index: FunctionComponent<Props> = ({ version }) => {
|
|
|
185
235
|
|
|
186
236
|
{mountedModals.keyMap && <KeyMapModal isOpen={modals.keyMap} onClose={handleCloseKeyMap} />}
|
|
187
237
|
|
|
188
|
-
{mountedModals.words &&
|
|
238
|
+
{mountedModals.words && (
|
|
239
|
+
<WordsModal isOpen={modals.words} onClose={handleCloseWords} onPreview={handlePreviewWord} />
|
|
240
|
+
)}
|
|
189
241
|
|
|
190
242
|
{config.supportsRemainingTiles && mountedModals.remainingTiles && (
|
|
191
243
|
<RemainingTilesModal isOpen={modals.remainingTiles} onClose={handleCloseRemainingTiles} />
|
package/src/pages/not-found.tsx
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
'
|
|
1
|
+
import Head from 'next/head';
|
|
2
|
+
import { type FunctionComponent } from 'react';
|
|
2
3
|
|
|
3
4
|
import { NotFound } from '@/components/NotFound';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const NotFoundPage: FunctionComponent = () => (
|
|
7
|
+
<>
|
|
8
|
+
<Head>
|
|
9
|
+
<title>Page not found - Scrabble Solver</title>
|
|
10
|
+
</Head>
|
|
11
|
+
|
|
12
|
+
<NotFound />
|
|
13
|
+
</>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export default NotFoundPage;
|
package/src/parameters/index.ts
CHANGED
|
@@ -8,8 +8,17 @@ export const BREAKPOINTS = {
|
|
|
8
8
|
xl: Number(variables.breakpointXl),
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
export const BREAKPOINT_HEIGHT_L = Number(variables.breakpointHeightL);
|
|
12
|
+
|
|
11
13
|
export const GITHUB_PROJECT_URL = 'https://github.com/kamilmielnik/scrabble-solver';
|
|
12
14
|
|
|
15
|
+
export const NPM_PACKAGE_URL = 'https://www.npmjs.com/package/scrabble-solver';
|
|
16
|
+
|
|
17
|
+
export const SITE_URL = 'https://scrabble-solver.org';
|
|
18
|
+
|
|
19
|
+
export const SITE_DESCRIPTION =
|
|
20
|
+
'Free, open-source board solver and word finder for Scrabble, Letter League, and Literaki. Enter your letters and board to get the highest-scoring words.';
|
|
21
|
+
|
|
13
22
|
// Must match the class name in global.scss
|
|
14
23
|
export const CONFIG_PENDING_CLASS = 'config-pending';
|
|
15
24
|
|
package/src/sdk/verify.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type BoardJson, type Game, type Locale } from '@scrabble-solver/types';
|
|
2
2
|
|
|
3
3
|
import { verifyLocally } from '@/solver-worker';
|
|
4
|
+
import { type VerifiedWord } from '@/types';
|
|
4
5
|
|
|
5
6
|
import { fetchJson } from './fetchJson';
|
|
6
7
|
|
|
@@ -11,8 +12,8 @@ interface Payload {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
interface Response {
|
|
14
|
-
invalidWords:
|
|
15
|
-
validWords:
|
|
15
|
+
invalidWords: VerifiedWord[];
|
|
16
|
+
validWords: VerifiedWord[];
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export const verify = async ({ board, game, locale }: Payload): Promise<Response> => {
|
|
@@ -2,7 +2,7 @@ import { type ResultJson } from '@scrabble-solver/types';
|
|
|
2
2
|
import { afterAll, beforeEach, describe, expect, it } from 'bun:test';
|
|
3
3
|
|
|
4
4
|
import type * as clientModule from './client';
|
|
5
|
-
import { type SolverWorkerRequest, type SolverWorkerResponse } from './messages';
|
|
5
|
+
import { type SolverWorkerRequest, type SolverWorkerResponse, type VerifyResult } from './messages';
|
|
6
6
|
|
|
7
7
|
type Listener = (event: { data: SolverWorkerResponse }) => void;
|
|
8
8
|
|
|
@@ -102,9 +102,9 @@ describe('solver worker client', () => {
|
|
|
102
102
|
|
|
103
103
|
it('resolves a verify with the worker words', async () => {
|
|
104
104
|
const verify = verifyLocally(verifyPayload);
|
|
105
|
-
respond({ data:
|
|
105
|
+
respond({ data: createVerifyResult(), id: lastRequestId(), outcome: 'answered' });
|
|
106
106
|
|
|
107
|
-
expect(await verify).toEqual(
|
|
107
|
+
expect(await verify).toEqual(createVerifyResult());
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
it('does not wait for a prefetch to answer', () => {
|
|
@@ -149,3 +149,10 @@ function lastRequestId(): number {
|
|
|
149
149
|
function createResults(word: string): ResultJson[] {
|
|
150
150
|
return [{ blankIndices: [], id: 0, isHorizontal: true, points: 1, tiles: word.split(''), x: 0, y: 0 }];
|
|
151
151
|
}
|
|
152
|
+
|
|
153
|
+
function createVerifyResult(): VerifyResult {
|
|
154
|
+
return {
|
|
155
|
+
invalidWords: [{ direction: 'horizontal', isValid: false, word: 'zz', x: 0, y: 0 }],
|
|
156
|
+
validWords: [{ direction: 'vertical', isValid: true, word: 'ab', x: 3, y: 5 }],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -5,7 +5,7 @@ import { BLANK } from '@scrabble-solver/constants';
|
|
|
5
5
|
import { solve } from '@scrabble-solver/solver';
|
|
6
6
|
import { Board, type Locale, Tile } from '@scrabble-solver/types';
|
|
7
7
|
|
|
8
|
-
import { type SolveRequestPayload, type VerifyRequestPayload } from '@/types';
|
|
8
|
+
import { type SolveRequestPayload, type VerifiedWord, type VerifyRequestPayload } from '@/types';
|
|
9
9
|
|
|
10
10
|
import { revalidateDictionary } from './dictionaries';
|
|
11
11
|
import { getGaddag } from './getGaddag';
|
|
@@ -64,15 +64,15 @@ async function handleVerify(
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const board = Board.fromJson(boardJson);
|
|
67
|
-
const words = board.getWords().sort((a, b) => a.localeCompare(b, locale));
|
|
68
|
-
const invalidWords:
|
|
69
|
-
const validWords:
|
|
67
|
+
const words = board.getWords().sort((a, b) => a.word.localeCompare(b.word, locale));
|
|
68
|
+
const invalidWords: VerifiedWord[] = [];
|
|
69
|
+
const validWords: VerifiedWord[] = [];
|
|
70
70
|
|
|
71
71
|
for (const word of words) {
|
|
72
|
-
if (gaddag.has(word)) {
|
|
73
|
-
validWords.push(word);
|
|
72
|
+
if (gaddag.has(word.word)) {
|
|
73
|
+
validWords.push({ ...word, isValid: true });
|
|
74
74
|
} else {
|
|
75
|
-
invalidWords.push(word);
|
|
75
|
+
invalidWords.push({ ...word, isValid: false });
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type Locale, type ResultJson } from '@scrabble-solver/types';
|
|
2
2
|
|
|
3
|
-
import { type SolveRequestPayload, type VerifyRequestPayload } from '@/types';
|
|
3
|
+
import { type SolveRequestPayload, type VerifiedWord, type VerifyRequestPayload } from '@/types';
|
|
4
4
|
|
|
5
5
|
export interface VerifyResult {
|
|
6
|
-
invalidWords:
|
|
7
|
-
validWords:
|
|
6
|
+
invalidWords: VerifiedWord[];
|
|
7
|
+
validWords: VerifiedWord[];
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export type SolverWorkerRequest =
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
|
2
|
+
|
|
3
|
+
import { type VerifiedWord } from '@/types';
|
|
4
|
+
|
|
5
|
+
import { hoveredWordInitialState } from './initialState';
|
|
6
|
+
|
|
7
|
+
export const hoveredWordSlice = createSlice({
|
|
8
|
+
initialState: hoveredWordInitialState,
|
|
9
|
+
name: 'hoveredWord',
|
|
10
|
+
reducers: {
|
|
11
|
+
set: (state, action: PayloadAction<VerifiedWord>) => {
|
|
12
|
+
state.word = action.payload;
|
|
13
|
+
},
|
|
14
|
+
clear: (state) => {
|
|
15
|
+
state.word = null;
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
});
|
package/src/state/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Board, type BoardJson, isObject, type Locale } from '@scrabble-solver/types';
|
|
2
2
|
import store2 from 'store2';
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import { englishTranslations } from '@/i18n/i18n';
|
|
5
|
+
import type { Rack, TranslationKey, Translations } from '@/types';
|
|
5
6
|
|
|
6
7
|
import type { SettingsState } from './settings/types';
|
|
7
8
|
|
|
@@ -80,7 +81,13 @@ export const localStorage = {
|
|
|
80
81
|
return undefined;
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
if (
|
|
84
|
+
if (
|
|
85
|
+
!isObject(stored) ||
|
|
86
|
+
stored.locale !== locale ||
|
|
87
|
+
stored.version !== version ||
|
|
88
|
+
!isObject(stored.translations) ||
|
|
89
|
+
!hasEveryTranslation(stored.translations)
|
|
90
|
+
) {
|
|
84
91
|
store.remove(TRANSLATIONS);
|
|
85
92
|
return undefined;
|
|
86
93
|
}
|
|
@@ -93,6 +100,10 @@ export const localStorage = {
|
|
|
93
100
|
},
|
|
94
101
|
};
|
|
95
102
|
|
|
103
|
+
function hasEveryTranslation(translations: Translations): boolean {
|
|
104
|
+
return (Object.keys(englishTranslations) as TranslationKey[]).every((key) => typeof translations[key] === 'string');
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
/**
|
|
97
108
|
* Introduced in 2.15.26 on 2026/04/27.
|
|
98
109
|
* Life expectancy: 1y.
|
package/src/state/results/lib.ts
CHANGED
|
@@ -1,25 +1,15 @@
|
|
|
1
1
|
import { type Result, type ShowCoordinates } from '@scrabble-solver/types';
|
|
2
2
|
|
|
3
|
+
import { createCoordinatesComparator } from '@/lib/createCoordinatesComparator';
|
|
3
4
|
import { createKeyComparator } from '@/lib/createKeyComparator';
|
|
4
5
|
import { createRegExp } from '@/lib/createRegExp';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
type CellFilter,
|
|
10
|
-
type Comparator,
|
|
11
|
-
type GroupedResults,
|
|
12
|
-
ResultColumnId,
|
|
13
|
-
type Sort,
|
|
14
|
-
SortDirection,
|
|
15
|
-
} from '@/types';
|
|
6
|
+
import { createSortComparator } from '@/lib/createSortComparator';
|
|
7
|
+
import { getCoordinates as getPointCoordinates } from '@/lib/getCoordinates';
|
|
8
|
+
import { type CellFilter, type ComparatorFactory, type GroupedResults, ResultColumnId, type Sort } from '@/types';
|
|
16
9
|
|
|
17
10
|
export const getCoordinates = (result: Result, showCoordinates: ShowCoordinates): string => {
|
|
18
11
|
const firstCell = result.cells[0];
|
|
19
|
-
|
|
20
|
-
const y = getCoordinate(firstCell.y, showCoordinates === 'original' ? 'number' : 'letter');
|
|
21
|
-
|
|
22
|
-
return result.isHorizontal() ? `${y}${x}` : `${x}${y}`;
|
|
12
|
+
return getPointCoordinates({ isHorizontal: result.isHorizontal(), x: firstCell.x, y: firstCell.y }, showCoordinates);
|
|
23
13
|
};
|
|
24
14
|
|
|
25
15
|
export const groupResults = (
|
|
@@ -69,14 +59,11 @@ export const resultMatchesCellFilter = (result: Result, cellFilters: CellFilter[
|
|
|
69
59
|
return matchesExcludeFilters && matchesIncludeFilters;
|
|
70
60
|
};
|
|
71
61
|
|
|
72
|
-
const comparators: Record<ResultColumnId,
|
|
62
|
+
const comparators: Record<ResultColumnId, ComparatorFactory<Result>> = {
|
|
73
63
|
[ResultColumnId.BlanksCount]: (locale: string) => createKeyComparator('blanksCount', locale),
|
|
74
64
|
[ResultColumnId.ConsonantsCount]: (locale: string) => createKeyComparator('consonantsCount', locale),
|
|
75
|
-
[ResultColumnId.Coordinates]: (locale: string, showCoordinates: ShowCoordinates) =>
|
|
76
|
-
|
|
77
|
-
const aValue = getCoordinates(a, showCoordinates);
|
|
78
|
-
const bValue = getCoordinates(b, showCoordinates);
|
|
79
|
-
return stringComparator(aValue, bValue);
|
|
65
|
+
[ResultColumnId.Coordinates]: (locale: string, showCoordinates: ShowCoordinates) => {
|
|
66
|
+
return createCoordinatesComparator({ getItemCoordinates: getCoordinates, locale, showCoordinates });
|
|
80
67
|
},
|
|
81
68
|
[ResultColumnId.Points]: (locale: string) => createKeyComparator('points', locale),
|
|
82
69
|
[ResultColumnId.TilesCount]: (locale: string) => createKeyComparator('tilesCount', locale),
|
|
@@ -95,9 +82,5 @@ export const sortResults = (
|
|
|
95
82
|
return undefined;
|
|
96
83
|
}
|
|
97
84
|
|
|
98
|
-
|
|
99
|
-
const comparator = createComparator(locale, showCoordinates);
|
|
100
|
-
const finalComparator = sort.direction === SortDirection.Descending ? reverseComparator(comparator) : comparator;
|
|
101
|
-
const sortedResults = [...results].sort(finalComparator);
|
|
102
|
-
return sortedResults;
|
|
85
|
+
return [...results].sort(createSortComparator({ comparators, locale, showCoordinates, sort }));
|
|
103
86
|
};
|