mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-15 10:58:31 -05:00
Merge from master
This commit is contained in:
@@ -3,12 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./contextview';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose, toDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Range } from 'vs/base/common/range';
|
||||
|
||||
export interface IAnchor {
|
||||
x: number;
|
||||
@@ -17,17 +15,18 @@ export interface IAnchor {
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export enum AnchorAlignment {
|
||||
export const enum AnchorAlignment {
|
||||
LEFT, RIGHT
|
||||
}
|
||||
|
||||
export enum AnchorPosition {
|
||||
export const enum AnchorPosition {
|
||||
BELOW, ABOVE
|
||||
}
|
||||
|
||||
export interface IDelegate {
|
||||
getAnchor(): HTMLElement | IAnchor;
|
||||
render(container: HTMLElement): IDisposable;
|
||||
focus?(): void;
|
||||
layout?(): void;
|
||||
anchorAlignment?: AnchorAlignment; // default: left
|
||||
anchorPosition?: AnchorPosition; // default: below
|
||||
@@ -54,7 +53,7 @@ export interface ISize {
|
||||
|
||||
export interface IView extends IPosition, ISize { }
|
||||
|
||||
export enum LayoutAnchorPosition {
|
||||
export const enum LayoutAnchorPosition {
|
||||
Before,
|
||||
After
|
||||
}
|
||||
@@ -96,44 +95,54 @@ export function layout(viewportSize: number, viewSize: number, anchor: ILayoutAn
|
||||
}
|
||||
}
|
||||
|
||||
export class ContextView {
|
||||
export class ContextView extends Disposable {
|
||||
|
||||
private static readonly BUBBLE_UP_EVENTS = ['click', 'keydown', 'focus', 'blur'];
|
||||
private static readonly BUBBLE_DOWN_EVENTS = ['click'];
|
||||
|
||||
private $container: Builder;
|
||||
private $view: Builder;
|
||||
private delegate: IDelegate;
|
||||
private toDispose: IDisposable[];
|
||||
private toDisposeOnClean: IDisposable;
|
||||
private container: HTMLElement | null;
|
||||
private view: HTMLElement;
|
||||
private delegate: IDelegate | null;
|
||||
private toDisposeOnClean: IDisposable | null;
|
||||
private toDisposeOnSetContainer: IDisposable;
|
||||
|
||||
constructor(container: HTMLElement) {
|
||||
this.$view = $('.context-view').hide();
|
||||
super();
|
||||
|
||||
this.view = DOM.$('.context-view');
|
||||
|
||||
DOM.hide(this.view);
|
||||
|
||||
this.setContainer(container);
|
||||
|
||||
this.toDispose = [toDisposable(() => {
|
||||
this.setContainer(null);
|
||||
})];
|
||||
|
||||
this.toDisposeOnClean = null;
|
||||
this._register(toDisposable(() => this.setContainer(null)));
|
||||
}
|
||||
|
||||
public setContainer(container: HTMLElement): void {
|
||||
if (this.$container) {
|
||||
this.$container.getHTMLElement().removeChild(this.$view.getHTMLElement());
|
||||
this.$container.off(ContextView.BUBBLE_UP_EVENTS);
|
||||
this.$container.off(ContextView.BUBBLE_DOWN_EVENTS, true);
|
||||
this.$container = null;
|
||||
public setContainer(container: HTMLElement | null): void {
|
||||
if (this.container) {
|
||||
this.toDisposeOnSetContainer = dispose(this.toDisposeOnSetContainer);
|
||||
this.container.removeChild(this.view);
|
||||
this.container = null;
|
||||
}
|
||||
if (container) {
|
||||
this.$container = $(container);
|
||||
this.$view.appendTo(this.$container);
|
||||
this.$container.on(ContextView.BUBBLE_UP_EVENTS, (e: Event) => {
|
||||
this.onDOMEvent(e, <HTMLElement>document.activeElement, false);
|
||||
this.container = container;
|
||||
this.container.appendChild(this.view);
|
||||
|
||||
const toDisposeOnSetContainer: IDisposable[] = [];
|
||||
|
||||
ContextView.BUBBLE_UP_EVENTS.forEach(event => {
|
||||
toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => {
|
||||
this.onDOMEvent(e, <HTMLElement>document.activeElement, false);
|
||||
}));
|
||||
});
|
||||
this.$container.on(ContextView.BUBBLE_DOWN_EVENTS, (e: Event) => {
|
||||
this.onDOMEvent(e, <HTMLElement>document.activeElement, true);
|
||||
}, null, true);
|
||||
|
||||
ContextView.BUBBLE_DOWN_EVENTS.forEach(event => {
|
||||
toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => {
|
||||
this.onDOMEvent(e, <HTMLElement>document.activeElement, true);
|
||||
}, true));
|
||||
});
|
||||
|
||||
this.toDisposeOnSetContainer = combinedDisposable(toDisposeOnSetContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,16 +152,25 @@ export class ContextView {
|
||||
}
|
||||
|
||||
// Show static box
|
||||
this.$view.setClass('context-view').empty().style({ top: '0px', left: '0px' }).show();
|
||||
DOM.clearNode(this.view);
|
||||
this.view.className = 'context-view';
|
||||
this.view.style.top = '0px';
|
||||
this.view.style.left = '0px';
|
||||
DOM.show(this.view);
|
||||
|
||||
// Render content
|
||||
this.toDisposeOnClean = delegate.render(this.$view.getHTMLElement());
|
||||
this.toDisposeOnClean = delegate.render(this.view);
|
||||
|
||||
// Set active delegate
|
||||
this.delegate = delegate;
|
||||
|
||||
// Layout
|
||||
this.doLayout();
|
||||
|
||||
// Focus
|
||||
if (this.delegate.focus) {
|
||||
this.delegate.focus();
|
||||
}
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
@@ -160,21 +178,26 @@ export class ContextView {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.delegate.canRelayout === false) {
|
||||
if (this.delegate!.canRelayout === false) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.delegate.layout) {
|
||||
this.delegate.layout();
|
||||
if (this.delegate!.layout) {
|
||||
this.delegate!.layout!();
|
||||
}
|
||||
|
||||
this.doLayout();
|
||||
}
|
||||
|
||||
private doLayout(): void {
|
||||
// Check that we still have a delegate - this.delegate.layout may have hidden
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get anchor
|
||||
let anchor = this.delegate.getAnchor();
|
||||
let anchor = this.delegate!.getAnchor();
|
||||
|
||||
// Compute around
|
||||
let around: IView;
|
||||
@@ -195,16 +218,18 @@ export class ContextView {
|
||||
around = {
|
||||
top: realAnchor.y,
|
||||
left: realAnchor.x,
|
||||
width: realAnchor.width || 0,
|
||||
height: realAnchor.height || 0
|
||||
width: realAnchor.width || 1,
|
||||
height: realAnchor.height || 2
|
||||
};
|
||||
}
|
||||
|
||||
const viewSize = this.$view.getTotalSize();
|
||||
const anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW;
|
||||
const anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT;
|
||||
const viewSizeWidth = DOM.getTotalWidth(this.view);
|
||||
const viewSizeHeight = DOM.getTotalHeight(this.view);
|
||||
|
||||
const verticalAnchor: ILayoutAnchor = { offset: around.top, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After };
|
||||
const anchorPosition = this.delegate!.anchorPosition || AnchorPosition.BELOW;
|
||||
const anchorAlignment = this.delegate!.anchorAlignment || AnchorAlignment.LEFT;
|
||||
|
||||
const verticalAnchor: ILayoutAnchor = { offset: around.top - window.pageYOffset, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After };
|
||||
|
||||
let horizontalAnchor: ILayoutAnchor;
|
||||
|
||||
@@ -214,14 +239,23 @@ export class ContextView {
|
||||
horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After };
|
||||
}
|
||||
|
||||
const containerPosition = DOM.getDomNodePagePosition(this.$container.getHTMLElement());
|
||||
const top = layout(window.innerHeight, viewSize.height, verticalAnchor) - containerPosition.top;
|
||||
const left = layout(window.innerWidth, viewSize.width, horizontalAnchor) - containerPosition.left;
|
||||
const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) + window.pageYOffset;
|
||||
|
||||
this.$view.removeClass('top', 'bottom', 'left', 'right');
|
||||
this.$view.addClass(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top');
|
||||
this.$view.addClass(anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right');
|
||||
this.$view.style({ top: `${top}px`, left: `${left}px`, width: 'initial' });
|
||||
// if view intersects vertically with anchor, shift it horizontally
|
||||
if (Range.intersects({ start: top, end: top + viewSizeHeight }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) {
|
||||
horizontalAnchor.size = around.width;
|
||||
}
|
||||
|
||||
const left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor);
|
||||
|
||||
DOM.removeClasses(this.view, 'top', 'bottom', 'left', 'right');
|
||||
DOM.addClass(this.view, anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top');
|
||||
DOM.addClass(this.view, anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right');
|
||||
|
||||
const containerPosition = DOM.getDomNodePagePosition(this.container!);
|
||||
this.view.style.top = `${top - containerPosition.top}px`;
|
||||
this.view.style.left = `${left - containerPosition.left}px`;
|
||||
this.view.style.width = 'initial';
|
||||
}
|
||||
|
||||
public hide(data?: any): void {
|
||||
@@ -236,7 +270,7 @@ export class ContextView {
|
||||
this.toDisposeOnClean = null;
|
||||
}
|
||||
|
||||
this.$view.hide();
|
||||
DOM.hide(this.view);
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -248,7 +282,7 @@ export class ContextView {
|
||||
if (this.delegate) {
|
||||
if (this.delegate.onDOMEvent) {
|
||||
this.delegate.onDOMEvent(e, <HTMLElement>document.activeElement);
|
||||
} else if (onCapture && !DOM.isAncestor(<HTMLElement>e.target, this.$container.getHTMLElement())) {
|
||||
} else if (onCapture && !DOM.isAncestor(<HTMLElement>e.target, this.container)) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
@@ -257,6 +291,6 @@ export class ContextView {
|
||||
public dispose(): void {
|
||||
this.hide();
|
||||
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user