Skip to content

Commit

Permalink
refactor annotation components
Browse files Browse the repository at this point in the history
  • Loading branch information
fsimonjetz committed Aug 1, 2024
1 parent 75075e1 commit b4264d4
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 108 deletions.
82 changes: 67 additions & 15 deletions src/fragmentarium/ui/fragment/TokenAnnotationTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,70 @@ import { Fragment } from 'fragmentarium/domain/fragment'
import { AbstractLine } from 'transliteration/domain/abstract-line'
import { isTextLine } from 'transliteration/domain/type-guards'
import DisplayControlLine from 'transliteration/ui/DisplayControlLine'
import { LineNumber } from 'transliteration/ui/line-number'
import { TextLine } from 'transliteration/domain/text-line'
import { AnnotationLineColumns } from 'transliteration/ui/line-tokens'
import { lineComponents } from 'transliteration/ui/TransliterationLines'
import {
annotationLineAccFromColumns,
TextLineColumn,
} from 'transliteration/domain/columns'
import { LineNumber } from 'transliteration/ui/line-number'

function AnnotationLineColumns({
line,
lineIndex,
columns,
}: {
line: TextLine
lineIndex: number
columns: readonly TextLineColumn[]
}): JSX.Element {
const lineAccumulator = annotationLineAccFromColumns(columns)

return (
<>
<tr className={'annotation-line__source'}>
<td>
<LineNumber line={line} />
</td>
{lineAccumulator.flatResult.map((token, index) => {
return (
<td key={index}>
<span
onClick={() =>
console.log(
`clicked on token ${token.token.cleanValue} at line=${lineIndex}, index=${index}`,
token.token
)
}
>
{token.display()}
</span>
</td>
)
})}
</tr>
<tr className={'annotation-line__lemmatization'}>
<td></td>
{lineAccumulator.flatResult.map((token, index) => {
return (
<td key={index}>
<span
onClick={() =>
console.log(
`clicked on lemma of token ${token.token.cleanValue} at line=${lineIndex}, index=${index}`,
token.token
)
}
>
{token.token.uniqueLemma}
</span>
</td>
)
})}
</tr>
</>
)
}

type Props = {
fragment: Fragment
Expand All @@ -24,23 +84,16 @@ export default class TokenAnnotationTool extends Component<Props> {
displayMarkableLine({
line,
lineIndex,
numberOfColumns,
}: {
line: TextLine
lineIndex: number
numberOfColumns: number
}): JSX.Element {
return (
<tr>
<td>
<LineNumber line={line} />
</td>
<AnnotationLineColumns
lineIndex={lineIndex}
columns={line.columns}
maxColumns={numberOfColumns}
/>
</tr>
<AnnotationLineColumns
line={line}
lineIndex={lineIndex}
columns={line.columns}
/>
)
}

Expand All @@ -61,7 +114,6 @@ export default class TokenAnnotationTool extends Component<Props> {
key={index}
line={line}
lineIndex={index}
numberOfColumns={text.numberOfColumns}
/>
) : (
<tr key={index}>
Expand Down
6 changes: 2 additions & 4 deletions src/transliteration/domain/columns.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import _ from 'lodash'
import { Token } from './token'
import { isAkkadianWord, isColumn } from './type-guards'
import {
AnnotationLineAccumulator,
LineAccumulator,
} from 'transliteration/ui/LineAccumulator'
import { LineAccumulator } from 'transliteration/ui/LineAccumulator'
import { PhoneticProps } from 'akkadian/application/phonetics/segments'
import { AnnotationLineAccumulator } from 'transliteration/ui/AnnotationLineAccumulator'

export interface TextLineColumn {
span: number | null
Expand Down
144 changes: 144 additions & 0 deletions src/transliteration/ui/AnnotationLineAccumulator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React from 'react'
import _ from 'lodash'
import {
Protocol,
Shift,
CommentaryProtocol,
isLeftSide,
Token,
} from 'transliteration/domain/token'
import { isEnclosure } from 'transliteration/domain/type-guards'
import DisplayToken from './DisplayToken'
import {
GlossWrapper,
isCloseEnclosure,
isOpenEnclosure,
} from './LineAccumulator'

export class MarkableToken {
readonly token: Token
readonly isInGloss: boolean
readonly protocol: Protocol | null = null
readonly language: string
readonly hasLeadingWhitespace: boolean

constructor(
token: Token,
isInGloss: boolean,
protocol: Protocol | null,
language: string,
hasLeadingWhitespace?: boolean
) {
this.token = token
this.isInGloss = isInGloss
this.protocol = protocol
this.language = language
this.hasLeadingWhitespace = hasLeadingWhitespace || false
}

display(): JSX.Element {
return (
<>
{this.hasLeadingWhitespace && ' '}
<DisplayToken
token={this.token}
bemModifiers={
this.protocol === null
? [this.language]
: [
this.language,
this.protocol.replace('!', 'commentary-protocol-'),
]
}
Wrapper={
this.isInGloss && !isEnclosure(this.token)
? GlossWrapper
: undefined
}
isInPopover={true}
/>
</>
)
}
}

export interface MarkableColumnData {
span: number | null
content: MarkableToken[]
}

export class AnnotationLineAccumulator {
readonly columns: MarkableColumnData[] = []
private inGloss = false
private language = 'AKKADIAN'
private enclosureOpened = false
private protocol: Protocol | null = null
private isFirstWord = true
lemmas: string[] = []

get flatResult(): MarkableToken[] {
return this.columns.flatMap((column) => column.content)
}

applyLanguage(token: Shift): void {
this.language = token.language
}

applyCommentaryProtocol(token: CommentaryProtocol): void {
this.protocol = token.value
}

pushToken(token: Token, index: number): void {
if (_.isEmpty(this.columns)) {
this.addColumn(1)
}

_.last(this.columns)?.content.push(
new MarkableToken(
token,
this.inGloss,
this.protocol,
this.language,
this.requireSeparator(token, index)
)
)
this.enclosureOpened = isOpenEnclosure(token)
}

addColumn(span: number | null): void {
this.columns.push({ span: span, content: [] })
}

openGloss(): void {
this.inGloss = true
}

closeGloss(): void {
this.inGloss = false
}

addColumnToken(token: Token, index: number): void {
switch (token.type) {
case 'LanguageShift':
this.applyLanguage(token)
break
case 'CommentaryProtocol':
this.applyCommentaryProtocol(token)
break
case 'DocumentOrientedGloss':
isLeftSide(token) ? this.openGloss() : this.closeGloss()
break
case 'Column':
throw new Error('Unexpected column token.')
default:
this.pushToken(token, index)
this.isFirstWord = false
}
}

private requireSeparator(token: Token, index: number): boolean {
return (
!this.isFirstWord && !isCloseEnclosure(token) && !this.enclosureOpened
)
}
}
95 changes: 6 additions & 89 deletions src/transliteration/ui/LineAccumulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@ function WordSeparator({
)
}

function isCloseEnclosure(token: Token): boolean {
export function isCloseEnclosure(token: Token): boolean {
return isEnclosure(token) && ['CENTER', 'RIGHT'].includes(token.side)
}

function isOpenEnclosure(token: Token): boolean {
export function isOpenEnclosure(token: Token): boolean {
return isEnclosure(token) && ['CENTER', 'LEFT'].includes(token.side)
}

function GlossWrapper({ children }: PropsWithChildren<unknown>): JSX.Element {
export function GlossWrapper({
children,
}: PropsWithChildren<unknown>): JSX.Element {
return (
<sup className="Transliteration__DocumentOrientedGloss">{children}</sup>
)
Expand Down Expand Up @@ -198,7 +200,7 @@ export class LineAccumulator {
}
}

class MarkableToken {
export class MarkableToken {
readonly token: Token
readonly isInGloss: boolean
readonly protocol: Protocol | null = null
Expand Down Expand Up @@ -244,88 +246,3 @@ class MarkableToken {
)
}
}

export interface MarkableColumnData {
span: number | null
content: MarkableToken[]
}

export class AnnotationLineAccumulator {
readonly columns: MarkableColumnData[] = []
private inGloss = false
private language = 'AKKADIAN'
private enclosureOpened = false
private protocol: Protocol | null = null
private isFirstWord = true
lemmas: string[] = []

getColumns(maxColumns: number): React.ReactNode[] {
return this.columns.map((column: ColumnData, index: number) => (
<td key={index} colSpan={column.span ?? maxColumns}>
{column.content}
</td>
))
}

applyLanguage(token: Shift): void {
this.language = token.language
}

applyCommentaryProtocol(token: CommentaryProtocol): void {
this.protocol = token.value
}

pushToken(token: Token, index: number): void {
if (_.isEmpty(this.columns)) {
this.addColumn(1)
}

_.last(this.columns)?.content.push(
new MarkableToken(
token,
this.inGloss,
this.protocol,
this.language,
this.requireSeparator(token, index)
)
)
this.enclosureOpened = isOpenEnclosure(token)
}

addColumn(span: number | null): void {
this.columns.push({ span: span, content: [] })
}

openGloss(): void {
this.inGloss = true
}

closeGloss(): void {
this.inGloss = false
}

addColumnToken(token: Token, index: number): void {
switch (token.type) {
case 'LanguageShift':
this.applyLanguage(token)
break
case 'CommentaryProtocol':
this.applyCommentaryProtocol(token)
break
case 'DocumentOrientedGloss':
isLeftSide(token) ? this.openGloss() : this.closeGloss()
break
case 'Column':
throw new Error('Unexpected column token.')
default:
this.pushToken(token, index)
this.isFirstWord = false
}
}

private requireSeparator(token: Token, index: number): boolean {
return (
!this.isFirstWord && !isCloseEnclosure(token) && !this.enclosureOpened
)
}
}

0 comments on commit b4264d4

Please sign in to comment.