Подключение и конфигурирование#

Подключение редактора форм#

  1. Установите пакет:

  • В рамках данной инструкции требуется замена @*** на реальный scope из вашего репозитория пакетов:

npm install @***/script-editor
  1. Импортируйте компонент ScriptEditor:

import { ScriptEditor, IAppProps } from '@***/script-editor';
  1. Подключите стили конструктора форм:

  • Если требуется кастомизация стилей конструктора форм через LESS-переменные, то необходимо импортировать следующий файл и подключить less-loader

import '@***/script-editor/dist/src/app/styles/index.less'
  • Если кастомизация стилей конструктора форм не требуется, достаточно импортировать следующий файл:

import '@***/script-editor/dist/src/app/styles/index.css'
  1. Для работы локализации в календаре необходимо подключить плагин antd-dayjs-webpack-plugin в файле настроек webpack, а также подключить файл локализации для day.js

import dayjs from 'dayjs';
import 'dayjs/locale/ru';

dayjs.locale('ru_RU');

API редактора#

Интерфейс редактора форм#

Внешние свойства редактора скриптов форм

export interface IAppProps {
    /**  Данные об используемых маркетплейсах. */
    marketplaces?: {
      baseUrl: string;
      name: string;
     }[];
    /**  Пользовательские компоненты. */
    customWidgets?: IWidgetDefinition<any>[];
    /** Параметры плеера для отображения его отдельной вкладке редактора. */
    player?: {
      /** Компонент плеера */
      Player: JSXElementConstructor<any>;
      /** Уникальное имя скрипта в редьюсере плеера. */
      scriptName: string;
      /** Внешний контекст для инициализации скрипта. */
      onResult: (context: IPlayerContext) => void;
      /** Функция очистки и перезапуска скрипта скрипта. */
      clearScriptData?: (scriptName: string) => void;
      /** Внешняя функция для сохранения текущей истории плеера */
      onChangeHistory?: (history: IHistoryContext[]) => void;
      /**  Перезапуск скрипта с заданным контекстом */
      restartAndRunScript?: (data: IRestartAndRunScriptPayload) => void;
    };
    /** Подключаемый скрипт */
    script?:{
      subject?: string;
      label?: string;
      blocks?: IBlockRs[];
      macros?: IMacro[];
      phases?: string[];
      inputContext?: IContextParam[];
      outputContext?: IContextParam[];
    };
    /** Сallback на сохранение скрипта */
    onSave?: (script: IScriptRs) => void;
    /** Лимит действий для запоминания undo/redo */
    limitHistory?: number;
    /** Интервал по которому будет вызываться коллбэк onSave */
    intervalOnSave?: number;
    /** Настройки видимости меню */
    visibleMenu: {
      /** Отображение переменных */
      variables?: boolean,
      /** Отображение кнопки импорта */
      import?: boolean,
      /** Отображение кнопки экспорта */
      export?: boolean,
      /** Отображается только если передан пропс onSave */
      save?: boolean,
      /** Отображение кнопки макросы */
      macros?: boolean,
      /** Отображение кнопки фазы */
      phases?: boolean,
      /** Отображение кнопки открытия контекста */
      contextParams?: boolean,
      /** Отображается только если передан пропс player */
      player?: true,
    };
    /** Имя класса со стилями */
    className?: string;
}

Как использовать#

import React, { useState } from 'react';
import { ScriptEditor, useGetCurrentScript } from "@***/script-editor";
import {
	Player,
  clearScriptData as clearScriptDataAntd
} from '@***/script-player-react-antd';

const App = () => {
  const getCurrentScript = useGetCurrentScript()

  const [script, setScript] = useState(null);

  const onSave = () => {
    const savedScript = getCurrentScript();
  }

  return (
    <>
      <button onClick={onSave}>Сохранить</button>
      <ScriptEditor
          script={script}
          player={{
            Player,
            clearScriptData: clearScriptDataAntd,
            scriptName: 'demo',
            onResult: console.log,
          }}
          marketplaces={marketplaces}
          intervalOnSave={intervalOnSave}
          onSave={onSave}
          visibleMenu={visibleMenu}
          className="editor-prop"
      />
    </>
  )
}

Подключение плеера#

  1. Установите пакет @***/script-player-react-antd:

npm install --save @***/script-player-react-antd
  1. Импортируйте и подключите компонент плеера, передав обязательные параметры (props):

import { Player, IScriptRs, IPlayerContext } from '@***/script-player-react-antd';
 
interface IPlayerView {
  /** Скрипт для плеера. */
  script: IScriptRs; 
  /** Уникальное имя скрипта в редьюсере плеера. */
  scriptName: string;
  /** Результат прохождения скрипта. */
  onResult: (res: IPlayerContext) => void;
  /** Внешний контест для инициализации скрипта. */
  outsideContext?: IPlayerContext; 
  /** Пользовательские компоненты. */
  customWidgets?: IWidgetDefinition<any>[];
  /** Вызывается при смене фазы. */
  onPhaseChange?: (phase: string) => void;
  /** Выводит список отображений об ошибках. */
  onError?: (errors: IScriptBlockError[]) => void;
  /** Режим "только просмотр" для просмотра скрипта, без возможности изменения. */
  onlyShow?: boolean;
  /** Название для перехода к следующему блоку при режиме "только просмотр". */
  onlyShowNextLabel?: string;
  /** Внешняя функция для сохранения текущей истории плеера */
  onChangeHistory?: (history: IHistoryContext[]) => void;
  /** Cписок маркетплейсов */
  marketPlaceList?: string[];
  /** Внешний компонент для отрисовки блоков */
  SectionComponent?: JSXElementConstructor<ISectionComponentProps>;
}
 
/** Отрисовка компонента. */
const PlayerView = (props: IPlayerView) => {
  return (
    <Player
      script={props.script}
      scriptName="my_script"
      onResult={props.onResult}
      outsideContext={props.outsideContext ? props.outsideContext : {}}
      customWidgets={[]}
      onPhaseChange={console.log}
      onChangeHistory={onChangeHistory}
      marketPlaceList={props.marketPlaceList}
    />
  );
};
  1. Подключите стили Ant Design:

    • если инкапсуляция стилей Ant Design не нужна

import 'antd/dist/antd.css';
  • если нужна инкапсуляция стилей Ant Design, то вместо подключения всех стилей Ant Design можно подключить заранее подготовленный файл и подключить less-loader:

import '@***/script-player-react-antd/dist/styles/ant-global.less'

При этом нужно обернуть плеер дивом с классом ssp-ant-styles

<div className="ssp-ant-styles">
  <Player />
</div>
  1. Подключите стили плеера редактора форм:

    • если требуется кастомизация плеера через less-переменные, необходимо импортировать следующий файл и подключить less-loader:

import '@***/script-player-react-antd/dist/styles/index.less';
  • если кастомизация плеера не требуется, достаточно импортировать следующий файл:

import '@***/script-player-react-antd/dist/styles/index.css';
  1. Для корректной работы локализации в календаре нужно подключить в wepback.config плагин antd-dayjs-webpack-plugin и подключить в проект файл локализации для day.js

import dayjs from 'dayjs';
import 'dayjs/locale/ru';
 
dayjs.locale('ru_RU');

API плеера#

Интерфейс плеера форм#

interface IPlayerView {
  /** Скрипт для плеера. */
  script: IScriptRs;
  /** Уникальное имя скрипта в редьюсере плеера. */
  scriptName: string;
  /** Результат прохождения скрипта. */
  onResult: (res: IPlayerContext) => void;
  /** Внешний контест для инициализации скрипта. */
  outsideContext?: IPlayerContext;
  /** Пользовательские компоненты. */
  customWidgets?: IWidgetDefinition<any>[];
  /** Вызывается при смене фазы. */
  onPhaseChange?: (phase: string) => void;
  /** Выводит список отображений об ошибках. */
  onError?: (errors: IScriptBlockError[]) => void;
  /** Режим "только просмотр" для просмотра скрипта, без возможности изменения. */
  onlyShow?: boolean;
  /** Название для перехода к следующему блоку при режиме "только просмотр". */
  onlyShowNextLabel?: string;
  /** Внешняя функция для сохранения текущей истории плеера */
  onChangeHistory?: (history: IHistoryContext[]) => void;
  /** Cписок маркетплейсов */
  marketPlaceList?: string[];
  /** Внешний компонент для отрисовки блоков */
  SectionComponent?: JSXElementConstructor<ISectionComponentProps>;
}

Функции плеера форм#

Перезапуск скрипта с заданным контекстом#

import { 
	restartAndRunScript,
    IScriptRs, 
    IPlayerContext,
	IRestartAndRunScriptPayload 
} from '@***/script-player-react-antd';

restartAndRunScript(data as IRestartAndRunScriptPayload);

Очистка данных скрипта#

По умолчанию плеер сохраняет данные скрипта.

Если в процессе работы требуется сбросить или обновить данные скрипта в плеере форм, необходимо вызвать функцию Сбросить clearScriptData(scriptName as string), где в качестве единственного аргумента используется имя скрипта scriptName (строка), переданное в компонент плеера (атрибут scriptName).

import { clearScriptData } from '@***/script-player-react-antd';

clearScriptData(scriptName as string);

Восстановление истории прохождения скрипта#

import { 
	restoreScriptHistory, 
	IHistoryContext 
} from '@***/script-player-react-antd';

restoreScriptHistory(scriptName as string, history as IHistoryContext[]);

Получение текущего контекста#

import { 
	getCurrentContext, 
	IPlayerContext 
} from '@***/script-player-react-antd';

const context: IPlayerContext = getCurrentContext(scriptName as string);

Получение массива ошибок возникших в плеере при обработке переданного скрипта#

Использование onError при подключении компонента плеера#
/**
 * Информация по ошибке, возвращаемая сервером.
 *
 * @prop {string} [code] Код ошибки.
 * @prop {string} [system] Система, вернувшая ошибку.
 * @prop {string} [uuid] Uuid код ошибки.
 *
 * Ошибка при обработке блоков.
 *
 * @prop {any} [exception] Служебная информация об ошибке.
 *
 * Идентификация местоположения ошибки.
 *
 * @prop {string} [additionalId] Идентификатор выражения, по которому можно определить, где произошла ошибка.
 * @prop {string} [blockId] Идентификатор блока с ошибкой.
 */
interface IScriptBlockError {
  text?: string;
  title?: string;
  dateTime?: string;
  code?: string;
  system?: string;
  uuid?: string;
  exception?: any;
  additionalId?: string;
  blockId?: string;
}
 
/** Функция-обработчик принимающая от плеера массив ошибок и производящая с ними любые манипуляции в родительском компоненте */
const ErrorsParentHandlerFunction = (errors: IScriptBlockError[]) => {
    Logger.stash(errors);
}
 
/** Отрисовка компонента. */
return (
  <Player
    script={script}
    scriptName={SCRIPT_NAME}
    onResult={onResult}
    onError={ErrorsParentHandlerFunction}
    outsideContext={outsideContext ?? {}}
    onlyShow={onlyShow}
    onlyShowNextLabel={onlyShowLabel}
    onPhaseChange={log}
    onChangeHistory={onChangeHistory}
    customWidgets={customWidgets}
    marketplaces={marketplaceList}
    SectionComponent={useCustomSectionComponent ? SectionComponent : null}
  />
);

Принудительное обновление виджетов полученных с маркетплейсов#

Сам по себе метод reloadMarketplaceWidgets позволяет cache хранилищу с виджетами получить обновление, не вызывая обновление самого плеера. Это связано с тем, что измененные виджеты могут непредсказуемо влиять на runtime обрабатываемого блока. Новые контекстные переменные, потеря старых, логирование и сохранение истории могут повести себя некорректно, если виджет находится на раннем этапе разработки.

Для получения желаемого результата (мгновенного динамического обновления UI) необходимо использование в связке с методами restartAndRunScript или clearScriptData.

Пример с полным перезапуском скрипта (виджеты, контекст плеера)#
import { 
  reloadMarketplaceWidgets, 
  clearScriptData } 
from '@***/script-player-react-antd';
 
reloadMarketplaceWidgets(scriptName as string);
clearScriptData(scriptName as string)

Как использовать?#

import React, { useState } from 'react';
import { Player } from '@***/script-player-react-antd';
import '@***/script-editor/dist/src/app/styles/index.less'
import 'antd/dist/antd.css';

const PlayerView = (props: IPlayerView) => {
  const [script, setScript] = useState(null);

  return (
    <Player
      script={script}
      scriptName="my_script"
      onResult={() => {}}
      outsideContext={{}}
      customWidgets={[]}
      onPhaseChange={console.log}
      onChangeHistory={onChangeHistory}
      marketPlaceList={[]}
    />
  );
};