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

@medusa-vue/core

v0.4.2

Published

Vue composables to interact with @medusa api

Downloads

9

Readme

Medusa Vue

Vue 3 composables and components for seamless and streamlined interaction with a Medusa.

If you're building a custom vue based storefront that consumes a medusa backend and find yourself wishing you had something nice at hands like medusa-react to streamline your data management - this might be your library!

Installation

The library uses @tanstack/vue-query under the hood.

For the core composables run:

npm install @medusa-vue/core
# or
yarn add @medusa-vue/core

For the components (WIP :construction_worker:):

npm install @medusa-vue/components
# or
yarn add @medusa-vue/components

Quick Start

In order to use the composables exposed by this library you need to register it as a plugin in your main file before mounting your app. The plugin takes a config object that needs at least a baseUrl where it can reach your server. Optionally, it allows you to pass additional props to configure both the underlying medusa-js and the vue-query client. Here's the complete interface. Refer to these and these docs, respectively to get an idea on how the parts work together.

interface MedusaVueClientProps {
  baseUrl: string;
  maxRetries?: number;
  /**
   * Authentication token
   */
  apiKey?: string;
  /**
   * PublishableApiKey identifier that defines the scope of resources
   * available within the request
   */
  publishableApiKey?: string;

  queryClientProviderProps?: VueQueryPluginOptions;
}

Plug it in:

// main.ts
import { createApp } from 'vue';
import App from './App.vue';

import { createMedusaVueClient } from '@medusa-vue/core';

const client = createMedusaVueClient({
  baseUrl: '<YOUR_SERVER_BASE_URL>',
});

const app = createApp(App);

app.use(client).mount('#app');

The hooks exposed by medusa-vue fall into two main categories: queries and mutations.

Queries

Queries simply wrap around vue-query's useQuery hook to fetch some data from your medusa server

// ./my-product-list.vue
<script setup lang="ts">
import { watch } from 'vue';
import HelloWorld from './components/HelloWorld.vue';
import { useProducts } from '@medusa-vue/core';

const { data, error, isLoading } = useProducts();
</script>

<template>
  <ul v-for="products in data?.products">
    <li>...</li>
  </ul>
</template>

Note: If you've worked with @medusajs/medusa-react you might be used to being able to destructure the recordset returned by the server, i.e. const { products } = useProducts(). This is however not possible with vue due to the way it's reactive system works.

Mutations

Mutations wrap around vue-query's useMutation to mutate data and perform server-side effects on your medusa server. If you are not entirely familiar with this idea of "mutations", creating a cart would be a mutation because it creates a cart in your server (and database). Mutations also have to be invoked imperatively, meaning that calling for the mutation to take action, you will have to call a mutate() function returned from mutation hooks.

<script setup lang="ts">
import { useCreateCart } from '@medusa-vue/core';

const createCart = useCreateCart();
const handleClick = () => {
  createCart.mutate({}); // create an empty cart
};
</script>

<template>
  <Button isLoading="{createCart.isLoading}" onClick="{handleClick}">
    Create cart
  </Button>
</template>

The mutation hooks will return exactly what vue-query's useMutation returns. In addition, the options you pass in to the hooks will be passed along to useMutation.

Components

NOTE: This is still work in progress and new components will gradually be added!:construction_worker:

If you prefer declarative templates, @medusa-vue/components provided (almost) renderless components to use directly in your template and provide data through slot-props. This allows for extremely streamlinend and declarative templating:

<script setup lang="ts">
import { UseProducts } from '@medusa-vue/components';
</script>

<template>
  <use-products v-slot="{ data, isLoading }">
    <loading-spinner v-if="isLoading" />

    <product-list :products="data.products" />
  </use-products>
</template>

The component also allows to pass down the laoding indicating component via a slot:

<script setup lang="ts">
import { UseProducts } from '@medusa-vue/components';
</script>

<template>
  <use-products>
    <template #fallback>
      <div>Loading....</div>
    </template>

    <template v-slot="{ data, isLoading }">
      <product-list :products="data.products" />
    </template>
  </use-products>
</template>

Utilities

A set of utility functions are also exposed from the library to make your life easier when dealing with displaying money amounts

formatVariantPrice()

  • formatVariantPrice(params: FormatVariantPriceParams): string
type FormatVariantPriceParams = {
  variant: ProductVariantInfo;
  region: RegionInfo;
  includeTaxes?: boolean;
  minimumFractionDigits?: number;
  maximumFractionDigits?: number;
  locale?: string;
};

type ProductVariantInfo = Pick<ProductVariant, 'prices'>;

type RegionInfo = {
  currency_code: string;
  tax_code: string;
  tax_rate: number;
};

Given a variant and region, will return a string representing the localized amount (i.e: $19.50)

The behavior of minimumFractionDigits and maximumFractionDigits is the same as the one explained by MDN here. In fact, in order to convert the decimal amount, we use the browser's Intl.NumberFormat method.

computeVariantPrice()

  • computeVariantPrice(params: ComputeVariantPriceParams): number
type ComputeVariantPriceParams = {
  variant: ProductVariantInfo;
  region: RegionInfo;
  includeTaxes?: boolean;
};

Determines a variant's price based on the region provided. Returns a decimal number representing the amount.

formatAmount()

  • formatAmount(params: FormatAmountParams): string
type FormatAmountParams = {
  amount: number;
  region: RegionInfo;
  includeTaxes?: boolean;
  minimumFractionDigits?: number;
  maximumFractionDigits?: number;
  locale?: string;
};

Returns a localized string based on the input params representing the amount (i.e: "$10.99").

computeAmount()

  • computeAmount(params: ComputeAmountParams): number
type ComputeAmountParams = {
  amount: number;
  region: RegionInfo;
  includeTaxes?: boolean;
};

Takes an integer amount, a region, and includeTaxes boolean. Returns a decimal amount including (or excluding) taxes.

Credits

Based on and inspired by medusa-react. Keep up the good work! :beers: