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

Code before:
            {/* HACK: Put color underlay under remove icon because its glyph is transparent and doesn't look good on tags. */}
            <span style={removeIconStyle as React.CSSProperties} className={css.iconUnderlay}></span>
          </mark>
        )
      },
      handleMouseDown = (startElProps: TokenMouseEventProps) => () => {
        startElPropsRef.current = startElProps
      },
      handleMouseUp = (endElProps: TokenMouseEventProps) => () => {
        annotate(endElProps)
      },
      handleMouseLeave = (endElProps: TokenMouseEventProps) => (ev: React.MouseEvent<HTMLSpanElement>) => {
        if (startElPropsRef.current !== undefined && (ev.relatedTarget as any)?.nodeName !== 'P') annotate(endElProps) // nodeName prop is not typed yet
      },
      text = tokens.map((token, idx) => {
        return <Token
          key={idx}
          idx={idx}
          tokenProps={token}
          handleMouseDown={handleMouseDown}
          handleMouseUp={handleMouseUp}
          handleMouseLeave={handleMouseLeave}
          getMark={getMark}
          activeColor={activeTag ? tagColorMap.get(activeTag)! : undefined}
        />
      }, [] as React.ReactElement[])

    // eslint-disable-next-line react-hooks/exhaustive-deps
    React.useEffect(() => { wave.args[name] = items as unknown as Rec[] }, [])


    return (
      <div data-test={name} className={readonly ? css.readonly : ''}>
        <div className={clas('wave-s16 wave-w6', css.title)}>{title}</div>
        <AnnotatorTags tags={tags} activateTag={activateTag} activeTag={activeTag} />
        <Fluent.Toggle
          data-test={'toggle'}
          label={'Smart selection'}
          defaultChecked={smartSelection}
          onChange={() => setSmartSelection(!smartSelection)}
          onText="On"
          offText="Off"
          inlineLabel
        />
        <div className={clas(css.content, 'wave-s16 wave-t7 wave-w3')}>{text}</div>
      </div>
    )
  }

Review comment (suggestion):
nit

```suggestion
      text = tokens.map((token, idx) => (<Token
          key={idx}
          idx={idx}
          tokenProps={token}
          handleMouseDown={handleMouseDown}
          handleMouseUp={handleMouseUp}
          handleMouseLeave={handleMouseLeave}
          getMark={getMark}
          activeColor={activeTag ? tagColorMap.get(activeTag) : undefined}
        />
      ))
```
Can also be part of the JSX - probably better.

Code after:
        return (
          <mark
            onMouseOver={onMarkHover(idx)}
            onMouseOut={onMarkMouseOut}
            className={clas(css.mark, isFirst ? css.firstMark : '', isLast ? css.lastMark : '')}
            style={{ backgroundColor: cssVar(color), color: getContrast(color) }}>
            {text}
            <Fluent.Icon iconName='CircleAdditionSolid' styles={{ root: removeIconStyle }} className={clas(css.removeIcon, 'wave-w6')} onMouseUp={removeAnnotation(idx)} />
            {/* HACK: Put color underlay under remove icon because its glyph is transparent and doesn't look good on tags. */}
            <span style={removeIconStyle as React.CSSProperties} className={css.iconUnderlay}></span>
          </mark>
        )
      },
      handleMouseDown = (startElProps: TokenMouseEventProps) => () => startElPropsRef.current = startElProps,
      handleMouseUp = (endElProps: TokenMouseEventProps) => () => annotate(endElProps),
      handleMouseLeave = (endElProps: TokenMouseEventProps) => (ev: React.MouseEvent<HTMLSpanElement>) => {
        const nodeName = (ev.relatedTarget as any)?.nodeName
        if (startElPropsRef.current !== undefined && nodeName !== 'P' && nodeName !== 'MARK') annotate(endElProps) // nodeName prop is not typed yet
      }

    // eslint-disable-next-line react-hooks/exhaustive-deps
    React.useEffect(() => { wave.args[name] = items as unknown as Rec[] }, [])

    return (
      <div data-test={name} className={readonly ? css.readonly : ''}>
        <div className={clas('wave-s16 wave-w6', css.title)}>{title}</div>
        <AnnotatorTags tags={tags} activateTag={activateTag} activeTag={activeTag} />
        <Fluent.Toggle
          data-test='smart-selection'
          label='Smart selection'
          defaultChecked={smartSelection}
          onChange={() => setSmartSelection(prevSelection => !prevSelection)}
          onText="On"
          offText="Off"
          inlineLabel
        />
        <div className={clas(css.content, 'wave-s16 wave-t7 wave-w3')}>{
          tokens.map(({ start, end, text, tag }, idx) => {
            const activeColor = activeTag ? tagColorMap.get(activeTag) : undefined
            return (
              // Use paragraph instead of span to support text highlight.
              <p
                key={idx}
                onMouseDown={handleMouseDown({ key: idx, start, end })}
                onMouseUp={handleMouseUp({ key: idx, start, end })}
                onMouseLeave={handleMouseLeave({ key: idx, start, end })}
                style={{ display: 'inline' }}
                className={activeColor ? style({ $nest: { '&::selection': { background: activeColor, color: getContrast(activeColor) } } }) : undefined}
              >
                {tag ? getMark(text, idx, tag) : text}
              </p>