Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Popup submenu #8237

Merged
merged 48 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
03d9f00
work for #8129 Popup submenu
Apr 22, 2024
624bf38
work for #8129 Popup submenu
Apr 23, 2024
1fb0965
work for #8129 Popup submenu
Apr 24, 2024
47e3d19
work for #8129 Popup submenu
Apr 26, 2024
fbb0142
Merge branch 'master' of github.com:surveyjs/survey-library into issu…
May 2, 2024
12973f4
#8129 - allow search in subitems
novikov82 May 3, 2024
3eca7cf
Merge branch 'master' of github.com:surveyjs/survey-library into issu…
May 6, 2024
23996a0
work for #8129 Popup submenu
May 8, 2024
fe96be4
work for #8129 Popup submenu
May 15, 2024
a6f217e
#8129 change child popup position does not fit
novikov82 May 15, 2024
4359147
Merge branch 'issue/8129-popup-submenu' of github.com:surveyjs/survey…
May 16, 2024
9427fc6
merge "refactoring creation listmodel"
May 16, 2024
bb358b0
work for #8129 Popup submenu
May 16, 2024
87e308c
work for #8129 Popup submenu
May 16, 2024
e9ef712
work for #8129 Popup submenu
May 16, 2024
df4b984
Merge branch 'master' of github.com:surveyjs/survey-library into issu…
May 20, 2024
b12fcbc
fix for "refactoring creation listmodel"
May 20, 2024
9dbdcd1
work for #8129 extract list-item-content
May 20, 2024
d7b3106
#8129 - fix marker position
novikov82 May 21, 2024
6429431
Merge branch 'refactoring-listmodel' of github.com:surveyjs/survey-li…
May 22, 2024
0d53b95
work for #8129 add markup test for list-item-group
May 22, 2024
0d5ead5
work for #8129 extract list-item-content
May 22, 2024
3aedd4f
Merge branch 'master' of github.com:surveyjs/survey-library into issu…
May 22, 2024
2dd98f8
work for #8129 extract list-item-content
May 22, 2024
7a8b280
work for #8129 Popup submenu
May 23, 2024
c8266bf
work for #8129 Popup submenu
May 23, 2024
0d8bfef
#8129 - move delayed popup show/hide to library
novikov82 May 23, 2024
15cc176
#8129 - hover styles
May 23, 2024
ad925c9
Merge branch 'issue/8129-popup-submenu' of github.com:surveyjs/survey…
May 24, 2024
c91d96a
work for #8129 Popup submenu
May 24, 2024
8add434
work for #8129 Popup submenu
May 24, 2024
73f5746
work for #8129 Popup submenu
May 24, 2024
3f31c78
work for #8129 Popup submenu
May 24, 2024
47e2c8e
#8129 - add simple setItems unit test
May 26, 2024
ec8fe1e
#8129 - remove creator toolbox classes
novikov82 May 27, 2024
0806e0a
#8129 apply hovered classes only for dropdown popup
novikov82 May 27, 2024
d02f3e1
Revert "#8129 apply hovered classes only for dropdown popup"
May 27, 2024
eac05b4
work for #8129 Popup submenu
May 27, 2024
d05f94f
#8129 - remove skipped test
novikov82 May 27, 2024
d159389
#8129 - fix overlay popup test
novikov82 May 27, 2024
84ad713
work for #8129 Popup submenu - f-test
May 28, 2024
fdd42b2
#8229 - support popup area
novikov82 May 28, 2024
21cb5a9
Merge branch 'issue/8129-popup-submenu' of github.com:surveyjs/survey…
May 28, 2024
c99d4b4
work for #8129 Popup submenu - f-test
May 28, 2024
35077c0
work for #8129 Popup submenu - f-test
May 28, 2024
94397b5
work for #8129 Popup submenu - visual test
May 29, 2024
be194d4
#8229 - fix for support popup area
May 29, 2024
90304d3
Merge commit 'd1520cd73c0e53c957e8ad257145498bbbe14dc8' into issue/81…
May 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading
Loading