npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-tinacms-field-grid

v0.4.3

Published

A Flexbox grid builder field

Downloads

9

Readme

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>
  );
}