0.1.1 • Published 9 months ago

tm-textarea v0.1.1

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

📄 tm-textarea

pnpm

Textarea with syntax highlighting powered by solid-js, @lume/element and vscode-oniguruma.

https://github.com/user-attachments/assets/6e785c75-75ae-4274-a904-5e1004153b76

Table of Contents

Installation

npm i tm tm-textarea
# or
yarn add tm tm-textarea
# or
pnpm add tm tm-textarea

Custom Element (tm-textarea)

The main export is a custom-element <tm-textarea/> powered by @lume/element

import type { Grammar, Theme } from 'tm-textarea/tm'

interface tmTextareaAttributes extends ComponentProps<'div'> {
  grammar?: Grammar
  theme?: Theme
  value?: string
  editable?: boolean
  stylesheet?: string | CSSStyleSheet
  onInput?: (event: InputEvent & { currentTarget: tmTextareaElement }) => void
}

Usage

import 'tm-textarea'

import { setCDN } from 'tm-textarea/cdn'
setCDN('/tm')

export default () => (
  <tm-textarea
    grammar="tsx"
    theme="andromeeda"
    value="const sum = (a: string, b: string) => a + b"
    editable={true}
    style={{
      padding: '10px',
      'font-size': '16pt',
    }}
    stylesheet="code, code * { font-style:normal; }"
    onInput={e => console.log(e.currentTarget.value)}
  />
)

Styling The Custom Element

Some DOM ::part() are exported.

  • root exposes root container.
  • code exposes the code tag.
  • line exposes the lines.
  • textarea exposes textarea to maybe change the selection color.
tm-textarea {
  min-height: 100%;
  min-width: 100%;
  padding: 10px;
  line-height: 16pt;
}

/* overwrite the theme background-color */
tm-textarea::part(root) {
  background: transparent;
  /* set a  color for meanwhile the theme loads */
  color: grey;
}

/* overwrite the selected text background color */
tm-textarea::part(textarea)::selection {
  background: deepskyblue;
}

/* add line-numbers */
tm-textarea::part(line)::before {
  display: inline-block;
  counter-reset: variable calc(var(--line-number) + 1);
  min-width: 7ch;
  content: counter(variable);
}

tm-textarea::part(textarea) {
  margin-left: 7ch;
}

The attribute stylesheet could be used as a last resort to customize the theme. In the following example we avoid italics in the rendered coded. The stylesheet is created, cached and possibly reused on the different tm-textarea instances.

<tm-textarea
  grammar="tsx"
  theme="andromeeda"
  value="const sum = (a: string, b: string) => a + b"
  stylesheet="code, code * { font-style: normal; }"
/>

Solid Component (tm-textarea/solid)

A solid component of tm-textarea is available at tm-textarea/solid

import { Grammar, Theme } from 'tm-textarea/tm'

interface tmTextareaProps extends Omit<ComponentProps<'div'>, 'style'> {
  grammar: Grammar
  theme: Theme
  value: string
  editable?: boolean
  style?: JSX.CSSProperties
  onInput?: (event: InputEvent & { currentTarget: HTMLTextAreaElement }) => void
}

Usage

import { TmTextarea } from 'tm-textarea/solid'

export default () => (
  <TmTextarea
    grammar="tsx"
    theme="min-light"
    value="const sum = (a: string, b: string) => a + b"
    editable={true}
    style={{
      padding: '10px',
      'font-size': '16pt',
    }}
    onInput={e => console.log(e.currentTarget.value)}
  />
)

CDN (tm-textarea/cdn)

To ease development we provide a way to set themes/grammars by setting the theme or grammar property with a string. Without configuration these are resolved to tm-themes and tm-grammars hosted on esm.sh.

To provide a way to customize how these keys are resolved we provide a global function setCDN, exported from tm-textarea. This function accepts as arguments either a base-url or a callback-function.

When given a base-url, this will be used to fetch

  • ${cdn}/tm-themes/themes/${theme}.json for the themes
  • ${cdn}/tm-grammars/grammars/${grammar}.json for the grammars
  • ${cdn}/vscode-oniguruma/release/onig.wasm for the oniguruma wasm-file

When given a callback, the returned string will be used to fetch instead.

Usage

import { setCDN } from 'tm-textarea/cdn'

// Set absolute base-url
setCDN('https://unpkg.com')

// Set relative base-url (for local hosting)
setCDN('/assets/tm')

// Use the callback-form
setCDN((type, id) => (type === 'oniguruma' ? `./oniguruma.wasm` : `./${type}/${id}.json`))

Themes & Grammars (tm-textarea/tm)

We export a list of textmate grammars and themes that are hosted on tm-grammars and tm-themes. These are used internally and maintained by shiki.

import type { Theme, Grammar } from 'tm-textarea/tm'
import { themes, grammars } from 'tm-textarea/tm'

Bindings

In addition to the core functionality, tm-textarea provides bindings that enhance the text editing experience by introducing keyboard shortcuts and behaviors that are common in code editors.

TabIndentation (tm-textarea/bindings/tab-indentation)

The TabIndentation binding enables tab and shift-tab indentation for a native textarea of tm-textarea. It allows users to easily increase or decrease the indentation level of lines or selected blocks of text.

import { TmTextareaElement } from 'src'

interface TabIndentation {
  /** Adds event listeners to the passed element for handling 'keydown' and 'input' events specific to indentation. */
  binding: (element: HTMLTextAreaElement | TmTextareaElement) => () => void;
  /** Dispatches `formatIndent` and `formatOutdent` event-types when pressing tab */
  onKeyDown: (event: KeyboardEvent & { currentTarget: TmTextareaElement | HTMLTextAreaElement }) => void;
  /** Add indentation on `formatIndent` and `formatOutdent` event-type.*/
  onInput: (event: InputEvent & { currentTarget: TmTextareaElement | HTMLTextAreaElement }) => void;
  /** Format leading whitespace of given string according to given tab-size. */
  format: (source: string, tabSize: number) => string;
  /** Utilities */
  getLeadingWhitespace: (source: string) => string;
  getLineStart: (value: string, position: number) => number;
  getIndentationSegments: (leadingWhitespace: string, tabSize: number) => string[];

Features

  • Indentation and Outdentation: Automatically adjusts the indentation level based on the tab size.
  • Multi-Line Selection: Supports indenting and outdenting multiple lines at once.
  • Customizable: Works with any specified tab size and can be customized further if needed.

Importing and Usage

import { TmTextarea } from 'tm-textarea/solid'
import { TabIndentation } from 'tm-textarea/bindings/tab-indentation'
import source from "./source"

const App = () => {
  return (
    <TmTextarea
      ref={TabIndentation.binding}
      value={TabIndentation.format(source)}
      grammar="tsx"
      theme="andromeeda"
    />
  )
}

export default App