0.1.24 • Published 3 months ago

@tinloof/sanity-kit v0.1.24

Weekly downloads
-
License
ISC
Repository
-
Last release
3 months ago

Components and utils to extend Sanity


@tinloof/sanity-kit/navigator

Pages navigator to use in the new Presentation framework as "unstable_navigator".

preview

This plugin is in beta


Required dependencies and minimum versions

"react": "^18.2.0",
"react-dom": "^18.2.0",
"sanity": "^3.23.1"
"@sanity/overlays": "^2.3.0",
"@sanity/react-loader": "^1.6.4",
"@sanity/vision": "^3.23.1",

Note: The package is meant to be used with projects that use the new fetching system of loaders and overlays that is compatinble with presentation. Read more here →

This is the official Next.js starter from Sanity that uses this new updated system for reference


Getting started

Install

npm i @tinloof/sanity-kit

Wrap your Studio with NavigatorProvider

'use client'

import { NavigatorProvider } from  '@tinloof/sanity-kit/navigator'
import { NextStudio } from  'next-sanity/studio'

export  default  function  Studio() {
    return (
      <NavigatorProvider>
        <NextStudio  config={config}  />
      </NavigatorProvider>
  )
}

In your Sanity folder, wherever you keep your Sanity related config and files, create a React component called PagesNavigator (you can actually call it whatever you prefer, this is simply a suggested name becasue of how will be shown later in these instructions)

Start by importing the Navigator and all the needed hooks from the plugin

import Navigator, {
    ThemeProvider,
    useNavigator,
    useSanityFetch,
    useUpdateTree
} from '@tinloof/sanity-kit/navigator'

At this point you will need to import a query to fetch your pages and an array of locales (if you use multi locales, it is not required).

import { pagesRoutesQuery } from '@/sanity/lib/queries'
import locales from '@/sanity/schemas/locales'

To work with the Navigator, pages need to have these fields as required

_id:  string → // Sanity _id
_type:  string → // e.g "page" or "home" for home page singleton without slug
_updatedAt:  string → // From Sanity
_createdAt:  string → // From Sanity
title:  string → // Page title
routePath:  string → // Slug field from Sanity

While the locales array should look like this (if you are using locales); it goes without sayng that these locales should be reflecting and mirroring your locales in Sanity

[
    { title:  'English', value:  'en' },
    { title:  'French', value:  'fr' },
    { title:  'Spanish', value:  'es' },
]

Once these are setup, you can start composing your PagesNavigator

// ...imports from before

export  default  function  PagesNavigator() {
    const { items, locale } =  useNavigator()

    const  availableLocales  =  locales

    // Custom hook to fetch data: add your query and its variables
    const [data, loading] =  useSanityFetch({
      query:  pagesRoutesQuery,
      variables: {
        types: ['page', 'home'],
        locale:  !!locale  ?  locale  :  availableLocales[0].value,
      },
    })

    // This hook takes care of creating a tree with nested url segments pages
    useUpdateTree(data)

  function renderPages() {
    if(items.length) {
      return <Navigator.List  items={items} />
    } else {
      <Navigator.EmptySearchResults />
    }
  }

    return (
      <ThemeProvider>
            <Navigator.Header title="Pages Navigator">
                <Navigator.SearchBox />
                {availableLocales.length ? (
                    <Navigator.LocaleSelect locales={availableLocales} />
                ) : null}
            </Navigator.Header>

            {loading ? <Navigator.SkeletonListItems items={40} /> : null}

            {renderPages()}
      </ThemeProvider>
    )
}

At this point all is left is to go to your sanity.config.ts and add the PagesNavigator as unstable_component in your presentationTool plugin

import PagesNavigator from "./sanity/components/PagesNavigator";

export default defineConfig({
    // ...
    plugins: [
      // ...
      presentationTool({
        previewUrl: {
          origin:
            typeof  location  ===  "undefined"
            ?  "http://localhost:3000"
            :  location.origin,
          draftMode: {
            enable:  "/api/draft",
          },
      },
        components: {
          unstable_navigator: {
            component: PagesNavigator,
            minWidth: 360,
            maxWidth: 480,
          },
        },
      }),
      // ...
    ],
});

Once all is in plance you should be able to see your pages and navigate through them easily.


@tinloof/sanity-kit/utils

Getting started

Install

npm i @tinloof/sanity-kit

RoutePathField

preview

RoutePathField integrates with the navigator to simplify the task of creating route paths or slugs with nested segments/folders. It generates and appends the segments/folder structure of the URL, leaving only the last part of the slug (the actual last segment of the page) for the editor. For example, if the slug of a page is "/content/nested/page", the "content/nested" part will be an editable button that displays the current path up to the actual last segment of the page.

It takes 3 props, including the value coming from Sanity:

value: Spread the value obtained from component.field (refer to the example below).

siteDomain: The domain of the production URL, used for display purposes.

defaultLocale: The default locale for the site. It is used to exclude the default locale from the URL and only include the locales that require recognition.

In your Sanity schema, import the RoutePathField custom field:

import { RoutePathField } from '@tinloof/sanity-kit/utils'

// ... rest of your schema
defineField({
      type: 'slug',
      name: 'routePath',
      title: 'Route path',
      components: {
        field: (value) =>
          RoutePathField({
            ...value,
            siteDomain: 'https://example.com',
            defaultLocale: "en",
          }),
      },
      validation: (rule) => rule.required(),
    }),
// ... rest of your schema

Blocks Picker

preview preview

Blocks wizard is an abstraction in both UI and logic to build pages with a "blocks" mindset. The major abstraction in the UI is a modal picker to preview and select blocks visually.

The logic is abstracted using blockSchema, which lets you simply create your blocks wrapping this utility around it to make it work automatically, and BlockArrayField, which is the component to use in your page body field, also referred to in the following examples as blocksBody.

How to use it:

Step 1: Create your first block using the blockSchema utility. In this example we create a basic Hero block and we create a file in theblocks folder to export all blocks that we will use in the next steps:

// schemas/blocks/hero.ts
import { StarIcon } from '@sanity/icons'
import { blockSchema } from '@tinloof/sanity-kit/utils'

export const blockHero = blockSchema({
  name: 'block.hero',
  title: 'Hero',
  type: 'object',
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string',
      validation: (Rule) => Rule.required(),
    },
  ],
})

We use the name convention `block.` for consistency.*

// schemas/blocks/index.ts
import { hero } from './hero'

const blocks = [
  hero,
]

export default blocks

Step 2: create a body object to render all your blocks in a page

import { BlockArrayField } from '@tinloof/sanity-kit/utils'
import { defineType } from 'sanity'

// These are our previously created blocks!
import blocks from '../blocks'

// Differs from ptBody in that it specializes in adding & reordering blocks/modules.
export const body = defineType({
  name: 'body',
  title: 'Content',
  type: 'array',
  of: blocks.map((block) => ({
    type: block.name,
  })),
  components: {
    input: BlockArrayField,
  },
})

Step 3: Add your newly created schemas to Sanity config

export default defineConfig({
  basePath: studioUrl,
  projectId: projectId || '',
  dataset: dataset || '',
  title,
  schema: {
    // If you want more content types, you can add them to this array
    types: [
      // ... all the rest of your schemas
      body,
      hero,
    ],
  },
  plugins: // ... your plugins,
})

Step 4: use your body object in any page you want:

  export default defineType({
  type: 'document',
  name: 'page',
  title: 'Page',
  icon: DocumentIcon,
  fields: [
      // ... rest of your fields
    defineField({
      name: 'body',
      title: 'Content / body of the page',
      type: 'body',
    }),
  ],
  // ... rest of your fields
})
0.1.24

3 months ago

0.2.0

3 months ago

0.1.22

3 months ago

0.1.23

3 months ago

0.1.21

4 months ago

0.1.20

4 months ago

0.1.17

4 months ago

0.1.18

4 months ago

0.1.19

4 months ago

0.1.16

4 months ago

0.1.15

4 months ago

0.1.14

4 months ago

0.1.13

4 months ago

0.1.12

4 months ago

0.1.11

4 months ago

0.1.10

4 months ago

0.1.9

4 months ago

0.1.8

4 months ago

0.1.7

4 months ago