TypeScript: add ts-standard
This commit is contained in:
parent
36f21c8936
commit
2eeeb439ec
12 changed files with 4500 additions and 93 deletions
16
.eslintrc.js
Normal file
16
.eslintrc.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
module.exports = {
|
||||
extends: ['standard-with-typescript', 'standard-jsx'],
|
||||
parserOptions: {
|
||||
project: './tsconfig.json'
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/semi': ['error', 'always'],
|
||||
'@typescript-eslint/no-extra-semi': 'error',
|
||||
'@typescript-eslint/explicit-function-return-type': 0,
|
||||
'@typescript-eslint/strict-boolean-expressions': 0,
|
||||
'@typescript-eslint/no-floating-promises': 0,
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 0,
|
||||
'no-useless-return': 0,
|
||||
'@typescript-eslint/restrict-template-expressions': 0
|
||||
}
|
||||
}
|
4384
package-lock.json
generated
4384
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,12 +1,17 @@
|
|||
{
|
||||
"scripts": {
|
||||
"eslint": "node ./node_modules/eslint/bin/eslint.js src/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.18",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"classnames": "^2.3.2",
|
||||
"es-cookie": "^1.4.0",
|
||||
"eslint": "^8.26.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"ts-loader": "^9.3.1",
|
||||
"ts-standard": "^12.0.1",
|
||||
"typescript": "^4.8.2",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0"
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from "react";
|
||||
import { Surah } from "lib/Quran";
|
||||
import React from 'react';
|
||||
import { Surah } from 'lib/Quran';
|
||||
|
||||
export function AboutSurah({surah}: {surah: Surah}) {
|
||||
export function AboutSurah ({ surah }: { surah: Surah }) {
|
||||
return (
|
||||
<div className="about-surah">
|
||||
<div className='about-surah'>
|
||||
<span>
|
||||
{surah.translatedName}
|
||||
</span>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { Surah, Ayat, Ayah } from "lib/Quran"
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import classNames from "classnames";
|
||||
import { Surah, Ayat, Ayah } from 'lib/Quran';
|
||||
import React, { useEffect } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
type StreamProps = {
|
||||
surah: Surah,
|
||||
interface StreamProps {
|
||||
surah: Surah
|
||||
stream: Ayat
|
||||
}
|
||||
|
||||
export function Stream({surah, stream}: StreamProps) {
|
||||
export function Stream ({ surah, stream }: StreamProps) {
|
||||
const endOfStream = stream.length === surah.ayat.length;
|
||||
const ayat = stream.map((ayah: Ayah) => {
|
||||
return (
|
||||
<li key={ayah.id} className="ayah fade">
|
||||
<li key={ayah.id} className='ayah fade'>
|
||||
<span>Surah {surah.id}, Ayah {ayah.id}</span>
|
||||
<p>{ayah.text}</p>
|
||||
</li>
|
||||
|
@ -19,12 +19,12 @@ export function Stream({surah, stream}: StreamProps) {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
const el: HTMLElement = document.querySelector("ul.stream");
|
||||
el.scroll({top: el.offsetHeight * stream.length, behavior: "smooth"});
|
||||
const el: HTMLElement = document.querySelector('ul.stream');
|
||||
el.scroll({ top: el.offsetHeight * stream.length, behavior: 'smooth' });
|
||||
}, [stream]);
|
||||
|
||||
return (
|
||||
<ul className={classNames("stream", {"scroll-y": endOfStream})}>
|
||||
<ul className={classNames('stream', { 'scroll-y': endOfStream })}>
|
||||
{ayat}
|
||||
</ul>
|
||||
);
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import React from "react";
|
||||
import { set as setCookie } from "es-cookie";
|
||||
import React from 'react';
|
||||
import { set as setCookie } from 'es-cookie';
|
||||
|
||||
type Props = {
|
||||
setTheme: (theme: string) => void,
|
||||
interface Props {
|
||||
setTheme: (theme: string) => void
|
||||
theme: string
|
||||
}
|
||||
|
||||
export function ThemeSelect({setTheme, theme}: Props) {
|
||||
export function ThemeSelect ({ setTheme, theme }: Props) {
|
||||
const onThemeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setCookie("theme", e.target.value, {domain: location.host, expires: 365})
|
||||
setCookie('theme', e.target.value, { domain: location.host, expires: 365 });
|
||||
setTheme(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<select name="theme" value={theme} onChange={onThemeChange}>
|
||||
<option value="moon">The Moon 🌛</option>
|
||||
<option value="leaf">The Leaf 🌿</option>
|
||||
<select name='theme' value={theme} onChange={onThemeChange}>
|
||||
<option value='moon'>The Moon 🌛</option>
|
||||
<option value='leaf'>The Leaf 🌿</option>
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Quran, Surah, Ayah, Ayat } from "lib/Quran";
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Surah, Ayah, Ayat } from 'lib/Quran';
|
||||
|
||||
type TimerProps = {
|
||||
surah: Surah,
|
||||
ayah: Ayah,
|
||||
stream: Ayat,
|
||||
interface TimerProps {
|
||||
surah: Surah
|
||||
ayah: Ayah
|
||||
stream: Ayat
|
||||
setStream: (stream: Ayat) => void
|
||||
};
|
||||
}
|
||||
|
||||
export function Timer({surah, ayah, stream, setStream}: TimerProps) {
|
||||
export function Timer ({ surah, ayah, stream, setStream }: TimerProps) {
|
||||
const [ms, setMs] = useState(ayah.readTimeMs);
|
||||
useEffect(() => setMs(ayah.readTimeMs), [ayah]);
|
||||
useEffect(() => {
|
||||
|
@ -21,7 +21,7 @@ export function Timer({surah, ayah, stream, setStream}: TimerProps) {
|
|||
}
|
||||
}, [ms]);
|
||||
return (
|
||||
<div className="timer">
|
||||
<div className='timer'>
|
||||
{(ms / 1000).toFixed(1)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { Quran } from "lib/Quran";
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Quran } from 'lib/Quran';
|
||||
|
||||
export default function(locale: string, surahId: number) {
|
||||
export default function (locale: string, surahId: number) {
|
||||
const [surah, setSurah] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const res = await fetch(`/json/${locale}/${surahId}.json`);
|
||||
const res = await fetch(`/json/${locale}/${surahId}.json`);
|
||||
const json = await res.json();
|
||||
setSurah(Quran.Surah.fromJSON(json.shift(), json));
|
||||
})();
|
||||
|
@ -14,6 +14,6 @@ export default function(locale: string, surahId: number) {
|
|||
|
||||
return {
|
||||
surah,
|
||||
surahIsLoaded: surah !== null,
|
||||
surahIsLoaded: surah !== null
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Surah, Ayah, Ayat } from "./Quran/Surah";
|
||||
import { Surah, Ayah, Ayat } from './Quran/Surah';
|
||||
const Quran = {
|
||||
Surah: Surah
|
||||
Surah
|
||||
};
|
||||
export { Quran, Surah, Ayah, Ayat };
|
||||
|
|
|
@ -1,57 +1,58 @@
|
|||
type SurahDetails = {
|
||||
id: string,
|
||||
place_of_revelation: string,
|
||||
transliterated_name: string,
|
||||
translated_name: string,
|
||||
verse_count: number,
|
||||
slug: string,
|
||||
codepoints: Array<number>
|
||||
interface SurahDetails {
|
||||
id: string
|
||||
place_of_revelation: string
|
||||
transliterated_name: string
|
||||
translated_name: string
|
||||
verse_count: number
|
||||
slug: string
|
||||
codepoints: number[]
|
||||
}
|
||||
export type Ayah = {id: number, text: string, readTimeMs: number};
|
||||
export type Ayat = Array<Ayah>;
|
||||
export interface Ayah {id: number, text: string, readTimeMs: number}
|
||||
export type Ayat = Ayah[];
|
||||
|
||||
export class Surah {
|
||||
#details: SurahDetails;
|
||||
ayat: Ayat;
|
||||
|
||||
static fromJSON(details: SurahDetails, ayat: Array<[number, string]>): Surah {
|
||||
static fromJSON (details: SurahDetails, ayat: Array<[number, string]>): Surah {
|
||||
return new Surah(
|
||||
details,
|
||||
ayat.map(([id, text]) => {
|
||||
return {
|
||||
id, text,
|
||||
readTimeMs: text.split(" ").length * 500,
|
||||
}
|
||||
id,
|
||||
text,
|
||||
readTimeMs: text.split(' ').length * 500
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
constructor(details: SurahDetails, ayat: Ayat) {
|
||||
constructor (details: SurahDetails, ayat: Ayat) {
|
||||
this.#details = details;
|
||||
this.ayat = ayat;
|
||||
}
|
||||
|
||||
get id() {
|
||||
get id () {
|
||||
return this.#details.id;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return String.fromCodePoint(...this.#details.codepoints)
|
||||
get name () {
|
||||
return String.fromCodePoint(...this.#details.codepoints);
|
||||
}
|
||||
|
||||
get transliteratedName() {
|
||||
get transliteratedName () {
|
||||
return this.#details.transliterated_name;
|
||||
}
|
||||
|
||||
get translatedName() {
|
||||
get translatedName () {
|
||||
return this.#details.translated_name;
|
||||
}
|
||||
|
||||
get placeOfRevelation() {
|
||||
get placeOfRevelation () {
|
||||
return this.#details.place_of_revelation;
|
||||
}
|
||||
|
||||
get numberOfAyah() {
|
||||
get numberOfAyah () {
|
||||
return this.#details.verse_count;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +1,59 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import useSurah from "hooks/useSurah";
|
||||
import { Timer } from "components/TheQuran/Timer";
|
||||
import { Stream } from "components/TheQuran/Stream";
|
||||
import { AboutSurah } from "components/TheQuran/AboutSurah";
|
||||
import { ThemeSelect } from "components/TheQuran/ThemeSelect";
|
||||
import classNames from "classnames";
|
||||
import { get as getCookie } from "es-cookie";
|
||||
import useSurah from 'hooks/useSurah';
|
||||
import { Timer } from 'components/TheQuran/Timer';
|
||||
import { Stream } from 'components/TheQuran/Stream';
|
||||
import { AboutSurah } from 'components/TheQuran/AboutSurah';
|
||||
import { ThemeSelect } from 'components/TheQuran/ThemeSelect';
|
||||
import classNames from 'classnames';
|
||||
import { get as getCookie } from 'es-cookie';
|
||||
|
||||
type PageProps = {
|
||||
locale: string,
|
||||
interface PageProps {
|
||||
locale: string
|
||||
surahId: number
|
||||
}
|
||||
|
||||
function TheSurahPage({locale, surahId}: PageProps) {
|
||||
function TheSurahPage ({ locale, surahId }: PageProps) {
|
||||
const { surahIsLoaded, surah } = useSurah(locale, surahId);
|
||||
const [stream, setStream] = useState([]);
|
||||
const [theme , setTheme] = useState(getCookie("theme") || "moon");
|
||||
const streamIsLoaded = !!stream.length;
|
||||
const [theme, setTheme] = useState(getCookie('theme') || 'moon');
|
||||
const streamIsLoaded = !(stream.length === 0);
|
||||
|
||||
useEffect(() => {
|
||||
if (surahIsLoaded) {
|
||||
document.title = ["Al-Quran:",
|
||||
surah.transliteratedName,
|
||||
`(${surah.translatedName})`].join(" ");
|
||||
document.title = [
|
||||
'Al-Quran:',
|
||||
surah.transliteratedName,
|
||||
`(${surah.translatedName})`
|
||||
].join(' ');
|
||||
setStream([surah.ayat[stream.length]]);
|
||||
}
|
||||
}, [surahIsLoaded]);
|
||||
|
||||
return (
|
||||
<div className={classNames(theme, "theme")}>
|
||||
<div className="flex-image">
|
||||
<div className="image"></div>
|
||||
<div className={classNames(theme, 'theme')}>
|
||||
<div className='flex-image'>
|
||||
<div className='image' />
|
||||
</div>
|
||||
{streamIsLoaded &&
|
||||
<div className="flex-row">
|
||||
<span></span>
|
||||
<ThemeSelect theme={theme} setTheme={setTheme}/>
|
||||
<span></span>
|
||||
</div>
|
||||
}
|
||||
{streamIsLoaded && <AboutSurah surah={surah}/>}
|
||||
{streamIsLoaded && <Stream surah={surah} stream={stream}/>}
|
||||
<div className='flex-row'>
|
||||
<span />
|
||||
<ThemeSelect theme={theme} setTheme={setTheme} />
|
||||
<span />
|
||||
</div>}
|
||||
{streamIsLoaded && <AboutSurah surah={surah} />}
|
||||
{streamIsLoaded && <Stream surah={surah} stream={stream} />}
|
||||
{streamIsLoaded && stream.length < surah.numberOfAyah &&
|
||||
<Timer
|
||||
surah={surah}
|
||||
ayah={surah.ayat[stream.length - 1]}
|
||||
setStream={setStream}
|
||||
stream={stream}
|
||||
/>
|
||||
}
|
||||
/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const [locale, surahId] = location.pathname.split('/').filter((e) => e);
|
||||
const root = ReactDOM.createRoot(document.querySelector(".surah"));
|
||||
root.render(<TheSurahPage locale={locale} surahId={parseInt(surahId)}/>);
|
||||
const root = ReactDOM.createRoot(document.querySelector('.surah'));
|
||||
root.render(<TheSurahPage locale={locale} surahId={parseInt(surahId)} />);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"strictNullChecks": true
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue