From 4990f999d3fb9811fae59321a15676d9518e7f44 Mon Sep 17 00:00:00 2001 From: "Bhajneet S.K" Date: Tue, 3 Jan 2023 16:29:25 -0500 Subject: [PATCH] feat: add syntax colors to indicate pauses in source (#408) * feat: add syntax colors to indicate pauses in source * refactor: clean up lint error --- frontend/src/components/AsciiGurmukhi.tsx | 80 ++++++++++++- frontend/src/helpers/theme.ts | 11 ++ frontend/src/screens/Collections.tsx | 2 +- frontend/src/screens/LineView.tsx | 2 +- frontend/src/screens/SourceView.tsx | 130 ++++++++++------------ 5 files changed, 147 insertions(+), 78 deletions(-) diff --git a/frontend/src/components/AsciiGurmukhi.tsx b/frontend/src/components/AsciiGurmukhi.tsx index 23896214..ffaaab7f 100644 --- a/frontend/src/components/AsciiGurmukhi.tsx +++ b/frontend/src/components/AsciiGurmukhi.tsx @@ -1,8 +1,17 @@ -import { ReactNode } from 'react' +import { stripVishraams } from 'gurmukhi-utils' import { createUseStyles } from 'react-jss' +import theme from '../helpers/theme' + type AsciiGurmukhiProps = { - children?: ReactNode, + form?: Form, + text: string, +} + +export const enum Form { + plain, + continuous, + syntactical, } const useStyles = createUseStyles( { @@ -11,14 +20,77 @@ const useStyles = createUseStyles( { fontSize: '1.05em', fontWeight: 700, }, + + heavy: { + color: theme.HeavyVishraam, + }, + + medium: { + color: theme.MediumVishraam, + }, + + light: { + color: theme.LightVishraam, + }, + + '@media (prefers-color-scheme: dark)': { + heavy: { + color: theme.HeavyVishraamDarkScheme, + }, + + medium: { + color: theme.MediumVishraamDarkScheme, + }, + + light: { + color: theme.LightVishraamDarkScheme, + }, + }, } ) -const AsciiGurmukhi = ( { children }: AsciiGurmukhiProps ) => { +const SyntacticalGurmukhi = ( { text }: { text: string } ) => { const classes = useStyles() return ( - {children} + {text.split( ' ' ).map( ( word, index, array ) => { + if ( word.endsWith( ';' ) ) { + return {`${word.slice( 0, -1 )} `} + } + if ( word.endsWith( ',' ) ) { + return {`${word.slice( 0, -1 )} `} + } + if ( word.endsWith( '.' ) ) { + return {`${word.slice( 0, -1 )} `} + } + if ( index === array.length - 1 ) { + return word + } + return `${word} ` + } )} + + ) +} + +const AsciiGurmukhi = ( { form = Form.plain, text }: AsciiGurmukhiProps ) => { + const classes = useStyles() + + if ( form === Form.continuous ) { + return ( + + {stripVishraams( text ).replaceAll( ' ', '' )} + + ) + } + + if ( form === Form.syntactical ) { + return + } + + // if ( form === Form.plain ) + return ( + + {stripVishraams( text )} ) } diff --git a/frontend/src/helpers/theme.ts b/frontend/src/helpers/theme.ts index d32fb5ba..22a1abdb 100644 --- a/frontend/src/helpers/theme.ts +++ b/frontend/src/helpers/theme.ts @@ -24,6 +24,9 @@ const theme = { Gap: '0.5rem', BlankSpace: '0.2rem', + Shader: 'rgba(0% 0% 0% / 8%)', + Highlighter: 'rgba(100% 100% 100% / 8%)', + Separator: 'rgba(87.02% 86.48% 85.43% / 40%)', // lch(88% 1.5 85) SeparatorDarkScheme: 'rgba(28.38% 27.48% 27.77% / 40%)', // lch(30% 1.125 355) @@ -59,6 +62,14 @@ const theme = { Red: 'rgb(83.05% 24% 29.82%)', // lch(50% 66 25) RedDarkScheme: 'rgb(100% 52.49% 52.68%)', // lch(70% 53 25) + + HeavyVishraam: 'rgb(55.17% 30% 13.76%)', // lch(40% 44 55) + MediumVishraam: 'rgb(9.32% 42.55% 19.52%)', // lch(40% 44 145) + LightVishraam: 'rgb(53% 27.31% 53.51%)', // lch(40% 44 295) + + HeavyVishraamDarkScheme: 'rgb(68.3% 53.37% 44.51%)', // lch(40% 44 55) + MediumVishraamDarkScheme: 'rgb(45.94% 60.18% 47.63%)', // lch(40% 44 145) + LightVishraamDarkScheme: 'rgb(57.97% 54.89% 70.53%)', // lch(40% 44 295) } export default theme diff --git a/frontend/src/screens/Collections.tsx b/frontend/src/screens/Collections.tsx index b3469f7c..b472ed5e 100644 --- a/frontend/src/screens/Collections.tsx +++ b/frontend/src/screens/Collections.tsx @@ -44,7 +44,7 @@ const Collections = ( { setVisibleCollections }: CollectionsProps ) => { key={id} onClick={() => handleOnClick( `/sources/${id}/page/1/line/0` )} > - {nameGurmukhi} + ) )} diff --git a/frontend/src/screens/LineView.tsx b/frontend/src/screens/LineView.tsx index 99d8670c..97b043f7 100644 --- a/frontend/src/screens/LineView.tsx +++ b/frontend/src/screens/LineView.tsx @@ -260,7 +260,7 @@ const LineView = ( { sources }: LineViewProps ) => { target="_blank" rel="noreferrer" > - {word} + {toSyllabicSymbols( word )} diff --git a/frontend/src/screens/SourceView.tsx b/frontend/src/screens/SourceView.tsx index 0e80322f..f5c702f9 100644 --- a/frontend/src/screens/SourceView.tsx +++ b/frontend/src/screens/SourceView.tsx @@ -1,6 +1,5 @@ /* eslint-disable jsx-a11y/click-events-have-key-events */ -import { stripVishraams } from 'gurmukhi-utils' import { useAtomValue } from 'jotai' import { mapValues } from 'lodash' import { SkipBack, SkipForward } from 'lucide-react' @@ -11,7 +10,7 @@ import { Link, useLocation, useNavigate, useParams } from 'react-router-dom' import useSWR from 'swr' import { useDebounce } from 'use-debounce' -import AsciiGurmukhi from '../components/AsciiGurmukhi' +import AsciiGurmukhi, { Form } from '../components/AsciiGurmukhi' import Button from '../components/Button' import Content from '../components/Content' import Error from '../components/Error' @@ -33,7 +32,7 @@ const useStyles = createUseStyles( { bottom: 0, zIndex: 0, borderTop: '1px solid rgba(0,0,0,0.1)', - background: '#E3E0DC', + background: theme.Shader, width: '100%', '& + $sourceContent': { paddingBottom: `calc(${theme.Gutter})`, @@ -41,18 +40,27 @@ const useStyles = createUseStyles( { }, line: { - marginLeft: '0.2em', + padding: [ theme.Gap, theme.BlankSpace ], + borderRadius: theme.Gap, + outline: [ '2px', 'solid', 'transparent' ], transition: theme.Normally, '&:first-child': { marginLeft: 0, }, '&:hover': { - color: theme.Blue, + backgroundColor: theme.Shader, + }, + }, + + active: { + color: theme.Blue, + '& > span > span': { + filter: 'brightness(1.25) saturate(1.25)', }, }, focused: { - color: theme.Teal, + outlineColor: theme.Blue, }, controlsContent: { @@ -65,11 +73,20 @@ const useStyles = createUseStyles( { '@media (prefers-color-scheme: dark)': { line: { '&:hover': { - color: theme.BlueDarkScheme, + backgroundColor: theme.Highlighter, + }, + }, + active: { + color: theme.BlueDarkScheme, + '& > span > span': { + filter: 'brightness(1.25) saturate(1.5)', }, }, focused: { - color: theme.TealDarkScheme, + outlineColor: theme.BlueDarkScheme, + }, + sourceControls: { + background: theme.Highlighter, }, }, @@ -86,12 +103,12 @@ const useStyles = createUseStyles( { type SourceViewParams = 'page' | 'source' | 'line' const KEY_MAP = { - previousLine: [ 'shift+tab', 'left' ], - nextLine: [ 'tab', 'right' ], + activatePreviousLine: [ 'left' ], + activateNextLine: [ 'right' ], + focusPreviousLine: [ 'shift+tab' ], + focusNextLine: [ 'tab' ], firstLine: [ 'home' ], lastLine: [ 'end' ], - // belowLine: [ 'down' ], - // aboveLine: [ 'up' ], openLine: [ 'enter' ], previousPage: [ 'shift+left', 'pageup' ], nextPage: [ 'shift+right', 'pagedown' ], @@ -152,12 +169,17 @@ const SourceView = ( { sources }: SourceViewProps ) => { const { length, pageNameGurmukhi } = sources.find( ( { id } ) => id === source ) ?? {} - const focusLine = ( line: number ) => { + const activateLine = ( line: number ) => { navigate( `/sources/${source}/page/${page}/line/${line}`, { replace: true } ) lineRefs.current[ line ].scrollIntoView( { block: 'center' } ) } + const focusLine = ( line: number ) => { + lineRefs.current[ line ].focus() + lineRefs.current[ line ].scrollIntoView( { block: 'center' } ) + } + const goToPage = ( nextPage: number ) => { if ( nextPage && nextPage !== rawPage ) navigate( `/sources/${source}/page/${nextPage}/line/0`, { replace: true } ) } @@ -170,12 +192,22 @@ const SourceView = ( { sources }: SourceViewProps ) => { if ( rawPage > 1 ) goToPage( rawPage - 1 ) } - const nextLine = () => { + const activateNextLine = () => { + if ( rawLine < lines!.length - 1 ) activateLine( rawLine + 1 ) + else nextPage() + } + + const activatePreviousLine = () => { + if ( rawLine > 0 ) activateLine( rawLine - 1 ) + else previousPage() + } + + const focusNextLine = () => { if ( rawLine < lines!.length - 1 ) focusLine( rawLine + 1 ) else nextPage() } - const previousLine = () => { + const focusPreviousLine = () => { if ( rawLine > 0 ) focusLine( rawLine - 1 ) else previousPage() } @@ -185,59 +217,13 @@ const SourceView = ( { sources }: SourceViewProps ) => { const onLineEnter = () => navigate( `${location.pathname}/view` ) - const belowLine = () => { - const lineRef = lineRefs.current[ rawLine ] - const { offsetTop, offsetLeft } = lineRef - const { scrollY } = window - - // Scroll element into view - lineRef.scrollIntoView( { block: 'center' } ) - - // Calculate element's relative y position to viewport - const styles = getComputedStyle( lineRef ) - const [ lineHeight ] = styles.lineHeight.split( 'px' ) - const relativeY = offsetTop - scrollY - - // Get below the line element and index - const element = document.elementFromPoint( offsetLeft + 4, +lineHeight + relativeY ) - - const [ index ] = Object - .entries( lineRefs.current ) - .find( ( [ , line ] ) => line === element ) - || [ line ] - - focusLine( +index ) - } - - const aboveLine = () => { - const lineRef = lineRefs.current[ line ] - const { offsetTop, offsetLeft } = lineRef - const { scrollY } = window - - // Scroll element into view - lineRef.scrollIntoView( { block: 'center' } ) - - // Calculate element's relative y position to viewport - const relativeY = offsetTop - scrollY - - // Get above the line element and index - const element = document.elementFromPoint( offsetLeft + 4, relativeY - 4 ) - - const [ index ] = Object - .entries( lineRefs.current ) - .find( ( [ , line ] ) => line === element ) - ?? [ line ] - - focusLine( +index ) - } - const handlers = { - previousLine, - nextLine, + activatePreviousLine, + activateNextLine, + focusPreviousLine, + focusNextLine, firstLine, lastLine, - belowLine, - aboveLine, previousPage, nextPage, openLine: onLineEnter, @@ -259,14 +245,14 @@ const SourceView = ( { sources }: SourceViewProps ) => { - - {pageNameGurmukhi ? `${pageNameGurmukhi} ` : ''} - {rawPage} + + {pageNameGurmukhi ? : ''} + {' '} / {' '} - {length} - + +