gd-bs 6.7.6 → 6.7.8

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/indexv2.html CHANGED
@@ -177,7 +177,7 @@
177
177
 
178
178
  <h5>Dropdown</h5>
179
179
 
180
- <bs-dropdown auto-select="true" is-split="false" label="My Dropdown:" on-change="MyLib.ddlChange">
180
+ <bs-dropdown auto-select="true" is-split="false" label="My Dropdown:" search="true" on-change="MyLib.ddlChange">
181
181
  <item is-header="true">Header 1</item>
182
182
  <item is-divider="true"></item>
183
183
  <item value="1">Item 1</item>
@@ -192,6 +192,16 @@
192
192
  <item value="8">Item 8</item>
193
193
  <item value="9">Item 9</item>
194
194
  <item value="10">Item 10</item>
195
+ <item value="11">Item 11</item>
196
+ <item value="12">Item 12</item>
197
+ <item value="13">Item 13</item>
198
+ <item value="14">Item 14</item>
199
+ <item value="15">Item 15</item>
200
+ <item value="16">Item 16</item>
201
+ <item value="17">Item 17</item>
202
+ <item value="18">Item 18</item>
203
+ <item value="19">Item 19</item>
204
+ <item value="20">Item 20</item>
195
205
  </bs-dropdown>
196
206
 
197
207
  <h5>Form (Rows)</h5>
@@ -218,6 +228,21 @@
218
228
  <item value="3">Item 3</item>
219
229
  <item value="4">Item 4</item>
220
230
  <item value="5">Item 5</item>
231
+ <item value="6">Item 6</item>
232
+ <item value="7">Item 7</item>
233
+ <item value="8">Item 8</item>
234
+ <item value="9">Item 9</item>
235
+ <item value="10">Item 10</item>
236
+ <item value="11">Item 11</item>
237
+ <item value="12">Item 12</item>
238
+ <item value="13">Item 13</item>
239
+ <item value="14">Item 14</item>
240
+ <item value="15">Item 15</item>
241
+ <item value="16">Item 16</item>
242
+ <item value="17">Item 17</item>
243
+ <item value="18">Item 18</item>
244
+ <item value="19">Item 19</item>
245
+ <item value="20">Item 20</item>
221
246
  </bs-form-control>
222
247
  </row>
223
248
  <div>
package/package.json CHANGED
@@ -1,61 +1,61 @@
1
- {
2
- "name": "gd-bs",
3
- "version": "6.7.6",
4
- "description": "Bootstrap JavaScript, TypeScript and Web Components library.",
5
- "main": "build/index.js",
6
- "typings": "src/index.d.ts",
7
- "scripts": {
8
- "all": "npm run clean && npm run generate-icons && npm run build && npm run build-icons && npm run prod && npm run prod-icons && npm run typings",
9
- "build": "tsc && npm run pre-build && npm run build-bs && webpack --mode=development && npm run post-build",
10
- "build-bs": "webpack --mode=production --config webpack.sass.js",
11
- "build-icons": "webpack --mode=development --config webpack.icons.js",
12
- "pre-build": "node ./pre-build",
13
- "post-build": "node ./post-build",
14
- "clean": "node ./clean.js",
15
- "docs": "typedoc",
16
- "generate-icons": "node ./generateIcons",
17
- "package": "npm run all && npm run docs",
18
- "prod": "webpack --mode=production",
19
- "prod-icons": "webpack --mode=production --config webpack.icons.js",
20
- "typings": "dts-bundle --configJson dts-bundle.json"
21
- },
22
- "repository": {
23
- "type": "git",
24
- "url": "git+https://github.com/gunjandatta/gd-bs.git"
25
- },
26
- "keywords": [
27
- "Bootstrap",
28
- "TypeScript"
29
- ],
30
- "author": "Gunjan Datta <me@dattabase.com> (https://dattabase.com)",
31
- "license": "MIT",
32
- "bugs": {
33
- "url": "https://github.com/gunjandatta/gd-bs/issues"
34
- },
35
- "homepage": "https://dattabase.com/extras/bs",
36
- "dependencies": {
37
- "@floating-ui/dom": "^1.7.1",
38
- "bootstrap": "^5.3.6",
39
- "bootstrap-icons": "^1.13.1",
40
- "core-js": "^3.43.0"
41
- },
42
- "devDependencies": {
43
- "@babel/core": "^7.27.4",
44
- "@babel/preset-env": "^7.27.2",
45
- "@types/node": "^18.19.111",
46
- "autoprefixer": "^10.4.21",
47
- "babel-loader": "^9.2.1",
48
- "css-loader": "^6.11.0",
49
- "dts-bundle": "^0.7.3",
50
- "postcss-loader": "^7.3.4",
51
- "sass": "^1.89.2",
52
- "sass-loader": "^13.3.3",
53
- "style-loader": "^3.3.4",
54
- "terser-webpack-plugin": "^5.3.14",
55
- "ts-loader": "^9.5.2",
56
- "typedoc": "^0.24.8",
57
- "typescript": "4.9.5",
58
- "webpack": "^5.99.9",
59
- "webpack-cli": "^5.1.4"
60
- }
1
+ {
2
+ "name": "gd-bs",
3
+ "version": "6.7.8",
4
+ "description": "Bootstrap JavaScript, TypeScript and Web Components library.",
5
+ "main": "build/index.js",
6
+ "typings": "src/index.d.ts",
7
+ "scripts": {
8
+ "all": "npm run clean && npm run generate-icons && npm run build && npm run build-icons && npm run prod && npm run prod-icons && npm run typings",
9
+ "build": "tsc && npm run pre-build && npm run build-bs && webpack --mode=development && npm run post-build",
10
+ "build-bs": "webpack --mode=production --config webpack.sass.js",
11
+ "build-icons": "webpack --mode=development --config webpack.icons.js",
12
+ "pre-build": "node ./pre-build",
13
+ "post-build": "node ./post-build",
14
+ "clean": "node ./clean.js",
15
+ "docs": "typedoc",
16
+ "generate-icons": "node ./generateIcons",
17
+ "package": "npm run all && npm run docs",
18
+ "prod": "webpack --mode=production",
19
+ "prod-icons": "webpack --mode=production --config webpack.icons.js",
20
+ "typings": "dts-bundle --configJson dts-bundle.json"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/gunjandatta/gd-bs.git"
25
+ },
26
+ "keywords": [
27
+ "Bootstrap",
28
+ "TypeScript"
29
+ ],
30
+ "author": "Gunjan Datta <me@dattabase.com> (https://dattabase.com)",
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/gunjandatta/gd-bs/issues"
34
+ },
35
+ "homepage": "https://dattabase.com/extras/bs",
36
+ "dependencies": {
37
+ "@floating-ui/dom": "^1.7.1",
38
+ "bootstrap": "^5.3.6",
39
+ "bootstrap-icons": "^1.13.1",
40
+ "core-js": "^3.43.0"
41
+ },
42
+ "devDependencies": {
43
+ "@babel/core": "^7.27.4",
44
+ "@babel/preset-env": "^7.27.2",
45
+ "@types/node": "^18.19.111",
46
+ "autoprefixer": "^10.4.21",
47
+ "babel-loader": "^9.2.1",
48
+ "css-loader": "^6.11.0",
49
+ "dts-bundle": "^0.7.3",
50
+ "postcss-loader": "^7.3.4",
51
+ "sass": "^1.89.2",
52
+ "sass-loader": "^13.3.3",
53
+ "style-loader": "^3.3.4",
54
+ "terser-webpack-plugin": "^5.3.14",
55
+ "ts-loader": "^9.5.2",
56
+ "typedoc": "^0.24.8",
57
+ "typescript": "4.9.5",
58
+ "webpack": "^5.99.9",
59
+ "webpack-cli": "^5.1.4"
60
+ }
61
61
  }
@@ -87,6 +87,9 @@ export class DropdownFormItem {
87
87
  // The component HTML element
88
88
  get el(): HTMLElement { return this._el; }
89
89
 
90
+ // Hides the item
91
+ hide() { this._el.classList.add("d-none"); }
92
+
90
93
  // Returns true if the item is selected
91
94
  get isSelected(): boolean { return this._isSelected; }
92
95
  set isSelected(value: boolean) { this._isSelected = value; }
@@ -94,6 +97,9 @@ export class DropdownFormItem {
94
97
  // The component properties
95
98
  get props(): IDropdownItem { return this._props; }
96
99
 
100
+ // Shows the item
101
+ show() { this._el.classList.remove("d-none"); }
102
+
97
103
  // Toggles the item selection
98
104
  toggle() {
99
105
  // Skip the dividers, headers
@@ -38,6 +38,7 @@ class _Dropdown extends Base<IDropdownProps> implements IDropdown {
38
38
  private _autoSelect: boolean = null;
39
39
  private _cb: ICheckboxGroup = null;
40
40
  private _elMenu: HTMLElement;
41
+ private _elSearch: HTMLInputElement;
41
42
  private _floatingUI: IFloatingUI = null;
42
43
  private _initFl: boolean = false;
43
44
  private _items: Array<DropdownFormItem | DropdownItem> = null;
@@ -52,6 +53,9 @@ class _Dropdown extends Base<IDropdownProps> implements IDropdown {
52
53
  // Configure the events
53
54
  this.configureEvents();
54
55
 
56
+ // Configure search
57
+ this.configureSearch();
58
+
55
59
  // Configure the parent
56
60
  this.configureParent();
57
61
 
@@ -258,6 +262,16 @@ class _Dropdown extends Base<IDropdownProps> implements IDropdown {
258
262
  elTarget: toggle,
259
263
  placement: typeof (this.props.placement) === "number" ? this.props.placement : FloatingUIPlacements.BottomStart,
260
264
  theme: popoverType,
265
+ onShow: () => {
266
+ // See if the search element exists
267
+ if (this._elSearch) {
268
+ // Clear the search
269
+ this._elSearch.value = "";
270
+
271
+ // Show all the items
272
+ for (let i = 0; i < this._items.length; i++) { this._items[i].show(); }
273
+ }
274
+ },
261
275
  options: {
262
276
  arrow: false,
263
277
  flip: true,
@@ -396,6 +410,73 @@ class _Dropdown extends Base<IDropdownProps> implements IDropdown {
396
410
  }
397
411
  }
398
412
 
413
+ // Configures the search option for the dropdown
414
+ private configureSearch() {
415
+ // See if search is enabled and the menu exists
416
+ if (this.props.search != true || this._elMenu == null) { return; }
417
+
418
+ // Create the search textbox
419
+ this._elSearch = document.createElement("input");
420
+ this._elSearch.classList.add("form-control");
421
+ this._elSearch.type = "search";
422
+ this._elSearch.placeholder = "Search for item...";
423
+
424
+ // Insert the item as the first element
425
+ this._elMenu.firstChild ? this._elMenu.insertBefore(this._elSearch, this._elMenu.firstChild) : this._elMenu.appendChild(this._elSearch);
426
+
427
+ // Create the empty text
428
+ let elEmptyText = document.createElement("h6")
429
+ elEmptyText.classList.add("dropdown-header");
430
+ elEmptyText.classList.add("d-none");
431
+ elEmptyText.innerHTML = "No items were found...";
432
+ this._elMenu.appendChild(elEmptyText);
433
+
434
+ // Add the element to the ignore list
435
+ this._floatingUI.addIgnoreElement(this._elSearch);
436
+
437
+ // Add the event
438
+ this._elSearch.addEventListener("input", () => {
439
+ // Get the value
440
+ let searchText = (this._elSearch.value || "").toLocaleLowerCase();
441
+
442
+ // Set the flags
443
+ let itemsFound = false;
444
+ let showAll = searchText == "";
445
+
446
+ // Hide the empty text
447
+ elEmptyText.classList.add("d-none");
448
+
449
+ // Parse the items
450
+ for (let i = 0; i < this._items.length; i++) {
451
+ let item = this._items[i];
452
+
453
+ // See if we are showing all the items
454
+ if (showAll) {
455
+ // Show the item
456
+ item.show();
457
+ } else {
458
+ // See if the value contains the text
459
+ if ((item.props.text || "").toLowerCase().indexOf(searchText) >= 0) {
460
+ // Show the item
461
+ item.show();
462
+
463
+ // Set the flag
464
+ itemsFound = true;
465
+ } else {
466
+ // Hide the item
467
+ item.hide();
468
+ }
469
+ }
470
+ }
471
+
472
+ // See if no items were found
473
+ if (!showAll && !itemsFound) {
474
+ // Show the empty message
475
+ elEmptyText.classList.remove("d-none");
476
+ }
477
+ });
478
+ }
479
+
399
480
  // Generates the checkbox items
400
481
  private generateCheckboxItems(): ICheckboxGroupItem[] {
401
482
  let cbItems: ICheckboxGroupItem[] = [];
@@ -644,6 +725,9 @@ class _Dropdown extends Base<IDropdownProps> implements IDropdown {
644
725
  }
645
726
  }
646
727
  }
728
+
729
+ // Configure search
730
+ this.configureSearch();
647
731
  }
648
732
 
649
733
  // Sets the label of the dropdown
@@ -153,12 +153,18 @@ export class DropdownItem {
153
153
  // The component HTML element
154
154
  get el(): HTMLElement { return this._el; }
155
155
 
156
+ // Hides the item
157
+ hide() { this._el.classList.add("d-none"); }
158
+
156
159
  // Returns true if the item is selected
157
160
  get isSelected(): boolean { return this._isSelected; }
158
161
 
159
162
  // The component properties
160
163
  get props(): IDropdownItem { return this._props; }
161
164
 
165
+ // Shows the item
166
+ show() { this._el.classList.remove("d-none"); }
167
+
162
168
  // Toggles the item selection
163
169
  toggle() {
164
170
  // Skip the dividers, headers and nav items
@@ -155,6 +155,7 @@ export interface IDropdownProps extends IBaseProps<IDropdown> {
155
155
  onMenuRendering?: (props: IFloatingUIProps) => IFloatingUIProps;
156
156
  placement?: number;
157
157
  required?: boolean;
158
+ search?: boolean;
158
159
  title?: string;
159
160
  type?: number;
160
161
  updateLabel?: boolean;
@@ -49,18 +49,15 @@ export enum FloatingUITypes {
49
49
  */
50
50
  class _FloatingUI {
51
51
  private _elArrow: HTMLElement = null;
52
- private _elTarget: HTMLElement = null;
53
52
  private _elContent: HTMLElement = null;
53
+ private _elIgnore: HTMLElement[] = null;
54
+ private _elTarget: HTMLElement = null;
54
55
  private _options: ComputePositionConfig = null;
55
56
  private _props: IFloatingUIProps = null;
56
57
 
57
- // Static events
58
- private static Events = [];
59
- private static EventsCreated = false;
60
- private static ScrollEvents = [];
61
-
62
58
  // Constructor
63
59
  constructor(props: IFloatingUIProps) {
60
+ this._elIgnore = [];
64
61
  this._elTarget = props.elTarget;
65
62
  this._props = props;
66
63
 
@@ -82,6 +79,7 @@ class _FloatingUI {
82
79
  this._props.show ? this.show() : this.hide();
83
80
  }
84
81
 
82
+ // Add the events to trigger, refresh and hide the element
85
83
  private addEvents(trigger: string = "") {
86
84
  // Events
87
85
  if (trigger.indexOf("mouse") >= 0) {
@@ -99,48 +97,29 @@ class _FloatingUI {
99
97
  });
100
98
  }
101
99
 
102
- // Add the events
103
- _FloatingUI.Events.push((ev: Event) => {
104
- // See if it's outside the target element
105
- if (!this._elTarget.contains(ev.target as any)) {
106
- // Hide the element
107
- this.hide();
108
- }
109
- });
110
- _FloatingUI.ScrollEvents.push((ev: Event) => {
111
- // Refresh the content
112
- this.refresh();
113
- });
100
+ // Create the event
101
+ document.addEventListener("click", (ev) => {
102
+ // Do nothing if we toggled this component
103
+ if (this._elTarget.contains(ev.target as HTMLElement)) { return; }
114
104
 
115
- // Ensure the click event exists
116
- if (!_FloatingUI.EventsCreated) {
117
- // Create the event
118
- document.addEventListener("click", (ev) => {
119
- // Wait for the other events to run
120
- setTimeout(() => {
121
- // Parse the events
122
- _FloatingUI.Events.forEach(fnEvent => {
123
- // Call the event
124
- fnEvent(ev);
125
- });
126
- }, 10);
127
- });
105
+ // Parse the elements to ignore
106
+ for (let i = 0; i < this._elIgnore.length; i++) {
107
+ // Do nothing if it triggered the click
108
+ if (this._elIgnore[i].contains(ev.target as HTMLElement)) { return; }
109
+ }
128
110
 
129
- // Create the scroll event
130
- window.addEventListener("scroll", (ev) => {
131
- // Wait for the other events to run
132
- setTimeout(() => {
133
- // Parse the events
134
- _FloatingUI.ScrollEvents.forEach(fnEvent => {
135
- // Call the event
136
- fnEvent(ev);
137
- });
138
- }, 10);
139
- });
111
+ // Hide the element
112
+ this.hide();
113
+ });
140
114
 
141
- // Set the flag
142
- _FloatingUI.EventsCreated = true;
143
- }
115
+ // Create the scroll event
116
+ window.addEventListener("scroll", (ev) => {
117
+ // Wait for the other events to run
118
+ setTimeout(() => {
119
+ // Refresh the content
120
+ this.refresh();
121
+ }, 10);
122
+ });
144
123
  }
145
124
 
146
125
  // Creates the floating ui
@@ -346,12 +325,16 @@ class _FloatingUI {
346
325
  if (this._elArrow) {
347
326
  let arrowX = middlewareData.arrow.x;
348
327
  let arrowY = middlewareData.arrow.y;
328
+ let placement = (middlewareData.offset?.placement || this._options.placement).split('-')[0];
349
329
  let side = {
350
330
  top: 'bottom',
351
331
  right: 'left',
352
332
  bottom: 'top',
353
333
  left: 'right'
354
- }[(middlewareData.offset?.placement || this._options.placement).split('-')[0]]
334
+ }[placement]
335
+
336
+ // Set the placement
337
+ this._elContent.setAttribute("data-placement", placement);
355
338
 
356
339
  // Update the location
357
340
  Object.assign(this._elArrow.style, {
@@ -369,13 +352,33 @@ class _FloatingUI {
369
352
  * Public Methods
370
353
  */
371
354
 
355
+ addIgnoreElement(el: HTMLElement) { this._elIgnore.push(el); }
356
+
357
+ removeIgnoreElement(el: HTMLElement) {
358
+ // Parse the elements
359
+ for (let i = 0; i < this._elIgnore.length; i++) {
360
+ // See if this is the element to remove
361
+ if (this._elIgnore[i].isEqualNode(el)) {
362
+ // Remove it
363
+ this._elIgnore.splice(i, 1);
364
+ return;
365
+ }
366
+ }
367
+ }
368
+
372
369
  setContent(el) { this._elContent = el; this.refresh(); }
373
370
 
374
371
  // Hides the content
375
372
  hide() {
376
373
  // Remove it from the document
377
374
  this._elContent.classList.add("d-none");
378
- if (document.body.contains(this._elContent)) { document.body.removeChild(this._elContent); }
375
+ if (document.body.contains(this._elContent)) {
376
+ // Remove the element from the page
377
+ document.body.removeChild(this._elContent);
378
+
379
+ // Call the event
380
+ this._props.onHide ? this._props.onHide() : null;
381
+ }
379
382
  }
380
383
 
381
384
  // Determines if the content is visible
@@ -385,7 +388,16 @@ class _FloatingUI {
385
388
  show() {
386
389
  // Append it to the document
387
390
  this._elContent.classList.remove("d-none");
388
- if (!document.body.contains(this._elContent)) { document.body.appendChild(this._elContent); this.refresh(); }
391
+ if (!document.body.contains(this._elContent)) {
392
+ // Add the element to the page
393
+ document.body.appendChild(this._elContent);
394
+
395
+ // Refresh the position
396
+ this.refresh();
397
+
398
+ // Call the event
399
+ this._props.onShow ? this._props.onShow() : null;
400
+ }
389
401
  }
390
402
 
391
403
  // Toggles the floating ui
@@ -5,8 +5,10 @@ import { IBaseProps } from "../types";
5
5
  export const FloatingUIPlacements: IFloatingUIPlacements;
6
6
 
7
7
  export interface IFloatingUI {
8
+ addIgnoreElement: (el: Element) => void;
8
9
  hide: () => void;
9
10
  isVisible: boolean;
11
+ removeIgnoreElement: (el: Element) => void;
10
12
  setContent: (el: string | Element) => void;
11
13
  show: () => void;
12
14
  toggle: () => void;
@@ -29,6 +31,8 @@ export interface IFloatingUIOptions {
29
31
  export interface IFloatingUIProps extends IBaseProps<IFloatingUI> {
30
32
  elContent: HTMLElement;
31
33
  elTarget: HTMLElement;
34
+ onHide?: (el?: HTMLElement) => void;
35
+ onShow?: (el?: HTMLElement) => void;
32
36
  options?: IFloatingUIOptions;
33
37
  placement?: number;
34
38
  show?: boolean;