2.16.6 • Published 8 months ago

frontend-checkout v2.16.6

Weekly downloads
57
License
UNLICENSED
Repository
-
Last release
8 months ago

frontend-checkout hooks

Frontend checkout hooks that make it easy to fetch products, adding/removing products to cart and retrieving checkout url.

Installing and importing

frontend-checkout is already preinstalled on every Shogun Frontend site. To use it, you only need to import hooks in a component.

import { useCartState, useCartActions } from 'frontend-checkout'

API reference

  • useCartState hook - current state of user's cart.
const cart = useCartState()

// Cart
{
 id: string // ID of current cart.
 items: Items[] // Array of items currently in cart.
 inventory: {
   products: Record<productId, {
     availableForSale: boolean  // Indicates should you allow purchasing of a product, e.g. out of stock.
     quantity: number // The available quantity of a given product, if allowed on store.
     minOrder?: number // Minimum order constraint, adjustable in Shogun CMS - Product Content Group.
     maxOrder?: number // Maximum order constraint.
   }
   productVariants: Record<variantId, {
     availableForSale: boolean
     quantity: number
     minOrder?: number
     maxOrder?: number
   }
   status: 'loading' | 'loaded' | 'error' // Status of loading products from CMS
 }
 subtotalPrice: number // Total price of all items in cart, before shipping, taxes, and discounts.
 currencyCode: string // Cart currency code, e.g. "USD".
 isCartShown: boolean // Flag for managing should cart modal or drawer be visible.
 checkoutUrl: string // Url to redirect users when they press `Checkout` link/button.
}
  • useCartActions hook - actions available for manipulating carts.
/**
 * Fetch single product by ID.
 * fetchProduct: (id: string) => Promise<Product>
 */
const { fetchProduct } = useCartActions()

// Shopify Storefront API (GraphQL)
const id = 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' // product id or product handle can be used 'product-handle'

// Shopify AJAX API (REST)
const id = 'product-handle'

// BigCommerce STENCIL API doesn't support fetchProduct method
const product = await fetchProduct(id)

/**
 * Add items to cart.
 * addItems: (items: Item | Item[]) => Promise<Cart>
 */
const { addItems } = useCartActions()

/**
 * Item: {
 *  id: string | number
 *  quantity: number
 *  [key: string]?: any
 */

// Shopify Storefront API (GraphQL)
const item = {
  id: 'Z2lk1i8vc2hvcGlmeS7Qcm9kdWN0LzUwMAk2NjU4MTg3MjM=', // variant id, in Shogun Frontend CMS that is `product.variant.storefrontId`
  quantity: 1,
  // optionally, you can add any custom properities:
  // customAttributes: [{key: 'MyKey', value: 'MyValue'}]
}

// Shopify AJAX API (REST)
const item = {
  id: 7009655818753, // variant id, in Shogun Frontend CMS that is `product.variant.externalId`
  quantity: 1,
  // optionally, you can add any custom properties
  // properties: {key: 'value'}
}

// BigCommerce STENCIL API (REST)
const item = {
  id: 124810, // product id, in Shogun Frontend CMS that is `product.id`
  quantity: 1,
  // optionSelections required if a product has options
  optionSelections: [{ optionId: 11, optionValue: 117 }],
}

addItems(item) // to add multiple items `addItems([item, item2])`

/**
 * Update items in cart.
 * updateItems: (items: Item | Item[]) => Promise<Cart>
 */
const { updateItems } = useCartActions()

// Shopify Storefront API (GraphQL)
const { items } = useCartState()
const item = {
  id: items[0].id, // id of item in cart you want to update
  quantity: 2, // change quantity to 2
}

// Shopify AJAX API (REST)
const { items } = useCartState()
const item = {
  id: items[0].variant_id, // variant_id of item in cart you want to update.
  // Note, if you add 'variant_id' that is not in the cart then a new item will be added to cart.
  // If there are multiple items in cart with the same 'variant_id' only first will be updated.
  quantity: 2, // change quantity to 2 (quantity won't be adjusted to maximum items available in a stock)
}

// If there are multiple items in cart with same 'variant_id' and you need to update/change one that is not first, instead of 'id' you should provide 'line' property, which is 1-based index position of the item in the cart.
const item = {
  line: 2, // update second item in cart
  quantity: 2, // change quantity to 2 (quantity will be adjusted to maximum items in a stock)
}

// BigCommerce STENCIL API (REST)
const { items } = useCartState()
const item = {
  id: items[0].id, // id of item in cart you want to update
  lineItemId: items[0].lineItemId, // lineItemId of item in cart you want to update
  quantity: 2, // change quantity to 2
  // optionSelections required if a product has options
  optionSelections: items[0].optionSelections, // optionSelections can be also updated
}

updateItems(item) // to update multiple items `updateItems([item, item2])`

/**
 * Remove items from cart.
 * removeItems: (itemIds: string | string[]) => Promise<Cart>
 */
const { removeItems } = useCartActions()

// Shopify Storefront API (GraphQL)
const { items } = useCartState()
const itemId = items[0].id // remove first item in cart

// Shopify AJAX API (REST)
const { items } = useCartState()
const itemId = items[0].variant_id // remove first item in cart
// Note, if there are multiple items with the same 'variant_id', only first will be removed.

// BigCommerce STENCIL API (REST)
const { items } = useCartState()
const itemId = items[0].lineItemId // remove first item in cart

removeItems(itemId) // to remove multiple items `removeItems([item, item2])`

/**
 * Show cart.
 * showCart: () => void
 */
const { showCart } = useCartActions()
showCart() // 'isCartShown' will become true.

/**
 * Hide cart.
 * hideCart: () => void
 */
const { hideCart } = useCartActions()
hideCart() // 'isCartShown' will become false.

/**
 * ProductType describes the available product types.
 *
 * It is used when specifying where to search in the inventory (products or variants).
 *
 * In Shopify Product and ProductVariant are used but ProductVariant is used more frequently.
 *
 * In BigCommerce, only Product is used.
 *
 * Helpers that access the inventory will default to the ProductType that is most suitable for
 * the platform. (ProductVariant for Shopify and Product for BigCommerce)
 */
type ProductType = 'Product' | 'ProductVariant'

const defaultProductType = {
  shopify: 'ProductVariant',
  bigCommerce: 'Product',
}[platform]

/**
 * Get product is in inventory.
 * isProductInInventory: ({ id: ItemId, type: ProductType = defaultProductType }) => boolean
 */
const { isProductInInventory } = useCartActions()

// Shopify Storefront API (GraphQL)
const id = 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' // storefrontId

// Shopify AJAX API (REST)
const id = 6690750136476 // externalId

// BigCommerce STENCIL API
const product = 124810

const isInInventory = isProductInInventory({ id })

/**
 * Get item is available for sale from inventory.
 * isProductAvailableForSale: ({ id: ItemId, type: ProductType = defaultProductType  }) => boolean
 */
const { isProductAvailableForSale } = useCartActions()

// Shopify Storefront API (GraphQL)
const id = 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' // storefrontId

// Shopify AJAX API (REST)
const id = 6690750136476 // externalId

// BigCommerce STENCIL API
const product = 124810

const isItemAvailableForSale = isProductAvailableForSale({ id })

/**
 * Get item quantity from inventory.
 * getProductQuantity: ({ id: ItemId, type: ProductType = defaultProductType }) => number
 */
const { getProductQuantity } = useCartActions()

// Shopify Storefront API (GraphQL)
const id = 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' // storefrontId

// Shopify AJAX API (REST)
const id = 6690750136476 // externalId

// BigCommerce STENCIL API
const product = 124810

const quantity = getProductQuantity({ id })

/**
 * Get item min order from inventory.
 * getProductMinOrder: ({ id: ItemId, type: ProductType = defaultProductType }) => number | undefined
 */
const { getProductMinOrder } = useCartActions()

// Shopify Storefront API (GraphQL)
const id = 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' // storefrontId

// Shopify AJAX API (REST)
const id = 6690750136476 // externalId

// BigCommerce STENCIL API
const product = 124810

const itemMinOrder = getProductMinOrder({ id })

/**
 * Get item max order from inventory.
 * getProductMaxOrder: ({ id: ItemId, type: ProductType = defaultProductType }) => number | undefined
 */
const { getProductMaxOrder } = useCartActions()

// Shopify Storefront API (GraphQL)
const id = 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' // storefrontId

// Shopify AJAX API (REST)
const id = 6690750136476 // externalId

// BigCommerce STENCIL API doesn't support fetchProduct method
const product = 124810

const itemMaxOrder = getProductMaxOrder({ id })

Examples

a) Creating add to cart button component.

import React from 'react'
import { useCartState, useCartActions } from 'frontend-checkout'
import LoadingSpinner from 'Components/LoadingSpinner'

import './styles.css'

// Button states
const IDLE = 'idle'
const LOADING = 'loading'
const SOLD_OUT = 'sold out'
const ERROR = 'error'

// Error message duration
const THREE_SECONDS = 3 * 1000

const AddToCart = ({ id, children }) => {
  const [buttonState, setButtonState] = React.useState(IDLE)

  const { inventory } = useCartState()
  const { addItems, isProductAvailableForSale } = useCartActions()

  React.useEffect(
    function setSoldOutStateIfItemIsNotAvailable() {
      if (inventory.status === LOADING || inventory.status === ERROR) return

      const availableForSale = isProductAvailableForSale({
        id: variantId,
        type: 'ProductVariant',
      })

      if (!availableForSale) setButtonState(SOLD_OUT)
    },
    [inventory.status],
  )

  function clearError() {
    setButtonState(IDLE)
  }

  async function handleAddItemToCart() {
    setButtonState(LOADING)
    try {
      await addItems({ id, quantity: 1 })
      setButtonState(IDLE)
    } catch (e) {
      setButtonState(ERROR)
      setTimeout(clearError, THREE_SECONDS) // Remove error message after 3 seconds.
    }
  }

  if (buttonState === LOADING)
    return (
      <button className="AddToCart">
        <LoadingSpinner />
      </button>
    )

  if (buttonState === SOLD_OUT)
    return (
      <button className="AddToCart AddToCart--sold" disabled>
        Sold out
      </button>
    )

  if (buttonState === ERROR)
    return (
      <button className="AddToCart" onClick={clearError}>
        Adding failed
      </button>
    )

  return (
    <button className="AddToCart" onClick={handleAddItemToCart}>
      {children}
    </button>
  )
}

export default AddToCart

b) Creating checkout link/button component

import React from 'react'
import { useCartState } from 'frontend-checkout'

import './styles.css'

const CheckoutLink = ({ children }) => {
  const { checkoutUrl } = useCartState()

  return (
    <a className="CheckoutLink" href={checkoutUrl}>
      {children}
    </a>
  )
}

export default CheckoutLink

c) Creating component for changing item quantity

import React from 'react'
import { useCartActions } from 'frontend-checkout'

import './styles.css'

const IDLE = 'idle'
const LOADING = 'loading'

const ItemQuantity = ({ id, initialQuantity = 0 }) => {
  const [inputStatus, setInputStatus] = React.useState(IDLE)
  const { updateItems, removeItems } = useCartActions()

  async function handleItemQuantityChange(requestedQtyChange) {
    const newQuantity = quantity + requestedQtyChange

    if (newQuantity === quantity) return

    setInputStatus(LOADING)
    try {
      if (newQuantity === 0) {
        await removeItems(id)
      } else {
        await updateItems({ id, quantity: newQuantity })
        setInputStatus(IDLE)
      }
    } catch (e) {
      setInputValue(quantity)
      setInputStatus(IDLE)
      // Show error message
    }
  }

  return (
    <React.Fragment>
      <button
        className="ItemQuantityInput-btn--minus"
        disabled={inputStatus === LOADING}
        onClick={() => handleItemQuantityChange(-1)}
      >
        Reduce item quantity by one
      </button>
      <label htmlFor="itemQty">Item quantity</label>
      <input
        id="itemQty"
        className="ItemQuantityInput-input"
        type="number"
        min={0}
        disabled={inputStatus === LOADING}
        onBlur={(e) =>
          handleItemQuantityChange(
            Number(e.target.value) - quantity /* difference between new and current quantity */,
          )
        }
      />
      <button
        className="ItemQuantityInput-btn--plus"
        disabled={inputStatus === LOADING}
        onClick={() => handleItemQuantityChange(1)}
      >
        Increase item quantity by one
      </button>
    </React.Fragment>
  )
}

export default ItemQuantity

d) Changing variant in cart

// Components/CartPage
import React from 'react'
import { useCartState } from 'frontend-checkout'

import './styles.css'

const CartPage = () => {
  const { items } = useCartState()

  return (
    <CartContainer>
      {items.map((item) => (
        <CartItem key={item.id} item={item} />
      ))}
    </CartContainer>
  )
}

export default CartPage

// Components/CartItem
import React, { useEffect, useState } from 'react'
import { useCartActions } from 'frontend-checkout'

import './styles.css'

const CartItem = ({ item }) => {
  const { fetchProduct } = useCartActions()
  const [variants, setVariants] = useState([])

  // Shopify Storefront API (GraphQL)
  const productId = item.variant.product.id // product id must be used in GraphQL API (Variant and CheckoutLineItem id will not work)

  // Shopify AJAX API (REST)
  const productId = item.handle // product handle must be used in REST API

  useEffect(
    async function getProductVariants() {
      const product = await fetchProduct(productId)

      if (product == null) return

      const { variants } = product

      setVariants(variants)
    },
    [productId, fetchProduct, setVariants],
  )

  return (
    <CartItemContainer>
      <CartItemTitle item={item} />
      <ItemQuantity item={item} />
      <VariantsDropdown itemId={item.id} quantity={item.quantity} variants={variants} />
    </CartItemContainer>
  )
}

export default CartItem

// Components/VariantsDropdown
import React, { useEffect, useState } from 'react'
import { useCartActions } from 'frontend-checkout'

import './styles.css'

const VariantsDropdown = ({ itemId, quantity, variants }) => {
  const { addItems, removeItems } = useCartActions()

  async function handleVariantChange(variant) {
    // we can't update existing item in cart
    // we need to remove and then add new variant
    await removeItems(itemId)

    await addItems({ id: variant.id, quantity })
  }

  return (
    <VariantsDropdownContainer>
      {variants.map((variant) => (
        <VariantsDropdownItem variant={variant} onSelect={handleVariantChange} />
      ))}
    </VariantsDropdownContainer>
  )
}

export default VariantsDropdown
2.16.6

8 months ago

3.10.4-ca

8 months ago

3.10.4

8 months ago

3.10.4-gb

8 months ago

3.10.2-au

1 year ago

3.10.2-rc1

1 year ago

3.10.2-ca

1 year ago

2.16.3-rc1

1 year ago

3.10.2-uk

1 year ago

3.10.3

1 year ago

3.10.2

1 year ago

2.16.5

1 year ago

2.16.4

1 year ago

3.10.2-GB

1 year ago

2.15.4-alpha1

1 year ago

3.10.1

1 year ago

3.10.0

1 year ago

2.16.2-alpha3

1 year ago

2.16.2-alpha2

1 year ago

2.16.3

1 year ago

2.16.2

1 year ago

3.10.1-mclaren

1 year ago

3.10.1-rc3

1 year ago

3.10.1-rc2

1 year ago

3.9.0-alpha1

1 year ago

3.9.0-rc.0

1 year ago

3.9.0-rc.1

1 year ago

3.9.0-rc.4

1 year ago

3.8.2-rc.1

1 year ago

3.7.4-alpha1

2 years ago

3.8.2-rc.2

1 year ago

3.8.2-rc.3

1 year ago

3.8.2-rc.0

1 year ago

3.9.0

1 year ago

3.8.1-alpha9

1 year ago

3.8.0

1 year ago

3.8.1-alpha1

1 year ago

3.8.1-alpha2

1 year ago

3.8.1-alpha3

1 year ago

3.8.1-alpha4

1 year ago

3.8.1-alpha5

1 year ago

3.8.1-alpha6

1 year ago

3.8.1-alpha7

1 year ago

3.8.1-alpha8

1 year ago

3.8.3

1 year ago

3.8.2

1 year ago

3.8.1

1 year ago

2.16.1-rc1

1 year ago

3.7.1

2 years ago

2.16.1

1 year ago

2.16.0

1 year ago

3.8.4-rc1

1 year ago

3.7.5

2 years ago

3.7.4

2 years ago

3.7.3

2 years ago

3.7.2

2 years ago

2.15.3

2 years ago

3.7.5-alpha1

1 year ago

3.7.5-alpha2

1 year ago

3.9.0-rc1

1 year ago

3.9.0-rc3

1 year ago

3.9.0-rc2

1 year ago

3.10.0-alpha2

1 year ago

3.10.0-alpha1

1 year ago

3.8.0-rc2

1 year ago

3.10.0-alpha3

1 year ago

3.8.0-rc1

1 year ago

3.8.0-rc4

1 year ago

3.8.0-rc3

1 year ago

2.16.0-rc1

1 year ago

3.10.0-rc1

1 year ago

3.7.3-alpha1

2 years ago

3.8.3-rc1

1 year ago

3.10.1-rc1

1 year ago

3.6.0-alpha1

2 years ago

3.7.0

2 years ago

2.13.9-alpha-1

2 years ago

3.4.0-alpha2

2 years ago

3.4.0-alpha1

2 years ago

3.6.0

2 years ago

2.15.2

2 years ago

2.15.0

2 years ago

2.15.1

2 years ago

3.5.0

2 years ago

2.14.0

2 years ago

3.4.0-alpha-1

2 years ago

3.4.0-alpha-2

2 years ago

3.4.0

2 years ago

3.5.0-alpha1

2 years ago

3.5.0-alpha2

2 years ago

2.13.8

2 years ago

3.4.1

2 years ago

3.4.1-alpha1

2 years ago

3.4.1-alpha2

2 years ago

3.4.0-ovo-1

2 years ago

3.4.0-ovo-2

2 years ago

3.4.0-ovo-3

2 years ago

3.3.6

2 years ago

3.3.6-alpha20

2 years ago

3.3.6-alpha10

2 years ago

3.3.7-alpha.10

2 years ago

3.3.7-alpha.13

2 years ago

3.3.7-alpha.14

2 years ago

3.3.7-alpha.11

2 years ago

3.3.7-alpha.12

2 years ago

3.3.7-alpha.8

2 years ago

3.3.7-alpha.9

2 years ago

3.3.7-alpha.4

2 years ago

3.3.7-alpha.5

2 years ago

3.3.7-alpha.6

2 years ago

3.3.7-alpha.7

2 years ago

3.3.7-alpha.1

2 years ago

3.3.7-alpha.2

2 years ago

3.3.7-alpha.3

2 years ago

3.3.3-alpha1

2 years ago

2.13.7-alpha-5

2 years ago

2.13.7-alpha-2

2 years ago

2.13.7-alpha-1

2 years ago

2.13.7-alpha-4

2 years ago

2.13.7-alpha-3

2 years ago

2.13.7-alpha-0

2 years ago

3.3.7-alpha.17

2 years ago

3.3.7-alpha.18

2 years ago

3.3.7-alpha.15

2 years ago

3.3.7-alpha.16

2 years ago

3.3.7-alpha.19

2 years ago

3.3.7-alpha.20

2 years ago

3.3.7-alpha.21

2 years ago

3.3.7-alpha.24

2 years ago

3.3.7-alpha.25

2 years ago

3.3.7-alpha.22

2 years ago

3.3.7-alpha.23

2 years ago

3.3.7-alpha.28

2 years ago

3.3.7-alpha.29

2 years ago

3.3.7-alpha.26

2 years ago

3.3.7-alpha.27

2 years ago

2.13.6

2 years ago

2.13.8-alpha-0

2 years ago

2.13.8-alpha-1

2 years ago

3.3.7-alpha.31

2 years ago

3.3.7-alpha.30

2 years ago

3.3.4-stft2237

2 years ago

3.3.5

2 years ago

3.3.4

2 years ago

3.3.3

2 years ago

3.3.2

2 years ago

3.3.6-alpha3

2 years ago

3.3.4-alpha3

2 years ago

3.3.6-alpha4

2 years ago

3.3.4-alpha4

2 years ago

3.3.6-alpha1

2 years ago

3.3.4-alpha5

2 years ago

3.3.6-alpha2

2 years ago

3.3.4-alpha6

2 years ago

3.3.4-alpha1

2 years ago

3.3.4-alpha2

2 years ago

3.3.4-alpha7

2 years ago

3.3.2-alpha-2

2 years ago

3.2.2

2 years ago

3.2.1

2 years ago

3.2.0

2 years ago

3.2.2-alpha1

2 years ago

3.2.2-alpha2

2 years ago

3.2.1-alpha2

2 years ago

3.2.1-alpha1

2 years ago

3.2.1-alpha4

2 years ago

3.2.1-alpha3

2 years ago

3.3.1

2 years ago

3.3.0

2 years ago

2.13.3-alpha-1

2 years ago

3.0.3-alpha2

2 years ago

3.0.3-alpha1

2 years ago

3.0.3-alpha3

2 years ago

3.0.3

2 years ago

3.0.0

2 years ago

3.0.1-alpha.5

2 years ago

3.0.1-alpha.2

2 years ago

3.0.1-alpha.1

2 years ago

3.0.1-alpha.4

2 years ago

3.0.1-alpha.3

2 years ago

3.0.1-alpha.0

2 years ago

3.0.0-rc.2

2 years ago

3.0.0-rc.1

2 years ago

3.0.0-rc.6

2 years ago

3.0.0-rc.5

2 years ago

3.0.0-rc.4

2 years ago

3.0.0-rc.3

2 years ago

3.0.0-rc.8

2 years ago

3.0.0-rc.7

2 years ago

3.0.0-rc-1

2 years ago

3.0.0-rc-0

2 years ago

3.0.0-rc-2

2 years ago

0.25.0

2 years ago

2.13.4

2 years ago

2.13.5

2 years ago

2.13.2

2 years ago

2.13.0

2 years ago

2.13.1

2 years ago

2.13.0-alpha-0

2 years ago

2.13.0-alpha-1

2 years ago

2.13.0-alpha-2

2 years ago

2.13.0-alpha-3

2 years ago

2.13.0-alpha-8

2 years ago

2.13.0-alpha-4

2 years ago

2.13.0-alpha-5

2 years ago

2.13.0-alpha-6

2 years ago

2.13.0-alpha-7

2 years ago

2.12.14-alpha-3

2 years ago

2.12.14-alpha-2

2 years ago

2.12.14-alpha-1

2 years ago

2.12.14

2 years ago

2.13.11-alpha-12

3 years ago

2.13.11-alpha-11

3 years ago

2.13.11-alpha-10

3 years ago

2.12.13-alpha-1

3 years ago

2.13.11-alpha-7

3 years ago

2.13.11-alpha-6

3 years ago

2.13.11-alpha-5

3 years ago

2.13.11-alpha-4

3 years ago

2.13.11-alpha-9

3 years ago

2.13.11-alpha-8

3 years ago

2.13.11-alpha-3

3 years ago

2.13.11-alpha-2

3 years ago

2.13.11-alpha-1

3 years ago

2.12.13

3 years ago

2.12.10-alpha-1

3 years ago

2.12.10-alpha-3

3 years ago

2.12.10-alpha-2

3 years ago

2.12.10-alpha-5

3 years ago

2.12.10-alpha-4

3 years ago

2.12.10-alpha-7

3 years ago

2.12.10-alpha-6

3 years ago

2.12.10-alpha-8

3 years ago

2.12.7-alpha-1

3 years ago

2.12.12-alpha-1

3 years ago

2.12.9

3 years ago

2.12.7

3 years ago

2.12.8

3 years ago

2.12.12

3 years ago

2.12.11

3 years ago

2.12.10

3 years ago

2.12.5-alpha-2

3 years ago

2.12.5-alpha-1

3 years ago

2.12.6-alpha-3

3 years ago

2.12.6-alpha-1

3 years ago

2.12.6-alpha-2

3 years ago

2.12.5

3 years ago

2.12.6

3 years ago

2.12.4

3 years ago

2.12.3-alpha-4

3 years ago

2.12.3-alpha-3

3 years ago

2.12.3-alpha-2

3 years ago

2.12.3-alpha-1

3 years ago

2.12.0

3 years ago

2.12.2-alpha-1

3 years ago

2.12.3

3 years ago

2.12.1

3 years ago

2.12.2

3 years ago

2.11.12-alpha-2

3 years ago

2.11.12-alpha-1

3 years ago

2.11.10

3 years ago

2.11.11

3 years ago

2.11.7-alpha-1

3 years ago

2.11.8

3 years ago

2.11.9

3 years ago

2.11.7

3 years ago

2.11.6

3 years ago

2.11.4

3 years ago

2.11.5

3 years ago

2.11.0

3 years ago

2.11.1

3 years ago

2.11.2

3 years ago

2.11.3

3 years ago

2.9.4

3 years ago

2.10.0

3 years ago

3.0.0-alpha-6

3 years ago

3.0.0-alpha-5

3 years ago

2.9.2

3 years ago

2.9.3

3 years ago

3.0.0-alpha-2

3 years ago

3.0.0-alpha-1

3 years ago

3.0.0-alpha-4

3 years ago

3.0.0-alpha-3

3 years ago

2.9.1

3 years ago

2.8.0-alpha-1

3 years ago

2.8.1

3 years ago

2.7.0

3 years ago

2.7.1

3 years ago

2.7.1-alpha-4

3 years ago

2.7.1-alpha-3

3 years ago

2.7.1-alpha-2

3 years ago

2.7.1-alpha-1

3 years ago

2.6.4

3 years ago

2.6.3

3 years ago

2.6.1-alpha1

3 years ago

2.6.1

3 years ago

2.6.2

3 years ago

2.7.0-alpha-1

3 years ago

2.5.7

3 years ago

2.6.0

3 years ago

2.5.5-alpha-1

3 years ago

2.5.6

3 years ago

2.5.5

3 years ago

2.5.4

3 years ago

2.5.3

3 years ago

2.5.2

3 years ago

2.5.1-alpha-1

3 years ago

2.5.1

3 years ago

2.5.0

3 years ago

2.4.1

3 years ago

2.5.0-alpha-2

3 years ago

2.5.0-alpha-1

3 years ago

2.4.0

3 years ago

2.3.4-alpha-10

3 years ago

2.3.4-alpha-11

3 years ago

2.3.4-alpha-8

3 years ago

2.3.4-alpha-9

3 years ago

2.3.4-alpha-3

3 years ago

2.3.4-alpha-4

3 years ago

2.3.4-alpha-5

3 years ago

2.3.4-alpha-6

3 years ago

2.3.3-alpha-1

3 years ago

2.3.3-alpha-3

3 years ago

2.3.3-alpha-2

3 years ago

2.3.2

3 years ago

2.3.4

3 years ago

2.3.3

3 years ago

2.3.4-alpha-1

3 years ago

2.3.4-alpha-2

3 years ago

2.2.1

3 years ago

2.2.0

3 years ago

2.2.2

3 years ago

2.2.0-alpha-6

3 years ago

2.1.0-alpha-5

3 years ago

2.1.0-alpha-4

3 years ago

2.1.0-alpha-3

3 years ago

2.1.0-alpha-2

3 years ago

2.1.0-alpha-1

3 years ago

2.1.1

3 years ago

2.0.2

3 years ago

2.0.1

3 years ago

2.0.0

3 years ago

2.1.0

3 years ago

1.4.0-alpha-1

3 years ago

1.3.0

3 years ago

1.2.2

3 years ago

1.2.1

3 years ago

1.2.0

3 years ago

1.1.3

3 years ago

1.3.0-alpha-1

3 years ago

1.3.0-alpha-2

3 years ago

2.0.0-alpha-2

3 years ago

2.0.0-alpha-1

3 years ago

1.1.1

3 years ago

1.0.2

3 years ago

1.1.0

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago

0.1.35

3 years ago

0.1.36

3 years ago

1.0.4

3 years ago

1.1.2

3 years ago

1.0.3

3 years ago

0.1.34

3 years ago

0.1.32

3 years ago

0.1.33

3 years ago

0.1.31

3 years ago

0.1.30

3 years ago

0.1.29

3 years ago

0.1.28

3 years ago

0.1.27

3 years ago

0.1.26

3 years ago

0.1.25

3 years ago

0.1.24

3 years ago

0.1.23

3 years ago

0.1.22

3 years ago

0.1.21

3 years ago

0.1.20

3 years ago

0.1.19

4 years ago

0.1.18

4 years ago

0.1.17

4 years ago

0.1.16

4 years ago

0.1.15

4 years ago

0.1.14

4 years ago

0.1.13

4 years ago

0.1.12

4 years ago

0.1.11

4 years ago

0.1.10

4 years ago

0.1.9

4 years ago

0.1.8

4 years ago

0.1.7

4 years ago

0.1.6

4 years ago

0.1.5

4 years ago

0.1.4

4 years ago

0.1.3

4 years ago

0.1.2

4 years ago