WireframeEditor

<WireframeEditor> is tsquare's code editor for writing wireframes: the one in the playground. It highlights the language, autocompletes components, props, option words, icons and accents, and underlines problems on their lines. Use it where people edit wireframe text, for example to adjust what a model wrote; for the editor and the board side by side, use the playground.

import { useState } from "react";
import { WireframeEditor } from "@tsquare-js/react/editor";

export function EditSketch({ initial }: { initial: string }) {
  const [text, setText] = useState(initial);
  return <WireframeEditor value={text} onChange={setText} />;
}

The editor fills its box, so give it a height: with the stylesheet it's 20rem unless you set one (or --tsquare-editor-height).

Props

Prop Default
value The text, when your app keeps it in state. Pass onChange too.
defaultValue The starting text, when the editor keeps it.
onChange Called with the whole text after every change.
onProblems Called with the problems when the text has some, after a pause in typing, and with [] once they're fixed. See Problems.
onCursorLine Called with the caret's line (1 is the first) when it moves to another line. See Next to a viewer.
autocomplete true Suggestions for components, props, option words, icons and accents as you type (Ctrl+Space to ask).
lineNumbers true Line numbers in a gutter.
lineWrapping true Wrap long lines instead of scrolling sideways. A wrapped line continues 4 spaces past its own indentation, so it doesn't look like a nested element.
ariaLabel "Wireframe text" The editable area's accessible name.
className Added to the root, next to tsquare-editor.
style On the root.

autocomplete, lineNumbers, lineWrapping and ariaLabel are read when the editor is created; give the component a new key to change them later.

What it does as you type

Control it from your code

A ref gets the editor's methods, for a "Load example" button or jumping to a problem:

import { useRef } from "react";
import { WireframeEditor, type WireframeEditorHandle } from "@tsquare-js/react/editor";

export function EditorWithExample({ example }: { example: string }) {
  const editor = useRef<WireframeEditorHandle>(null);
  return (
    <>
      <button onClick={() => editor.current?.setText(example)}>Load the example</button>
      <WireframeEditor ref={editor} defaultValue="" />
    </>
  );
}
Method
getText() The current text.
setText(text) Replace everything, as one step undo reverts; onChange reports it.
focus() Put the cursor in the editor.
goToLine(n) Select line n, scroll to it and focus the editor.

Next to a viewer

To build your own layout around the editor and a viewer, connect them by line: onCursorLine gives the caret's line, which the viewer's selected outlines, and a click on the board moves the caret with goToLine. This is what WireframePlayground does.

import { useRef, useState } from "react";
import { WireframeViewer } from "@tsquare-js/react";
import { WireframeEditor, type WireframeEditorHandle } from "@tsquare-js/react/editor";

export function Split({ initial }: { initial: string }) {
  const [text, setText] = useState(initial);
  const [caret, setCaret] = useState<number | null>(null);
  const editor = useRef<WireframeEditorHandle>(null);
  return (
    <div style={{ display: "flex", gap: 16 }}>
      <WireframeEditor ref={editor} value={text} onChange={setText} onCursorLine={setCaret} style={{ flex: 1 }} />
      <WireframeViewer
        source={text}
        selected={caret}
        onElementClick={(hit) => { if (hit && hit.type !== "Board") editor.current?.goToLine(hit.line); }}
        style={{ flex: 1 }}
      />
    </div>
  );
}

Why it's imported from @tsquare-js/react/editor

The editor is built on CodeMirror, bundled inside tsquare (about 125 KB compressed after minifying, on top of the renderer). Importing it from its own entry point keeps it out of apps that only show boards: @tsquare-js/react never loads it. It doesn't share CodeMirror with the rest of your app and takes no CodeMirror extensions. If only some pages edit wireframes, load it when it's first needed (lazy() or a dynamic import()).

Colors and fonts follow the stylesheet's variables; the highlighting has its own (--tsquare-syntax-*).

@tsquare-js/react 0.5.0 · View this page on GitHub