Skip to content

Commit

Permalink
#6038 Implement expand-collapse API (#6057)
Browse files Browse the repository at this point in the history
* #6038 Implement expand-collapse API
Fixes #6038

* fix old test #6038

* fix build #6038

* do not raise event for panels and questions on drag start  #6038

* #6038 - change expanded to collapsed, do not use undefined

* Add descriptions

---------

Co-authored-by: RomanTsukanov <[email protected]>
  • Loading branch information
novikov82 and RomanTsukanov authored Nov 13, 2024
1 parent e108384 commit 4bf73a7
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ export class SurveyElementAdornerBase<T extends SurveyElement = SurveyElement> e
}
};;
protected surveyElement: T
get element() {
return this.surveyElement;
}
constructor(
public creator: SurveyCreatorModel,
surveyElement: T
Expand All @@ -294,7 +297,7 @@ export class SurveyElementAdornerBase<T extends SurveyElement = SurveyElement> e
protected restoreState(): void {
if (!!this.surveyElement) {
const state = this.creator.designerStateManager?.getElementState(this.surveyElement);
this.collapsed = state.collapsed;
this.collapsed = this.creator.getElementExpandCollapseState(this.surveyElement as any, "loading", state.collapsed);
}
this.needToRenderContent = !this.collapsed;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/survey-creator-core/src/components/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ export class PageAdorner extends SurveyElementAdornerBase<PageModel> {
this.dragDropHelper.startDragSurveyElement(event, element, isElementSelected);
if (this.creator.collapsePagesOnDrag) {
this.creator.designerStateManager?.suspend();
this.creator.collapseAllPages();
this.creator.collapseAllElements();
}
return true;
}
Expand Down
3 changes: 0 additions & 3 deletions packages/survey-creator-core/src/components/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@ export class QuestionAdornerViewModel extends SurveyElementAdornerBase {
StringItemsNavigatorBase.setQuestion(this);
}

get element() {
return this.surveyElement;
}
protected canSelectElement(): boolean {
return super.canSelectElement() && this.surveyElement.isInteractiveDesignElement;
}
Expand Down
63 changes: 46 additions & 17 deletions packages/survey-creator-core/src/creator-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ import {
ElementFocusedEvent, OpenFileChooserEvent, UploadFileEvent, TranslationStringVisibilityEvent, TranslationImportItemEvent,
TranslationImportedEvent, TranslationExportItemEvent, MachineTranslateEvent, TranslationItemChangingEvent, DragDropAllowEvent,
CreateCustomMessagePanelEvent, ActiveTabChangingEvent, ActiveTabChangedEvent, BeforeUndoEvent, BeforeRedoEvent,
PageAddingEvent, DragStartEndEvent
PageAddingEvent, DragStartEndEvent,
ElementGetExpandCollapseStateEvent,
ElementGetExpandCollapseStateEventReason
} from "./creator-events-api";
import { ExpandCollapseManager } from "./expand-collapse-manager";
import designTabSurveyThemeJSON from "./designTabSurveyThemeJSON";
Expand Down Expand Up @@ -433,6 +435,10 @@ export class SurveyCreatorModel extends Base
*/
public onHtmlToMarkdown: EventBase<SurveyCreatorModel, HtmlToMarkdownEvent> = this.addCreatorEvent<SurveyCreatorModel, HtmlToMarkdownEvent>();

/*
* An event that is raised when Survey Creator obtains the expand/collapse state of a survey element on the design surface. Handle this event to set a required state.
*/
public onElementGetExpandCollapseState: EventBase<SurveyCreatorModel, ElementGetExpandCollapseStateEvent> = this.addCreatorEvent<SurveyCreatorModel, ElementGetExpandCollapseStateEvent>();
/**
* An event that is raised when Survey Creator obtains permitted operations for a survey element. Use this event to disable user interactions with a question, panel, or page on the design surface.
*
Expand Down Expand Up @@ -1199,7 +1205,7 @@ export class SurveyCreatorModel extends Base
const chaningOptions = { tabName: viewName, allow: allow, model: this.currentPlugin?.model };
this.onActiveTabChanging.fire(this, chaningOptions);
if (!chaningOptions.allow) return;
if(!!this.currentPlugin?.deactivate && !this.currentPlugin.deactivate()) return;
if (!!this.currentPlugin?.deactivate && !this.currentPlugin.deactivate()) return;
const plugin = this.activatePlugin(viewName);
this.viewType = viewName;
this.onActiveTabChanged.fire(this, { tabName: viewName, plugin: plugin, model: !!plugin ? plugin.model : undefined });
Expand Down Expand Up @@ -1923,30 +1929,53 @@ export class SurveyCreatorModel extends Base
this.stopUndoRedoTransaction();
if (this.collapsePagesOnDrag) {
this.designerStateManager?.release();
this.restorePagesState();
this.restoreElementsState();
}
});
}
public get designerStateManager() {
return (this.getPlugin("designer") as TabDesignerPlugin).designerStateManager;
}
public collapseAllPages(): void {
this.survey.pages.forEach(page => {
const pageAdorner = SurveyElementAdornerBase.GetAdorner(page);
if (pageAdorner) {
pageAdorner.collapsed = true;

private getCollapsableElements() {
return this.survey.pages;
}

public collapseAllElements(): void {
this.getCollapsableElements().forEach(element => {
const elementAdorner = SurveyElementAdornerBase.GetAdorner(element);
if (elementAdorner) {
elementAdorner.collapsed = this.getElementExpandCollapseState(element as Question | PageModel | PanelModel, "drag-start", true);
}
});
}
public restorePagesState(): void {
this.survey.pages.forEach(page => {
if(page["draggedFrom"] !== undefined) {
const adorner = SurveyElementAdornerBase.GetAdorner(page);

public getElementExpandCollapseState(element: Question | PageModel | PanelModel, reason: ElementGetExpandCollapseStateEventReason, defaultValue: boolean): boolean {
const options: ElementGetExpandCollapseStateEvent = {
element: element,
reason: reason,
collapsed: defaultValue
};
this.onElementGetExpandCollapseState.fire(this, options);
return options.collapsed;
}

private restoreState(element: SurveyElement) {
const state = this.getElementExpandCollapseState(element as any, "drag-end", undefined);
if (state !== undefined) {
SurveyElementAdornerBase.GetAdorner(element).collapsed = state;
}
SurveyElementAdornerBase.RestoreStateFor(element);
}
public restoreElementsState(): void {
this.getCollapsableElements().forEach(element => {
if (element["draggedFrom"] !== undefined) {
const adorner = SurveyElementAdornerBase.GetAdorner(element);
adorner?.blockAnimations();
SurveyElementAdornerBase.RestoreStateFor(page);
this.restoreState(element);
adorner?.releaseAnimations();
} else {
SurveyElementAdornerBase.RestoreStateFor(page);
this.restoreState(element);
}
});
}
Expand Down Expand Up @@ -2162,12 +2191,12 @@ export class SurveyCreatorModel extends Base
this.initSurveyWithJSON(undefined, clearState);
} else {
let jsonValue = trustJSON ? this.parseJSON(value) : undefined;
if(!trustJSON) {
if (!trustJSON) {
const textWorker = new SurveyTextWorker(value);
if(textWorker.isJsonCorrect) {
if (textWorker.isJsonCorrect) {
jsonValue = this.parseJSON(value);
}
else if(!!textWorker.survey) {
else if (!!textWorker.survey) {
jsonValue = textWorker.survey.toJSON();
}
}
Expand Down
16 changes: 16 additions & 0 deletions packages/survey-creator-core/src/creator-events-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,22 @@ export interface HtmlToMarkdownEvent {
text: string;
}

export type ElementGetExpandCollapseStateEventReason = "loading" | "collapse-all" | "expand-all" | "drag-start" | "drag-end";
export interface ElementGetExpandCollapseStateEvent {
/**
* A survey element (question, panel, or page) whose expand/collapse state you can switch.
*/
element: Question | PanelModel | PageModel;
/**
* Indicates whether the element is currently collapsed or expanded. Set this parameter to `true` if you want to collapse the element or `false` to expand it.
*/
collapsed: boolean;
/**
* A value that indicates what caused the event to raise: the loading of a survey JSON schema, a click on the Expand All or Collapse All button, or the beginning or end of a drag and drop operation.
*/
reason: ElementGetExpandCollapseStateEventReason;
}

export interface ElementAllowOperationsEvent {
/**
* A survey element (question or panel) for which you can disable user interactions.
Expand Down
10 changes: 9 additions & 1 deletion packages/survey-creator-core/src/creator-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,14 @@ export interface ICreatorOptions {
*/
pageEditMode?: "standard" | "single" | "bypage";
enableLinkFileEditor?: boolean;

/*
* Specifies the visibility of the buttons that expand and collapse survey elements on the design surface.
*
* Possible values:
*
* - `"onhover"` (default) - Displays an expand/collapse button when a survey element is hovered over or selected.
* - `"always"` - Displays the expand/collapse buttons permanently.
* - `"never"` - Hides the expand/collapse buttons.
*/
expandCollapseButtonVisibility?: "never" | "onhover" | "always";
}
5 changes: 3 additions & 2 deletions packages/survey-creator-core/src/expand-collapse-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CreatorBase } from "./creator-base";
import { SurveyElementAdornerBase } from "./components/action-container-view-model";

export class ExpandCollapseManager {
constructor(creator: CreatorBase) {
constructor(private creator: CreatorBase) {
creator.onSurfaceToolbarActionExecuted.add((_, options) => {
const isCollapseAction = options.action.id == "collapseAll";
const isExpandAction = options.action.id == "expandAll";
Expand All @@ -15,7 +15,8 @@ export class ExpandCollapseManager {
public updateCollapsed(isCollapsed: boolean) {
for (let i = this.adorners.length - 1; i >= 0; i--) {
if (this.adorners[i].allowExpandCollapse) {
this.adorners[i].collapsed = isCollapsed;
const reason = isCollapsed ? "collapse-all" : "expand-all";
this.adorners[i].collapsed = this.creator.getElementExpandCollapseState(this.adorners[i].element as any, reason, isCollapsed);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/survey-creator-core/tests/question-adorner.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -986,13 +986,13 @@ test("Don't reset collapased state for moved question", () => {
const page1 = creator.survey.pages[0];
const page2 = creator.survey.pages[1];
let pageAdorner = new PageAdorner(creator, page1);
creator.collapseAllPages();
creator.collapseAllElements();
expect(pageAdorner.collapsed).toBeTruthy();
creator.designerStateManager.suspend();
creator.survey.pages.splice(0, 1);
creator.survey.pages.splice(1, 0, page1);
pageAdorner = new PageAdorner(creator, page1);
creator.designerStateManager.release();
creator.restorePagesState();
creator.restoreElementsState();
expect(pageAdorner.collapsed).toBeTruthy();
});
96 changes: 95 additions & 1 deletion packages/survey-creator-core/tests/tabs/designer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SurveyModel, ILocalizableOwner, LocalizableString, Serializer, JsonObjectProperty, QuestionMatrixDynamicModel, RegexValidator, IAnimationGroupConsumer, PageModel } from "survey-core";
import { SurveyModel, ILocalizableOwner, LocalizableString, Serializer, JsonObjectProperty, QuestionMatrixDynamicModel, RegexValidator, IAnimationGroupConsumer, PageModel, settings as surveySettings } from "survey-core";
import { editorLocalization } from "../../src/editorLocalization";
import { StringEditorViewModelBase } from "../../src/components/string-editor";
import { CreatorTester } from "../creator-tester";
Expand Down Expand Up @@ -432,3 +432,97 @@ test("check pages animation", () => {
expect(options.deletedItems).toEqual([]);
expect(options.mergedItems).toEqual([p1, p2]);
});
test("expand/collapse event - loading", () => {
surveySettings.animationEnabled = false;
const creator = new CreatorTester();
creator.expandCollapseButtonVisibility = "onhover";
creator.JSON = {
"pages": [
{
"name": "page1",
"elements": [
{
"type": "text",
"name": "question1"
}
]
},
{
"name": "page2",
"elements": [
{
"type": "panel",
"name": "panel1"
}
]
}
]
};
var designerPlugin = <TabDesignerPlugin>(
creator.getPlugin("designer")
);

creator.onElementGetExpandCollapseState.add((_, o) => {
if (o.reason == "loading") {
if (o.element.name == "page2") o.collapsed = false;
if (o.element.name == "question1") o.collapsed = true;
}
if (o.reason == "collapse-all") {
if (o.element.name == "page2") o.collapsed = true;
if (o.element.name == "question1") o.collapsed = false;
}
if (o.reason == "expand-all") {
if (o.element.name == "page1") o.collapsed = false;
if (o.element.name == "page2") o.collapsed = false;
if (o.element.name == "question1") o.collapsed = true;
}
if (o.reason == "drag-start") {
if (o.element.name == "page1") o.collapsed = true;
if (o.element.name == "page2") o.collapsed = false;
if (o.element.name == "panel1") o.collapsed = true;
}
if (o.reason == "drag-end") {
if (o.element.name == "page1") o.collapsed = false;
if (o.element.name == "page2") o.collapsed = false;
if (o.element.name == "panel1") o.collapsed = true;
}
});

const page1Adorner = new PageAdorner(creator, creator.survey.pages[0]);
const page2Adorner = new PageAdorner(creator, creator.survey.pages[1]);
const questionAdorner = new QuestionAdornerViewModel(creator, creator.survey.getAllQuestions()[0], undefined);
const panelAdorner = new QuestionAdornerViewModel(creator, creator.survey.getAllPanels()[0] as any, undefined);

expect(page1Adorner.collapsed).toBeFalsy();
expect(page2Adorner.collapsed).toBeFalsy();
expect(questionAdorner.collapsed).toBeTruthy();
expect(panelAdorner.collapsed).toBeFalsy();

const collapseAll = designerPlugin.model.actionContainer.getActionById("collapseAll");
collapseAll.action(collapseAll);

expect(page1Adorner.collapsed).toBeTruthy();
expect(page2Adorner.collapsed).toBeTruthy();
expect(questionAdorner.collapsed).toBeFalsy();
expect(panelAdorner.collapsed).toBeTruthy();

const expandAll = designerPlugin.model.actionContainer.getActionById("expandAll");
expandAll.action(expandAll);

expect(page1Adorner.collapsed).toBeFalsy();
expect(page2Adorner.collapsed).toBeFalsy();
expect(questionAdorner.collapsed).toBeTruthy();
expect(panelAdorner.collapsed).toBeFalsy();

creator.collapseAllElements();
expect(page1Adorner.collapsed).toBeTruthy();
expect(page2Adorner.collapsed).toBeFalsy();
expect(questionAdorner.collapsed).toBeTruthy();
expect(panelAdorner.collapsed).toBeFalsy();

creator.restoreElementsState();
expect(page1Adorner.collapsed).toBeFalsy();
expect(page2Adorner.collapsed).toBeFalsy();
expect(questionAdorner.collapsed).toBeTruthy();
expect(panelAdorner.collapsed).toBeFalsy();
});

0 comments on commit 4bf73a7

Please sign in to comment.