Skip to content

Commit 7b6f8db

Browse files
authored
Create adorner action container on demand (#6619)
* Create adorner action container on demand * FIx unit tests * Refactor visibility actions processing * Fix page adorners visual tests * Fix top container css classes
1 parent f60cab5 commit 7b6f8db

File tree

5 files changed

+135
-49
lines changed

5 files changed

+135
-49
lines changed

packages/survey-creator-core/src/components/action-container-view-model.ts

+61-19
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ export class SurveyElementActionContainer extends AdaptiveActionContainer {
9595

9696
export class SurveyElementAdornerBase<T extends SurveyElement = SurveyElement> extends Base {
9797
public static AdornerValueName = "__sjs_creator_adorner";
98-
public actionContainer: SurveyElementActionContainer;
99-
public topActionContainer: ActionContainer;
10098
protected expandCollapseAction: IAction;
10199
@property({ defaultValue: true }) allowDragging: boolean;
102100
@property({ defaultValue: false }) expandCollapseAnimationRunning: boolean;
@@ -228,20 +226,20 @@ export class SurveyElementAdornerBase<T extends SurveyElement = SurveyElement> e
228226
public get renderedCollapsed(): boolean {
229227
return !!this._renderedCollapsed;
230228
}
231-
protected createActionContainers() {
232-
this.actionContainer = this.createActionContainer();
233-
this.topActionContainer = new ActionContainer();
234-
this.topActionContainer.sizeMode = "small";
229+
protected createTopActionContainer(): ActionContainer {
230+
const actionContainer = new ActionContainer();
231+
actionContainer.sizeMode = "small";
235232
if (this.creator.expandCollapseButtonVisibility != "never") {
236-
this.topActionContainer.setItems([this.expandCollapseAction]);
237-
this.topActionContainer.cssClasses = {
233+
actionContainer.setItems([this.expandCollapseAction]);
234+
actionContainer.cssClasses = {
238235
root: "svc-survey-element-top-toolbar sv-action-bar",
239236
item: "svc-survey-element-top-toolbar__item",
240237
itemIcon: "svc-survey-element-toolbar-item__icon",
241238
itemTitle: "svc-survey-element-toolbar-item__title",
242239
itemTitleWithIcon: "svc-survey-element-toolbar-item__title--with-icon",
243240
};
244241
}
242+
return actionContainer;
245243
}
246244

247245
protected createActionContainer(): SurveyElementActionContainer {
@@ -302,9 +300,33 @@ export class SurveyElementAdornerBase<T extends SurveyElement = SurveyElement> e
302300
) {
303301
super();
304302
this.expandCollapseAction = this.getExpandCollapseAction();
305-
this.createActionContainers();
306303
this.attachToUI(surveyElement);
307304
}
305+
private actionContainerValue: SurveyElementActionContainer;
306+
protected get isActionContainerCreated(): boolean {
307+
return !!this.actionContainerValue;
308+
}
309+
public get actionContainer(): SurveyElementActionContainer {
310+
if (!this.actionContainerValue) {
311+
this.actionContainerValue = this.createActionContainer();
312+
if(this.surveyElement) {
313+
this.updateActionsContainer(this.surveyElement);
314+
this.updateActionsVisibility(false);
315+
}
316+
}
317+
return this.actionContainerValue;
318+
}
319+
private topActionContainerValue: ActionContainer;
320+
public get topActionContainer(): ActionContainer {
321+
if(!this.topActionContainerValue) {
322+
this.topActionContainerValue = this.createTopActionContainer();
323+
if(this.surveyElement) {
324+
this.updateActionsVisibility(true);
325+
}
326+
}
327+
return this.topActionContainerValue;
328+
}
329+
308330
private creatorOnLocaleChanged: (sender: Base, options: any) => void = (_, options) => {
309331
if(this.surveyElement) {
310332
this.updateActionsContainer(this.surveyElement);
@@ -389,13 +411,17 @@ export class SurveyElementAdornerBase<T extends SurveyElement = SurveyElement> e
389411
}
390412
public dispose(): void {
391413
this.detachFromUI();
392-
if (!this.actionContainer.isDisposed) {
393-
this.actionContainer.dispose();
394-
}
414+
this.disposeActions(this.actionContainerValue);
415+
this.disposeActions(this.topActionContainerValue);
395416
super.dispose();
396417
this.sidebarFlyoutModeChangedFunc = undefined;
397418
this.animationCollapsed = undefined;
398419
}
420+
private disposeActions(container: ActionContainer): void {
421+
if(!!container && !container.isDisposed) {
422+
container.dispose();
423+
}
424+
}
399425
protected onElementSelectedChanged(isSelected: boolean): void {
400426
if (!isSelected) return;
401427
this.updateActionsProperties();
@@ -415,18 +441,24 @@ export class SurveyElementAdornerBase<T extends SurveyElement = SurveyElement> e
415441
};
416442
}
417443
protected cleanActionsContainer() {
418-
const actions = this.actionContainer.actions;
419-
this.actionContainer.setItems([]);
444+
const container = this.actionContainerValue;
445+
if(!container) return;
446+
const actions = container.actions;
447+
container.setItems([]);
420448
actions.forEach(action => action.dispose && action.dispose());
421449
}
422450
protected updateActionsContainer(surveyElement: SurveyElement) {
451+
if(!this.actionContainerValue) return;
423452
const actions: Array<Action> = [];
424453
this.buildActions(actions);
425454
this.creator.onElementMenuItemsChanged(surveyElement, actions);
426-
this.actionContainer.setItems(actions);
455+
this.actionContainerValue.setItems(actions);
427456
}
428457
protected updateActionsProperties(): void {
429458
if (this.isDisposed) return;
459+
this.updateActionsPropertiesCore();
460+
}
461+
protected updateActionsPropertiesCore(): void {
430462
this.updateElementAllowOptions(
431463
this.creator.getElementAllowOperations(this.surveyElement),
432464
this.isOperationsAllow()
@@ -456,11 +488,21 @@ export class SurveyElementAdornerBase<T extends SurveyElement = SurveyElement> e
456488
protected isOperationsAllow(): boolean {
457489
return !this.creator.readOnly;
458490
}
491+
private actionVisibilityCache: { [index: string]: boolean } = {};
459492
protected updateActionVisibility(id: string, isVisible: boolean) {
460-
var action = this.getActionById(id);
461-
if (!action) return;
462-
if (action.visible == isVisible) return;
463-
action.visible = isVisible;
493+
var action = this.actionContainerValue?.getActionById(id) || this.topActionContainerValue?.getActionById(id);
494+
if (!action) {
495+
this.actionVisibilityCache[id] = isVisible;
496+
} else {
497+
if (action.visible !== isVisible) {
498+
action.visible = isVisible;
499+
}
500+
}
501+
}
502+
protected updateActionsVisibility(isTop: boolean): void {
503+
for (var key in this.actionVisibilityCache) {
504+
this.updateActionVisibility(key, this.actionVisibilityCache[key]);
505+
}
464506
}
465507
public getActionById(id: string): Action {
466508
return this.actionContainer.getActionById(id) || this.topActionContainer.getActionById(id);

packages/survey-creator-core/src/components/page.ts

+16-13
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export class PageAdorner extends SurveyElementAdornerBase<PageModel> {
4242
}
4343
);
4444
this.dragOrClickHelper = new DragOrClickHelper(this.startDragSurveyElement);
45-
this.actionContainer.alwaysShrink = this.creator.isMobileView;
4645
this.creator.onPropertyChanged.add(this.creatorPropertyChanged);
4746
}
4847
public dispose() {
@@ -174,8 +173,22 @@ export class PageAdorner extends SurveyElementAdornerBase<PageModel> {
174173

175174
protected createActionContainer(): SurveyElementActionContainer {
176175
const container = super.createActionContainer();
176+
container.alwaysShrink = this.creator.isMobileView;
177177
container.sizeMode = "small";
178-
container.cssClasses = {
178+
container.cssClasses = this.containerCssClasses();
179+
container.dotsItem.iconSize = "auto";
180+
container.dotsItem.cssClasses.itemIcon += " svc-page-toolbar-item__icon";
181+
return container;
182+
}
183+
184+
protected createTopActionContainer(): ActionContainer {
185+
const container = super.createTopActionContainer();
186+
container.cssClasses = { ...this.containerCssClasses() };
187+
container.cssClasses.root += " svc-page-toolbar--collapse";
188+
return container;
189+
}
190+
private containerCssClasses(): any {
191+
return {
179192
root: "svc-page-toolbar sv-action-bar",
180193
item: "svc-page-toolbar__item",
181194
itemWithTitle: "svc-page-toolbar__item--with-text",
@@ -185,17 +198,7 @@ export class PageAdorner extends SurveyElementAdornerBase<PageModel> {
185198
itemTitle: "svc-page-toolbar-item__title",
186199
itemTitleWithIcon: "svc-page-toolbar-item__title--with-icon",
187200
};
188-
container.dotsItem.iconSize = "auto";
189-
container.dotsItem.cssClasses.itemIcon += " svc-page-toolbar-item__icon";
190-
return container;
191201
}
192-
193-
protected createActionContainers() {
194-
super.createActionContainers();
195-
this.topActionContainer.cssClasses = { ...this.actionContainer.cssClasses };
196-
this.topActionContainer.cssClasses.root += " svc-page-toolbar--collapse";
197-
}
198-
199202
protected allowExpandCollapseByDblClick(element: any) {
200203
return element.classList.contains("svc-page__content") ||
201204
element.classList.contains("sd-page") ||
@@ -295,7 +298,7 @@ export class PageAdorner extends SurveyElementAdornerBase<PageModel> {
295298
return result.trim();
296299
}
297300
private creatorPropertyChanged = (sender, options) => {
298-
if (options.name === "isMobileView") {
301+
if (options.name === "isMobileView" && this.isActionContainerCreated) {
299302
this.actionContainer.alwaysShrink = options.newValue;
300303
}
301304
}

packages/survey-creator-core/src/components/question.ts

+22-16
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,10 @@ export class QuestionAdornerViewModel extends SurveyElementAdornerBase {
285285
super.attachElement(surveyElement);
286286
if (surveyElement) {
287287
surveyElement.registerFunctionOnPropertyValueChanged("isRequired", (newValue: any) => {
288-
const requiredAction = this.actionContainer.getActionById("isrequired");
289-
this.updateRequiredAction(requiredAction);
288+
if(this.isActionContainerCreated) {
289+
const requiredAction = this.actionContainer.getActionById("isrequired");
290+
this.updateRequiredAction(requiredAction);
291+
}
290292
}, "isRequiredAdorner");
291293
}
292294
}
@@ -305,8 +307,8 @@ export class QuestionAdornerViewModel extends SurveyElementAdornerBase {
305307
}
306308
super.hover(event, element);
307309
}
308-
protected createActionContainers(): void {
309-
super.createActionContainers();
310+
protected createActionContainer(): SurveyElementActionContainer {
311+
const actionContainer = super.createActionContainer();
310312
const defaultCssClasses = {
311313
root: "svc-survey-element-toolbar sv-action-bar",
312314
item: "svc-survey-element-toolbar__item",
@@ -319,21 +321,25 @@ export class QuestionAdornerViewModel extends SurveyElementAdornerBase {
319321
itemTitleWithIcon: "svc-survey-element-toolbar-item__title--with-icon",
320322
};
321323

322-
this.actionContainer.sizeMode = "small";
323-
this.actionContainer.cssClasses = defaultCssClasses;
324-
(<SurveyElementActionContainer>this.actionContainer).dotsItem.css += " svc-survey-element-toolbar__dots-item";
325-
(<SurveyElementActionContainer>this.actionContainer).dotsItem.innerCss += " svc-survey-element-toolbar__item";
324+
actionContainer.sizeMode = "small";
325+
actionContainer.cssClasses = defaultCssClasses;
326+
(<SurveyElementActionContainer>actionContainer).dotsItem.css += " svc-survey-element-toolbar__dots-item";
327+
(<SurveyElementActionContainer>actionContainer).dotsItem.innerCss += " svc-survey-element-toolbar__item";
328+
return actionContainer;
326329
}
327-
public getActionById(id: string): Action {
328-
let res = super.getActionById(id);
329-
if (!res) {
330-
res = this.topActionContainer.getActionById(id);
330+
protected updateActionsPropertiesCore(): void {
331+
super.updateActionsPropertiesCore();
332+
if(this.isActionContainerCreated) {
333+
this.updateActionsLocations();
334+
}
335+
}
336+
protected updateActionsVisibility(isTop: boolean): void {
337+
super.updateActionsVisibility(isTop);
338+
if(!isTop) {
339+
this.updateActionsLocations();
331340
}
332-
return res;
333341
}
334-
protected updateActionsProperties(): void {
335-
if (this.isDisposed) return;
336-
super.updateActionsProperties();
342+
private updateActionsLocations(): void {
337343
const actions = this.actionContainer.visibleActions;
338344
let switchToStartLocation = false;
339345
for (var i = actions.length - 1; i >= 0; i--) {

packages/survey-creator-core/tests/creator-base-1.tests.ts

+36
Original file line numberDiff line numberDiff line change
@@ -2255,6 +2255,42 @@ test("QuestionAdornerViewModel and onElementAllowOperations, allowExpandCollapse
22552255
expect(q2Model.topActionContainer.getActionById("collapse").visible).toBeFalsy();
22562256
});
22572257

2258+
test("QuestionAdornerViewModel actionContainer&topActionContainer on demand", (): any => {
2259+
const creator = new CreatorTester();
2260+
creator.expandCollapseButtonVisibility = "onhover";
2261+
creator.JSON = {
2262+
elements: [
2263+
{ type: "text", name: "q1" },
2264+
{ type: "text", name: "q2" },
2265+
{ type: "text", name: "q3" }
2266+
]
2267+
};
2268+
let counter = 0;
2269+
creator.onElementAllowOperations.add((sender, options) => {
2270+
counter ++;
2271+
});
2272+
const q1Model = new QuestionAdornerViewModel(creator, creator.survey.getAllQuestions()[0], undefined);
2273+
expect(q1Model["actionContainerValue"]).toBeFalsy();
2274+
expect(q1Model["topActionContainerValue"]).toBeFalsy();
2275+
expect(q1Model.topActionContainer).toBeTruthy();
2276+
expect(q1Model["actionContainerValue"]).toBeFalsy();
2277+
expect(q1Model["topActionContainerValue"]).toBeTruthy();
2278+
expect(q1Model.actionContainer).toBeTruthy();
2279+
expect(q1Model["actionContainerValue"]).toBeTruthy();
2280+
expect(q1Model["topActionContainerValue"]).toBeTruthy();
2281+
2282+
const q2Model = new QuestionAdornerViewModel(creator, creator.survey.getAllQuestions()[1], undefined);
2283+
expect(q2Model["actionContainerValue"]).toBeFalsy();
2284+
expect(q2Model["topActionContainerValue"]).toBeFalsy();
2285+
expect(q2Model.actionContainer).toBeTruthy();
2286+
expect(q2Model["actionContainerValue"]).toBeTruthy();
2287+
expect(q2Model["topActionContainerValue"]).toBeFalsy();
2288+
expect(q2Model.topActionContainer).toBeTruthy();
2289+
expect(q2Model["actionContainerValue"]).toBeTruthy();
2290+
expect(q2Model["topActionContainerValue"]).toBeTruthy();
2291+
expect(counter).toEqual(2);
2292+
});
2293+
22582294
test("QuestionAdornerViewModel and onElementAllowOperations on new elements", (): any => {
22592295
const creator = new CreatorTester();
22602296
creator.JSON = {

packages/survey-creator-core/tests/creator-base-2.tests.ts

-1
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,6 @@ test("onElementAllowOperations for pages and allowDragging in page adorner", ():
404404
});
405405

406406
const pageAdorner = new PageAdorner(creator, creator.survey.pages[0]);
407-
408407
expect(creator.allowDragPages).toBeTruthy();
409408
expect(pageAdorner.allowDragging).toBeTruthy();
410409
expect(reason).toHaveLength(1);

0 commit comments

Comments
 (0)