question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Composing decorators

See original GitHub issue

Hey there. big fan šŸ™Œ

I’m writing a small that implements a markup language that resembles to Markdown using decorators:

  • *text* => <Em>{ text }</Em>
  • ~text~ => <Strike>{ text }</Em>

It was all fine and worked great, until I tried to compose them one inside of the other:

A really cool example

I can’t really declare if its a bug or a feature, but it leads me to my question:

Is there a way of making decorators components composable? Or - is there another way of solving this problem? Decorators keep the code declarative and awesome, the only real struggle will be to handle char arrays or to get children instead of text… but I don’t really care in my specific use case.

Thank you!

Technical stuff:

  • Chrome Canary 54.0.2791.0 on OS X El Capitan
  • Draft.js 0.7.0

Issue Analytics

  • State:open
  • Created 7 years ago
  • Comments:11 (3 by maintainers)

github_iconTop GitHub Comments

9reactions
paulyoungcommented, Jan 30, 2017

This technique I recently devised may be of some help: https://jsfiddle.net/paulyoung85/2unzgt68/

1reaction
joncursicommented, Oct 26, 2020

@paulyoung thanks, your solution works great! There is one issue, however, getting the CompoundDecorator to work with either SimpleDecorator or DraftJS Plugins.

Both of these libraries support a 3rd argument, contentState, in the strategy function signature:

strategy(contentBlock, callback, contentState) => { ... }

When we replace the stock CompositeDecorator with the new CompoundDecorator, this third argument is ā€œlostā€, and you’ll get a JavaScript exception if the strategy depends on the presence of the contentState argument.

Any ideas on how to overcome this?

Solved by making a small modification to the getDecorations method, to pass contentState through. Here’s my full CompoundDecorator now:

/**
 * @prettier
 */

import { CompositeDecorator } from 'draft-js';
import { List } from 'immutable';

const Span = props => <span>{props.children}</span>;

export default class CompoundDecorator {
  constructor(decorators = []) {
    this.decorators = decorators.map(decorator => {
      return decorator.strategy && decorator.component
        ? new CompositeDecorator([decorator])
        : decorator;
    });
  }

  getDecorations(block, contentState) {
    const emptyTuples = Array(block.getText().length).fill(
      Array(this.decorators.length).fill(null),
    );

    const decorations = this.decorators.reduce((tuples, decorator, index) => {
      const blockDecorations = decorator.getDecorations(block, contentState);

      return tuples.map((tuple, tupleIndex) => {
        return [
          ...tuple.slice(0, index),
          blockDecorations.get(tupleIndex),
          ...tuple.slice(index + 1),
        ];
      });
    }, emptyTuples);

    return List(decorations.map(JSON.stringify));
  }

  getComponentForKey(key) {
    const tuple = JSON.parse(key);
    return props => {
      const { decoratorProps, ...compositionProps } = props;
      const Composed = tuple.reduce((Composition, decoration, index) => {
        if (decoration !== null) {
          const decorator = this.decorators[index];
          const Component = decorator.getComponentForKey(decoration);
          const componentProps = {
            ...compositionProps,
            ...decoratorProps[index],
          };
          return () => (
            <Component {...componentProps}>
              <Composition {...compositionProps} />
            </Component>
          );
        }
        return Composition;
      }, Span);
      return <Composed>{props.children}</Composed>;
    };
  }

  getPropsForKey(key) {
    const tuple = JSON.parse(key);
    return {
      decoratorProps: tuple.map((decoration, index) => {
        const decorator = this.decorators[index];
        return decoration !== null ? decorator.getPropsForKey(decoration) : {};
      }),
    };
  }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

How to compose multiple TypeScript class decorators?
I have a family of class decorators that I repeat over many classes. Something similar to this: @foo @bar @baz export class MyClass...
Read more >
Decorators Composition in TypeScript | by Ujjwal Ojha | Medium
Decorators are an experimental feature of TypeScript that can be attached to a class declaration, method, accessor, property, or parameterĀ ...
Read more >
Documentation - Decorators - TypeScript
A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use...
Read more >
Decorators Composition in TypeScript - DEV Community ā€ ā€
Decorators are an experimental feature of TypeScript that can be attached to a class declaration, method, accessor, property, or parameterĀ ...
Read more >
Decorator - Refactoring.Guru
Decorator is a structural design pattern that lets you attach new ... The client code must be responsible for creating decorators and composing...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found