@syncfusion/ej2-splitbuttons 29.1.33 → 30.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ej2-splitbuttons.min.js +2 -2
- package/dist/ej2-splitbuttons.umd.min.js +2 -2
- package/dist/ej2-splitbuttons.umd.min.js.map +1 -1
- package/dist/es6/ej2-splitbuttons.es2015.js +3 -1
- package/dist/es6/ej2-splitbuttons.es2015.js.map +1 -1
- package/dist/es6/ej2-splitbuttons.es5.js +3 -1
- package/dist/es6/ej2-splitbuttons.es5.js.map +1 -1
- package/dist/global/ej2-splitbuttons.min.js +2 -2
- package/dist/global/ej2-splitbuttons.min.js.map +1 -1
- package/dist/global/index.d.ts +1 -1
- package/dist/ts/button-group/button-group.d.ts +38 -0
- package/dist/ts/button-group/button-group.ts +84 -0
- package/dist/ts/button-group/index.d.ts +4 -0
- package/dist/ts/button-group/index.ts +4 -0
- package/dist/ts/common/common-model.d.ts +51 -0
- package/dist/ts/common/common.d.ts +95 -0
- package/dist/ts/common/common.ts +218 -0
- package/dist/ts/common/index.d.ts +5 -0
- package/dist/ts/common/index.ts +5 -0
- package/dist/ts/drop-down-button/drop-down-button-model.d.ts +195 -0
- package/dist/ts/drop-down-button/drop-down-button.d.ts +308 -0
- package/dist/ts/drop-down-button/drop-down-button.ts +1110 -0
- package/dist/ts/drop-down-button/index.d.ts +5 -0
- package/dist/ts/drop-down-button/index.ts +5 -0
- package/dist/ts/index.d.ts +8 -0
- package/dist/ts/index.ts +8 -0
- package/dist/ts/progress-button/index.d.ts +5 -0
- package/dist/ts/progress-button/index.ts +5 -0
- package/dist/ts/progress-button/progress-button-model.d.ts +206 -0
- package/dist/ts/progress-button/progress-button.d.ts +323 -0
- package/dist/ts/progress-button/progress-button.ts +689 -0
- package/dist/ts/split-button/index.d.ts +5 -0
- package/dist/ts/split-button/index.ts +5 -0
- package/dist/ts/split-button/split-button-model.d.ts +133 -0
- package/dist/ts/split-button/split-button.d.ts +236 -0
- package/dist/ts/split-button/split-button.ts +535 -0
- package/package.json +9 -9
- package/src/drop-down-button/drop-down-button.js +3 -1
- package/styles/bds-lite.css +1 -7
- package/styles/bds.css +2 -7
- package/styles/bootstrap-dark-lite.css +0 -1
- package/styles/bootstrap-dark.css +0 -1
- package/styles/bootstrap-lite.css +0 -1
- package/styles/bootstrap.css +0 -1
- package/styles/bootstrap4-lite.css +0 -1
- package/styles/bootstrap4.css +3 -1
- package/styles/bootstrap5-dark-lite.css +2 -4
- package/styles/bootstrap5-dark.css +2 -4
- package/styles/bootstrap5-lite.css +2 -4
- package/styles/bootstrap5.3-lite.css +0 -2
- package/styles/bootstrap5.3.css +0 -2
- package/styles/bootstrap5.css +2 -4
- package/styles/drop-down-button/_layout.scss +0 -1
- package/styles/drop-down-button/material3-dark.css +0 -1
- package/styles/drop-down-button/material3.css +0 -1
- package/styles/fabric-dark-lite.css +0 -1
- package/styles/fabric-dark.css +0 -1
- package/styles/fabric-lite.css +0 -1
- package/styles/fabric.css +0 -1
- package/styles/fluent-dark-lite.css +0 -1
- package/styles/fluent-dark.css +1 -1
- package/styles/fluent-lite.css +0 -1
- package/styles/fluent.css +1 -1
- package/styles/fluent2-lite.css +1 -3
- package/styles/fluent2.css +1 -3
- package/styles/highcontrast-light-lite.css +0 -1
- package/styles/highcontrast-light.css +0 -1
- package/styles/highcontrast-lite.css +0 -1
- package/styles/highcontrast.css +0 -1
- package/styles/material-dark-lite.css +0 -1
- package/styles/material-dark.css +0 -1
- package/styles/material-lite.css +0 -1
- package/styles/material.css +0 -1
- package/styles/material3-dark-lite.css +3 -6
- package/styles/material3-dark.css +5 -8
- package/styles/material3-lite.css +3 -6
- package/styles/material3.css +5 -8
- package/styles/split-button/_bigger.scss +7 -1
- package/styles/split-button/_layout.scss +7 -13
- package/styles/split-button/_theme.scss +2 -8
- package/styles/split-button/bds.css +2 -7
- package/styles/split-button/bootstrap-dark.css +0 -1
- package/styles/split-button/bootstrap.css +0 -1
- package/styles/split-button/bootstrap4.css +3 -1
- package/styles/split-button/bootstrap5-dark.css +2 -4
- package/styles/split-button/bootstrap5.3.css +0 -2
- package/styles/split-button/bootstrap5.css +2 -4
- package/styles/split-button/fabric-dark.css +0 -1
- package/styles/split-button/fabric.css +0 -1
- package/styles/split-button/fluent-dark.css +1 -1
- package/styles/split-button/fluent.css +1 -1
- package/styles/split-button/fluent2.css +1 -3
- package/styles/split-button/highcontrast-light.css +0 -1
- package/styles/split-button/highcontrast.css +0 -1
- package/styles/split-button/material-dark.css +0 -1
- package/styles/split-button/material.css +0 -1
- package/styles/split-button/material3-dark.css +5 -7
- package/styles/split-button/material3.css +5 -7
- package/styles/split-button/tailwind-dark.css +2 -7
- package/styles/split-button/tailwind.css +2 -7
- package/styles/split-button/tailwind3.css +1 -3
- package/styles/tailwind-dark-lite.css +1 -7
- package/styles/tailwind-dark.css +2 -7
- package/styles/tailwind-lite.css +1 -7
- package/styles/tailwind.css +2 -7
- package/styles/tailwind3-lite.css +1 -3
- package/styles/tailwind3.css +1 -3
|
@@ -0,0 +1,1110 @@
|
|
|
1
|
+
import { Collection, Event, NotifyPropertyChanges, detach, Property, EventHandler, EmitType, isRippleEnabled, isNullOrUndefined, append, formatUnit, Animation } from '@syncfusion/ej2-base';
|
|
2
|
+
import { addClass, INotifyPropertyChanged, getUniqueID, rippleEffect, getComponent, ChildProperty, Complex, AnimationModel } from '@syncfusion/ej2-base';
|
|
3
|
+
import { attributes, Component, closest, select, KeyboardEventArgs, SanitizeHtmlHelper } from '@syncfusion/ej2-base';
|
|
4
|
+
import { classList, removeClass, compile } from '@syncfusion/ej2-base';
|
|
5
|
+
import { Button } from '@syncfusion/ej2-buttons';
|
|
6
|
+
import { Popup } from '@syncfusion/ej2-popups';
|
|
7
|
+
import { SplitButton } from '../split-button/split-button';
|
|
8
|
+
import { MenuEventArgs, BeforeOpenCloseMenuEventArgs, OpenCloseMenuEventArgs, upDownKeyHandler, DropDownAnimationEffect } from './../common/common';
|
|
9
|
+
import { getModel, SplitButtonIconPosition, Item, setBlankIconStyle } from './../common/common';
|
|
10
|
+
import { ItemModel } from './../common/common-model';
|
|
11
|
+
import { DropDownButtonModel, DropDownMenuAnimationSettingsModel } from './drop-down-button-model';
|
|
12
|
+
|
|
13
|
+
const classNames: ClassNames = {
|
|
14
|
+
DISABLED: 'e-disabled',
|
|
15
|
+
FOCUS: 'e-focused',
|
|
16
|
+
ICON: 'e-menu-icon',
|
|
17
|
+
ITEM: 'e-item',
|
|
18
|
+
POPUP: 'e-dropdown-popup',
|
|
19
|
+
RTL: 'e-rtl',
|
|
20
|
+
SEPARATOR: 'e-separator',
|
|
21
|
+
VERTICAL: 'e-vertical',
|
|
22
|
+
POPUPWIDTH: 'e-dropdown-popup-width'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Animation configuration settings.
|
|
27
|
+
*/
|
|
28
|
+
export class DropDownMenuAnimationSettings extends ChildProperty<DropDownMenuAnimationSettings> {
|
|
29
|
+
/**
|
|
30
|
+
* Specifies the animation effect applied when the DropDownMenu is shown.
|
|
31
|
+
* The possible effects are:
|
|
32
|
+
* * None: Specifies that the DropDownMenu appears without any animation effect.
|
|
33
|
+
* * SlideDown: Specifies that the DropDownMenu appears with a slide down effect.
|
|
34
|
+
* * ZoomIn: Specifies that the DropDownMenu appears with a zoom in effect.
|
|
35
|
+
* * FadeIn: Specifies that the DropDownMenu appears with a fade in effect.
|
|
36
|
+
*
|
|
37
|
+
* @default 'SlideDown'
|
|
38
|
+
* @isEnumeration true
|
|
39
|
+
*/
|
|
40
|
+
@Property('SlideDown')
|
|
41
|
+
public effect: DropDownAnimationEffect;
|
|
42
|
+
/**
|
|
43
|
+
* Specifies the time duration (in milliseconds) of the animation effect when the DropDownMenu is displayed.
|
|
44
|
+
*
|
|
45
|
+
* @default 400
|
|
46
|
+
*/
|
|
47
|
+
@Property(400)
|
|
48
|
+
public duration: number;
|
|
49
|
+
/**
|
|
50
|
+
* Specifies the easing function applied during the animation effect of the DropDownMenu.
|
|
51
|
+
*
|
|
52
|
+
* @default 'ease'
|
|
53
|
+
*/
|
|
54
|
+
@Property('ease')
|
|
55
|
+
public easing: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* DropDownButton component is used to toggle contextual overlays for displaying list of action items.
|
|
60
|
+
* It can contain both text and images.
|
|
61
|
+
* ```html
|
|
62
|
+
* <button id="element">DropDownButton</button>
|
|
63
|
+
* ```
|
|
64
|
+
* ```typescript
|
|
65
|
+
* <script>
|
|
66
|
+
* var dropDownButtonObj = new DropDownButton({items: [{ text: 'Action1' }, { text: 'Action2' },{ text: 'Action3' }]);
|
|
67
|
+
* dropDownButtonObj.appendTo("#element");
|
|
68
|
+
* </script>
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
@NotifyPropertyChanges
|
|
72
|
+
export class DropDownButton extends Component<HTMLButtonElement> implements INotifyPropertyChanged {
|
|
73
|
+
/** @hidden */
|
|
74
|
+
public dropDown: Popup;
|
|
75
|
+
protected button: Button;
|
|
76
|
+
/** @hidden */
|
|
77
|
+
public activeElem: HTMLElement[];
|
|
78
|
+
private rippleFn: Function;
|
|
79
|
+
private delegateMousedownHandler: Function;
|
|
80
|
+
private isPopupCreated: boolean = true;
|
|
81
|
+
private popupContent: HTMLElement;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Defines the content of the DropDownButton element that can either be a text or HTML elements.
|
|
85
|
+
*
|
|
86
|
+
* @default ""
|
|
87
|
+
*/
|
|
88
|
+
@Property('')
|
|
89
|
+
public content: string;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Defines class/multiple classes separated by a space in the DropDownButton element. The
|
|
93
|
+
* DropDownButton size and styles can be customized by using this.
|
|
94
|
+
*
|
|
95
|
+
* @default ""
|
|
96
|
+
*/
|
|
97
|
+
@Property('')
|
|
98
|
+
public cssClass: string;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Specifies a value that indicates whether the DropDownButton is `disabled` or not.
|
|
102
|
+
*
|
|
103
|
+
* @default false.
|
|
104
|
+
*/
|
|
105
|
+
@Property(false)
|
|
106
|
+
public disabled: boolean;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Defines class/multiple classes separated by a space for the DropDownButton that is used to
|
|
110
|
+
* include an icon. DropDownButton can also include font icon and sprite image.
|
|
111
|
+
*
|
|
112
|
+
* @default ""
|
|
113
|
+
*/
|
|
114
|
+
@Property('')
|
|
115
|
+
public iconCss: string;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Positions the icon before/top of the text content in the DropDownButton. The possible values are:
|
|
119
|
+
* * Left: The icon will be positioned to the left of the text content.
|
|
120
|
+
* * Top: The icon will be positioned to the top of the text content.
|
|
121
|
+
*
|
|
122
|
+
* @default "Left"
|
|
123
|
+
*/
|
|
124
|
+
@Property('Left')
|
|
125
|
+
public iconPosition: SplitButtonIconPosition;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Specifies whether to enable the rendering of untrusted HTML values in the DropDownButton component.
|
|
129
|
+
* If 'enableHtmlSanitizer' set to true, the component will sanitize any suspected untrusted strings and scripts before rendering them.
|
|
130
|
+
*
|
|
131
|
+
* @default true
|
|
132
|
+
*/
|
|
133
|
+
@Property(true)
|
|
134
|
+
public enableHtmlSanitizer: boolean;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Specifies action items with its properties which will be rendered as DropDownButton popup.
|
|
138
|
+
*
|
|
139
|
+
* @default []
|
|
140
|
+
*/
|
|
141
|
+
@Collection<ItemModel>([], Item)
|
|
142
|
+
public items: ItemModel[];
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Specifies the popup element creation on open.
|
|
146
|
+
*
|
|
147
|
+
* @default false
|
|
148
|
+
*/
|
|
149
|
+
@Property(false)
|
|
150
|
+
public createPopupOnClick: boolean;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Allows to specify the DropDownButton popup item element.
|
|
154
|
+
*
|
|
155
|
+
* @default ""
|
|
156
|
+
*/
|
|
157
|
+
@Property('')
|
|
158
|
+
public target: string | Element;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Specifies the event to close the DropDownButton popup.
|
|
162
|
+
*
|
|
163
|
+
* @default ""
|
|
164
|
+
*/
|
|
165
|
+
@Property('')
|
|
166
|
+
public closeActionEvents: string;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Specifies the template content to be displayed.
|
|
170
|
+
*
|
|
171
|
+
* @default null
|
|
172
|
+
* @aspType string
|
|
173
|
+
*/
|
|
174
|
+
@Property(null)
|
|
175
|
+
public itemTemplate: string | Function;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* This property defines the width of the dropdown popup for the DropDownButton component.
|
|
179
|
+
*
|
|
180
|
+
* @property {string | number} popupWidth - A string or number representing the width of the dropdown.
|
|
181
|
+
* It can be a valid CSS unit such as `px`, `%`, or `rem`, or a number interpreted as pixels.
|
|
182
|
+
* @default "auto"
|
|
183
|
+
* @remarks
|
|
184
|
+
* The `popupWidth` property allows developers to control the width of the dropdown popup, ensuring it fits their design requirements.
|
|
185
|
+
* The default value of `auto` allows the popup to adjust based on the content length, but a specific width can be provided for more precise control.
|
|
186
|
+
*/
|
|
187
|
+
@Property('auto')
|
|
188
|
+
public popupWidth: string | number;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Specifies the animation settings for opening the sub menu in the DropDownMenu.
|
|
192
|
+
* The settings control the duration, easing, and effect of the animation applied when the sub menu opens.
|
|
193
|
+
*
|
|
194
|
+
* @default { effect: 'None' }
|
|
195
|
+
*/
|
|
196
|
+
@Complex<DropDownMenuAnimationSettingsModel>({ effect: 'None' }, DropDownMenuAnimationSettings)
|
|
197
|
+
public animationSettings: DropDownMenuAnimationSettingsModel;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Triggers while rendering each Popup item of DropDownButton.
|
|
201
|
+
*
|
|
202
|
+
* @event beforeItemRender
|
|
203
|
+
*/
|
|
204
|
+
@Event()
|
|
205
|
+
public beforeItemRender: EmitType<MenuEventArgs>;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Triggers before opening the DropDownButton popup.
|
|
209
|
+
*
|
|
210
|
+
* @event beforeOpen
|
|
211
|
+
*/
|
|
212
|
+
@Event()
|
|
213
|
+
public beforeOpen: EmitType<BeforeOpenCloseMenuEventArgs>;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Triggers before closing the DropDownButton popup.
|
|
217
|
+
*
|
|
218
|
+
* @event beforeClose
|
|
219
|
+
*/
|
|
220
|
+
@Event()
|
|
221
|
+
public beforeClose: EmitType<BeforeOpenCloseMenuEventArgs>;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Triggers while closing the DropDownButton popup.
|
|
225
|
+
*
|
|
226
|
+
* @event close
|
|
227
|
+
*/
|
|
228
|
+
@Event()
|
|
229
|
+
public close: EmitType<OpenCloseMenuEventArgs>;
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Triggers while opening the DropDownButton popup.
|
|
233
|
+
*
|
|
234
|
+
* @event open
|
|
235
|
+
*/
|
|
236
|
+
@Event()
|
|
237
|
+
public open: EmitType<OpenCloseMenuEventArgs>;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Triggers while selecting action item in DropDownButton popup.
|
|
241
|
+
*
|
|
242
|
+
* @event select
|
|
243
|
+
*/
|
|
244
|
+
@Event()
|
|
245
|
+
public select: EmitType<MenuEventArgs>;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Triggers once the component rendering is completed.
|
|
249
|
+
*
|
|
250
|
+
* @event created
|
|
251
|
+
*/
|
|
252
|
+
@Event()
|
|
253
|
+
public created: EmitType<Event>;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Constructor for creating the widget
|
|
257
|
+
*
|
|
258
|
+
* @param {DropDownButtonModel} options - Specifies dropdown button model
|
|
259
|
+
* @param {string|HTMLButtonElement} element - Specifies element
|
|
260
|
+
* @hidden
|
|
261
|
+
*/
|
|
262
|
+
public constructor(options?: DropDownButtonModel, element?: string | HTMLButtonElement) {
|
|
263
|
+
super(options, <string | HTMLButtonElement>element);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
protected preRender(): void {
|
|
267
|
+
/** */
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Get the properties to be maintained in the persisted state.
|
|
272
|
+
*
|
|
273
|
+
* @returns {string} - Persist data
|
|
274
|
+
*/
|
|
275
|
+
public getPersistData(): string {
|
|
276
|
+
return this.addOnPersist([]);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* To open/close DropDownButton popup based on current state of the DropDownButton.
|
|
281
|
+
*
|
|
282
|
+
* @returns {void}
|
|
283
|
+
*/
|
|
284
|
+
public toggle(): void {
|
|
285
|
+
if (this.canOpen()) {
|
|
286
|
+
this.openPopUp();
|
|
287
|
+
} else if (this.createPopupOnClick && !this.isPopupCreated) {
|
|
288
|
+
this.createPopup();
|
|
289
|
+
this.openPopUp();
|
|
290
|
+
} else {
|
|
291
|
+
this.closePopup();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Initialize the Component rendering
|
|
297
|
+
*
|
|
298
|
+
* @returns {void}
|
|
299
|
+
* @private
|
|
300
|
+
*/
|
|
301
|
+
public render(): void {
|
|
302
|
+
this.initialize();
|
|
303
|
+
if (!this.disabled) {
|
|
304
|
+
this.wireEvents();
|
|
305
|
+
}
|
|
306
|
+
this.renderComplete();
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Adds a new item to the menu. By default, new item appends to the list as the last item,
|
|
310
|
+
* but you can insert based on the text parameter.
|
|
311
|
+
*
|
|
312
|
+
* @param { ItemModel[] } items - Specifies an array of JSON data.
|
|
313
|
+
* @param { string } text - Specifies the text to insert the newly added item in the menu.
|
|
314
|
+
* @returns {void}.
|
|
315
|
+
*/
|
|
316
|
+
public addItems(items: ItemModel[], text?: string): void {
|
|
317
|
+
let newItem: ItemModel;
|
|
318
|
+
let idx: number = this.items.length;
|
|
319
|
+
for (let j: number = 0, len: number = this.items.length; j < len; j++) {
|
|
320
|
+
if (text === this.items[j as number].text) {
|
|
321
|
+
idx = j;
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
for (let i: number = items.length - 1 ; i >= 0; i--) {
|
|
326
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
327
|
+
newItem = new Item(this as any, 'items', items[i as number], true);
|
|
328
|
+
this.items.splice(idx, 0, newItem);
|
|
329
|
+
}
|
|
330
|
+
if (!this.canOpen()) { this.createItems(); }
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Removes the items from the menu.
|
|
334
|
+
*
|
|
335
|
+
* @param { string[] } items - Specifies an array of string to remove the items.
|
|
336
|
+
* @param { string } isUniqueId - Set `true` if specified items is a collection of unique id.
|
|
337
|
+
* @returns {void}.
|
|
338
|
+
*/
|
|
339
|
+
public removeItems(items: string[], isUniqueId?: boolean): void {
|
|
340
|
+
let refresh: boolean = false;
|
|
341
|
+
for (let i: number = 0, len: number = items.length; i < len; i++) {
|
|
342
|
+
for (let j: number = 0, len: number = this.items.length; j < len; j++) {
|
|
343
|
+
if (items[i as number] === (isUniqueId ? this.items[j as number].id : this.items[j as number].text)) {
|
|
344
|
+
this.items.splice(j, 1); refresh = true;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (refresh && this.getULElement()) { this.createItems(); }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private createPopup(): void {
|
|
353
|
+
const div: HTMLElement = this.createElement('div', {
|
|
354
|
+
className: this.popupWidth !== 'auto' ? `${classNames.POPUP} ${classNames.POPUPWIDTH}` : classNames.POPUP,
|
|
355
|
+
id: this.element.id + '-popup'
|
|
356
|
+
});
|
|
357
|
+
document.body.appendChild(div);
|
|
358
|
+
this.dropDown = new Popup(div, {
|
|
359
|
+
width: this.popupWidth,
|
|
360
|
+
relateTo: this.element,
|
|
361
|
+
collision: { X: 'fit', Y: 'flip' },
|
|
362
|
+
position: { X: 'left', Y: 'bottom' },
|
|
363
|
+
targetType: 'relative',
|
|
364
|
+
content: this.target ? this.getTargetElement() as HTMLElement : '',
|
|
365
|
+
enableRtl: this.enableRtl
|
|
366
|
+
});
|
|
367
|
+
this.dropDown.element.setAttribute('role', 'dialog');
|
|
368
|
+
this.dropDown.element.setAttribute('aria-label', 'dropdown menu');
|
|
369
|
+
if (!isNullOrUndefined(this.popupContent)) {
|
|
370
|
+
this.popupContent.style.display = '';
|
|
371
|
+
}
|
|
372
|
+
if (this.dropDown.element.style.position === 'fixed') {
|
|
373
|
+
this.dropDown.refreshPosition(this.element);
|
|
374
|
+
}
|
|
375
|
+
this.dropDown.hide();
|
|
376
|
+
attributes(this.element, {
|
|
377
|
+
['aria-haspopup']: this.items.length || this.target ? 'true' : 'false', ['aria-expanded']: 'false',
|
|
378
|
+
['type']: 'button'
|
|
379
|
+
});
|
|
380
|
+
if (this.cssClass) { addClass([div], this.cssClass.replace(/\s+/g, ' ').trim().split(' ')); }
|
|
381
|
+
this.isPopupCreated = true;
|
|
382
|
+
if (this.createPopupOnClick) {
|
|
383
|
+
const splitButton: SplitButton = getComponent(this.activeElem[0], 'split-btn');
|
|
384
|
+
if (splitButton) {
|
|
385
|
+
splitButton.isPopupCreated = true;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private getTargetElement(): Element {
|
|
391
|
+
if (this.createPopupOnClick && !this.isColorPicker() && !isNullOrUndefined(this.popupContent)) {
|
|
392
|
+
return this.popupContent as HTMLElement;
|
|
393
|
+
}
|
|
394
|
+
return typeof (this.target) === 'string' ? select(this.target as string) : this.target;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private createItems(appendItems?: boolean): void {
|
|
398
|
+
const items: ItemModel[] = this.items;
|
|
399
|
+
const showIcon: boolean = this.hasIcon(this.items, 'iconCss');
|
|
400
|
+
let span: Element; let item: ItemModel; let li: Element; let eventArgs: MenuEventArgs;
|
|
401
|
+
let ul: HTMLElement = this.getULElement();
|
|
402
|
+
if (ul) {
|
|
403
|
+
ul.innerHTML = '';
|
|
404
|
+
} else {
|
|
405
|
+
ul = this.createElement('ul', {
|
|
406
|
+
attrs: { 'role': 'menu', 'tabindex': '0' }
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
if (this.itemTemplate) {
|
|
410
|
+
const compiledTemplate: Function = this.compiletemplate(this.itemTemplate);
|
|
411
|
+
items.forEach((item: ItemModel) => {
|
|
412
|
+
const li: Element = this.createElement('li', {
|
|
413
|
+
className: item.separator ? classNames.ITEM + ' ' + classNames.SEPARATOR : classNames.ITEM,
|
|
414
|
+
attrs: item.separator
|
|
415
|
+
? { 'role': 'separator', 'tabindex': '-1', 'aria-label': 'separator', 'aria-hidden': 'true' }
|
|
416
|
+
: { 'role': 'menuitem', 'tabindex': '-1', 'aria-label': item.text },
|
|
417
|
+
id: item.id ? item.id : getUniqueID('e-' + this.getModuleName() + '-item')
|
|
418
|
+
});
|
|
419
|
+
const compiledElement: HTMLElement[] = compiledTemplate(item, this, 'template', null, false, null, li);
|
|
420
|
+
if (compiledElement) {
|
|
421
|
+
append(compiledElement, li);
|
|
422
|
+
}
|
|
423
|
+
if (item.disabled) {
|
|
424
|
+
li.classList.add('e-disabled');
|
|
425
|
+
}
|
|
426
|
+
const eventArgs: MenuEventArgs = { item, element: li as HTMLElement };
|
|
427
|
+
this.trigger('beforeItemRender', eventArgs);
|
|
428
|
+
if (eventArgs.item.disabled !== item.disabled) {
|
|
429
|
+
li.classList[eventArgs.item.disabled ? 'add' : 'remove']('e-disabled');
|
|
430
|
+
}
|
|
431
|
+
ul.appendChild(li);
|
|
432
|
+
});
|
|
433
|
+
if ((this as any).isReact) {
|
|
434
|
+
this.renderReactTemplates();
|
|
435
|
+
}
|
|
436
|
+
} else {
|
|
437
|
+
for (let i: number = 0; i < items.length; i++) {
|
|
438
|
+
item = items[i as number];
|
|
439
|
+
if (this.enableHtmlSanitizer) {
|
|
440
|
+
item.text = SanitizeHtmlHelper.sanitize(item.text);
|
|
441
|
+
}
|
|
442
|
+
const tempItem: string = item.text;
|
|
443
|
+
li = this.createElement('li', {
|
|
444
|
+
innerHTML: item.url ? '' : tempItem,
|
|
445
|
+
className: item.separator ? classNames.ITEM + ' ' + classNames.SEPARATOR : classNames.ITEM,
|
|
446
|
+
attrs: item.separator ? {'role' : 'separator', 'tabindex': '-1', 'aria-label': 'separator', 'aria-hidden': 'true'} : { 'role': 'menuitem', 'tabindex': '-1', 'aria-label': tempItem },
|
|
447
|
+
id: item.id ? item.id : getUniqueID('e-' + this.getModuleName() + '-item')
|
|
448
|
+
});
|
|
449
|
+
if (this.enableHtmlSanitizer) {
|
|
450
|
+
li.textContent = item.url ? '' : tempItem;
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
li.innerHTML = item.url ? '' : tempItem;
|
|
454
|
+
}
|
|
455
|
+
if (item.url) {
|
|
456
|
+
li.appendChild(this.createAnchor(item));
|
|
457
|
+
li.classList.add('e-url');
|
|
458
|
+
}
|
|
459
|
+
if (item.iconCss) {
|
|
460
|
+
span = this.createElement('span', { className: classNames.ICON + ' ' + item.iconCss });
|
|
461
|
+
if (item.url) {
|
|
462
|
+
li.childNodes[0].appendChild(span);
|
|
463
|
+
} else {
|
|
464
|
+
li.insertBefore(span, li.childNodes[0]);
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
if (showIcon && !item.separator) {
|
|
468
|
+
li.classList.add('e-blank-icon');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const beforeDisabled: boolean = item.disabled;
|
|
472
|
+
if (item.disabled) { li.classList.add('e-disabled'); }
|
|
473
|
+
eventArgs = { item: item, element: li as HTMLElement };
|
|
474
|
+
this.trigger('beforeItemRender', eventArgs);
|
|
475
|
+
const afterDisabled: boolean = eventArgs.item.disabled;
|
|
476
|
+
if (beforeDisabled !== afterDisabled) {
|
|
477
|
+
if (eventArgs.item.disabled) {
|
|
478
|
+
li.classList.add('e-disabled');
|
|
479
|
+
} else {
|
|
480
|
+
li.classList.remove('e-disabled');
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
ul.appendChild(li);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if (appendItems) {
|
|
487
|
+
this.getPopUpElement().appendChild(ul);
|
|
488
|
+
}
|
|
489
|
+
if (showIcon) { setBlankIconStyle(this.getPopUpElement()); }
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
493
|
+
private compiletemplate(template: string | Function): any {
|
|
494
|
+
if (!this.itemTemplate) {
|
|
495
|
+
return undefined;
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
if (typeof this.itemTemplate !== 'function') {
|
|
499
|
+
const templateElement: Element = document.querySelector(this.itemTemplate as string);
|
|
500
|
+
if (templateElement) {
|
|
501
|
+
return compile(templateElement.innerHTML.trim());
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return compile(template);
|
|
505
|
+
} catch {
|
|
506
|
+
return compile(template);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
private hasIcon(items: ItemModel[], field: string): boolean {
|
|
511
|
+
for (let i: number = 0, len: number = items.length; i < len; i++) {
|
|
512
|
+
if ((<{ [key: string]: object }>items[i as number])[`${field}`]) {
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
private createAnchor(item: ItemModel): HTMLElement {
|
|
520
|
+
const tempItem: string = item.text;
|
|
521
|
+
const anchor: HTMLElement = this.createElement('a', { className: 'e-menu-text e-menu-url', attrs: { 'href': item.url } });
|
|
522
|
+
if (this.enableHtmlSanitizer) {
|
|
523
|
+
anchor.textContent = tempItem;
|
|
524
|
+
} else {
|
|
525
|
+
anchor.innerHTML = tempItem;
|
|
526
|
+
}
|
|
527
|
+
return anchor;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private initialize(): void {
|
|
531
|
+
this.button = new Button({
|
|
532
|
+
iconCss: this.iconCss, iconPosition: this.iconPosition, cssClass: this.cssClass, content: this.content,
|
|
533
|
+
disabled: this.disabled, enableRtl: this.enableRtl, enablePersistence: this.enablePersistence
|
|
534
|
+
});
|
|
535
|
+
this.button.createElement = this.createElement;
|
|
536
|
+
this.button.appendTo(this.element);
|
|
537
|
+
if (!this.element.id) {
|
|
538
|
+
this.element.id = getUniqueID('e-' + this.getModuleName());
|
|
539
|
+
}
|
|
540
|
+
this.appendArrowSpan();
|
|
541
|
+
this.setActiveElem([this.element]);
|
|
542
|
+
this.element.setAttribute('tabindex', '0');
|
|
543
|
+
this.element.setAttribute('aria-label', this.element.textContent ? this.element.textContent : 'dropdownbutton');
|
|
544
|
+
if ((this.target && !this.isColorPicker() && !this.createPopupOnClick) || !this.createPopupOnClick) {
|
|
545
|
+
this.createPopup();
|
|
546
|
+
} else {
|
|
547
|
+
this.isPopupCreated = false;
|
|
548
|
+
if (this.target && !this.isColorPicker() && this.createPopupOnClick) {
|
|
549
|
+
this.popupContent = this.getTargetElement() as HTMLElement;
|
|
550
|
+
this.popupContent.style.display = 'none';
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private isColorPicker(): boolean {
|
|
556
|
+
if (!this.element) {
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
const prevElem: HTMLElement = this.element.previousSibling as HTMLElement;
|
|
560
|
+
if (prevElem && prevElem.classList && prevElem.classList.contains('e-split-colorpicker')) {
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private appendArrowSpan(): void {
|
|
567
|
+
this.cssClass = isNullOrUndefined(this.cssClass) ? '' : this.cssClass;
|
|
568
|
+
this.element.appendChild(this.createElement('span', {
|
|
569
|
+
className: 'e-btn-icon e-icons ' + 'e-icon-' + (this.cssClass.indexOf(classNames.VERTICAL) > -1
|
|
570
|
+
? 'bottom' : 'right') + ' e-caret'
|
|
571
|
+
}));
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
protected setActiveElem(elem: HTMLElement[]): void {
|
|
575
|
+
this.activeElem = elem;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Get component name.
|
|
580
|
+
*
|
|
581
|
+
* @returns {string} - Module Name
|
|
582
|
+
* @private
|
|
583
|
+
*/
|
|
584
|
+
public getModuleName(): string {
|
|
585
|
+
return 'dropdown-btn';
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
private canOpen(): boolean {
|
|
589
|
+
let val: boolean = false;
|
|
590
|
+
if (this.isPopupCreated) {
|
|
591
|
+
val = this.getPopUpElement().classList.contains('e-popup-close');
|
|
592
|
+
}
|
|
593
|
+
return val;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Destroys the widget.
|
|
598
|
+
*
|
|
599
|
+
* @returns {void}
|
|
600
|
+
*/
|
|
601
|
+
public destroy(): void {
|
|
602
|
+
super.destroy();
|
|
603
|
+
if (this.getModuleName() === 'dropdown-btn') {
|
|
604
|
+
let classList: string[];
|
|
605
|
+
if (this.element.querySelector('span.e-caret')) {
|
|
606
|
+
detach(this.element.querySelector('span.e-caret'));
|
|
607
|
+
}
|
|
608
|
+
if (this.cssClass) {
|
|
609
|
+
classList = this.cssClass.split(' ');
|
|
610
|
+
}
|
|
611
|
+
this.button.destroy();
|
|
612
|
+
if (classList) {
|
|
613
|
+
removeClass([this.element], classList);
|
|
614
|
+
}
|
|
615
|
+
removeClass(this.activeElem, ['e-active']);
|
|
616
|
+
const attrList: string[] = this.element.getAttribute('class') ? ['aria-haspopup', 'aria-expanded', 'aria-owns', 'type']
|
|
617
|
+
: ['aria-haspopup', 'aria-expanded', 'aria-owns', 'type', 'class'];
|
|
618
|
+
attrList.forEach((key: string) => {
|
|
619
|
+
this.element.removeAttribute(key);
|
|
620
|
+
});
|
|
621
|
+
this.popupUnWireEvents();
|
|
622
|
+
this.destroyPopup();
|
|
623
|
+
this.isPopupCreated = false;
|
|
624
|
+
if (!this.disabled) {
|
|
625
|
+
this.unWireEvents();
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
protected destroyPopup(): void {
|
|
631
|
+
if (this.isPopupCreated) {
|
|
632
|
+
this.dropDown.destroy();
|
|
633
|
+
if (this.getPopUpElement()) {
|
|
634
|
+
const popupEle: HTMLElement = document.getElementById(this.getPopUpElement().id);
|
|
635
|
+
if (popupEle) {
|
|
636
|
+
removeClass([popupEle], ['e-popup-open', 'e-popup-close']);
|
|
637
|
+
detach(popupEle);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
EventHandler.remove(this.getPopUpElement(), 'click', this.clickHandler);
|
|
641
|
+
EventHandler.remove(this.getPopUpElement(), 'keydown', this.keyBoardHandler);
|
|
642
|
+
if (this.isPopupCreated && this.dropDown) {
|
|
643
|
+
this.dropDown.element = null;
|
|
644
|
+
this.dropDown = undefined;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
this.isPopupCreated = false;
|
|
648
|
+
const splitButton: SplitButton = getComponent(this.activeElem[0], 'split-btn');
|
|
649
|
+
if (this.createPopupOnClick && splitButton) {
|
|
650
|
+
const dropDownButton: DropDownButton = getComponent(this.activeElem[1], 'dropdown-btn');
|
|
651
|
+
if (dropDownButton) {
|
|
652
|
+
dropDownButton.isPopupCreated = false;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
protected getPopUpElement(): HTMLElement {
|
|
658
|
+
let val: HTMLElement = null;
|
|
659
|
+
if (!this.dropDown && this.activeElem[0].classList.contains('e-split-btn')) {
|
|
660
|
+
const dropDownBtn: DropDownButton = getComponent(this.activeElem[1], 'dropdown-btn');
|
|
661
|
+
if (dropDownBtn) { this.dropDown = dropDownBtn.dropDown; }
|
|
662
|
+
}
|
|
663
|
+
if (this.dropDown) {
|
|
664
|
+
val = this.dropDown.element;
|
|
665
|
+
}
|
|
666
|
+
return val;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
protected getULElement(): HTMLElement {
|
|
670
|
+
let val: HTMLElement = null;
|
|
671
|
+
if (this.getPopUpElement()) {
|
|
672
|
+
val = this.getPopUpElement().children[0] as HTMLElement;
|
|
673
|
+
}
|
|
674
|
+
return val;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
protected wireEvents(): void {
|
|
678
|
+
this.delegateMousedownHandler = this.mousedownHandler.bind(this);
|
|
679
|
+
if (!this.createPopupOnClick) {
|
|
680
|
+
EventHandler.add(document, 'mousedown touchstart', this.delegateMousedownHandler, this);
|
|
681
|
+
}
|
|
682
|
+
EventHandler.add(this.element, 'click', this.clickHandler, this);
|
|
683
|
+
EventHandler.add(this.element, 'keydown', this.keyBoardHandler, this);
|
|
684
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
685
|
+
EventHandler.add(window as any, 'resize', this.windowResize, this);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
protected windowResize(): void {
|
|
689
|
+
if (!this.canOpen() && this.dropDown) {
|
|
690
|
+
this.dropDown.refreshPosition(this.element);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
protected popupWireEvents(): void {
|
|
695
|
+
if (!this.delegateMousedownHandler) {
|
|
696
|
+
this.delegateMousedownHandler = this.mousedownHandler.bind(this);
|
|
697
|
+
}
|
|
698
|
+
const popupElement: HTMLElement = this.getPopUpElement();
|
|
699
|
+
if (this.createPopupOnClick) {
|
|
700
|
+
EventHandler.add(document, 'mousedown touchstart', this.delegateMousedownHandler, this);
|
|
701
|
+
}
|
|
702
|
+
if (popupElement) {
|
|
703
|
+
EventHandler.add(popupElement, 'click', this.clickHandler, this);
|
|
704
|
+
EventHandler.add(popupElement, 'keydown', this.keyBoardHandler, this);
|
|
705
|
+
if (this.closeActionEvents) {
|
|
706
|
+
EventHandler.add(popupElement, this.closeActionEvents, this.focusoutHandler, this);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
this.rippleFn = rippleEffect(popupElement, { selector: '.' + classNames.ITEM });
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
protected popupUnWireEvents(): void {
|
|
713
|
+
const popupElement: HTMLElement = this.getPopUpElement();
|
|
714
|
+
if (this.createPopupOnClick) {
|
|
715
|
+
EventHandler.remove(document, 'mousedown touchstart', this.delegateMousedownHandler);
|
|
716
|
+
}
|
|
717
|
+
if (popupElement && popupElement.parentElement) {
|
|
718
|
+
EventHandler.remove(popupElement, 'click', this.clickHandler);
|
|
719
|
+
EventHandler.remove(popupElement, 'keydown', this.keyBoardHandler);
|
|
720
|
+
if (this.closeActionEvents) {
|
|
721
|
+
EventHandler.remove(popupElement, this.closeActionEvents, this.focusoutHandler);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (isRippleEnabled && this.rippleFn) {
|
|
725
|
+
this.rippleFn();
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Handles the keyboard interactions.
|
|
731
|
+
*
|
|
732
|
+
* @param {KeyboardEventArgs} e - Specifies keyboard event args.
|
|
733
|
+
* @returns {void}
|
|
734
|
+
* @hidden
|
|
735
|
+
*/
|
|
736
|
+
public keyBoardHandler(e: KeyboardEventArgs): void {
|
|
737
|
+
if (e.target === this.element && (e.keyCode === 9 || (!e.altKey && e.keyCode === 40) || e.keyCode === 38)) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
if (e.target && ((e.target as Element).classList.contains('e-item') || ((e.target as Element).parentElement && (e.target as Element).parentElement.classList.contains('e-split-btn-wrapper'))) && e.keyCode === 13) {
|
|
741
|
+
e.preventDefault();
|
|
742
|
+
}
|
|
743
|
+
switch (e.keyCode) {
|
|
744
|
+
case 38:
|
|
745
|
+
case 40:
|
|
746
|
+
if (e.altKey && (e.keyCode === 38 || e.keyCode === 40)) {
|
|
747
|
+
this.keyEventHandler(e);
|
|
748
|
+
} else {
|
|
749
|
+
this.upDownKeyHandler(e);
|
|
750
|
+
}
|
|
751
|
+
break;
|
|
752
|
+
case 9:
|
|
753
|
+
case 13:
|
|
754
|
+
case 27:
|
|
755
|
+
case 32:
|
|
756
|
+
this.keyEventHandler(e);
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
private isSafari(): boolean {
|
|
762
|
+
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
763
|
+
}
|
|
764
|
+
protected upDownKeyHandler(e: KeyboardEventArgs): void {
|
|
765
|
+
if (this.target && (e.keyCode === 38 || e.keyCode === 40)) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
e.preventDefault();
|
|
769
|
+
upDownKeyHandler(this.getULElement(), e.keyCode);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
private keyEventHandler(e: KeyboardEventArgs): void {
|
|
773
|
+
if (this.target && (e.keyCode === 13 || e.keyCode === 9 )) {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (e.keyCode === 13 && this.activeElem[0].classList.contains('e-split-btn')) {
|
|
777
|
+
this.triggerSelect(e);
|
|
778
|
+
this.activeElem[0].focus();
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (e.target && (e.target as Element).className.indexOf('e-edit-template') > -1 && e.keyCode === 32) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
if (e.keyCode !== 9) {
|
|
785
|
+
e.preventDefault();
|
|
786
|
+
}
|
|
787
|
+
if (e.keyCode === 27 || e.keyCode === 38 || e.keyCode === 9) {
|
|
788
|
+
if (!this.canOpen()) {
|
|
789
|
+
this.closePopup(e, this.element);
|
|
790
|
+
}
|
|
791
|
+
} else {
|
|
792
|
+
this.clickHandler(e);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
private getLI(elem: Element): Element {
|
|
797
|
+
return elem.tagName === 'LI' ? elem : closest(elem, 'li');
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
private mousedownHandler(e: MouseEvent): void {
|
|
801
|
+
const trgt: HTMLElement = e.target as HTMLElement;
|
|
802
|
+
if (this.dropDown && !this.canOpen() && this.getPopUpElement() && !(closest(trgt, '[id="' + this.getPopUpElement().id + '"]')
|
|
803
|
+
|| closest(trgt, '[id="' + this.element.id + '"]'))) {
|
|
804
|
+
this.closePopup(e);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
private focusoutHandler(e: MouseEvent): void {
|
|
809
|
+
if (this.isPopupCreated && !this.canOpen()) {
|
|
810
|
+
const liTarget : HTMLElement = (e.relatedTarget || e.target) as HTMLElement;
|
|
811
|
+
if (liTarget && liTarget.className.indexOf('e-item') > -1) {
|
|
812
|
+
const li: Element = this.getLI(liTarget);
|
|
813
|
+
if (li) {
|
|
814
|
+
const liIdx: number = Array.prototype.indexOf.call(this.getULElement().children, li);
|
|
815
|
+
const item: ItemModel = this.items[liIdx as number];
|
|
816
|
+
if (item) {
|
|
817
|
+
const selectEventArgs: MenuEventArgs = { element: li as HTMLElement, item: item, event: e };
|
|
818
|
+
this.trigger('select', selectEventArgs);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
this.closePopup(e);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
protected clickHandler(e: MouseEvent | KeyboardEventArgs): void {
|
|
827
|
+
const trgt: HTMLElement = e.target as HTMLElement;
|
|
828
|
+
if (closest(trgt, '[id="' + this.element.id + '"]')) {
|
|
829
|
+
if (!this.createPopupOnClick || (this.target && this.target !== '' && !this.isColorPicker() && !this.createPopupOnClick)) {
|
|
830
|
+
if (this.getPopUpElement().classList.contains('e-popup-close')) {
|
|
831
|
+
this.openPopUp(e);
|
|
832
|
+
} else {
|
|
833
|
+
this.closePopup(e);
|
|
834
|
+
}
|
|
835
|
+
} else if (this.isPopupCreated) {
|
|
836
|
+
this.closePopup(e, this.activeElem[0]);
|
|
837
|
+
} else {
|
|
838
|
+
this.createPopup();
|
|
839
|
+
this.openPopUp(e);
|
|
840
|
+
}
|
|
841
|
+
} else {
|
|
842
|
+
if (closest(trgt, '[id="' + this.getPopUpElement().id + '"]')) {
|
|
843
|
+
const li: Element = this.getLI(e.target as HTMLElement);
|
|
844
|
+
if (li) {
|
|
845
|
+
this.triggerSelect(e);
|
|
846
|
+
this.closePopup(e, this.activeElem[0]);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
private triggerSelect(e: MouseEvent | KeyboardEventArgs): void {
|
|
853
|
+
let eventArgs: MenuEventArgs; let liIdx: number; let item: ItemModel;
|
|
854
|
+
const li: Element = this.getLI(e.target as HTMLElement);
|
|
855
|
+
if (li) {
|
|
856
|
+
liIdx = Array.prototype.indexOf.call(this.getULElement().children, li);
|
|
857
|
+
item = this.items[liIdx as number];
|
|
858
|
+
if (item) {
|
|
859
|
+
eventArgs = { element: li as HTMLElement, item: item, event: e };
|
|
860
|
+
this.trigger('select', eventArgs);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
private openPopUp(e: MouseEvent | KeyboardEventArgs = null): void {
|
|
866
|
+
let isReact: boolean = false; const popupElem: HTMLElement = this.getPopUpElement();
|
|
867
|
+
if (this.activeElem[0] && this.activeElem[0].classList.contains('e-dropdown-btn') && popupElem.style.width && popupElem.style.width !== 'auto') {
|
|
868
|
+
this.setWidth(popupElem);
|
|
869
|
+
}
|
|
870
|
+
if (!this.target) {
|
|
871
|
+
this.createItems(true);
|
|
872
|
+
} else {
|
|
873
|
+
if (this.activeElem.length > 1) {
|
|
874
|
+
const splitButton: SplitButton = getComponent(this.activeElem[0], 'split-btn');
|
|
875
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
876
|
+
if ((splitButton as any).isReact && popupElem.childNodes.length < 1) {
|
|
877
|
+
isReact = true;
|
|
878
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
879
|
+
if ((splitButton as any).appendReactElement) {
|
|
880
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
881
|
+
(splitButton as any).appendReactElement(this.getTargetElement(), this.getPopUpElement());
|
|
882
|
+
}
|
|
883
|
+
this.renderReactTemplates();
|
|
884
|
+
}
|
|
885
|
+
} else {
|
|
886
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
887
|
+
if ((this as any).isReact && popupElem.childNodes.length < 1) {
|
|
888
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
889
|
+
isReact = true;
|
|
890
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
891
|
+
if ((this as any).appendReactElement) {
|
|
892
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
893
|
+
(this as any).appendReactElement(this.getTargetElement(), this.getPopUpElement());
|
|
894
|
+
}
|
|
895
|
+
this.renderReactTemplates();
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
const ul: HTMLElement = this.getULElement();
|
|
900
|
+
this.popupWireEvents();
|
|
901
|
+
const beforeOpenArgs: BeforeOpenCloseMenuEventArgs = { element: ul, items: this.items, event: e, cancel: false };
|
|
902
|
+
this.trigger('beforeOpen', beforeOpenArgs, (observedArgs: BeforeOpenCloseMenuEventArgs) => {
|
|
903
|
+
if (!observedArgs.cancel) {
|
|
904
|
+
const ul: HTMLElement = this.getULElement();
|
|
905
|
+
const animationOptions: AnimationModel = this.animationSettings.effect !== 'None' ? {
|
|
906
|
+
name: this.animationSettings.effect, duration: this.animationSettings.duration,
|
|
907
|
+
timingFunction: this.animationSettings.easing
|
|
908
|
+
} : null;
|
|
909
|
+
if (animationOptions) {
|
|
910
|
+
this.animatePopup(animationOptions, ul);
|
|
911
|
+
}
|
|
912
|
+
this.dropDown.show(null, this.element);
|
|
913
|
+
addClass([this.element], 'e-active');
|
|
914
|
+
this.element.setAttribute('aria-expanded', 'true');
|
|
915
|
+
this.element.setAttribute('aria-owns', this.getPopUpElement().id);
|
|
916
|
+
if (ul && !this.isSafari()) {
|
|
917
|
+
ul.focus();
|
|
918
|
+
}
|
|
919
|
+
if (this.enableRtl && ul.parentElement.style.left !== '0px')
|
|
920
|
+
{
|
|
921
|
+
let wrapperWidth: number;
|
|
922
|
+
if (this.element.parentElement && this.element.parentElement.classList.contains('e-split-btn-wrapper')) {
|
|
923
|
+
wrapperWidth = this.element.parentElement.offsetWidth;
|
|
924
|
+
} else {
|
|
925
|
+
wrapperWidth = this.element.offsetWidth;
|
|
926
|
+
}
|
|
927
|
+
const popupRect: number = ul.parentElement.offsetWidth - wrapperWidth;
|
|
928
|
+
let popupLeft: number = parseFloat(ul.parentElement.style.left) - popupRect;
|
|
929
|
+
if (popupLeft < 0) {
|
|
930
|
+
popupLeft = 0;
|
|
931
|
+
}
|
|
932
|
+
ul.parentElement.style.left = popupLeft + 'px';
|
|
933
|
+
}
|
|
934
|
+
const openArgs: OpenCloseMenuEventArgs = { element: ul, items: this.items };
|
|
935
|
+
this.trigger('open', openArgs);
|
|
936
|
+
if (ul && this.isSafari()) {
|
|
937
|
+
ul.focus();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
private animatePopup(animationOptions: AnimationModel, element: HTMLElement): void {
|
|
944
|
+
new Animation(animationOptions).animate(element, {
|
|
945
|
+
begin: (args: {element: HTMLElement}) => {
|
|
946
|
+
args.element.parentElement.style.height = args.element.parentElement.offsetHeight + 'px';
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
private setWidth(popupElem: HTMLElement): void {
|
|
952
|
+
const width: string = formatUnit(popupElem.style.width);
|
|
953
|
+
if (width.indexOf('%') > -1) {
|
|
954
|
+
const btnWidth: number = this.element.offsetWidth * parseFloat(width) / 100;
|
|
955
|
+
popupElem.style.width = `${btnWidth}px`;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
private closePopup(e: MouseEvent | KeyboardEventArgs = null, focusEle?: HTMLElement): void {
|
|
960
|
+
const ul: HTMLElement = this.getULElement();
|
|
961
|
+
const beforeCloseArgs: BeforeOpenCloseMenuEventArgs = { element: ul, items: this.items, event: e, cancel: false };
|
|
962
|
+
this.trigger('beforeClose', beforeCloseArgs, (observedArgs: BeforeOpenCloseMenuEventArgs) => {
|
|
963
|
+
if (!observedArgs.cancel) {
|
|
964
|
+
const popupElement: HTMLElement = this.getPopUpElement();
|
|
965
|
+
if (popupElement) {
|
|
966
|
+
EventHandler.remove(popupElement, 'keydown', this.keyBoardHandler);
|
|
967
|
+
}
|
|
968
|
+
this.popupUnWireEvents();
|
|
969
|
+
const ul: HTMLElement = this.getULElement();
|
|
970
|
+
let selectedLi: Element;
|
|
971
|
+
if (ul) {
|
|
972
|
+
selectedLi = ul.querySelector('.e-selected');
|
|
973
|
+
}
|
|
974
|
+
if (selectedLi) { selectedLi.classList.remove('e-selected'); }
|
|
975
|
+
if (this.dropDown) { this.dropDown.hide(); }
|
|
976
|
+
removeClass(this.activeElem, 'e-active');
|
|
977
|
+
this.element.setAttribute('aria-expanded', 'false');
|
|
978
|
+
this.element.removeAttribute('aria-owns');
|
|
979
|
+
if (focusEle) {
|
|
980
|
+
if (!this.isSafari()) {
|
|
981
|
+
if (!(this.isColorPicker() && (e as KeyboardEvent).keyCode === 27)) {
|
|
982
|
+
focusEle.focus();
|
|
983
|
+
}
|
|
984
|
+
} else {
|
|
985
|
+
focusEle.focus({ preventScroll: true });
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
const closeArgs: OpenCloseMenuEventArgs = { element: ul, items: this.items };
|
|
989
|
+
this.trigger('close', closeArgs);
|
|
990
|
+
if (!this.target && ul) { detach(ul); }
|
|
991
|
+
if (!this.target || this.isColorPicker() || (this.target && !this.isColorPicker())) {
|
|
992
|
+
if (this.createPopupOnClick) { this.destroyPopup(); }
|
|
993
|
+
}
|
|
994
|
+
} else {
|
|
995
|
+
if (ul) {
|
|
996
|
+
ul.focus();
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
protected unWireEvents(): void {
|
|
1003
|
+
if (!this.createPopupOnClick) {
|
|
1004
|
+
EventHandler.remove(document, 'mousedown touchstart', this.delegateMousedownHandler);
|
|
1005
|
+
}
|
|
1006
|
+
EventHandler.remove(this.element, 'click', this.clickHandler);
|
|
1007
|
+
EventHandler.remove(this.element, 'keydown', this.keyBoardHandler);
|
|
1008
|
+
if (this.isPopupCreated) {
|
|
1009
|
+
EventHandler.remove(this.getPopUpElement(), 'click', this.clickHandler);
|
|
1010
|
+
EventHandler.remove(this.getPopUpElement(), 'keydown', this.keyBoardHandler);
|
|
1011
|
+
}
|
|
1012
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1013
|
+
EventHandler.remove(window as any, 'resize', this.windowResize);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* Called internally if any of the property value changed.
|
|
1018
|
+
*
|
|
1019
|
+
* @param {DropDownButtonModel} newProp - Specifies new properties
|
|
1020
|
+
* @param {DropDownButtonModel} oldProp - Specifies old properties
|
|
1021
|
+
* @returns {void}
|
|
1022
|
+
* @private
|
|
1023
|
+
*/
|
|
1024
|
+
public onPropertyChanged(newProp: DropDownButtonModel, oldProp: DropDownButtonModel): void {
|
|
1025
|
+
const btnModel: string[] = ['content', 'cssClass', 'iconCss', 'iconPosition', 'disabled', 'enableRtl'];
|
|
1026
|
+
this.button.setProperties(getModel(newProp, btnModel));
|
|
1027
|
+
let popupElement: Element;
|
|
1028
|
+
if (this.isPopupCreated) {
|
|
1029
|
+
popupElement = this.getPopUpElement();
|
|
1030
|
+
this.dropDown.setProperties(getModel(newProp, ['enableRtl']));
|
|
1031
|
+
}
|
|
1032
|
+
for (const prop of Object.keys(newProp)) {
|
|
1033
|
+
switch (prop) {
|
|
1034
|
+
case 'content':
|
|
1035
|
+
if (!this.element.querySelector('span.e-caret')) {
|
|
1036
|
+
this.appendArrowSpan();
|
|
1037
|
+
}
|
|
1038
|
+
break;
|
|
1039
|
+
case 'disabled':
|
|
1040
|
+
if (newProp.disabled) {
|
|
1041
|
+
this.unWireEvents();
|
|
1042
|
+
if (this.isPopupCreated && !this.canOpen()) {
|
|
1043
|
+
this.closePopup();
|
|
1044
|
+
}
|
|
1045
|
+
} else {
|
|
1046
|
+
this.wireEvents();
|
|
1047
|
+
}
|
|
1048
|
+
break;
|
|
1049
|
+
case 'cssClass':
|
|
1050
|
+
oldProp.cssClass = isNullOrUndefined(oldProp.cssClass) ? '' : oldProp.cssClass;
|
|
1051
|
+
if (newProp.cssClass.indexOf(classNames.VERTICAL) > -1 || oldProp.cssClass.indexOf(classNames.VERTICAL) > -1) {
|
|
1052
|
+
if (!this.element.querySelector('span.e-caret')) {
|
|
1053
|
+
this.appendArrowSpan();
|
|
1054
|
+
}
|
|
1055
|
+
const arrowSpan: Element = this.element.querySelector('span.e-caret');
|
|
1056
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
1057
|
+
newProp.cssClass.indexOf(classNames.VERTICAL) > -1 ? classList(arrowSpan, ['e-icon-bottom'], ['e-icon-right'])
|
|
1058
|
+
: classList(arrowSpan, ['e-icon-right'], ['e-icon-bottom']);
|
|
1059
|
+
}
|
|
1060
|
+
if (this.isPopupCreated) {
|
|
1061
|
+
if (oldProp.cssClass) {
|
|
1062
|
+
removeClass([popupElement], oldProp.cssClass.split(' '));
|
|
1063
|
+
}
|
|
1064
|
+
if (newProp.cssClass) {
|
|
1065
|
+
addClass([popupElement], newProp.cssClass.replace(/\s+/g, ' ').trim().split(' '));
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
break;
|
|
1069
|
+
case 'target':
|
|
1070
|
+
this.dropDown.content = this.getTargetElement() as HTMLElement;
|
|
1071
|
+
this.dropDown.dataBind();
|
|
1072
|
+
break;
|
|
1073
|
+
case 'items':
|
|
1074
|
+
if (this.isPopupCreated && this.getULElement()) { this.createItems(); }
|
|
1075
|
+
break;
|
|
1076
|
+
case 'createPopupOnClick':
|
|
1077
|
+
if (newProp.createPopupOnClick) {
|
|
1078
|
+
this.destroyPopup();
|
|
1079
|
+
} else {
|
|
1080
|
+
this.createPopup();
|
|
1081
|
+
}
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/**
|
|
1088
|
+
* Sets the focus to DropDownButton
|
|
1089
|
+
* its native method
|
|
1090
|
+
*
|
|
1091
|
+
* @public
|
|
1092
|
+
* @returns {void}
|
|
1093
|
+
*/
|
|
1094
|
+
public focusIn(): void {
|
|
1095
|
+
this.element.focus();
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
interface ClassNames {
|
|
1101
|
+
DISABLED: string;
|
|
1102
|
+
FOCUS: string;
|
|
1103
|
+
ICON: string;
|
|
1104
|
+
ITEM: string;
|
|
1105
|
+
POPUP: string;
|
|
1106
|
+
RTL: string;
|
|
1107
|
+
SEPARATOR: string;
|
|
1108
|
+
VERTICAL: string;
|
|
1109
|
+
POPUPWIDTH: string;
|
|
1110
|
+
}
|