Skip to content

Commit

Permalink
#8229 - support popup area
Browse files Browse the repository at this point in the history
  • Loading branch information
novikov82 committed May 28, 2024
1 parent d159389 commit fdd42b2
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 11 deletions.
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
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
2 changes: 1 addition & 1 deletion src/knockout/components/popup/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ ko.components.register("sv-popup", {
createViewModel: (params: any, componentInfo: any) => {
const container = componentInfo.element.nodeType === Node.COMMENT_NODE ? componentInfo.element.nextElementSibling : componentInfo.element;
const viewModel = createPopupViewModel(ko.unwrap(params.model));
viewModel.setComponentElement(container, params.getTarget ? params.getTarget(container) : undefined);
viewModel.setComponentElement(container, params.getTarget ? params.getTarget(container) : undefined, params.getArea ? params.getArea(container) : undefined);
return new PopupViewModel(viewModel);
},
},
Expand Down
28 changes: 22 additions & 6 deletions src/popup-dropdown-view-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,20 @@ export class PopupDropdownViewModel extends PopupBaseViewModel {
this.preventScrollOuside(event, this.clientY - event.changedTouches[0].clientY);
}

protected getAvailableAreaRect() {
if (this.areaElement) return this.areaElement.getBoundingClientRect();
return new DOMRect(0, 0, DomWindowHelper.getInnerWidth(), DomWindowHelper.getInnerHeight());
}
protected getTargetElementRect() {
const rect = this.targetElement.getBoundingClientRect();
const areaRect = this.getAvailableAreaRect();
return new DOMRect(rect.left - areaRect.left, rect.top - areaRect.top, rect.width, rect.height);
}

private _updatePosition() {
if (!this.targetElement) return;
const targetElementRect = this.targetElement.getBoundingClientRect();
const targetElementRect = this.getTargetElementRect();
const area = this.getAvailableAreaRect();
const popupContainer = <HTMLElement>this.container?.querySelector(this.containerSelector);
if (!popupContainer) return;
const fixedPopupContainer = <HTMLElement>this.container?.querySelector(this.fixedPopupContainer) as HTMLElement;
Expand All @@ -70,14 +81,14 @@ export class PopupDropdownViewModel extends PopupBaseViewModel {
height,
this.model.horizontalPosition,
this.model.verticalPosition,
DomWindowHelper.getInnerHeight()
area.height
);

actualHorizontalPosition = PopupUtils.updateHorizontalPosition(
targetElementRect,
width,
this.model.horizontalPosition,
DomWindowHelper.getInnerWidth()
area.width
);
}
this.popupDirection = PopupUtils.calculatePopupDirection(
Expand All @@ -97,7 +108,7 @@ export class PopupDropdownViewModel extends PopupBaseViewModel {
const newVerticalDimensions = PopupUtils.getCorrectedVerticalDimensions(
pos.top,
height,
DomWindowHelper.getInnerHeight(),
area.height,
verticalPosition,
this.model.canShrink
);
Expand Down Expand Up @@ -129,6 +140,10 @@ export class PopupDropdownViewModel extends PopupBaseViewModel {
pos.top -= rect.top;
pos.left -= rect.left;
}

pos.left += area.left;
pos.top += area.top;

this.left = pos.left + "px";
this.top = pos.top + "px";

Expand Down Expand Up @@ -181,15 +196,16 @@ export class PopupDropdownViewModel extends PopupBaseViewModel {

private recalculatePositionHandler: (_: any, options: { isResetHeight: boolean }) => void;

constructor(model: PopupModel, public targetElement?: HTMLElement) {
constructor(model: PopupModel, public targetElement?: HTMLElement, public areaElement?: HTMLElement) {
super(model);
this.model.onRecalculatePosition.add(this.recalculatePositionHandler);
}
public setComponentElement(componentRoot: HTMLElement, targetElement?: HTMLElement | null): void {
public setComponentElement(componentRoot: HTMLElement, targetElement?: HTMLElement | null, areaElement?: HTMLElement | null): void {
super.setComponentElement(componentRoot);

if (!!componentRoot && !!componentRoot.parentElement && !this.isModal) {
this.targetElement = targetElement || componentRoot.parentElement;
this.areaElement = areaElement;
}
}
public resetComponentElement() {
Expand Down
2 changes: 1 addition & 1 deletion src/popup-view-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ export class PopupBaseViewModel extends Base implements IAnimationConsumer {
getElement(settings.environment.popupMountContainer).appendChild(container);
}
}
public setComponentElement(componentRoot: HTMLElement, targetElement?: HTMLElement | null): void {
public setComponentElement(componentRoot: HTMLElement, targetElement?: HTMLElement | null, areaElement?: HTMLElement | null): void {
if (!!componentRoot) {
this.containerElement = componentRoot;
}
Expand Down
3 changes: 2 additions & 1 deletion src/vue/components/popup/popup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { BaseVue } from "../../base";
export class Popup extends BaseVue {
@Prop() model: PopupModel;
@Prop() getTarget?: (container: HTMLElement) => HTMLElement;
@Prop() getArea?: (container: HTMLElement) => HTMLElement;
popupViewModel: PopupBaseViewModel;
protected getModel() {
return this.model;
Expand All @@ -21,7 +22,7 @@ export class Popup extends BaseVue {
}
onMounted() {
const container = (this.$el as HTMLElement) as HTMLElement;
this.popupViewModel.setComponentElement(container, this.getTarget ? this.getTarget(container) : undefined);
this.popupViewModel.setComponentElement(container, this.getTarget ? this.getTarget(container) : undefined, this.getArea ? this.getArea(container) : undefined);
}
destroyed() {
this.popupViewModel.dispose();
Expand Down

0 comments on commit fdd42b2

Please sign in to comment.