diff --git a/src/datepicker/themes/bs/bs-datepicker-container.component.ts b/src/datepicker/themes/bs/bs-datepicker-container.component.ts index 6896bd3864..122166e7f0 100644 --- a/src/datepicker/themes/bs/bs-datepicker-container.component.ts +++ b/src/datepicker/themes/bs/bs-datepicker-container.component.ts @@ -46,7 +46,8 @@ export class BsDatepickerContainerComponent extends BsDatepickerAbstractComponen flip: { enabled: this._config.adaptivePosition } - } + }, + allowedPositions: ['top', 'bottom'] }); this.isOtherMonthsActive = this._config.selectFromOtherMonth; diff --git a/src/datepicker/themes/bs/bs-daterangepicker-container.component.ts b/src/datepicker/themes/bs/bs-daterangepicker-container.component.ts index f0ad25f7ab..dd1f212251 100644 --- a/src/datepicker/themes/bs/bs-daterangepicker-container.component.ts +++ b/src/datepicker/themes/bs/bs-daterangepicker-container.component.ts @@ -49,7 +49,8 @@ export class BsDaterangepickerContainerComponent extends BsDatepickerAbstractCom flip: { enabled: this._config.adaptivePosition } - } + }, + allowedPositions: ['top', 'bottom'] }); this.containerClass = this._config.containerClass; diff --git a/src/dropdown/bs-dropdown.directive.ts b/src/dropdown/bs-dropdown.directive.ts index 9add2fcb3e..a2fea72531 100644 --- a/src/dropdown/bs-dropdown.directive.ts +++ b/src/dropdown/bs-dropdown.directive.ts @@ -240,7 +240,7 @@ export class BsDropdownDirective implements OnInit, OnDestroy { (typeof this.dropup !== 'undefined' && this.dropup); this._state.direction = _dropup ? 'up' : 'down'; const _placement = - this.placement || (_dropup ? 'top left' : 'bottom left'); + this.placement || (_dropup ? 'top start' : 'bottom start'); // show dropdown this._dropdown diff --git a/src/positioning/models/index.ts b/src/positioning/models/index.ts index ee99ee84d9..cb4848abc0 100644 --- a/src/positioning/models/index.ts +++ b/src/positioning/models/index.ts @@ -36,4 +36,5 @@ export interface Options { enabled: boolean; }; }; + allowedPositions?: string[]; } diff --git a/src/positioning/modifiers/flip.ts b/src/positioning/modifiers/flip.ts index 57c5bca651..1f4a5edb01 100644 --- a/src/positioning/modifiers/flip.ts +++ b/src/positioning/modifiers/flip.ts @@ -41,10 +41,7 @@ export function flip(data: Data): Data { const target = data.instance.target; const host = data.instance.host; - const adaptivePosition = variation - ? computeAutoPlacement('auto', offsetsHost, target, host, ['top', 'bottom']) - : computeAutoPlacement('auto', offsetsHost, target, host); - + const adaptivePosition = computeAutoPlacement('auto', offsetsHost, target, host, data.options.allowedPositions); const flipOrder = [placement, adaptivePosition]; /* tslint:disable-next-line: cyclomatic-complexity */ diff --git a/src/positioning/modifiers/initData.ts b/src/positioning/modifiers/initData.ts index ae037a22c9..1ce61ca4b8 100644 --- a/src/positioning/modifiers/initData.ts +++ b/src/positioning/modifiers/initData.ts @@ -11,15 +11,29 @@ export function initData( ): Data { const hostElPosition = getReferenceOffsets(targetElement, hostElement); + + if (!position.match(/^(auto)*\s*(left|right|top|bottom)*$/) + && !position.match(/^(left|right|top|bottom)*\s*(start|end)*$/)) { + /* tslint:disable-next-line: no-parameter-reassignment */ + position = 'auto'; + } + const placementAuto = !!position.match(/auto/g); // support old placements 'auto left|right|top|bottom' - let placement = !!position.match(/auto\s(left|right|top|bottom)/g) - ? position.split(' ')[1] || '' + let placement = position.match(/auto\s(left|right|top|bottom)/) + ? position.split(' ')[1] || 'auto' : position; const targetOffset = getTargetOffsets(targetElement, hostElPosition, placement); - placement = computeAutoPlacement(placement, hostElPosition, targetElement, hostElement); + + placement = computeAutoPlacement( + placement, + hostElPosition, + targetElement, + hostElement, + options ? options.allowedPositions : undefined + ); return { options, diff --git a/src/positioning/modifiers/preventOverflow.ts b/src/positioning/modifiers/preventOverflow.ts index 88a851b216..ed235f9399 100644 --- a/src/positioning/modifiers/preventOverflow.ts +++ b/src/positioning/modifiers/preventOverflow.ts @@ -71,7 +71,10 @@ export function preventOverflow(data: Data) { ? 'primary' : 'secondary'; - data.offsets.target = { ...data.offsets.target, ...(check as any)[side](placement) }; + data.offsets.target = { + ...data.offsets.target, + ...(check as any)[side](placement) + }; }); diff --git a/src/positioning/modifiers/shift.ts b/src/positioning/modifiers/shift.ts index 223d60c725..2190cf4879 100644 --- a/src/positioning/modifiers/shift.ts +++ b/src/positioning/modifiers/shift.ts @@ -12,9 +12,9 @@ export function shift(data: Data): Data { const measurement = isVertical ? 'width' : 'height'; const shiftOffsets = { - left: { [side]: host[side] }, - right: { - [side]: host[side] + host[measurement] - host[measurement] + start: { [side]: host[side] }, + end: { + [side]: host[side] + host[measurement] - target[measurement] } }; diff --git a/src/positioning/ng-positioning.ts b/src/positioning/ng-positioning.ts index 7328ccc555..e93cfcd840 100644 --- a/src/positioning/ng-positioning.ts +++ b/src/positioning/ng-positioning.ts @@ -4,7 +4,7 @@ */ import { Renderer2 } from '@angular/core'; -import { getReferenceOffsets, setAllStyles } from './utils'; +import { getOffsets, getReferenceOffsets, updateContainerClass, setStyles } from './utils'; import { arrow, flip, preventOverflow, shift, initData } from './modifiers'; import { Data, Offsets, Options } from './models'; @@ -54,5 +54,18 @@ export function positionElements( options ); - setAllStyles(data, renderer); + const offsets = getOffsets(data); + + setStyles(targetElement, { + 'will-change': 'transform', + top: '0px', + left: '0px', + transform: `translate3d(${offsets.left}px, ${offsets.top}px, 0px)` + }, renderer); + + if (data.instance.arrow) { + setStyles(data.instance.arrow, data.offsets.arrow, renderer); + } + + updateContainerClass(data, renderer); } diff --git a/src/positioning/utils/computeAutoPlacement.ts b/src/positioning/utils/computeAutoPlacement.ts index 306818b978..a32626feb9 100644 --- a/src/positioning/utils/computeAutoPlacement.ts +++ b/src/positioning/utils/computeAutoPlacement.ts @@ -14,7 +14,7 @@ export function computeAutoPlacement( refRect: Offsets, target: HTMLElement, host: HTMLElement, - allowedPositions: any[] = ['top', 'left', 'bottom', 'right'], + allowedPositions: any[] = ['top', 'bottom', 'right', 'left'], boundariesElement = 'viewport', padding = 0 ) { @@ -52,14 +52,18 @@ export function computeAutoPlacement( .sort((a, b) => b.area - a.area); let filteredAreas: any[] = sortedAreas.filter( - ({ width, height }) => - width >= target.clientWidth && height >= target.clientHeight + ({ width, height }) => { + return width >= target.clientWidth + && height >= target.clientHeight; + } ); - filteredAreas = allowedPositions - .reduce((obj, key) => { - return { ...obj, [key]: filteredAreas[key] }; - }, {}); + filteredAreas = filteredAreas.filter((position: any) => { + return allowedPositions + .some((allowedPosition: string) => { + return allowedPosition === position.key; + }); + }); const computedPlacement: string = filteredAreas.length > 0 ? filteredAreas[0].key @@ -67,7 +71,8 @@ export function computeAutoPlacement( const variation = placement.split(' ')[1]; - target.className = target.className.replace(/auto/g, computedPlacement); + // for tooltip on auto position + target.className = target.className.replace(/bs-tooltip-auto/g, `bs-tooltip-${computedPlacement}`); return computedPlacement + (variation ? `-${variation}` : ''); } diff --git a/src/positioning/utils/index.ts b/src/positioning/utils/index.ts index d20716540d..58b7c8153c 100644 --- a/src/positioning/utils/index.ts +++ b/src/positioning/utils/index.ts @@ -19,5 +19,5 @@ export { getWindowSizes } from './getWindowSizes'; export { isFixed } from './isFixed'; export { isModifierEnabled } from './isModifierEnabled'; export { isNumeric } from './isNumeric'; -export { setAllStyles } from './setAllStyles'; +export { updateContainerClass } from './updateContainerClass'; export { setStyles } from './setStyles'; diff --git a/src/positioning/utils/setAllStyles.ts b/src/positioning/utils/setAllStyles.ts deleted file mode 100644 index e7e15b147d..0000000000 --- a/src/positioning/utils/setAllStyles.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Set the style to the given popper - */ -import { Renderer2 } from '@angular/core'; - -import { Data } from '../models'; -import { getOffsets, setStyles } from './index'; - -export function setAllStyles(data: Data, renderer?: Renderer2): void { - const target = data.instance.target; - - const offsets = getOffsets(data); - - setStyles(target, { - 'will-change': 'transform', - top: '0px', - left: '0px', - transform: `translate3d(${offsets.left}px, ${offsets.top}px, 0px)` - }, renderer); - - if (data.instance.arrow) { - setStyles(data.instance.arrow, data.offsets.arrow, renderer); - } - - if (data.placementAuto) { - if (renderer) { - renderer.setAttribute(target, 'class', - target.className.replace(/bs-popover-auto/g, `bs-popover-${data.placement}`) - ); - renderer.setAttribute(target, 'class', - target.className.replace(/bs-tooltip-auto/g, `bs-tooltip-${data.placement}`) - ); - - renderer.setAttribute(target, 'class', - target.className.replace(/\sauto/g, `\s${data.placement}`) - ); - - if (target.className.match(/popover/g)) { - renderer.addClass(target, 'popover-auto'); - } - - if (target.className.match(/tooltip/g)) { - renderer.addClass(target, 'tooltip-auto'); - } - - - } else { - target.className = target.className.replace(/bs-popover-auto/g, `bs-popover-${data.placement}`); - target.className = target.className.replace(/bs-tooltip-auto/g, `bs-tooltip-${data.placement}`); - target.className = target.className.replace(/\sauto/g, `\s${data.placement}`); - - if (target.className.match(/popover/g)) { - target.classList.add('popover-auto'); - } - - if (target.className.match(/tooltip/g)) { - target.classList.add('tooltip-auto'); - } - } - } - - if (renderer) { - renderer.setAttribute( - target, - 'class', - target.className.replace(/left|right|top|bottom/g, `${data.placement.split(' ')[0]}`) - ); - } else { - target.className = target.className.replace(/left|right|top|bottom/g, `${data.placement.split(' ')[0]}`); - } -} diff --git a/src/positioning/utils/updateContainerClass.ts b/src/positioning/utils/updateContainerClass.ts new file mode 100644 index 0000000000..2be1e657e7 --- /dev/null +++ b/src/positioning/utils/updateContainerClass.ts @@ -0,0 +1,35 @@ +/** + * Update class for the given popper + */ +import { Renderer2 } from '@angular/core'; +import { Data } from '../models'; + +export function updateContainerClass(data: Data, renderer?: Renderer2): void { + const target = data.instance.target; + + let containerClass = target.className; + + if (data.placementAuto) { + containerClass = containerClass.replace(/bs-popover-auto/g, `bs-popover-${data.placement}`); + containerClass = containerClass.replace(/bs-tooltip-auto/g, `bs-tooltip-${data.placement}`); + containerClass = containerClass.replace(/\sauto/g, ` ${data.placement}`); + + if (containerClass.indexOf('popover') !== -1 && containerClass.indexOf('popover-auto') === -1) { + containerClass += ' popover-auto'; + } + + if (containerClass.indexOf('tooltip') !== -1 && containerClass.indexOf('tooltip-auto') === -1) { + containerClass += ' tooltip-auto'; + } + } + + containerClass = containerClass.replace(/left|right|top|bottom/g, `${data.placement.split(' ')[0]}`); + + if (renderer) { + renderer.setAttribute(target, 'class', containerClass); + + return; + } + + target.className = containerClass; +} diff --git a/src/typeahead/typeahead.directive.ts b/src/typeahead/typeahead.directive.ts index 04a87510b4..f48ec14951 100644 --- a/src/typeahead/typeahead.directive.ts +++ b/src/typeahead/typeahead.directive.ts @@ -322,14 +322,15 @@ export class TypeaheadDirective implements OnInit, OnDestroy { flip: { enabled: this.adaptivePosition } - } + }, + allowedPositions: ['top', 'bottom'] }); this._typeahead .attach(TypeaheadContainerComponent) // todo: add append to body, after updating positioning service .to(this.container) - .position({attachment: `${this.dropup ? 'top' : 'bottom'} left`}) + .position({attachment: `${this.dropup ? 'top' : 'bottom'} start`}) .show({ typeaheadRef: this, placement: this.placement,