Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
6056 efrain 1
import $ from 'jquery';
2
import Popover from 'bootstrap/js/src/popover';
3
 
4
/**
5
 * ------------------------------------------------------------------------
6
 * Constants
7
 * ------------------------------------------------------------------------
8
 */
9
 
10
const NAME = 'confirmation';
11
const VERSION = '$VERSION';
12
const DATA_KEY = `bs.${NAME}`;
13
const EVENT_KEY = `.${DATA_KEY}`;
14
const JQUERY_NO_CONFLICT = $.fn[NAME];
15
const BTN_CLASS_BASE = 'h-100 d-flex align-items-center';
16
const BTN_CLASS_DEFAULT = 'btn btn-sm';
17
 
18
const DefaultType = {
19
  ...Popover.DefaultType,
20
  singleton           : 'boolean',
21
  popout              : 'boolean',
22
  copyAttributes      : '(string|array)',
23
  onConfirm           : 'function',
24
  onCancel            : 'function',
25
  btnOkClass          : 'string',
26
  btnOkLabel          : 'string',
27
  btnOkIconClass      : 'string',
28
  btnOkIconContent    : 'string',
29
  btnCancelClass      : 'string',
30
  btnCancelLabel      : 'string',
31
  btnCancelIconClass  : 'string',
32
  btnCancelIconContent: 'string',
33
  buttons             : 'array',
34
};
35
 
36
const Default = {
37
  ...Popover.Default,
38
  _attributes         : {},
39
  _selector           : null,
40
  placement           : 'top',
41
  title               : 'Are you sure?',
42
  trigger             : 'click',
43
  confirmationEvent   : undefined,
44
  content             : '',
45
  singleton           : false,
46
  popout              : false,
47
  copyAttributes      : 'href target',
48
  onConfirm           : $.noop,
49
  onCancel            : $.noop,
50
  btnOkClass          : `${BTN_CLASS_DEFAULT} btn-primary`,
51
  btnOkLabel          : 'Yes',
52
  btnOkIconClass      : '',
53
  btnOkIconContent    : '',
54
  btnCancelClass      : `${BTN_CLASS_DEFAULT} btn-secondary`,
55
  btnCancelLabel      : 'No',
56
  btnCancelIconClass  : '',
57
  btnCancelIconContent: '',
58
  buttons             : [],
59
  // @formatter:off
60
  template            : `
61
<div class="popover confirmation">
62
  <div class="arrow"></div>
63
  <h3 class="popover-header"></h3>
64
  <div class="popover-body">
65
    <p class="confirmation-content"></p>
66
    <div class="confirmation-buttons text-center">
67
      <div class="btn-group"></div>
68
    </div>
69
  </div>
70
</div>`,
71
  // @formatter:on
72
};
73
 
74
if (Default.whiteList) {
75
  Default.whiteList['*'].push('data-apply', 'data-dismiss');
76
}
77
 
78
const ClassName = {
79
  FADE: 'fade',
80
  SHOW: 'show',
81
};
82
 
83
const Selector = {
84
  TITLE  : '.popover-header',
85
  CONTENT: '.confirmation-content',
86
  BUTTONS: '.confirmation-buttons .btn-group',
87
};
88
 
89
const Keymap = {
90
  13: 'Enter',
91
  27: 'Escape',
92
  39: 'ArrowRight',
93
  40: 'ArrowDown',
94
};
95
 
96
const Event = {
97
  HIDE      : `hide${EVENT_KEY}`,
98
  HIDDEN    : `hidden${EVENT_KEY}`,
99
  SHOW      : `show${EVENT_KEY}`,
100
  SHOWN     : `shown${EVENT_KEY}`,
101
  INSERTED  : `inserted${EVENT_KEY}`,
102
  CLICK     : `click${EVENT_KEY}`,
103
  FOCUSIN   : `focusin${EVENT_KEY}`,
104
  FOCUSOUT  : `focusout${EVENT_KEY}`,
105
  MOUSEENTER: `mouseenter${EVENT_KEY}`,
106
  MOUSELEAVE: `mouseleave${EVENT_KEY}`,
107
  CONFIRMED : `confirmed${EVENT_KEY}`,
108
  CANCELED  : `canceled${EVENT_KEY}`,
109
  KEYUP     : `keyup${EVENT_KEY}`,
110
};
111
 
112
/**
113
 * ------------------------------------------------------------------------
114
 * Class Definition
115
 * ------------------------------------------------------------------------
116
 */
117
 
118
// keep track of the last openned confirmation for keyboard navigation
119
let activeConfirmation;
120
 
121
class Confirmation extends Popover {
122
  // Getters
123
 
124
  static get VERSION() {
125
    return VERSION;
126
  }
127
 
128
  static get Default() {
129
    return Default;
130
  }
131
 
132
  static get NAME() {
133
    return NAME;
134
  }
135
 
136
  static get DATA_KEY() {
137
    return DATA_KEY;
138
  }
139
 
140
  static get Event() {
141
    return Event;
142
  }
143
 
144
  static get EVENT_KEY() {
145
    return EVENT_KEY;
146
  }
147
 
148
  static get DefaultType() {
149
    return DefaultType;
150
  }
151
 
152
  // Constructor
153
 
154
  constructor(element, config) {
155
    super(element, config);
156
 
157
    if ((this.config.popout || this.config.singleton) && !this.config.rootSelector) {
158
      throw new Error('The rootSelector option is required to use popout and singleton features since jQuery 3.');
159
    }
160
 
161
    // keep trace of selectors
162
    this._isDelegate = false;
163
 
164
    if (config.selector) { // container of buttons
165
      config._selector = `${config.rootSelector} ${config.selector}`;
166
      this.config._selector = config._selector;
167
    }
168
    else if (config._selector) { // children of container
169
      this.config._selector = config._selector;
170
      this._isDelegate = true;
171
    }
172
    else { // standalone
173
      this.config._selector = config.rootSelector;
174
    }
175
 
176
    if (this.config.confirmationEvent === undefined) {
177
      this.config.confirmationEvent = this.config.trigger;
178
    }
179
 
180
    if (!this.config.selector) {
181
      this._copyAttributes();
182
    }
183
 
184
    this._setConfirmationListeners();
185
  }
186
 
187
  // Overrides
188
 
189
  isWithContent() {
190
    return true;
191
  }
192
 
193
  setContent() {
194
    const $tip = $(this.getTipElement());
195
    let content = this._getContent();
196
 
197
    if (typeof content === 'function') {
198
      content = content.call(this.element);
199
    }
200
 
201
    this.setElementContent($tip.find(Selector.TITLE), this.getTitle());
202
 
203
    $tip.find(Selector.CONTENT).toggle(!!content);
204
    if (content) {
205
      this.setElementContent($tip.find(Selector.CONTENT), content);
206
    }
207
 
208
    if (this.config.buttons.length > 0) {
209
      this._setButtons($tip, this.config.buttons);
210
    }
211
    else {
212
      this._setStandardButtons($tip);
213
    }
214
 
215
    $tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`);
216
 
217
    this._setupKeyupEvent();
218
  }
219
 
220
  dispose() {
221
    $('body').off(`${Event.CLICK}.${this.uid}`);
222
    this.eventBody = false;
223
    this._cleanKeyupEvent();
224
    super.dispose();
225
  }
226
 
227
  hide(callback) {
228
    this._cleanKeyupEvent();
229
    super.hide(callback);
230
  }
231
 
232
  // Private
233
 
234
  /**
235
   * Copy the value of `copyAttributes` on the config object
236
   * @private
237
   */
238
  _copyAttributes() {
239
    this.config._attributes = {};
240
    if (this.config.copyAttributes) {
241
      if (typeof this.config.copyAttributes === 'string') {
242
        this.config.copyAttributes = this.config.copyAttributes.split(' ');
243
      }
244
    }
245
    else {
246
      this.config.copyAttributes = [];
247
    }
248
 
249
    this.config.copyAttributes.forEach((attr) => {
250
      this.config._attributes[attr] = $(this.element).attr(attr);
251
    });
252
  }
253
 
254
  /**
255
   * Custom event listeners for popouts and singletons
256
   * @private
257
   */
258
  _setConfirmationListeners() {
259
    const self = this;
260
 
261
    if (!this.config.selector) {
262
      // cancel original event
263
      $(this.element).on(this.config.trigger, (e, ack) => {
264
        if (!ack) {
265
          e.preventDefault();
266
          e.stopPropagation();
267
          e.stopImmediatePropagation();
268
        }
269
      });
270
 
271
      // manage singleton
272
      $(this.element).on(Event.SHOWN, function () {
273
        if (self.config.singleton) {
274
          // close all other popover already initialized
275
          $(self.config._selector).not($(this)).filter(function () {
276
            return $(this).data(DATA_KEY) !== undefined;
277
          }).confirmation('hide');
278
        }
279
      });
280
    }
281
    else {
282
      // cancel original event
283
      $(this.element).on(this.config.trigger, this.config.selector, (e, ack) => {
284
        if (!ack) {
285
          e.preventDefault();
286
          e.stopPropagation();
287
          e.stopImmediatePropagation();
288
        }
289
      });
290
    }
291
 
292
    if (!this._isDelegate) {
293
      // manage popout
294
      this.eventBody = false;
295
      this.uid = this.element.id || Confirmation.getUID(`${NAME}_group`);
296
 
297
      $(this.element).on(Event.SHOWN, () => {
298
        if (self.config.popout && !self.eventBody) {
299
          self.eventBody = $('body').on(`${Event.CLICK}.${self.uid}`, (e) => {
300
            if ($(self.config._selector).is(e.target) || $(self.config._selector).has(e.target).length > 0) {
301
              return;
302
            }
303
            // close all popover already initialized
304
            $(self.config._selector).filter(function () {
305
              return $(this).data(DATA_KEY) !== undefined;
306
            }).confirmation('hide');
307
 
308
            $('body').off(`${Event.CLICK}.${self.uid}`);
309
            self.eventBody = false;
310
          });
311
        }
312
      });
313
    }
314
  }
315
 
316
  /**
317
   * Init the standard ok/cancel buttons
318
   * @param $tip
319
   * @private
320
   */
321
  _setStandardButtons($tip) {
322
    const buttons = [
323
      {
324
        class      : this.config.btnOkClass,
325
        label      : this.config.btnOkLabel,
326
        iconClass  : this.config.btnOkIconClass,
327
        iconContent: this.config.btnOkIconContent,
328
        attr       : this.config._attributes,
329
      },
330
      {
331
        class      : this.config.btnCancelClass,
332
        label      : this.config.btnCancelLabel,
333
        iconClass  : this.config.btnCancelIconClass,
334
        iconContent: this.config.btnCancelIconContent,
335
        cancel     : true,
336
      },
337
    ];
338
 
339
    this._setButtons($tip, buttons);
340
  }
341
 
342
  /**
343
   * Init the buttons
344
   * @param $tip
345
   * @param buttons
346
   * @private
347
   */
348
  _setButtons($tip, buttons) {
349
    const self = this;
350
    const $group = $tip.find(Selector.BUTTONS).empty();
351
 
352
    buttons.forEach((button) => {
353
      const btn = $('<a href="#"></a>')
354
        .addClass(BTN_CLASS_BASE)
355
        .addClass(button.class || `${BTN_CLASS_DEFAULT} btn-secondary`)
356
        .html(button.label || '')
357
        .attr(button.attr || {});
358
 
359
      if (button.iconClass || button.iconContent) {
360
        btn.prepend($('<i></i>')
361
          .addClass(button.iconClass || '')
362
          .text(button.iconContent || ''));
363
      }
364
 
365
      btn.one('click', function (e) {
366
        if ($(this).attr('href') === '#') {
367
          e.preventDefault();
368
        }
369
 
370
        if (button.onClick) {
371
          button.onClick.call($(self.element));
372
        }
373
 
374
        if (button.cancel) {
375
          self.config.onCancel.call(self.element, button.value);
376
          $(self.element).trigger(Event.CANCELED, [button.value]);
377
        }
378
        else {
379
          self.config.onConfirm.call(self.element, button.value);
380
          $(self.element).trigger(Event.CONFIRMED, [button.value]);
381
          $(self.element).trigger(self.config.confirmationEvent, [true]);
382
        }
383
 
384
        self.hide();
385
      });
386
 
387
      $group.append(btn);
388
    });
389
  }
390
 
391
  /**
392
   * Install the keyboatd event handler
393
   * @private
394
   */
395
  _setupKeyupEvent() {
396
    activeConfirmation = this;
397
    $(window)
398
      .off(Event.KEYUP)
399
      .on(Event.KEYUP, this._onKeyup.bind(this));
400
  }
401
 
402
  /**
403
   * Remove the keyboard event handler
404
   * @private
405
   */
406
  _cleanKeyupEvent() {
407
    if (activeConfirmation === this) {
408
      activeConfirmation = undefined;
409
      $(window).off(Event.KEYUP);
410
    }
411
  }
412
 
413
  /**
414
   * Event handler for keyboard navigation
415
   * @param event
416
   * @private
417
   */
418
  _onKeyup(event) {
419
    if (!this.tip) {
420
      this._cleanKeyupEvent();
421
      return;
422
    }
423
 
424
    const $tip = $(this.getTipElement());
425
    const key = event.key || Keymap[event.keyCode || event.which];
426
 
427
    const $group = $tip.find(Selector.BUTTONS);
428
    const $active = $group.find('.active');
429
    let $next;
430
 
431
    switch (key) {
432
      case 'Escape':
433
        this.hide();
434
        break;
435
 
436
      case 'ArrowRight':
437
        if ($active.length && $active.next().length) {
438
          $next = $active.next();
439
        }
440
        else {
441
          $next = $group.children().first();
442
        }
443
        $active.removeClass('active');
444
        $next.addClass('active').focus();
445
        break;
446
 
447
      case 'ArrowLeft':
448
        if ($active.length && $active.prev().length) {
449
          $next = $active.prev();
450
        }
451
        else {
452
          $next = $group.children().last();
453
        }
454
        $active.removeClass('active');
455
        $next.addClass('active').focus();
456
        break;
457
 
458
      default:
459
        break;
460
    }
461
  }
462
 
463
  // Static
464
 
465
  /**
466
   * Generates an uui, copied from Bootrap's utils
467
   * @param {string} prefix
468
   * @returns {string}
469
   */
470
  static getUID(prefix) {
471
    let uid = prefix;
472
    do {
473
      // eslint-disable-next-line no-bitwise
474
      uid += ~~(Math.random() * 1000000); // "~~" acts like a faster Math.floor() here
475
    } while (document.getElementById(uid));
476
    return uid;
477
  }
478
 
479
  static _jQueryInterface(config) {
480
    return this.each(function () {
481
      let data = $(this).data(DATA_KEY);
482
 
483
      const _config = typeof config === 'object' ? config : {};
484
      _config.rootSelector = $(this).selector || _config.rootSelector; // this.selector removed in jQuery > 3
485
 
486
      if (!data && /destroy|hide/.test(config)) {
487
        return;
488
      }
489
 
490
      if (!data) {
491
        data = new Confirmation(this, _config);
492
        $(this).data(DATA_KEY, data);
493
      }
494
 
495
      if (typeof config === 'string') {
496
        if (typeof data[config] === 'undefined') {
497
          throw new TypeError(`No method named "${config}"`);
498
        }
499
        data[config]();
500
      }
501
    });
502
  }
503
}
504
 
505
/**
506
 * ------------------------------------------------------------------------
507
 * jQuery
508
 * ------------------------------------------------------------------------
509
 */
510
 
511
$.fn[NAME] = Confirmation._jQueryInterface;
512
$.fn[NAME].Constructor = Confirmation;
513
$.fn[NAME].noConflict = function () {
514
  $.fn[NAME] = JQUERY_NO_CONFLICT;
515
  return Confirmation._jQueryInterface;
516
};
517
 
518
export default Confirmation;