Giter VIP home page Giter VIP logo

slate-edit-list's Introduction

⚠️ This repository is archived and has moved to GitBook's fork of ianstormtaylor/slate. Previous versions are still available on NPM All the versions using GitBook's fork of slate are now published under the @gitbook NPM scope. To learn more about why we forked Slate, read our manifest

slate-edit-list

NPM version Linux Build Status

A Slate plugin to handle keyboard events in lists. List items can contain blocks.

Demo: gitbookio.github.io/slate-edit-list/

Install

npm install slate-edit-list

Features

Natural keybindings:

  • Pressing Enter insert a new list item
  • Pressing Shift+Enter split the block in the list item
  • Pressing Tab increase the depth of the item (creates a sub-list)
  • Pressing Shift+Tab decrease the depth of the item
  • Pressing Delete (OSX) or Backspace at the start, remove the list item (or the list)

Simple validation/normalization (see assumptions about the schema):

  • Lists can contain only list items, and at least one.
  • List items can only be the direct children of a list.
  • List items must always contain blocks.

Useful transforms: see Utilities and Transform.

Simple Usage

import EditList from 'slate-edit-list'

const plugins = [
  EditList()
]

Arguments

This plugin accepts options to redefine the following block types:

  • types: string = ["ol_list", "ul_list"] — the array of possible types for list containers. First value will be used as default.
  • typeItem: string = "list_item" — type for list items.
  • typeDefault: string = "paragraph" — type for default block in list items.
  • canMerge: (Node, Node) => boolean — controls which list can be merged automatically (for example when they are adjacent). Defaults to merging list with identical types

Assumption about the schema

You can use this plugins with custom list block types (using plugin arguments). But your lists structure should still conform to a few rules. These rules are implemented as schema.

Here is what a minimal list would look like:

nodes:
    - kind: block
      type: ul_list # Default type for bulleted lists container
      nodes:
          - kind: block
            type: list_item # List containers can only contain list items
            nodes:
              # List items contain blocks. They cannot be the direct container of text.
              - kind: block
                type: paragraph # Default type of blocks in a list item
                nodes:
                  - kind: text
                    text: Hello World

And here is an example of a multi-level list:

nodes:
  - kind: block
    type: ol_list
    nodes:
      - kind: block
        type: list_item
        nodes:
          - kind: block
            type: paragraph
            nodes:
              - kind: text
                text: Item 1
          - kind: block
            type: ol_list
            nodes:
              - kind: block
                type: list_item
                nodes:
                  - kind: block
                    type: paragraph
                    nodes:
                      - kind: text
                        text: Item 1.1
              - kind: block
                type: list_item
                nodes:
                  - kind: block
                    type: paragraph
                    nodes:
                      - kind: text
                        text: Item 1.2

Utilities and Transform

slate-edit-list exports utilities and transforms:

plugin.utils.isSelectionInList(value: Value, type?: string) => Boolean

Return true if selection is inside a list (and it can be unwrap). Optional param type can be supplied to deduce whether list is of specified type.

plugin.utils.isList(node: Node) => Boolean

Return true if the node is one of the list type.

plugin.utils.getItemDepth(value: Value, block: Block?) => Number

Returns the depth of the current item (or the depth of the given block) in a list. 0 means not in a list.

plugin.utils.getCurrentItem(value: Value, block: Block?) => Block || Void

Returns the current item at selection (or at the given block).

plugin.utils.getCurrentList(value: Value, block: Block?) => Block || Void

Returns the current list at selection (or at the given block).

plugin.utils.getItemsAtRange(value: Value, range: Selection?) => List<Node>

Return the list of items at the given range. The returned items are the highest list of of successive items that cover the given range.

The returned list is empty if no such list can be found.

plugin.changes.increaseItemDepth(change: Change) => Transform

Increase the depth of the current item.

plugin.changes.decreaseItemDepth(change: Change) => Transform

Decrease the depth of the current item.

plugin.changes.wrapInList(change: Change, type: String?, data: Object|Data?) => Transform

Wrap the current blocks in list items of a list container of the given type. You can pass optional data for the created list container.

plugin.changes.unwrapList(change: Change) => Transform

Unwrap all items at range from their list.

plugin.changes.splitListItem(change: Change) => Transform

Split current block into a new list item.

slate-edit-list's People

Contributors

chilijung avatar justinweiss avatar oyeanuj avatar rebulus avatar samypesse avatar soreine avatar thomaswhyyou avatar tommoor avatar wootencl avatar yurkaninryan avatar zhujinxuan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

slate-edit-list's Issues

getCurrentItem in fresh lists

Hi,
when I wrap Blocks in list and I run getCurrentItem for that state, it returns null even thought selection is clearly in that new list.

When I change selection, it works fine.

A schema rule could not be validated after sufficient iterations

normalize.js:213 Uncaught Error: A schema rule could not be validated after sufficient iterations. This is usually due to a 'rule.validate' or 'rule.normalize' function of a schema being incorrectly written, causing an infinite loop.

I have tried this plugin with no other plugins (except for one that makes Ctrl+O set a the block type to ol_list) and it still gives me this error. I can insert list blocks when I disable this plugin, but not with it on.

Any ideas?

Running the latest version of Slate.

Pressing 'enter' after empty soft return line seems broken

Hey @SamyPesse @Soreine!

Just tested the soft-return behavior again and encountered some weird behavior.

On the example, if I press soft return after "First Item in the List", it takes you to the next paragraph, as expected. But now if I just press 'enter' without entering anything, it unwraps the list into a paragraph.

I would expect it to start the next bullet point (based on how Google Docs, etc work. It is often used as a formatting technique so that list items have more whitespace between them.

Thank you!

Different plugin design: More flexibility with list types

Looking into adding lists to an editor I'm working on, and thinking about three types:

  • ordered lists
  • unordered lists
  • check lists

And arguably there could be more that I'm not thinking about. It might be nice to refactor this plugin to make it less opinionated about the specific types of lists someone would use, and to instead just add the plugin multiple times, once for each list. Something like:

const plugins = [
  EditList({
    listType: 'ordered_list',
    itemType: 'list_item',
    defaultType: 'paragraph'
  }),

  EditList({
    listType: 'unordered_list',
    itemType: 'list_item',
    defaultType: 'paragraph'
  }),

  EditList({
    listType: 'check_list',
    itemType: 'check_list_item',
    defaultType: 'paragraph'
  }),

   ...
]

Just a thought for flexibility.

Still Maintained?

Since gitbook has moved to only supporting their own fork of slate, does that mean this plugin will no longer receive updates to support the current versions of slate?

I see there are a few of issues labeled with "Upgrade Slate". Are those planned to be resolved or should we explore alternative plugins?

I also found this fork while digging through the issues. Is there a plan to incorporate the changes made there into this repo or should we abandon this plugin for that fork?

Collapsable Items

Are collapsable list items something your team would be interested in as a feature? I implemented it inside of my own app on top of slate-edit-list using something along the lines of

const text = props.children[0].props.node.text
return (
  <li>
    {minimize ? <p>{text}</p> : props.children}
  </li>
)

If you are interested in having this feature and maintaining it as part of the project, I could potentially put together a PR. Either way, do you have rough suggestions on the best way to go about implementing it properly?

Unwrapping multilevel list

Hi.

Currently, when I try to unwrap multilevel list
For example, a list like this:

  • a
    • b
      • c
        • d

plugin just unwrap the first one item. Please, watch the slideshow below:

list_behavior

What do you think, if plugin would unwrap the whole list structure? The same behavior you can find in Google Docs, Pages and Word.

wrapInList for expanded selection

Hi,
when I run wrapInList at selection across multiple blocks, it creates single list item in list.

I think that correct behavior should be to create list item for each of selected blocks and wrap those in list. It works like that even at Rich Text example of Slate http://slatejs.org/#/rich-text

Normalize on wrapInList

Hey! Awesome work on this plugin, I'm really enjoying it!

I'm using the wrapInList and unwrapList functions to toggle lists. But since they always pass { normalize: false to Slate's internal change methods, I get some undesirable behavior at times:

Let's say I have the following list:

1. Item one
2. Item two
3. Item three

Now I select "Item two" and unwrap it. That leaves me with:

1. Item one

Item two

1. Item three

So far so good. But now if I select "Item two" again, and wrap it in a list, I get:

1. Item one

1. Item two

1. Item three

The next time I make a change (e.g. by typing something), the normalization process will run and transform my list back into:

1. Item one
2. Item two
3. Item three

I don't think the third state in my example should ever be possible, i.e.:

1. Item one

1. Item two

1. Item three

What's the reasoning behind { normalize: false }? Performance concerns?

onEnter sometimes create invalid lists

Items that contain only text nodes are not correctly splitted on pressing Enter.

{
    "kind": "block",
    "type": "list_item",
    "nodes": [{
        "kind": "text",
        "ranges": [
            {
                "text": "Second item in the nested list"
            }
        ]
    }]
}

And splitting items containing a sublist breaks the sublist.

Normalization fails on list items that end with inline nodes

Right now in normalization if there are any non-block nodes it will wrap them in the default block. But consider the case where someone has pasted HTML that is:

<li>
  <a>A Link</a>
</li>

That will look like this in Slate:

block(item)
  text
  inline(link)
  text 

But when the normalization tries to do its stuff, it will fail since by the time it gets to the last text node, it won't exist anymore, since it was no longer needed for padding the inline.

Instead, I think normalization should check to see if all children are texts/inlines, and if so, just wrap them completely in a single default block.

Shift + Return for soft returns.

Hey @SamyPesse, as per our conversation, here is the Github issue.

Doing shift + return, still carries forward the bullet point. It should act like a soft return without the bullet point IMO. Mod + return seems to do that here but shift + return is also fairly standard in other editors.

Utility functions do not distinguish between types

Hi @Soreine, just another API suggestion based on my experience with the library.

Currently, only the wrapInList takes a type parameter to define what kind of list to create. It would be super useful for functions like isSelectionInList to take an optional type to check if the selection is in a particular kind of list?

Use-case for example, with check-lists, a different behavior might need to occur than with an unordered list before unwrapping or toggling.

Thoughts?

[Question/Feature] Change sublist type via options

I found no other way of communicating this, so hence the issue.

It is not clear to me if I can set what kind of nested list behavior I can have.

In my concrete example I want that all nested lists of Number list to be Bullet lists.

Setting Slate's schema does not seem to validate correctly out of the box.

unexpected tab behavior

Hey @SamyPesse !

Just installed this plugin to handle lists in my editor, and I ran into unexpected tab behavior. What I assumed would happen is (based on editors like Dropbox Paper or Notion):

  • On tab, any list items in the selection are indented by one, regardless of whether it's collapsed or not, and whether it's at the start or end of the block.
  • On shift-tab, any list items in the selection are unindented by one.
  • You can't tab to create a new list item from a paragraph.
  • You can't shift-tab to return a list item to a paragraph.

But it seems like the tab behavior is more specific about the location of the cursor, and a few other strange things going on.

Deprecation warnings

Using the latest versions of slate and slate-edit-list, I'm seeing tons of warnings in the console about the use of deprecated methods.

screen shot 2018-08-17 at 7 26 35 pm

Versions used:

  • slate: 0.37.7
  • slate-edit-list: 0.12.0

when wrapInList. it generates <ul><p></p><p></p></ul> ....

Have two

block, select both of them, click wrapInList button, it becomes <ul><p></p><p></p></ul>, there's no any <li> inside <ul>.

I can't reproduce it in the online demo, but it happened in my project with almost exactly same code.

"react": "^16.4.1",
"slate": "^0.37.3",
"slate-edit-list": "^0.12.0",
"slate-react": "^0.15.4",

Thanks
Gary

Enforce that items contain only blocks (no text nodes)

It is simpler that list items always contain blocks, and no direct text nodes. This should be enforced as a schema rule, and documented as a requirement to use the plugin.

Here is a minimal list:

nodes:
    - kind: block
      type: ul_list
      nodes:
          - kind: block
            type: list_item
            nodes:
              - kind: block
                type: paragraph
                nodes:
                  - kind: text
                    text: Hello World

The default block type should be provided to the plugin (default to paragraph).

Here is an example of how a sublist is made

nodes:
  - kind: block
    type: ol_list
    nodes:
      - kind: block
        type: list_item
        nodes:
          - kind: block
            type: paragraph
            nodes:
              - kind: text
                text: Item 1
          - kind: block
            type: ol_list
            nodes:
              - kind: block
                type: list_item
                nodes:
                  - kind: block
                    type: paragraph
                    nodes:
                      - kind: text
                        text: Item 1.1
              - kind: block
                type: list_item
                nodes:
                  - kind: block
                    type: paragraph
                    nodes:
                      - kind: text
                        text: Item 1.2

How to know whether we are at first "li"

<button className={inList ? '' : 'disabled'} onClick={() => {this.call(increaseItemDepth)}}>

In the sample code, we can disabled the button by checking inList. If we are at the first "li", we should also disable the button because it will do nothing to increaseItemDepth, how can we know whether we are at the first "li"?

Thanks lot
Gary

Nested bullet rendering in next line.

Hey @SamyPesse, as per our conversation, here is the Github issue.

On a new bullet point, if I press Tab, it indents to a nested bullet point but on the next line, not to the current one. So, essentially, creates an extra line.

Update to Slate 0.14.x

In Slate 0.14.x, a PR was merged : ianstormtaylor/slate#249

It adds useful, low-level transformation, that could simplify the current implementation (which relies heavily on setNodeByKey and sometimes even updates the selection by hand).

Indent should not indent sub-items as well

Please see the following video: http://recordit.co/3BQ4heTdK5

When indenting a list item all of its sub-items are indented as well. One could make the case that this is good behavior. However, it is at least unconventional. For example, in Google Docs only the current list item will be indented. If you want to increase/decrease indentation of several items, you would apply a text selection over the list items.

What do you think? Should slate-edit-list copy Google Docs' behavior?

Disable changes on enter event

How to write the rule that will disable all changes on Enter event. Just using event.preventDefault() and return undefined will still create a new line in the list item.

Definition lists

I'm attempted to build support for definition list markup in slate. slate-edit-list has lots of relevant functionality but it's not suitable out of the box.

As a reminder, here's a definition list:

<!-- A definition list -->
<dl>
<dt>Definition term</dt>
<dd>Description details</dd>
</dl>

And a usage example:

Apple 🍎
An apple is a sweet, edible fruit produced by an apple tree (Malus pumila).
Pear 🍐
The pear is any of several tree and shrub species of genus Pyrus, in the family Rosaceae.
[...]

.
I'm currently looking to create a schema to allow enforce the structure as it might be easiest, but wanted to post this here incase there has been any consideration towards definition list support in slate-edit-list.

Thanks :)

Bug: Incorrect list item creation under certain circumstances.

Hi,

https://gitbookio.github.io/slate-edit-list/

If you place your cursor at the end of this line for example Third item in the list, with a nested list and press enter a new child item is created however it looks like another parent list is created at the same level of what use to be the first child First item in the nested list.

I would presume what should occur is that it would just create a new list item at the same level of the item enter was pressed on.

On another note, is it possible to disable or prevent certain list edit behaviour occuring. For example i would like to stop the block splitting behaviour being possible (shift + enter).

Thanks

Ability to configure schema rules

Hi @SamyPesse @Soreine! I ran into a small question while using this excellent plugin. The schema makes an assumption that the child of the list_item is a paragraph node (and thus also, text as the child node of that).

In my case, the editor has images, etc, and it would be ideal to be able to have the child nodes of list_item be configurable to user-specified list of block elements. Is that possible right now, or easy to do with the library?

Slate 0.33.6 causes pasted list items to become nested lists

I fully understand you only support slate 0.32, however I will flag this bug regardless.

A change was introduced in slate 0.33.5 (works fine in 0.33.4) that causes nested lists to be created on pasting of a list item.

Scenario:

  • copy some text from a list item
  • paste the text into a list item

expected behaviour:

  • text is pasted inline into the list item

actual behaviour:

  • a new unordered list is created, with an item of the pasted text.
  • The text to the right of what was pasted is moved to a new paragraph

List item with multiple blocks, last empty block outdents

Place the cursor in the last block of a multi list item. When the last block in the list item is an empty paragraph, hit the enter key. The expected behavior is to create a new list item below the current one. Instead, the current multi block list item is outdented.

If the last block in the list item has content, a new list item is created below the initial multi block list item. This is an edge case that is totally worth eliminating.

Initial State:

* list item
  > with multiple blocks (can just be paragraphs)
  <cursor at empty paragraph>
* list item

Result:

list item

> with multiple block types

<cursor at empty paragraph>

* list item

Example Gif:

Tab behavior is not practical

Tab should increase the depth of the current item if possible.

Current behavior is impractical:
current-tab

The work around is to first Shift+Enter then Tab.

Here is Google Doc for example:
better-tab

Unwrapping breaks nested lists

  • Item 1
  • Item 2
    • Item 2.1
    • Item 2.2

Backspace at start of Item 2 will unwrap Item 2 correctly, but the sublist will end up being wrapped in an extra list block.

screen shot 2016-09-08 at 16 58 05

I can't fix this until Slate either provide:

  • A transform to update the Document nodes directly.
  • A transform that unwrap a single block by key (not the behavior of unwrapBlockAtRange which unwrap all blocks of a certain type, or unwrapBlock which unwrap only the deepest blocks.

Prevent data inheritance on sub-list creation

When I'm pressing tab the new list element inherits the parent list data, so If im having custom style like:
"list-style-type: circle" and start="10" the child list will have same data as parent!
Is there any way to prevent that behaviour?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.