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

@zen_flow/react-tinacms-inline

v0.36.1-zenflow.0

Published

This package provides the components and helpers for [Inline Editing](https://tinacms.org/docs/ui/inline-editing).

Downloads

3

Readme

react-tinacms-inline

This package provides the components and helpers for Inline Editing.

Install

yarn add react-tinacms-inline

For specific steps on setting up Inline Editing on a page, refer to the Inline Editing documentation.

Inline Form

The InlineForm component provides an inline editing context. To use, wrap the component where you want to render inline fields and pass the form.

Usage

export function Page(props) {
  const [, form] = useForm(props.data)
  usePlugin(form)

  return (
    <InlineForm form={form}>
      <main>
        {
            //...
        }
      </main>
    </InlineForm>
  )
}

Interface

interface InlineFormProps {
  form: Form
  children: React.ReactElement | React.ReactElement[] | InlineFormRenderChild
}

interface InlineFormRenderChild {
  (props: InlineFormRenderChildOptions):
    | React.ReactElement
    | React.ReactElement[]
}

type InlineFormRenderChildOptions = InlineFormState &
  Omit<FormRenderProps<any>, 'form'>

/**
 * This state is available via context
*/
interface InlineFormState {
  form: Form
  focussedField: string
  setFocussedField(field: string): void
}

| Key | Description | | ------------- | -------------------------------------------------------------------------------------------- | | form | A Tina Form. | | children | Child components to render — Either React Elements or Render Props Children that receive the form state and other Form Render Props |

useInlineForm

useInlineForm(): InlineFormState

The useInlineForm hook can be used to access the inline editing context. It must be used within a child component of InlineForm.

Inline Field

Inline Fields should provide basic inputs for editing data on the page and account for both enabled / disabled CMS states. All Inline Fields must be children of an InlineForm.

Interface

export interface InlineFieldProps {
  name: string
  children(fieldProps: InlineFieldRenderProps): React.ReactElement
}

export interface InlineFieldRenderProps<V = any>
  extends FieldRenderProps<V>,
    InlineFormState {}

| Key | Description | | ------------- | -------------------------------------------------------------------------------------------- | | name | The path to the editable data. | | children | Child components to render — React Elements that receive the form state and Field Render Props. |

Available Inline Fields

See the full list of inline fields or learn how to make custom inline fields.

Below is a list of fields provided by the react-tinacms-inline package:

Inline Field Styles

Styles are stripped as much as possible to prevent interference with base site styling. When toggling between enabled / disabled states, the default inline fields will switch between rendering child elements with any additional editing UI and just passing the child elements alone.

For example with InlineText:

export function InlineText({
  name,
  className,
  focusRing = true,
}: InlineTextProps) {
  const cms: CMS = useCMS()

  return (
    <InlineField name={name}>
      {({ input }) => {
        /**
        * If the cms is enabled, render the input
        * with the focus ring
        */
        if (cms.enabled) {
          if (!focusRing) {
            return <Input type="text" {...input} className={className} />
          }

          return (
            <FocusRing name={name} options={focusRing}>
              <Input type="text" {...input} className={className} />
            </FocusRing>
          )
        }
        /**
        * Otherwise, pass the input value
        */
        return <>{input.value}</>
      }}
    </InlineField>
  )
}

Input is a styled-component with some base styling aimed at making this component mesh well with the surrounding site. If you ever need to override default Inline Field styles, read about this workaround to extend styles.

Focus Ring

The common UI element on all Inline Fields is the FocusRing. The focus ring provides context to which field is active / available to edit.

Interface

interface FocusRingProps {
  name?: string
  children: React.ReactNode | ((active: boolean) => React.ReactNode)
  options?: boolean | FocusRingOptions
}

interface FocusRingOptions {
  offset?: number | { x: number; y: number }
  borderRadius?: number
  nestedFocus?: boolean
}

Focus Ring Props

You would only use these options if you were creating custom inline fields and working with the FocusRing directly.

| Key | Description | | ------------- | -------------------------------------------------------------------------------------------- | | children | Optional: Child elements to render. | | name | Optional: This value is used to set the focused / active field. | | options | Optional: The FocusRingOptions outlined below. |

Focus Ring Children

FocusRing optionally accepts render prop patterned children, which receive the active state and can be used to conditionally render elements based on whether the FocusRing currently has focus.

<FocusRing>
 {active => {
   if (active) {
     return <ComplicatedEditableComponent />
   }
   return <SimpleDisplayComponent />
 }}
</FocusRing>

Focus Ring Options

These options are passed to default inline fields or inline block fields via the focusRing prop on most default inline fields. The options are configurable by the developer setting up the inline form & fields. Refer to individual inline field documentation for additional examples.

| Key | Description | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | offset | Optional: Sets the distance from the focus ring to the edge of the child element(s). It can either be a number (in pixels) for both x & y offsets, or individual x & y offset values passed in an object.| | borderRadius| Optional: Determines (in pixels) the rounded corners of the focus ring. | | nestedFocus | Optional: Disables pointer-events (clicking) and hides blocks empty state.|

Inline Blocks

Inline Blocks consist of an array of Blocks to render in an Inline Form. Refer to the Inline Blocks Documentation or Guide for code examples on creating Inline Blocks.

Interface

export interface InlineBlocksProps {
  name: string
  blocks: {
    [key: string]: Block
  }
  className?: string
  direction?: 'vertical' | 'horizontal'
  itemProps?: {
    [key: string]: any
  }
  min?: number
  max?: number
  components?: {
    Container?: React.FunctionComponent<BlocksContainerProps>
  }
}

| Key | Purpose | | ----------- | -----------------------------------------------------------------------------------------------------------------------------------------: | | name | The path to the source data for the blocks. | | blocks | An object composed of individual Blocks. | | className | Optional — To set styles directly on the input or extend via styled components. | | direction | Optional — Sets the orientation of the AddBlock button position. | | itemProps | Optional — An object that passes additional props to every block child element. | | min | Optional — Controls the minimum number of blocks. Once reached, blocks won't be able to be removed. (Optional) | | max | Optional — Controls the maximum number of blocks allowed. Once reached, blocks won't be able to be added. (Optional) |

Actions

The Inline Blocks Actions are used by the Inline Blocks Controls. Use these if you are building your own custom Inline Block Field Controls. These actions are avaiable in the Inline Blocks Context.

interface InlineBlocksActions {
  count: number
  insert(index: number, data: any): void
  move(from: number, to: number): void
  remove(index: number): void
  blocks: {
    [key: string]: Block
  }
  activeBlock: number | null
  setActiveBlock: any
  direction: 'vertical' | 'horizontal'
  min?: number
  max?: number
}

useInlineBlocks

useInlineBlocks(): InlineBlocksActions

useInlineBlocks is a hook that can be used to access the Inline Blocks Context when creating custom controls.

Inline Block Field Controls

Editors can add / delete / rearrange blocks with the blocks controls. They can also access additional fields in a Settings Modal.

Interface

interface BlocksControlsProps {
  index: number
  insetControls?: boolean
  focusRing?: false | FocusRingProps
  customActions?: BlocksControlActionItem[]
  label?: boolean
  children: React.ReactChild
}

interface FocusRingProps {
  offset?: number | { x: number; y: number }
  borderRadius?: number
}

export interface BlocksControlActionItem {
  icon: React.ReactNode
  onClick: () => void
}

| Key | Description | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | index | The index of the block associated with these controls. | | insetControls | A boolean to denote whether the group controls display within or outside the group. | | focusRing | Either an object to style the focus ring or false, which hides the focus ring entirely. For styles, offset (in pixels) controls the distance from the ring to the edge of the group; borderRadius(in pixels) controls the rounding edge of the focus ring. | | label | A boolean to control whether or not a block label is rendered. | | children | Any child components, typically inline field(s). | | customActions | An array of objects containing custom block action configuration. icon is the component to render in the toolbar. onClick handles the action behavior. |

Block Definition

A block is made of two parts: a component that renders in edit mode, and a template to configure fields, defaults and other required data.

Interface

interface Block {
  Component: React.FC<BlockComponentProps>
  template: BlockTemplate
}

interface BlockComponentProps {
  name: string
  index: number
  data: any
}

interface BlockTemplate {
  label: string
  defaultItem?: object | (() => object)
  fields?: Field[]
}

Block Component

| Key | Purpose | | ------------- | ---------------------------------------------------------------------------------------------------------------------: | | name | A unique identifier and pseudo-path to the block from the parent blocks array. e.g. the first child would be 'blocks.0' | | index | Position in the block array. | | data | The source data. |

Block Template

| Key | Purpose | | ------------- | ---------------------------------------------------------------------------------------------------------------------: | | label | A human readable label. | | defaultItem | Optional — Populates new blocks with default data. | | fields | Optional — Populates fields in the Settings Modal. |

Examples

Block Components can render Inline Fields to expose the data for editing. When using Inline Fields within a block, the name property should be relative to the block object in the source data.

For example:

import { useForm } from 'tinacms'
import { InlineForm, InlineBlocks, BlocksControls, InlineTextarea } from 'react-tinacms-inline'

function FeaturePage({ data }) {
  const [ , form ] = useForm({
    id: 'my-features-id',
    label: 'Edit Features',
    fields: [],
    initialValues: data,
  })

  return (
    <InlineForm form={form}>
      <div className="wrapper">
        <InlineBlocks name="features_blocks" blocks={FEATURE_BLOCKS} />
      </div>
    </InlineForm>
  )
}

function Feature({ index }) {
  return (
    <BlocksControls index={index}>
      <div className="feature">
        <h3>
        {/**
        * The `name` property is relative to individual
        * `features_blocks` array items (blocks). The full path
        * in the source file (example below) would be
        *  `features_blocks[index].heading`
        */}
          <InlineTextarea name="heading" focusRing={false} />
        </h3>
        <p>
          <InlineTextarea name="supporting_copy" focusRing={false} />
        </p>
      </div>
    </BlocksControls>
  )
}

const featureBlock = {
  Component: Feature,
  template: {
    label: 'Feature',
    defaultItem: {
      _template: 'feature',
      heading: 'Marie Skłodowska Curie',
      supporting_copy:
        'Muse about vastness.',
    },
    fields: [],
  },
}

const FEATURE_BLOCKS = {
    featureBlock
}

Example JSON data

{
    "features_blocks": [
        {
            "_template": "feature",
            "heading": "Drake Equation",
            "supporting_copy": "Light years gathered by gravity Rig Veda.."
        },
        {
            "_template": "feature",
            "heading": "Jean-François Champollion",
            "supporting_copy": "Not a sunrise but a galaxyrise."
        },
        {
            "_template": "feature",
            "heading": "Sea of Tranquility",
            "supporting_copy": "Bits of moving fluff take root and flourish."
        }
    ]
}

Below is another example using InlineBlocks with multiple block definitions:

import { useJsonForm } from 'next-tinacms-json'
import { InlineForm, InlineBlocks, BlocksControls, InlineTextarea } from 'react-tinacms-inline'

export default function PageBlocks({ jsonFile }) {
  const [, form] = useJsonForm(jsonFile)

  return (
    <InlineForm form={form}>
      <InlineBlocks name="my_blocks" blocks={PAGE_BLOCKS} />
    </InlineForm>
  )
}

/** Example Heading Block Definition
 * Component + template
*/
function Heading({ index }) {
  return (
    <BlocksControls index={index}>
      <InlineTextarea name="text" />
    </BlocksControls>
  )
}

const heading_template = {
  label: 'Heading',
  defaultItem: {
    text: 'At vero eos et accusamus',
  },
  fields: [],
}

const headingBlock = {
    Component: Heading,
    template: heading_template
}

/**
 * Example Paragraph Block
 * Component + template
*/

function Paragraph({ index }) {
  return (
    <BlocksControls index={index} focusRing={{ offset: 0 }} insetControls>
      <div className="paragraph__background">
        <div className="wrapper wrapper--narrow">
          <p className="paragraph__text">
            <InlineTextarea name="text" focusRing={false} />
          </p>
        </div>
      </div>
    </BlocksControls>
  );
}

const paragraphBlock = {
  Component: Paragraph,
  // template defined inline
  template: {
    label: 'Paragraph',
    defaultItem: {
      text:
        'Take root and flourish quis nostrum exercitationem ullam',
    },
    fields: [],
  },
};

/**
 * Available blocks passed to InlineBlocks to render
*/

const PAGE_BLOCKS = {
    headingBlock,
    paragraphBlock
}

Configuring the Container

InlineBlocks wraps your blocks with a <div> element by default. This can be an issue if your styles require direct inheritance, such as a flexbox grid:

<div class="row">
  <div class="column">
  </div>
</div>

To handle this, you can pass a custom container component to InlineBlocks.

Interface

interface BlocksContainerProps {s
  className?: string
  children?: React.ReactNode
}

Example

import { useJsonForm } from 'next-tinacms-json'
import { InlineForm, InlineBlocks, BlocksControls, InlineTextarea } from 'react-tinacms-inline'

const MyBlocksContainer = (children}) => (
  <div>
    {children}
  </div>
)

export default function PageBlocks({ jsonFile }) {
  const [, form] = useJsonForm(jsonFile)

  return (
    <InlineForm form={form}>
      <InlineBlocks
        name="my_blocks"
        blocks={PAGE_BLOCKS}
        components={{
          Container: MyBlocksContainer
        }}
      />
    </InlineForm>
  )
}

Checkout this guide to learn more on using Inline Blocks.