@silexlabs/grapesjs-data-source
Version:
Grapesjs Data Source
667 lines (663 loc) • 20.5 kB
TypeScript
import Backbone from 'backbone';
import { Model, ModelSetOptions } from 'backbone';
import { Button, Component, Editor, Page } from 'grapesjs';
import { LitElement, TemplateResult } from 'lit';
export interface ComponentExpression {
expression: Expression;
component: Component;
}
/**
* Options of the data tree
* They can be set on the instance too
*/
export interface DataTreeOptions {
filters: Partial<Filter>[] | string;
dataSources: IDataSource[];
}
export declare const STANDARD_TYPES: Type[];
export declare class DataTree {
protected editor: DataSourceEditor;
protected options: {
dataSources: IDataSource[];
filters: Filter[];
};
dataSources: IDataSource[];
filters: Filter[];
/**
* All types from all data sources
* Read only, updated when data sources are updated
*/
protected _allTypes: Type[];
get allTypes(): Type[];
/**
* All queryable fields from all data sources
*/
protected _queryables: Field[];
get queryables(): Field[];
constructor(editor: DataSourceEditor, options: {
dataSources: IDataSource[];
filters: Filter[];
});
/**
* Get type from typeId and dataSourceId
*/
getTypes(dataSourceId?: DataSourceId): Type[];
/**
* Get type from typeId and dataSourceId
* @throws Error if type is not found
* @param componentId is used for error messages
*/
getType(typeId: TypeId, dataSourceId: DataSourceId | null, componentId: string | null): Type;
/**
* Get all types from all data sources
*/
getAllTypes(): Type[];
/**
* Get all queryable fields from all data sources
*/
getAllQueryables(): Field[];
/**
* Evaluate an expression to a value
*/
getValue(context: Context, expression: ComponentExpression): unknown;
/**
* Get all expressions used in all pages
*/
getAllPagesExpressions(): {
page: Page;
expressions: ComponentExpression[];
}[];
/**
* Get all expressions used in a page
* This will be used to fetch data for the page
*/
getPageExpressions(page: Page): ComponentExpression[];
/**
* Get all expressions used by a component and its children
*/
getComponentExpressionsRecursive(component: Component): ComponentExpression[];
/**
* Get all expressions used by a component
*/
getComponentExpressions(component: Component): ComponentExpression[];
/**
* Build a tree of expressions
*/
getTrees({ expression, component }: ComponentExpression, dataSourceId: DataSourceId): Tree[];
/**
* Check if a property is relative to a type
* A type is "relative" if parent has a type which has a field of type tree.token
* FIXME: need a better way to check if a property is relative, e.g. have a "relative" flag on the property, set depending on <state-editor root-type
*/
isRelative(parent: Property, child: Property, dataSourceId: DataSourceId): boolean;
/**
* From expressions to a tree
*/
toTrees(expressions: ComponentExpression[], dataSourceId: DataSourceId): Tree[];
/**
* Recursively merge two trees
*/
protected mergeTrees(tree1: Tree, tree2: Tree): Tree;
/**
* Get all expressions used by a component
* Resolves all states token as expressions recursively
* Resulting expressions contain properties and filters only, no states anymore
*/
resolveState(state: State, component: Component): Expression | null;
}
/**
* FIXME: Why sometimes the methods of the data source are in the attributes?
* @return ds if it has the getTypes method or ds.attributes if it has the getTypes method
*/
export declare function getDataSourceClass(ds: IDataSource | {
attributes: IDataSource;
}): IDataSource;
/**
* GrapesJs plugin to manage data sources
*/
export declare class DataSourceManager extends Backbone.Collection<IDataSourceModel> {
protected editor: DataSourceEditor;
protected options: DataSourceEditorOptions;
protected dataTree: DataTree;
get filters(): Filter[];
set filters(filters: Filter[]);
constructor(models: IDataSource[], editor: DataSourceEditor, options: DataSourceEditorOptions);
/**
* Get all data sources
*/
getAll(): IDataSourceModel[];
/**
* Forward events from data sources to the editor
*/
protected dataChangedBinded: (e?: CustomEvent) => void;
dataChanged(e?: CustomEvent): void;
/**
* Forward events from data sources to the editor
*/
protected dataSourceReadyBinded: (ds: IDataSource) => void;
dataSourceReady(ds: IDataSource): void;
/**
* Forward events from data sources to the editor
*/
protected dataSourceErrorBinded: (message: string, ds: IDataSource) => void;
dataSourceError(message: string, ds: IDataSource): void;
/**
* Listen to data source changes
*/
modelChanged(e?: CustomEvent): void;
/**
* Listen to data source changes
*/
modelReady(ds: IDataSource): void;
getDataTree(): DataTree;
getPageQuery(page: Page): Record<DataSourceId, string>;
}
/**
* Add the DataSourceManager to the GrapesJs editor
*/
export interface DataSourceEditor extends Editor {
DataSourceManager: DataSourceManager;
}
export interface DataSourceEditorViewOptions {
el?: HTMLElement | string | undefined | (() => HTMLElement);
settingsEl?: HTMLElement | string | (() => HTMLElement);
button?: Button | (() => Button);
styles?: string;
optionsStyles?: string;
defaultFixed?: boolean;
disableStates?: boolean;
disableAttributes?: boolean;
disableProperties?: boolean;
}
/**
* Options for the DataSourceEditor plugin
*/
export interface DataSourceEditorOptions {
dataSources: IDataSourceOptions[];
view: DataSourceEditorViewOptions;
filters: Filter[] | string;
commands: {
refresh: string;
};
}
export type PageId = string;
export interface Query {
expression: Expression;
}
/**
* Tree structure for creating query from components states
*/
export interface Tree {
token: Property;
children: Tree[];
}
export type DataSourceId = string | number;
export interface IDataSource {
id: DataSourceId;
hidden?: boolean;
connect(): Promise<void>;
isConnected(): boolean;
getTypes(): Type[];
getQueryables(): Field[];
getQuery(trees: Tree[]): string;
}
export declare const DATA_SOURCE_READY = "data-source:ready";
export declare const DATA_SOURCE_ERROR = "data-source:error";
export declare const DATA_SOURCE_CHANGED = "data-source:changed";
export declare const COMPONENT_STATE_CHANGED = "component:state:changed";
export declare const COMMAND_REFRESH = "data-source:refresh";
export interface IDataSourceModel extends Model<any, ModelSetOptions, any>, IDataSource {
}
export type DataSourceType = "graphql";
export interface IDataSourceOptions extends Backbone.ModelSetOptions {
id: DataSourceId;
label: string;
type: DataSourceType;
readonly?: boolean;
}
export type TypeId = string;
export type Type = {
id: TypeId;
label: string;
fields: Field[];
dataSourceId?: DataSourceId;
};
export declare const builtinTypeIds: string[];
export declare const builtinTypes: Type[];
export type FieldId = string;
export type FieldKind = "scalar" | "object" | "list" | "unknown";
export interface FieldArgument {
name: string;
typeId: TypeId;
defaultValue?: unknown;
}
export interface Field {
id: FieldId;
label: string;
typeIds: TypeId[];
kind: FieldKind;
dataSourceId?: DataSourceId;
arguments?: FieldArgument[];
}
/**
* A token can be a property or a filter
*/
export type Token = Property | Filter | State;
/**
* Stored tokens are how the tokens are stored in the component as JSON
* Use DataTree#fromStored to convert them back to tokens
*/
export type StoredToken = StoredProperty | StoredFilter | State;
export type Options = Record<string, unknown>;
/**
* A property is used to make expressions and access data from the data source
*/
export interface BaseProperty {
type: "property";
propType: "field";
dataSourceId?: DataSourceId;
}
export type PropertyOptions = Record<string, unknown>;
export interface StoredProperty extends BaseProperty {
typeIds: TypeId[];
fieldId: FieldId;
label: string;
kind: FieldKind;
options?: PropertyOptions;
}
export interface Property extends StoredProperty {
optionsForm?: (selected: Component, input: Field | null, options: Options, stateName: string) => TemplateResult | null;
}
/**
* A filter is used to alter data in an expression
* It is provided in the options
*/
export type FilterId = string;
export interface StoredFilter {
type: "filter";
id: FilterId;
filterName?: FilterId;
label: string;
options: Options;
quotedOptions?: string[];
optionsKeys?: string[];
}
export interface Filter extends StoredFilter {
optionsForm?: (selected: Component, input: Field | null, options: Options, stateName: string) => TemplateResult | null;
validate: (input: Field | null) => boolean;
output: (input: Field | null, options: Options) => Field | null;
apply: (input: unknown, options: Options) => unknown;
}
/**
* A component state
*/
export type StateId = string;
export interface State {
type: "state";
storedStateId: StateId;
label: string;
componentId: string;
exposed: boolean;
forceKind?: FieldKind;
}
/**
* A context is a list of available tokens for a component
*/
export type Context = Token[];
/**
* An expression is a list of tokens which can be evaluated to a value
* It is used to access data from the data source
*/
export type Expression = StoredToken[];
/**
* Operators for condition in visibility property
*/
export declare enum UnariOperator {
TRUTHY = "truthy",
FALSY = "falsy",
EMPTY_ARR = "empty array",
NOT_EMPTY_ARR = "not empty array"
}
/**
* Operators for condition in visibility property
*/
export declare enum BinariOperator {
EQUAL = "==",
NOT_EQUAL = "!=",
GREATER_THAN = ">",
LESS_THAN = "<",
GREATER_THAN_OR_EQUAL = ">=",
LESS_THAN_OR_EQUAL = "<="
}
/**
* Properties of elements
* What is not a property is an attribute or a state
*/
export declare enum Properties {
innerHTML = "innerHTML",
condition = "condition",
condition2 = "condition2",
__data = "__data"
}
/**
* Override the prefix of state names
*/
export declare const COMPONENT_NAME_PREFIX = "nameForDataSource";
/**
* Types
*/
export interface StoredState {
label?: string;
hidden?: boolean;
expression: Expression;
}
export interface StoredStateWithId extends StoredState {
id: StateId;
}
export type PersistantId = string;
/**
* Get the persistant ID of a component
*/
export declare function getPersistantId(component: Component): PersistantId | null;
/**
* Get the persistant ID of a component and create it if it does not exist
*/
export declare function getOrCreatePersistantId(component: Component): PersistantId;
/**
* Find a component by its persistant ID in the current page
*/
export declare function getComponentByPersistentId(id: PersistantId, editor: DataSourceEditor): Component | null;
/**
* Find a component by its persistant ID in
*/
export declare function getChildByPersistantId(id: PersistantId, parent: Component): Component | null;
/**
* Find a component by its persistant ID in the current page
*/
export declare function getParentByPersistentId(id: PersistantId, component: Component | undefined): Component | null;
/**
* Get the display name of a state
*/
export declare function getStateDisplayName(child: Component, state: State): string;
export declare function onStateChange(callback: (state: StoredState | null, component: Component) => void): () => void;
/**
* List all exported states
*/
export declare function getStateIds(component: Component, exported?: boolean, before?: StateId): StateId[];
/**
* List all exported states
*/
export declare function getStates(component: Component, exported?: boolean): StoredState[];
/**
* Get the name of a state variable
* Useful to generate code
*/
export declare function getStateVariableName(componentId: string, stateId: StateId): string;
/**
* Get a state
*/
export declare function getState(component: Component, id: StateId, exported?: boolean): StoredState | null;
/**
* Set a state
* The state will be updated or created at the end of the list
* Note: index is not used in this project anymore (maybe in apps using this plugins)
*/
export declare function setState(component: Component, id: StateId, state: StoredState, exported?: boolean, index?: number): void;
/**
* Remove a state
*/
export declare function removeState(component: Component, id: StateId, exported?: boolean): void;
/**
* Add missing methonds to the filter
* When filters are stored they lose their methods
* @throws Error if the filter is not found
*/
export declare function getFilterFromToken(token: Filter, filters: Filter[]): Filter;
/**
* Get the token from its stored form
* @throws Error if the token type is not found
*/
export declare function fromStored<T extends Token = Token>(token: StoredToken, dataTree: DataTree, componentId: string | null): T;
/**
* Get the type corresponding to a token
*/
export declare function tokenToField(token: Token, prev: Field | null, component: Component, dataTree: DataTree): Field | null;
/**
* Get the type corresponding to a property
* @throws Error if the type is not found
*/
export declare function propertyToField(property: Property, dataTree: DataTree, componentId: string | null): Field;
/**
* Evaluate the types of each token in an expression
*/
export declare function expressionToFields(expression: Expression, component: Component, dataTree: DataTree): Field[];
/**
* Evaluate an expression to a type
* This is used to validate expressions and for autocompletion
* @throws Error if the token type is not found
*/
export declare function getExpressionResultType(expression: Expression, component: Component, dataTree: DataTree): Field | null;
/**
* Get the options of a token
*/
export declare function getTokenOptions(field: Field): {
optionsForm: (selected: Component, input: Field | null, options: Options) => TemplateResult;
options: Options;
} | null;
/**
* Get the options of a token or a field
*/
export declare function optionsToOptionsForm(arr: {
name: string;
value: unknown;
}[]): (selected: Component, input: Field | null, options: Options) => TemplateResult;
/**
* Utility function to shallow compare two objects
* Used to compare options of tree items
*/
export declare function getOptionObject(option1: PropertyOptions | undefined, option2: PropertyOptions | undefined): {
error: boolean;
result: PropertyOptions | undefined;
};
export declare function buildArgs(options: PropertyOptions | undefined): string;
/**
* Get the context of a component
* This includes all parents states, data sources queryable values, values provided in the options
*/
export declare function getContext(component: Component, dataTree: DataTree, currentStateId?: StateId, hideLoopData?: boolean): Context;
/**
* Create a property token from a field
*/
export declare function fieldToToken(field: Field): Property;
/**
* Auto complete an expression
* @returns a list of possible tokens to add to the expression
*/
export declare function getCompletion(options: {
component: Component;
expression: Expression;
dataTree: DataTree;
rootType?: TypeId;
currentStateId?: StateId;
hideLoopData?: boolean;
}): Context;
/**
* @fileoverview GraphQL DataSource implementation
*/
/**
* GraphQL Data source options
*/
export interface GraphQLQueryOptions {
url: string;
headers: Record<string, string>;
method: "GET" | "POST";
queryable?: TypeId[];
readonly?: boolean;
}
/**
* GraphQL Data source options with server to server options
*/
export interface GraphQLOptions extends GraphQLQueryOptions, IDataSourceOptions {
serverToServer?: GraphQLQueryOptions;
}
export declare const NOTIFICATION_GROUP = "Data source";
/**
* Get the display name of a field
*/
export declare function cleanStateName(name: string | null): string | undefined;
export declare function getComponentDebug(component: Component): string;
/**
* Concatenate strings to get a desired length string as result
* Exported for tests
*/
export declare function concatWithLength(desiredNumChars: number, ...strings: string[]): string;
/**
* Get the label for a token
* This is mostly about formatting a string for the dropdowns
*/
export declare function getTokenDisplayName(component: Component, token: Token): string;
/**
* Group tokens by type
* This is used to create the groups in dropdowns
*/
export declare function groupByType(editor: DataSourceEditor, component: Component, completion: Token[], expression: Expression): Record<string, Token[]>;
/**
* Create a "fixed" token
* It is a hard coded content with which you can start an expression
*/
export declare const FIXED_TOKEN_ID = "fixed";
export declare function getFixedToken(value: string): Token;
/**
* Convert a token to a string
* This is used to store the token in the component
*/
export declare function toValue(token: Token): string;
/**
* Convert a token to an option's tag value (json string)
*/
export declare function toId(token: Token): string;
/**
* Revert an option's tag value to a token
* @throws Error if the token type is not found
*/
export declare function fromString(editor: DataSourceEditor, id: string, componentId: string | null): Token;
/**
* Check if a json is an expression, i.e. an array of tokens
*/
export declare function isExpression(json: unknown): boolean;
/**
* Convert a json to an expression
*/
export declare function toExpression(json: unknown | string): Expression | null;
/**
* Apply a kind to a field
*/
export declare function convertKind(field: Field | null, from: FieldKind, to: FieldKind): Field | null;
/**
* Get the type of a field, as provided by the data source
* @throws Error if the field has a token with an unknown type
*/
export declare function getFieldType(editor: DataSourceEditor, field: Field | null, key: string | undefined, componentId: string | null): Field | null;
/**
* Generate a form to edit the options of a token
* @throws Error if the field has a token with an unknown type
*/
export declare function optionsFormKeySelector(editor: DataSourceEditor, field: Field | null, options: Options, name: string): TemplateResult;
/**
* Get a container element from an option
* @throws Error if the option is not a string or an HTMLElement or a function
* @throws Error if the element is not found
*/
export declare function getElementFromOption(option: HTMLElement | string | (() => HTMLElement) | undefined, optionNameForError: string): HTMLElement;
export declare function getDefaultOptions(postFix?: string): GraphQLOptions;
export declare function createDataSource(opts?: Partial<GraphQLOptions>, postFix?: string): IDataSource;
/**
* Editor for a state of the selected element's properties
*
* Usage:
*
* ```
* <state-editor
* name="state"
* disabled
* hide-loop-data
* parent-name="parent"
* no-filters
* root-type="root"
* default-fixed
* dismiss-current-component-states
* ></state-editor>
* ```
*
*/
export declare class StateEditor extends LitElement {
disabled: boolean;
name: string;
hideLoopData: boolean;
/**
* used in the expressions found in filters options
* This will be used to filter states which are not defined yet
*/
parentName: string;
noFilters: boolean;
rootType: string;
defaultFixed: boolean;
dismissCurrentComponentStates: boolean;
private _selected;
get selected(): Component | null;
set selected(value: Component | null);
/**
* Value string for for submissions
*/
get value(): string;
set value(newValue: string);
/**
* Form id
* This is the same API as input elements
*/
for: string;
/**
* Binded listeners
*/
private onFormdata_;
private renderBinded;
connectedCallback(): void;
disconnectedCallback(): void;
/**
* Handle formdata event to add the current value to the form
*/
private onFormdata;
/**
* Form setter
* Handle formdata event to add the current value to the form
*/
protected _form: HTMLFormElement | null;
set form(newForm: HTMLFormElement | null);
get form(): HTMLFormElement | null;
/**
* Structured data
*/
private _data;
get data(): Token[];
set data(value: Token[] | string);
private _editor;
get editor(): DataSourceEditor | null;
set editor(value: DataSourceEditor | null);
private redrawing;
private expressionInputRef;
private popinsRef;
render(): TemplateResult<1>;
private onChangeValue;
private onChangeOptions;
private getOptions;
}
declare const _default: (editor: DataSourceEditor, opts?: Partial<DataSourceEditorOptions>) => void;
/**
* Version of the plugin
* This is replaced by the build script
*/
export declare const version = "__VERSION__";
export {
_default as default,
};
export {};