-
-
Notifications
You must be signed in to change notification settings - Fork 558
feat: Toggle blocks #1707
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
base: main
Are you sure you want to change the base?
feat: Toggle blocks #1707
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
@blocknote/ariakit
@blocknote/code-block
@blocknote/core
@blocknote/mantine
@blocknote/react
@blocknote/server-util
@blocknote/shadcn
@blocknote/xl-ai
@blocknote/xl-docx-exporter
@blocknote/xl-multi-column
@blocknote/xl-odt-exporter
@blocknote/xl-pdf-exporter
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I'm more interested in seeing here is how we can add this to existing block types (such as headings with a prop) rather than as a separate element like this implements.
export const ToggleHeading = createBlockSpec( | ||
{ | ||
...headingConfig, | ||
type: "toggleHeading", | ||
}, | ||
{ | ||
...headingImplementation, | ||
render: (block, editor) => | ||
createToggleWrapper( | ||
block, | ||
editor, | ||
headingImplementation.render(block, editor), | ||
), | ||
}, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fantastic, composability!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now, this is creating a new block type, can we instead just "wrap over" heading, and if it has a prop isToggleable
it switches it's render implementation from the default, into using this toggle wrapper?
So that there is only one heading implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I also revert the heading block to using createStronglyTypedTiptapNode
? Since the keyboard shortcuts and input rules won't work if we stick with createBlockSpec
. Or do you wanna see how it looks using the createBlockSpec
first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, it is more important to see that the pattern can work before being caught up in those sort of details.
Once we can see that it works for headings, we can see if we can do it for something like list items
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, updated the code👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, this looks about right. Ideally it'd be "around" the heading component, but that is minor and fixable.
To explain more, like this you made this:
function Heading(props){
function renderHeading(){}
return props.isToggleable ? <Toggle>{renderHeading()}</Toggle> : renderHeading()
}
blocks: { heading: Heading }
Where I'd like something more like this:
function Heading() {
return renderHeading()
}
const WrapWithToggle=(component) => props => {
return props.isToggleable ? <Toggle>{component}</Toggle> : component
}
blocks: { heading: WrapWithToggle(heading) }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we've validated this approach, I do think you have the right idea with making this a strongly typed tiptap node to keep all previous functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented your suggestions, think it looks pretty decent. There are 2 things that aren't super nice though:
- Headings now always need to include a node view, even if they're not togglable.
- Because
createStronglyTypedTiptapNode
works on the TipTap API level whilecreateToggleWrapper
works on the BlockNote API level, we have to convert from a node to a block in the node view and do some slightly weird stuff there.
How much of a concern are those 2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- should be fine, node views are essentially "ejecting" from normal rendering but shouldn't be different (if in pure JS not react)
- Yea, so the ideal here is to build everything in terms of the blocknote API instead in the future, just don't wanna invest into that at the moment. May be worth extracting into something common, but not necessary for the moment either.
So, I think this is good from here. 🚦 green light to implement the lists & other ones, it seems like a sound approach for the moment.
Toggle wrapper element/component
This PR adds a new component,
ToggleWrapper
, as well as a vanilla HTML element implementation of it,createToggleWrapper
. These can be used to wrap existing blocks, or create new ones and allow the user to toggle showing/hiding their children. The toggle wrapper renders a button to the left of the block to show/hide the children. Additionally, if children are shown but none exist, a button is rendered to prompt the user to add a block. Because the toggled state is only in the view, toggling the visibility of children only happens client-side when using collaboration.Schema changes
This PR also adds toggle headings and toggle list items to the default editor schema. The toggle headings are implemented by adding an
isTogglable
prop, and rendering the block in a toggle wrapper when this is set totrue
. The toggle list item is a new, separate block, which always renders a paragraph in a toggle wrapper. Like the other list items though, hitting Enter in one creates a new block of the same type, which is why I made it a separate block rather than addingisTogglable
to the existing paragraph or list item blocks. Additionally, the block is exported differently to formats like docx, as the content gets prepended with a ">".TODO
Currently some
xl-ai
tests are failing, and these have been temporarily disabled.Closes #196
Closes #549
Closes #910