npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tiptap-solid

v1.0.4

Published

SolidJS components for tiptap v2

Downloads

432

Readme

tiptap-solid

Solid components for tiptap v2

Open in Visual Studio Code NPM Version Total Downloads Monthly Downloads License

Installation

npm i tiptap-solid

# or

yarn add tiptap-solid

Note: This package just provides components for solid. For configuring/customizing the editor, refer tiptap's official documentation.

For any issues with the editor. You may need to open the issue on tiptap's repository

Issues

There's a recurring issue caused by prosemirror dependencies: RangeError: Adding different instances of a keyed plugin (plugin$) These are the steps to fix it:

  1. add all prosemirror dependencies explicityly: yarn add prosemirror-state prosemirror-transform prosemirror-model prosemirror-view
  2. include the packages in vite.config.ts in optimizeDeps
  optimizeDeps: {
    include: [
      'prosemirror-state',
      'prosemirror-transform',
      'prosemirror-model',
      'prosemirror-view'
    ]
  }

Usage

The structure of the helper components has been designed to mimic the React components provided by tiptap, so for further inspiration on how to use tiptap-solid see tiptap's section on React

A Simple editor

import { Component } from "solid-js";
import StarterKit from "@tiptap/starter-kit";
import { createEditor, EditorContent } from "tiptap-solid";

const App: Component = () => {
  const editor = createEditor({
    extensions: [StarterKit],
    content: `Hello world!`,
  });
  return <EditorContent editor={editor()} />;
};

Rendering a Solid component inside the editor:

Create a component Counter.tsx

import { NodeViewProps } from "@tiptap/core";
import { Component } from "solid-js";
import { NodeViewWrapper, NodeViewContent } from "tiptap-solid";

const Counter: Component<NodeViewProps> = (props) => {
  const increase = () => {
    props.updateAttributes({
      count: props.node.attrs.count + 1,
    });
  };

  return (
    <NodeViewWrapper className="solid-component">
      <span contenteditable={false} className="label">
        Solid Component
      </span>

      <div contenteditable={false} className="content">
        <button onClick={increase}>
          This button has been clicked {props.node.attrs.count} times.
        </button>
      </div>

      <NodeViewContent />
    </NodeViewWrapper>
  );
};
export default Counter;

Create a node extension Extension.ts

import { Node, mergeAttributes } from "@tiptap/core";
import { SolidNodeViewRenderer } from "tiptap-solid";
import Counter from "./Counter";

export default Node.create({
  name: "solidComponent",
  group: "block",
  content: 'inline*',
  addAttributes() {
    return {
      count: {
        default: 0,
      },
    };
  },
  parseHTML() {
    return [
      {
        tag: "solid-component",
      },
    ];
  },
  renderHTML({ HTMLAttributes }) {
    return ["solid-component", mergeAttributes(HTMLAttributes)];
  },
  addNodeView() {
    return SolidNodeViewRenderer(Counter);
  },
});

Register extension in your editor, and use the component's tag App.tsx

import { Component } from "solid-js";
import StarterKit from "@tiptap/starter-kit";
import { createEditor, EditorContent } from "tiptap-solid";
import Extension from "./Extension";

const App: Component = () => {
  const editor = createEditor({
    extensions: [StarterKit, Extension],
    content: `
    <p>
      This is still the text editor you’re used to, but enriched with node views.
    </p>

    <solid-component count="5">
      <p>
        Editable
      </p>
    </solid-component>
    
    <p>
      Did you see that? That’s a Solid component. We are really living in the future.
    </p>
    `,
  });

  return <EditorContent editor={editor()} />;
};

Solid Contexts

solid works with the concept of reactivity owners. Any createEffect, useContext, etc. is tied to the owner it's defined in.

Owners can have children and parents similarly to how components have children and parents, when you try to use a context. Solid will look up the chain of reactivity owners for the closest one that has the context.

The custom node components in tiptap-solid are rendered somewhat independently, but the library has some internal logic that automatically attaches the owner of EditorContent to any node components rendered inside of it.

The issue is if you need to use contexts inside of the extensions classes. The classes are instantiated and called outside of the usual reactivity tree, and don't have access to any of the contexts.

As a workaround the library exports a function called getTiptapSolidReactiveOwner that will return the owner of the EditorContent component. Using the return with runWithOwner will let you have access to any context editor has access to.

Example:

export default Node.create({
  name: "solidComponent",
  group: "block",
  atom: true,
  addAttributes() {
    return {
      count: {
        default: 0,
      },
    };
  },
  parseHTML() {
    return [
      {
        tag: "solid-component",
      },
    ];
  },
  renderHTML({ HTMLAttributes }) {
    const owner = getTiptapSolidReactiveOwner(this.editor);
    const context = runWithOwner(owner, () => useContext(SomeContext));
    // Do some logic based on the context, render stuff differently, etc.
    return ["solid-component", mergeAttributes(HTMLAttributes)];
  },
  addNodeView() {
    return SolidNodeViewRenderer(Counter);
  },
});