Repository: h2oai/wave
File: ui/src/text_annotator.tsx (TypeScript)

Code before:
          return (
            <div key={name} data-test={name} className={clas(css.tagWrapper, activeTag === name ? style.activeTag : '')}>
              <div className={clas(css.tag, style.tag, 'wave-s12')} onClick={activateTag(name)}>{label}</div>
            </div>
          )
        })
      }
    </div>
  ),
  XTextAnnotator = ({ model: { name, title, tags, items, trigger, readonly } }: { model: TextAnnotator }) => {
    const
      [activeTag, setActiveTag] = React.useState<S | undefined>(readonly ? undefined : tags[0]?.name),
      [hoveredTagIdx, setHoveredTagIdx] = React.useState<U | null>(),
      [smartSelection, setSmartSelection] = React.useState(true),
      startElPropsRef = React.useRef<TokenMouseEventProps>(),
      tagColorMap = new Map(tags.map(t => [t.name, t.color])),
      [tokens, setTokens] = React.useState(items.reduce((arr, { text, tag }) => {
        // If the smart_selection is True, split by any non-letter and non-number character.
        text.split(/(?=[^a-z0-9])/ig).forEach((textItem) => {
          const
            start = arr.length === 0 ? 0 : arr[arr.length - 1].end + 1,
            end = arr.length === 0 ? textItem.length - 1 : arr[arr.length - 1].end + textItem.length,
            word = textItem.split(/[^a-z]/i),
            hasNonWordChar = word.length === 2

          if (hasNonWordChar) arr.push({ text: textItem.substring(0, 1), tag, start, end: start })
          arr.push(...word[hasNonWordChar ? 1 : 0].split("").map(it => { return { text: it, tag, start: start + (hasNonWordChar ? 1 : 0), end } }))
        })
        return arr
      }, [] as (TokenProps)[])),
      submitWaveArgs = () => {
        let currentText = ''
        let currentTag: S | undefined
        wave.args[name] = tokens.reduce((arr, { text, tag }, idx, tokens) => {
          if (idx === tokens.length - 1) {
            if (tag !== currentTag) {
              arr.push({ text: currentText, tag: currentTag })
              arr.push({ text, tag })
            }
            else {
              arr.push({ text: currentText + text, tag })
            }
          }
          else if (idx !== 0 && tag !== currentTag) {
            arr.push({ text: currentText, tag: currentTag })
            currentText = text
          } else {
            currentText += text
          }
          currentTag = tag
          return arr

Review comment (suggestion):
```suggestion
          if (nonWord) arr.push({ text: textItem.substring(0, 1), tag, start, end: start })
```
Maybe nonLetter or nonAlphabetic? Not sure which is the best.

Why do we only want only a single letter here? (substring(0,1))

Code after:
              <div className={clas(css.tag, style.tag, 'wave-s12')} onClick={activateTag(name)}>{label}</div>
            </div>
          )
        })
      }
    </div>
  ),
  XTextAnnotator = ({ model: { name, title, tags, items, trigger, readonly } }: { model: TextAnnotator }) => {
    const
      [activeTag, setActiveTag] = React.useState<S | undefined>(readonly ? undefined : tags[0]?.name),
      [hoveredTagIdx, setHoveredTagIdx] = React.useState<U | null>(),
      [smartSelection, setSmartSelection] = React.useState(true),
      startElPropsRef = React.useRef<TokenMouseEventProps>(),
      tagColorMap = new Map(tags.map(t => [t.name, t.color])),
      [tokens, setTokens] = React.useState(items.reduce((tokenArr, { text, tag }) => {
        // Split by any non-letter.
        text.split(/([^a-z])/ig).filter(word => word !== '').forEach(textItem => {
          const
            start = tokenArr.length === 0 ? 0 : tokenArr[tokenArr.length - 1].end + 1,
            end = tokenArr.length === 0 ? textItem.length - 1 : tokenArr[tokenArr.length - 1].end + textItem.length

          tokenArr.push(...textItem.split('').map(char => ({ text: char, tag, start, end })))
        })
        return tokenArr
      }, [] as TokenProps[])),
      submitWaveArgs = () => {
        let currentText = ''
        let currentTag: S | undefined
        wave.args[name] = tokens.reduce((arr, { text, tag }, idx, tokens) => {
          if (idx === tokens.length - 1) {
            if (tag !== currentTag) {
              arr.push({ text: currentText, tag: currentTag })
              arr.push({ text, tag })
            }
            else {
              arr.push({ text: currentText + text, tag })
            }
          }
          else if (idx !== 0 && tag !== currentTag) {
            arr.push({ text: currentText, tag: currentTag })
            currentText = text
          } else {
            currentText += text
          }
          currentTag = tag
          return arr
        }, [] as TextAnnotatorItem[]) as unknown as Rec[]
        if (trigger) wave.push()
      },
      activateTag = (tagName: S) => () => setActiveTag(tagName),
      removeAnnotation = (idx: U) => (ev: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {