simian 0.2.0

A command-line tool for exploring and implementing Machine Learning algorithms in Rust.
import { FC } from 'react'
import clsx from 'clsx'
import { Text, Editor, Path } from 'slate'
import { ReactEditor } from 'slate-react'

import { ElementProps } from '@/ui/editor/types'
import { useEditor } from '@/ui/editor/context'

import { Block } from '../../block'
import { contextualize } from '../../context'

import { useHeading } from './context'
import { useTranslations } from '@/i18n/context'

const HeadingBase: FC<
  ElementProps<'heading'> & { className?: string; omitTopMargin?: boolean }
> = ({ attributes, className, children, element, omitTopMargin }) => {
  switch (element.level) {
    case 1:
      return (
        <Block
          as="h2"
          element={element}
          className={clsx([
            'mb-5 border-b dark:border-white/10 pb-2 text-3xl font-semibold tracking-tight',
            !omitTopMargin && 'mt-12',
            className,
          ])}
          {...attributes}
        >
          {children}
        </Block>
      )
    case 2:
      return (
        <Block
          as="h3"
          element={element}
          className={clsx([
            'mb-4 text-2xl font-semibold tracking-tight',
            !omitTopMargin && 'mt-10',
            className,
          ])}
          {...attributes}
        >
          {children}
        </Block>
      )
    case 3:
      return (
        <Block
          as="h4"
          element={element}
          className={clsx([
            'mb-4 text-xl font-semibold tracking-tight',
            !omitTopMargin && 'mt-8',
            className,
          ])}
          {...attributes}
        >
          {children}
        </Block>
      )
    case 4:
      return (
        <Block
          as="h5"
          element={element}
          className={clsx([
            'mb-3 text-lg font-semibold tracking-tight',
            !omitTopMargin && 'mt-8',
            className,
          ])}
          {...attributes}
        >
          {children}
        </Block>
      )
    case 5:
      return (
        <Block
          as="h6"
          element={element}
          className={clsx([
            'mb-3 text-base font-semibold tracking-tight text-gray-500 dark:text-gray-400',
            !omitTopMargin && 'mt-6',
            className,
          ])}
          {...attributes}
        >
          {children}
        </Block>
      )
    case 6:
    default:
      return (
        <Block
          as="h6"
          element={element}
          className={clsx([
            'mb-3 text-sm font-semibold tracking-tight text-gray-500 dark:text-gray-400 uppercase tracking-widest',
            !omitTopMargin && 'mt-6',
            className,
          ])}
          {...attributes}
        >
          {children}
        </Block>
      )
  }
}

export const Heading = contextualize<ElementProps<'heading'>>()(
  [],
  ({ children, ...props }) => {
    const { element } = props

    const { editor, mode } = useEditor()
    const t = useTranslations()
    const { emptyPlaceholder } = useHeading()
    const placeholder = emptyPlaceholder?.(element.level, t)

    // Check if the node is empty
    const isEmpty =
      element.children.length === 1 && (element.children[0] as Text).text === ''

    if (mode == 'read' && isEmpty && !emptyPlaceholder) {
      return null
    }

    const path = ReactEditor.findPath(editor, element)
    const isFirst = path.length > 0 && path[0] === 0
    let isAfterTitle = false

    if (!isFirst && path.length > 0) {
      try {
        const prevPath = Path.previous(path)
        const [prevNode] = Editor.node(editor, prevPath)
        if (prevNode && (prevNode as any).type === 'title') {
          isAfterTitle = true
        }
      } catch {
        // Just in case Path.previous fails
      }
    }

    const omitTopMargin = isFirst || isAfterTitle

    return (
      <HeadingBase
        {...props}
        omitTopMargin={omitTopMargin}
        className={clsx([isEmpty ? 'relative' : ''])}
      >
        {placeholder && isEmpty && (
          <span
            contentEditable={false}
            className="absolute pointer-events-none opacity-30 select-none"
          >
            {placeholder}
          </span>
        )}

        {children}
      </HeadingBase>
    )
  },
)