A completely customizable framework for building rich text editors. (Currently in beta.)

Overview

A completely customizable framework
for building rich text editors.


Why? · Principles · Demo · Examples · Documentation · Contributing!



Slate lets you build rich, intuitive editors like those in Medium, Dropbox Paper or Google Docs—which are becoming table stakes for applications on the web—without your codebase getting mired in complexity.

It can do this because all of its logic is implemented with a series of plugins, so you aren't ever constrained by what is or isn't in "core". You can think of it like a pluggable implementation of contenteditable built on top of React. It was inspired by libraries like Draft.js, Prosemirror and Quill.

🤖 Slate is currently in beta. Its core API is useable right now, but you might need to pull request improvements for advanced use cases, or fixes for some bugs. Some of its APIs are not "finalized" and will have breaking changes over time as we discover better solutions. There isn't currently a 1.0 release schedule, we're still getting the architecture right.

🤖 Slate is also contributor-driven. It is not backed by any huge company, which means that all contributions are voluntary and done by the people who need them. If you need something improved, added, or fixed, please contribute it yourself or no one will. And if you want to become a more active maintainer, let us know in the Slack channel.


Why?

Why create Slate? Well... (Beware: this section has a few of my opinions!)

Before creating Slate, I tried a lot of the other rich text libraries out there—Draft.js, Prosemirror, Quill, etc. What I found was that while getting simple examples to work was easy enough, once you started trying to build something like Medium, Dropbox Paper or Google Docs, you ran into deeper issues...

  • The editor's "schema" was hardcoded and hard to customize. Things like bold and italic were supported out of the box, but what about comments, or embeds, or even more domain-specific needs?

  • Transforming the documents programmatically was very convoluted. Writing as a user may have worked, but making programmatic changes, which is critical for building advanced behaviors, was needlessly complex.

  • Serializing to HTML, Markdown, etc. seemed like an afterthought. Simple things like transforming a document to HTML or Markdown involved writing lots of boilerplate code, for what seemed like very common use cases.

  • Re-inventing the view layer seemed inefficient and limiting. Most editors rolled their own views, instead of using existing technologies like React, so you have to learn a whole new system with new "gotchas".

  • Collaborative editing wasn't designed for in advance. Often the editor's internal representation of data made it impossible to use to for a realtime, collaborative editing use case without basically rewriting the editor.

  • The repositories were monolithic, not small and reusable. The code bases for many of the editors often didn't expose the internal tooling that could have been re-used by developers, leading to having to reinvent the wheel.

  • Building complex, nested documents was impossible. Many editors were designed around simplistic "flat" documents, making things like tables, embeds and captions difficult to reason about and sometimes impossible.

Of course not every editor exhibits all of these issues, but if you've tried using another editor you might have run into similar problems. To get around the limitations of their API's and achieve the user experience you're after, you have to resort to very hacky things. And some experiences are just plain impossible to achieve.

If that sounds familiar, you might like Slate.

Which brings me to how Slate solves all of that...


Principles

Slate tries to solve the question of "Why?" with a few principles:

  1. First-class plugins. The most important part of Slate is that plugins are first-class entities. That means you can completely customize the editing experience, to build complex editors like Medium's or Dropbox's, without having to fight against the library's assumptions.

  2. Schema-less core. Slate's core logic assumes very little about the schema of the data you'll be editing, which means that there are no assumptions baked into the library that'll trip you up when you need to go beyond the most basic use cases.

  3. Nested document model. The document model used for Slate is a nested, recursive tree, just like the DOM itself. This means that creating complex components like tables or nested block quotes are possible for advanced use cases. But it's also easy to keep it simple by only using a single level of hierarchy.

  4. Parallel to the DOM. Slate's data model is based on the DOM—the document is a nested tree, it uses selections and ranges, and it exposes all the standard event handlers. This means that advanced behaviors like tables or nested block quotes are possible. Pretty much anything you can do in the DOM, you can do in Slate.

  5. Intuitive commands. Slate documents are edited using "commands", that are designed to be high-level and extremely intuitive to write and read, so that custom functionality is as expressive as possible. This greatly increases your ability to reason about your code.

  6. Collaboration-ready data model. The data model Slate uses—specifically how operations are applied to the document—has been designed to allow for collaborative editing to be layered on top, so you won't need to rethink everything if you decide to make your editor collaborative.

  7. Clear "core" boundaries. With a plugin-first architecture, and a schema-less core, it becomes a lot clearer where the boundary is between "core" and "custom", which means that the core experience doesn't get bogged down in edge cases.


Demo

Check out the live demo of all of the examples!


Examples

To get a sense for how you might use Slate, check out a few of the examples:

  • Plain text — showing the most basic case: a glorified <textarea>.
  • Rich text — showing the features you'd expect from a basic editor.
  • Markdown preview — showing how to add key handlers for Markdown-like shortcuts.
  • Links — showing how wrap text in inline nodes with associated data.
  • Images — showing how to use void (text-less) nodes to add images.
  • Hovering toolbar — showing how a hovering toolbar can be implemented.
  • Tables — showing how to nest blocks to render more advanced components.
  • Paste HTML — showing how to use an HTML serializer to handle pasted HTML.
  • Mentions — showing how to use inline void nodes for simple @-mentions.
  • See all the examples...

If you have an idea for an example that shows a common use case, pull request it!


Documentation

If you're using Slate for the first time, check out the Getting Started walkthroughs and the Concepts to familiarize yourself with Slate's architecture and mental models.

If even that's not enough, you can always read the source itself, which is heavily commented.

There are also translations of the documentation into other languages:

If you're maintaining a translation, feel free to pull request it here!


Packages

Slate's codebase is monorepo managed with Lerna. It consists of a handful of packages—although you won't always use all of them. They are:

Package Version Size Description
slate Slate's core data model logic.
slate-history A plugin that adds undo/redo history to Slate.
slate-hyperscript A hyperscript tool to write JSX Slate documents!
slate-react React components for rendering Slate editors.

Contributing!

All contributions are super welcome! Check out the Contributing instructions for more info!

Slate is MIT-licensed.

Comments
  • fix editing with

    fix editing with "soft keyboards" (eg. Android, IMEs)

    Do you want to request a feature or report a bug?

    Bug.

    What's the current behavior?

    The issue with using Slate on Android is complex, and due to a difference in how its OS keyboard is designed. On mobile devices, keyboards are starting to move away from the "keys" concepts in many ways...

    • Autocorrect will make changes without any "key" being press.
    • Autosuggest will insert words that don't map to keys.
    • Swipe-to-type will insert entire words in one go, instead of using keys.
    • etc.

    Because of this, it sounds like the Android team (reasonably from their point of view) decided to not reliably fire key-related events.

    As soft input methods can use multiple and inventive ways of inputting text, there is no guarantee that any key press on a soft keyboard will generate a key event: this is left to the IME's discretion, and in fact sending such events is discouraged. You should never rely on receiving KeyEvents for any key on a soft input method. —KeyEvent, Android Reference

    It sounds like the behavior is:

    • The keypress event is never triggered, which is fine for us.
    • Text-based keys like a, b, etc. fire a keydown event but always with a key code of 229, indicating that the key is unidentifiable because the keyboard is still busy processing IME input, which may invalidate the actual key pressed.
    • Pressing keys like enter fires a keydown event as normal, with an event.key that can be recognized? (This is unclear whether this happens or not.)
    • Pressing backspace does not fire a proper keydown event.

    A few different resources:

    • Great Android postmortem writeup: https://docs.google.com/document/d/1Hex89Di-r-Wfpo1DLAtxpetoX588ziXVoNyC87Je3Xc/edit#
    • Chrome "bug": https://bugs.chromium.org/p/chromium/issues/detail?id=118639
    • React issues: https://github.com/facebook/react/issues/4079 https://github.com/facebook/react/issues/6176 https://github.com/facebook/react/issues/11231
    • VSCode's IME test docs: https://github.com/Microsoft/vscode/wiki/IME-Test
    • @danburzo's input methods: https://github.com/danburzo/input-methods

    What's the expected behavior?

    The fix for this is also complicated. There are a handful of different, overlapping pieces of logic that need to change, to accommodate a handful of different input types...

    The first stage is to handle basic insertions, and auto-suggestions...

    • [ ] Remove the preventDefault in onBeforeInput, so that the DOM is updated, and the onInput logic will trigger, diffing the insertion and then "fixing" it.
    • [ ] Update the <Leaf> (and other?) components to increment their key such that React properly unmounts and reconciles the DOM, since it has changed out from under it.

    This is actually the same starting steps as is required for https://github.com/ianstormtaylor/slate/issues/2060, so I'd recommend we solve that issue in its entirety first, to work from a solid base.

    This fixes the actual text insertion pieces, and probably deletions as well. Splitting blocks can still be handled by enter because it still provide proper key codes.

    • [ ] Check that all other behaviors that aren't text insertions (eg. splitting blocks) are handled properly without access to many keydown events.

    And then there's some selection issues, which apparently can mess up Android's IME (and potentially others) if the selection is manually changed during a composition.

    • [ ] Prevent re-rendering the editor on compositionstart and compositionend.
    • [ ] Prevent updating the selection while a composition is taking place.

    I think this would solve the 90% case for soft keyboard input.


    Separately, there's still another question of how to properly handle these kinds of behaviors other plugins. For example if a plugin uses backspace at the start of a block to reset that block, that won't work on Android. So after solving the input issues, we need to step back to an architectural level and solve this plugin handling problem. But that can wait.

    bug ♥ help ⚑ cross platform ⚑ ime ⚑ mobile 
    opened by ianstormtaylor 222
  • Collaborative editing

    Collaborative editing

    @yatskevich and me did a research and tried to design an appropriate architecture for supporting collaborative editing. There are some key points with explanation below.

    The main idea is to implement a kind of Redux-like behavior within Slate. <Editor store={Store} … /> The goal of having Store is ability to have clear separation and understandable flow.

    UseCase:

    • User press key (types symbol)
    • Action is generated
    • Action is dispatched to Store
    • Store updates State
    • setState called
    • Editor re-rendered

    This way allows us to have ability to manage operations in any suitable way (CRDT, OT, etc.) within Store.

    Let’s see how to handle text paste operation in more details:

    1. Action pasteText dispatched to Store (data: {position, text}) No need to get current state, call transform() and return new state. Just dispatch an Action.
    2. Store has a Reducer for handling such an Action If we’re talking about ColloborativeStore - one type, if about SimpleStore - another one. We can even have few implementation of CollaborativeStores (CRDT and OT).

    CollaborativeStore:

    • Splits pasteText operation into a set of atomic insertCharacter operations
    • Applies operations to State
    • Translates this operations set to server
    • Listens for updates from server (i.e. smb else is editing this doc and generates the same type of operations set)

    SimpleStore:

    • Applies operations to State (no need to split to atomic operations)

    In our mind such architecture allows Slate to be more flexible and extensible. But this is not a completely analyzed proposal - just an idea.

    And a few pictures for clarification.

    Workflow Chart: fullsizerender

    Implementation example fullsizerender 5

    pastText in depth: fullsizerender 3

    P.S. Possibly, Store should send Transfroms (not Actions) to Server and get Transforms back.

    @ianstormtaylor: Could you please take a look at this and share your opinion?

    discussion 
    opened by davolokh 125
  • Screen Jumps/Scrolls cursor out of view during editing on iOS (iPad, iPhone)

    Screen Jumps/Scrolls cursor out of view during editing on iOS (iPad, iPhone)

    Do you want to request a feature or report a bug?

    Report a Bug

    What's the current behavior?

    Using the rich text editor on the SlateJS demo site (or any of the demos for that matter):

    http://slatejs.org/#/rich-text

    If you start editing and the cursor goes below the virtual keyboard, the text doesn't scroll up properly. Instead, it jumps all the way to the top of the screen.

    image-2

    Note that the jump happened as soon as I typed anything. In this case, I hit the SPACE on the virtual keyboard.

    This is on an iPad iOS 11 but the same happens on my iPad. The latest version of Slate as on the demo as of today.

    From what I can remember, this did not happen before; however, it may have been quite an old version where it worked properly.

    What's the expected behavior?

    Normally, the screen will stay scrolled to the part of the editor we are editing.

    bug ♥ help ⚑ cross platform 
    opened by thesunny 75
  • consider migrating from Immutable.js

    consider migrating from Immutable.js "Records" to plain objects

    Do you want to request a feature or report a bug?

    Discussion.

    What's the current behavior?

    When Slate was first created, Immutable.js was the best and most popular way to handle immutability in React. However, it has many downsides for our use case:

    • CON: It requires a fromJS step to build the collections/records. Since the objects aren't the native JavaScript data types you get from JSON, we have to have an interim step that instantiates the Immutable.js data model. This can be costly for large documents, and there is no way around it. This is especially problematic in server-side environments where serializing to/from JSON blocks Node's single thread.

    • CON: Reading values is more expensive. Immutable.js is optimized for non-crazy-slow writes, at the expense of read operations being much slower than native JavaScript. Since Slate's model is a tree, with many node lists, this ends up having a significant impact on many of the common operations that take place on a document. (See this question for more information.)

    • CON: It introduces a fairly steep learning curve. People getting started with Slate often get tripped up by not knowing how Immutable.js works, and its documentation isn't easy to understand. This results in lots of "wheel reinvention" because people don't even realize that helper methods are available to them.

    • CON: It makes debugging harder. In addition to a learning curve, debugging Immutable.js objects adds extra challenges. There's a browser extension that helps, but that's not good enough in lots of places, and you end up having to us toJS to print the objects out which is very tedious.

    • CON: It increases bundle size. The first increase in size comes from just including the immutable package in the bundle at all, which adds ~50kb. But there is also a more insidious bundle size increase because the class-based Record API encourages monolithic objects that can't be easy tree-shaken to eliminate unused code. Whereas using a utility-function-based API, similar to Lodash, would not have this issue.

    However, it does have some benefits:

    • PRO: Custom records allow for methods/getters. This has been the primary way that people read values from Slate objects. Because Slate (and rich text in general) deals with some complex data structures, having common things packaged up as methods allows us to reduce a lot of boilerplate and reinventing the wheel for common use cases.

    • PRO: It offers built-in concepts like OrderedSet. These allow for more expressive code in core than otherwise, because we'd need to reinvent the wheel a bit to account for JavaScript not having some of these concepts. Although truth be told this is probably a fairly minimal gain.


    Since Slate was first created, immer has come on the scene (thanks to @klis87 for kickstarting discussion of it in #2190) which offers a way to use native JavaScript data structures while maintaining immutability. This is really interesting, because it could potentially have big performance and simplicity benefits for us.

    All of the CON's above would go away. But we'd also lose the PRO's. That's what I'm most concerned about, and what I'd like to discuss in this issue... to see what a Slate without Immutable.js might look like, and how we could mitigate losing some of its benefits.

    Without the ability to use classes and prototypes for our data models, we'd need to switch to using a more functional approach—exporting helpers in a namespace, like we currently already do for PathUtils. One question is whether this will be painful...

    Looking at our rich-text example, we'd need to change how we do things in several places.

    https://github.com/ianstormtaylor/slate/blob/8eb8e26958ef6c9337e838bfd46fc2cc55d7fe51/examples/rich-text/index.js#L54

    We no longer have getters on our models, so value.activeMarks doesn't work. Instead, we'd need to change this to:

    return Value.getActiveMarks(value).some(mark => mark.type == type)
    

    Similarly, there's no value.blocks any more:

    https://github.com/ianstormtaylor/slate/blob/8eb8e26958ef6c9337e838bfd46fc2cc55d7fe51/examples/rich-text/index.js#L66

    So we'd need:

    return Value.getClosestBlocks(value).some(node => node.type == type)
    

    This is actually nice because we're no longer using potentially expensive getters to handle common use cases—calling functions is more clear.

    But we also can't use helper methods like document.getParent.

    https://github.com/ianstormtaylor/slate/blob/8eb8e26958ef6c9337e838bfd46fc2cc55d7fe51/examples/rich-text/index.js#L148

    Instead we'd need to use:

    const blocks = Value.getClosestBlocks(value)
    const parent = Node.getParent(value.document, blocks[0].key)
    

    Similarly, we can't do:

    https://github.com/ianstormtaylor/slate/blob/8eb8e26958ef6c9337e838bfd46fc2cc55d7fe51/examples/rich-text/index.js#L293-L295

    And would have to instead do:

    const blocks = Value.getBlocks(value)
    const isType = blocks.some(block => {
      return !!Node.getClosest(document, block.key, parent => parent.type == type)
    })
    

    But we also get to remove some expensive code, since we don't need to deserialize anymore:

    https://github.com/ianstormtaylor/slate/blob/8eb8e26958ef6c9337e838bfd46fc2cc55d7fe51/examples/rich-text/index.js#L41-L43

    That's all for the rich text example, but it would definitely be a big change.


    I'm curious to get other folks's input. Are there other PROS or CONS to Immutable.js that I haven't listed above? Are there other ways of solving this that I haven't considered? Any thoughts! Thank you!

    ⚑ perf improvement ✶ breaking discussion ⚑ filesize 
    opened by ianstormtaylor 70
  • Proposal for Sponsored Open Source Android Support

    Proposal for Sponsored Open Source Android Support

    I am considering a sponsored version of an Open Source Android Plugin for Slate for USD $25,000.

    1. This is an alternative to the proposal for a Paid Android Plugin (also proposed by me) for USD $500 per license as described here https://github.com/ianstormtaylor/slate/issues/3573

    2. The Android Plugin will be released as open source under the same license as Slate and will support at least the latest version of Android with similar features and limitations as the 0.47x Android Support

    3. Building Android support is a lot of work. We would love to be able to contribute for free (like Ian!), but the cost is high to burden alone. We contributed Android Support for Slate 0.47 which was working although not perfect and that cost about USD $40,000. That doesn’t include research we did prior to starting or the devices we had to purchase to test on.

    4. It's uncertain how much it will cost to complete the 0.50x version of Slate Android. It may cost more or it may cost less. Either way, we are committing to completing it if we raise the funds. If it costs less, we will not be returning unused funds; if it costs more, we will cover the difference. Note that the 0.50x port is not trivial as another user worked for 3 months on it and it was never completed.

    5. For clarity, we already spent USD $40,000, so even if the port costs $0, there will be no profit from this.

    The port will be completed in partnership with Wayne Leroux with assistance from me. Wayne has built an impressive collaborative editor based on Slate (https://github.com/wleroux/slate-collaborative) so I have confidence in his ability to complete this project. He has also contributed other open source projects (https://github.com/wleroux?tab=repositories). I expect him to be able to complete the project; however, if for any reason he cannot, I will either complete it myself or will return the funds. Wayne and I intend to work together on a different Slate related project which would benefit from Android support.

    If we are unable to raise the funds through sponsorship, we will revert to the paid model at USD $500 per license. For clarity, if we don't raise the funds through sponsorship and use a paid model but later receive the equivalent revenue in licensing fees from the paid model (USD $25,000 or 50 licenses), we will not then open source the code.

    I recognize that this is a non-trivial amount to raise. I expect that companies that can afford to do so, will need to contribute more in order to meet this fundraising goal. To put the amount into perspective, this is the equivalent of contracting a senior developer for about 6 weeks. 3 organizations, contributing the equivalent of a contract valued at 2 weeks worth of work will reach the sponsorship goal (about USD $8000 each) or 50 smaller contributors paying what would be the licensing fee of USD $500.

    Update: Now Live!

    Add Android Support to Slate - KickStarter Campaign Now Live

    opened by thesunny 58
  • Benchmark consistence && Allow users to select benches to run

    Benchmark consistence && Allow users to select benches to run

    How to use this PR before merge

    1. fecth this branch to your local repo
    2. git checkout benchmark-consistence && yarn build && yarn benchmark:save
    3. git checout your-repo && yarn build
    4. git checkout benchmark-consistence && yarn benchmark

    Problem:

    We always have an inconsistent benchmark result. The problems are caused by Most important problem:

    1. before and after in the Matcha is called by each suite, not by each bench.
      Other problems:
    2. We need to call global.gc() before each bench. Avoid calculating gc times in bench
    3. We shall not use babel-node in benchmark. Because code parsing time and other time related in babel-code will be introduced in the benchmark.
    4. We shall have different input in each bench run, to avoid some potential optimization. (Generate new blocks/value with same data)
    5. a user-side problem: I do not want to wait with no output when running the benchmark.
    6. Perhaps we shall use process.cpuUsage, to eliminate the effects of node optimization and influence of other processes.
    7. [Not in this PR] Perhaps we shall run node --expose-gc benchmark multi times for better robustness.

    Proposal: Why we need a benchmark framework instead of using the existing one:

    1. : Other benchmark framework uses process.hrtime() to evaluate the time consuming. It is good for I/O heavy tasks, but our focus is CPU heavy. If we want to eliminate the problem by other processes, we would prefer process.cpuUsage. (For benchmarkJS, it uses lodash.now, which is a slow Date.now system call)

    2. Most benchmark frameworks requires a fixed run number, but our tasks varies from 4 ops/sec (from-json) to 10,000 ops/sec (get-blocks). It is very unhappy to special numbers for each tasks.

    3. There is an adaptive mode in matcha, without specializing the run number, but 3.1 It is out of maintenance 3.2 It calls process.hrtime() in each loop for adaptive mode. I am not sure whether the time waiting for process.hrtime is comparable to 10,000 ops/sec.
      3.3 It does not solve other problems.

    4. We want to exclude the time of gc in our processes, then we want to node --expose-gc and run global.gc each time before we run each bench.

    5. Allow users to config about which benchmarks to run, we do not want to re-run all benchmarks every time.

    ⚑ perf debt 
    opened by zhujinxuan 54
  • Expose transforms

    Expose transforms

    Fixes #903 Fixes #973 Fixes #970 Fixes #912 Fixes #1003 Fixes #841 Fixes #833 Fixes #813 Fixes #767 Fixes #683 Fixes #787 Fixes #1030

    This is a breaking change to the API.

    This PR adds the last piece of functionality to core that I think is required for #259 to have everything needed for Operational Transform–style collaborative editing.

    (Still in progress, but if you have thoughts please comment!)

    A summary of the breaking changes so far...


    onChange now receives a Change object (previously named Transform) instead of a State. Plugins and users will need to now use:

    onChange(change) {
      this.setState({ state: change.state })
    }
    

    Or more tersely:

    onChange({ state }) {
      this.setState({ state })
    }
    

    Which achieves the same behavior as before. This is needed because it enforces that all changes are represented by a single set of operations. Otherwise right now it's possible to do things like state.transform()....apply({ save: false }).transform()....apply() and result in losing the operation information in the history. With OT, we need all transforms that may happen to be exposed and emitted by the editor.


    Similarly, handlers now receive e, data, change instead of e, data, state. Instead of doing return state.transform()....apply() the plugins can now act on the change object directly.

    function onKeyDown(e, data, change) {
      if (data.key == 'enter') {
        return change.splitBlock()
      }
    }
    

    Plugins can still return change... if they want to break the stack from continuing on to other plugins. (Any != null value will break out.) But they can also now not return anything, and the stack will apply their changes and continue onwards. This was previously impossible.


    The onChange and on[Before]Change handlers now receive Change objects. Previously they would also receive a state object, but now they receive change objects like the rest of the plugin API.


    The transform.apply() method is deprecated. Previously this is where the saving into the history would happen, but it created an awkward convention that wasn't necessary. Now operations are saved into the history as they are created with change methods, instead of waiting until the end. You can access the new State of a change at any time via change.state.


    The .apply({ save }) option is now state.change({ save }) instead. This is the easiest way to use it, but requires that you know whether to save or not up front. If you want to use it inline after already saving some changes, you can use the change.setSave(save) flag instead. This shouldn't be necessary for 99% of use cases though.


    The .undo() and .redo() transforms don't save by default. Previously you had to specifically tell these transforms not to save into the history, which was awkward. Now they won't save the operations they're undoing/redoing by default.


    onBeforeChange is no longer called from componentWillReceiveProps, when a new state is passed in as props to the <Editor> component. This caused lots of state-management issues and was weird in the first place because passing in props would result in changes firing. It is now the parent component's responsibility to not pass in improperly formatted State objects.


    The splitNodeByKey change method has changed to be shallow. Previously, it would deeply split to an offset. But now it is shallow and another splitDescendantsByKey change method has been added (with a different signature) for the deep splitting behavior. This is needed because splitting and joining operations have been changed to all be shallow, which is required so that operational transforms can be written against them.


    The shape of many operations has changed. This was needed to make operations completely invertible without any extra context. The operations were never really exposed in a consumable way, so I won't detail all of the changes here, but feel free to look at the source to see the details.


    All references to "joining" nodes is now called "merging". This is to be slightly clearer, since merging can only happen with adjacent nodes already, and to have a nicer parallel with "splitting", as in cells. The operation is now called merge_node, and the transforms are now merge*.


    The .length property of nodes has been removed. This property caused issues with code like in Lodash that checked for "array-likeness" by simply looking for a .length property that was a number.

    ✶ breaking 
    opened by ianstormtaylor 44
  • Delete all will fail when there are three blocks or more

    Delete all will fail when there are three blocks or more

    EDIT: previously this was only an issue when the last block ended in a inline void node. Now it is always an issue.

    Do you want to request a feature or report a bug?

    Bug

    What's the current behavior?

    If you have three blocks or more, where the last block ends with an inline void node, and you select everything and hit backspace (delete), it will fail with error:

    Uncaught Error: `Node.assertNode` could not find node with path or key: List [ 1 ]
    

    Reproduction:

    1. Goto the emoji-example
    2. Add an emoji at the end of the last block.
    3. Select everything (command/ctrl-A)
    4. Hit backspace

    What's the expected behavior?

    That everything will be deleted without error.

    I have noticed that when deleting all (and you have three or more blocks) the change will include an operation with type move_node where the path property is a block path (i.ie [2] with the same value as the newPath prop [2]. I think Slate will regenerate keys for this operation and it may lead to issues.

    bug ♥ help 
    opened by skogsmaskin 42
  • RFC: Fix for Android devices

    RFC: Fix for Android devices

    Is this adding or improving a feature or fixing a bug?

    Fixing a bug

    What's the new behavior?

    It works

    Proof: https://photos.google.com/share/AF1QipO2uaYQFgq8PuYPq2ojGLokBHhqPaLoFQif8fQFyq220giwtcfz6x1FFdl8HWp7XQ/photo/AF1QipMrSTxFBM5GeXc98E_2gh7p9SKveXRmphrDQfJ1?key=Q19FaENxc01ka3VKZnZINXgzNGxfZThhSG1XYXhn

    Device is an OG Pixel, using GBoard on Android 8.1.0. YMMV. PLEASE TEST ON YOUR DEVICES!

    How does this change work?

    1. It doesn't hijack events. it just lets android do its thing
    2. it debounces updates to slate so the IME auto-suggest isn't constantly refreshing when the selection gets reset every render. Without a debounce, you can't type faster than ~3 keys per second and holding the backspace doesn't work.

    NOTE:

    • I use snapshotting, so react version is pinned at 16.3, making this a breaking change. you can use cWU if you want, but the package will need to be made react 17-ready soon anyways...
    • Before merging, please consider using snapshotting to replace updateSelection for all devices, not just android.

    Does this fix any issues or need any specific reviewers?

    fix #725 fix #1857

    Reviewers: @Slapbox @ianstormtaylor

    opened by mattkrick 42
  • Input is wonky on Android devices

    Input is wonky on Android devices

    I just did some testing on Chrome on a Samsung Galaxy J3, and the input was pretty wonky (iPhone was ok). It's hard to nail down exactly what's happening, but I see a couple things:

    • Often the first space press while at the end of a word doesn't add a space, instead selecting a default autocorrect
    • Sometimes words are duplicated or appear and disappear while typing

    This project looks great so far, and I'd love to use it, but I need Android support. I don't know anything about the nuances of input on contenteditable and/or Android, but if you have a rough direction/hypothesis for why this may be happening and don't have time to fix it now, I can dig in more and see what I can do.

    Thanks!

    bug ♥ help ⚑ cross platform ⚑ mobile 
    opened by iEchoic 42
  • Next

    Next

    This work is ongoing and not ready yet.


    Differences (incomplete)

    There are lots of changes in this branch. But here's an overview of the big differences from an architectural point of view.

    More coming, still in progress…

    JSON

    The data model is now comprised of simple JSON objects. Previously, it used Immutable.js data structures. This is a huge change, and one that unlocks many other things. Hopefully it will also increase the average performance when using Slate. It also makes it much easier to get started for newcomers. This will be a large change to migrate from, but it will be worth it.

    Interfaces & Namespaces

    The data model is interface-based. Previously each model was an instance of a class. Now, not only is the data plain objects, but Slate only expects that the objects implement an interface. So custom properties that used to live in node.data can now live at the top-level of the nodes. Helpers are exposed as a collection of helper functions on a namespace. For example, Node.get(root, path) or Range.isCollapsed(range). This ends up making code much clearer because you can always quickly see what interface you're working with.

    TypeScript

    The codebase now uses TypeScript. Working with pure JSON as a data model, and using an interface-based API are two things that have been made easier by migrating to TypeScript. You don't need to use it yourself, but if you do you'll get a lot more security when using the APIs. (And if you use VS Code you'll get nice autocompletion regardless!)

    Fewer Concepts

    The number of interfaces and commands has been reduced. Previously Selection, Annotation, Decoration used to all be separate classes. Now they are simply objects that implement the Range interface. Previously Block and Inline were separate, now they are objects that implement the Element interface. Previously there was a Document and Value, but now the top-level Editor contains the children nodes of the document itself.

    The number of commands has been reduced too. Previously we had commands for every type of input, like insertText, insertTextAtRange, insertTextAtPath. These have been merged into a smaller set of more customizable commands, eg. insertText which can take at: Path | Range | Point.

    Fewer Packages

    In attempt to decrease the maintenance burden, and because the new abstraction and APIs in Slate's core packages make things much easier, the total number of packages has been reduced. Things like slate-plain-serializer, slate-base64-serializer, etc. have been removed and can be implemented in userland easily if needed. Even the slate-html-deserializer can now be implemented in userland (in ~10 LOC leveraging slate-hyperscript). And internal packages like slate-dev-environment, slate-dev-test-utils, etc. are no longer exposed because they are implementation details.

    Plugins

    Plugins are now plain functions that augment the Editor object they receive and return it again. For example can augment the command execution by composing the editor.exec function. Or listen to operations by composing editor.apply. Previously they relied on a custom middleware stack, and they were just bags of handlers that got merged onto an editor. Now we're using plain old function composition (aka wrapping) instead.

    Elements

    Block-ness and inline-ness is now a runtime choice. Previously it was baked into the data model with the object: 'block' or object: 'inline' attributes. Now, it checks whether an "element" is inline or not at runtime. For example, you might check to see that element.type === 'link' and treat it as inline.

    beforeinput

    We now use the beforeinput event almost exclusively. Instead of having relying on a series of shims and the quirks of React synthetic events, we're now using the standardized beforeinput event as our baseline. It is fully supported in Safari and Chrome, will soon be supported in the new Chromium-based Edge, and is currently being worked on in Firefox. In the meantime there are a few patches to make Firefox work. Internet Explorer is no longer supported in core out of the box.

    More React-ish

    Rendering and event-handling is no longer a plugin's concern. Previously plugins had full control over the rendering logic, and event-handling logic in the editor. This creates a bad incentive to start putting all rendering logic in plugins, which puts Slate in a position of being a wrapper around all of React, which is very hard to do well. Instead, the new architecture has plugins focused purely on the rich-text aspects, and leaves the rendering and event handling aspects to React.

    Context

    Previously the <Editor> component was doing double duty as a sort of "controller" object and also the contenteditable DOM element. This led to a lot of awkwardness in how other components worked with Slate. In the new version, there is a new <Slate> context provider and a simpler <Editable> contenteditable-like component. By putting the <Slate> provider higher up in your component tree, you can share the editor directly with toolbars, buttons, etc. using the useSlate hook.

    Hooks

    In addition to the useSlate hook, there are a handful of other hooks. For example the useSelected and useFocused hooks help with knowing when to render selected states (often for void nodes). And since the use React's Content API they will automatically re-render when their state changes.


    Reductions

    One of the goals was to dramatically simplify a lot of the logic in Slate to make it easier to maintain and iterate on. This was done by refactoring to better base abstractions that can be built on, by leveraging modern DOM APIs, and by migrating to simpler React patterns.

    To give you a sense for the change in total lines of code:

    slate                       8,436  ->  4,038  (48%)
    slate-react                 3,905  ->    715  (18%)
    
    slate-base64-serializer        38  ->      0
    slate-dev-benchmark           340  ->      0
    slate-dev-environment         102  ->      0
    slate-dev-test-utils           44  ->      0
    slate-history                   0  ->    201
    slate-hotkeys                  62  ->      0
    slate-html-serializer         253  ->      0
    slate-hyperscript             447  ->    410
    slate-plain-serializer         56  ->      0
    slate-prop-types               62  ->      0
    slate-react-placeholder        62  ->      0
    slate-schema                    0  ->    504
    
    total                      13,807  ->  5,868  (43%)
    

    It's quite a big difference, although it's not done so the final sizes will likely grow a bit before it's ready. But that doesn't even include the dependencies that were shed in the process too.


    Fixes

    This is an estimate of which issues are fixed by this pull request. There are a lot of them, because it changes a lot of things. There might be a few incorrectly "fixed" ones here, so if one of them is your issue and you don't think it's fixed feel free to reopen a new issue.

    Fixes https://github.com/ianstormtaylor/slate/issues/3087 Fixes https://github.com/ianstormtaylor/slate/issues/3056 Fixes https://github.com/ianstormtaylor/slate/issues/3090 Fixes https://github.com/ianstormtaylor/slate/issues/3075 Fixes https://github.com/ianstormtaylor/slate/issues/2890 Fixes https://github.com/ianstormtaylor/slate/issues/2325 Fixes https://github.com/ianstormtaylor/slate/issues/3007 Fixes https://github.com/ianstormtaylor/slate/issues/3061 Fixes https://github.com/ianstormtaylor/slate/issues/560 Fixes https://github.com/ianstormtaylor/slate/issues/2869 Fixes https://github.com/ianstormtaylor/slate/issues/3028 Fixes https://github.com/ianstormtaylor/slate/issues/3027 Fixes https://github.com/ianstormtaylor/slate/issues/1762 Fixes https://github.com/ianstormtaylor/slate/issues/1022 Fixes https://github.com/ianstormtaylor/slate/issues/3020 Fixes https://github.com/ianstormtaylor/slate/issues/2746 Fixes https://github.com/ianstormtaylor/slate/issues/2991 Fixes https://github.com/ianstormtaylor/slate/issues/2333 Fixes https://github.com/ianstormtaylor/slate/issues/2711 Fixes https://github.com/ianstormtaylor/slate/issues/2413 Fixes https://github.com/ianstormtaylor/slate/issues/2345 Fixes https://github.com/ianstormtaylor/slate/issues/2812 Fixes https://github.com/ianstormtaylor/slate/issues/2859 Fixes https://github.com/ianstormtaylor/slate/issues/2862 Fixes https://github.com/ianstormtaylor/slate/issues/2863 Fixes https://github.com/ianstormtaylor/slate/issues/2495 Fixes https://github.com/ianstormtaylor/slate/issues/2864 Fixes https://github.com/ianstormtaylor/slate/issues/2860 Fixes https://github.com/ianstormtaylor/slate/issues/2878 Fixes https://github.com/ianstormtaylor/slate/issues/2858 Fixes https://github.com/ianstormtaylor/slate/issues/2861 Fixes https://github.com/ianstormtaylor/slate/issues/2985 Fixes https://github.com/ianstormtaylor/slate/issues/2987 Fixes https://github.com/ianstormtaylor/slate/issues/1883 Fixes https://github.com/ianstormtaylor/slate/issues/2002 Fixes https://github.com/ianstormtaylor/slate/issues/2022 Fixes https://github.com/ianstormtaylor/slate/issues/2989 Fixes https://github.com/ianstormtaylor/slate/issues/2990 Fixes https://github.com/ianstormtaylor/slate/issues/2876 Fixes https://github.com/ianstormtaylor/slate/issues/2983 Fixes https://github.com/ianstormtaylor/slate/issues/2945 Fixes https://github.com/ianstormtaylor/slate/issues/2986 Fixes https://github.com/ianstormtaylor/slate/issues/2939 Fixes https://github.com/ianstormtaylor/slate/issues/2968 Fixes https://github.com/ianstormtaylor/slate/issues/2895 Fixes https://github.com/ianstormtaylor/slate/issues/2900 Fixes https://github.com/ianstormtaylor/slate/issues/2873 Fixes https://github.com/ianstormtaylor/slate/issues/2567 Fixes https://github.com/ianstormtaylor/slate/issues/2868 Fixes https://github.com/ianstormtaylor/slate/issues/2867 Fixes https://github.com/ianstormtaylor/slate/issues/2668 Fixes https://github.com/ianstormtaylor/slate/issues/2029 Fixes https://github.com/ianstormtaylor/slate/issues/2759 Fixes https://github.com/ianstormtaylor/slate/issues/2701 Fixes https://github.com/ianstormtaylor/slate/issues/1884 Fixes https://github.com/ianstormtaylor/slate/issues/2503 Fixes https://github.com/ianstormtaylor/slate/issues/2620 Fixes https://github.com/ianstormtaylor/slate/issues/2420 Fixes https://github.com/ianstormtaylor/slate/issues/2708 Fixes https://github.com/ianstormtaylor/slate/issues/2538 Fixes https://github.com/ianstormtaylor/slate/issues/1247 Fixes https://github.com/ianstormtaylor/slate/issues/2466 Fixes https://github.com/ianstormtaylor/slate/issues/2111 Fixes https://github.com/ianstormtaylor/slate/issues/2297 Fixes https://github.com/ianstormtaylor/slate/issues/2321 Fixes https://github.com/ianstormtaylor/slate/issues/2329 Fixes https://github.com/ianstormtaylor/slate/issues/2060 Fixes https://github.com/ianstormtaylor/slate/issues/2274 Fixes https://github.com/ianstormtaylor/slate/issues/2108 Fixes https://github.com/ianstormtaylor/slate/issues/1466 Fixes https://github.com/ianstormtaylor/slate/issues/1759 Fixes https://github.com/ianstormtaylor/slate/issues/2043 Fixes https://github.com/ianstormtaylor/slate/issues/1128 Fixes https://github.com/ianstormtaylor/slate/issues/1464 Fixes https://github.com/ianstormtaylor/slate/issues/721 Fixes https://github.com/ianstormtaylor/slate/issues/674 Fixes https://github.com/ianstormtaylor/slate/issues/2977 Fixes https://github.com/ianstormtaylor/slate/issues/2336 Fixes https://github.com/ianstormtaylor/slate/issues/2361 Fixes https://github.com/ianstormtaylor/slate/issues/2093 Fixes https://github.com/ianstormtaylor/slate/issues/802 Fixes https://github.com/ianstormtaylor/slate/pull/2871 Fixes https://github.com/ianstormtaylor/slate/pull/3113 Fixes https://github.com/ianstormtaylor/slate/pull/2734 Fixes https://github.com/ianstormtaylor/slate/pull/3077 Fixes https://github.com/ianstormtaylor/slate/pull/3040 Fixes https://github.com/ianstormtaylor/slate/pull/2144 Fixes https://github.com/ianstormtaylor/slate/pull/3041 Fixes https://github.com/ianstormtaylor/slate/pull/2743 Fixes https://github.com/ianstormtaylor/slate/pull/2680 Fixes https://github.com/ianstormtaylor/slate/pull/2937 Fixes https://github.com/ianstormtaylor/slate/pull/2907 Fixes https://github.com/ianstormtaylor/slate/pull/2931 Fixes https://github.com/ianstormtaylor/slate/pull/2871 Fixes https://github.com/ianstormtaylor/slate/pull/2850 Fixes https://github.com/ianstormtaylor/slate/pull/2436 Fixes https://github.com/ianstormtaylor/slate/pull/1823 Fixes https://github.com/ianstormtaylor/slate/pull/2073 Fixes https://github.com/ianstormtaylor/slate/pull/1576 Fixes https://github.com/ianstormtaylor/slate/pull/1490 Fixes https://github.com/ianstormtaylor/slate/pull/1202 Fixes https://github.com/ianstormtaylor/slate/pull/1027


    Eliminates

    In addition to fixing a lot of things, the significant changes to the architecture mean that a lot of open issues are no longer relevant because they are talking about concepts that no longer exist.

    https://github.com/ianstormtaylor/slate/issues/3022 https://github.com/ianstormtaylor/slate/issues/3050 https://github.com/ianstormtaylor/slate/issues/3061 https://github.com/ianstormtaylor/slate/issues/3013 https://github.com/ianstormtaylor/slate/issues/3012 https://github.com/ianstormtaylor/slate/issues/3006 https://github.com/ianstormtaylor/slate/issues/2886 https://github.com/ianstormtaylor/slate/issues/2932 https://github.com/ianstormtaylor/slate/issues/2829 https://github.com/ianstormtaylor/slate/issues/2328 https://github.com/ianstormtaylor/slate/issues/2856 https://github.com/ianstormtaylor/slate/issues/2634 https://github.com/ianstormtaylor/slate/issues/2472 https://github.com/ianstormtaylor/slate/issues/2742 https://github.com/ianstormtaylor/slate/issues/2744 https://github.com/ianstormtaylor/slate/issues/2549 https://github.com/ianstormtaylor/slate/issues/2672 https://github.com/ianstormtaylor/slate/issues/2673 https://github.com/ianstormtaylor/slate/issues/2640 https://github.com/ianstormtaylor/slate/issues/2296 https://github.com/ianstormtaylor/slate/issues/826 https://github.com/ianstormtaylor/slate/issues/2330 https://github.com/ianstormtaylor/slate/issues/1915 https://github.com/ianstormtaylor/slate/issues/2331 https://github.com/ianstormtaylor/slate/issues/1749 https://github.com/ianstormtaylor/slate/issues/2289 https://github.com/ianstormtaylor/slate/issues/2082 https://github.com/ianstormtaylor/slate/issues/2078 https://github.com/ianstormtaylor/slate/issues/1843 https://github.com/ianstormtaylor/slate/issues/1703 https://github.com/ianstormtaylor/slate/issues/1794 https://github.com/ianstormtaylor/slate/issues/1821 https://github.com/ianstormtaylor/slate/issues/1652 https://github.com/ianstormtaylor/slate/issues/1464 https://github.com/ianstormtaylor/slate/issues/803 https://github.com/ianstormtaylor/slate/issues/1367 https://github.com/ianstormtaylor/slate/issues/651 https://github.com/ianstormtaylor/slate/issues/873 https://github.com/ianstormtaylor/slate/pull/1023 https://github.com/ianstormtaylor/slate/pull/784 https://github.com/ianstormtaylor/slate/pull/1756 https://github.com/ianstormtaylor/slate/pull/1626 https://github.com/ianstormtaylor/slate/pull/1827 https://github.com/ianstormtaylor/slate/pull/2280 https://github.com/ianstormtaylor/slate/pull/2410 https://github.com/ianstormtaylor/slate/pull/2448 https://github.com/ianstormtaylor/slate/pull/2660 https://github.com/ianstormtaylor/slate/pull/2602 https://github.com/ianstormtaylor/slate/pull/2505 https://github.com/ianstormtaylor/slate/pull/2674 https://github.com/ianstormtaylor/slate/pull/2928 https://github.com/ianstormtaylor/slate/pull/3003 https://github.com/ianstormtaylor/slate/pull/3092

    improvement debt ✶ breaking 
    opened by ianstormtaylor 38
  • Support for loading portable text JSON

    Support for loading portable text JSON

    Problem We use portable text to define document templates for users, much like the markdown is populated for the issues templates on Github. We would like the ability to define a template for the user when they load the slate UI The templates are saved as portable text https://github.com/portabletext/portabletext

    Solution On load, we would like to fetch the portable text JSON, parse it and then render the text in the slate UI

    Alternatives We are limited to portable text formatting as this is what our CMS uses. https://www.sanity.io/

    feature 
    opened by simon-guider 0
  • fix QQ browser IME characters input failure

    fix QQ browser IME characters input failure

    Description Users cannot input any Chinese charactors on latest version(11.5.0) of QQ browser.

    Issue Fixes: https://github.com/ianstormtaylor/slate/pull/3730#issuecomment-1074819628 and #4962

    Example before: before after: after

    Context The problem is caused by an earlier commit #3730. The changes could work for legacy versions. But for now, as mentioned in https://github.com/ianstormtaylor/slate/pull/3730#issuecomment-1074819628 and #4962, QQ browser might has changed its behavior of beforeinput since version 10.8.x in early 2022. And now in latest version of QQ browser, its chromium core version had updated to 94(And it's really hard to find legacy version installer of QQ browser except http://web.archive.org). So it's OK to remove these campat codes.

    Checks

    • [x] The new code matches the existing patterns and styles.
    • [x] The tests pass with yarn test.
    • [x] The linter passes with yarn lint. (Fix errors with yarn fix.)
    • [x] The relevant examples still work. (Run examples with yarn start.)
    • [x] You've added a changeset if changing functionality. (Add one with yarn changeset add.)
    opened by frellica 1
  • Fix the cursor jump to an unexpected position after deleting in android

    Fix the cursor jump to an unexpected position after deleting in android

    Description In Android, deleting text doesn't trigger the selectionchange event, so EDITOR_TO_PENDING_SELECTION doesn't be updated when deleting text. And after deleting all the input that it makes hasPendingDiffs to be false, will triggers applyPendingSelection and it makes the the cursor jump to an unexpected position. So i have to force update EDITOR_TO_PENDING_SELECTION when deleting text.

    Issue Fixes: #5249

    Example before:

    https://user-images.githubusercontent.com/11460856/211196919-1d65e01b-fd89-49b3-a91a-3d50048619ed.mp4

    after:

    https://user-images.githubusercontent.com/11460856/211197195-9070f9b7-e540-43e7-8761-e7fc6e8daf50.mp4

    Checks

    • [x] The new code matches the existing patterns and styles.
    • [x] The tests pass with yarn test.
    • [x] The linter passes with yarn lint. (Fix errors with yarn fix.)
    • [x] The relevant examples still work. (Run examples with yarn start.)
    • [x] You've added a changeset if changing functionality. (Add one with yarn changeset add.)
    opened by YaoKaiLun 1
  • Insert text,then undo and redo,the editor reports an error

    Insert text,then undo and redo,the editor reports an error

    Description Insert text at the end of the editor, then undo and redo, the editor reports an error

    Recording

    https://user-images.githubusercontent.com/40337000/211189566-b650bbd2-5c75-40fe-b295-9f3cd2188d29.mov

    Sandbox https://codesandbox.io/s/nervous-rhodes-4q1cj7?file=/src/App.tsx

    Steps To reproduce the behavior:

    1. insertText at the end
    2. cmd+z: undo
    3. cmd+shift+z: redo
    4. See error

    Expectation no error

    Environment

    • Slate Version: [0.88.1]
    • Operating System: [iOS]
    • Browser: [Chrome]
    bug 
    opened by JokerLHF 0
  • [Android Cursor Issue] After deleting, the cursor jump to an unexpected position

    [Android Cursor Issue] After deleting, the cursor jump to an unexpected position

    Description In Android, inputing some numbers or other non-alphabetic characters in an existed paragraph, and deleted them, then the cursor jump to an unexpected position.

    Recording

    https://user-images.githubusercontent.com/11460856/211146098-4620c989-f212-4ec7-b4e5-67279d0ae0f4.mp4

    Sandbox https://www.slatejs.org/examples/richtext

    Steps To reproduce the behavior:

    1. Input some numbers in an existed paragraph
    2. Delete the input just now
    3. See the cursor jump to an unexpected position

    Expectation The cursor remains at the last deleted position.

    Environment

    • Slate Version: [0.88.1]
    • Operating System: [Android 9]
    • Browser: [Chrome 90.0.4430.210]
    bug ⚑ cross platform 
    opened by YaoKaiLun 0
  • Switch from cypress to playwright.

    Switch from cypress to playwright.

    Description TLDR this PR:

    • [x] Switches existing integration tests from cypress to playwright
    • [x] Updates the version of yarn used to install dependencies
    • [x] Adds FF and Safari (when on mac) to the matrix of integration tests.

    Issue Fixes: (link to issue)

    Example N/A

    Context We're using slate for a project, but found that it has a couple of issues w/ Android (specifically around placeholders and auto-capitalize). I'm not sure I'll be able to make the change that fixes our issues, but I did want to try to contribute to the fix.

    My ultimate goal here is to add an additional integration test for android that ensures autocapitalization is respected, and that the placeholder behaves as expected. To do this, I've switched the integration test runner from cypress to playwright (which has experimental android support).

    As a neat side effect, the test suite is now MUCH faster. On my machine, it's come down from 7m 8s to 1m 9s

    I've also updated the version of yarn being used as I noticed w/ the old version that CI would periodically fail.

    Checks

    • [x] The new code matches the existing patterns and styles.
    • [x] The tests pass with yarn test.
    • [x] The linter passes with yarn lint. (Fix errors with yarn fix.)
    • [x] The relevant examples still work. (Run examples with yarn start.)
    • [ ] You've added a changeset if changing functionality. (Add one with yarn changeset add.)
    opened by gdborton 2
Owner
Ian Storm Taylor
Ian Storm Taylor
Demo app for refine.dev integration, a react-based framework for rapid building of internal tools.

Demo app for refine.dev integration, a react-based framework for rapid building of internal tools.

Permify 7 Apr 28, 2022
Mobile App Framework powered by React

TouchstoneJS Mobile App Framework powered by React. See the demo at touchstonejs.io. Follow @touchstonejs on Twitter for updates. See the touchstone-s

TouchstoneJS 3.3k Jan 5, 2023
Maple.js is a React webcomponents based framework mixing ES6 with Custom Elements, HTML Imports and Shadow DOM. It has in-built support for SASS and JSX, including a Gulp task for vulcanizing your project.

Heroku: http://maple-app.herokuapp.com/ npm: npm install maple.js Bower: bower install maple.js Maple is a seamless module that allows you to organise

Adam Timberlake 430 Dec 23, 2022
👻 Zero-configuration framework-agnostic static prerendering for SPAs

react-snap Pre-renders a web app into static HTML. Uses Headless Chrome to crawl all available links starting from the root. Heavily inspired by prep

null 4.8k Jan 7, 2023
React, React Native and Vue UI components for building data-driven apps with Elasticsearch

Reactive Search UI components library for Elasticsearch: Available for React, Vue and React Native Read how to build an e-commerce search UI a.) with

appbase.io 4.7k Jan 4, 2023
React library for ux4iot for easily building IoT web applications

React library for ux4iot for easily building IoT web applications

Device Insight 15 Oct 31, 2022
:postbox: A simple and customizable React notifications system

Reapop A simple and customizable React notifications system Summary Compatibility Demo Installation Integration & usage With React & Redux With React

Louis Barranqueiro 1.4k Jan 5, 2023
A completely customizable framework for building rich text editors

A completely customizable framework for building rich text editors

Ian Storm Taylor 26.2k Jan 8, 2023
Solana Metaplex AuctionHouse React APP based on CLI code. Code not completely working. Currently under development

Solana React Metaplex AuctionHouse UI test ALREADY IN DEVELOPMENT Not all function works correctly This is a simple UI app with TypeScript and Solana

3dvolt 6 Oct 16, 2022
Contentful rich text renderer for solid-js, based upon rich-text-react-renderer but with some tweaks

Contentful rich text renderer for solid-js, based upon rich-text-react-renderer but with some tweaks

Joel D'Souza 4 Aug 4, 2022
A React framework for building text editors.

Draft.js Draft.js is a JavaScript rich text editor framework, built for React and backed by an immutable model. Extensible and Customizable: We provid

Facebook 22.3k Jan 8, 2023
A React framework for building text editors.

Draft.js Draft.js is a JavaScript rich text editor framework, built for React and backed by an immutable model. Extensible and Customizable: We provid

Facebook 22.3k Dec 31, 2022
A toolkit to test Slate rich text editors with Jest, React Testing Library, and hyperscript

Slate Test Utils slate-test-utils-wave.mp4 Created with Wave Snippets A toolkit to test Slate rich text editors with Jest, React Testing Library, and

Marcus Wood 53 Dec 6, 2022
☁️🎨 An experimental universal, customizable styling and animation library for React Native. (beta)

An experimental universal, customizable styling and animation library for React Native. Why? ⎯ Features ⎯ Website ⎯ Documentation ⎯ Get Started ⎯ Exam

SkyleJS 24 Apr 14, 2022
Highly customizable library for building interactive node-based UIs, editors, flow charts and diagrams

React Flow is a library for building node-based graphs. You can easily implement custom node types and it comes with components like a mini-map and graph controls.

webkid 13.2k Jan 4, 2023
JS React based rich editors based on Draft.js

React - Rich editors Synopsis React rich-editors-like components. Based on draft-js. Package provide these public components: RichEditor EmailRichEdit

OpusCapita Solutions 3 Jul 8, 2021
React 18 Beta (Suspense, concurrent rendering, HTTP streaming, Server Components) + Next.js 12.0.4 demo & benchmark (performance & UX)

Next.js 12.0.4 and React 18 Beta Demo & Benchmark Next.js 12 comes with React 18 Beta support. React 18 will add features like Suspense, automatic bat

Cedric Chee 4 Jun 20, 2022
A high-performance framework with fine-grained observable-based reactivity for building rich applications

A high-performance framework with fine-grained observable-based reactivity for building rich applications

Fabio Spampinato 613 Dec 30, 2022
Voby - A high-performance framework with fine-grained observable-based reactivity for building rich applications.

Voby A high-performance framework with fine-grained observable-based reactivity for building rich applications. Features This works similarly to Solid

Voby 615 Jan 4, 2023
🎸 React Hook to use realtime last.fm data and display your currently played song in your application.

Stream your currently playing song through last.fm as a React hook. Features ?? Tiny: use-last-fm weighs in at less than 700 bytes minified and gzippe

Alistair Smith 101 Jan 9, 2023