Git Product home page Git Product logo

react-tinacms-field-grid's Introduction

react-tinacms-field-grid

A Flexbox grid builder field

Getting Started

First, install the plugin with its peer dependencies:

npm i react-tinacms-field-grid react react-dom tinacms react-tinacms-inline

Pre-requisites

This field assumes you alread have Tina setup in a React application.

If not, please see the Tina Documentation to get started.

Usage

First, setup a Tina form inside an InlineForm:

import { useForm } from "tinacms";
import { InlineForm } from "react-tinacms-inline";

export function MyApp(props) {
  const form = useForm(props);

  return (
    <InlineForm form={form}>
    </InlineForm>
  );
}

Now, you need to add your InlineGrid, which requires:

  1. A row block and a column block
  2. A set of UI blocks to render in the grid (these can be any blocks compatible with the InlineBlocks component).
  3. A name, specifying where the InlineGrid's data is stored on the form

First, let's create the row and column blocks.

import { BlocksControls } from 'react-tinacms-inline'

export const RowBlock = ({index, children}) => (
  <BlocksControls index={props.index}>
    <div style={{display: "flex"}}>
      {children}
    </div>
  </BlocksControls>
)

export const RowBlockTemplate = {
  label: 'Row',
  defaultItem: {
    columns: []
  }
  fields: []
}

export const RowBlockPreview = () => <RowBlock>This is a row</RowBlock>;

export default {
  Component: RowBlock,
  template: RowBlockTemplate,
  preview: RowBlockPreview
}
export const ColumnBlock = ({index, children}) => (
  <div style={{display: "flex"}}>
    <BlocksControls index={props.index}>
      {children}
    </BlocksControls>
  </div>
)

export const ColumnBlockTemplate = {
  label: 'Column',
  defaultItem: {
    blocks: []
  }
  fields: []
}

export const ColumnBlockPreview = () => <ColumnBlock>This is a column</ColumnBlock>;

export default {
  Component: RowBlock,
  template: RowBlockTemplate,
  preview: RowBlockPreview
}

Now, let's create a paragraph block to use in the grid.

export const ParagraphBlock = ({index, text}) => (
  <p>
    {children}
  </p>
)

export const ParagraphBlockTemplate = {
  label: 'Paragraph',
  defaultItem: {
    text: 'Hello world!',
  },
  fields: [
    { name: 'text', component: 'textarea', label: "Paragraph text"}
  ]
}

export const ParagraphBlockPreview = () => <ParagraphBlock text="This is a column" />;

export default {
  Component: ParagraphBlock,
  template: ParagraphBlockTemplate,
  preview: ParagraphBlockPreview
}
import { useForm } from "tinacms";
import { InlineForm } from "react-tinacms-inline";
import { InlineGrid } from "react-tinacms-field-grid";
import RowBlock from "./RowBlock";
import ColumnBlock from "./ColumnBlock";
import ParagraphBlock from "./ParagraphBlock";

export function MyApp(props) {
  const form = useForm(props);
  const blocks = useMemo(() => {
    return {
      paragraph: ParagraphBlock
    }
  }, []);

  return (
    <InlineForm form={form}>
      <InlineGrid
        row={RowBlock}
        column={ColumnBlock}
        blocks={blocks}
      />
    </InlineForm>
  );
}

Now, open up your app and see the result:

  • Add a row
  • Add a column
  • Add a paragraph
  • Click the "settings" icon on the paragraph to edit the text!

Adding Row and Column configuration

To add configuration for your row or column, such as setting how many column lengths a give column should span, you can update the template.

For example, to add a "colspan" attribute that updates the flex properties of the column, we'll first update the defaultItem of the column template:

export const ColumnBlockTemplate = {
  label: 'Column',
  defaultItem: {
    blocks: []
    colspan: 4
  }
  fields: []
}

Then we'll update the fields option of the column template to allow the colspan to be changed:

export const ColumnBlockTemplate = {
  label: 'Column',
  defaultItem: {
    blocks: []
    colspan: 4
  }
  fields: [
    { name: 'colspan', component: 'number', label: "Column span" }
  ]
}

Lastly, we'll update the actual component to use the value by:

  • Passing in colspan via the data prop passed to the block, defaulting to 4 (one third) if not provided
  • Creating a function to take a column span (1-12) to a percentage width
  • Set the flex-basis and max-width styles of the column
export function ColumnBlock = ({index, data, children}) {
  const colspan = data.colspan ?? 3;
  const round = (value, decimals) => Number(Math.round(value+'e'+decimals)+'e-'+decimals);
  const width = round(100 / colspan, 2) * 100;
  
  return (
    <div style={{flexBasis: width + "%", maxWidth: width + "%"}}>
      <BlocksControls index={props.index}>
        {children}
      </BlocksControls>
    </div>
  )
)

Now columns have a settings modal to configure their column span.

Providing default column(s)

If you want new rows to default to having a certain number of columns, we can update the rows template.

For example, to default to three columns in every row, add 3 columns to the row template's defaultItem.

export const RowBlockTemplate = {
  label: 'Row',
  defaultItem: {
    columns: [
      {
        _template: "column",
        blocks: []
      },
      {
        _template: "column",
        blocks: []
      },
      {
        _template: "column",
        blocks: []
      }
    ]
  }
  fields: []
}

Providing default block(s)

If you want new columns to default to having a certain set of blocks, we can update the columns template.

For example, if we wanted every new column to have a paragraph when created:

export const ColumnBlockTemplate = {
  label: 'Column',
  defaultItem: {
    blocks: [
      {
        _template: "paragraph",
        text: "Hello world, I'm a default paragraph!"
      }
    ]
  }
  fields: []
}

Lazy Loading Blocks

For a simple InlineGrid with a few blocks, performance is not a problem. But once your get to dozens or even hundreds of blocks, this becomes a major performance concern for users.

Considering, InlineGrid allows the lazy loading of blocks, and will:

  • Lazy load all available blocks when in editing mode
  • Lazy load only the blocks used by the layout when not in editing mode

This is done by passing an array of block resolvers, which match the interface:

{
  id: string;
  importFunc: () => {
    Component: React.FunctionalComponent
    template: BlockTemplate
    preview: React.FunctionalComponent
  }
}

For example, for our example above:

export function MyApp(props) {
  const form = useForm(props);
  const blocks = useMemo(() => {
    return [
      {
        id: "paragraph",
        importFunc: async () => (await import("./ParagraphBlock)).default
      }
    ]
  }, []);

  return (
    <InlineForm form={form}>
      <InlineGrid
        row={RowBlock}
        column={ColumnBlock}
        blocks={blocks}
      />
    </InlineForm>
  );
}

react-tinacms-field-grid's People

Contributors

chrisdmacrae avatar

Watchers

 avatar  avatar

react-tinacms-field-grid's Issues

Add sidebar support

Setup sidebar/screen support for this field.

It should:

  • render a visual grid layout
  • render the form block UI for the internals of columns

Move from tsdx to jest + tina-scripts

Install required deps:

npm i tina-scripts rollup rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-typescript2 rollup-plugin-uglify typescript typescript-plugin-styled-components

Then update package scripts:

  "scripts": {
    "start": "npm run watch",
    "watch": "tinacms-scripts watch",
    "dev": "tinacms-scripts dev",
    "build": "tinacms-scripts build",
    "test": "jest --passWithNoTests"
  }

Then update "main" and "types" of package.json:

  "main": "build/index.js",
  "types": "build/index.d.ts",

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.