Giter Club home page Giter Club logo

sanity-blocks-vue-component's People

Contributors

mornir avatar rdunk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

sanity-blocks-vue-component's Issues

Custom blocks for headings with nested other component

Hi, thank you for the amazing plugin. Sanity + Gridsome is the best.

I am trying to modify the default block serializer with the following code:

serializers: {
        types: {
          block: ({ node }) => {
            switch (node.style) {
              case 'h2':
                return <h2 class="blub-h2">{node.children}</h2>
              case 'h3':
                return <h3 class="blub-h3 >{node.children}</h3>
              case 'h4':
                return <h4 class="blub-h4">{node.children}</h4>
              default: 
                return <p>{node.children}</p>
            }  
          },..........

And this does work as long as there is text inside the heading block. However, when I use a custom inline block inside a heading, it keeps returning undefined.
With the default block serializer it processes the custom component inside fine. Any idea how I can make that work?

Thanks!

Custom Mark serializers work, custom style serializers ignored

With version 0.1.0, I’m finding that styles are only applied to a set of styles that use predefined HTML tags e.g. blockquote, h1, h2, etc.

When I try to create my own styles, these are ignored.

Mark serialization and custom annotations have always worked perfectly so I’m not sure where I am going wrong.

Any insight would be greatly appreciated!

Thanks so much,

Simon

PortableText.vue:

<template>
  <BlockContent
    v-if="blocks"
    :blocks="blocks"
    :className="className ? className :'text'"
    :renderContainerOnSingleChild="true"
    :serializers="serializers"
  />
</template>

<script>
import PortableTextCenter from '~/components/PortableTextCenter.vue';
import PortableTextUnderline from '~/components/PortableTextUnderline.vue';
import PortableTextExternalLink from '~/components/PortableTextExternalLink.vue';
export default {
  props: ['blocks', 'className'],
  data() {
    return {
      serializers: {
        styles: {
          center: PortableTextCenter,
          center: (props,children) => {
            return h("blockquote", {}, children.slots.default()[0].children);
          },
        },
        marks: {
          underline: PortableTextUnderline,
          link: PortableTextExternalLink
        }
      }
    }
  }
}
</script>

PortableTextCenter.vue:

<template>
  <span class="center"><slot /></span>
</template>

<style lang="scss" scoped>
.center {
  display: block;
  width: 100%;
  text-align: center;
}
</style>

CustomComponent Serializer Example?

Hi, I'm having trouble using a custom component as a serializer. I've tried looking for examples, but am also struggling to find those.

I'm trying to create a serializer for a code block, and my component looks like this:

<script>
export default {
  name: 'Code',
  functional: true,
  render(h, { props }) {
    return h(
      'pre',
      {
        props,
      },
      props.code
    )
  },
}
</script>

and I'm importing it into my view:

<template>
  <div>
      <SanityContent
        :blocks="fetchedBlocks" // Assignment removed for clarity
        :serializers="serializers"
      />
  </div>
</template>
<script>
import Code from '~/components/Code.vue'

export default {
  name: 'Page',
  data() {
    return {
      serializers: {
        types: {
          code: Code,
        },
      },
    }
  },
}
</script>

I'm using Nuxt.js with the SanityContent component, and am able to load these changes client-side when it hot reloads, but after refreshing the page I get a Maximum call stack size exceeded in regards to @nuxt/devalue getting stuck in a loop.

Is there something I'm missing in regards to creating a component serializer or importing it?
Thanks!

Trimmed dependencies

At the moment this package depends on @sanity/client through a number of other dependencies. Would it be possible to refactor so this package could be a bit lighter?

Allow for empty or undefined value

Hey! :)

Would it make sense to avoid an error when retrieving an empty or undefined value for :blocks?

I have the situation that the speaker.description could be accidentally empty because sanity doesn't have a default or empty value for empty fields:

<BlockContent
  :blocks="speaker.description"
/>

Which would lead in nuxt.js to a server error.

I could of course check like:

<BlockContent
  v-if="speaker.description"
  :blocks="speaker.description"
/>

But I would like to avoid doing so everywhere and everytime I use the component. Looking forward to your feedback!

<em> Not rendering in SanityContent

Hello there,<em> marks are not rendering in SanityContent. Is this intentional? In src/sanity-module.ts there is code that sets it as default.. but on the front end you have to explicitly set em in the marks object. strong is working as intended..

Current work around is:

data() {
    return {
      serializers: {
        types: ...,
        marks: {
          em: 'em',
        },
      },
    }
  },

Am I doing something wrong?

Example in readme doesn't typecheck

Using e.g. vue-tsc to get typechecking in Vue templates the example in the readme doesn't actually typecheck. Specifically you'll get an error that the key block is missing from the types object nested in serializers. This seems to be because block is defined as a required key here: https://github.com/rdunk/sanity-blocks-vue-component/blob/master/src/types.ts#L77. Though the serializers prop is a Partial<Serializers>, Partial doesn't extend to nested objects, so it doesn't stop block from being required.

Block list items render marks only as span

As of version 1.0.1, all marks inside blocks defined as list items (bullet or numbered):

{
   "_type": "block",
   "children": [...],
   "level": 1,
   "listItem": "bullet",
   "style": "normal"
   "markDefs": [
      {
         "_type": "link",
         "href": "https://test.com"
      }
   ]
}

will be ignored and rendered as <span> rather than <a>. Custom marks passed in the serializers prop will also be ignored.

"TypeError: Cannot read property 'call' of undefined" when using it with Gridsome

Hi

I am only working development from time to time so this might be a novice question, sorry.
I am trying to use it with Gridsome but I get "TypeError: Cannot read property 'call' of undefined"

My block content is basic rich text from an about page. Paragraphs with links.

I registered the component in the main.js file for global use:

import DefaultLayout from '~/layouts/Default.vue'
import { SanityBlocks } from 'sanity-blocks-vue-component';

export default function (Vue, { router, head, isClient }) {
  Vue.component('Layout', DefaultLayout)
  Vue.component('sanity-blocks', SanityBlocks)
}

And my component looks like this:

<template>
  <div>
    <sanity-blocks :blocks="$static.about._rawColumn_1" />
  </div>
</template>

<static-query>
query {
  about: sanityAbout (id: "about") {
    title
    _rawColumn_1
  }
}
</static-query>

I am not using any custom serializers as my block content is basic. In case I interpretted the description correctly "This package comes with default serializers for rendering basic block content"

I tried everything I could find online but I end up at the same error.

Anyone who can point me in the right direction? Thank you.

Access ordinal position within blocks array

More a question than an issue. Is there a way of determining the position of a component within the blocks array?

Use case would be to apply a class to a component's outer element if it's the first item in the parent blocks array.

I guess I could use:

this.$parent.blocks.findIndex(block => block._key == $attrs._key)

Is there a better option?

Add option to specify container element

Current all the blocks are wrapped in a div.
It would be nice to be able to specify the containing element either as a simple string or a component.
For example if you know all the blocks will be list-elements, one should be able to wrap them in anul-element.

The React package does this through the serializers.container prop.

custom serializer types is error when using ssr: true

I have created 2 custom serializer types, when develop using ssr: false, all images and video iframe shows up correctly, but when using ssr: true, there's and error in nuxt generate,
when in browser, all custom types is showing at first load then disappearing.
this is my nuxtJs code:

<template v-if="$fetchState.pending && !contents.length"> </template>
    <template v-else>
<article>
              <client-only>
                <PortableText
                  :blocks="contents.slugData.body"
                  :serializers="serializers"
                />
              </client-only>
            </article>
<template>


import PortableText from 'sanity-blocks-vue-component'
export default {
  name: 'NewsSlug',
  components: {
    PortableText,
    LazyHydrate,
  },
  async fetch() {
    if (this.$route.query.preview === 'true') {
      this.contents = await sanityClient.fetch(previewQuery, this.$route.params)
    }
    this.contents = await sanityClient.fetch(prodQuery, this.$route.params)
  },
  data() {
    return {
      heroImageSize: {
        width: 672,
        height: 488,
      },
      serializers: {
        marks: {
          internalLink: InternalLink,
          link: ExternalLink,
        },
        types: {
          fullWidthImage: ({ node, children }) => (
            <client-only>
              <SanityImageCaption image={node} />
            </client-only>
          ),
          videoEmbed: ({ node, children }) => (
            <client-only>
              <VideoEmbed node={node} />
            </client-only>
          ),
        },
      },
      contents: {},
      shareList: ['facebook', 'twitter', 'linkedin'],
    }
  }, 
}

sanityImageCaption components

<template>
  <figure>
    <img
      :key="image._key"
      v-lazy-load
      :src="imageUrl"
      :alt="image.alt"
      class="object-center object-cover h-full w-full"
    />

    <figcaption>{{ image.caption }}</figcaption>
  </figure>
</template>

<script>
import imageUrlBuilder from '@sanity/image-url'
import sanityClient from '~/sanityClient'
const builder = imageUrlBuilder(sanityClient)
export default {
  props: {
    image: {
      type: Object,
      required: true,
    },
    auto: {
      default: 'format',
      type: String,
    },
    fit: {
      default: 'max',
      type: String,
    },
  },
  computed: {
    imageUrl() {
      return builder.image(this.image).auto(this.auto).fit(this.fit)
    },
  },
}
</script>

error message from nuxt generate

 WARN  Cannot stringify a function renderWithStyleInjection                                                                                                                                                                                                         11:42:31


 WARN  Cannot stringify a function hook                                                                                                                                                                                                                             11:42:31


 WARN  Cannot stringify a function hook                                                                                                                                                                                                                             11:42:31


 WARN  Cannot stringify a function fullWidthImage                                                                                                                                                                                                                   11:42:31


 WARN  Cannot stringify a function videoEmbed

ERROR   /news/contact-us                                                                                                                                                                                                                                           11:42:35

TypeError: Cannot read property '_key' of undefined
    at /Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/@sanity/block-content-to-hyperscript/lib/generateKeys.js:7:15
    at Array.map (<anonymous>)
    at module.exports (/Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/@sanity/block-content-to-hyperscript/lib/generateKeys.js:6:17)
    at blocksToNodes (/Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/@sanity/block-content-to-hyperscript/lib/blocksToNodes.js:27:21)
    at blocksToNodes (/Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/@sanity/block-content-to-hyperscript/lib/internals.js:14:14)
    at blocksToVue (/Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/sanity-blocks-vue-component/lib/blocksToVue.js:51:10)
    at render (/Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/sanity-blocks-vue-component/lib/BlockContent.js:46:12)
    at /Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/vue/dist/vue.runtime.common.prod.js:6:22507
    at Ie (/Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/vue/dist/vue.runtime.common.prod.js:6:22689)
    at /Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/vue/dist/vue.runtime.common.prod.js:6:23819
    at Le (/Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/vue/dist/vue.runtime.common.prod.js:6:24170)
    at e._c (/Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/vue/dist/vue.runtime.common.prod.js:6:32380)
    at a.render (pages/news/_slug.vue?d307:1:0)
    at a.t._render (/Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/vue/dist/vue.runtime.common.prod.js:6:35273)
    at /Users/Baydiwo/Projects/dark-horse-nuxtjs/node_modules/vue-server-renderer/build.prod.js:1:70637
    at runMicrotasks (<anonymous>)

I've tried using client-only but it seems not solving the problem

Getting additional metadata image component?

I have photos in PortableText in Sanity. I have created a component that handles lazy load and creates a placeholder that is displayed while the image loads.

To make the placeholder have the same size as the image, I need metadata from the image. I can do this if I create my own image field. But when the image is inserted into PortableText and via the sanity-blocks-vue component, I do not get sent with parameters so I can get this metadata.

Is it possible to get the sanity-blocks-vue component to retrieve metadata about the image? If so, how do I do it?

Issue with keying of blocks (?)

Hi,

and thanks for making and sharing this! It works brilliantly in most cases, however I have the following scenario where it does not (a bit hard to explain but I'll try):

I have a page with two languages, and that I use the same component for. I use
<block-content :blocks="blocks" :serializers="serializers" /> to render the content on this page. I use watch $route to catch language change and to update the block variable, so the page component does not get recreated, only the data updated. I also do some computed styles based on the content of the blocks. The number of blocks in the two languages is not the same, e.g.: { en: {blocks: Array[6]}, nb: {blocks: Array[8]} }

So what happens when I change language is that all the content in the various blocks get updated correctly, but the computed styling gets mixed up and apparently sticks to the order the element had in the previous language. So if the third block element was pink in the first instance, it will stay pink in also when changing languages, even though it should be red.

My hunch is that this is related to the way the blocks are keyed, and I see you use index in some cases, to that might be the sinner? I'd be willing to help out with anything if I can, just let me know.

Thanks again!

Using custom image serializer gives a type error

I'm creating a blog for myself using Sanity and Vue with TypeScript. Since I encountered some bumps on the road regarding this library, I'd like to let you know and maybe there could be something to improve. I can make a pull request later too if it's necessary. So here's the beef.

I'm not sure if the default block image serializer is supposed to work? Because without making a custom image serializer (as you will see below) the default serializer simply detected that there's an image among my blocks array and emitted an empty image tag into the DOM without any props or content.

But since I did make a custom serializer, the types of sanity-blocks-vue-component doesn't seem to like it very much. See the following simple example:

<template>
  <div>
    <div v-if="post" class="content">
      <h1>{{ post.title }}</h1>
      <SanityBlocks :blocks="blocks" :serializers="serializers" />
    </div>
  </div>
</template>

<script setup lang="ts">
import imageUrlBuilder from "@sanity/image-url";
import { SanityBlocks } from "sanity-blocks-vue-component";
import { defineComponent, h, ref } from "vue";
import type { Ref } from "vue";
import { useRoute } from "vue-router";

import sanity from "../client";

const imageBuilder = imageUrlBuilder(sanity);

const loading = ref(false);
const post: Ref<Record<string, any> | null> = ref(null);

const query = `*[slug.current == $slug] {
  _id,
  title,
  slug,
  body
}[0]
`;

function imageUrlFor(source: any) {
  return imageBuilder.image(source);
}

const serializers = {
  types: {
    image: defineComponent({
      props: ["asset"],
      setup(props) {
        return () =>
          h("img", {
            src: imageUrlFor(props.asset).width(480).url(),
          });
      },
    }),
  },
};


function fetchData() {
  const route = useRoute();
  loading.value = true;
  sanity.fetch(query, { slug: route.params.slug }).then(
    (data) => {
      loading.value = false;
      post.value = data;
    },
    (err) => {
      console.error(error);
      loading.value = false;
    }
  );
}

fetchData();
</script>

The issue comes from the SanityBlocks component's serializer prop. As I pass it my custom serializer it does work, but it gives the following type error:

Type '{ types: { image: DefineComponent<Readonly<{ asset?: any; }>, () => VNode<RendererNode, RendererElement, { [key: string]: any; }>, unknown, {}, {}, ComponentOptionsMixin, ... 5 more ..., { ...; }>; }; }' is not assignable to type 'Partial<Serializers>'.
  Types of property 'types' are incompatible.ts(2322)
types.d.ts(43, 9): 'block' is declared here.
index.d.ts(19, 5): The expected type comes from property 'serializers' which is declared here on type 'IntrinsicAttributes & Partial<{ blocks: Block[]; serializers: Partial<Serializers>; }> & Omit<Readonly<{ blocks: Block[]; serializers: Partial<...>; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, "blocks" | "serializers">'

I can supress this by setting the const serializer = { ... } as any, but it's not really a long term solution.

Allow custom serializer for "block" type

One major feature from other Portable Text tools is the ability to pass a custom serializer for block types. The default block serializer works "out of the box" only if the styles correspond to certain HTML elements, which doesn't lend itself to more abstract definitions e.g. headline instead of h1.

This is obviously possible through slots! Though perhaps an example could be added to the readme.

Provide an example for marks serializer

I think that documentation regarding marks needs to be updated or an example of the correct marks serializer usage should be provided. Right now it's not clear how exactly you should define and access marks props. It's really hard to find a correct answer anywhere in the web.

Current readme.md section:

When using custom Vue components as mark serializers, all properties of the block object will be passed via v-bind. To access the data, define the corresponding props in your component. You can use slots (e.g. this.$slots.default) to access the mark text or content.

Mismatching childNodes vs. VNodes

Hi Rupert,

This is probably a dumb question... sorry! I’ve been really wanting to use this for a while but on a couple of projects now I just get stumped at the classic mismatching nodes warning...

Mismatching childNodes vs. VNodes:  NodeList(11) [comment, text, div.header, text, blockcontent, text, div.section-images.margin-bottom-double.clickable, text, div.section-text, text, comment] (11) [VNode, VNode, VNode, VNode, VNode, VNode, VNode, VNode, VNode, VNode, VNode]

vue.common.dev.js?4650:630 [Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render.

I’m sure I must be, but don’t think I’m doing anything untoward – just a standard async fetch where I check if my Vuex store is already populated, and if not, dispatch an API call from an action in the store.

  async fetch ({ store, params }) {
    if (store.state.homepage === '') {
      await store.dispatch('HOMEPAGE_CALL');
    }
  },

In my pages I am just doing the following:

<div 
    v-if="homepage" 
    v-for="(section, index) in homepage.sections" 
    :key="section._id" 
    :id="makeId(section.title)" 
    class="section">
[...]
    <BlockContent 
        v-if="section.text" 
        :blocks="section.text" 
        className="section-text" 
        :renderContainerOnSingleChild="true" />
[...]
</div>

Sections is simply returned as a computed property:

homepage() {
    return this.$store.state.homepage
}

The issue stops if I comment out the BlockContent component, so I really don’t think I’m doing anything strange here – but would hugely appreciate any thoughts!

Block container option does not work

Hi there... I can't seem to get this feature to work. has this been removed since v3 released? I am using serializer to apply custom components to custom block types. These are all wrapped in a section tag. But I also want the Generic block text to be wrapped in sections too. How do I do this?

const serializers = {
    container: 'section',
    types: {
      gallery:Gallery,
      annotatedVisual:AnnotatedVisual,
      testimonial:Testimonial,
      videos:Videos,
    },
  }

Originally posted by @toddpadwick in #3 (comment)

[Question] How do you handle marks with empty text?

Hi!
By accident and without realizing it, one of my client added a mark (internal link) to an empty space, resulting in a empty and, in my case, invisible <a href="/about"></a> on the front-end. However it is still focusable when tabbing.

Ideally, I don't want that link to be rendered at all. I was able to solve the problem with functional template components by checking for children[0] && children[0].text. Example:

<template functional>
  <nuxt-link
    v-if="props.slug && children[0] && children[0].text"
    :to="props.slug + '/'"
    class="underline"
  >
    <slot />
  </nuxt-link>
</template>

However, for normal vue components, I only found convoluted ways and not clean and easy ways to check for empty slot. From what I understood "this.$slots.default[0]" is "false" at first and then "true", but it can't be track in a computed property because it's not reactive.

I was thinking, maybe we could add an option to this library for automatically hiding marks with empty text property?

'Catch All' type

Is it possible to define a 'catch all' serializer?

Use case is to have a 'Fallback' vue component that does not break the build but simply renders a warning explaining the serializer hasn't been set

Full example based on v1.0

I realize I might just be braindead right now but I just can't figure out how to pass the block array I get from sanity into

...
setup() {
    const blocks = [...];
...

Total vue noob, just figured I'd give it a try coming from react/gatsby so I'm most likely overlooking something obvious here. I've tried looking through all the vue/gridsome + sanity starters and repos I could find but they all use v0.1.0 of sanity-blocks-vue-component which seems to handle things a little differently.

Basically I'd just like to see a complete example or an updated starter if anyone has the time and patience for a vue noob ;)

Global configration for serializers

I'm thinking of having a global configration regarding serializers in Nuxt.js projects.

As we are going to use the block component on so many pages I would like to make the component globally available so we don't have to import it in hundreds of pages.

In addition I would like to define "global" serializers which should be available everywhere and we wouldn't need to add a serializers object in all of these pages.

Do you have an idea or suggestion how I could achieve that?

list-items each being rendered as individual lists, as opposed as part of the same list

Hi there,

The issue i'm having is exactly the same as this one from block-content-to-react, so I'll just quote it.

sanity-io/block-content-to-react#23

I am using block-content-to-react for some basic rich text. Somewhere in the pipeline it is turning what should be:

<ol>
   <li>Alpha<li>
   <li>Bravo<li>
   <li>Charlie<li>
</ol>

Into

<ol>
   <li>Alpha<li>
</ol>
<ol>
   <li>Bravo<li>
</ol>
<ol>
   <li>Charlie<li>
</ol>

Which is affecting the numberd list and intended spacing for the elements.

Here is what I have in sanity and what I expect:
In Sanity

...but here in staging it breaks what should be a list into multiple lists:
In Prod

Any help would be much appreciated, thank you!

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.