Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next #3093

Closed
wants to merge 168 commits into from
Closed

Next #3093

wants to merge 168 commits into from

Conversation

ianstormtaylor
Copy link
Owner

@ianstormtaylor ianstormtaylor commented Nov 6, 2019

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

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

@seanlaff
Copy link

Awesome! Everything sounds great- and stoked to see it in typescript 👍

@sherwinyu
Copy link
Contributor

Will there be a migration guide (or any other documentation on how to upgrade) coming?

If not, I can try to contribute to one , if that would be helpful for others (will probably take detailed notes when I attempt this upgrade).

Also, this is amazing. I'm so excited for so many of these changes! Thank you!

@sherwinyu
Copy link
Contributor

Oh, just kidding there's a guide here! https://docs.slatejs.org/concepts/xx-migrating (in case other people read this comment and are looking).

@wmertens
Copy link
Contributor

wmertens commented Dec 2, 2019

Note for posterity: this was merged in 4ff6972 but GitHub somehow missed that

includes(range: Range, target: Path | Point | Range): boolean {
if (Range.isRange(target)) {
if (
Range.includes(range, target.anchor) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be '&&'? Or the next isBefore and isAfter should reverse parameters' order?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. First it checks whether either anchor or focus is inside range to cover overlap cases. If that isn't the case, it checks whether target is wholly inside range.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then, it does not cover the range is wholly inside the target case. which shouldn't be also the "part of another range" case?

Copy link
Collaborator

@TheSpyder TheSpyder Jan 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm you're right it probably needs both range includes target and target includes range checks. There aren't any tests for this, only the path and point scenarios. From a quick search, internally Slate only uses the Path code branch so it will never hit this bug itself.

Would you mind logging a new ticket about it?

z2devil pushed a commit to z2devil/slate that referenced this pull request Dec 6, 2024
* remove some key usage from core, refactor Operations.apply

* undeprecate some methods

* convert more key usage to paths

* update deprecations

* convert selection commands to use all paths

* refactor word boundary selection logic

* convert many at-range commands to use paths

* convert wrapBlock and wrapInline to not use keys

* cleanup

* remove chainability from editor

* simplify commands, queries and middleware

* convert deleteAtRange

* remove key usage from schema, deprecate *ByKey methods

* migrate *ByKey tests, remove index from *ByPath signatures

* rename at-current-range tests

* deprecate mode key usage, migrate more tests away from keys

* deprecate range and point methods which rely on keys to work

* refactor insertBlock, without fixing warnings

* add pathRef/pointRef, fix insertBlock/Inline deprecations, work on insertFragment

* refactor insertFragment

* get rich-text example rendering

* fix lint

* refactor query files, fix more tests

* remove unused queries, refactor others

* deprecate splitDescendantsByPath

* merge master

* add typescript, convert slate, slate-hyperscript, slate-plain-serializer

* add Point, Path, Range, Annotation tests

* add Annotation, Change, Element, Fragment, Mark, Range, Selection, Value interfaces tests

* add Operation and Text tests

* add Node tests

* get operations and normalization tests working for slate

* get *AtPath command tests passing

* rename *AtPath command tests

* rename

* get *AtPoint tests working

* rename

* rename

* add value queries tests

* add element, mark and path queries tests

* convert most on-selection tests

* convert on-selection commands

* rename

* get addMarks and delete commands working

* rename

* rename

* rename

* refactor value.positions(), work on delete tests

* progress on delete tests

* more delete work

* finish delete tests

* start converting to at-based commands

* restructure query tests

* restructure operations tests

* more work converting to multi-purpose commands

* lots of progress on converting to at-based commands

* add unwrapNodes

* remove setValue

* more progress

* refactor node commands to use consistent matching logic

* cleanup, get non-fragment commands passing

* remove annotations and isAtomic

* rename surround/pluck to cover/uncover

* add location concept, change at-path to from-path for iterables

* refactor batches

* add location-based queries

* refactor hanging logic

* more location query work

* renaming

* use getMatch more

* add split to wrap/unwrap

* flip levels/ancestors ordering

* switch splitNodes to use levels

* change split to always:false by default

* fix tests

* add more queries tests

* fixing more delete logic

* add more splitNodes tests

* get rest of delete tests passing

* fix location-based logic in some commands

* cleanup

* get previous packages tests passing again

* add slate-history package

* start slate-schema work

* start of react working

* rendering fixes

* get rich and plain text examples working

* get image example working with hooks and dropping

* refactor onDrop to be internal

* inline more event handlers

* refactor lots of event-related logic

* change rendering to use render props

* delete unused stuff

* cleanup dom utils

* remove unused deps

* remove unnecessary packages, add placeholder

* remove slate-react-placeholder package

* remove unused dep

* remove unnecessary tests, fix readonly example

* convert checklists example

* switch to next from webpack

* get link example working

* convert more examples

* preserve keys, memoized leafs/texts, fix node lookup

* fix to always useLayoutEffect for ordering

* fix annotations to be maps, memoize elements

* remove Change interface

* remove String interface

* rename Node.entries to Node.nodes

* remove unnecessary value queries

* default to selection when iterating, cleanup

* remove unused files

* update scroll into view logic

* fix undoing, remove constructor types

* dont sync selection while composing

* add workflows

* remove unused deps

* convert mentions example

* tweaks

* convert remaining examples

* rename h to jsx, update schema

* fix schema tests

* fix slate-schema logic and tests

* really fix slate-schema and forced-layout example

* get start of insertFragment tests working

* remove Fragment interface

* remove debugger

* get all non-skipped tests passing

* cleanup deps

* run prettier

* configure eslint for typescript

* more eslint fixes...

* more passing

* update some docs

* fix examples

* port windows undo hotkey change

* fix deps, add basic firefox support

* add event overriding, update walkthroughs

* add commands, remove classes, cleanup examples

* cleanup rollup config

* update tests

* rename queries tests

* update other tests

* update walkthroughs

* cleanup interface exports

* cleanup, change mark transforms to require location

* undo mark transform change

* more

* fix tests

* fix example

* update walkthroughs

* update docs

* update docs

* remove annotations

* remove value, move selection and children to editor

* add migrating doc

* fix lint

* fix tests

* fix DOM types aliasing

* add next export

* update deps, fix prod build

* fix prod build

* update scripts

* update docs and changelogs

* update workflow and pull request template
z2devil pushed a commit to z2devil/slate that referenced this pull request Dec 6, 2024
* remove some key usage from core, refactor Operations.apply

* undeprecate some methods

* convert more key usage to paths

* update deprecations

* convert selection commands to use all paths

* refactor word boundary selection logic

* convert many at-range commands to use paths

* convert wrapBlock and wrapInline to not use keys

* cleanup

* remove chainability from editor

* simplify commands, queries and middleware

* convert deleteAtRange

* remove key usage from schema, deprecate *ByKey methods

* migrate *ByKey tests, remove index from *ByPath signatures

* rename at-current-range tests

* deprecate mode key usage, migrate more tests away from keys

* deprecate range and point methods which rely on keys to work

* refactor insertBlock, without fixing warnings

* add pathRef/pointRef, fix insertBlock/Inline deprecations, work on insertFragment

* refactor insertFragment

* get rich-text example rendering

* fix lint

* refactor query files, fix more tests

* remove unused queries, refactor others

* deprecate splitDescendantsByPath

* merge master

* add typescript, convert slate, slate-hyperscript, slate-plain-serializer

* add Point, Path, Range, Annotation tests

* add Annotation, Change, Element, Fragment, Mark, Range, Selection, Value interfaces tests

* add Operation and Text tests

* add Node tests

* get operations and normalization tests working for slate

* get *AtPath command tests passing

* rename *AtPath command tests

* rename

* get *AtPoint tests working

* rename

* rename

* add value queries tests

* add element, mark and path queries tests

* convert most on-selection tests

* convert on-selection commands

* rename

* get addMarks and delete commands working

* rename

* rename

* rename

* refactor value.positions(), work on delete tests

* progress on delete tests

* more delete work

* finish delete tests

* start converting to at-based commands

* restructure query tests

* restructure operations tests

* more work converting to multi-purpose commands

* lots of progress on converting to at-based commands

* add unwrapNodes

* remove setValue

* more progress

* refactor node commands to use consistent matching logic

* cleanup, get non-fragment commands passing

* remove annotations and isAtomic

* rename surround/pluck to cover/uncover

* add location concept, change at-path to from-path for iterables

* refactor batches

* add location-based queries

* refactor hanging logic

* more location query work

* renaming

* use getMatch more

* add split to wrap/unwrap

* flip levels/ancestors ordering

* switch splitNodes to use levels

* change split to always:false by default

* fix tests

* add more queries tests

* fixing more delete logic

* add more splitNodes tests

* get rest of delete tests passing

* fix location-based logic in some commands

* cleanup

* get previous packages tests passing again

* add slate-history package

* start slate-schema work

* start of react working

* rendering fixes

* get rich and plain text examples working

* get image example working with hooks and dropping

* refactor onDrop to be internal

* inline more event handlers

* refactor lots of event-related logic

* change rendering to use render props

* delete unused stuff

* cleanup dom utils

* remove unused deps

* remove unnecessary packages, add placeholder

* remove slate-react-placeholder package

* remove unused dep

* remove unnecessary tests, fix readonly example

* convert checklists example

* switch to next from webpack

* get link example working

* convert more examples

* preserve keys, memoized leafs/texts, fix node lookup

* fix to always useLayoutEffect for ordering

* fix annotations to be maps, memoize elements

* remove Change interface

* remove String interface

* rename Node.entries to Node.nodes

* remove unnecessary value queries

* default to selection when iterating, cleanup

* remove unused files

* update scroll into view logic

* fix undoing, remove constructor types

* dont sync selection while composing

* add workflows

* remove unused deps

* convert mentions example

* tweaks

* convert remaining examples

* rename h to jsx, update schema

* fix schema tests

* fix slate-schema logic and tests

* really fix slate-schema and forced-layout example

* get start of insertFragment tests working

* remove Fragment interface

* remove debugger

* get all non-skipped tests passing

* cleanup deps

* run prettier

* configure eslint for typescript

* more eslint fixes...

* more passing

* update some docs

* fix examples

* port windows undo hotkey change

* fix deps, add basic firefox support

* add event overriding, update walkthroughs

* add commands, remove classes, cleanup examples

* cleanup rollup config

* update tests

* rename queries tests

* update other tests

* update walkthroughs

* cleanup interface exports

* cleanup, change mark transforms to require location

* undo mark transform change

* more

* fix tests

* fix example

* update walkthroughs

* update docs

* update docs

* remove annotations

* remove value, move selection and children to editor

* add migrating doc

* fix lint

* fix tests

* fix DOM types aliasing

* add next export

* update deps, fix prod build

* fix prod build

* update scripts

* update docs and changelogs

* update workflow and pull request template
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment