Настройка конструктора фреймов
Конструктор фреймов - компонент, который строит указанный ему фрейм в режиме редактирования(designtime
). В данном режиме можно выполнять различные операции над виджетами: перетаскивать, добавлять/удалять, менять их свойства. Результатом работы компонента является json
конфиг - фрейм(результат конструирования).

Конфигурация конструктора
Чтобы добавить конструктор на страницу, необходимо:
- Добавить контрол
SiteEditorBase/constructor:View
в шаблон своего контрола. - Определить опции:
widgets
— фрейм(набор отображаемых виджетов).decorators
— набор декораторов, применяемых к каждому виджету.widgetsData
— набор данных для виджетов.onWidgetsChanged
— функция-коллбек, которая будет вызвана при изменении фрейма(при добавлении/удалении/перемещении/редактировании виджетов).
Настройка опций widgets, decorators и widgetsData аналогична настройке для плеера, пример можно найти в статье.
Платформенный декоратор
В большинстве случаев, в разных конструкторах присутствуют одни и те же операции (удаление/редактирование свойств), которые отображаются одинаковыми управляющими элементами. Для этого разработан платформенный декоратор виджетов SiteEditorBase/widgets:BaseDecorator
, который позволяет удалять виджеты и редактировать их свойства через PropertyGrid
.
Декоратор имеет следующие опции:
- allowDeleteCallback — функция, которая должна вернуть признак возможности удаления виджета; Если опция не задана, то удалять можно все виджеты.
- allowEditCallback — функция, которая должна вернуть признак возможности редактирования свойств виджета. Если опция не задана, то редактировать можно все виджеты.
В функции будут переданы следующие аргументы:
- widgetId — идентификатор виджета.
- widgetType — тип виджета.
- widgetProps — текущие свойства виджета.
Для подключения декоратора необходимо передать его в опцию decorators
конструктора.
Пример:
<!-- WML -->
<SiteEditorBase.constructor:View
decorators="{{_decorators}}"
/>
// TypeScript
protected _decorators = [{
templateName: 'SiteEditorBase/widgets:BaseDecorator',
props: {
allowEditCallback(widgetId, widgetType, widgetProps): {
return widgetType === 'baseWidget';
}
}
}];
При нажатии на кнопку редактирования декоратор открывает окно с PropertyGrid
, в котором отображаются все поля, указанные в propTypes у supportedWidgets
, у которых есть PGEditorConfig. Подробнее о настройке propTypes
можно найти в статье.
При нажатии на кнопку удаления/редактирования будет вызван метод onWidgetsChanged, который был передан в конструктор, и в него будет передан актуальный фрейм(результат конструирования).
Настройка onWidgetsChanged
Для того, чтобы получить текущий фрейм, необходимо задать опцию onWidgetsChanged
. Это функция-коллбэк, которая будет вызываться при любом изменении фрейма(добавление, удаление, перемещение, редактирование виджетов), в нее будет передаваться актуальный фрейм, который нужно передать в конструктор для его обновления.
<!-- WML -->
<SiteEditorBase.constructor:View
widgets="{{_widgets}}"
onWidgetsChanged="{{_onWidgetsChanged}}"
/>
// TypeScript
protected _widgets = [...];
protected _onWidgetsChanged = (widgets) => {
this._widgets = widgets;
}
Изменение фрейма из виджетов/декораторов
Конструктор позволяет изменять отображаемый фрейм. Для этого он в качестве опций во все декораторы передает следующие поля и методы:
- id — идентификатор виджета.
- type — тип виджета.
- props — свойства виджета, которые были указаны в поле props виджета. Если в фрейме у виджета не было указано значение свойства, то будет взято дефолтные значения из описания виджета в supportedWidgets.
- deleteWidget — метод для удаления виджета. В метод должны передаваться следующие параметры:
- widgetId — идентификатор удаляемого виджета.
- addWidget — метод для добавления виджета. В метод должны передаваться следующие параметры:
- widgetType — тип добавляемого виджета.
- targetId — идентификатор виджета, относительного которого нужно добавить новый виджет.
- position:
'after' | 'before'
— позиция, в которую нужно добавить виджет.
- onPropsChange - функция обратного вызова принимающая функцию которая вызывается когда декоратор изменяет опции контрола. В метод должны передаваться параметры:
- newProps — новые свойства виджета.
В виджет передаются свойства в соответствии с их значениями описанными во фрейме. Так же для каждого свойства передается функция обратного вызова, вызвав которую виджет в режиме редактирования может оповестить конструктор, что свойство поменялось. Опции, через которые передаются эти функции обратного вызова именуются стандартным образом. Если опция называется myOption
, то опция функция обратного вызова должна называться onMyOptionChange
.
Также в виджет передаются опции deleteWidget и addWidget.
С помощью этих методов виджеты и декораторы могут изменять фрейм, в результате вызова этих методов будет вызван коллбэк, который был передан в опцию onWidgetsChanged.
Drag'n'drop виджетов
Конструктор предоставляет инструменты для реализации drag’n’drop
виджетов. В корне конструктора находится платформенный контрол работы с drag'n'drop
. За запуск и окончание drag'n'drop
должны отвечать виджеты и декораторы.
Запуск перемещения
Для запуска drag'n'drop
виджет/декоратор должен вызвать метод startDrag, который был передан конструктором в опции виджета/декоратора. В метод необходимо передать:
- Объект, на основе которого будет создана сущность перемещения. Объект должен содержать следующие поля:
- id — идентификатор виджета. . type — тип перемещаемого виджета. . props — установленные свойства перемещаемого виджета.
- dragEndCallback — функция-колбек, которая будет вызвана при завершении
drag'n'drop
(подробнее в разделе "Окончание перемещения"). - draggingTemplate — путь до компонента, который будет использоваться в качестве шаблона перемещаемого элемента.
- draggingTemplateOptions — опции, которые будут переданы в компонент, указанный в параметре draggingTemplate.
Метод создаст сущность перемещения и запустит механизм drag'n'drop
. С помощью стандартного механизма drag'n'drop
сущность перемещения будет передана во все конструкторы на странице, а конструкторы передадут сущность во все виджеты и декораторы через опцию dragEntity (если перемещаемый виджет находится в списке поддерживаемых виджетов конструктора).
Пример:
// WML
<div class="myWidget" on:mousedown="_startDrag()">
...
</div>
// TypeScript
protected _startDrag(event, widgetId, widgetType, widgetProps): void {
this._options.startDrag(event, {
id: this._options.widgetId,
widgetType: this._options.widgetType,
props: this._options.widgetProps
}, this.dragEndCallback);
}
dragEndCallback = () => {
...
}
Перемещение
В процессе перемещения виджеты и декораторы должны самостоятельно обрабатывать наведение курсора мыши на их контейнеры. Если пользователь навел на часть виджета/декоратора, в которую можно переместить виджет, виджет/декоратор может визуально выделить эту область, а также он должен вызвать опцию-коллбек canProcessDragEnd — метод для указания, что виджет или декоратор могут обработать окончание перемещения. В метод нужно передать сущность, которая обработает окончание перемещения. У этой сущности должен быть реализован метод dragEnd, который будет вызываться при окончании перемещения (подробнее в разделе "Окончание перемещения").
// WML
<div>
<div class="dragLine" on:mousemove="_mouseMoveHandler('before', 0)"></div>
<Widget1/>
<div class="dragLine" on:mousemove="_mouseMoveHandler('after', 0)"></div>
<Widget2/>
<div class="dragLine" on:mousemove="_mouseMoveHandler('after', 1)"></div>
</div>
// TypeScript
protected _dragState;
protected _mouseMoveHandler = (event, position, widgetIndex) => {
if (this._options.dragEntity) {
this._options.canProcessDragEnd(this);
this._dragState = {
position,
widgetIndex
};
}
}
dragEnd = () => {
...
}
Окончание перемещения
Когда пользователь завершит drag'n'drop
, все конструкторы будут оповещены об этом платформенным механизмом. Конструктор, у которого есть установленная сущность, которая должна обработать окончание перемещения, вызывает метод dragEnd у этой сущности, в метод передается сущность перемещения. Внутри этого метода необходимо:
- произвести добавление виджета в нужное место.
- вызвать метод editProperty, который был передан в виджет конструтором, и передать в него новый массив виджетов.
- вернуть
delete
, если перемещение завершилось успешно и перемещаемый виджет необходимо удалить из места перетаскивания, иначе ничего не возвращать.
Пример:
// TypeScript
dragEnd = (dragEntity) => {
const draggedWidgetPosition = this._widgets.findIndex((widget) => widget.id === dragEntity.id);
let newWidgets;
// если перемещаемый виджет из текущего компонента
if (draggedWidgetPosition !== -1) {
newWidgets = ...;
// если перемещаемый виджет из другого компонента
} else {
newWidgets = ...;
}
this._options.editProperty(this._options.id, 'widgets', newWidgets);
return draggedWidgetPosition === -1 ? 'delete' : null;
}
После этого будет вызван dragEndCallback, который был установлен при запуске перемещения. В метод будет передана сущность перемещения, а также результат, который вернул метод dragEnd. Если dragEnd вернул delete
, внутри dragEndCallback необходимо:
- удалить перемещаемый виджет.
- вызвать метод editProperty, который был передан в виджет конструтором, и передать в него новый массив виджетов.
Пример:
// TypeScript
dragEndCallback = (dragEntity, dragEndResult) => {
if (dragEndResult === 'delete') {
const newWidgets = this._options.widgets.filter((widget) => widget.id !== dragEntity.id);
this._options.editProperty(this._options.id, 'widgets', newWidgets);
}
}