• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 13331632524

14 Feb 2025 02:51PM CUT coverage: 22.015% (-69.6%) from 91.622%
13331632524

Pull #15372

github

web-flow
Merge d52d57714 into bcb78ae0a
Pull Request #15372: chore(*): test ci passing

1990 of 15592 branches covered (12.76%)

431 of 964 new or added lines in 18 files covered. (44.71%)

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

249.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

8.86
/projects/igniteui-angular/src/lib/select/select-positioning-strategy.ts
1
import { VerticalAlignment, HorizontalAlignment, PositionSettings, Size, Util, ConnectedFit, Point  } from '../services/overlay/utilities';
2
import { IPositionStrategy } from '../services/overlay/position';
3

4
import { IgxSelectBase } from './select.common';
5
import { BaseFitPositionStrategy } from '../services/overlay/position/base-fit-position-strategy';
6
import { PlatformUtil } from '../core/utils';
7
import { Optional } from '@angular/core';
8
import { fadeIn, fadeOut } from 'igniteui-angular/animations';
9

10
/** @hidden @internal */
11
export class SelectPositioningStrategy extends BaseFitPositionStrategy implements IPositionStrategy {
12
    private _selectDefaultSettings = {
94✔
13
        horizontalDirection: HorizontalAlignment.Right,
14
        verticalDirection: VerticalAlignment.Bottom,
15
        horizontalStartPoint: HorizontalAlignment.Left,
16
        verticalStartPoint: VerticalAlignment.Top,
17
        openAnimation: fadeIn,
18
        closeAnimation: fadeOut
19
    };
20

21
    // Global variables required for cases of !initialCall (page scroll/overlay repositionAll)
22
    private global_yOffset = 0;
94✔
23
    private global_xOffset = 0;
94✔
24
    private global_styles: SelectStyles = {};
94✔
25

26
    constructor(public select: IgxSelectBase, settings?: PositionSettings, @Optional() protected platform?: PlatformUtil) {
94✔
27
        super();
94✔
28
        this.settings = Object.assign({}, this._selectDefaultSettings, settings);
94✔
29
    }
30

31
    /**
32
     * Position the element based on the PositionStrategy implementing this interface.
33
     *
34
     * @param contentElement The HTML element to be positioned
35
     * @param size Size of the element
36
     * @param document reference to the Document object
37
     * @param initialCall should be true if this is the initial call to the method
38
     * @param target attaching target for the component to show
39
     * ```typescript
40
     * settings.positionStrategy.position(content, size, document, true);
41
     * ```
42
     */
43
    public override position(contentElement: HTMLElement,
44
                    size: Size,
45
                    document?: Document,
46
                    initialCall?: boolean,
47
                    target?: Point | HTMLElement): void {
UNCOV
48
        const targetElement = target;
×
UNCOV
49
        const rects = super.calculateElementRectangles(contentElement, targetElement);
×
50
        // selectFit obj, to be used for both cases of initialCall and !initialCall(page scroll/overlay repositionAll)
UNCOV
51
        const selectFit: SelectFit = {
×
52
            verticalOffset: this.global_yOffset,
53
            horizontalOffset: this.global_xOffset,
54
            targetRect: rects.targetRect,
55
            contentElementRect: rects.elementRect,
56
            styles: this.global_styles,
57
            scrollContainer: this.select.scrollContainer,
58
            scrollContainerRect: this.select.scrollContainer.getBoundingClientRect()
59
        };
60

UNCOV
61
        if (initialCall) {
×
UNCOV
62
            this.select.scrollContainer.scrollTop = 0;
×
63
            // Fill in the required selectFit object properties.
UNCOV
64
            selectFit.viewPortRect = Util.getViewportRect(document);
×
UNCOV
65
            selectFit.itemElement = this.getInteractionItemElement();
×
UNCOV
66
            selectFit.itemRect = selectFit.itemElement.getBoundingClientRect();
×
67

68
            // Calculate input and selected item elements style related variables
UNCOV
69
            selectFit.styles = this.calculateStyles(selectFit, targetElement);
×
70

UNCOV
71
            selectFit.scrollAmount = this.calculateScrollAmount(selectFit);
×
72
            // Calculate how much to offset the overlay container.
UNCOV
73
            this.calculateYoffset(selectFit);
×
UNCOV
74
            this.calculateXoffset(selectFit);
×
75

UNCOV
76
            super.updateViewPortFit(selectFit);
×
77
            // container does not fit in viewPort and is out on Top or Bottom
UNCOV
78
            if (selectFit.fitVertical.back < 0 || selectFit.fitVertical.forward < 0) {
×
UNCOV
79
                this.fitInViewport(contentElement, selectFit);
×
80
            }
81
            // Calculate scrollTop independently of the dropdown, as we cover all `igsSelect` specific positioning and
82
            // scrolling to selected item scenarios here.
UNCOV
83
            this.select.scrollContainer.scrollTop = selectFit.scrollAmount;
×
84
        }
UNCOV
85
        this.setStyles(contentElement, selectFit);
×
86
    }
87

88
    /**
89
     * Obtain the selected item if there is such one or otherwise use the first one
90
     */
91
    public getInteractionItemElement(): HTMLElement {
92
        let itemElement;
UNCOV
93
        if (this.select.selectedItem) {
×
UNCOV
94
            itemElement = this.select.selectedItem.element.nativeElement;
×
95
        } else {
UNCOV
96
            itemElement = this.select.getFirstItemElement();
×
97
        }
UNCOV
98
        return itemElement;
×
99
    }
100

101
    /**
102
     * Position the items outer container so selected item text is positioned over input text and if header
103
     * And/OR footer - both header/footer are visible
104
     *
105
     * @param selectFit selectFit to use for computation.
106
     */
107
    protected fitInViewport(contentElement: HTMLElement, selectFit: SelectFit) {
UNCOV
108
        const footer = selectFit.scrollContainerRect.bottom - selectFit.contentElementRect.bottom;
×
UNCOV
109
        const header = selectFit.scrollContainerRect.top - selectFit.contentElementRect.top;
×
UNCOV
110
        const lastItemFitSize = selectFit.targetRect.bottom + selectFit.styles.itemTextToInputTextDiff - footer;
×
UNCOV
111
        const firstItemFitSize = selectFit.targetRect.top - selectFit.styles.itemTextToInputTextDiff - header;
×
112
        // out of viewPort on Top
UNCOV
113
        if (selectFit.fitVertical.back < 0) {
×
UNCOV
114
            const possibleScrollAmount = selectFit.scrollContainer.scrollHeight -
×
115
                selectFit.scrollContainerRect.height - selectFit.scrollAmount;
UNCOV
116
            if (possibleScrollAmount + selectFit.fitVertical.back > 0 && firstItemFitSize > selectFit.viewPortRect.top) {
×
UNCOV
117
                selectFit.scrollAmount -= selectFit.fitVertical.back;
×
UNCOV
118
                selectFit.verticalOffset -= selectFit.fitVertical.back;
×
UNCOV
119
                this.global_yOffset = selectFit.verticalOffset;
×
120
            } else {
UNCOV
121
                selectFit.verticalOffset = 0 ;
×
UNCOV
122
                this.global_yOffset = 0;
×
123
            }
124
        // out of viewPort on Bottom
UNCOV
125
        } else if (selectFit.fitVertical.forward < 0) {
×
UNCOV
126
            if (selectFit.scrollAmount + selectFit.fitVertical.forward > 0 && lastItemFitSize < selectFit.viewPortRect.bottom) {
×
UNCOV
127
                selectFit.scrollAmount += selectFit.fitVertical.forward;
×
UNCOV
128
                selectFit.verticalOffset += selectFit.fitVertical.forward;
×
UNCOV
129
                this.global_yOffset = selectFit.verticalOffset;
×
130
            } else {
UNCOV
131
                selectFit.verticalOffset = -selectFit.contentElementRect.height + selectFit.targetRect.height;
×
UNCOV
132
                this.global_yOffset = selectFit.verticalOffset;
×
133
            }
134
        }
135
    }
136

137
    /**
138
     * Sets element's style which effectively positions the provided element
139
     *
140
     * @param element Element to position
141
     * @param selectFit selectFit to use for computation.
142
     * @param initialCall should be true if this is the initial call to the position method calling setStyles
143
     */
144
    protected setStyles(contentElement: HTMLElement, selectFit: SelectFit) {
UNCOV
145
        super.setStyle(contentElement, selectFit.targetRect, selectFit.contentElementRect, selectFit);
×
UNCOV
146
        contentElement.style.width = `${selectFit.styles.contentElementNewWidth}px`; // manage container based on paddings?
×
UNCOV
147
        this.global_styles.contentElementNewWidth = selectFit.styles.contentElementNewWidth;
×
148
    }
149

150
    /**
151
     * Calculate selected item scroll position.
152
     */
153
    private calculateScrollAmount(selectFit: SelectFit): number {
UNCOV
154
        const itemElementRect = selectFit.itemRect;
×
UNCOV
155
        const scrollContainer = selectFit.scrollContainer;
×
UNCOV
156
        const scrollContainerRect = selectFit.scrollContainerRect;
×
UNCOV
157
        const scrollDelta = scrollContainerRect.top - itemElementRect.top;
×
UNCOV
158
        let scrollPosition = scrollContainer.scrollTop - scrollDelta;
×
159

UNCOV
160
        const dropDownHeight = scrollContainer.clientHeight;
×
UNCOV
161
        scrollPosition -= dropDownHeight / 2;
×
UNCOV
162
        scrollPosition += itemElementRect.height / 2;
×
163

UNCOV
164
        return Math.round(Math.min(Math.max(0, scrollPosition), scrollContainer.scrollHeight - scrollContainerRect.height));
×
165
    }
166

167
    /**
168
     * Calculate the necessary input and selected item styles to be used for positioning item text over input text.
169
     * Calculate & Set default items container width.
170
     *
171
     * @param selectFit selectFit to use for computation.
172
     */
173
    private calculateStyles(selectFit: SelectFit, target: Point | HTMLElement): SelectStyles  {
UNCOV
174
        const styles: SelectStyles = {};
×
UNCOV
175
        const inputElementStyles = window.getComputedStyle(target as Element);
×
UNCOV
176
        const itemElementStyles = window.getComputedStyle(selectFit.itemElement);
×
UNCOV
177
        const numericInputFontSize = parseFloat(inputElementStyles.fontSize);
×
UNCOV
178
        const numericInputPaddingTop = parseFloat(inputElementStyles.paddingTop);
×
UNCOV
179
        const numericInputPaddingBottom = parseFloat(inputElementStyles.paddingBottom);
×
UNCOV
180
        const numericItemFontSize = parseFloat(itemElementStyles.fontSize);
×
UNCOV
181
        const inputTextToInputTop = ((selectFit.targetRect.bottom - numericInputPaddingBottom)
×
182
            - (selectFit.targetRect.top + numericInputPaddingTop) - numericInputFontSize) / 2;
UNCOV
183
        const itemTextToItemTop = (selectFit.itemRect.height - numericItemFontSize) / 2;
×
UNCOV
184
        styles.itemTextToInputTextDiff = Math.round(itemTextToItemTop - inputTextToInputTop - numericInputPaddingTop);
×
185

UNCOV
186
        const numericLeftPadding = parseFloat(itemElementStyles.paddingLeft);
×
UNCOV
187
        const numericTextIndent = parseFloat(itemElementStyles.textIndent);
×
188

UNCOV
189
        styles.itemTextPadding = numericLeftPadding;
×
UNCOV
190
        styles.itemTextIndent = numericTextIndent;
×
191
        // 24 is the input's toggle ddl icon width
UNCOV
192
        styles.contentElementNewWidth = selectFit.targetRect.width + 24 + numericLeftPadding * 2;
×
193

UNCOV
194
        return styles;
×
195
    }
196

197
    /**
198
     * Calculate how much to offset the overlay container for Y-axis.
199
     */
200
    private calculateYoffset(selectFit: SelectFit) {
UNCOV
201
        selectFit.verticalOffset = -(selectFit.itemRect.top - selectFit.contentElementRect.top +
×
202
            selectFit.styles.itemTextToInputTextDiff - selectFit.scrollAmount);
UNCOV
203
        this.global_yOffset = selectFit.verticalOffset;
×
204
    }
205

206
    /**
207
     * Calculate how much to offset the overlay container for X-axis.
208
     */
209
    private calculateXoffset(selectFit: SelectFit) {
UNCOV
210
        selectFit.horizontalOffset = selectFit.styles.itemTextIndent - selectFit.styles.itemTextPadding;
×
UNCOV
211
        this.global_xOffset = selectFit.horizontalOffset;
×
212
    }
213
}
214

215
/** @hidden */
216
export interface SelectFit extends ConnectedFit {
217
    itemElement?: HTMLElement;
218
    scrollContainer: HTMLElement;
219
    scrollContainerRect: ClientRect;
220
    itemRect?: ClientRect;
221
    styles?: SelectStyles;
222
    scrollAmount?: number;
223
}
224

225
/** @hidden */
226
export interface SelectStyles {
227
    itemTextPadding?: number;
228
    itemTextIndent?: number;
229
    itemTextToInputTextDiff?: number;
230
    contentElementNewWidth?: number;
231
    numericLeftPadding?: number;
232
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc