Giter VIP home page Giter VIP logo

react-native-draftjs-display's Introduction

react-native-draftjs-display

CI CodeFactor

Barebones React Native component to display Draft.js content.

Partly forked from jdponomarev/react-native-draftjs-render, with the original code coming from globocom/react-native-draftjs-render. Re-written to use TypeScript, and to better fit in with my use-case.

This library is intentionally very minimal to allow you to use your own components (or a component kit like material design) and styling easily, and as such has very few defaults in that regard.

Usage

// Use DraftText but with our own text component, or one from a UI kit.
const TextBlockHandler = useCallback(
  (category: string) => ({block}: {block: DraftContentBlock}) => (
    <DraftText
      block={block}
      TextComponent={(props) => <MyCustomText {...props} category={category} />}
    />
  ),
  [],
);
// ...
<>
  {data.getPost?.content && (
    <DraftDisplay
      contentState={JSON.parse(data.getPost.content)}
      blockHandlers={{
        // This is where the magic happens. Use this to render your content block types.
        atomic: ({block}) => <PostMediaBlock block={block} />,
        'header-one': TextBlockHandler('h1'),
        'header-two': TextBlockHandler('h2'),
        'header-three': TextBlockHandler('h3'),
        'header-four': TextBlockHandler('h4'),
        'header-five': TextBlockHandler('h5'),
        'header-six': TextBlockHandler('h6'),
        paragraph: TextBlockHandler('p1'),
        unstyled: TextBlockHandler('p1'),
        // see below, "List Handlers"
        'ordered-list-item': OrderedListBlockHandler(),
        'unordered-list-item': UnorderedListBlockHandler(),
      }}
    />
  )}
</>;

List Handlers

This library, as of writing, has no list handling functionality or helpers. That may change, but for now, here's my provisional implementation of rendering nested lists:

In these examples, tw('...') is from tailwind-rn, and <ListItem>, <Icon>, and <Text> are from react-native-ui-kitten. Use this as an example for your own apps.

Unordered list

These are easy, as you just render an indent and a prefix icon based on the block.depth property.

const UnorderedListBlockHandler = useCallback(
  () => ({block}: {block: DraftContentBlock}) => (
    <ListItem
      // ...other properties and styles
      style={[
        // render indent based on `block.depth`
        block.depth === 0
          ? tw('ml-0')
          : block.depth === 1
          ? tw('ml-4')
          : block.depth === 2
          ? tw('ml-8')
          : block.depth === 3
          ? tw('ml-12')
          : block.depth === 4
          ? tw('ml-16')
          : tw('ml-0'),
      ]}
      accessoryLeft={() => (
        <Icon
          // ...again, other proprties and styles
          name={
            // render different icons based on indentation. not strictly necessary, but very pretty!
            block.depth === 0
              ? 'minus-circle'
              : block.depth === 1
              ? 'minus-circle-outline'
              : block.depth === 2
              ? 'minus-square'
              : block.depth === 3
              ? 'minus-square-outline'
              : block.depth === 4
              ? 'square'
              : 'minus-circle'
          }
        />
      )}
      title={() => (
        <DraftText
          block={block}
          TextComponent={(props) => <Text {...props} category="p1" />}
        />
      )}
    />
  ),
  [],
);
Ordered list

Now these are a little more involved, as you need to have your own counter, which counts each depth separately, and then resets between separate lists. Here's my implementation:

const OrderedListBlockHandler = useCallback(() => {
  // an array of numbers, with the index being the depth, and the number being the counter
  let listCountersAtDepth: number[] = [];

  return ({
    block,
    prevBlock,
  }: {
    block: DraftContentBlock;
    prevBlock?: DraftContentBlock; // introduced in v1.3.0
  }) => {
    // reset list counter completely if previous block is not a list item
    if (prevBlock && prevBlock.type !== 'ordered-list-item') {
      listCountersAtDepth = [];
    }

    // initialise counter at this block's depth
    if (!listCountersAtDepth[block.depth]) {
      listCountersAtDepth[block.depth] = 0;
    }

    // increase counter
    listCountersAtDepth[block.depth]++;

    return (
      <View
        style={[
          tw('flex flex-row py-1'),
          // same indentation as unordered lists, pretty simple
          block.depth === 0
            ? tw('ml-0')
            : block.depth === 1
            ? tw('ml-4')
            : block.depth === 2
            ? tw('ml-8')
            : block.depth === 3
            ? tw('ml-12')
            : block.depth === 4
            ? tw('ml-16')
            : tw('ml-0'),
        ]}>
        <Text category="p1" style={[tw('pl-0 ml-0 mr-2')]}>
          {/*
            you could just do `listCountersAtDepth[block.depth]` and call it a day,
            but this renders each indentation a different way, to differentiate them

            numberToBase26 does exactly as it sounds, takes a number and turns it in to the alphabet
            with 1 being a, 26 being z, and 27 being aa etc.

            arabicToRoman also does exactly what you'd expect

            the implementation of these is left to the reader (hint: StackOverflow!)

            The `block.depth % 3` is so that it loops, as technically you could have any maximum block.depth,
            but we only have 3 options to display the counter
          */}
          {block.depth % 3 === 0
            ? listCountersAtDepth[block.depth]
            : block.depth % 3 === 1
            ? numberToBase26(listCountersAtDepth[block.depth]).toLowerCase()
            : block.depth % 3 === 2
            ? arabicToRoman(listCountersAtDepth[block.depth]).toLowerCase()
            : listCountersAtDepth[block.depth]}.
        </Text>
        <DraftText
          block={block}
          TextComponent={(props) => <Text {...props} category="p1" />}
        />
      </View>
    );
  };
}, []);

Development

Clone this repository and install its dependencies:

git clone https://github.com/lukeramsden/react-native-draftjs-display
cd react-native-draftjs-display
yarn # or npm install, if you're so inclined

yarn run build builds the library to dist.

yarn test builds the library, then tests it.

License

ISC.

react-native-draftjs-display's People

Contributors

dependabot[bot] avatar lukeramsden avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

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.