Skip to content

Commit

Permalink
Popup submenu (#8237)
Browse files Browse the repository at this point in the history
* work for #8129 Popup submenu

* work for #8129 Popup submenu

* work for #8129 Popup submenu

* work for #8129 Popup submenu

* #8129 - allow search in subitems

* work for #8129 Popup submenu

* work for #8129 Popup submenu

* #8129 change child popup position does not fit

* merge "refactoring creation listmodel"

* work for #8129 Popup submenu

* work for #8129 Popup submenu

* work for #8129 Popup submenu

* fix for "refactoring creation listmodel"

* work for #8129 extract list-item-content

* #8129 - fix marker position

* work for #8129 add markup test for list-item-group

* work for #8129 extract list-item-content

* work for #8129 extract list-item-content

* work for #8129 Popup submenu

* work for #8129 Popup submenu

* #8129 - move delayed popup show/hide to library

* #8129 - hover styles

* work for #8129 Popup submenu

* work for #8129 Popup submenu

* work for #8129 Popup submenu

* work for #8129 Popup submenu

* #8129 - add simple setItems unit test

* #8129 - remove creator toolbox classes

* #8129 apply hovered classes only for dropdown popup

* Revert "#8129 apply hovered classes only for dropdown popup"

This reverts commit 0806e0a.

* work for #8129 Popup submenu

* #8129 - remove skipped test

* #8129 - fix overlay popup test

* work for #8129 Popup submenu - f-test

* #8229 - support popup area

* work for #8129 Popup submenu - f-test

* work for #8129 Popup submenu - f-test

* work for #8129 Popup submenu - visual test

* #8229 - fix for support popup area

---------

Co-authored-by: OlgaLarina <olga.larina.dev@gmail.com>
Co-authored-by: Aleksey Novikov <novikov82@gmail.com>
Co-authored-by: Aleksey Novikov <novikov@abrisplatform.com>
  • Loading branch information
4 people committed May 30, 2024
1 parent d1520cd commit d7934ca
Show file tree
Hide file tree
Showing 51 changed files with 1,018 additions and 270 deletions.
6 changes: 4 additions & 2 deletions packages/survey-angular-ui/src/angular-ui.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import { SurveyHeaderComponent } from "./components/survey-header/survey-header.
import { DynamicHeadComponent } from "./components/element-title/dynamic-head.component";
import { ListComponent } from "./components/list/list.component";
import { ListItemComponent } from "./components/list/list-item.component";
import { ListItemContentComponent } from "./components/list/list-item-content.component";
import { ListItemGroupComponent } from "./components/list/list-item-group.component";
import { RatingItemComponent } from "./components/rating/rating-item.component";
import { RatingItemStarComponent } from "./components/rating/rating-item-star.component";
import { RatingItemSmileyComponent } from "./components/rating/rating-item-smiley.component";
Expand Down Expand Up @@ -129,7 +131,7 @@ import { SvgBundleComponent } from "./svgbundle.component";
QuestionSkeletonComponent, TextQuestionComponent, RadiogroupComponent, RadiogroupItemComponent, CheckboxComponent, CheckboxItemComponent,
DropdownComponent, DropdownQuestionComponent, DropdownSelectComponent, DropdownOptionItemComponent,
PopupComponent, PopupBaseContainerComponent, PopupPointerComponent,
CharacterCounterComponent, ListComponent, ListItemComponent, RatingItemComponent, RatingItemStarComponent, RatingItemSmileyComponent,
CharacterCounterComponent, ListComponent, ListItemComponent, ListItemContentComponent, ListItemGroupComponent, RatingItemComponent, RatingItemStarComponent, RatingItemSmileyComponent,
TagboxFilterComponent, TagboxComponent, TagboxQuestionComponent, TagboxItemComponent,
ActionBarComponent, ActionComponent, ActionBarItemComponent, ActionBarItemDropdownComponent, HtmlQuestionComponent,
SelectBaseItemComponent, SelectBaseComponent, SurveyCommentComponent, SurveyCommentOtherComponent, ElementHeaderComponent, ElementTitleActionsComponent, ElementTitleComponent, DynamicHeadComponent, RowComponent,
Expand All @@ -151,7 +153,7 @@ import { SvgBundleComponent } from "./svgbundle.component";
CharacterCounterComponent,
DropdownComponent, DropdownQuestionComponent, DropdownSelectComponent, DropdownOptionItemComponent,
PopupComponent, PopupBaseContainerComponent, PopupPointerComponent,
CharacterCounterComponent, ListComponent, ListItemComponent, RatingItemComponent, RatingItemStarComponent, RatingItemSmileyComponent,
CharacterCounterComponent, ListComponent, ListItemComponent, ListItemContentComponent, ListItemGroupComponent, RatingItemComponent, RatingItemStarComponent, RatingItemSmileyComponent,
TagboxFilterComponent, TagboxComponent, TagboxQuestionComponent, TagboxItemComponent,
ActionBarComponent, ActionComponent, ActionBarItemComponent, ActionBarItemDropdownComponent, HtmlQuestionComponent,
SelectBaseItemComponent, SelectBaseComponent, SurveyCommentComponent, SurveyCommentOtherComponent, ElementHeaderComponent, ElementTitleComponent, DynamicHeadComponent, RowComponent,
Expand Down
2 changes: 2 additions & 0 deletions packages/survey-angular-ui/src/angular-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export * from "./components/notifier/notifier.component";
export * from "./components/element-title/dynamic-head.component";
export * from "./components/list/list.component";
export * from "./components/list/list-item.component";
export * from "./components/list/list-item-content.component";
export * from "./components/list/list-item-group.component";
export * from "./components/rating/rating-item.component";
export * from "./components/rating/rating-item-star.component";
export * from "./components/rating/rating-item-smiley.component";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<ng-template #template>
<svg *ngIf="model.iconName" [class]="listModel.cssClasses.itemIcon" [iconName]="model.iconName" [size]="model.iconSize"
sv-ng-svg-icon></svg>
<sv-ng-string [model]="model.locTitle"></sv-ng-string>
<svg *ngIf="model.markerIconName" [class]="model.cssClasses.itemMarkerIcon" [iconName]="model.markerIconName" [size]="model.markerIconSize"
sv-ng-svg-icon></svg>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Component, Input } from "@angular/core";
import { ListModel, Action } from "survey-core";
import { BaseAngular } from "../../base-angular";
import { AngularComponentFactory } from "../../component-factory";

@Component({
selector: "sv-ng-list-item-content, '[sv-ng-list-item-content]'",
templateUrl: "./list-item-content.component.html",
styleUrls: ["../../hide-host.scss"],
})

export class ListItemContentComponent extends BaseAngular {
@Input() element: any;
@Input() model!: Action;
@Input() listModel!: ListModel;

getModel() {
return this.model;
}
}

AngularComponentFactory.Instance.registerComponent("sv-list-item-content", ListItemContentComponent);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<ng-template #template>
<sv-ng-list-item-content [model]="model" [listModel]="listModel"></sv-ng-list-item-content>
<sv-ng-popup [popupModel]="model.popupModel"></sv-ng-popup>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Component, Input } from "@angular/core";
import { ListModel, Action } from "survey-core";
import { BaseAngular } from "../../base-angular";
import { AngularComponentFactory } from "../../component-factory";

@Component({
selector: "sv-ng-list-item-group, '[sv-ng-list-item-group]'",
templateUrl: "./list-item-group.component.html",
styleUrls: ["../../hide-host.scss"],
})

export class ListItemGroupComponent extends BaseAngular {
@Input() element: any;
@Input() model!: Action;
@Input() listModel!: ListModel;

getModel() {
return this.model;
}
}

AngularComponentFactory.Instance.registerComponent("sv-list-item-group", ListItemGroupComponent);
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@
<ng-container *ngIf="model.needSeparator">
<div [class]="listModel.cssClasses.itemSeparator"></div>
</ng-container>
<div [class]="listModel.cssClasses.itemBody" [style.paddingInlineStart]="paddingLeft" [attr.title]="model.locTitle.calculatedText">
<ng-container *ngIf="!model.component">
<svg *ngIf="model.iconName" [class]="listModel.cssClasses.itemIcon" [iconName]="model.iconName" [size]="model.iconSize"
sv-ng-svg-icon></svg>
<sv-ng-string [model]="model.locTitle"></sv-ng-string>
</ng-container>
<ng-container *ngIf="model.component">
<ng-template [component]="{ name: model.component, data: { model: model } }"></ng-template>
</ng-container>
<div [class]="listModel.cssClasses.itemBody" [style.paddingInlineStart]="paddingLeft" [attr.title]="model.locTitle.calculatedText"
(mouseover)="listModel.onItemHover(model)"
(mouseleave)="listModel.onItemLeave(model)">
<ng-template [component]="{ name: model.component || 'sv-list-item-content', data: { model: model, listModel: listModel } }"></ng-template>
</div>
</li>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { PopupBaseViewModel, PopupModel, createPopupViewModel } from "survey-cor
export class PopupComponent extends BaseAngular<PopupModel> {
@Input() popupModel!: PopupModel;
@Input() getTarget?: (container: HTMLElement) => HTMLElement;
@Input() getArea?: (container: HTMLElement) => HTMLElement;
@ViewChild("containerRef") containerRef!: ElementRef<HTMLDivElement>;

public model!: PopupBaseViewModel;
Expand All @@ -31,7 +32,9 @@ export class PopupComponent extends BaseAngular<PopupModel> {
ngAfterViewInit(): void {
if (!!this.containerRef?.nativeElement) {
const container = this.containerRef.nativeElement as HTMLElement;
this.model.setComponentElement(container, this.getTarget ? this.getTarget(container.parentElement as HTMLElement) : container?.parentElement?.parentElement);
this.model.setComponentElement(container,
this.getTarget ? this.getTarget(container.parentElement as HTMLElement) : container?.parentElement?.parentElement,
this.getArea ? this.getArea(container.parentElement as HTMLElement) : undefined);
}
}
override ngOnInit() {
Expand Down
11 changes: 3 additions & 8 deletions packages/survey-vue3-ui/src/components/list/ListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,10 @@
:style="{ paddingInlineStart: model.getItemIndent(item) }"
v-bind:class="model.cssClasses.itemBody"
:title="item.locTitle.calculatedText"
@mouseover="(e) => model.onItemHover(item)"
@mouseleave="(e) => model.onItemLeave(item)"
>
<sv-svg-icon
v-if="item.iconName && !item.component"
v-bind:class="model.cssClasses.itemIcon"
:iconName="item.iconName"
:size="item.iconSize"
></sv-svg-icon>
<survey-string v-if="!item.component" :locString="item.locTitle" />
<component v-if="item.component" :is="item.component" :item="item">
<component :is="item.component || 'sv-list-item-content'" :item="item" :model="model">
</component>
</div>
</li>
Expand Down
24 changes: 24 additions & 0 deletions packages/survey-vue3-ui/src/components/list/ListItemContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<sv-svg-icon
v-if="item.iconName"
v-bind:class="model.cssClasses.itemIcon"
:iconName="item.iconName"
:size="item.iconSize"
></sv-svg-icon>
<survey-string :locString="item.locTitle" />
<sv-svg-icon
v-if="item.markerIconName"
v-bind:class="item.cssClasses.itemMarkerIcon"
:iconName="item.markerIconName"
:size="item.markerIconSize"
></sv-svg-icon>
</template>

<script lang="ts" setup>
import { useBase } from "@/base";
import type { ListModel, Action, IAction } from "survey-core";
const props = defineProps<{ model: ListModel; item: Action }>();
useBase(() => props.item);
</script>
13 changes: 13 additions & 0 deletions packages/survey-vue3-ui/src/components/list/ListItemGroup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<sv-list-item-content :item="item" :model="model"></sv-list-item-content>
<sv-popup :model="item.popupModel"></sv-popup>
</template>

<script lang="ts" setup>
import { useBase } from "@/base";
import type { ListModel, Action } from "survey-core";
const props = defineProps<{ model: ListModel; item: Action }>();
useBase(() => props.item);
</script>
4 changes: 3 additions & 1 deletion packages/survey-vue3-ui/src/components/popup/Popup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PopupModel, createPopupViewModel } from "survey-core";
import { shallowRef, ref, onMounted, watch, onUnmounted } from "vue";
const props = defineProps<{
getTarget?: (el: HTMLElement) => HTMLElement;
getArea?: (el: HTMLElement) => HTMLElement;
model: PopupModel;
}>();
const popupViewModel = shallowRef();
Expand All @@ -30,7 +31,8 @@ onMounted(() => {
const container = root.value;
popupViewModel.value.setComponentElement(
container,
props.getTarget ? props.getTarget(container) : undefined
props.getTarget ? props.getTarget(container) : undefined,
props.getArea ? props.getArea(container) : undefined
);
});
onUnmounted(() => {
Expand Down
4 changes: 4 additions & 0 deletions packages/survey-vue3-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ import ActionBarSeparator from "./components/action-bar/ActionBarSeparator.vue";

import List from "./components/list/List.vue";
import ListItem from "./components/list/ListItem.vue";
import ListItemContent from "./components/list/ListItemContent.vue";
import ListItemGroup from "./components/list/ListItemGroup.vue";

import Popup from "./components/popup/Popup.vue";
import PopupContainer from "./components/popup/PopupContainer.vue";
Expand Down Expand Up @@ -240,6 +242,8 @@ function registerComponents(app: App) {
app.component("sv-action-bar-separator", ActionBarSeparator);

app.component("sv-list", List);
app.component("sv-list-item-content", ListItemContent);
app.component("sv-list-item-group", ListItemGroup);
app.component("sv-list-item", ListItem);

app.component("sv-popup", Popup);
Expand Down
68 changes: 68 additions & 0 deletions src/actions/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ export interface IAction {
ariaExpanded?: boolean;
ariaRole?: string;
elementId?: string;
items?: Array<IAction>;
markerIconName?: string;
markerIconSize?: number;
showPopup?: () => void;
hidePopup?: () => void;
}

export interface IActionDropdownPopupOptions extends IListModel, IPopupOptionsBase {
Expand Down Expand Up @@ -208,6 +213,7 @@ export function getActionDropdownButtonTarget(container: HTMLElement): HTMLEleme
}

export abstract class BaseAction extends Base implements IAction {
items?: IAction[];
private static renderedId = 1;
private static getNextRendredId(): number { return BaseAction.renderedId++; }
private cssClassesValue: any;
Expand Down Expand Up @@ -235,6 +241,8 @@ export abstract class BaseAction extends Base implements IAction {
public removePriority: number;
@property() iconName: string;
@property({ defaultValue: 24 }) iconSize: number;
@property() markerIconName: string;
@property() markerIconSize: number = 16;
@property() css?: string
minDimension: number;
maxDimension: number;
Expand Down Expand Up @@ -333,6 +341,54 @@ export abstract class BaseAction extends Base implements IAction {
}
return args.isTrusted;
}
public showPopup(): void {
if (!!this.popupModel) {
this.popupModel.show();
}
}
public hidePopup(): void {
if (!!this.popupModel) {
this.popupModel.hide();
}
}

@property({ defaultValue: false }) isPressed: boolean;
@property({ defaultValue: false }) isHovered: boolean;

private showPopupTimeout: NodeJS.Timeout;
private hidePopupTimeout: NodeJS.Timeout;
private clearPopupTimeouts() {
if (this.showPopupTimeout) clearTimeout(this.showPopupTimeout);
if (this.hidePopupTimeout) clearTimeout(this.hidePopupTimeout);
}
public showPopupDelayed(delay: number) {

this.clearPopupTimeouts();
this.showPopupTimeout = setTimeout(() => {
this.clearPopupTimeouts();

this.showPopup();

}, delay);
}

public hidePopupDelayed(delay: number) {
if (this.popupModel?.isVisible) {

this.clearPopupTimeouts();
this.hidePopupTimeout = setTimeout(() => {
this.clearPopupTimeouts();

this.hidePopup();
this.isHovered = false;

}, delay);
} else {
this.clearPopupTimeouts();
this.isHovered = false;
}
}

protected abstract getEnabled(): boolean;
protected abstract setEnabled(val: boolean): void;
protected abstract getVisible(): boolean;
Expand Down Expand Up @@ -372,6 +428,18 @@ export class Action extends BaseAction implements IAction, ILocalizableOwner {
private createLocTitle(): LocalizableString {
return this.createLocalizableString("title", this, true);
}
public setItems(items: Array<IAction>, onSelectionChanged: (item: Action, ...params: any[]) => void): void {
this.markerIconName = "icon-next_16x16";
this.component = "sv-list-item-group";
this.items = [...items];
const popupModel = createPopupModelWithListModel(
{ items: items, onSelectionChanged: onSelectionChanged, searchEnabled: false },
{ horizontalPosition: "right", showPointer: false, canShrink: false }
);
popupModel.cssClass = "sv-popup-inner";
this.popupModel = popupModel;
}

location?: string;
@property() id: string;
@property({
Expand Down
19 changes: 19 additions & 0 deletions src/actions/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,25 @@ export class ActionContainer<T extends BaseAction = Action> extends Base impleme
this.sortItems();
}
}
@property({ defaultValue: 300 }) subItemsShowDelay: number;
@property({ defaultValue: 300 }) subItemsHideDelay: number;
protected popupAfterShowCallback(itemValue: T): void {

}

public mouseOverHandler(itemValue: T): void {
itemValue.isHovered = true;
this.actions.forEach(action => {
if (action === itemValue && !!itemValue.popupModel) {
itemValue.showPopupDelayed(this.subItemsShowDelay);
this.popupAfterShowCallback(itemValue);

} else if (!!action.popupModel && action.popupModel.isVisible) {
action.hidePopupDelayed(this.subItemsHideDelay);
}
});
}

public initResponsivityManager(container: HTMLDivElement, delayedUpdateFunction?: (callback: () => void) => void): void {
return;
}
Expand Down

0 comments on commit d7934ca

Please sign in to comment.