Compare commits
96 commits
production
...
main
Author | SHA1 | Date | |
---|---|---|---|
76b58e0cc3 | |||
e82c059098 | |||
36a472a506 | |||
ebd4bb36ca | |||
8f409c4d68 | |||
1b1d72246f | |||
156a34dc9a | |||
900547c44e | |||
5c2bdea8b4 | |||
938c4f5fae | |||
bfec7a8069 | |||
8d2138468c | |||
026b5248c8 | |||
74d444031c | |||
265b580a08 | |||
4146973cf6 | |||
3802de7df7 | |||
b9471c8421 | |||
d9cd9f680e | |||
2f349df4ec | |||
a28903c8b7 | |||
e24cc3d7fc | |||
84b19edf04 | |||
355a4c75b5 | |||
ed0d493a17 | |||
8518b5f491 | |||
b829068bd5 | |||
a6973b439d | |||
69f8b0511a | |||
111ae08175 | |||
438cf9dab4 | |||
4d44e72954 | |||
2324189992 | |||
de3fcdde1a | |||
78dba7fe57 | |||
bd4502cb63 | |||
18a454075d | |||
3250fae3f3 | |||
4951dada90 | |||
d6d6b10be3 | |||
743f326343 | |||
5023b494b1 | |||
8adfca235a | |||
9cce03e299 | |||
cd2d320c6f | |||
f5ba2b9388 | |||
deb61e94bd | |||
4ec69db283 | |||
8be703b261 | |||
df84efbda5 | |||
34c3fccd6c | |||
1b3af0b6d7 | |||
d31160e7f5 | |||
3a2873b2cc | |||
188797bf58 | |||
3f662805e4 | |||
5c0e26183c | |||
058f423e4a | |||
5cd13e28ab | |||
685b1415ca | |||
22bb9301e2 | |||
38224a7417 | |||
92b0f4f6c1 | |||
f66040965d | |||
44e143254e | |||
07ce6cdf61 | |||
630592d71f | |||
7a9091fd6c | |||
bf2d01c812 | |||
03e33bdc37 | |||
3e7f3271d5 | |||
024fc1c4ed | |||
4889d67157 | |||
9599c2ca5f | |||
78f7fc2076 | |||
590e912066 | |||
ca8fe77554 | |||
26a989f6b7 | |||
1b78d3c2c9 | |||
b66e66caee | |||
06ae1173b6 | |||
4573fbae0e | |||
195dad288a | |||
e7d1241459 | |||
a5f66044e1 | |||
cfce51e29c | |||
735e8bcc20 | |||
d4588413e4 | |||
21263d4574 | |||
105b7b18cb | |||
fa47510dd2 | |||
22fb6b87fa | |||
07788b4e5e | |||
94aaa6e10c | |||
de96eccd7e | |||
a2a856cd45 |
86 changed files with 961 additions and 840 deletions
|
@ -1,8 +1,14 @@
|
|||
[*.rb,*.erb]
|
||||
root = true
|
||||
|
||||
[*.html]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.js,*.ts,*.tsx]
|
||||
[*.rb, *.erb]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.js, *.ts, *.tsx]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
|
|
9
.gitmodules
vendored
9
.gitmodules
vendored
|
@ -1,9 +0,0 @@
|
|||
[submodule "submodules/surah-name-glyphs"]
|
||||
path = submodules/surah-name-glyphs
|
||||
url = https://github.com/ReflectsLight/surah-name-glyphs
|
||||
[submodule "submodules/quran-json"]
|
||||
path = submodules/quran-json
|
||||
url = https://github.com/ReflectsLight/quran-json
|
||||
[submodule "submodules/tail.css"]
|
||||
path = submodules/tail.css
|
||||
url = https://github.com/0x1eef/tail.css
|
1
Gemfile
1
Gemfile
|
@ -10,6 +10,7 @@ gem "nanoc", "~> 4.12"
|
|||
gem "nanoc-gzip.rb"
|
||||
gem "nanoc-webpack.rb", "~> 0.10"
|
||||
gem "nanoc-tidy.rb", "~> 0.8.4"
|
||||
gem "rainpress"
|
||||
|
||||
##
|
||||
# dev
|
||||
|
|
|
@ -82,11 +82,12 @@ GEM
|
|||
psych (5.1.2)
|
||||
stringio
|
||||
public_suffix (6.0.1)
|
||||
puma (6.4.2)
|
||||
puma (6.4.3)
|
||||
nio4r (~> 2.0)
|
||||
racc (1.8.1)
|
||||
rack (3.1.7)
|
||||
rainbow (3.1.1)
|
||||
rainpress (1.0.1)
|
||||
rake (13.2.1)
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.11.1)
|
||||
|
@ -155,6 +156,7 @@ DEPENDENCIES
|
|||
nanoc-tidy.rb (~> 0.8.4)
|
||||
nanoc-webpack.rb (~> 0.10)
|
||||
paint (~> 2.3)
|
||||
rainpress
|
||||
rake (~> 13.2)
|
||||
rexml (~> 3.3.3)
|
||||
ryo.rb
|
||||
|
|
14
README.md
14
README.md
|
@ -8,7 +8,9 @@ the build directory consists of HTML, CSS, JavaScript
|
|||
and other static assets that can be hosted by
|
||||
a web server such as nginx, apache, et cetera.
|
||||
|
||||
## Requirements
|
||||
## Development
|
||||
|
||||
### Requirements
|
||||
|
||||
The following languages and tools have to be
|
||||
installed to build the website from source:
|
||||
|
@ -17,7 +19,7 @@ installed to build the website from source:
|
|||
* NodeJS v18.15 (or later)
|
||||
* [tidy-html5](https://github.com/htacg/tidy-html5)
|
||||
|
||||
## Development
|
||||
### Examples
|
||||
|
||||
# Clone repository
|
||||
git clone https://github.com/ReflectsLight/al-quran.reflectslight.io
|
||||
|
@ -101,7 +103,13 @@ installed to build the website from source:
|
|||
- Sahl Yassin
|
||||
- Hani ar-Rifai
|
||||
|
||||
## Sources
|
||||
|
||||
* [GitHub](https://github.com/ReflectsLight/al-quran.reflectslight.io)
|
||||
* [GitLab](https://gitlab.com/0x1eef/al-quran.reflectslight.io)
|
||||
* [brew.bsd.cafe/@0x1eef](https://brew.bsd.cafe/0x1eef/al-quran.reflectslight.io)
|
||||
|
||||
## License
|
||||
|
||||
The "source code" is released under the terms of the GPL <br>
|
||||
See [LICENSE](./share/al-quran.reflectslight.io/LICENSE) for details
|
||||
See [LICENSE.txt](./share/al-quran.reflectslight.io/LICENSE.txt) for details
|
||||
|
|
36
Rules
36
Rules
|
@ -24,13 +24,10 @@ tdata = Ryo.from_json(path: File.join(dirs.content, "json", "t.json"))
|
|||
surahs = Ryo.from_json(path: File.join(dirs.content, "json", "surahs.json"))
|
||||
tidy = `which tidy || which tidy5`.chomp
|
||||
buildenv = ENV["buildenv"] || "development"
|
||||
etcdir = File.join(__dir__, "etc")
|
||||
globals = {buildenv:, locales:, tidy:, tdata:, surahs:, name_by_id:}
|
||||
|
||||
##
|
||||
# Filters
|
||||
Nanoc::Webpack
|
||||
.default_argv
|
||||
.replace([*Nanoc::Webpack.default_argv, "--config", File.join(etcdir, "webpack.#{buildenv}.js")].uniq)
|
||||
Nanoc::Tidy
|
||||
.default_argv
|
||||
.replace([*Nanoc::Tidy.default_argv, "-upper"].uniq)
|
||||
|
@ -38,6 +35,7 @@ Nanoc::Tidy
|
|||
##
|
||||
# See packages/typescript/postman
|
||||
compile "/css/vendor/postman.css" do
|
||||
filter(:rainpress)
|
||||
write("/css/vendor/postman.css")
|
||||
end
|
||||
|
||||
|
@ -49,25 +47,21 @@ compile "/sitemap.xml.erb" do
|
|||
write("/sitemap.xml")
|
||||
end
|
||||
|
||||
##
|
||||
# /robots.txt
|
||||
compile "/robots.txt" do
|
||||
write("/robots.txt")
|
||||
end
|
||||
|
||||
##
|
||||
# /json/durations/*.json
|
||||
passthrough "/json/durations/*.json"
|
||||
|
||||
##
|
||||
# Rules
|
||||
require_rules "nanoc/rules/assets"
|
||||
require_rules "nanoc/rules/redirect", {locales:, tidy:}
|
||||
require_rules "nanoc/rules/random", {locales:, tdata:, tidy:}
|
||||
require_rules "nanoc/rules/surah-stream", {locales:, tdata:, surahs:, name_by_id:, tidy:}
|
||||
require_rules "nanoc/rules/surah-index", {locales:, tdata:, tidy:}
|
||||
passthrough "/json/durations/*.json"
|
||||
require_rules "nanoc/rules/assets", globals
|
||||
require_rules "nanoc/rules/redirect", globals
|
||||
require_rules "nanoc/rules/random", globals
|
||||
require_rules "nanoc/rules/surah-stream", globals
|
||||
require_rules "nanoc/rules/surah-index", globals
|
||||
|
||||
##
|
||||
# Catch-all
|
||||
compile "/js/main/vendor.ts" do
|
||||
filter :webpack,
|
||||
argv: %w[--config etc/webpack.vendor.js]
|
||||
write("/js/main/vendor.js")
|
||||
filter :gzip
|
||||
write("/js/main/vendor.js.gz")
|
||||
end
|
||||
compile("/**/*") { write(nil) }
|
||||
layout("**/*", :erb)
|
||||
|
|
|
@ -4,6 +4,7 @@ AllCops:
|
|||
TargetRubyVersion: 3.2
|
||||
Exclude:
|
||||
- submodules/**/*
|
||||
- source/**/*
|
||||
Include:
|
||||
- Rakefile.rb
|
||||
- rake/tasks/*.rake
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
{
|
||||
"include": ["../src/**/*.ts", "../src/**/*.tsx"],
|
||||
"exclude": ["../node_modules"],
|
||||
"include": [
|
||||
"../src/**/*.ts",
|
||||
"../src/**/*.tsx",
|
||||
],
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"module": "commonjs",
|
||||
"target": "ES2020",
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react",
|
||||
"jsx": "react-jsx",
|
||||
"lib": [ "ES2020", "DOM" ],
|
||||
|
||||
"typeRoots": ["../src/js/types/globals.d.ts", "../node_modules/@types"],
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": "../src/",
|
||||
"paths": {
|
||||
"~/*": ["./js/*"],
|
||||
|
|
|
@ -7,6 +7,10 @@ module.exports = {
|
|||
"@css": path.resolve(__dirname, "..", "src", "css"),
|
||||
"@json": path.resolve(__dirname, "..", "src", "json"),
|
||||
"~": path.resolve(__dirname, "..", "src", "js"),
|
||||
"react": "preact/compat",
|
||||
"react-dom/test-utils": "preact/test-utils",
|
||||
"react-dom": "preact/compat",
|
||||
"react/jsx-runtime": "preact/jsx-runtime",
|
||||
},
|
||||
extensions: [".js", ".ts", ".tsx", ".json", ".css", ".scss"],
|
||||
},
|
||||
|
|
14
etc/webpack.vendor.js
Normal file
14
etc/webpack.vendor.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
const path = require('path');
|
||||
const common = require('./webpack.common.js');
|
||||
const { merge } = require('webpack-merge');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return merge(
|
||||
common,
|
||||
{
|
||||
output: { library: { type: 'umd' } },
|
||||
resolve: { extensions: ['.ts', '.tsx'] },
|
||||
optimization: { minimize: true }
|
||||
}
|
||||
)
|
||||
}
|
|
@ -27,6 +27,7 @@ server:
|
|||
base_url: https://al-quran.reflectslight.io
|
||||
unix:
|
||||
path:
|
||||
mode: ug=rw,o=
|
||||
tcp:
|
||||
host: 127.0.0.1
|
||||
port: 7777
|
||||
|
|
|
@ -9,9 +9,8 @@ module Nanoc::Extension
|
|||
require "securerandom"
|
||||
##
|
||||
# @example
|
||||
# compile "/sitemap.xml.erb" do
|
||||
# filter(:erb)
|
||||
# filter Proc.new { _1.each_line.reject { |s| s.strip.empty? }.join }
|
||||
# compile "/sitemap.xml" do
|
||||
# filter proc { _1.chomp }
|
||||
# write("/sitemap.xml")
|
||||
# end
|
||||
# @param [Proc, Symbol] fn
|
||||
|
@ -21,9 +20,9 @@ module Nanoc::Extension
|
|||
# @return [void]
|
||||
def filter(fn, options = {})
|
||||
if Proc === fn
|
||||
id = anonymous_id
|
||||
Nanoc::Filter.define(id) { fn.call(_1, _2) }
|
||||
super(id, options)
|
||||
anonid = anonymous_id
|
||||
Nanoc::Filter.define(anonid) { fn.call(_1, _2) }
|
||||
super(anonid, options)
|
||||
else
|
||||
super(fn, options)
|
||||
end
|
||||
|
|
|
@ -15,7 +15,9 @@ locales.each do |locale|
|
|||
end
|
||||
|
||||
compile "/js/main/random.ts" do
|
||||
filter(:webpack, depend_on: ["/js/lib/"])
|
||||
filter :webpack,
|
||||
argv: %W[--config etc/webpack.#{buildenv}.js],
|
||||
depend_on: ["/js/lib/"]
|
||||
write("/js/main/random.js")
|
||||
filter(:gzip)
|
||||
write("/js/main/random.js.gz")
|
||||
|
|
|
@ -12,7 +12,9 @@ compile "/html/main/redirect.html.erb" do
|
|||
end
|
||||
|
||||
compile "/js/main/redirect.ts" do
|
||||
filter(:webpack, depend_on: ["/js/lib/"])
|
||||
filter :webpack,
|
||||
argv: %W[--config etc/webpack.#{buildenv}.js],
|
||||
depend_on: ["/js/lib/"]
|
||||
write("/js/main/redirect.js")
|
||||
filter(:gzip)
|
||||
write("/js/main/redirect.js.gz")
|
||||
|
|
|
@ -15,19 +15,22 @@ locales.each do |locale|
|
|||
end
|
||||
|
||||
compile "/js/main/surah-index.tsx" do
|
||||
filter :webpack, depend_on: [
|
||||
"/js/components",
|
||||
"/js/lib",
|
||||
"/js/hooks",
|
||||
"/css"
|
||||
]
|
||||
filter :webpack,
|
||||
argv: %W[--config etc/webpack.#{buildenv}.js],
|
||||
depend_on: [
|
||||
"/js/components",
|
||||
"/js/lib",
|
||||
"/js/hooks",
|
||||
"/css"
|
||||
]
|
||||
write "/js/main/surah-index.js"
|
||||
filter :gzip
|
||||
write "/js/main/surah-index.js.gz"
|
||||
end
|
||||
|
||||
compile "/js/loaders/SurahIndexLoader.ts" do
|
||||
filter :webpack
|
||||
filter :webpack,
|
||||
argv: %W[--config etc/webpack.#{buildenv}.js]
|
||||
write "/js/loaders/surah-index-loader.js"
|
||||
filter :gzip
|
||||
write "/js/loaders/surah-index-loader.js.gz"
|
||||
|
|
|
@ -32,19 +32,22 @@ Ryo.each(name_by_id) do |id, slug|
|
|||
end
|
||||
|
||||
compile "/js/main/surah-stream.tsx" do
|
||||
filter :webpack, depend_on: [
|
||||
"/js/components",
|
||||
"/js/lib",
|
||||
"/js/hooks",
|
||||
"/css"
|
||||
]
|
||||
filter :webpack,
|
||||
argv: %W[--config etc/webpack.#{buildenv}.js],
|
||||
depend_on: [
|
||||
"/js/components",
|
||||
"/js/lib",
|
||||
"/js/hooks",
|
||||
"/css"
|
||||
]
|
||||
write "/js/main/surah-stream.js"
|
||||
filter :gzip
|
||||
write "/js/main/surah-stream.js.gz"
|
||||
end
|
||||
|
||||
compile "/js/loaders/SurahStreamLoader.ts" do
|
||||
filter :webpack
|
||||
filter :webpack,
|
||||
argv: %W[--config etc/webpack.#{buildenv}.js]
|
||||
write "/js/loaders/surah-stream-loader.js"
|
||||
filter :gzip
|
||||
write "/js/loaders/surah-stream-loader.js.gz"
|
||||
|
|
99
package-lock.json
generated
99
package-lock.json
generated
|
@ -10,13 +10,14 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"classnames": "^2.3",
|
||||
"react": "^18.2",
|
||||
"react-dom": "^18.2"
|
||||
"preact": "^10.23.2",
|
||||
"react": "npm:@preact/compat",
|
||||
"react-dom": "npm:@preact/compat",
|
||||
"react-jsxruntime": "npm:@preact/compat"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/css-font-loading-module": "^0.0.13",
|
||||
"@types/react": "^18.0",
|
||||
"@types/react-dom": "^18.0",
|
||||
"@types/react": "^18.3.5",
|
||||
"css-loader": "^7.1",
|
||||
"esbuild-loader": "^4.1",
|
||||
"eslint": "^9.8",
|
||||
|
@ -715,9 +716,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz",
|
||||
"integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==",
|
||||
"version": "18.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz",
|
||||
"integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -725,16 +726,6 @@
|
|||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "18.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
|
||||
"integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz",
|
||||
|
@ -2638,12 +2629,6 @@
|
|||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
|
@ -2780,18 +2765,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
|
@ -3245,6 +3218,16 @@
|
|||
"resolved": "packages/typescript/postman",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.23.2",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.23.2.tgz",
|
||||
"integrity": "sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
|
@ -3346,28 +3329,33 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"name": "@preact/compat",
|
||||
"version": "17.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@preact/compat/-/compat-17.1.2.tgz",
|
||||
"integrity": "sha512-7pOZN9lMDDRQ+6aWvjwTp483KR8/zOpfS83wmOo3zfuLKdngS8/5RLbsFWzFZMGdYlotAhX980hJ75bjOHTwWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"peerDependencies": {
|
||||
"preact": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"name": "@preact/compat",
|
||||
"version": "17.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@preact/compat/-/compat-17.1.2.tgz",
|
||||
"integrity": "sha512-7pOZN9lMDDRQ+6aWvjwTp483KR8/zOpfS83wmOo3zfuLKdngS8/5RLbsFWzFZMGdYlotAhX980hJ75bjOHTwWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.3.1"
|
||||
"preact": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-jsxruntime": {
|
||||
"name": "@preact/compat",
|
||||
"version": "17.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@preact/compat/-/compat-17.1.2.tgz",
|
||||
"integrity": "sha512-7pOZN9lMDDRQ+6aWvjwTp483KR8/zOpfS83wmOo3zfuLKdngS8/5RLbsFWzFZMGdYlotAhX980hJ75bjOHTwWg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"preact": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
|
@ -3572,15 +3560,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.23.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||
|
|
|
@ -10,13 +10,14 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.3",
|
||||
"react": "^18.2",
|
||||
"react-dom": "^18.2"
|
||||
"preact": "^10.23.2",
|
||||
"react": "npm:@preact/compat",
|
||||
"react-dom": "npm:@preact/compat",
|
||||
"react-jsxruntime": "npm:@preact/compat"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/css-font-loading-module": "^0.0.13",
|
||||
"@types/react": "^18.0",
|
||||
"@types/react-dom": "^18.0",
|
||||
"@types/react": "^18.3.5",
|
||||
"css-loader": "^7.1",
|
||||
"esbuild-loader": "^4.1",
|
||||
"eslint": "^9.8",
|
||||
|
|
|
@ -2,10 +2,10 @@ import type { Item, FontItem } from './postman/item';
|
|||
import item from './postman/item';
|
||||
import request from './postman/request';
|
||||
|
||||
type Postman = { fetch: () => Promise<Package> };
|
||||
type Postman = { deliver: () => Promise<Parcel> };
|
||||
type Args = Array<Item | FontItem | Function>
|
||||
type Items = Array<Item | FontItem>;
|
||||
type Package = {
|
||||
export type Parcel = {
|
||||
fonts: FontFace[]
|
||||
images: HTMLElement[]
|
||||
css: HTMLElement[]
|
||||
|
@ -30,7 +30,7 @@ export { item };
|
|||
|
||||
export default function (...args: Args) {
|
||||
const self: Postman = Object.create(null);
|
||||
const result: Package = { fonts: [], images: [], css: [], scripts: [], json: [] };
|
||||
const result: Parcel = { fonts: [], images: [], css: [], scripts: [], json: [] };
|
||||
const [items, callback] = parseArgs(args);
|
||||
items.sort((i1, i2) => i1.priority >= i2.priority ? 1 : -1);
|
||||
|
||||
|
@ -62,11 +62,11 @@ export default function (...args: Args) {
|
|||
/* unreachable */
|
||||
return null;
|
||||
});
|
||||
return reqs as Array<Promise<Package>>;
|
||||
return reqs as Array<Promise<Parcel>>;
|
||||
};
|
||||
|
||||
self.fetch = async () => {
|
||||
await Promise.all<Package>(spawnRequests());
|
||||
self.deliver = async () => {
|
||||
await Promise.all<Parcel>(spawnRequests());
|
||||
return result;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ export default {
|
|||
const { href } = item;
|
||||
return fetch(href, options)
|
||||
.then((res) => res.text())
|
||||
.then((text) => ({ type: 'application/javascript', text }))
|
||||
.then((text) => ({ type: 'application/javascript', text, ...item.props }))
|
||||
.then((props) => Object.assign(document.createElement('script'), props));
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
namespace :format do
|
||||
desc "Apply all formats"
|
||||
task apply: %i[format:rubocop:apply format:eslint:apply]
|
||||
|
||||
desc "Run rubocop (Ruby)"
|
||||
task :rubocop do
|
||||
sh "bundle exec rubocop --config etc/rubocop.yml"
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace :nanoc do
|
|||
task :clean do
|
||||
Dir.chdir(dirs.root) do
|
||||
sh "rm -rf node_modules/.cache/"
|
||||
sh "rm -rf build/"
|
||||
sh "rm -rf #{nanoc.output_dir}/*"
|
||||
sh "rm -rf tmp/"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,19 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
cwd = File.realpath File.join(__dir__, "..", "..", ".")
|
||||
desc "Start web server"
|
||||
task :server, [:protocol] do |_t, args|
|
||||
require "server"
|
||||
nanoc = Ryo.from_yaml(path: File.join(cwd, "nanoc.yaml"))
|
||||
h = args.to_h
|
||||
p = h[:protocol] || "tcp"
|
||||
n = File.basename(dirs.root)
|
||||
n = File.basename(cwd)
|
||||
o = if p == "unix"
|
||||
{unix: nanoc.server.unix.path}
|
||||
else
|
||||
{host: nanoc.server.tcp.host, port: nanoc.server.tcp.port}
|
||||
end
|
||||
Process.setproctitle "rake server[#{p}] [#{n}]"
|
||||
s = Server.dir(nanoc.output_dir, o)
|
||||
s.start(block: true)
|
||||
Process.setproctitle "rake server[#{p}] [#{n}]"
|
||||
if p == "unix"
|
||||
t = s.start(block: false)
|
||||
unix = nanoc.server.unix
|
||||
chmod(unix.mode, unix.path, verbose: false)
|
||||
t.join
|
||||
else
|
||||
s.start(block: true)
|
||||
end
|
||||
rescue Interrupt
|
||||
s.stop
|
||||
end
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
# -*- mode: org -*-
|
||||
|
||||
** vNEXT
|
||||
|
||||
**** Change default ~audio.base_url~
|
||||
This change sets the default audio URL to
|
||||
https://audio1.al-quran.reflectslight.io/rifai
|
||||
|
||||
**** Improve KaiOS support
|
||||
This change optimizes the layout for KaiOS devices through
|
||||
specialized media queries
|
||||
|
||||
**** Add ~etc/~
|
||||
This change moves a large portion of the website's configuration
|
||||
files to the ~/etc~ directory
|
||||
|
||||
** v0.9.0
|
||||
|
||||
**** Add ~share/al-quran.reflectslight.io/documentation/~
|
||||
This change replaces ~share/doc/al-quran.reflectslight.io~
|
||||
|
||||
**** Add new recitation
|
||||
A new recitation by Hani ar-Rifai has been added
|
||||
|
||||
**** Replace ~opengraph.rb~ with ~_opengraph.html.erb~
|
||||
This change simplifies how we render opengraph-related meta tags
|
||||
|
||||
**** Move to nodejs for scss compiler
|
||||
This change replaces the deprecated Ruby scss compiler with the
|
||||
nodejs compiler
|
||||
|
||||
**** Add ~audio.base_url~ to nanoc.yaml
|
||||
This change provides increased flexibility in how the website
|
||||
serve audio files
|
||||
|
||||
**** Rename packages/typescript/Quran/ properties
|
||||
This change introduces urlName, translitName to Surah objects
|
||||
|
||||
**** eslint upgrade
|
||||
This change migrates to the most recent version of eslint (^9.8)
|
156
share/al-quran.reflectslight.io/CHANGELOG.txt
Normal file
156
share/al-quran.reflectslight.io/CHANGELOG.txt
Normal file
|
@ -0,0 +1,156 @@
|
|||
# -*- mode: org -*-
|
||||
|
||||
* vNEXT
|
||||
|
||||
** Revisit ~ThemeSelect.tsx~ for touch devices
|
||||
On touch devices it is now easier to change themes thanks to
|
||||
a container that has a larger height and width than the theme
|
||||
icon it contains. Along with this change, the theme icons have
|
||||
become larger and less circular
|
||||
|
||||
** Reduce default width
|
||||
The look and feel of the website has changed to be more like
|
||||
a book that could fit in the palm of your hand
|
||||
|
||||
** Add src/images/albumcover-600x600.jpg
|
||||
A copy of the album art that has been embedded as metadata
|
||||
in the MP3 files served from audio.al-quran.reflectslight.io
|
||||
|
||||
** Apply Amiri font for Farsi
|
||||
Similar to Arabic - apply the Amiri Regular font for the content
|
||||
of a surah. With this change the distinction is more so between a
|
||||
RTL language vs a LTR language rather than between Arabic vs English
|
||||
vs Farsi
|
||||
|
||||
** Render ~LanguageSelect.tsx~ items in an optimal font
|
||||
RTL languages have ~Cairo Regular~ applied.
|
||||
LTR languages have ~Kanit Regular~ applied
|
||||
|
||||
** Fix the render of Amiri font on iOS
|
||||
On modern versions of iOS the "Amiri Quran Regular" font rendered
|
||||
as invisible text. This change reverts to the "Amiri Regular" font
|
||||
instead, which appears to render without any issues
|
||||
|
||||
** Download fonts based on language direction
|
||||
RTL and LTR languages use a different set of fonts, and before this
|
||||
change we would download all fonts regardless of whether the font
|
||||
would be used or not
|
||||
|
||||
** Optimize inline of ~postman.css~
|
||||
The inlined CSS is minified before being inserted into HTML documents
|
||||
|
||||
** Remove KaiOS breakpoints
|
||||
There is a separate branch (~kaios/main~) for KaiOS support
|
||||
|
||||
** Rewrite theme implementation
|
||||
The theme implementation has been reduced to a set of colors
|
||||
that are applied when the theme is active, and in the process
|
||||
we made the creation of new themes easier
|
||||
|
||||
|
||||
* v0.10.0
|
||||
|
||||
** Add Cairo fonts
|
||||
Two new fonts for RTL languages: Cairo Regular, Cairo Bold
|
||||
|
||||
** Render three columns on ~SurahIndex.tsx~
|
||||
The ~SurahIndex.tsx~ component renders three columns for RTL
|
||||
languages (compared with two columns before)
|
||||
|
||||
** Revisit color scheme
|
||||
Redo the website color scheme with a focus on improving the
|
||||
Arabic / Farsi variants
|
||||
|
||||
** Revisit ~SurahStream.tsx~ for RTL languages
|
||||
The appearance of the ~SurahStream.tsx~ component was revisted
|
||||
for RTL languages, especially Arabic
|
||||
|
||||
** Add "Amiri Quran Regular" to ~SurahIndex.tsx~
|
||||
The name of each surah is rendered in the Amiri Quran Regular
|
||||
font in the Arabic locale / language
|
||||
|
||||
** Add "Amiri Quran Regular" font
|
||||
This new font is used specifically for the contents of a surah
|
||||
in the Arabic locale / language
|
||||
|
||||
** Replace custom z-index rule
|
||||
Replace custom z-index rule with the CSS class ~.z-10~
|
||||
|
||||
** Update tail.css
|
||||
The vendored copy of ~tail.css~ has been updated
|
||||
|
||||
** Add ~rake format:apply~
|
||||
This new rake task tries to auto-correct typescript and
|
||||
ruby lint errors
|
||||
|
||||
** Apply larger font size in ~Head.tsx~
|
||||
The text "The Noble Quran" has a larger size (~.text-2xl~)
|
||||
|
||||
** Remove ~/robots.txt~
|
||||
We don't need this file anymore. It was used to block crawlers
|
||||
indexing audio content at /audio but audio content is now served
|
||||
on a separate domain
|
||||
|
||||
** Add file mode option to ~nanoc.yaml~
|
||||
The file mode option can be set on the server's unix socket.
|
||||
Default: ~ug=rw,o=~
|
||||
|
||||
|
||||
* v0.9.1
|
||||
|
||||
** Fix ~_opengraph.html.erb~ typos
|
||||
Fix multiple issues with the render of ~_opengraph.html.erb~
|
||||
|
||||
** Replace font-size rules with tail.css classes
|
||||
Replace custom 'font-size' rules with standard tail.css
|
||||
classes (eg text-lg)
|
||||
|
||||
** Remove ~mada-regular.ttf~
|
||||
The Arabic font is now based on standard web fonts rather than
|
||||
a custom fonts.google.com font
|
||||
|
||||
** Add ~src/js/main/vendor.ts~
|
||||
The new vendor entry point bundles preact, and other
|
||||
third party dependencies in a single file. This change
|
||||
is an improvement imported from the ~kaios/main~ branch
|
||||
|
||||
** Replace React with preact
|
||||
The preact library is a lightweight alternative to React,
|
||||
with a smaller footprint. This change is an improvement
|
||||
imported from the ~kaios/main~ branch
|
||||
|
||||
** Change default ~audio.base_url~
|
||||
Set the default audio URL to
|
||||
https://audio.al-quran.reflectslight.io/rifai
|
||||
|
||||
**** Improve KaiOS support
|
||||
Optimize the layout for KaiOS devices through specialized
|
||||
media queries. A new branch (~kaios/main~) will focus on
|
||||
KaiOS support
|
||||
|
||||
**** Add ~etc/~
|
||||
Move a large portion of the website's configuration files to
|
||||
the ~/etc~ directory
|
||||
|
||||
|
||||
* v0.9.0
|
||||
|
||||
** Add ~share/al-quran.reflectslight.io/documentation/~
|
||||
Replace ~share/doc/al-quran.reflectslight.io~
|
||||
|
||||
** Add new recitation
|
||||
Add a new recitation by Hani ar-Rifai
|
||||
|
||||
** Replace ~opengraph.rb~ with ~_opengraph.html.erb~
|
||||
Simplify how we render opengraph meta tags
|
||||
|
||||
** Move to nodejs for scss compiler
|
||||
Replace the deprecated Ruby scss compiler with the nodejs compiler
|
||||
** Add ~audio.base_url~ to nanoc.yaml
|
||||
Provide extra flexibility for audio content
|
||||
|
||||
** Rename packages/typescript/Quran/ properties
|
||||
Introduce urlName, translitName to Surah objects
|
||||
|
||||
** Upgrade eslint
|
||||
Upgrade to the most recent version of eslint (^9.8)
|
|
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
|||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
al-quran (ebook) Copyright (C) 2024 ReflectsLight
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
|
@ -1 +1 @@
|
|||
v0.9.0
|
||||
v0.10.0
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
# -*- mode: org -*-
|
||||
|
||||
** Introduction
|
||||
|
||||
This document explains the process that should be
|
||||
followed when adding a new locale or language
|
||||
to the website. Ideally the process would require
|
||||
a minimal amount of manual work and be fast to complete
|
||||
but we're not quite there yet.
|
||||
|
||||
** Steps
|
||||
|
||||
**** Add new locale to file ~/Rules~
|
||||
The ~/Rules~ file has to be updated
|
||||
|
||||
**** Add new locale to array ~Quran.locales~
|
||||
The ~/packages/Quran/src/index.ts~ file has to be updated
|
||||
|
||||
**** Add new locale (+ translations)
|
||||
The following files have to updated:
|
||||
+ ~/src/json/t.json~
|
||||
+ ~/src/json/surahs.json~
|
||||
+ ~/src/json/<locale>/<surahId>/{info,surah}.json~
|
||||
|
||||
**** Save time
|
||||
The ~/src/json/<locale>/<surahId>/info.json~ files can
|
||||
be generated from the contents of ~/src/json/surahs.json~ -
|
||||
and that leaves us with one less step to complete:
|
||||
#+BEGIN_SRC sh
|
||||
user@localhost$ bundle exec rake t:surahs.json
|
||||
#+END_SRC
|
|
@ -0,0 +1,44 @@
|
|||
# -*- mode: org -*-
|
||||
|
||||
* Introduction
|
||||
|
||||
This document explains the process that should be
|
||||
followed when adding a new locale or language
|
||||
to the website.
|
||||
|
||||
Ideally the process would require a minimal amount
|
||||
of manual work and be fast to complete but we're not
|
||||
quite there yet.
|
||||
|
||||
** Note
|
||||
|
||||
Experience has taught me that there's more work than
|
||||
you might think in maintaining support for a language,
|
||||
and that starts with finding a good translation.
|
||||
|
||||
But it does not end there: the pros and cons of adopting
|
||||
a new language have to be carefully considered. Going
|
||||
forward I don't plan to add another language unless a native
|
||||
speaker can help maintain its support.
|
||||
|
||||
* Steps
|
||||
|
||||
** Add new locale to file ~/Rules~
|
||||
The ~/Rules~ file has to be updated
|
||||
|
||||
** Add new locale to array ~Quran.locales~
|
||||
The ~/packages/Quran/src/index.ts~ file has to be updated
|
||||
|
||||
** Add new locale (+ translations)
|
||||
The following files have to updated:
|
||||
- /src/json/t.json
|
||||
- /src/json/surahs.json
|
||||
- /src/json/<locale>/<surahId>/{info,surah}.json
|
||||
|
||||
** Automation
|
||||
The ~/src/json/<locale>/<surahId>/info.json~ files can be
|
||||
generated from the contents of ~/src/json/surahs.json~:
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
user@localhost$ bundle exec rake t:surahs.json
|
||||
#+END_SRC
|
|
@ -1,4 +1,3 @@
|
|||
@import "base/colors";
|
||||
@import "base/breakpoints";
|
||||
@import "base/icon";
|
||||
@import "base/select";
|
||||
|
@ -8,57 +7,96 @@ html {
|
|||
height: 100%;
|
||||
body {
|
||||
height: 100%;
|
||||
color: $black;
|
||||
margin: 0;
|
||||
|
||||
.root {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ltr {
|
||||
font-family: "Kanit Regular";
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.rtl {
|
||||
font-family: "Mada Regular";
|
||||
font-family: "Cairo Regular", "Arial", "Tahoma", sans-serif;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.invisible, .hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.outline-0 {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.scroll-y {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.font-cairo {
|
||||
font-family: "Cairo Regular";
|
||||
}
|
||||
|
||||
.font-cairo-bold {
|
||||
font-family: "Cairo Bold";
|
||||
}
|
||||
|
||||
.font-amiri {
|
||||
font-family: "Amiri Regular", "Scheherazade", "Arial", sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body .root .content.theme {
|
||||
margin: 0 auto;
|
||||
max-width: $breakpoint-md;
|
||||
width: 85%;
|
||||
max-width: $breakpoint-sm;
|
||||
width: 100%;
|
||||
color: var(--color-accent);
|
||||
|
||||
/* <= $breakpoint-sm */
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
header {
|
||||
a[data-testid="h1"] {
|
||||
font-size: x-large;
|
||||
@media screen and
|
||||
(max-width: $breakpoint-kaiOS-portrait) and
|
||||
(orientation: portrait) {
|
||||
font-size: larger;
|
||||
}
|
||||
background: var(--primary-color);
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
nav, div {
|
||||
font-size: large;
|
||||
}
|
||||
}
|
||||
ul.body {
|
||||
font-size: large;
|
||||
scrollbar-color: var(--primary-color) var(--secondary-color);
|
||||
}
|
||||
footer {
|
||||
font-size: large;
|
||||
|
||||
.color-primary {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.color-secondary {
|
||||
color: var(--secondary-color) !important;
|
||||
}
|
||||
|
||||
.color-accent {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.background-primary {
|
||||
background: var(--primary-color);
|
||||
}
|
||||
|
||||
.background-secondary {
|
||||
background: var(--secondary-color);
|
||||
}
|
||||
|
||||
.background-accent {
|
||||
background: var(--accent-color);
|
||||
}
|
||||
|
||||
.border-accent {
|
||||
border: 1px solid var(--accent-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,21 +104,14 @@ body .root .content.theme {
|
|||
* RTL languages
|
||||
*/
|
||||
body .root .content.theme.rtl {
|
||||
header h1 {
|
||||
font-size: xx-large;
|
||||
}
|
||||
|
||||
/* <= $breakpoint-sm */
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
header h1 {
|
||||
font-size: x-large;
|
||||
}
|
||||
}
|
||||
|
||||
/* <= $breakpoint-xxl */
|
||||
@media (max-width: $breakpoint-xxl) {
|
||||
ul.body {
|
||||
font-size: larger;
|
||||
}
|
||||
direction: rtl;
|
||||
header a[data-testid="h1"] {
|
||||
font-family: "Cairo Bold";
|
||||
}
|
||||
}
|
||||
|
||||
html[dir="rtl"] {
|
||||
.font-extrabold {
|
||||
font-family: "Cairo Bold" !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
/* KaiOS: max widths */
|
||||
$breakpoint-kaiOS-portrait: 240px;
|
||||
$breakpoint-kaiOS-landscape: 320px;
|
||||
|
||||
/* Standard max widths */
|
||||
/* max-width */
|
||||
$breakpoint-sm: 576px;
|
||||
$breakpoint-md: 768px;
|
||||
$breakpoint-lg: 992px;
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
$black: #454545;
|
||||
$white: #FFF;
|
|
@ -57,6 +57,7 @@
|
|||
.refresh.icon,
|
||||
.right-arrow.icon,
|
||||
.left-arrow.icon {
|
||||
fill: var(--primary-color);
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
@ -69,7 +70,7 @@
|
|||
g rect {
|
||||
width: 15px;
|
||||
height: 40px;
|
||||
fill: #FFF;
|
||||
fill: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,7 +101,7 @@
|
|||
.root .content.theme.rtl {
|
||||
.stalled.icon {
|
||||
left: 0px;
|
||||
right: 7px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
ul.body.stream {
|
||||
|
@ -120,3 +121,42 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content.theme {
|
||||
ul.body.stream li, footer {
|
||||
.play.icon {
|
||||
fill: var(--primary-color);
|
||||
stroke: var(--secondary-color);
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.pause.icon {
|
||||
rect {
|
||||
fill: var(--primary-color);
|
||||
stroke: var(--secondary-color);
|
||||
stroke-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.refresh.icon {
|
||||
fill: var(--primary-color);
|
||||
}
|
||||
|
||||
.sound-on.icon, .sound-off.icon {
|
||||
polygon {
|
||||
fill: var(--primary-color);
|
||||
stroke-width: 2;
|
||||
}
|
||||
}
|
||||
.right-arrow.icon,
|
||||
.left-arrow.icon {
|
||||
g {
|
||||
fill: var(--secondary--color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stalled.icon {
|
||||
div { background: var(--secondary-color); }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,37 @@
|
|||
.root .content.theme {
|
||||
.green {
|
||||
background: #6d765b !important;
|
||||
}
|
||||
.blue {
|
||||
background: #3383C3 !important;
|
||||
}
|
||||
|
||||
.react-select {
|
||||
z-index: 2;
|
||||
.active {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.react-select.theme-select {
|
||||
ul li,
|
||||
ul li a {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.circle {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 12px;
|
||||
background: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.react-select.language-select {
|
||||
li a {
|
||||
border: 1px solid $black;
|
||||
background: var(--primary-color);
|
||||
color: var(--secondary-color);
|
||||
&:active, &:visited, &:link {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
&:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
body .root .content.theme {
|
||||
ul.body.index {
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
li, li a {
|
||||
li, li a {
|
||||
color: var(--accent-color);
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
@ -30,11 +31,22 @@ body .root .content.theme {
|
|||
}
|
||||
|
||||
footer {
|
||||
@media screen and (max-width: $breakpoint-kaiOS-landscape) {
|
||||
display: none;
|
||||
a {
|
||||
color: var(--accent-color);
|
||||
&:active, &:link, &:visited {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
&:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: $breakpoint-kaiOS-portrait) {
|
||||
display: none;
|
||||
|
||||
input {
|
||||
color: var(--accent-color);
|
||||
border: 1px solid var(--primary-color);
|
||||
&:focus {
|
||||
outline-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint-sm) {
|
||||
|
@ -49,6 +61,12 @@ body .root .content.theme {
|
|||
}
|
||||
}
|
||||
|
||||
@media(max-width: $breakpoint-sm) {
|
||||
input[data-testid="SurahIndex/Filter"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media(hover: none) {
|
||||
input[data-testid="SurahIndex/Filter"] {
|
||||
display: none;
|
||||
|
@ -62,23 +80,19 @@ body .root .content.theme {
|
|||
*/
|
||||
body .root .content.theme.rtl {
|
||||
ul.body.index {
|
||||
li a {
|
||||
span:first-child {
|
||||
border: 1px solid $black;
|
||||
}
|
||||
}
|
||||
|
||||
li.surah {
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
width: 100%;
|
||||
}
|
||||
span.transliterated { display: none; }
|
||||
}
|
||||
}
|
||||
|
||||
/* >= $breakpoint-xxl */
|
||||
@media (min-width: $breakpoint-xxl) {
|
||||
ul.body.index {
|
||||
li.surah a {
|
||||
span:last-child {
|
||||
font-size: larger;
|
||||
@media (hover: hover) {
|
||||
footer {
|
||||
a {
|
||||
&:hover {
|
||||
font-weight: normal;
|
||||
font-family: "Cairo Bold";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,32 +21,8 @@ body .root .content.theme {
|
|||
body .root .content.theme.rtl {
|
||||
ul.body.stream {
|
||||
li.ayah p {
|
||||
line-height: 1.7;
|
||||
max-width: 470px;
|
||||
}
|
||||
}
|
||||
/* <= $breakpoint-sm */
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
ul.body.stream {
|
||||
li.ayah p {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* >= $breakpoint-xxl */
|
||||
@media (min-width: $breakpoint-xxl) {
|
||||
ul.body.stream {
|
||||
$gap: 2rem;
|
||||
margin-top: $gap;
|
||||
li.ayah {
|
||||
font-size: larger;
|
||||
margin-bottom: $gap;
|
||||
}
|
||||
}
|
||||
footer {
|
||||
.timer {
|
||||
font-size: larger;
|
||||
}
|
||||
width: 100%;
|
||||
@extend .font-amiri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +1,5 @@
|
|||
.root .content.theme.blue.rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.root .content.theme.blue {
|
||||
@import "blue/base/colors";
|
||||
|
||||
.color-white {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.color-primary {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.color-secondary {
|
||||
color: $secondary-color;
|
||||
}
|
||||
|
||||
.color-accent {
|
||||
color: $accent-color;
|
||||
}
|
||||
|
||||
.background-primary {
|
||||
background: $primary-color;
|
||||
}
|
||||
|
||||
.background-secondary {
|
||||
background: $secondary-color;
|
||||
}
|
||||
|
||||
.background-accent {
|
||||
background: $accent-color;
|
||||
}
|
||||
--primary-color: #3383C3;
|
||||
--secondary-color: #FFF;
|
||||
--accent-color: #444;
|
||||
}
|
||||
|
||||
@import "blue/base";
|
||||
@import "blue/main/SurahIndex";
|
||||
@import "blue/main/SurahStream";
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
@import "base/icon";
|
||||
@import "base/select";
|
||||
|
||||
.root .content.theme.blue {
|
||||
@import "base/colors";
|
||||
scrollbar-color: $primary-color #FFF;
|
||||
|
||||
header {
|
||||
a[data-testid="h1"] {
|
||||
background: $primary-color;
|
||||
border: 1px solid $black;
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
$primary-color: #3383C3;
|
||||
$secondary-color: #3383C3;
|
||||
$accent-color: lighten($primary-color, 38%);
|
|
@ -1,40 +0,0 @@
|
|||
.content.theme.blue {
|
||||
@import "themes/blue/base/colors";
|
||||
|
||||
ul.body.stream li, footer {
|
||||
.play.icon {
|
||||
fill: $primary-color;
|
||||
stroke: $secondary-color;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.pause.icon {
|
||||
rect {
|
||||
fill: $primary-color;
|
||||
stroke: $secondary-color;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.refresh.icon {
|
||||
fill: $primary-color;
|
||||
}
|
||||
|
||||
.sound-on.icon, .sound-off.icon {
|
||||
polygon {
|
||||
fill: $primary-color;
|
||||
stroke-width: 2;
|
||||
}
|
||||
}
|
||||
.right-arrow.icon,
|
||||
.left-arrow.icon {
|
||||
g {
|
||||
fill: $secondary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stalled.icon {
|
||||
div { background: $secondary-color; }
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
.content.theme {
|
||||
@import "themes/blue/base/colors";
|
||||
.blue {
|
||||
background: $secondary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.content.theme.blue {
|
||||
@import "themes/blue/base/colors";
|
||||
|
||||
.react-select.theme-select {
|
||||
.active .circle {
|
||||
background: $secondary-color;
|
||||
}
|
||||
ul li.blue .circle {
|
||||
background: $secondary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.react-select.language-select {
|
||||
li a {
|
||||
background: $secondary-color;
|
||||
color: $white;
|
||||
&:active, &:visited, &:link {
|
||||
color: $white;
|
||||
}
|
||||
&:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
.root .surah-index.content.theme.blue {
|
||||
@import "themes/blue/base/colors";
|
||||
@import "base/breakpoints";
|
||||
|
||||
ul.body.index {
|
||||
li.surah a {
|
||||
&:active, &:link, &:visited {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
span.id {
|
||||
color: $secondary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
a {
|
||||
&:active, &:link, &:visited {
|
||||
color: $primary-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
&:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
input {
|
||||
border: 1px solid $secondary-color;
|
||||
&:focus {
|
||||
outline-color: $secondary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.root .content.theme.blue.en {
|
||||
@import "themes/blue/base/colors";
|
||||
header {
|
||||
div {
|
||||
color: $secondary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.root .content.theme.blue.rtl {
|
||||
@import "themes/blue/base/colors";
|
||||
ul.body.index {
|
||||
li.surah a {
|
||||
span.id {
|
||||
color: $secondary-color;
|
||||
}
|
||||
span.name {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
.root .content.theme.blue {
|
||||
@import "themes/blue/base/colors";
|
||||
@import "base/breakpoints";
|
||||
|
||||
ul.body.stream {
|
||||
li.ayah {
|
||||
span span {
|
||||
color: $secondary-color;
|
||||
}
|
||||
p { }
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
.timer {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +1,5 @@
|
|||
.root .content.theme.green.rtl {
|
||||
@import "green/base/colors";
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.root .content.theme.green {
|
||||
@import "green/base/colors";
|
||||
|
||||
.color-primary {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.color-secondary {
|
||||
color: $secondary-color;
|
||||
}
|
||||
|
||||
.color-accent {
|
||||
color: $accent-color;
|
||||
}
|
||||
|
||||
.color-white {
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.background-primary {
|
||||
background: $primary-color;
|
||||
}
|
||||
|
||||
.background-secondary {
|
||||
background: $secondary-color;
|
||||
}
|
||||
|
||||
.background-accent {
|
||||
background: $accent-color;
|
||||
}
|
||||
--primary-color: #6d765b;
|
||||
--secondary-color: #FFF;
|
||||
--accent-color: #444;
|
||||
}
|
||||
|
||||
@import "green/base";
|
||||
@import "green/main/SurahIndex";
|
||||
@import "green/main/SurahStream";
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
@import "base/icon";
|
||||
@import "base/select";
|
||||
|
||||
.root .content.theme.green {
|
||||
@import "base/colors";
|
||||
scrollbar-color: $primary-color #FFF;
|
||||
|
||||
header {
|
||||
a[data-testid="h1"] {
|
||||
background: $primary-color;
|
||||
border: 1px solid $black;
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
$primary-color: #6d765b;
|
||||
$secondary-color: #6C755A;
|
||||
$accent-color: #FFF;
|
|
@ -1,41 +0,0 @@
|
|||
.content.theme.green {
|
||||
@import "themes/green/base/colors";
|
||||
|
||||
ul.body.stream li, footer {
|
||||
.play.icon {
|
||||
g path {
|
||||
fill: $primary-color;
|
||||
stroke: $primary-color;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.pause.icon {
|
||||
g rect {
|
||||
fill: $primary-color;
|
||||
stroke: $primary-color;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.refresh.icon {
|
||||
fill: $primary-color;
|
||||
}
|
||||
|
||||
.sound-on.icon, .sound-off.icon {
|
||||
g polygon {
|
||||
fill: $primary-color;
|
||||
}
|
||||
}
|
||||
.right-arrow.icon,
|
||||
.left-arrow.icon {
|
||||
g {
|
||||
fill: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stalled.icon {
|
||||
div { background: $primary-color; }
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
.content.theme {
|
||||
@import "themes/green/base/colors";
|
||||
.green {
|
||||
background: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.content.theme.green {
|
||||
@import "themes/green/base/colors";
|
||||
|
||||
.react-select.theme-select {
|
||||
.active {
|
||||
.circle {
|
||||
background: $primary-color;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.react-select.language-select {
|
||||
li a {
|
||||
background: $primary-color;
|
||||
color: $white;
|
||||
&:active, &:visited, &:link {
|
||||
color: $white;
|
||||
}
|
||||
&:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
.root .surah-index.content.theme.green {
|
||||
@import "base/breakpoints";
|
||||
@import "themes/green/base/colors";
|
||||
|
||||
ul.body.index a {
|
||||
&:active, &:link, &:visited {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
a {
|
||||
&:active, &:link, &:visited {
|
||||
color: $primary-color;
|
||||
}
|
||||
&:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
input {
|
||||
border: 1px solid $primary-color;
|
||||
&:focus {
|
||||
outline-color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
.root .content.theme.green {
|
||||
@import "themes/green/base/colors";
|
||||
@import "base/breakpoints";
|
||||
|
||||
header {
|
||||
h1, h1 a { color: $primary-color; }
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
ul.body.stream {
|
||||
li.ayah {
|
||||
color: $primary-color;
|
||||
p { color: $black; }
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
.timer {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.sound-on.icon, .svg.sound-off.icon {
|
||||
polygon {
|
||||
fill: $primary-color;
|
||||
stroke-width: 2;
|
||||
}
|
||||
}
|
||||
}
|
8
src/css/vendor/postman.css
vendored
8
src/css/vendor/postman.css
vendored
|
@ -8,11 +8,11 @@ body {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.postman.loader {
|
||||
.postman.main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.postman.loader div:first-child {
|
||||
.postman.main div:first-child {
|
||||
width: 200px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
|
@ -21,11 +21,11 @@ body {
|
|||
top: 250px;
|
||||
}
|
||||
|
||||
.postman.loader progress {
|
||||
.postman.main progress {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.postman.loader .percentage {
|
||||
.postman.main .percentage {
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
|
244
src/css/vendor/tail.scss
vendored
244
src/css/vendor/tail.scss
vendored
File diff suppressed because one or more lines are too long
BIN
src/fonts/amiri-regular.ttf
Normal file
BIN
src/fonts/amiri-regular.ttf
Normal file
Binary file not shown.
BIN
src/fonts/cairo-bold.ttf
Normal file
BIN
src/fonts/cairo-bold.ttf
Normal file
Binary file not shown.
BIN
src/fonts/cairo-regular.ttf
Normal file
BIN
src/fonts/cairo-regular.ttf
Normal file
Binary file not shown.
Binary file not shown.
|
@ -9,7 +9,7 @@
|
|||
<meta property="og:type" content="article"/>
|
||||
<meta property="og:title" content="<%= t(context.locale, 'TheNobleQuran') %>"/>
|
||||
<meta property="og:description" content="<%= context.surah.name %>"/>
|
||||
<meta property="og:url" content="<%= base_url %>/<%= context.locale %>/<%= context.surah.urlname %>/"/>
|
||||
<meta property="og:url" content="<%= base_url %>/<%= context.locale %>/<%= context.surah.urlName %>/"/>
|
||||
<meta property="og:image" content="<%= base_url %>/images/og/<%= context.surah.id %>.png?v=<%= commit %>"/>
|
||||
<meta property="og:image:type" content="image/png"/>
|
||||
<% elsif file == "random.html.erb" %>
|
||||
|
@ -19,4 +19,6 @@
|
|||
<meta property="og:url" content="<%= base_url %>/<%= context.locale %>/random/"/>
|
||||
<meta property="og:image" content="<%= base_url %>/images/og/0.png?v=<%= commit %>"/>
|
||||
<meta property="og:image:type" content="image/png"/>
|
||||
<% else %>
|
||||
<% error! "unknown file: #{file}" %>
|
||||
<% end %>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="postman loader" dir="<%= dir %>">
|
||||
<div class="postman main" dir="<%= dir %>">
|
||||
<div>
|
||||
<progress value="0" max="100"></progress>
|
||||
<span class="percentage"><%= t(locale, "loading") %></span>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<title><%= t("en", "TheNobleQuran") %></title>
|
||||
<meta name="description" content="<%= t('en', 'meta.index.description') %>">
|
||||
<%= erb("_version.html.erb") %>
|
||||
<%= erb("_opengraph.html.erb", {page: "redirect.html.erb", context:}) %>
|
||||
<%= erb("_opengraph.html.erb", {file: "redirect.html.erb", context:}) %>
|
||||
<link rel="canonical" href="<%= base_url %>/en/" />
|
||||
<% locales.each do |locale| %>
|
||||
<link
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<%= erb("_postman.html.erb", {locale: context.locale, dir: context.dir}) %>
|
||||
<div class="root h-full"></div>
|
||||
<div class="app mount root h-full"></div>
|
||||
<script src="/js/loaders/surah-index-loader.js?v=<%= commit %>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="description" content="<%= t(context.locale, 'meta.stream.description') %>">
|
||||
<%= erb("_version.html.erb") %>
|
||||
<%= erb("_opengraph.html.erb", {file: "stream-stream.html.erb", context:}) %>
|
||||
<%= erb("_opengraph.html.erb", {file: "surah-stream.html.erb", context:}) %>
|
||||
<link
|
||||
rel="canonical"
|
||||
href="<%= base_url %>/<%= context.locale %>/<%= context.surah.urlName %>/"
|
||||
|
@ -21,9 +21,10 @@
|
|||
</head>
|
||||
<body>
|
||||
<%= erb("_postman.html.erb", {locale: context.locale, dir: context.dir}) %>
|
||||
<div class="root"
|
||||
<div class="app mount root"
|
||||
data-surah-id="<%= context.surah.id %>"
|
||||
data-audio-base-url="<%= audio_base_url %>">
|
||||
data-audio-base-url="<%= audio_base_url %>"
|
||||
data-commit-id="<%= commit %>">
|
||||
</div>
|
||||
<script src="/js/loaders/surah-stream-loader.js?v=<%= commit %>"></script>
|
||||
</body>
|
||||
|
|
BIN
src/images/albumcover-600x600.jpg
Normal file
BIN
src/images/albumcover-600x600.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -1,4 +1,3 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import type { Surah, Ayah } from "Quran";
|
||||
import { SoundOnIcon, SoundOffIcon } from "~/components/Icon";
|
||||
|
||||
|
@ -24,6 +23,7 @@ export function AudioControl({
|
|||
const [enabled, setEnabled] = useState<boolean>(false);
|
||||
const [audioStatus, setAudioStatus] = useState<Maybe<TAudioStatus>>(null);
|
||||
const [audioBaseUrl, setAudioBaseUrl] = useState<Maybe<string>>(null);
|
||||
const [commitId, setCommitId] = useState<Maybe<string>>(null);
|
||||
const play = (audio: HTMLAudioElement) => audio.play().catch(() => null);
|
||||
const pause = (audio: HTMLAudioElement) => audio.pause();
|
||||
|
||||
|
@ -32,7 +32,9 @@ export function AudioControl({
|
|||
return;
|
||||
}
|
||||
if (enabled) {
|
||||
audio.src = [audioBaseUrl, surah.id, `${ayah.id}.mp3`].join("/");
|
||||
audio.src = [audioBaseUrl, surah.id, `${ayah.id}.mp3?v=${commitId}`].join(
|
||||
"/",
|
||||
);
|
||||
play(audio);
|
||||
}
|
||||
}, [hidden, enabled, ayah?.id, audioBaseUrl]);
|
||||
|
@ -42,8 +44,10 @@ export function AudioControl({
|
|||
"[data-audio-base-url]",
|
||||
);
|
||||
const url = el?.dataset?.audioBaseUrl;
|
||||
const commit = el?.dataset?.commitId;
|
||||
if (url?.length) {
|
||||
setAudioBaseUrl(url);
|
||||
setCommitId(commit);
|
||||
} else {
|
||||
console.warn("audio.base_url is not set");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { LanguageSelect, ThemeSelect } from "~/components/Select";
|
||||
import type { TLocale } from "Quran";
|
||||
|
@ -16,8 +15,8 @@ export function Head({ locale, theme, setTheme, children }: Props) {
|
|||
return (
|
||||
<header
|
||||
className={classNames("flex flex-col h-20 mt-4", {
|
||||
"mb-4": locale.direction === "ltr",
|
||||
"mb-6": locale.direction === "rtl",
|
||||
"mb-6": locale.direction === "ltr",
|
||||
"mb-8": locale.direction === "rtl",
|
||||
})}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
|
@ -25,12 +24,12 @@ export function Head({ locale, theme, setTheme, children }: Props) {
|
|||
<a
|
||||
data-testid="h1"
|
||||
href={`/${locale.name}/`}
|
||||
className="flex rounded justify-center p-3 m-0 mb-4 w-full no-underline"
|
||||
className="flex rounded justify-center p-3 m-0 mb-4 w-full no-underline font-semibold color-secondary text-2xl"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
</div>
|
||||
<nav className="flex flex-row justify-between">
|
||||
<nav className="flex flex-row justify-between text-lg">
|
||||
<LanguageSelect locale={locale} />
|
||||
<ThemeSelect theme={theme} setTheme={setTheme} />
|
||||
</nav>
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
onClick: () => void;
|
||||
};
|
||||
|
@ -153,9 +150,9 @@ export function RefreshIcon({ onClick }: Props) {
|
|||
export function StalledIcon() {
|
||||
return (
|
||||
<div className="stalled icon flex justify-end w-10">
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div className="background-primary" />
|
||||
<div className="background-primary" />
|
||||
<div className="background-primary" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from "react";
|
||||
import { Quran, TLocale } from "Quran";
|
||||
import { Select } from "~/components/Select";
|
||||
import classNames from "classnames";
|
||||
|
@ -17,7 +16,8 @@ export function LanguageSelect({ locale }: Props) {
|
|||
<Select.Option
|
||||
key={i}
|
||||
className={classNames(
|
||||
"flex h-6 w-full justify-center no-underline mb-1 rounded",
|
||||
"flex h-6 w-full justify-center no-underline mb-1 rounded border-accent",
|
||||
l.direction === "rtl" ? "font-cairo" : "font-kanit",
|
||||
l.direction,
|
||||
l.name === locale.name ? "active" : undefined,
|
||||
)}
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
import React, { ReactNode, AnchorHTMLAttributes } from "react";
|
||||
import type { ReactNode } from "preact/compat";
|
||||
|
||||
type Rest = AnchorHTMLAttributes<HTMLAnchorElement>;
|
||||
type Props = {
|
||||
value: string;
|
||||
href?: string | undefined;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
children: ReactNode;
|
||||
} & Rest;
|
||||
};
|
||||
|
||||
export function Option({ children, ...rest }: Props) {
|
||||
return <a {...rest}>{children}</a>;
|
||||
export function Option({ children, href, className, onClick }: Props) {
|
||||
const ref = createRef();
|
||||
return (
|
||||
<a href={href} className={className} onClick={onClick} ref={ref}>
|
||||
{children as string}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import React from "react";
|
||||
import { Select } from "~/components/Select";
|
||||
import type { Theme } from "~/hooks/useTheme";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
theme: string;
|
||||
|
@ -17,10 +15,10 @@ export function ThemeSelect({ theme, setTheme }: Props) {
|
|||
<Select.Option
|
||||
key={i}
|
||||
onClick={() => setTheme(t)}
|
||||
className={classNames("block circle mb-1", t)}
|
||||
className="flex justify-end w-10 h-6"
|
||||
value={t}
|
||||
>
|
||||
<span className="block w-full h-full" />
|
||||
<span className={classNames("rounded w-5 h-5", t)} />
|
||||
</Select.Option>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Option } from "./Option";
|
||||
import { ThemeSelect } from "./ThemeSelect";
|
||||
import { LanguageSelect } from "./LanguageSelect";
|
||||
|
@ -28,7 +26,7 @@ function Select({ value, children: options, className }: Props) {
|
|||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"react-select flex flex-col h-full relative",
|
||||
"react-select flex flex-col h-full relative z-10",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
@ -39,7 +37,12 @@ function Select({ value, children: options, className }: Props) {
|
|||
<li
|
||||
key={key}
|
||||
className={classNames({ hidden: isHidden })}
|
||||
onClick={(e) => [e.stopPropagation(), setOpen(!isOpen)]}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const { ref } = n.props;
|
||||
setOpen(!isOpen);
|
||||
ref?.current?.click();
|
||||
}}
|
||||
>
|
||||
{n}
|
||||
</li>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from "react";
|
||||
import { TFunction, formatNumber } from "~/lib/t";
|
||||
import type { Surah, TLocale } from "Quran";
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import React, { useRef, useState, useEffect } from "react";
|
||||
import type { Surah, TLocale } from "Quran";
|
||||
import { useTheme } from "~/hooks/useTheme";
|
||||
import { formatNumber, TFunction } from "~/lib/t";
|
||||
import { Arrow } from "~/components/Icon";
|
||||
import { Head } from "~/components/Head";
|
||||
import { Filter } from "./Filter";
|
||||
import classNames from "classnames";
|
||||
import "@css/main/SurahIndex.scss";
|
||||
|
||||
type Props = {
|
||||
|
@ -39,7 +37,10 @@ export function SurahIndex({ locale, surahs, t }: Props) {
|
|||
<Head locale={locale} theme={theme} setTheme={setTheme}>
|
||||
{t(locale, "TheNobleQuran")}
|
||||
</Head>
|
||||
<ul className="flex flex-wrap body index scroll-y list-none m-0 p-0 pt-4 m-auto w-full h-5/6">
|
||||
<ul
|
||||
lang={locale.name}
|
||||
className="flex flex-wrap body index scroll-y list-none m-0 p-0 pt-4 m-auto w-full h-5/6 text-lg"
|
||||
>
|
||||
{index.map((surah, key) => (
|
||||
<li
|
||||
className={classNames("flex justify-center surah", {
|
||||
|
@ -53,11 +54,11 @@ export function SurahIndex({ locale, surahs, t }: Props) {
|
|||
href={`/${locale.name}/${surah.urlName}/`}
|
||||
>
|
||||
{locale.direction === "ltr" ? (
|
||||
<span className="color-secondary font-extrabold w-10 text-center">
|
||||
<span className="background-primary color-secondary ml-2 mr-3 font-extrabold w-10 text-center">
|
||||
{formatNumber(locale, surah.id)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="flex items-center justify-center color-white background-secondary w-8 h-8 p-1 ml-5 rounded">
|
||||
<span className="flex items-center font-extrabold justify-center color-secondary background-primary w-8 h-8 p-1 ml-5 rounded">
|
||||
{formatNumber(locale, surah.id)}
|
||||
</span>
|
||||
)}
|
||||
|
@ -79,9 +80,9 @@ export function SurahIndex({ locale, surahs, t }: Props) {
|
|||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<footer className="flex flex-row justify-between mb-5 h-12">
|
||||
<footer className="flex flex-row justify-between mb-5 h-12 text-lg">
|
||||
<a
|
||||
className="flex flex-row items-center no-underline"
|
||||
className="flex flex-row items-center color-black no-underline"
|
||||
href={`/${locale.name}/random/`}
|
||||
>
|
||||
{locale.direction === "ltr" ? (
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React, { useEffect, useMemo, useRef } from "react";
|
||||
import type { Surah, Ayah, TAyat, TLocale } from "Quran";
|
||||
import { AudioControl } from "~/components/AudioControl";
|
||||
import { formatNumber, TFunction } from "~/lib/t";
|
||||
|
@ -22,17 +21,15 @@ export function Stream({
|
|||
t,
|
||||
}: Props) {
|
||||
const className = endOfStream || isPaused ? ["scroll-y"] : [];
|
||||
const isRTL = locale.direction === "rtl";
|
||||
const ref = useRef<HTMLUListElement>(null);
|
||||
const ul = useMemo<JSX.Element>(() => {
|
||||
const ltr = locale.direction === "ltr";
|
||||
const rtl = locale.direction === "rtl";
|
||||
return (
|
||||
<ul
|
||||
lang={locale.name}
|
||||
className={classNames(
|
||||
"body stream scroll-y list-none p-0 m-0 h-5/6",
|
||||
"body stream scroll-y text-lg list-none p-0 m-0 h-5/6 mt-4",
|
||||
...className,
|
||||
{ "mt-6": ltr || rtl },
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
|
@ -40,9 +37,12 @@ export function Stream({
|
|||
return (
|
||||
<li
|
||||
key={ayah.id}
|
||||
className={classNames("ayah fade", { "mb-5": rtl, "mb-4": ltr })}
|
||||
className={classNames("ayah fade", {
|
||||
"mb-8": isRTL,
|
||||
"mb-5": !isRTL,
|
||||
})}
|
||||
>
|
||||
<span className="flex h-8 items-center">
|
||||
<span className="flex h-8 items-center color-primary">
|
||||
<AudioControl
|
||||
hidden={!(isPaused || endOfStream)}
|
||||
audio={new Audio()}
|
||||
|
@ -54,14 +54,20 @@ export function Stream({
|
|||
}
|
||||
}}
|
||||
/>
|
||||
<span>
|
||||
<span className="color-primary font-extrabold">
|
||||
{t(locale, "surah")} {formatNumber(locale, surah.id)}
|
||||
{t(locale, "comma")} {t(locale, "ayah")}{" "}
|
||||
{formatNumber(locale, ayah.id)} {t(locale, "of")}{" "}
|
||||
{formatNumber(locale, surah.ayat.length)}
|
||||
</span>
|
||||
</span>
|
||||
<p className="m-0">{ayah.body}</p>
|
||||
<p
|
||||
className={classNames("m-0 color-accent", {
|
||||
"text-2xl mt-5": isRTL,
|
||||
})}
|
||||
>
|
||||
{ayah.body}
|
||||
</p>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import React, { useState, useEffect, useMemo, useRef } from "react";
|
||||
import classNames from "classnames";
|
||||
import type { Surah, Ayah, TAyat, TLocale } from "Quran";
|
||||
import { useTheme } from "~/hooks/useTheme";
|
||||
import { AudioControl, TAudioStatus } from "~/components/AudioControl";
|
||||
|
@ -69,7 +67,7 @@ export function SurahStream({ surah, locale, t }: Props) {
|
|||
isPaused={isPaused}
|
||||
t={t}
|
||||
/>
|
||||
<footer className="flex justify-between items-center h-16">
|
||||
<footer className="flex justify-between items-center h-16 text-lg">
|
||||
{!endOfStream && isPaused && (
|
||||
<PlayIcon onClick={() => setIsPaused(false)} />
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import type { Surah, Ayah, TLocale } from "Quran";
|
||||
import { formatNumber } from "~/lib/t";
|
||||
|
||||
|
@ -67,7 +66,7 @@ export function Timer({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="timer text-base w-10 flex justify-end">
|
||||
<div className="timer font-extrabold text-base w-10 flex justify-end color-primary">
|
||||
{!ms || ms / 1000 <= 0
|
||||
? formatNumber(locale, 0)
|
||||
: formatNumber(locale, ms / 1000, { maximumFractionDigits: 0 })}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { useState } from "react";
|
||||
|
||||
export type Theme = "blue" | "green";
|
||||
type Result = [Theme, (t: Theme) => void];
|
||||
const THEMES: Theme[] = ["blue", "green"];
|
||||
|
|
|
@ -18,11 +18,18 @@ export function T(phrases: PhraseMap<string>): TFunction {
|
|||
}
|
||||
|
||||
export function formatNumber(
|
||||
locale: TLocale,
|
||||
locale: TLocale | string,
|
||||
num: number,
|
||||
options = {},
|
||||
): string {
|
||||
const numl = locale.name === "ar" ? "ar-SA" : locale.name;
|
||||
const name = (() => {
|
||||
if (typeof locale === "string") {
|
||||
return locale;
|
||||
} else {
|
||||
return locale.name;
|
||||
}
|
||||
})();
|
||||
const numl = name === "ar" ? "ar-SA" : name;
|
||||
return new Intl.NumberFormat(numl, {
|
||||
maximumFractionDigits: 1,
|
||||
...options,
|
||||
|
|
|
@ -1,41 +1,41 @@
|
|||
import postman, { item } from "postman";
|
||||
import { formatNumber } from "~/lib/t";
|
||||
|
||||
(function () {
|
||||
const doc = document.documentElement;
|
||||
const rev = doc
|
||||
(async function () {
|
||||
const docel = document.documentElement;
|
||||
const main: HTMLElement = docel.querySelector(".postman.main")!;
|
||||
const css: HTMLStyleElement = docel.querySelector(".postman.css")!;
|
||||
const rev = docel
|
||||
.querySelector("meta[name='revision']")!
|
||||
.getAttribute("content")!;
|
||||
const locale = {
|
||||
name: doc.lang,
|
||||
direction: doc.dir as "rtl" | "ltr",
|
||||
displayName: "",
|
||||
};
|
||||
const fonts = (() => {
|
||||
const f = [
|
||||
item.font("Cairo Regular", "url(/fonts/cairo-regular.ttf)"),
|
||||
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
|
||||
];
|
||||
if (docel.dir === "rtl") {
|
||||
f.push(item.font("Cairo Bold", "url(/fonts/cairo-bold.ttf)"));
|
||||
}
|
||||
return f;
|
||||
})();
|
||||
|
||||
/* Postman */
|
||||
const loader: HTMLElement = doc.querySelector(".postman.loader")!;
|
||||
const style: HTMLStyleElement = doc.querySelector(".css.postman")!;
|
||||
const progressBar: HTMLProgressElement = loader.querySelector("progress")!;
|
||||
const progressNumber: HTMLSpanElement = loader.querySelector(".percentage")!;
|
||||
postman(
|
||||
item.script(`/js/main/surah-index.js?v=${rev}`),
|
||||
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
|
||||
item.font("Mada Regular", "url(/fonts/mada-regular.ttf"),
|
||||
const parcel = await postman(
|
||||
item.script(`/js/main/vendor.js?v=${rev}`, { id: "0" }),
|
||||
item.script(`/js/main/surah-index.js?v=${rev}`, { id: "1" }),
|
||||
...fonts,
|
||||
item.progress((percent: number) => {
|
||||
progressBar.value = percent;
|
||||
progressNumber.innerText = formatNumber(
|
||||
locale,
|
||||
Number(percent.toFixed(0)),
|
||||
);
|
||||
const bar: HTMLProgressElement = main.querySelector("progress")!;
|
||||
const num: HTMLSpanElement = main.querySelector(".percentage")!;
|
||||
bar.value = percent;
|
||||
num.innerText = formatNumber(docel.lang, Number(percent.toFixed(0)));
|
||||
}),
|
||||
)
|
||||
.fetch()
|
||||
.then((pkg) => {
|
||||
[loader, style].forEach((el) => el.remove());
|
||||
pkg.fonts.forEach((f) => document.fonts.add(f));
|
||||
pkg.css.forEach((s) => document.head.appendChild(s));
|
||||
pkg.scripts.forEach((s) =>
|
||||
document.body.removeChild(document.body.appendChild(s)),
|
||||
);
|
||||
).deliver();
|
||||
[main, css].forEach((el) => el.remove());
|
||||
parcel.fonts.forEach((f) => document.fonts.add(f));
|
||||
parcel.css.forEach((s: HTMLElement) => document.head.appendChild(s));
|
||||
parcel.scripts
|
||||
.sort((a, b) => Number(a.id) - Number(b.id))
|
||||
.forEach((s) => {
|
||||
document.body.removeChild(document.body.appendChild(s));
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -1,48 +1,49 @@
|
|||
import postman, { item } from "postman";
|
||||
import { formatNumber } from "~/lib/t";
|
||||
|
||||
(function () {
|
||||
const doc = document.documentElement;
|
||||
const rev = doc
|
||||
(async function () {
|
||||
const docel = document.documentElement;
|
||||
const main = docel.querySelector(".postman.main")!;
|
||||
const css = docel.querySelector(".postman.css")!;
|
||||
const { surahId } = docel.querySelector<HTMLElement>(".app.mount")!.dataset;
|
||||
const rev = docel
|
||||
.querySelector("meta[name='revision']")!
|
||||
.getAttribute("content")!;
|
||||
const { surahId } = document.querySelector<HTMLElement>(".root")!.dataset;
|
||||
const locale = {
|
||||
name: doc.lang,
|
||||
direction: doc.dir as "rtl" | "ltr",
|
||||
displayName: "",
|
||||
};
|
||||
const fonts = (() => {
|
||||
const f = [
|
||||
item.font("Cairo Regular", "url(/fonts/cairo-regular.ttf)"),
|
||||
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
|
||||
];
|
||||
if (docel.dir === "rtl") {
|
||||
f.push(item.font("Cairo Bold", "url(/fonts/cairo-bold.ttf)"));
|
||||
f.push(item.font("Amiri Regular", "url(/fonts/amiri-regular.ttf)"));
|
||||
}
|
||||
return f;
|
||||
})();
|
||||
|
||||
/* Postman */
|
||||
const loader = doc.querySelector(".postman.loader")!;
|
||||
const style = doc.querySelector(".css.postman")!;
|
||||
const progressBar = loader.querySelector("progress")!;
|
||||
const progressNumber: HTMLSpanElement = loader.querySelector(".percentage")!;
|
||||
postman(
|
||||
item.script(`/js/main/surah-stream.js?v=${rev}`),
|
||||
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
|
||||
item.font("Mada Regular", "url(/fonts/mada-regular.ttf"),
|
||||
const parcel = await postman(
|
||||
item.script(`/js/main/vendor.js?v=${rev}`, { id: "0" }),
|
||||
item.script(`/js/main/surah-stream.js?v=${rev}`, { id: "1" }),
|
||||
...fonts,
|
||||
/* eslint-disable */
|
||||
item.json(`/json/${doc.lang}/${surahId}/info.json?v=${rev}`, { className: "json surahinfo" }),
|
||||
item.json(`/json/${doc.lang}/${surahId}/surah.json?v=${rev}`, { className: "json surah" }),
|
||||
item.json(`/json/${docel.lang}/${surahId}/info.json?v=${rev}`, { className: "json surahinfo" }),
|
||||
item.json(`/json/${docel.lang}/${surahId}/surah.json?v=${rev}`, { className: "json surah" }),
|
||||
item.json(`/json/durations/${surahId}.json?v=${rev}`, { className: "json durations" }),
|
||||
/* eslint-enable */
|
||||
item.progress((percent: number) => {
|
||||
progressBar.value = percent;
|
||||
progressNumber.innerText = formatNumber(
|
||||
locale,
|
||||
Number(percent.toFixed(0)),
|
||||
);
|
||||
const bar = main.querySelector("progress")!;
|
||||
const num: HTMLSpanElement = main.querySelector(".percentage")!;
|
||||
bar.value = percent;
|
||||
num.innerText = formatNumber(docel.lang, Number(percent.toFixed(0)));
|
||||
}),
|
||||
)
|
||||
.fetch()
|
||||
.then((pkg) => {
|
||||
[loader, style].forEach((el) => el.remove());
|
||||
pkg.fonts.forEach((f) => document.fonts.add(f));
|
||||
pkg.css.forEach((s) => document.head.appendChild(s));
|
||||
pkg.json.forEach((o) => document.body.appendChild(o));
|
||||
pkg.scripts.forEach((s) =>
|
||||
document.body.removeChild(document.body.appendChild(s)),
|
||||
);
|
||||
).deliver();
|
||||
[main, css].forEach((el) => el.remove());
|
||||
parcel.fonts.forEach((f) => document.fonts.add(f));
|
||||
parcel.css.forEach((s) => document.head.appendChild(s));
|
||||
parcel.json.forEach((o) => document.body.appendChild(o));
|
||||
parcel.scripts
|
||||
.sort((a, b) => Number(a.id) - Number(b.id))
|
||||
.forEach((s) => {
|
||||
document.body.removeChild(document.body.appendChild(s));
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { Surah, TSurah, Quran } from "Quran";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { T } from "~/lib/t";
|
||||
import { SurahIndex } from "~/components/SurahIndex";
|
||||
|
||||
|
@ -16,7 +14,5 @@ import { SurahIndex } from "~/components/SurahIndex";
|
|||
(e: TSurah) => new Surah(e),
|
||||
);
|
||||
|
||||
ReactDOM.createRoot(root).render(
|
||||
<SurahIndex locale={locale} surahs={surahs} t={t} />,
|
||||
);
|
||||
render(<SurahIndex locale={locale} surahs={surahs} t={t} />, root);
|
||||
})();
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { Quran, Surah, Ayah, TSurah } from "Quran";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { T } from "~/lib/t";
|
||||
import { SurahStream } from "~/components/SurahStream";
|
||||
|
||||
|
@ -32,7 +30,5 @@ import { SurahStream } from "~/components/SurahStream";
|
|||
ayah.ms = ms * 1000;
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(root).render(
|
||||
<SurahStream surah={surah} locale={locale} t={t} />,
|
||||
);
|
||||
render(<SurahStream surah={surah} locale={locale} t={t} />, root);
|
||||
})();
|
||||
|
|
16
src/js/main/vendor.ts
Normal file
16
src/js/main/vendor.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { render, createRef } from "preact";
|
||||
import { useState, useEffect, useMemo, useRef } from "preact/hooks";
|
||||
import * as React from "preact/compat";
|
||||
import classNames from "classnames";
|
||||
|
||||
const exports = {
|
||||
React,
|
||||
render,
|
||||
useState,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
createRef,
|
||||
classNames,
|
||||
};
|
||||
Object.assign(window, exports);
|
13
src/js/types/globals.d.ts
vendored
Normal file
13
src/js/types/globals.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
import * as preact from "preact";
|
||||
import * as hooks from "preact/hooks";
|
||||
import classn from "classnames";
|
||||
|
||||
declare global {
|
||||
const render: typeof preact.render;
|
||||
const useState: typeof hooks.useState;
|
||||
const useEffect: typeof hooks.useEffect;
|
||||
const useRef: typeof hooks.useRef;
|
||||
const useMemo: typeof hooks.useMemo;
|
||||
const createRef: typeof preact.createRef;
|
||||
const classNames: typeof classn;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
user-agent: *
|
||||
disallow: /audio/
|
|
@ -1 +0,0 @@
|
|||
Subproject commit f44a820680efcf4a1756c83f7d63e08595b7a643
|
|
@ -1 +0,0 @@
|
|||
Subproject commit c4c4c5079c534c71f9566434458ed3ad9bcd8b19
|
|
@ -1 +0,0 @@
|
|||
Subproject commit c2c43ec193426653e704de90c83d4094d802a7bd
|
Loading…
Reference in a new issue