Reading view feature added
This commit is contained in:
parent
f08592e0f8
commit
cc8f30a8ce
1479
package-lock.json
generated
1479
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,9 @@
|
|||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-i18next": "^15.4.1",
|
"react-i18next": "^15.4.1",
|
||||||
"react-icons": "^5.3.0"
|
"react-icons": "^5.3.0",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
|
"remark-gfm": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
|
|||||||
42
src/components/Popover.jsx
Normal file
42
src/components/Popover.jsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
import { useTextSelection } from '../hooks/useTextSelection';
|
||||||
|
|
||||||
|
function Portal(props) {
|
||||||
|
const mountNode = props.mount || (typeof document !== 'undefined' ? document.body : null);
|
||||||
|
|
||||||
|
if (!mountNode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createPortal(props.children, mountNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Popover(props) {
|
||||||
|
const { children, selectionRef: target } = props;
|
||||||
|
const textSelectionProps = useTextSelection(target);
|
||||||
|
|
||||||
|
// If there's no selection or it's collapsed, don't render anything
|
||||||
|
if (textSelectionProps.isCollapsed || !textSelectionProps.clientRect) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the position of the selected text
|
||||||
|
const { clientRect } = textSelectionProps;
|
||||||
|
const popoverStyle = {
|
||||||
|
position: 'absolute',
|
||||||
|
left: `${clientRect.left + window.scrollX}px`,
|
||||||
|
top: `${clientRect.top + window.scrollY + 40}px`,
|
||||||
|
zIndex: 9999
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Portal mount={props.mount}>
|
||||||
|
<div style={popoverStyle}>
|
||||||
|
{typeof children === 'function'
|
||||||
|
? children(textSelectionProps)
|
||||||
|
: children}
|
||||||
|
</div>
|
||||||
|
</Portal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -13,7 +13,7 @@ export default function Toggle({className}) {
|
|||||||
<center className={className}>
|
<center className={className}>
|
||||||
<div className={styles.switch}>
|
<div className={styles.switch}>
|
||||||
<input id="language-toggle" className={`${styles.checkToggle} ${styles.checkToggleRoundFlat}`} type="checkbox" onChange={onLanguageChange} />
|
<input id="language-toggle" className={`${styles.checkToggle} ${styles.checkToggleRoundFlat}`} type="checkbox" onChange={onLanguageChange} />
|
||||||
<label for="language-toggle"></label>
|
<label htmlFor="language-toggle"></label>
|
||||||
<span className={styles.on}>EN</span>
|
<span className={styles.on}>EN</span>
|
||||||
<span className={styles.off}>AR</span>
|
<span className={styles.off}>AR</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
159
src/hooks/useTextSelection.ts
Normal file
159
src/hooks/useTextSelection.ts
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import { useCallback, useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
|
type ClientRect = Record<keyof Omit<DOMRect, "toJSON">, number>;
|
||||||
|
|
||||||
|
function roundValues(_rect: ClientRect) {
|
||||||
|
const rect = {
|
||||||
|
..._rect
|
||||||
|
}
|
||||||
|
for (const key of Object.keys(rect)) {
|
||||||
|
// @ts-ignore
|
||||||
|
rect[key] = Math.round(rect[key])
|
||||||
|
}
|
||||||
|
return rect
|
||||||
|
}
|
||||||
|
|
||||||
|
function shallowDiff(prev: any, next: any) {
|
||||||
|
if (prev != null && next != null) {
|
||||||
|
for (const key of Object.keys(next)) {
|
||||||
|
if (prev[key] != next[key]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (prev != next) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type Verse = {
|
||||||
|
surahNo?: number,
|
||||||
|
verseNo: number,
|
||||||
|
verseText: string
|
||||||
|
};
|
||||||
|
|
||||||
|
type TextSelectionState = {
|
||||||
|
clientRect?: ClientRect,
|
||||||
|
isCollapsed?: boolean,
|
||||||
|
textContent?: string,
|
||||||
|
selectedVerses?: Verse[],
|
||||||
|
clearSelection?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultState: TextSelectionState = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* useTextSelection(ref)
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* hook to get information about the current text selection
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import { RefObject } from 'react';
|
||||||
|
|
||||||
|
export function useTextSelection(target?: HTMLElement | RefObject<HTMLElement>) {
|
||||||
|
|
||||||
|
const [{
|
||||||
|
clientRect,
|
||||||
|
isCollapsed,
|
||||||
|
textContent,
|
||||||
|
selectedVerses
|
||||||
|
}, setState] = useState<TextSelectionState>(defaultState)
|
||||||
|
|
||||||
|
const reset = useCallback(() => {
|
||||||
|
setState(defaultState)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const clearSelection = useCallback(() => {
|
||||||
|
//@ts-ignore
|
||||||
|
var sel = window.getSelection ? window.getSelection() : document.selection;
|
||||||
|
if (sel) {
|
||||||
|
if (sel.removeAllRanges) {
|
||||||
|
sel.removeAllRanges();
|
||||||
|
} else if (sel.empty) {
|
||||||
|
sel.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handler = useCallback(() => {
|
||||||
|
let newRect: ClientRect
|
||||||
|
const selection = window.getSelection()
|
||||||
|
let newState: TextSelectionState = { }
|
||||||
|
|
||||||
|
if (selection == null || !selection.rangeCount) {
|
||||||
|
setState(newState)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const range = selection.getRangeAt(0)
|
||||||
|
let targetElement: HTMLElement | null | undefined = null;
|
||||||
|
if (target) {
|
||||||
|
if ('current' in target) {
|
||||||
|
targetElement = target.current;
|
||||||
|
} else {
|
||||||
|
targetElement = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (targetElement != null && !targetElement.contains(range.commonAncestorContainer)) {
|
||||||
|
setState(newState)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range == null) {
|
||||||
|
setState(newState)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const contents = range.cloneContents();
|
||||||
|
|
||||||
|
if (contents.textContent != null) {
|
||||||
|
newState.textContent = contents.textContent;
|
||||||
|
newState.selectedVerses = Array.from(contents.querySelectorAll('span[verseno]')).map((span) => {
|
||||||
|
const surahNo = parseInt(span.getAttribute('surahno') || '0');
|
||||||
|
const verseNo = parseInt(span.getAttribute('verseno') || '0');
|
||||||
|
const verseText = span.textContent || '';
|
||||||
|
return { surahNo, verseNo, verseText };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const rects = range.getClientRects()
|
||||||
|
|
||||||
|
if (rects.length === 0 && range.commonAncestorContainer != null) {
|
||||||
|
const el = range.commonAncestorContainer as HTMLElement
|
||||||
|
newRect = roundValues(el.getBoundingClientRect().toJSON())
|
||||||
|
} else {
|
||||||
|
if (rects.length < 1) return
|
||||||
|
newRect = roundValues(rects[0].toJSON())
|
||||||
|
}
|
||||||
|
if (shallowDiff(clientRect, newRect)) {
|
||||||
|
newState.clientRect = newRect
|
||||||
|
}
|
||||||
|
|
||||||
|
newState.isCollapsed = targetElement !== null ? range.collapsed : true;
|
||||||
|
|
||||||
|
setState(newState)
|
||||||
|
}, [target])
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
document.addEventListener('selectionchange', handler)
|
||||||
|
document.addEventListener('keydown', handler)
|
||||||
|
document.addEventListener('keyup', handler)
|
||||||
|
window.addEventListener('resize', handler)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('selectionchange', handler)
|
||||||
|
document.removeEventListener('keydown', handler)
|
||||||
|
document.removeEventListener('keyup', handler)
|
||||||
|
window.removeEventListener('resize', handler)
|
||||||
|
}
|
||||||
|
}, [target])
|
||||||
|
|
||||||
|
return {
|
||||||
|
clientRect,
|
||||||
|
isCollapsed,
|
||||||
|
textContent,
|
||||||
|
selectedVerses,
|
||||||
|
clearSelection
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,29 +2,35 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { FaCopy, FaShareAlt } from 'react-icons/fa';
|
|
||||||
import SeoHead from '../../components/SeoHead';
|
import SeoHead from '../../components/SeoHead';
|
||||||
import styles from '../../styles/Surah.module.css';
|
import styles from '../../styles/Surah.module.css';
|
||||||
import convertToArabicNumerals, { getNumberConversion } from '../../utils/convertToArabicNumerals';
|
import convertToArabicNumerals, { getNumberConversion } from '../../utils/convertToArabicNumerals';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { getSurahName, getSurahRevealationPlace } from '@/utils/surahUtil';
|
import { getSurahName, getSurahRevealationPlace } from '@/utils/surahUtil';
|
||||||
import { useLanguage } from '@/utils/LanguageProvider';
|
import { useLanguage } from '@/utils/LanguageProvider';
|
||||||
import SelectableParagraph from '@/components/SelectableParagraph';
|
import { Button, Modal, Switch } from 'antd';
|
||||||
import { Button, Modal } from 'antd';
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { Popover } from '@/components/Popover';
|
||||||
|
import { useTextSelection } from '@/hooks/useTextSelection';
|
||||||
|
import { FaBook, FaBookOpen, FaCopy, FaShareAlt } from 'react-icons/fa';
|
||||||
|
import Markdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
|
||||||
const SurahPage = ({ surah, prevSurah, nextSurah }) => {
|
const SurahPage = ({ surah, prevSurah, nextSurah }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { language } = useLanguage();
|
const { language } = useLanguage();
|
||||||
|
const inputFocusRef = React.useRef(null);
|
||||||
|
const refVerseContainer = React.useRef(null);
|
||||||
|
const selectedText = useTextSelection(refVerseContainer);
|
||||||
|
|
||||||
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
||||||
|
const [isReadingMode, setIsReadingMode] = React.useState(true);
|
||||||
const [selectedVerse, setSelectedVerse] = React.useState({
|
const [selectedVerse, setSelectedVerse] = React.useState({
|
||||||
surahNo: surah ? surah.number : null,
|
verses: [],
|
||||||
verseNo: null,
|
tafsir: null
|
||||||
text: "",
|
|
||||||
detail: null
|
|
||||||
});
|
});
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
|
||||||
if (!surah) {
|
if (!surah) {
|
||||||
return (
|
return (
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
@ -59,29 +65,27 @@ const SurahPage = ({ surah, prevSurah, nextSurah }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDetailClick = async (detail) => {
|
const handleAction = async () => {
|
||||||
// setSelectedVerse(detail);
|
selectedText.clearSelection();
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
const res = await axios.post(process.env.NEXT_PUBLIC_API_URL + '/tafsir', {
|
||||||
const res = await axios.post(process.env.NEXT_PUBLIC_API_URL + '/tafsir', {
|
verses: selectedText.selectedVerses
|
||||||
verse_key: `${detail.surahNo}:${detail.verseNo}`,
|
});
|
||||||
verse_text: detail.selection
|
if (res.data){
|
||||||
});
|
const verseDetail = {
|
||||||
console.log('Tafsir response:', res.data);
|
verses: selectedText.selectedVerses,
|
||||||
const verseDetail = {
|
tafsir: res.data.tafsir
|
||||||
surahNo: detail.surahNo,
|
};
|
||||||
verseNo: detail.verseNo,
|
setSelectedVerse(verseDetail);
|
||||||
text: detail.selection,
|
}
|
||||||
detail: res.data
|
setLoading(false);
|
||||||
};
|
} catch (error) {
|
||||||
|
console.error('Error during action:', error);
|
||||||
setSelectedVerse(verseDetail);
|
setLoading(false);
|
||||||
setLoading(false);
|
return;
|
||||||
} catch (error) {
|
}
|
||||||
console.error('Error during action:', error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -119,6 +123,9 @@ const SurahPage = ({ surah, prevSurah, nextSurah }) => {
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span><Switch style={{backgroundColor: isReadingMode ? '#4DB6AC' : '#999999'}} checkedChildren={<span className={styles.switch}><FaBookOpen /></span>} unCheckedChildren={<span className={styles.switch}><FaBook /></span>} defaultChecked onChange={() => setIsReadingMode(!isReadingMode)} /></span>
|
||||||
|
|
||||||
<span className={styles.navButtonWrapper}>
|
<span className={styles.navButtonWrapper}>
|
||||||
{nextSurah && (
|
{nextSurah && (
|
||||||
<Link href={`/quran/${nextSurah.number}`} className={styles.navButton}>
|
<Link href={`/quran/${nextSurah.number}`} className={styles.navButton}>
|
||||||
@ -128,11 +135,43 @@ const SurahPage = ({ surah, prevSurah, nextSurah }) => {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{prefaceText && (
|
||||||
<div className={styles.surahText}>
|
|
||||||
{prefaceText && (
|
|
||||||
<p className={styles.bismillah}>{prefaceText}</p>
|
<p className={styles.bismillah}>{prefaceText}</p>
|
||||||
)}
|
)}
|
||||||
|
{isReadingMode ?
|
||||||
|
<div className={styles.surahContainer} ref={refVerseContainer} suppressContentEditableWarning>
|
||||||
|
|
||||||
|
{surah.verses.map((verse, index) => {
|
||||||
|
// const formattedText = `
|
||||||
|
// "${verse.text.ar}"
|
||||||
|
|
||||||
|
// 🌐 ترجمة:
|
||||||
|
|
||||||
|
// "${verse.text.en}"
|
||||||
|
|
||||||
|
// 🔖 — ${surah.name.ar}:${index + 1}`;
|
||||||
|
|
||||||
|
let willPageChange = false;
|
||||||
|
const nextVerse = surah.verses[index + 1];
|
||||||
|
if (nextVerse && nextVerse.page !== verse.page) {
|
||||||
|
willPageChange = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span key={`vp_${index}`}>
|
||||||
|
<span key={`verse_${index}`} className={styles.verseText} surahno={surah.number} verseno={index + 1}>
|
||||||
|
{verse.text.ar}
|
||||||
|
</span>
|
||||||
|
<span key={`stop_${index}`} className={styles.ayahNumber} title={index + 1} aria-label={index + 1}>
|
||||||
|
{convertToArabicNumerals(index + 1)}
|
||||||
|
</span>
|
||||||
|
{willPageChange && <div className={styles.pageSeperator}>{verse.page}</div>}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div className={styles.surahText} suppressContentEditableWarning>
|
||||||
{surah.verses.map((verse, index) => {
|
{surah.verses.map((verse, index) => {
|
||||||
const formattedText = `
|
const formattedText = `
|
||||||
"${verse.text.ar}"
|
"${verse.text.ar}"
|
||||||
@ -146,11 +185,12 @@ const SurahPage = ({ surah, prevSurah, nextSurah }) => {
|
|||||||
return (
|
return (
|
||||||
<div key={index} className={styles.verseContainer}>
|
<div key={index} className={styles.verseContainer}>
|
||||||
<div className={styles.verseBox}>
|
<div className={styles.verseBox}>
|
||||||
<SelectableParagraph surahNo={surah.number} verseNo={index + 1} key={`p_${index}`}
|
{/* <SelectableParagraph surahNo={surah.number} verseNo={index + 1} key={`p_${index}`}
|
||||||
index={index}
|
index={index}
|
||||||
onDetailClick={onDetailClick}
|
onDetailClick={onDetailClick}
|
||||||
content={<p className={styles.verseText} title={verse.text.ar} aria-label={verse.text.ar.split(" ").join("_")}>{verse.text.ar}</p>}>
|
content={<p className={styles.verseText} title={verse.text.ar} aria-label={verse.text.ar.split(" ").join("_")}>{verse.text.ar}</p>}>
|
||||||
</SelectableParagraph>
|
</SelectableParagraph> */}
|
||||||
|
<p className={styles.verseText} title={verse.text.ar} aria-label={verse.text.ar.split(" ").join("_")}>{verse.text.ar}</p>
|
||||||
<p className={styles.verseTextEn} title={verse.text.en} aria-label={verse.text.en.split(" ").join("_")}>{verse.text.en}</p>
|
<p className={styles.verseTextEn} title={verse.text.en} aria-label={verse.text.en.split(" ").join("_")}>{verse.text.en}</p>
|
||||||
</div>
|
</div>
|
||||||
<span className={styles.VerseNumber} title={index + 1} aria-label={index + 1}>
|
<span className={styles.VerseNumber} title={index + 1} aria-label={index + 1}>
|
||||||
@ -168,8 +208,24 @@ const SurahPage = ({ surah, prevSurah, nextSurah }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div> }
|
||||||
|
|
||||||
|
<Popover selectionRef={refVerseContainer}>
|
||||||
|
<div style={{
|
||||||
|
background: 'grey',
|
||||||
|
borderRadius: '10px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
transition: '300ms',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
display: 'flex',
|
||||||
|
userSelect: 'none',
|
||||||
|
}}>
|
||||||
|
<p style={{ margin: '0', padding: '.5em' }}>
|
||||||
|
<Button size="middle" onClick={() => handleAction()}>{t('surah.meta-tafsir')}</Button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
</main>
|
</main>
|
||||||
<Modal
|
<Modal
|
||||||
title={<p>{`${getSurahName(surah, language)}`}</p>}
|
title={<p>{`${getSurahName(surah, language)}`}</p>}
|
||||||
@ -182,13 +238,23 @@ const SurahPage = ({ surah, prevSurah, nextSurah }) => {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
open={isModalOpen}
|
open={isModalOpen}
|
||||||
onCancel={() => setIsModalOpen(false)}
|
onCancel={() => setIsModalOpen(false)}
|
||||||
|
maskClosable={false}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h1 className={styles.title}>{t('surah.title')} {getSurahName(surah, language)}</h1>
|
<h1 className={styles.title}>{t('surah.title')} {getSurahName(surah, language)}</h1>
|
||||||
<div>
|
<div className={styles.detailsParent}>
|
||||||
<p className={styles.details}><b>{t('surah.meta-verse')}</b>: <span>{selectedVerse.verseNo && getNumberConversion(selectedVerse.verseNo, language)}</span></p>
|
<div className={styles.details}><b>{t('surah.meta-selected-verse')}</b>:
|
||||||
<p className={styles.details}><b>{t('surah.meta-selected-verse')}</b>: <span>{selectedVerse.text && selectedVerse.text}</span></p>
|
<span>
|
||||||
<p className={styles.details}><b>{t('surah.meta-tafsir')}</b>: <span>{selectedVerse.detail && selectedVerse.detail.tafsir}</span></p>
|
{selectedVerse.verses && selectedVerse.verses.map((verse, index) => (
|
||||||
|
<div key={`vn_${index}`} className={styles.detailsSelectedVerses}>{`${verse.surahNo}:${verse.verseNo} - ${verse.verseText}`}</div>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.details}><b>{t('surah.meta-tafsir')}</b>:
|
||||||
|
<span>
|
||||||
|
<Markdown remarkPlugins={[remarkGfm]}>{selectedVerse.tafsir && selectedVerse.tafsir}</Markdown>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@ -26,11 +26,22 @@
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details {
|
.detailsParent{
|
||||||
overflow-y: hidden;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailsSelectedVerses {
|
||||||
|
font-family: "hafs";
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
max-height: 200px;
|
|
||||||
|
|
||||||
background-color: var(--box-color);
|
background-color: var(--box-color);
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
@ -70,12 +81,41 @@
|
|||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.surahContainer {
|
||||||
|
width: 60%;
|
||||||
|
text-align: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ayahNumber {
|
||||||
|
font-family: "hafs";
|
||||||
|
font-size: 29px;
|
||||||
|
margin: 0 10px;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageSeperator{
|
||||||
|
background-color: var(--secondary-color);;
|
||||||
|
width: 100%;
|
||||||
|
height: 25px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-white);
|
||||||
|
}
|
||||||
|
|
||||||
/* حاوية الآية */
|
/* حاوية الآية */
|
||||||
.surahText {
|
.surahText {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switch{
|
||||||
|
display: flex;
|
||||||
|
height: 22px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.verseContainer {
|
.verseContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -109,6 +149,7 @@
|
|||||||
.verseText {
|
.verseText {
|
||||||
font-family: "hafs";
|
font-family: "hafs";
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
|
word-break: auto-phrase;
|
||||||
}
|
}
|
||||||
.verseTextEn {
|
.verseTextEn {
|
||||||
font-family: "hafs";
|
font-family: "hafs";
|
||||||
@ -150,6 +191,7 @@
|
|||||||
.navButtonWrapper {
|
.navButtonWrapper {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
min-width: 230px
|
||||||
}
|
}
|
||||||
|
|
||||||
.navButton {
|
.navButton {
|
||||||
@ -208,4 +250,10 @@
|
|||||||
|
|
||||||
.actions button:focus {
|
.actions button:focus {
|
||||||
outline: 2px solid var(--primary-color);
|
outline: 2px solid var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.surahContainer {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user