TypeScript: add ts-standard

This commit is contained in:
0x1eef 2022-11-02 00:04:40 -03:00 committed by Robert
parent 36f21c8936
commit 2eeeb439ec
12 changed files with 4500 additions and 93 deletions

16
.eslintrc.js Normal file
View 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

File diff suppressed because it is too large Load diff

View file

@ -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"

View file

@ -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>

View file

@ -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>
);

View file

@ -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>
);
}

View file

@ -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>
);

View file

@ -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
};
}

View file

@ -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 };

View file

@ -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;
}
}

View file

@ -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)} />);

View file

@ -13,6 +13,7 @@
"jsx": "react",
"allowJs": true,
"moduleResolution": "node",
"esModuleInterop": true
"esModuleInterop": true,
"strictNullChecks": true
}
}