Skip to content

Commit

Permalink
Resolved #5873 - Implement pages drag/drop on design surface
Browse files Browse the repository at this point in the history
  • Loading branch information
tsv2013 committed Oct 1, 2024
1 parent 9a35073 commit 5593764
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 54 deletions.
4 changes: 2 additions & 2 deletions packages/survey-creator-core/src/components/item-value.scss
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@
}

.svc-item-value--ghost {
height: calcSize(6);

.svc-item-value__ghost {
display: block;
}
Expand All @@ -218,8 +220,6 @@
display: none;
}

height: calcSize(6);

.sv-string-viewer,
.sv-string-editor {
white-space: unset;
Expand Down
20 changes: 19 additions & 1 deletion packages/survey-creator-core/src/components/page.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ svc-page {
margin: 0;
padding: calcSize(1) calcSize(3) 0;
}

&.svc-question__drop-indicator--top {
top: calcSize(1);
}

&.svc-question__drop-indicator--bottom {
bottom: calcSize(1);
}
}

.svc-page__content-actions {
Expand Down Expand Up @@ -127,7 +135,17 @@ svc-page {
.svc-page__content-actions {
opacity: 1;
}

&>.svc-question__drag-area {
padding-top: calcSize(1.25);
z-index: 1;

.svc-question__drag-element {
opacity: var(--ctr-survey-page-drag-indicator-opacity, 0.5);
}
}
}

.svc-page__content--selected.svc-page__content--selected {
box-shadow: 0 0 0 2px $secondary;
background: $secondary-backcolor-semi-light;
Expand Down Expand Up @@ -312,7 +330,7 @@ svc-page {
}

.svc-page__content--collapsed {
&>* {
&>.sv-action-bar {
display: none;
}

Expand Down
53 changes: 39 additions & 14 deletions packages/survey-creator-core/src/components/page.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Action, ActionContainer, classesToSelector, ComputedUpdater, CssClassBuilder, DragTypeOverMeEnum, IAction, IElement, PageModel, property } from "survey-core";
import { Action, ActionContainer, classesToSelector, ComputedUpdater, CssClassBuilder, DragOrClickHelper, DragTypeOverMeEnum, IAction, IElement, PageModel, property } from "survey-core";
import { SurveyCreatorModel } from "../creator-base";
import { IPortableMouseEvent } from "../utils/events";
import { SurveyElementAdornerBase } from "./action-container-view-model";
Expand All @@ -7,12 +7,14 @@ import { getLocString } from "../editorLocalization";
require("./page.scss");
import { SurveyHelper } from "../survey-helper";
import { settings } from "../creator-settings";
import { DragDropSurveyElements } from "../survey-elements";

export class PageAdorner extends SurveyElementAdornerBase<PageModel> {
@property({ defaultValue: false }) isSelected: boolean;
@property({ defaultValue: true }) isPageLive: boolean;
@property() showPlaceholder: boolean;
public questionTypeSelectorModel: any;
private dragOrClickHelper: DragOrClickHelper;
@property({ defaultValue: "" }) currentAddQuestionType: string;
@property({ defaultValue: null }) dragTypeOverMe: DragTypeOverMeEnum;
private updateDragTypeOverMe() {
Expand All @@ -38,6 +40,7 @@ export class PageAdorner extends SurveyElementAdornerBase<PageModel> {
}
);
this.attachElement(page);
this.dragOrClickHelper = new DragOrClickHelper(this.startDragSurveyElement);
}

protected get dragInsideCollapsedContainer(): boolean {
Expand Down Expand Up @@ -184,21 +187,30 @@ export class PageAdorner extends SurveyElementAdornerBase<PageModel> {

get css(): string {
let result = super.getCss();
if (!!this.dragTypeOverMe && this.showPlaceholder) {
result = "svc-question__content--drag-over-inside";
} else if (!!this.dragTypeOverMe && this.page.elements.length === 0 && this.creator.survey.pages.length > 0) {
result = "svc-page--drag-over-empty";
if (!!this.creator && !this.creator.showAddQuestionButton) {
result += " svc-page--drag-over-empty-no-add-button";
if (this.dragDropHelper.draggedElement && this.dragDropHelper.draggedElement.isPage) {
if (this.dragTypeOverMe === DragTypeOverMeEnum.Top) {
result += " svc-question__content--drag-over-top";
}
if (this.dragTypeOverMe === DragTypeOverMeEnum.Bottom) {
result += " svc-question__content--drag-over-bottom";
}
}
if (!!this.dragTypeOverMe && this.collapsed) {
this.dragIn();
result += " svc-page__content--collapsed-drag-over-inside";
} else {
this.dragOut();
if (!!this.dragTypeOverMe && this.showPlaceholder) {
result = "svc-question__content--drag-over-inside";
} else if (!!this.dragTypeOverMe && this.page.elements.length === 0 && this.creator.survey.pages.length > 0) {
result = "svc-page--drag-over-empty";
if (!!this.creator && !this.creator.showAddQuestionButton) {
result += " svc-page--drag-over-empty-no-add-button";
}
}
if (!!this.dragTypeOverMe && this.collapsed) {
this.dragIn();
result += " svc-page__content--collapsed-drag-over-inside";
} else {
this.dragOut();
}
}
if(this.allowExpandCollapse) {
if (this.allowExpandCollapse) {
result += (" svc-page__content--collapse-" + this.creator.expandCollapseButtonVisibility);
if (this.renderedCollapsed) result += (" svc-page__content--collapsed");
if (this.expandCollapseAnimationRunning) result += (" svc-page__content--animation-running");
Expand Down Expand Up @@ -262,8 +274,21 @@ export class PageAdorner extends SurveyElementAdornerBase<PageModel> {
return null;
}
public onPageSelected() {
if(this.rootElement) {
if (this.rootElement) {
SurveyHelper.scrollIntoViewIfNeeded(this.rootElement);
}
}
private get dragDropHelper(): DragDropSurveyElements {
return this.creator.dragDropSurveyElements;
}
onPointerDown(pointerDownEvent: PointerEvent) {
this.dragOrClickHelper.onPointerDown(pointerDownEvent);
}
startDragSurveyElement = (event: PointerEvent) => {
const element = <any>this.surveyElement;
const isElementSelected = this.creator.selectedElement === element;
this.dragDropHelper.startDragSurveyElement(event, element, isElementSelected);
this.creator.expandCollapseManager.updateCollapsed(true);
return true;
}
}
1 change: 0 additions & 1 deletion packages/survey-creator-core/src/components/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export interface QuestionBannerParams {
}

export class QuestionAdornerViewModel extends SurveyElementAdornerBase {
@property() isDragged: boolean;
@property({ defaultValue: "" }) currentAddQuestionType: string;

placeholderComponent: string;
Expand Down
16 changes: 10 additions & 6 deletions packages/survey-creator-core/src/expand-collapse-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ export class ExpandCollapseManager {
creator.onSurfaceToolbarActionExecuted.add((_, options) => {
const isCollapseAction = options.action.id == "collapseAll";
const isExpandAction = options.action.id == "expandAll";
if(isCollapseAction || isExpandAction) {
for(let i = this.adorners.length - 1; i >= 0; i--) {
if (this.adorners[i].allowExpandCollapse) {
this.adorners[i].collapsed = isCollapseAction;
}
}
if (isCollapseAction || isExpandAction) {
this.updateCollapsed(isCollapseAction);
}
});
}
private adorners: Array<SurveyElementAdornerBase> = [];
public updateCollapsed(isCollapsed: boolean) {
for (let i = this.adorners.length - 1; i >= 0; i--) {
if (this.adorners[i].allowExpandCollapse) {
this.adorners[i].collapsed = isCollapsed;
}
}
}

public add(adorner: SurveyElementAdornerBase) {
this.adorners.push(adorner);
}
Expand Down
25 changes: 13 additions & 12 deletions packages/survey-creator-core/src/survey-elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ export function calculateIsSide(dropTargetNode: HTMLElement, clientX: number) {
const rect = dropTargetNode.getBoundingClientRect();
return clientX - rect.left <= DragDropSurveyElements.edgeHeight || rect.right - clientX <= DragDropSurveyElements.edgeHeight;
}
export function calculateDragOverLocation(clientX: number, clientY: number, dropTargetNode: HTMLElement): DragTypeOverMeEnum {
export function calculateDragOverLocation(clientX: number, clientY: number, dropTargetNode: HTMLElement, dragBeforeOrAfterOnly = false): DragTypeOverMeEnum {
const rect = dropTargetNode.getBoundingClientRect();
const tg = rect.height / rect.width;
const dx = clientX - rect.x;
const dy = clientY - rect.y;

if (!settings.dragDrop.allowDragToTheSameLine) {
if (dragBeforeOrAfterOnly) {
if (dy >= rect.height / 2) {
return DragTypeOverMeEnum.Bottom;
} else {
Expand Down Expand Up @@ -61,6 +61,9 @@ export class DragDropSurveyElements extends DragDropCore<any> {
protected dragOverLocation: DragTypeOverMeEnum;

protected get draggedElementType(): string {
if (!!this.draggedElement && this.draggedElement.isPage) {
return "survey-page";
}
return "survey-element";
}
protected isDraggedElementSelected: boolean = false;
Expand Down Expand Up @@ -255,15 +258,6 @@ export class DragDropSurveyElements extends DragDropCore<any> {
return oldPage && oldPage !== newPage;
}

private getPanelDropTarget(HTMLElement: HTMLElement, dropTarget: any, event: PointerEvent) {
if (dropTarget.questions.length !== 0) {
HTMLElement = this.findDeepestDropTargetChild(HTMLElement);
dropTarget = this.getDropTargetByNode(HTMLElement, event);
}

return dropTarget;
}

protected findDeepestDropTargetChild(parent: HTMLElement): HTMLElement {
const selector = this.dropTargetDataAttributeName;

Expand Down Expand Up @@ -362,7 +356,7 @@ export class DragDropSurveyElements extends DragDropCore<any> {
const dropTarget = this.getDropTargetByNode(dropTargetNode, event);

if (!!oldInsideContainer != !!this.insideContainer) dropTarget.dragTypeOverMe = null;
let dragOverLocation = calculateDragOverLocation(event.clientX, event.clientY, dropTargetNode);
let dragOverLocation = calculateDragOverLocation(event.clientX, event.clientY, dropTargetNode, !settings.dragDrop.allowDragToTheSameLine || (!!this.draggedElement && this.draggedElement.isPage));
if (dropTarget && ((dropTarget.isPanel || dropTarget.isPage) && dropTarget.elements.length === 0 || isPanelDynamic(dropTarget) && dropTarget.template.elements.length == 0)) {
if (dropTarget.isPage || this.insideContainer) {
dragOverLocation = DragTypeOverMeEnum.InsideEmptyPanel;
Expand Down Expand Up @@ -406,6 +400,13 @@ export class DragDropSurveyElements extends DragDropCore<any> {
const dragged = this.draggedElement;
const src = this.draggedElement;

if (dragged.isPage && dragged instanceof PageModel) {
const survey = dragged.survey;
survey.pages.splice(survey.pages.indexOf(dragged), 1);
survey.pages.splice(survey.pages.indexOf(this.dropTarget) + (this.dragOverLocation === DragTypeOverMeEnum.Top ? 0 : 1), 0, dragged);
return dragged;
}

const convertLocation = () => {
switch (this.dragOverLocation) {
case 4: return "top";
Expand Down
3 changes: 0 additions & 3 deletions packages/survey-creator-react/src/ImageItemValueWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ export class ImageItemValueAdornerComponent extends CreatorModelElement<
}

render(): JSX.Element {
// if (this.model.question.isDragged) {
// return null;
// }
this.model.item = this.props.item;
const isNew = !this.props.question.isItemInList(this.props.item);
this.model.isNew = isNew;
Expand Down
3 changes: 0 additions & 3 deletions packages/survey-creator-react/src/ItemValueWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ export class ItemValueAdornerComponent extends CreatorModelElement<
}

render(): JSX.Element {
// if (this.model.question.isDragged) {
// return null;
// }
this.model.item = this.props.item;
const button = this.model.allowAdd ? (
attachKey2click(<span
Expand Down
4 changes: 0 additions & 4 deletions packages/survey-creator-react/src/adorners/CellQuestion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ export class CellQuestionAdornerComponent extends CreatorModelElement<
return ["question", "componentData"];
}
render(): JSX.Element {
if (this.model.isDragged) {
return null;
}

return (
<React.Fragment>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ export class CellQuestionDropdownAdornerComponent extends CreatorModelElement<
return this.model;
}
render(): JSX.Element {
if (this.model.isDragged) {
return null;
}
const question = this.props.question as QuestionSelectBase;
const textStyle = (this.props.question as any).textStyle;
return (
Expand Down
18 changes: 16 additions & 2 deletions packages/survey-creator-react/src/adorners/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,18 @@ export class CreatorSurveyPageComponent extends CreatorModelElement<
return (
attachKey2click(<div
ref={this.rootRef}
className={"svc-page__content " + this.model.css}
id={this.props.page.id}
data-sv-drop-target-survey-page={!this.model.isGhost ? this.model.page.name || null : null}
className={"svc-page__content " + this.model.css}
onClick={(e) => {
return this.model.select(this.model, new ReactMouseEvent(e));
}}
onDoubleClick={e => this.model.dblclick(e.nativeEvent)}
onMouseLeave={(e) => this.model.hover(e.nativeEvent, e.currentTarget)}
onMouseOver={(e) => this.model.hover(e.nativeEvent, e.currentTarget)}
>
<div className="svc-question__drop-indicator svc-question__drop-indicator--top"></div>
<div className="svc-question__drop-indicator svc-question__drop-indicator--bottom"></div>
{this.renderHeader()}
{this.renderContent()}
{this.renderPlaceholder()}
Expand All @@ -108,9 +111,20 @@ export class CreatorSurveyPageComponent extends CreatorModelElement<
return (<SurveyPage page={this.props.page} survey={this.props.survey} creator={this.props.creator} css={this.model.css}></SurveyPage>);
}
protected renderHeader(): JSX.Element {
return (<div className="svc-page__content-actions">
const actions = (<div className="svc-page__content-actions">
<SurveyActionBar model={this.model.actionContainer}></SurveyActionBar>
</div>);
if (this.model.isGhost) {
return actions;
}
return (
<div className={"svc-question__drag-area"}
onPointerDown={(event: any) => this.model.onPointerDown(event)}
>
<SvgIcon className="svc-question__drag-element" size={24} iconName={"icon-drag-area-indicator_24x16"}></SvgIcon>
{actions}
</div>
);
}
protected renderFooter(): JSX.Element {
return <SurveyActionBar model={this.model.footerActionsBar}></SurveyActionBar>;
Expand Down
3 changes: 0 additions & 3 deletions packages/survey-creator-react/src/adorners/Question.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ export class QuestionAdornerComponent extends CreatorModelElement<
protected getStateElement(): Base {
return this.model;
}
protected canRender(): boolean {
return super.canRender() && !this.model.isDragged;
}

renderElement(): JSX.Element {
const allowInteractions = this.model.element
Expand Down

0 comments on commit 5593764

Please sign in to comment.