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

ZUIEditor tools: Ordered list, bullet list, bold and italic #2479

Merged

Conversation

richardolsson
Copy link
Member

Description

This PR integrates four builtin remirror extensions into the editor, and introduces the link extension into the same patterns as all other tools.

Screenshots

image

Changes

  • Refactors block factories to support more arbitrary commands
  • Enable built-in ordered list extension
  • Enable built-in unordered (bullet) list extension extension
  • Enable built-in bold extension
  • Enable built-in italic extension
  • Fix bug causing block toolbar to be available in non-paragraph blocks
  • Introduce new pattern for toolbar buttons that are more self-contained components

Notes to reviewer

None

Related issues

Undocumented

Copy link
Contributor

@ziggabyte ziggabyte left a comment

Choose a reason for hiding this comment

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

I'm approving, but if the "Foo button" text was not intentionally left for later, it needs internationalisation i think.

Also thought about this when i played with it, that I'm not enjoying that the toolbar is open already when i go to the tab. Arriving in this state does not feel smooth to me. Also not for this pr to solve, but noticed it now so writing it now.
bild

const { dispatch, state, tr } = props;
const newNode = this.type.create(
null,
this.type.schema.text('Foo button')
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm assuming this is either left in here accidentally, or intentionally left to be internationalised at a later point?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yea, this is definitely an embarrassing mistake. The string should be passed in either as an option to the extension, or as an argument to the command, both of which should allow internationalization quite easily since both are invoked from a react component that have access to useMessages().

const blockExtensions: ZetkinExtension[] = [];
const otherExtensions: Extension[] = [];
const otherExtensions: AnyExtension[] = [];
const blockExtensions: BlockExtension[] = [];

if (enableButton) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't say I like this very long list of "if this - add that" but maybe it is inevitable. Also not to be fixed in this pr, just a thought for now.

Copy link
Member Author

Choose a reason for hiding this comment

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

I can't think of a better solution, unfortunately. I can think of other solutions but they are all more complex or more difficult to read. And long isn't necessarily bad IMO, if it's simple and repetitive the eye can quickly glance over it.

If typescript allowed the same "if expression" that dart does, which evaluates as an expression instead of just being executed as a statement, we could have done this:

const blockExtensions = [
  if (enableButton) new ButtonExtension(),
  if (enableImage) new ImageExtension(),
  // etc
];

But that's not possible in typescript. The "closest" we get in typescript is this crazy concoction which is very difficult to read IMO:

const blockExtensions = [
  ...(enableButton? [new ButtonExtension()] : []),
  ...(enableImage? [new ImageExtension()] : []),
  // etc
];

But then the additional problem is that even if this works for putting the extensions in the array, we also need the extension instances later in the block factories for the block menu.

So that leaves the option of creating an abstraction, e.g. a class `ExtensionRegistry` or something, that can create a list of extensions based on the `enableX` props, and also create the factories. But I think that's perhaps unnecessarily complex.

Copy link
Member Author

Choose a reason for hiding this comment

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

All of that said, I do think that it's probably a good idea to put all of this in a useMemo() in order to not re-create all the extensions every re-render.

I'm pretty sure the useRemirror() hook already does this, which prevents remirror from being re-initialized with new extensions. But still, it's unnecessary for us to create a new array of new extension instances and then just pass them to a hook that ignores them because it's already been initialized before.

@ziggabyte ziggabyte merged commit 21b0d81 into undocumented/ZUI-text-editor Jan 21, 2025
6 checks passed
@ziggabyte ziggabyte deleted the undocumented/zui-editor-li-ol-b-i branch January 21, 2025 12:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants