UNPKG

@silexlabs/grapesjs-data-source

Version:
387 lines (342 loc) 12.8 kB
/* * Silex website builder, free/libre no-code tool for makers. * Copyright (c) 2023 lexoyo and Silex Labs foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ /** * Public API for @silexlabs/grapesjs-data-source * * This file contains all the functions that calling apps should use. * It provides clean, simple APIs that hide internal complexity like DataTree management. */ import { Page, Editor, Component } from 'grapesjs' import { DataSourceId, IDataSource, Expression, StateId, StoredToken, Token, Field, Context, TypeId, Filter } from './types' // Internal imports import { getPageQuery as getPageQueryInternal, buildPageQueries as buildPageQueriesInternal } from './model/queryBuilder' import { refreshDataSources as refreshDataSourcesInternal, getFilters as getFiltersInternal, setFilters as setFiltersInternal, setPreviewData as setPreviewDataInternal, getManager } from './model/dataSourceManager' import { getAllDataSources as getAllDataSourcesInternal, getDataSource as getDataSourceInternal, addDataSource as addDataSourceInternal, removeDataSource as removeDataSourceInternal, } from './model/dataSourceRegistry' import { loadPreviewData as loadPreviewDataInternal, getPreviewData as getPreviewDataInternal, clearPreviewData as clearPreviewDataInternal, } from './model/previewDataLoader' import { fromStored as fromStoredInternal, getExpressionResultType as getExpressionResultTypeInternal, } from './model/token' import { getPersistantId as getPersistantIdInternal, getOrCreatePersistantId as getOrCreatePersistantIdInternal, getState as getStateInternal, getStateIds as getStateIdsInternal, setState as setStateInternal, removeState as removeStateInternal, getStateVariableName as getStateVariableNameInternal, COMPONENT_NAME_PREFIX as COMPONENT_NAME_PREFIX_INTERNAL, type PersistantId, type StoredState, } from './model/state' import { NOTIFICATION_GROUP as NOTIFICATION_GROUP_INTERNAL, toExpression as toExpressionInternal, createDataSource as createDataSourceInternal } from './utils' import { getPageExpressions as getPageExpressionsInternal } from './model/ExpressionTree' import { evaluateExpressionTokens, EvaluationContext } from './model/expressionEvaluator' import { GraphQLOptions } from './datasources/GraphQL' import { getCompletion as getCompletionInternal } from './model/completion' import { setPreviewIndex as setPreviewIndexInternal } from './view/canvas' // =============================== // QUERY GENERATION // =============================== /** * Generate GraphQL query for a single page * Used by both preview and production (11ty site generation) * * @param page - The GrapesJS page to generate query for * @param editor - The GrapesJS editor instance * @returns Record of data source ID to GraphQL query string */ export function getPageQuery(page: Page, editor: Editor): Record<DataSourceId, string> { return getPageQueryInternal(page, editor) } /** * Generate queries for multiple pages * Useful for batch operations like static site generation * * @param pages - Array of GrapesJS pages * @param editor - The GrapesJS editor instance * @returns Record of page ID to data source queries */ export function buildPageQueries(pages: Page[], editor: Editor): Record<string, Record<DataSourceId, string>> { return buildPageQueriesInternal(pages, editor) } // =============================== // DATA SOURCE MANAGEMENT // =============================== /** * Get all data sources * @returns Array of all registered data sources */ export function getAllDataSources(): IDataSource[] { return getAllDataSourcesInternal() } /** * Get a specific data source by ID * @param id - The data source ID * @returns The data source or undefined if not found */ export function getDataSource(id: DataSourceId): IDataSource | undefined { return getDataSourceInternal(id) } /** * Add a new data source * @param dataSource - The data source to add */ export function addDataSource(dataSource: IDataSource): void { return addDataSourceInternal(dataSource) } /** * Remove a data source * @param dataSource - The data source to remove */ export function removeDataSource(dataSource: IDataSource): void { return removeDataSourceInternal(dataSource) } /** * Refresh preview data from all data sources * Triggers data loading for the current page */ export function refreshDataSources(): void { return refreshDataSourcesInternal() } // =============================== // PREVIEW DATA MANAGEMENT // =============================== /** * Load preview data for the current page * @param forceRefresh - If true, bypass query comparison and force refresh * @returns Promise that resolves when data is loaded */ export function loadPreviewData(forceRefresh: boolean = false): Promise<void> { return loadPreviewDataInternal(forceRefresh) } /** * Get current preview data * @returns Record of data source ID to preview data */ export function getPreviewData(): Record<DataSourceId, unknown> { return getPreviewDataInternal() } /** * Set preview data for all data sources * @param data - Record mapping data source IDs to their preview data */ export function setPreviewData(data: Record<DataSourceId, unknown>): void { setPreviewDataInternal(data) } /** * Set preview index on an expression of a component */ export function setPreviewIndex(expression: StoredToken[], index: number, group?: number) { setPreviewIndexInternal(expression, index, group) } /** * Clear all preview data */ export function clearPreviewData(): void { return clearPreviewDataInternal() } // =============================== // DATA TREE OPERATIONS // =============================== /** * Evaluate an expression with current preview data * @param expression - The expression to evaluate * @param component - The component context * @returns The evaluated result */ export function getValue(expression: Expression, component: Component, resolvePreviewIndex = true): unknown { const context: EvaluationContext = { dataSources: getAllDataSources(), filters: getFiltersInternal(), previewData: getPreviewDataInternal(), component, resolvePreviewIndex, } return evaluateExpressionTokens(expression, context) } /** * Get all expressions used by components on a page * @param page - The page to analyze * @returns Array of component expressions */ export function getPageExpressions(page: Page) { const manager = getManager() return getPageExpressionsInternal(manager, page) } /** * Get auto-completion options for expressions * @param options - Completion options including component, expression, etc. * @returns Context with available tokens for completion */ export function getCompletion(options: { component: Component, expression: Expression, rootType?: TypeId, currentStateId?: StateId, hideLoopData?: boolean}): Context { const manager = getManager() return getCompletionInternal({ ...options, manager, }) } /** * Convert a stored token to its full form with methods and properties * @param token - The stored token to convert * @param componentId - The component ID for context (can be null) * @returns The full token with all properties and methods */ export function fromStored<T extends Token = Token>(token: StoredToken, componentId: string | null): T { return fromStoredInternal(token, componentId) } /** * Get the result type of an expression * @param expression - The expression to analyze * @param component - The component context * @returns The field describing the result type, or null if invalid */ export function getExpressionResultType(expression: Expression, component: Component): Field | null { return getExpressionResultTypeInternal(expression, component) } export function addFilters(filters: Filter | Filter[]) { const currentFilters = getFiltersInternal() const newFilters = Array.isArray(filters) ? filters : [filters] setFiltersInternal(currentFilters.concat(newFilters)) } /** * Remove filters from the data tree. * Accepts a single Filter or an array of Filters. * Removes by reference. */ export function removeFilters(filters: Filter | Filter[]) { const currentFilters = getFiltersInternal() if (Array.isArray(filters)) { setFiltersInternal(currentFilters.filter(f => !filters.includes(f))) } else { setFiltersInternal(currentFilters.filter(f => f !== filters)) } } // =============================== // STATE MANAGEMENT // =============================== /** * Get the persistent ID of a component * @param component - The component to get ID for * @returns The persistent ID or null if not set */ export function getPersistantId(component: Component): PersistantId | null { return getPersistantIdInternal(component) } /** * Get or create the persistent ID of a component * @param component - The component to get/create ID for * @returns The persistent ID */ export function getOrCreatePersistantId(component: Component): PersistantId { return getOrCreatePersistantIdInternal(component) } /** * Get a state from a component * @param component - The component to get state from * @param id - The state ID * @param exported - Whether to get exported (public) or private state * @returns The state or null if not found */ export function getState(component: Component, id: StateId, exported: boolean = true): StoredState | null { return getStateInternal(component, id, exported) } /** * Get all state IDs from a component * @param component - The component to get state IDs from * @param exported - Whether to get exported (public) or private state IDs * @param before - Optional state ID to get IDs before * @returns Array of state IDs */ export function getStateIds(component: Component, exported: boolean = true, before?: StateId): StateId[] { return getStateIdsInternal(component, exported, before) } /** * Set a state on a component * @param component - The component to set state on * @param id - The state ID * @param state - The state to set * @param exported - Whether to set as exported (public) or private state * @param index - Optional index to insert at */ export function setState(component: Component, id: StateId, state: StoredState, exported: boolean = true, index: number = -1): void { return setStateInternal(component, id, state, exported, index) } /** * Remove a state from a component * @param component - The component to remove state from * @param id - The state ID to remove * @param exported - Whether to remove from exported (public) or private states */ export function removeState(component: Component, id: StateId, exported: boolean = true): void { return removeStateInternal(component, id, exported) } /** * Get the variable name for a state * @param componentId - The component ID * @param stateId - The state ID * @returns The variable name */ export function getStateVariableName(componentId: string, stateId: StateId): string { return getStateVariableNameInternal(componentId, stateId) } // =============================== // TOKEN AND EXPRESSION UTILITIES // =============================== /** * Convert JSON or string to an Expression object * @param json - The JSON or string to convert * @returns The Expression object or null if invalid */ export function toExpression(json: unknown | string): Expression | null { return toExpressionInternal(json) } // =============================== // UTILITY FUNCTIONS // =============================== /** * Create a data source instance * @param options - The data source configuration * @returns The created data source */ export function createDataSource(opts: Partial<GraphQLOptions> = {}): IDataSource { return createDataSourceInternal(opts) } // =============================== // CONSTANTS // =============================== /** * Notification group name for data source notifications */ export const NOTIFICATION_GROUP = NOTIFICATION_GROUP_INTERNAL /** * Component name prefix for data source components */ export const COMPONENT_NAME_PREFIX = COMPONENT_NAME_PREFIX_INTERNAL // =============================== // Types // =============================== export * from './types'