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

@argann-jse/vue-datocms

v1.1.7

Published

A set of components and utilities to work faster with DatoCMS in Vue.js environments

Downloads

358

Readme

vue-datocms

MIT MIT Build Status

A set of components and utilities to work faster with DatoCMS in Vue.js environments. Integrates seamlessy with DatoCMS's GraphQL Content Delivery API.

  • Works with both Vue 2 (only on JS projects, not TypeScript) and Vue 3;
  • TypeScript ready;
  • Compatible with any data-fetching library (axios, Apollo);
  • Usable both client and server side;
  • Compatible with vanilla Vue, Nuxt.js and pretty much any other Vue-based solution;

Table of Contents

Demos

Installation

npm install vue-datocms

In Vue 2, also install @vue/composition-api as a dependency. Everything else should be similar to the example above for Vue 3. Please note that with Vue 2 support is limited to JS-only projects. TypeScript + Vue 2 is not supported at the moment, sorry.

Live real-time updates

Thanks to the subscribeToQuery helper provided by the datocms-listen package you can get real-time updates for the page when the content changes. This helper connects to the DatoCMS's Real-time Updates API to receive the updated query results in real-time, and is able to reconnect in case of network failures.

Live updates are great both to get instant previews of your content while editing it inside DatoCMS, or to offer real-time updates of content to your visitors (ie. news site).

Reference

subscribeToQuery provides a way to create a subscription to DatoCMS's Real-time Updates API.

Please consult the datocms-listen package documentation to learn more about how to configure subscribeToQuery.

In Vue.js v2, the subscription can be created inside of the mounted lifecycle method. Please refer to the query-subscription example for a sample implementation.

In Vue.js v3, subscribeToQuery can be used to implement a custom hook. Please take a look at the React.js one for a sample implementation.

Progressive/responsive image

<datocms-image> is a Vue component specially designed to work seamlessly with DatoCMS’s responsiveImage GraphQL query that optimizes image loading for your sites.

Out-of-the-box features

  • Offers WebP version of images for browsers that support the format
  • Generates multiple smaller images so smartphones and tablets don’t download desktop-sized images
  • Efficiently lazy loads images to speed initial page load and save bandwidth
  • Holds the image position so your page doesn’t jump while images load
  • Uses either blur-up or background color techniques to show a preview of the image while it loads

Intersection Observer

Intersection Observer is the API used to determine if the image is inside the viewport or not. Browser support is really good - With Safari adding support in 12.1, all major browsers now support Intersection Observers natively.

If the IntersectionObserver object is not available, the component treats the image as it's always visible in the viewport. Feel free to add a polyfill so that it will also 100% work on older versions of iOS and IE11.

Setup

You can register the component globally so it's available in all your apps:

import Vue from 'vue';
import { DatocmsImagePlugin } from 'vue-datocms';

Vue.use(DatocmsImagePlugin);

Or use it locally in any of your components:

import { Image } from 'vue-datocms';

export default {
  components: {
    'datocms-image': Image,
  },
};

Usage

  1. Use <datocms-image> it in place of the regular <img /> tag
  2. Write a GraphQL query to your DatoCMS project using the responsiveImage query

The GraphQL query returns multiple thumbnails with optimized compression. The <datocms-image> component automatically sets up the "blur-up" effect as well as lazy loading of images further down the screen.

Example

For a fully working example take a look at our examples directory.

<template>
  <article>
    <div v-if="data">
      <h1>{{ data.blogPost.title }}</h1>
      <datocms-image :data="data.blogPost.cover.responsiveImage" />
    </div>
  </article>
</template>

<script>
import { request } from './lib/datocms';
import { Image } from 'vue-datocms';

const query = gql`
  query {
    blogPost {
      title
      cover {
        responsiveImage(
          imgixParams: { fit: crop, w: 300, h: 300, auto: format }
        ) {
          # HTML5 src/srcset/sizes attributes
          srcSet
          webpSrcSet
          sizes
          src

          # size information (post-transformations)
          width
          height
          aspectRatio

          # SEO attributes
          alt
          title

          # background color placeholder or...
          bgColor

          # blur-up placeholder, JPEG format, base64-encoded
          base64
        }
      }
    }
  }
`;

export default {
  components: {
    'datocms-image': Image,
  },
  data() {
    return {
      data: null,
    };
  },
  async mounted() {
    this.data = await request({ query });
  },
};
</script>

Props

| prop | type | default | required | description | | --------------------- | ------------------------ | ----------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | data | ResponsiveImage object | | :white_check_mark: | The actual response you get from a DatoCMS responsiveImage GraphQL query. | | class | string | null | :x: | Additional CSS class of root node | | root-style | CSS properties | null | :x: | Additional CSS rules to add to the root node | | picture-class | string | null | :x: | Additional CSS class for the inner <picture /> tag | | picture-style | CSS properties | null | :x: | Additional CSS rules to add to the inner <picture /> tag | | fade-in-duration | integer | 500 | :x: | Duration (in ms) of the fade-in transition effect upoad image loading | | intersection-treshold | float | 0 | :x: | Indicate at what percentage of the placeholder visibility the loading of the image should be triggered. A value of 0 means that as soon as even one pixel is visible, the callback will be run. A value of 1.0 means that the threshold isn't considered passed until every pixel is visible. | | intersection-tmargin | string | "0px 0px 0px 0px" | :x: | Margin around the placeholder. Can have values similar to the CSS margin property (top, right, bottom, left). The values can be percentages. This set of values serves to grow or shrink each side of the placeholder element's bounding box before computing intersections. | | lazy-load | Boolean | true | :x: | Wheter enable lazy loading or not | | explicitWidth | Boolean | false | :x: | Wheter the image wrapper should explicitely declare the width of the image or keep it fluid |

The ResponsiveImage object

The data prop expects an object with the same shape as the one returned by responsiveImage GraphQL call. It's up to you to make a GraphQL query that will return the properties you need for a specific use of the <datocms-image> component.

  • The minimum required properties for data are: aspectRatio, width, sizes, srcSet and src;
  • alt and title, while not mandatory, are all highly suggested, so remember to use them!
  • You either want to add the webpSrcSet field or specify { auto: format } in your imgixParams, to automatically use WebP images in browsers that support the format;
  • If you provide both the bgColor and base64 property, the latter will take precedence, so just avoiding querying both fields at the same time, it will only make the response bigger :wink:

Here's a complete recap of what responsiveImage offers:

| property | type | required | description | | ----------- | ------- | ------------------ | ----------------------------------------------------------------------------------------------- | | aspectRatio | float | :white_check_mark: | The aspect ratio (width/height) of the image | | width | integer | :white_check_mark: | The width of the image | | sizes | string | :white_check_mark: | The HTML5 sizes attribute for the image | | srcSet | string | :white_check_mark: | The HTML5 srcSet attribute for the image | | src | string | :white_check_mark: | The fallback src attribute for the image | | webpSrcSet | string | :x: | The HTML5 srcSet attribute for the image in WebP format, for browsers that support the format | | alt | string | :x: | Alternate text (alt) for the image | | title | string | :x: | Title attribute (title) for the image | | bgColor | string | :x: | The background color for the image placeholder | | base64 | string | :x: | A base64-encoded thumbnail to offer during image loading |

Social share, SEO and Favicon meta tags

Just like the image component, toHead() is a helper specially designed to work seamlessly with DatoCMS’s _seoMetaTags and faviconMetaTags GraphQL queries so that you can handle proper SEO in your pages.

You can use toHead() inside the metaInfo (or head, in Nuxt.js) property of your components, and it will return meta tags as required by the vue-meta package.

Usage

toHead() takes an array of Tags in the exact form they're returned by the following DatoCMS GraphQL API queries:

  • _seoMetaTags query on any record, or
  • faviconMetaTags on the global _site object.

You can pass multiple arrays of Tags together and pass them to a single toHead() call.

Example

For a working example take a look at our examples directory.

<template>
  <article>
    <h1 v-if="data">{{ data.page.title }}</h1>
  </article>
</template>

<script>
import { request } from './lib/datocms';
import { toHead } from 'vue-datocms';

const query = gql`
  query {
    page: homepage {
      title
      seo: _seoMetaTags {
        attributes
        content
        tag
      }
    }

    site: _site {
      favicon: faviconMetaTags {
        attributes
        content
        tag
      }
    }
  }
`;

export default {
  data() {
    return {
      data: null,
    };
  },
  async mounted() {
    this.data = await request({ query });
  },
  metaInfo() {
    if (!this || !this.data) {
      return;
    }
    return toHead(this.data.page.seo, this.data.site.favicon);
  },
};
</script>

Structured text

<datocms-structured-text /> is a Vue component that you can use to render the value contained inside a DatoCMS Structured Text field type.

Setup

You can register the component globally so it's available in all your apps:

import Vue from 'vue';
import { DatocmsStructuredTextPlugin } from 'vue-datocms';

Vue.use(DatocmsStructuredTextPlugin);

Or use it locally in any of your components:

import { StructuredText } from 'vue-datocms';

export default {
  components: {
    'datocms-structured-text': StructuredText,
  },
};

Basic usage

<template>
  <article>
    <div v-if="data">
      <h1>{{ data.blogPost.title }}</h1>
      <datocms-structured-text :data="data.blogPost.content" />
      <!--
        Final result:
        <h1>Hello <strong>world!</strong></h1>
      -->
    </div>
  </article>
</template>

<script>
import { request } from './lib/datocms';
import { StructuredText } from 'vue-datocms';

const query = gql`
  query {
    blogPost {
      title
      content {
        value
      }
    }
  }
`;

export default {
  components: {
    'datocms-structured-text': StructuredText,
  },
  data() {
    return {
      data: null,
    };
  },
  async mounted() {
    this.data = await request({ query });
    // data.blogPost.content ->
    // {
    //   value: {
    //     schema: "dast",
    //     document: {
    //       type: "root",
    //       children: [
    //         {
    //           type: "heading",
    //           level: 1,
    //           children: [
    //             {
    //               type: "span",
    //               value: "Hello ",
    //             },
    //             {
    //               type: "span",
    //               marks: ["strong"],
    //               value: "world!",
    //             },
    //           ],
    //         },
    //       ],
    //     },
    //   },
    // }
  },
};
</script>

Custom renderers

You can also pass custom renderers for special nodes (inline records, record links and blocks) as an optional parameter like so:

<template>
  <article>
    <div v-if="data">
      <h1>{{ data.blogPost.title }}</h1>
      <datocms-structured-text
        :data="data.blogPost.content"
        :renderInlineRecord="renderInlineRecord"
        :renderLinkToRecord="renderLinkToRecord"
        :renderBlock="renderBlock"
      />
      <!--
        Final result:

        <h1>Welcome onboard <a href="/team/mark-smith">Mark</a></h1>
        <p>
          So happy to have
          <a href="/team/mark-smith">this awesome humang being</a> in our team!
        </p>
        <img
          src="https://www.datocms-assets.com/205/1597757278-austin-distel-wd1lrb9oeeo-unsplash.jpg"
          alt="Our team at work"
        />
      -->
    </div>
  </article>
</template>

<script>
import { request } from './lib/datocms';
import { StructuredText, Image } from 'vue-datocms';
import { h } from 'vue-demi';

const query = gql`
  query {
    blogPost {
      title
      content {
        value
        links {
          __typename
          ... on TeamMemberRecord {
            id
            firstName
            slug
          }
        }
        blocks {
          __typename
          ... on ImageRecord {
            id
            image {
              responsiveImage(
                imgixParams: { fit: crop, w: 300, h: 300, auto: format }
              ) {
                srcSet
                webpSrcSet
                sizes
                src
                width
                height
                aspectRatio
                alt
                title
                base64
              }
            }
          }
        }
      }
    }
  }
`;

export default {
  components: {
    'datocms-structured-text': StructuredText,
    'datocms-image': Image,
  },
  data() {
    return {
      data: null,
    };
  },
  methods: {
    renderInlineRecord: ({ record }) => {
      switch (record.__typename) {
        case 'TeamMemberRecord':
          return h('a', { href: `/team/${record.slug}` }, record.firstName);
        default:
          return null;
      }
    },
    renderLinkToRecord: ({ record, children, transformedMeta }) => {
      switch (record.__typename) {
        case 'TeamMemberRecord':
          return h(
            'a',
            { ...transformedMeta, href: `/team/${record.slug}` },
            children,
          );
        default:
          return null;
      }
    },
    renderBlock: ({ record }) => {
      switch (record.__typename) {
        case 'ImageRecord':
          return h('datocms-image', {
            data: record.image.responsiveImage,
          });
        default:
          return null;
      }
    },
  },
  async mounted() {
    this.data = await request({ query });
    // data.blogPost.content ->
    // {
    //   value: {
    //     schema: "dast",
    //     document: {
    //       type: "root",
    //       children: [
    //         {
    //           type: "heading",
    //           level: 1,
    //           children: [
    //             { type: "span", value: "Welcome onboard " },
    //             { type: "inlineItem", item: "324321" },
    //           ],
    //         },
    //         {
    //           type: "paragraph",
    //           children: [
    //             { type: "span", value: "So happy to have " },
    //             {
    //               type: "itemLink",
    //               item: "324321",
    //               children: [
    //                 {
    //                   type: "span",
    //                   marks: ["strong"],
    //                   value: "this awesome humang being",
    //                 },
    //               ]
    //             },
    //             { type: "span", value: " in our team!" },
    //           ]
    //         },
    //         { type: "block", item: "1984559" }
    //       ],
    //     },
    //   },
    //   links: [
    //     {
    //       id: "324321",
    //       __typename: "TeamMemberRecord",
    //       firstName: "Mark",
    //       slug: "mark-smith",
    //     },
    //   ],
    //   blocks: [
    //     {
    //       id: "324321",
    //       __typename: "ImageRecord",
    //       image: {
    //         responsiveImage: { ... },
    //       },
    //     },
    //   ],
    // }
  },
};
</script>

Override default rendering of nodes

This component automatically renders all nodes except for inline_item, item_link and block using a set of default rules, but you might want to customize those. For example:

  • For heading nodes, you might want to add an anchor;
  • For code nodes, you might want to use a custom sytax highlighting component;

In this case, you can easily override default rendering rules with the customNodeRules and customMarkRules props.

<template>
  <datocms-structured-text
    :data="data.blogPost.content"
    :customNodeRules="customNodeRules"
    :customMarkRules="customMarkRules"
  />
</template>

<script>
import { StructuredText, renderNodeRule, renderMarkRule } from "vue-datocms";
import { isHeading, isCode } from "datocms-structured-text-utils";
import { render as toPlainText } from 'datocms-structured-text-to-plain-text';
import SyntaxHighlight from './components/SyntaxHighlight';

export default {
  components: {
    "datocms-structured-text": StructuredText,
    "syntax-highlight": SyntaxHighlight,
  },
  data() {
    return {
      data: /* ... */,
      customNodeRules: [
        renderNodeRule(isHeading, ({ adapter: { renderNode: h }, node, children, key }) => {
          const anchor = toPlainText(node)
            .toLowerCase()
            .replace(/ /g, '-')
            .replace(/[^\w-]+/g, '');

          return h(
            `h${node.level}`, { key }, [
              ...children,
              h('a', { attrs: { id: anchor } }, []),
              h('a', { attrs: { href: `#${anchor}` } }, []),
            ]
          );
        }),
        renderNodeRule(isCode, ({ adapter: { renderNode: h }, node, key }) => {
          return h('syntax-highlight', {
            key,
            code: node.code,
            language: node.language,
            linesToBeHighlighted: node.highlight,
          }, []);
        }),
      ],
      customMarkRules: [
        // convert "strong" marks into <b> tags
        renderMarkRule('strong', ({ adapter: { renderNode: h }, mark, children, key }) => {
          return h('b', {key}, children);
        }),
      ],
    };
  },
};
</script>

Note: if you override the rules for inline_item, item_link or block nodes, then the renderInlineRecord, renderLinkToRecord and renderBlock props won't be considered!

Props

| prop | type | required | description | default | | ------------------ | ---------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | | data | StructuredTextGraphQlResponse \| DastNode | :white_check_mark: | The actual field value you get from DatoCMS | | | renderInlineRecord | ({ record }) => VNode \| null | Only required if document contains inlineItem nodes | Convert an inlineItem DAST node into a VNode | [] | | renderLinkToRecord | ({ record, children, transformedMeta }) => VNode \| null | Only required if document contains itemLink nodes | Convert an itemLink DAST node into a VNode | null | | renderBlock | ({ record }) => VNode \| null | Only required if document contains block nodes | Convert a block DAST node into a VNode | null | | metaTransformer | ({ node, meta }) => Object \| null | :x: | Transform link and itemLink meta property into HTML props | See function | | customNodeRules | Array<RenderRule> | :x: | Customize how nodes are converted in JSX (use renderNodeRule() to generate) | null | | customMarkRules | Array<RenderMarkRule> | :x: | Customize how marks are converted in JSX (use renderMarkRule() to generate) | null | | renderText | (text: string, key: string) => VNode \| string \| null | :x: | Convert a simple string text into a VNode | (text) => text |