@spiffcommerce/core
Version:
Core client API for interacting with the Spiff Commerce backend.
1,351 lines (1,334 loc) • 231 kB
TypeScript
import { FunctionComponent, ReactNode } from 'preact/compat';
import * as _apollo_client_core from '@apollo/client/core';
import { OperationVariables, QueryOptions, ApolloQueryResult, DefaultContext, MutationOptions, FetchResult, ApolloClient } from '@apollo/client/core';
import { RenderableContextService, RenderableContext, ModelContainer, ThreeDPreviewService } from '@spiffcommerce/preview';
import * as lodash from 'lodash';
import { CompleteQuoteMessage, ThemeInstallConfigurationGraphQl, ConversionConfiguration } from '@spiffcommerce/theme-bridge';
export { ConversionConfiguration, ConversionData, ConversionDataType, ConversionLocation } from '@spiffcommerce/theme-bridge';
import { Currency } from 'dinero.js';
import { FuseResult } from 'fuse.js';
import { Font } from 'opentype.js';
/**
* A renderable scene is a scene that can be displayed to the user. This is based on the workflow state.
*/
interface RenderableScene {
/**
* The id of the scene.
*/
id: string;
/**
* The title of the scene.
*/
title: string;
/**
* The id of each step inside the scene that can be rendered. This is based on the workflow state.
*/
renderableSteps: string[];
/**
* The WorkflowScene representation of this object. Provided for backwards compatibility.
* @deprecated
*/
workflowScene: WorkflowScene;
}
/**
* Information parsed froma workflow structure that is relevant
* to a given scene.
*/
interface WorkflowScene {
/**
* A unique identifier for the scene.
*/
name: string;
/**
* A human-readable title for the scene.
*/
title: string;
/**
* Steps which can display to the user.
* But may be conditionally hidden based on workflow logic
*/
renderableSteps: Step<AnyStepData>[];
/**
* Steps which don't display to the user.
* Their behavior is always silent & executed in the background.
* @deprecated Silent steps are no longer handled seperately from normal steps.
*/
silentSteps: Step<AnyStepData>[];
}
/**
* A queue promise is a container for a promise that can be
* executed at a later time.
*/
declare abstract class QueueablePromise {
readonly timestamp: number;
abstract execute(): Promise<any>;
}
/**
* A promise queue contains any number of QueuePromise objects. These objects are stored within a PromiseQueue and executed
* as quickly as possible in order. This is ideal in situations where a specific operation should be
* applied in an ordered way while still making.
*/
declare class PromiseQueue<T extends QueueablePromise> {
private queue;
private activePromise?;
private queueMaxSize;
private isEnabled;
/**
* Constructs a new promise queue.
* @param queueMaxSize An optional maximum size, when the max size is hit.
* The older promises will be discarded.
* @param enabled When false, the queue will not process any jobs. Assign `enabled` to true to start processing.
*/
constructor(queueMaxSize?: number, enabled?: boolean);
/**
* Enqueue a new promise.
* @param promise A new promise to add to the queue.
*/
enqueue(promise: T): void;
get enabled(): boolean;
/**
* Enable or disable the queue. When disabled, the queue will not process any jobs.
* Disabling processing will not cancel any active promises.
*/
set enabled(value: boolean);
/**
* @returns Returns true when work is being actively processed by this queue.
*/
hasActivePromise(): boolean;
/**
* @returns The number of unexecuted jobs remaining in the queue. Not including the active job.
*/
getRemainingQueueSize(): number;
/**
* Finalize the queue, any jobs that come in while this is in progress will result
* in the promise being extended.
*/
finalize(): Promise<void>;
/**
* Once called will recursively resolve the jobs in the
* queue until no more are available.
*/
private dequeue;
}
/**
* Bounds the offsets for an image to the box, preventing
* the user from moving the image in a way that wouldn't be intended.
* @param newOffsets The new intended offsets for the image.
* @param frameData The current frame information
* @param borderWidth The width of the border added by the cropper.
* @param mustCover When true the image sgould be bounded in such a way that it covers the entire frame at all times.
*/
declare function getBoundedOffsets(newOffsets: FrameOffsets, frameData: FrameData, imageData: PatternImageData, mustCover?: boolean): FrameOffsets;
declare class FrameService {
private offsets;
private thresholdSettings;
private forceImageCover?;
private initialZoom?;
private targetElements;
private imageData?;
private frameData?;
private readonly _debouncedUpdateFrameOffsets;
/**
* The calculated minimum zoom value, per frame.
*/
minZoomScale: number[];
/**
* The calculated maximum zoom value. Note: This is calculated based on the first frame.
*/
maxZoomScale: number[];
private onFrameDataChangeListeners;
private onZoomChangeListeners;
private workflowManager?;
private stepName?;
constructor(forceImageCover?: boolean, initialZoom?: number);
/**
* When we want to connect a workflow manager to the state of the image cropper we
* can pass it to this function. Inside we'll attach any required event listeners.
* @param workflowManager The workflow manager to attach.
* @param stepName The specific step we want to attach to within the manager.
*/
connectWorkflowManager(workflowManager: WorkflowManager, stepName?: string): void;
/**
* Sets the elements that should be update when changes are made to
* the cropper that owns this service.
* @param targetElements A list of element Ids to track
*/
setTargetElements(targetElements: string[]): void;
/**
* Gets the current calculated frame data
* @returns A FrameData object or undefined if no frame has been set.
*/
getFrameData(): FrameData[] | undefined;
/**
* Sets the current frame data. Note:
* @param paths The paths to lookup in our frame data cache.
*/
setFrameData(paths: string[] | undefined): void;
/**
* Gets the currently set image of the frame..
* @returns A PatternImageData object, or undefined if no image is set.
*/
getImageData(): PatternImageData | undefined;
/**
* Helper function to get the src of the image of the frame, since it can be an svg with modified colors.
* @returns A url (a data url if svg), or undefined if no image is set.
*/
getImageSrc(): string | undefined;
/**
* Gets the current calculated offsets of the pattern within the frame.
* @returns A FrameOffsets object or undefined if no offsets are defined.
*/
getOffsets(): FrameOffsets[] | undefined;
/**
* Updates the frame offsets explicitly.
*/
setOffsets(offsets: FrameOffsets[]): void;
/**
* Sets the zoom of the cropper that owns this service.
* @param zoom The new zoom value, per frame.
* @param cX The center of zoom on x axis, per frame.
* @param cY The center of zoom on Y axis, per frame.
* @param onComplete A function to call when zoom changes have been completed
*/
setZoom(zoom: number[], cX: number[], cY: number[], onComplete?: () => void): void;
/**
* Sets the image currently contained by this frame.
* @param value The new image as an ImageData property
* @param [recalculateOffsets=true] Optional: Enable/disable re-calculating of frame offsets. Default: `true`.
* Note: Will always calculate when offsets have not been calculated.
*/
setPatternData(value: PatternImageData, recalculateOffsets?: boolean): void;
/**
* Modify the offsets of the frame.
* @param value The new FrameOffsets objects.
* @param onComplete A callback, called when the modification is complete
* @param forceUpdate When true the offsets will be updated even if they haven't changed.
*/
updateOffsets(value: FrameOffsets[], onComplete?: () => void, forceUpdate?: boolean): void;
getThresholdSettings(): FrameThresholdSettings;
setThresholdSettings(settings: FrameThresholdSettings): void;
onFrameDataChanged(newListener: (frameData: FrameData[] | undefined) => void): void;
/**
* Append a new listener to zoom events on this frame.
* @param newListener
*/
onZoom(newListener: (zoomValue: number[]) => void): void;
/**
* Updates the offsets of the frame
* @param newOffsets New offset object
* @param imageData The image data
* @param frameData The frame data
* @param targetElements A list of elements that need updating, by ID
* @param onComplete A callback when the operation is completed.
*/
private updateFrameOffsets;
/**
* Determines limitations of zoom based on relative size of image and frame.
* @param imageData The image to include in calculations
* @param frameData The frame to include in calculations.
*/
private recalculateZoomLimits;
private recalculateOffsets;
}
declare class Poller {
private pollingId;
private attempts;
private readonly interval;
private readonly maxAttempts;
private readonly predicate;
private readonly onSuccess;
private readonly onFailure;
private poll;
/**
* Constructs a new polling service.
* @param predicate An async function that returns true when polling has returned a successful result.
* @param onSuccess The callback to be called when polling has returned a successful result.
* @param onFailure The callback to be called when polling has returned a failed result.
* @param interval The number of milliseconds to wait between each poll.
* @param maxAttempts The maximum amount of times to check the condition.
*/
constructor(predicate: () => Promise<boolean>, onSuccess: () => void, onFailure: () => void, interval?: number, maxAttempts?: number);
}
declare abstract class ModuleProduct {
/**
* Name used by class. Usually product or brand name.
*/
abstract moduleName: string;
/**
* SVG with styled path positioned on a background image. To be displayed to user.
*/
abstract svgPreview(text: string, region: Region): string;
/**
* SVG with styled path positioned on a background image. To be submitted for print.
*/
abstract svgPrint(text: string, region: Region): string;
}
interface DesignInputStep {
data: DesignInputStepData;
name: string;
}
interface DesignInputStepData {
}
interface SVGLayoutProps {
configuration: RenderingConfiguration;
preserveAspectRatio?: string;
outlineArea?: {
x?: number;
y?: number;
width?: number;
height?: number;
scale?: number;
hidden?: boolean;
};
viewBox?: {
x: number;
y: number;
width: number;
height: number;
};
width: number | string;
height: number | string;
position?: string;
maxWidth?: string;
maxHeight?: string;
elements: LayoutElement[];
backgroundColor?: string;
outlineColor?: string;
omitBoundClipping?: boolean;
borderRadius?: number;
}
/**
* An abstract base class from which to extend all potential element commands. These commands
* follow the command design pattern in software development. To learn more about this pattern
* take a look at the following link.
*
* https://sourcemaking.com/design_patterns/command
*
*/
declare abstract class CanvasCommand {
abstract apply(state: LayoutsState): LayoutsState;
protected oldState?: LayoutsState;
undo(): LayoutsState;
overrideOldState(state: LayoutsState): void;
sequenceId?: string;
}
/**
* Shifts an element with given ID from its current position to a new position.
*/
declare class MoveCommand extends CanvasCommand {
private id;
private x;
private y;
constructor(id: string, x: number, y: number);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState;
};
serializableWorkflow: SerializableWorkflow;
};
}
/**
* Rotates an element to a given angle in degrees.
*/
declare class RotateCommand extends CanvasCommand {
private id;
private angle;
constructor(id: string, angle: number);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState;
};
serializableWorkflow: SerializableWorkflow;
};
}
/**
* Updates the width and height of an element to reflect a new size. Negative values will be
* converted to their absolute value. ie. -10 will become 10.
*/
declare class ResizeCommand extends CanvasCommand {
private id;
private width;
private height;
constructor(id: string, width: number, height: number);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState;
};
serializableWorkflow: SerializableWorkflow;
};
}
/**
* Applys a list of command objects to the current state, the final state
* will be that of all commands applied in the order of left to right.
*/
declare class GroupCommand extends CanvasCommand {
private commands;
constructor(commands: CanvasCommand[]);
apply(state: LayoutsState): LayoutsState;
}
declare class CreateLayoutCommand extends CanvasCommand {
private layout;
constructor(layout: ILayout);
apply(state: LayoutsState): LayoutsState;
}
/**
* Add an element to the canvas
*/
declare class CreateElementCommand<T extends LayoutElement> extends CanvasCommand {
private element;
private layout;
/**
* @param initialParams The initial parameters to be set on this new object
* @param callback An optional callback function to be notified when the object has been instantiated
* @param layout
*/
constructor(element: T, layout: ILayout);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState | {
elements: LayoutElement[];
modificationID: string;
layout: ILayout;
};
};
serializableWorkflow: SerializableWorkflow;
};
private assignIndex;
}
/**
* Delete an element on the canvas
*/
declare class DeleteElementCommand extends CanvasCommand {
private id;
constructor(id: string);
apply(state: LayoutsState): {
layouts: {
[key: string]: LayoutState;
};
serializableWorkflow: SerializableWorkflow;
};
}
declare class FontColorCommand extends CanvasCommand {
private id;
private color;
private textFillSpotColor?;
constructor(id: string, color: string, textFillSpotColor?: TextFillSpotColor$1);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState;
};
serializableWorkflow: SerializableWorkflow;
};
}
declare class FontSizeCommand extends CanvasCommand {
private id;
private size;
constructor(id: string, size: number);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState;
};
serializableWorkflow: SerializableWorkflow;
};
}
declare class FontSourceCommand extends CanvasCommand {
private id;
private fontData;
constructor(id: string, fontData: FontData);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState;
};
serializableWorkflow: SerializableWorkflow;
};
}
declare class FontAlignmentCommand extends CanvasCommand {
private id;
private align;
constructor(id: string, align: "left" | "center" | "right");
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState;
};
serializableWorkflow: SerializableWorkflow;
};
}
declare class UpdateImageSourceCommand extends CanvasCommand {
private id;
private src;
constructor(id: string, src: string);
apply(state: LayoutsState): LayoutsState;
}
/**
* TextChange will modify the text displayed in a text box
*/
declare class TextChangeCommand extends CanvasCommand {
private id;
private text;
constructor(id: string, text: string);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState;
};
serializableWorkflow: SerializableWorkflow;
};
}
declare class BringToFrontCommand extends CanvasCommand {
private id;
/**
* @param id The targeted element to bring to the front.
*/
constructor(id: string);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState | {
elements: LayoutElement[];
modificationID: string;
layout: ILayout;
};
};
serializableWorkflow: SerializableWorkflow;
};
}
declare class BringToBackCommand extends CanvasCommand {
private id;
/**
* @param id The targeted element to bring to the back.
*/
constructor(id: string);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState | {
elements: LayoutElement[];
modificationID: string;
layout: ILayout;
};
};
serializableWorkflow: SerializableWorkflow;
};
}
/**
* Bring an element forward by one layer. Does nothing if the element is already at the front.
*/
declare class BringForwardCommand extends CanvasCommand {
private id;
/**
* @param id The targeted element to bring to the front.
*/
constructor(id: string);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState | {
elements: LayoutElement[];
modificationID: string;
layout: ILayout;
};
};
serializableWorkflow: SerializableWorkflow;
};
}
/**
* Send an element backwards by one layer. Does nothing if the element is already at the back.
*/
declare class SendBackwardsCommand extends CanvasCommand {
private id;
/**
* @param id The targeted element to bring to the front.
*/
constructor(id: string);
apply(state: LayoutsState): {
layouts: {
[x: string]: LayoutState | {
elements: LayoutElement[];
modificationID: string;
layout: ILayout;
};
};
serializableWorkflow: SerializableWorkflow;
};
}
interface CommandState {
transaction: LayoutsState;
}
interface LayoutComponentConfiguration {
renderingConfiguration: RenderingConfiguration;
outlineArea?: {
x?: number;
y?: number;
width?: number;
height?: number;
scale?: number;
hidden?: boolean;
};
viewBox?: {
x: number;
y: number;
width: number;
height: number;
};
maxHeight?: string;
maxWidth?: string;
height?: string | number;
width?: string | number;
position?: string;
borderRadius?: number;
outlineColor?: string;
backgroundColor?: string;
}
declare class CommandContext {
private state;
private stateCallbacks;
private id;
private prevCommands;
private nextCommands;
constructor();
registerStateCallback(callback: () => void): void;
unregisterStateCallback(callback: () => void): void;
getState(): CommandState | undefined;
private runStateCallbacks;
apply(command: CanvasCommand, leaveOffUndoStack?: boolean): void;
undo(): void;
redo(): void;
/**
* Find all commands in history with the given sequence ID,
* discard all but the last and overwrite its oldState.
*/
flattenSequence(sequenceId: string, initialState: LayoutsState): void;
getLayoutById(layoutId: string): LayoutData;
getAllLayouts(): LayoutData[];
private getLayoutDataWithState;
initialize(layouts: ILayout[], reloadedState?: LayoutsState): void;
private commandReducer;
}
declare const getSvgElement: (layout: ILayout, elements: LayoutElement[], configuration: LayoutComponentConfiguration) => PapyrusNode;
interface LayoutData {
layoutState: LayoutState;
Component: PapyrusComponent<SVGLayoutProps>;
getComponentWithProps: (configuration: LayoutComponentConfiguration) => PapyrusComponent<SVGLayoutProps>;
}
declare class LayoutPreviewService implements RenderableContextService {
private readonly layouts;
private workflowManager?;
private handleCompleteRender;
constructor(layouts: ILayout[]);
setCompleteRenderCallback(handleCompleteRender: (layouts: LayoutPreviewBridge[]) => void): void;
onCompleteRender(): void;
getAll(): ReadonlyMap<string, LayoutPreviewBridge>;
setWorkflowManager(workflowManager: WorkflowManager): void;
getWorkflowManager(): WorkflowManager | undefined;
}
/**
* The panel canvas class that stores both the main rendering canvas as well as rendering context
* for a rendering context
*/
declare class LayoutPreviewBridge implements RenderableContext {
hasSetStaticContext: boolean;
private readonly id;
private readonly name;
private readonly panelSize;
private readonly getWorkflowManager?;
private service;
private interactiveDirty;
private textureCtx?;
private staticCtxDirty;
private lastRequestedRenderArguments;
private lastCompletedStaticRender;
private renderQueue;
constructor(id: string, name: string, service: LayoutPreviewService, panelSize: {
width: number;
height: number;
}, getWorkflowManager?: () => WorkflowManager | undefined);
getID(): string;
getName(): string;
getPanelSize(): {
width: number;
height: number;
};
getStaticContext(): CanvasRenderingContext2D | undefined;
/**
* Register canvas to be rendered to.
*/
setStaticContext(ctx: CanvasRenderingContext2D): void;
getStaticContextDirty(): boolean;
setStaticContextDirty(dirty: boolean): void;
getInteractiveCanvasDirty(): boolean;
setInteractiveCanvasDirty(dirty: boolean): void;
markLastCompletedStaticRender(): void;
/**
* Returns a timestamp for the last time that this canvas rendered to its dynamic texture in the
* form of Date.now(). If this panel has never rendered undefined will be returned.
*/
getLastCompletedStaticRender(): number | undefined;
/**
* Actions to perform when a static render event is fired for this canvas.
*/
render(layouts: LayoutData[]): Promise<void>;
}
interface StepAspectValue {
stepName: string;
stepAspectType: string;
value?: string;
}
declare const stepAspectValuesToDesignInputSteps: (stepAspectValues: StepAspectValue[], workflow: Workflow) => DesignInputStep[];
declare const generateStateFromDesignInputSteps: (designInputSteps: DesignInputStep[], workflow: Workflow, layouts: ILayout[], productOverlayImageUrl?: string) => Promise<LayoutsState>;
/**
* An asset manager provides a way to create and
* manage assets on the Spiff Commerce Platform.
*/
interface AssetManager {
/**
* Uploads a file to the Spiff Commerce Platform.
*/
uploadFile: (file: File, onProgress: (val: number) => void) => Promise<Asset>;
/**
* From an existing asset, generates a new asset that has the background replaced with transparency.
* This process is idempotent, i.e. it will only run once for a given asset.
* @param asset The existing asset to remove the background from.
* @returns A promise that resolves with a new asset.
*/
removeBackgroundFromAsset(asset: Asset): Promise<Asset>;
}
declare class AssetService implements AssetManager {
private cache;
/**
* Promise cache for BG removal processes. Values only present while process is active.
* Use BGRMStorage and the regular asset promise cache to cache the actual objects.
*/
private bgrmProcessCache;
private materialCache;
/**
* Allows for retrieving an asset, returns the option from a cache if possible.
*/
getLocalOrFromServer(assetKey: string): Promise<Asset>;
/**
* Retrieves the asset from the server, bypassing cache (but still writing the result to cache)
*/
getFromServer(assetKey: string): Promise<Asset>;
keyFromURL(url: string): string | undefined;
/**
* Caches an asset if it doesn't already exist.
*/
cacheAsset(asset: Asset): void;
/**
* Caches a material if it doesn't already exist.
*/
cacheMaterial(material: MaterialResource): void;
/**
* Allows for retrieving a material, returns the option from a cache if possible.
* @param id The option ID to be retrieved.
*/
getMaterialLocalOrFromServer(id: string): Promise<MaterialResource>;
/**
* Upload a user asset to the server. Using callbacks to notify important events.
* The asset will be stored via the persistence service for future access, if available.
*/
uploadAssetWithProgress(file: FileInfo, assetType: AssetType, onProgress: (val: number) => void, anonymous?: boolean, temporary?: boolean): Promise<Asset>;
uploadAsset(file: FileInfo, assetType: AssetType, anonymous?: boolean, temporary?: boolean): Promise<Asset>;
uploadFile(file: File, onProgress: (val: number) => void): Promise<Asset>;
removeBackgroundFromAsset(asset: Asset): Promise<Asset>;
removePersistedAsset(assetKey: string): void;
getPersistedAssets(): PersistedAsset[];
registerPersistedAssetListener(callback: () => void): void;
unRegisterPersistedAssetListener(callback: () => void): void;
/**
* Convert a File object for an image into a FileInfo.
*/
loadImageAsFileInfo: (file: File) => Promise<FileInfo>;
private isRaster;
private postProcessFileUpload;
/**
* Handles mimeType resolution & asset creation request
* @param file A file info object containing data about the file and its name
* @param assetType The type of asset we're expecting to upload
*/
private dispatchCreateAssetRequest;
private guessMIME;
}
interface PersistedAsset {
assetKey: string;
src: string;
}
declare const assetService: AssetService;
/**
* A wrapping component that provides a simple interface for interacting with a variant.
*/
declare class Variant {
private readonly variantData;
constructor(variant: VariantResource);
getType(): AssetType | undefined;
/**
* @returns The unique identifier for the variant.
*/
getId(): string;
/**
* @returns The configured name of the variant. Generally a human readable value.
*/
getName(): string;
/**
* @returns The price modifier for this variant. This is the amount that will be added to the base price of the product.
*/
getPriceFormatted(locales?: Intl.LocalesArgument, options?: Intl.NumberFormatOptions | undefined): string;
/**
* @returns The price modifier for this variant. This is the amount that will be added to the base price of the product.
* Presented in subunits of the currency of the option. For example, if the option is configured to use USD, the price modifier will be in cents.
*/
getPrice(): number;
/**
* @returns The URL for the base asset resource configured on this variant
*/
getAsset(): string | undefined;
/**
* @returns The URL for the base asset resource configured on this variant
*/
getAssetResource(): Asset | undefined;
/**
* @returns The URL for a thumbnail resource configured on this variant.
*/
getThumbnail(): string | undefined;
/**
* @returns When this variant is configured to have a color, this will return the hex value for that color.
*/
getColor(): string | undefined;
/**
* @returns True when the variant is the default for its containing option. False otherwise.
*/
isDefault(): boolean;
/**
* @returns The underlying variant resource. Generally not needed but made available just incase.
*/
getResource(): VariantResource;
/**
* @returns True when the variant is enabled. False otherwise.
*/
isEnabled(): boolean;
}
/**
* Steps that support custom color variants should implement the following interface to
* add the behavior in the way that makes sense to the step.
*/
interface CustomColorSupport {
/**
* Allows for setting a custom color when the custom variant is selected. Will
* throw when a non-custom variant is selected.
*/
setCustomColor(color: string): any;
/**
* Get the custom color that is currently set on the step.
*/
getCustomColor(): string;
}
/**
* A StepHandle allows for managing the state of a specific step in a workflow. This class
* abstracts away the complexities of dealing with a step directly and allows for using high level
* concepts instead of dealing with the underlying data structures.
*/
declare abstract class StepHandle<T extends AnyStepData> {
/**
* Stores whether or not the step is currently updating.
*/
private static readonly updateState;
/**
* Access to the workflow manager this step is contained by.
*/
protected readonly manager: WorkflowManager;
/**
* The step metadata, useful for determining logic based on configuration.
*/
protected readonly step: Step<T>;
/**
* The tags set against this step.
*/
protected readonly tags: string[];
constructor(manager: WorkflowManager, step: Step<T>, tags: string[]);
/**
* Set the current update state of this step. All step handles pointing to this step will
* see this value.
* @param value The new value
*/
protected setUpdateState(value: boolean): void;
/**
* @returns Gets the current update state of this step. All step handles for this step will see this value.
*/
protected getUpdateState(): boolean;
/**
* Gets the currently selected variant, or undefined if no variant is selected.
*/
getCurrentVariant(): Variant | undefined;
/**
* @returns A list of valid variants for this step. Does not include disabled variants.
*/
getAvailableVariants(): Variant[];
/**
* @returns A list of all variants for this step, including disabled ones.
*/
getAllVariants(): Variant[];
/**
* Most step types have a base option type that variants can be selected for.
* Selects a specific variant for this step. This will execute all required changes to
* the design and update the metadata to include the new selection. Any conditions
* that would be triggered will also be executed.
*/
abstract selectVariant(variant: Variant): Promise<void>;
/**
* @returns A unique identifier for this step within the workflow.
*/
getId(): string;
/**
* @returns The name of the step
*/
getName(): string;
/**
* @returns A message that can accompany the step name in UI components. Used to describe the purpose of the step in more detail.
*/
getHelpText(): string | undefined;
/**
* @returns The type of the step handle.
*/
getType(): StepType;
/**
* @returns The underlying data for this step. Favor using the step handle methods instead of this.
*/
getRaw(): Step<T>;
/**
* @returns Returns all of the tags that are present on this step.
*/
getTags(): string[];
/**
* @param tag The tag to check for.
* @returns True if the step has the specified tag, false otherwise.
*/
hasTag(tag: string): boolean;
/**
* @returns True if the step is required to be filled by the customer. False otherwise.
*/
getMandatory(): boolean;
/**
* @param type The AspectType to fetch the value for.
* @returns A boolean indicating whether this step should override Global Properties.
* Only relevant when the Workflow Experience is associated with a Bundle that is using Global Properties,
* and when this step is associated with one or more Global Property Aspects.
*/
getOverrideGlobalPropertyConfiguration(type: AspectType): boolean;
/**
* Sets whether or not this step should override Global Properties.
* Only relevant when the Workflow Experience is associated with a Bundle that is using Global Properties,
* and when this step is associated with one or more Global Property Aspects.
* @param type The AspectType to override.
*/
setOverrideGlobalPropertyConfiguration(type: AspectType, value: boolean): void;
/**
* Retrieves the identifiers of all of the configured aspects for the specified Global Property Configuration.
* @param configurationId The ID of the Global Property Configuration. You can usually find this with `bundle.getGlobalPropertyConfiguration()?.id`
* @returns An array of strings matching the keys of all the Global Property Aspects in the Configuration that this step is configured to use.
*/
getGlobalPropertyAspects(configurationId: string): string[];
/**
* Fires any configured animations on the 3D preview for this step.
* This includes camera & model animations. If the preview is unavailable
* this function will do nothing.
*/
executeAnimations(immediate?: boolean): void;
}
interface SavedDesign {
/**
* The user's name for this saved design.
*/
title: string;
/**
* A URL pointing to an image of the design. Typically a data URL
*/
thumbnail?: string;
/**
* The ID of the transaction relating to this design.
*/
transactionId: string;
/**
* The product ID for this transaction.
*/
productId: string;
/**
* The integration product ID related to this order.
*/
integrationProductId: string;
/**
* The name of the workflow annotated at time of save (may be different from current workflow name).
*/
workflowName: string;
/**
* The ID of the workflow annotated at time of save.
*/
workflowId: string;
/**
* The last edit that occured on this saved design.
*/
lastEdited: Date;
}
/**
* The design service exposes helper functionality wrapping important design management operations.
* NOTE: In the future this interface should allow for storing/pulling designs from the server.
*/
declare class DesignService {
readonly localPersistenceKey = "designTransactions";
private storageMethod;
private designSavedListeners;
/**
* @param func The function to call when a design is saved.
*/
attachSaveListener(func: (design: SavedDesign) => void): void;
/**
* @param func The function to remove from the list of listeners.
*/
detachSaveListener(func: (design: SavedDesign) => void): void;
/**
* Gets the currently persisted designs.
*/
getSavedDesigns(): Promise<SavedDesign[]>;
/**
* Search for a transaction that has been saved.
* @param transactionId The id to search for.
* @returns The transaction for the given id provided it has been saved. undefined if it doesn't exist.
*/
getSavedDesignByTransaction(transactionId: string): Promise<SavedDesign | undefined>;
/**
* Saves a design to storage.
* @param design The design to save.
*/
addDesign(design: SavedDesign): Promise<void>;
/**
* Change the user's name of the given saved design.
*/
renameDesign(transactionId: string, title: string): Promise<void>;
/**
* Removes a given design from storage.
* @param transactionId
*/
removeDesign(transactionId: string): Promise<void>;
private setDesigns;
}
declare const designService: DesignService;
interface GraphQlClient {
query<T = any, TVariables extends OperationVariables = OperationVariables>(options: QueryOptions<TVariables, T>): Promise<ApolloQueryResult<T>>;
mutate<TData = any, TVariables extends OperationVariables = OperationVariables, TContext extends Record<string, any> = DefaultContext>(options: MutationOptions<TData, TVariables, TContext>): Promise<FetchResult<TData>>;
}
type GraphQlClientFunc = () => GraphQlClient;
/**
* A scene is a collection of steps that can be used to group steps together.
*/
interface Scene {
/**
* The unique identifier for the scene.
*/
id?: string;
/**
* The name of the scene.
*/
name: string;
/**
* The steps that are part of the scene. A list of ids. See getStepById.
*/
stepIds: string[];
}
/**
* State related to a workflow experience.
*/
interface ExperienceOptions {
transaction: Transaction;
workflow?: Workflow;
modelContainer?: ModelContainer;
renderableContextService?: LayoutPreviewService;
reloadedState?: LayoutsState;
/**
* When true the experience is intended to be immutable.
*/
readOnly?: boolean;
/**
* A function that communicates state changes to the server.
*/
stateMutationFunc: StateMutationFunc;
/**
* The interface for the graphql client
*/
graphQlClient: GraphQlClientFunc;
/**
* Should be set to true when the experience is loaded from an existing transaction.
* FIXME: Wouldn't we know this from existance of reloadedState
*/
isReloadedTransaction?: boolean;
/**
* When true the system will treat steps with
* a single variant as renderable. False
* by default.
*/
singleVariantsRenderable?: boolean;
/**
* When true, will delay syncing the workflow state until manually enabled.
*/
delayWorkflowStateSync?: boolean;
}
/**
* A Workflow experience encapsulates the workflow manager and command context. It
* provides a simplified interface for interacting with the workflow manager. You
* should get an instance of this class from a Client you have constructed previously.
*/
interface WorkflowExperience {
/**
* Get the current transaction for this experience.
*/
getTransaction(): Transaction;
/**
* Get the bundle this experience is part of. May be undefined.
*/
getBundle(): Bundle$1 | undefined;
/**
* Set the bundle this experience is part of. Can be cleared using undefined.
*/
setBundle(bundle: Bundle$1 | undefined): any;
/**
* Get the current product for this experience. May be undefined.
*/
getProduct(): Product | undefined;
/**
* Get the current profanity list for this experience. May be empty.
*/
getProfanityList(): string[];
/**
* Set the current product for this experience.
* @param product The new product to set.
*/
setProduct(integrationProductId: string): Promise<void>;
/**
* Clear the product from this experience.
*/
clearProduct(): Promise<void>;
/**
* Get the current workflow for this experience. May be undefined.
*/
getWorkflow(): Workflow | undefined;
/**
* Set the current workflow for this experience.
* @param workflow The new workflow to set.
*/
setWorkflow(workflow: Workflow): Promise<void>;
/**
* Returns the client that was responsible for spawning this experience.
*/
getClient(): SpiffCommerceClient;
/**
* State related to the design of the user.
*/
getCommandContext(): CommandContext;
/**
* Returns true when the user may only view the design.
*/
getIsReadOnly(): boolean;
/**
* Get the low level workflow amanager instance for this experience. Don't touch this unless you're willing to break things.
*/
getWorkflowManager(): WorkflowManager;
/**
* Returns the step matching a given name, undefined if not found.
* @param id The id the step must match.
*/
getStepById(id: string): StepHandle<AnyStepData> | undefined;
/**
* Returns the step matching a given name, undefined if not found.
* @param name The name the step must match.
*/
getStepByName(name: string): StepHandle<AnyStepData> | undefined;
/**
* Returns all steps matching a specific type in the workflow. These steps
* may be across multiple scenes and may or may not be active based on condition state.
*/
getStepsByType(type: StepType): StepHandle<AnyStepData>[];
/**
* Returns all steps that are children of a given scene.
* @param scene The scene you want the steps for.
*/
getStepsByScene(scene: Scene): StepHandle<AnyStepData>[];
/**
* Returns all steps in the workflow. Ordered by scene and appearance within their respective scenes.
*/
getSteps(): StepHandle<AnyStepData>[];
/**
* Returns all steps in the workflow that are conditionally active. Ordered by scene and appearance within their respective scenes.
*/
getStepsConditionallyActive(): StepHandle<AnyStepData>[];
/**
* Returns a list of scenes that are configured in the workflow. Each scene
* contains a list of steps. See getStepsByScene to access these.
*/
getScenes(): Scene[];
/**
* Returns the total cost in subunits for all selections made on the design.
* @param disablePriceBreaks Whether to exclude price breaks from the calculation.
*/
getSelectionPriceSubunits(disablePriceBreaks?: boolean): number;
/**
* Returns the total cost in subunits for the base product.
* @param includeAdditionalProduct When true the additional product cost will be included in the total (if configured).
* @param disablePriceBreaks Whether to exclude price breaks from the calculation.
*/
getBasePriceSubunits(includeAdditionalProduct?: boolean, disablePriceBreaks?: boolean): number;
/**
* If an additional product is configured, returns the base price of that product. Returns undefined otherwise.
* @param disablePriceBreaks Whether to exclude price breaks from the calculation.
*/
getAdditionalProductPriceSubunits(disablePriceBreaks?: boolean): number | undefined;
/**
* A convenience function returning the sum of the selection and base price values.
* @param disablePriceBreaks Whether to exclude price breaks from the calculation.
*/
getTotalPriceSubunits(disablePriceBreaks?: boolean): number;
/**
* The price break percentage that is expected to be applied in price calculations.
*/
priceBreakToBeApplied(): number;
/**
* Calculates the price break and fires the "PriceBreakChanged" event if required.
* This function is primarily intended for internal use.
*/
checkForPriceBreakChanges(): void;
/**
* Takes selections made by the user in another workflow and applies them to this workflow. For
* selections to be copied they must both have a matching global property configuration.
* @param experience The experience to take selections from.
* @param filter A list of steps to apply the selections to. If undefined all steps will be updated.
*/
copySelectionsViaGlobalConfiguration(bundle: Bundle$1, experience: WorkflowExperience, filter?: StepHandle<AnyStepData>[]): Promise<void>;
/**
* Attach specific details about the customer to the experience. This is useful for things like retargeting. Currently only
* email is supported. From SpiffCommerce hosted experiences these details will be attached whenever the customer has provided & given permission.
* @param details The new customer details. Only email is supported.
* @deprecated Use assignCustomerDetails instead.
*/
attachCustomerDetails(details: {
/**
* An email used for things like sending a design to the user.
*/
email: string;
}): Promise<void>;
/**
* Attach specific details about the customer to the experience. This is useful for things like retargeting.
* From SpiffCommerce hosted experiences these details will be attached whenever the customer has provided & given permission.
* @param details The new customer details.
*/
assignCustomerDetails(details: CustomerDetailsInput): Promise<void>;
/**
* Attaches a listener to the scenes on a workflow experience. The scenes returned are a subset of the scenes configured in the
* workflow and are based on the current state of the experience. This is useful for building a navigation menu.
* @param cb The callback to be called when the scenes change. The new scenes are passed as an argument.
*/
attachRenderableSceneListener(cb: (scenes: RenderableScene[]) => void): void;
/**
* Detaches a listener from the scenes on a workflow experience.
*/
detachRenderableSceneListener(cb: (scenes: RenderableScene[]) => void): void;
/**
* Saves this experience to storage. This may be local or remote depending
* on configuration.
* @param title The title for the saved design.
*/
save(title: string): Promise<SavedDesign>;
/**
* Returns a copy of the currently loaded design as a new, seperated workflow experience with
* a fresh transaction.
*/
copy(): Promise<WorkflowExperience>;
/**
* Creates a data URL preview for the current design.
*/
createPreviewImage(isThreeD?: boolean, resolution?: number): Promise<string>;
/**
* To be called when the workflow experience is considered completed by the user.
* @param onProgressUpdate Progress callback for finalizing the design. Optional
* @param capturePreviewImage When true a preview image will be generated for the design. Defaults to true.
*/
onDesignFinished(capturePreviewImage?: boolean): Promise<DesignCreationMessage>;
/**
* Returns the metadata associated with this workflow experience.
* This is a combination of the metadata from the workflow, and the selections made by the user.
* @returns An array of ExportedStepData objects, each containing the step ID, title, and properties.
*/
getExportedStepData(): ExportedStepData[];
/**
* Get the quantity of this WorkflowExperience's Transaction.
* @returns The amount that was, or will be, ordered.
*/
getQuantity(): number;
/**
* Sets the quantity of this WorkflowExperience's Transaction.
* @throws {Error} if the WorkflowExperience is read-only.
* @throws {RangeError} if the value is below 1.
* @param quantity The amount that was, or will be, ordered.
* @returns A promise that resolves when the Transaction has been updated on the server.
*/
setQuantity(quantity: number): Promise<void>;
/**
* Registers a callback function to be called when the specified event is raised.
* TODO: We should include a second param to pass information about the event?
* @param type The type of event to listen for.
* @param callback The function to call when the event occurs.
*/
addEventListener(type: WorkflowExperienceEventType, callback: (workflowExperience: WorkflowExperience) => void): void;
/**
* Removes a previously registered callback.
* @param type The type of event.
* @param callback The function to remove.
*/
removeEventListener(type: WorkflowExperienceEventType, callback: (workflowExperience: WorkflowExperience) => void): void;
attachAddress(streetAddress?: string, apartment?: string, city?: string, country?: string, state?: string, postCode?: string): Promise<void>;
attachOrganization(name: string): Promise<void>;
callEvent(type: WorkflowExperienceEventType): any;
}
type ExportedStepDataPropertyType = "selection" | "color" | "image" | "text";
interface ExportedStepDataProperty {
type: ExportedStepDataPropertyType;
value: string;
priceModifier: number;
}
interface ExportedStepData {
stepId: string;
title: string;
properties: ExportedStepDataProperty[];
}
declare enum WorkflowExperienceEventType {
PriceBreakChanged = "PriceBreakChanged",
ProductChanged = "ProductChanged",
QuantityChanged = "QuantityChanged",
RecipientChanged = "RecipientChanged",
SelectionChanged = "SelectionChanged"
}
declare class WorkflowExperienceImpl implements WorkflowExperience {
readonly client: SpiffCommerceClient;
readonly transaction: Transaction;
readonly graphQlClient: GraphQlClientFunc;
readonly workflowManager: WorkflowManager;
readonly isReadOnly: boolean;
readonly cachedStepHandles: Map<string, StepHandle<any>>;
/**
* Bundle this experience has been added to.
*/
private bundle?;
private profanityList;
private workflow?;
private currentPriceBreak;
private renderableScenes;
private renderableSceneCallbacks;
private eventCallbacks;
constructor(client: SpiffCommerceClient, experienceOptions: ExperienceOptions);
getTransaction(): Transaction;
getProduct(): Product | undefined;
getProfanityList(): string[];
setProduct(integrationProductId: string): Promise<void>;
clearProduct(): Promise<void>;
getWorkflow(): Workflow | undefined;
setWorkflow(workflow: Workflow): Promise<void>;
attachAddress(streetAddress?: string, apartment?: string, city?: string, country?: string, state?: string, postCode?: string): Promise<void>;
attachOrganization(name: string): Promise<void>;
getBundle(): Bundle$1 | undefined;
setBundle(bundle: Bundle$1): void;
getClient(): SpiffCommerceClient;
getIsReadOnly(): boolean;
getCommandContext(): CommandContext;
getWorkflowManager(): WorkflowManager;
createPreviewImage(isThreeD?: boolean, resolution?: number): Promise<string>;