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

@best-apps/gfx-editor

v2.21.2

Published

GFX editor

Downloads

657

Readme

Best Apps GFX Editor

Use Best Apps' GFX Editor as a React component or in vanilla JS

Table of contents

Installation

npm install @best-apps/gfx-editor
yarn add @best-apps/gfx-editor

Usage in React

Release Flow:

  1. Your pull request will generate a new isolated Editor in vercel.
  2. Then, you should merge your changes to the staging branch and check the following: (gfx-editor-staging-best-apps.vercel.app)[gfx-editor-staging-best-apps.vercel.app]
  3. Now, once your changes are QAed and checked, merge your Pull request and it'll deploy your Pull Request.

TLDR

See demo in codesandbox

import { GFXInstanceProvider, GFXInstance } from "@best-apps/gfx-editor";

function App() {
  return (
    <GFXInstanceProvider>
      <div
        style={{
          width: 400,
          height: 600,
          margin: 20,
          border: "1px solid black",
          position: "relative", // You have to wrap the editor in an element with width/height and position: fixed/relative/absolute
        }}
      >
        <GFXInstance
          v1TemplateId={123} // The v1TemplateId for you product and design
        />
      </div>
    </GFXInstanceProvider>
  );
}

That will render the editor in an iframe and give you the full interface for interacting with your design/template/product

GFXInstanceProvider

Wherever you want to put the Editor you have to put it inside <GFXInstanceProvider />. This creates a React context so that every component nested within it has access to gfx with useGFXInstance().

GFXInstance

Use <GFXInstance {...props} /> wherever you want to render the Editor in an iframe.

  • First create a div with width, height, and position styles so that the editor can fill this container element.
  • Wrap this and whatever other components need access to gfx within a GFXInstanceProvider component.
  • Provide a react ref as gfxRef. It will get initialized with a gfx instance object.

useGFXInstance()

This hook will get gfx from context. Here is an example:

import { useGFXInstance } from "@best-app/gfx-editor";

function Button() {
  const gfx = useGFXInstance();
  return <button onClick={() => gfx?.actions.flipCanvas()}>Flip canvas</button>;
}

GFXInstanceContext

If you are attempting to get the parent gfx from context in a React class component, use GFXInstanceContext directly. Here is an example:

import { GFXInstanceContext } from "@best-app/gfx-editor";

class ButtonWrapper extends React.Component {
  static contextType = GFXInstanceContext;

  render() {
    const gfx = this.context;
    return (
      <button onClick={() => gfx?.actions.flipCanvas()}>Flip canvas</button>
    );
  }
}

GFXInstanceConsumer

You can also use GFXInstanceConsumer with a child render callback to access the parent gfx from context. Here is an example:

import { GFXInstanceConsumer } from "@best-app/gfx-editor";

function Button() {
  return (
    <GFXInstanceConsumer>
      {(gfx) => {
        <button onClick={() => gfx?.actions.flipCanvas()}>Flip canvas</button>;
      }}
    </GFXInstanceConsumer>
  );
}

useActiveObjectType()

NOTE: This is only useful if you are creating your own UI for the Editor.

When creating your own toolbars and buttons, you will often need to know exactly what TYPE OF object has been actively selected. This is where useActiveObjectType is useful.

This hook will return:

  • CustomImage
  • LikeCustomTextbox
  • Sticker
  • CustomizableTextSlot
  • CustomizableImageSlot
  • null

Here is an example:

function TopToolbar() {
  const activeObjectType = useActiveObjectType(gfx?.state);

  if (activeObjectType === "LikeCustomTextbox") {
    return (
      <button
        onClick={() =>
          gfx?.actions.openMenu({
            menuType: "strokeColorOnTextbox",
          })
        }
      >
        Open stroke color drawer
      </button>
    );
  }
}

Use as es6 module

Sort of like ReactDOM.render, you call this with a config object and an html element where we should insert the Editor.

import { embedGFX } from '@best-apps/gfx-editor';

const gfx = embedGFX({
  v1TemplateId: 1234
}, document.getElementById(#editor - container))

Once you have the gfx object, you can build a UI that incorporates gfx.actions and gfx.state

Use as script and window.embedGFX

It may be necessary to use our hosted script in some instances

  • Use in a Shopify Store

Using GFX Instance

GFX actions

You call these like gfx.actions.flipCanvas()

  • flipCanvas(): This flips the canvas (if there is a front and back to the design and product). Wall art, for example, will not flip.
  • rotateCanvas(): This will rotate the canvas (only when using wall art or poster printables)
  • openMenu: (activeMenu: ActiveMenuType) => Promise<void>. This opens a menu/drawer. ActiveMenuType is one of these:
    • openMenu({ menuType: 'sticker' }): to open the sticker drawer
    • openMenu({ menuType: 'printableColor' }): to open the color selection drawer to change the color on the garment or wall art (what we call "printables")
    • openMenu({ menuType: 'strokeColorOnTextbox' }): to open the color selection drawer to select the color for the stroke on text in a textbox
    • openMenu({ menuType: 'fillColorOnTextbox' }): to open the color selection drawer to select fill color on text in a textbox
    • openMenu({ menuType: 'fontFamilyOnTextbox' }): to open the sticker drawer
    • openMenu({ menuType: 'fillColorOnAllTextboxes' }): to open the color selection drawer to select fill color on ALL text in ALL textboxes and text slots
  • closeMenu: () => void: Closes the menu (any menu)
  • setFillColorOnTextbox: (color: string) => void: This will set the fill color on text in a textbox
  • setFillColorOnTextboxSlot: (slotId: number, color: string) => void: Set fill color on text in a text slot
  • setFillColorOnAllTextboxes: (color: string) => void: Sets the fill color on ALL text boxes
  • setFontFamilyOnTextbox: (...args: any) => void: Sets the font family on a textbox
  • setStrokeColorOnTextbox: (...args: any) => Promise<void>: Sets the stroke color on a given textbox
  • setImageOnImageSlot: (...args: any) => Promise<void>: Sets the image on an image slot
  • setPrintableColor: (color: string) => void: Sets the printable color to the specified color
  • nextColor: () => void: Toggles the color on the "printable"
  • updateDesignOnState: () => void: This just creates an export of the design and put is on gfx.state.design
  • debouncedUpdateDesignOnState: () => Promise<void>: A debounced version of the updateDesignOnState method
  • toggleZoom: () => void: Toggles the zoom on the editor canvas
  • addText: () => Promise<void>: Adds a customizable textbox to the canvas
  • removeBg: () => Promise<void>: Removes bg on the currently selected image
  • addImage: (urlOrBase64: string, addImageOptions?: AddImageOptions) => Promise<void>: Adds an image to the canvas
  • rotateActiveObject: (angle: number, animate?: boolean) => void: Rotates the active object
  • alignActiveObject: (position: AlignPosition, animate?: boolean) => void: Aligns the active object depending on the arguments you pass in:
    • AlignPosition: 'center' | 'centerHorizontally' | 'centerVertically' | 'alignToTop'
  • shareDesign: (args: { url: string; base64Image: string; title: string }) => void
  • showAlert: (alertOptions: AlertOptions) => void: Shows an alert you specify with alertOptions
    • title: string: The title of the alert
    • body?: string: The body of the alert
    • timeout?: number: How long before the alert is dismissed automatically
    • dismissable: boolean: Whether or not the alert can be dismissed
    • dismissableButtonLabel?: string: And what the dismiss button label is
  • hideAlert: () => void: Hides any active alert
  • getProofs: (quality?: number) => Promise<GFXProofs>: Gets proofs and sends back their base64 representation
  • saveDesign: () => Promise<void>: Saves the design to our db
  • handleImageUpload: (event: any) => Promise<void>: TBD

GFX State

These are properties on gfx state. You might access them like gfx.state.isLoading

  • isLoading: boolean
  • isAsyncLoading: boolean
  • isSyncing: boolean
  • cornerIcons: CornerIcons
  • canFlip: boolean
  • canRotate: boolean
  • canChangeColor: boolean
  • status: Status
  • statusCode: StatusCode
  • orientation: PrintableInfoOrientation
  • activeObject: GFXCanvasObjectType | null
  • isZooming: boolean
  • scale: number
  • centerPoints: GFXCenterPointsBySection
  • alert?: AlertOptions | null
  • activeMenu
  • design: V2Design
  • activeSection: SectionType
  • activeV2Printable: V2Printable
  • isSupportedBrowser: boolean
  • productId: number | string | null
  • designId: number | string | null
  • designNumber: string | null
  • v1TemplateId: number | string | null
  • initialData: GFXInitialDataType
  • windowWidth: number | null
  • windowHeight: number | null
  • stickers: number[]

GFX events

You call these like gfx.actions.flipCanvas()

  • onStateChange: (payload: GFXStateType) => void: your favorite probably, a way to listen for state changes
  • onColorChanged: (color: string) => void: when the printable/garment color is changed
  • onTextFillColorChanged: (color: string) => void: when text fill color is changed
  • onObjectRemoved: (obj: GFXCanvasObjectType) => void:
  • onObjectDoubleClicked: (obj: GFXCanvasObjectType) => void:
  • onSelectionCleared: (payload: {:
  • deselected: GFXCanvasObjectType[] | null:
  • selected: GFXCanvasObjectType[] | null:
  • }) => void:
  • onImageSelectedInSlot: (obj: GFXCanvasObjectType) => void:
  • onBeforeSelectionCleared: (payload: { deselected: GFXCanvasObjectType[] }) => void:
  • onObjectSelectionUpdated: (payload: {:
  • onImageIdUpdatedOnSlot: (payload: { slotId: number; imageId: number }) => void:
  • onUpdatedCustomSlot: (payload: any) => void:

Example usage:

var gfx = window.embedGFX(
  {
    v1TemplateId: 829, // Just an example v1TemplateId
    interfaceType: "full",
  },
  document.getElementById("gfx-product")
);

gfx.addEventListener("onStateChange", (state) => console.log(state));

Types

SavedDesign

  • designNumber: the uuid v4 string that represents the users custom design on this product or template
  • design: the users' custom design

Styling the editor

We use BEM style classnames throughout the editor UI, and you can provide a stylehseet as a text string in the gfxConfig.customOptions.css property in order to override our existing styles.

var gfx = window.embedGFX(
  {
    v1TemplateId: 829, // Just an example v1TemplateId
    interfaceType: "full",
    gfxConfig: {
      disableTOS: true, // to hide the watermark (only applicable on full interface)
      showWatermark: false, // to hide the watermark (only applicable on full interface)
      showInitialToast: true, // show the initial toast message by default
      customOptions: {
        css: `
        .AbstractDrawer {
          background: #fff !important;
          border-top: 1px solid #2b2c2d18;
          border-radius: 0;
        }
      `,
      },
    },
  },
  document.getElementById("gfx-product")
);

Integrations

Shopify

When integrating your Shopify store with gfx you must

  1. Add a property to line items: design_number
  2. Add an attribute to the order: app_flow: 'embedded'

To get the design_number, you must call gfx.actions.saveDesign() which will return a SavedDesign object

Here is an example of how you would do this with an ajax request with Jquery:

With AJAX

const savedDesign = await gfx.actions.saveDesign();

await $.post(
  "/cart/add.js",
  {
    quantity: parseInt(quantity.value),
    id: parseInt(window.currentVariant),
    attributes: {
      app_flow: "embedded",
    },
    properties: {
      design_number: savedDesign.designNumber,
    },
  },
  null,
  "json"
);

With liquid templates

This usually requires custom implementation support right now. Please contact your GFX account manager.

Basically, you must add the properties to your form:

<form method="post" action="/cart/add">
  <input type="hidden" name="properties[design_number]" value="DESIGN_NUMBER" />
  <input type="text" name="id" value="VARIANT_ID" />
  <input type="submit" value="submit" />
</form>

BigCommerce

Coming soon

FAQ

Coming soon.

Contributing

This is a private repo. If you are part of the Best Apps team, see CONTRIBUTING.md