Skip to content

Commit

Permalink
allow add subitems in toolbox (#5745)
Browse files Browse the repository at this point in the history
* work for #5737 Allow to add sub items in toolbox similar to text and rating

* work for #5737 Allow to add sub items in toolbox similar to text and rating

* work for #5737 fix Add new question functionality

* work for #5737 fix Convert question functionality

* work for #5737 update api

* work for #5737 update selector

* work for #5737 fix itemComponent

* work for #5737 update toolboxList

* work for #5737 add unit tests

* #5737 - fixed dots item popup component params

* #5735 - fix classes

* #5680 showSubItems should work in dropdowns
Fixes #5680

* #5737 - fixed subitems selection

* #5737 - add unit tests

* #5737 - fixed fail while conversion to matrix dropdown

* #5737 - fix type conversion

* Describe the new API

* #5737 - fixed conversion inside one type

---------

Co-authored-by: OlgaLarina <[email protected]>
Co-authored-by: Aleksey Novikov <[email protected]>
Co-authored-by: Aleksey Novikov <[email protected]>
Co-authored-by: RomanTsukanov <[email protected]>
  • Loading branch information
5 people authored Aug 15, 2024
1 parent 31b0125 commit 46d0e18
Show file tree
Hide file tree
Showing 21 changed files with 558 additions and 97 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<ng-template #template>
<div [class]="model.cssClasses.root">
<ng-container *ngFor="let item of model.renderedActions">
<svc-toolbox-tool [item]="item" [creator]="toolbox.creator" [parentModel]="model"
[isCompact]="false"></svc-toolbox-tool>
<svc-toolbox-tool [item]="item" [creator]="creator" [parentModel]="model" [isCompact]="false"></svc-toolbox-tool>
</ng-container>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, Input } from "@angular/core";
import { AngularComponentFactory, BaseAngular } from "survey-angular-ui";
import { ListModel } from "survey-core";
import { QuestionToolbox } from "survey-creator-core";
import { CreatorBase } from "survey-creator-core";

@Component({
selector: "svc-toolbox-list ",
Expand All @@ -10,7 +10,7 @@ import { QuestionToolbox } from "survey-creator-core";
})
export class ToolboxListComponent extends BaseAngular<ListModel> {
@Input() model!: ListModel;
@Input() toolbox!: QuestionToolbox;
@Input() creator!: CreatorBase;

getModel() {
return this.model;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<div class="sv-action__content" (pointerdown)="model.onPointerDown($event)"
(mouseover)="model.onMouseOver(item, $event)"
(mouseleave)="model.onMouseLeave(item, $event)">
<ng-template [component]="{ name: item.component || 'svc-toolbox-item', default: 'svc-toolbox-item',
<ng-template [component]="{ name: model.itemComponent, default: 'svc-toolbox-item',
data: { model: item, viewModel: model, creator: creator, isCompact: isCompact } }"></ng-template>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/survey-creator-core/src/components/popup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ survey-creator:not(.svd-simulator-content),
}
}

.sv-popup--dropdown.toolbox-subtypes {
.sv-popup--dropdown.svc-toolbox-subtypes {
.sv-popup__body-content {
background: unset;
}
Expand Down
29 changes: 20 additions & 9 deletions packages/survey-creator-core/src/components/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,9 @@ export class QuestionAdornerViewModel extends SurveyElementAdornerBase {
let lastItem = null;
availableItems.forEach((item: QuestionToolboxItem) => {
const needSeparator = lastItem && item.category != lastItem.category;
const action = this.creator.createIActionBarItemByClass(item, needSeparator, (questionType: string, subtype?: string) => {
if (this.surveyElement.getType() !== questionType) {
this.creator.convertCurrentQuestion(questionType);
}
let propertyName = QuestionToolbox.getSubTypePropertyName(questionType);
if (!!propertyName && !!subtype) this.creator.selectedElement.setPropertyValue(propertyName, subtype);
const action = this.creator.createIActionBarItemByClass(item, needSeparator, (questionType: string, json?: any) => {
const type = json?.type || questionType;
this.creator.convertCurrentQuestion(type, json);
parentAction?.hidePopup();
});
lastItem = item;
Expand Down Expand Up @@ -383,13 +380,26 @@ export class QuestionAdornerViewModel extends SurveyElementAdornerBase {
listModel.setItems(newItems);
listModel.selectedItem = this.getSelectedItem(newItems, this.currentType);

let propertyName = QuestionToolbox.getSubTypePropertyName(this.currentType);
newItems.forEach(action => {
if (action.items?.length > 0) {
const selectedSubItem = action.items.filter(item => item.id === this.element[propertyName])[0];
let selectedSubItem = undefined;
action.items.forEach(item => {
const elementType = this.element.getType();
const toolboxItem = (this.creator.toolbox.getItemByName(action.id) as QuestionToolboxItem).getSubitemByName(item.id);
const json = toolboxItem.json || {};
if (item.id == elementType || json.type == elementType) {
if (!selectedSubItem) selectedSubItem = item;
let jsonIsCorresponded = true;
Object.keys(json).forEach(p => {
if (p != "type" && json[p] != this.element[p]) jsonIsCorresponded = false;
});
if (jsonIsCorresponded) selectedSubItem = item;
}
});
if (selectedSubItem) {
const _listModel = action.popupModel.contentComponentData.model;
_listModel.selectedItem = selectedSubItem;
listModel.selectedItem = action;
}
}
});
Expand All @@ -410,8 +420,9 @@ export class QuestionAdornerViewModel extends SurveyElementAdornerBase {

private createConvertInputType() {
const questionType = this.surveyElement.getType();
if (questionType !== "text" && questionType !== "rating") return null;
const toolboxItem = this.creator.toolbox.items.filter(item => item.id === questionType)[0];
if (!toolboxItem || !toolboxItem.items || toolboxItem.items.length < 1) return null;
if (!toolboxItem || !toolboxItem.hasSubItems) return null;

let propName = QuestionToolbox.getSubTypePropertyName(questionType);
const questionSubType = this.surveyElement.getPropertyValue(propName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@
}
}

.toolbox-subtypes {
.svc-toolbox-subtypes {
.sv-popup__container {
box-shadow: unset;
background: unset;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Base, DragOrClickHelper, ActionContainer, ListModel } from "survey-core";
import { IQuestionToolboxItem, QuestionToolboxItem } from "../../toolbox";
import { Base, DragOrClickHelper, ActionContainer, ListModel, PopupModel, CssClassBuilder } from "survey-core";
import { IQuestionToolboxItem, QuestionToolbox, QuestionToolboxItem } from "../../toolbox";
import { SurveyCreatorModel } from "../../creator-base";
import { DragDropSurveyElements } from "../../survey-elements";
export class ToolboxToolViewModel extends Base {
Expand All @@ -11,6 +11,22 @@ export class ToolboxToolViewModel extends Base {
) {
super();
this.dragOrClickHelper = new DragOrClickHelper(this.startDragToolboxItem);

if (!this.isDotsItem()) {
const popup = item.popupModel as PopupModel;
if (!!popup) {
const className = new CssClassBuilder()
.append(popup.cssClass)
.append("svc-toolbox-subtypes")
.toString();

popup.cssClass = className;
popup.isFocusedContainer = false;
popup.contentComponentName = "svc-toolbox-list";
popup.contentComponentData["creator"] = creator;
popup.isFocusedContent = false;
}
}
}

public click = (event) => {
Expand All @@ -23,6 +39,13 @@ export class ToolboxToolViewModel extends Base {
return this.item as QuestionToolboxItem;
}

public get itemComponent(): string {
if (!!this.creator && !this.creator.toolbox.showSubitems && this.toolboxItem.hasSubItems) {
return QuestionToolbox.defaultItemComponent;
}
return this.item.component || QuestionToolbox.defaultItemComponent;
}

public get allowAdd() {
return !this.creator.readOnly;
}
Expand All @@ -39,7 +62,7 @@ export class ToolboxToolViewModel extends Base {
pointerDownEvent.stopPropagation();

if (!this.allowAdd) return;
if (this.item.id.indexOf("dotsItem-id") === 0) return true; //toolbox responsive popup
if (this.isDotsItem()) return true; //toolbox responsive popup
this.dragOrClickHelper.onPointerDown(pointerDownEvent);

this.toolboxItem.isPressed = true;
Expand All @@ -59,6 +82,10 @@ export class ToolboxToolViewModel extends Base {
return true;
};

private isDotsItem() {
return this.item.id.indexOf("dotsItem-id") === 0;
}

private hidePopup() {
this.toolboxItem.hidePopup();
this.toolboxItem.isHovered = false;
Expand Down
37 changes: 19 additions & 18 deletions packages/survey-creator-core/src/creator-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2294,15 +2294,15 @@ export class SurveyCreatorModel extends Base
}
}

protected convertQuestion(obj: Question, className: string): Question {
protected convertQuestion(obj: Question, className: string, defaultJSON: any = null): Question {
const objJSON = QuestionConverter.getObjJSON(obj, this.getDefaultElementJSON(obj.getType()));
const options: QuestionConvertingEvent = {
sourceQuestion: obj,
targetType: className,
json: objJSON
};
this.onQuestionConverting.fire(this, options);
const newQuestion = <Question>QuestionConverter.convertObject(obj, className, options.json, this.getDefaultElementJSON(className));
const newQuestion = <Question>QuestionConverter.convertObject(obj, className, options.json, defaultJSON || this.getDefaultElementJSON(className), !!defaultJSON);
this.setModified({
type: "QUESTION_CONVERTED",
className: className,
Expand Down Expand Up @@ -3562,13 +3562,13 @@ export class SurveyCreatorModel extends Base
this.showSaveButton = value != null && !this.isAutoSave;
}
@undoRedoTransaction()
public convertCurrentQuestion(newType: string) {
public convertCurrentQuestion(newType: string, defaultJSON: any = null) {
var el = this.selectedElement;
if (!el || el.getType() === newType) return;
if (!el || el.getType() === newType && !defaultJSON) return;
const objType = SurveyHelper.getObjectType(el);
if (objType !== ObjType.Question && objType !== ObjType.Panel) return;
this.addNewElementReason = "ELEMENT_CONVERTED";
el = this.convertQuestion(<Question>el, newType);
el = this.convertQuestion(<Question>el, newType, defaultJSON);
this.selectElement(el, null, "#convertTo button");
}

Expand Down Expand Up @@ -3603,9 +3603,9 @@ export class SurveyCreatorModel extends Base
}
public getQuestionTypeSelectorModel(beforeAdd: (type: string) => void, element?: SurveyElement) {
let panel = !!element && element.isPanel ? <PanelModel>element : null;
const onSelectQuestionType = (questionType: string, subtype?: string) => {
const onSelectQuestionType = (questionType: string, json?: any) => {
this.currentAddQuestionType = questionType;
this.addNewQuestionInPage(beforeAdd, panel, questionType, subtype);
this.addNewQuestionInPage(beforeAdd, panel, questionType, json);
newAction.popupModel.hide();
};
const getActions = () => {
Expand Down Expand Up @@ -3644,24 +3644,25 @@ export class SurveyCreatorModel extends Base
}

@undoRedoTransaction()
public addNewQuestionInPage(beforeAdd: (string) => void, panel: IPanel = null, type: string = null, subtype: string = null) {
public addNewQuestionInPage(beforeAdd: (string) => void, panel: IPanel = null, type: string = null, initJson: any = null) {
if (!type) type = this.currentAddQuestionType;
if (!type) type = settings.designer.defaultAddQuestionType;
beforeAdd(type);
let json = { type: type };
const toolboxItem = this.toolbox.getItemByName(type);
if (!!toolboxItem && !!toolboxItem.json) {
json = toolboxItem.json;
let json = initJson;
if (!json) {
const toolboxItem = this.toolbox.getItemByName(type);
if (!!toolboxItem && !!toolboxItem.json) {
json = toolboxItem.json;
} else {
json = { type: type };
}
}
let newElement = this.createNewElement(json);

let propertyName = QuestionToolbox.getSubTypePropertyName(type);
if (!!propertyName && !!subtype) (newElement as Question).setPropertyValue(propertyName, subtype);

this.clickToolboxItem(newElement, panel, "ADDED_FROM_PAGEBUTTON");
}

createIActionBarItemByClass(item: QuestionToolboxItem, needSeparator: boolean, onSelectQuestionType?: (questionType: string, subtype?: string) => void): Action {
createIActionBarItemByClass(item: QuestionToolboxItem, needSeparator: boolean, onSelectQuestionType?: (questionType: string, json?: any) => void): Action {
const action = new Action({
title: item.title,
id: item.name,
Expand All @@ -3674,13 +3675,13 @@ export class SurveyCreatorModel extends Base
onSelectQuestionType(item.typeName);
};

if (!!item.items && item.items.length > 0) {
if (!!item.items && item.items.length > 0 && this.toolbox.showSubitems) {
const innerItems = item.items.map(i => new Action({
id: i.id,
title: i.title,
action: () => {
action.hidePopup();
onSelectQuestionType(item.typeName, i.id);
onSelectQuestionType(item.typeName, i.json);
}
}));
action.setSubItems({ items: innerItems });
Expand Down
10 changes: 6 additions & 4 deletions packages/survey-creator-core/src/questionconverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,18 @@ export class QuestionConverter {
}
return res;
}
public static convertObject(obj: Question, convertToClass: string, objJSON: any, defaultJSON: any = null): Question {
if (!obj || !obj.parent || convertToClass == obj.getType()) return null;
public static convertObject(obj: Question, convertToClass: string, objJSON: any, defaultJSON: any = null, resetToDefault = false): Question {
if (!obj || !obj.parent || convertToClass == obj.getType() && !defaultJSON) return null;
let newQuestion = !defaultJSON ? QuestionFactory.Instance.createQuestion(convertToClass, obj.name) : undefined;
if(!newQuestion) {
newQuestion = Serializer.createClass(convertToClass, {});
}
newQuestion.name = obj.name;
const json = defaultJSON ? Helpers.createCopy(defaultJSON) : newQuestion.toJSON();
const sourceJSON = resetToDefault ? objJSON : defaultJSON;

const json = sourceJSON ? Helpers.createCopy(sourceJSON) : newQuestion.toJSON();
//const qJson = QuestionConverter.getObjJSON(obj, objJSON);
const qJson = objJSON || {};
const qJson = resetToDefault ? defaultJSON : objJSON || {};
for (let key in qJson) {
json[key] = qJson[key];
}
Expand Down
Loading

0 comments on commit 46d0e18

Please sign in to comment.