2023-12-12 09:58:20 +00:00
|
|
|
import * as style from './styles';
|
2024-01-18 17:23:50 +00:00
|
|
|
import {buildEditor} from 'app/client/components/Forms/Editor';
|
|
|
|
import {buildMenu} from 'app/client/components/Forms/Menu';
|
2024-01-23 20:52:57 +00:00
|
|
|
import {BoxModel} from 'app/client/components/Forms/Model';
|
2024-01-18 17:23:50 +00:00
|
|
|
import {makeTestId} from 'app/client/lib/domUtils';
|
2024-01-23 20:52:57 +00:00
|
|
|
import {Box} from 'app/common/Forms';
|
|
|
|
import {dom, styled} from 'grainjs';
|
2024-01-18 17:23:50 +00:00
|
|
|
|
|
|
|
const testId = makeTestId('test-forms-');
|
2023-12-12 09:58:20 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Component that renders a section of the form.
|
|
|
|
*/
|
|
|
|
export class SectionModel extends BoxModel {
|
2024-01-18 17:23:50 +00:00
|
|
|
public override render(): HTMLElement {
|
2023-12-12 09:58:20 +00:00
|
|
|
const children = this.children;
|
2024-01-18 17:23:50 +00:00
|
|
|
return buildEditor({
|
|
|
|
box: this,
|
|
|
|
// Custom drag element that is little bigger and at the top of the section.
|
|
|
|
drag: style.cssDragWrapper(style.cssDrag('DragDrop', style.cssDrag.cls('-top'))),
|
|
|
|
// No way to remove section now.
|
|
|
|
removeIcon: null,
|
|
|
|
// Content is just a list of children.
|
|
|
|
content: style.cssSection(
|
|
|
|
// Wrap them in a div that mutes hover events.
|
|
|
|
cssSectionItems(
|
|
|
|
dom.forEach(children, (child) => child.render()),
|
|
|
|
),
|
|
|
|
// Plus icon
|
|
|
|
style.cssPlusButton(
|
|
|
|
testId('plus'),
|
|
|
|
style.cssDrop(),
|
|
|
|
style.cssCircle(
|
|
|
|
style.cssPlusIcon('Plus'),
|
|
|
|
buildMenu({
|
|
|
|
box: this,
|
|
|
|
})
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)},
|
|
|
|
style.cssSectionEditor.cls(''),
|
2023-12-12 09:58:20 +00:00
|
|
|
);
|
2024-01-18 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2023-12-12 09:58:20 +00:00
|
|
|
|
2024-01-18 17:23:50 +00:00
|
|
|
/**
|
|
|
|
* Accepts box from clipboard and inserts it before this box or if this is a container box, then
|
|
|
|
* as a first child. Default implementation is to insert before self.
|
|
|
|
*/
|
|
|
|
public override accept(dropped: Box) {
|
|
|
|
// Get the box that was dropped.
|
|
|
|
if (!dropped) { return null; }
|
|
|
|
if (dropped.id === this.id) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
// We need to remove it from the parent, so find it first.
|
2024-01-23 20:52:57 +00:00
|
|
|
const droppedRef = dropped.id ? this.root().get(dropped.id) : null;
|
2024-01-18 17:23:50 +00:00
|
|
|
if (droppedRef) {
|
|
|
|
droppedRef.removeSelf();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Depending of the type of dropped box we need to insert it in different places.
|
|
|
|
// By default we insert it before this box.
|
|
|
|
let place = this.placeBeforeMe();
|
|
|
|
if (dropped.type === 'Field') {
|
|
|
|
// Fields are inserted after last child.
|
|
|
|
place = this.placeAfterListChild();
|
|
|
|
}
|
|
|
|
|
|
|
|
return place(dropped);
|
2023-12-12 09:58:20 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-18 17:23:50 +00:00
|
|
|
|
|
|
|
const cssSectionItems = styled('div.hover_border', `
|
|
|
|
`);
|