@nys-cui/cui-section 0.2.18 → 0.2.20

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/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "main": "./dist/js/section.js",
8
8
  "type": "module",
9
- "version": "0.2.18",
9
+ "version": "0.2.20",
10
10
  "scripts": {
11
11
  "clean": "rm -rf ./dist",
12
12
  "build": "npm run clean && cui --dep",
package/src/section.js CHANGED
@@ -29,11 +29,18 @@ export default class CUI_SECTION extends HTMLElement {
29
29
  this.oPropsToStateNames = {
30
30
  "sectiontitle": "sTitle",
31
31
  "hideControls": "bHideControls",
32
+ "hideHeader": "bHideHeader",
32
33
  "collapsed": "bCollapsed",
33
34
  "styles": "asStyles",
34
35
  "label": "sTitle",
35
36
  "instructions": "oInstructions"
36
37
  }
38
+
39
+ this.bSectionMenuDisplayed = false;
40
+ this.dSectionMenu = null;
41
+ this.bMobile = false;
42
+ this.dSectionMenuDialog = null;
43
+ this.fGlobalBodyClick = this.globalBodyClick.bind(this);
37
44
  }
38
45
 
39
46
  get style() {
@@ -42,34 +49,38 @@ export default class CUI_SECTION extends HTMLElement {
42
49
 
43
50
  get template() {
44
51
 
45
- let sTemplate = `<section part="root-section"><header part="section-header">
46
- <div class='header-info' part='header-info'><h2 id="section-title" part="section-title"></h2><slot name='section-help-tag'></slot></div>`;
52
+ let sTemplate = `<section part="root-section"><header part="section-header ${this.state.bHideHeader ? `headless" class="headless"` : `"`}>`;
47
53
 
48
- if (!this.state.bHideControls) {
54
+ if(!this.state.bHideHeader) {
55
+ sTemplate += `<div class='header-info' part='header-info'><h2 id="section-title" part="section-title"></h2><slot name='section-help-tag'></slot></div>`;
49
56
 
50
- if (this.sIconBasePath) {
57
+ if (!this.state.bHideControls) {
51
58
 
52
- sTemplate += `
53
- <div class="header-controls" part="header-controls">
54
- <button type="button" id="collapse-control" aria-controls="container" part="collapse-control">
55
- <cui-icon src="caret-down" basePath="${this.sIconBasePath}" part="collapse-control-icon"></cui-icon>
56
- </button>
57
- </div>`;
59
+ if (this.sIconBasePath) {
58
60
 
59
- }
60
- else {
61
+ sTemplate += `
62
+ <div class="header-controls" part="header-controls">
63
+ <button type="button" id="collapse-control" aria-controls="container" part="collapse-control">
64
+ <cui-icon src="caret-down" basePath="${this.sIconBasePath}" part="collapse-control-icon"></cui-icon>
65
+ </button>
66
+ </div>`;
61
67
 
62
- sTemplate += `
63
- <div class="header-controls" part="header-controls">
64
- <button type="button" id="collapse-control" aria-controls="container" part="collapse-control">
65
- <cui-icon src="caret-down" part="collapse-control-icon"></cui-icon>
66
- </button>
67
- </div>`;
68
- }
68
+ }
69
+ else {
70
+
71
+ sTemplate += `
72
+ <div class="header-controls" part="header-controls">
73
+ <button type="button" id="collapse-control" aria-controls="container" part="collapse-control">
74
+ <cui-icon src="caret-down" part="collapse-control-icon"></cui-icon>
75
+ </button>
76
+ </div>`;
77
+ }
69
78
 
79
+ }
70
80
  }
71
81
 
72
82
  sTemplate += `</header><div class="container" id="container" part="container">
83
+ <slot name="section-menu"></slot>
73
84
  <slot name="section-text"></slot>
74
85
  <slot name="section-messages"></slot>
75
86
  <slot name="section-contents"></slot>
@@ -202,11 +213,18 @@ export default class CUI_SECTION extends HTMLElement {
202
213
  this.state = {
203
214
  sTitle: this.state.sTitle || this.getAttribute('sectiontitle') || "SECTION TITLE",
204
215
  bHideControls: this.state.bHideControls || this.hasAttribute("hideControls") ? true : false,
216
+ bHideHeader: this.state.bHideHeader || this.hasAttribute("hideHeader") ? true : false,
205
217
  bCollapsed: this.state.bCollapsed || (this.getAttribute('collapsed') === "true") ? true : false,
206
218
  asStyles: this.state.asStyles || (this.getAttribute('styles')) ? this.getAttribute('styles').split(',') : null,
207
219
  oInstructions: this.state.oInstructions || null
208
220
  }
209
221
 
222
+ let match = window.matchMedia || window.msMatchMedia;
223
+ if(match) {
224
+ let mq = match("(pointer:coarse)");
225
+ this.bMobile = mq.matches;
226
+ }
227
+
210
228
  this.sIconBasePath = this.getAttribute('iconbase') || null;
211
229
 
212
230
  let dMessageList = this.querySelector(`[slot="section-messages"]`);
@@ -225,9 +243,11 @@ export default class CUI_SECTION extends HTMLElement {
225
243
  this.sdSectionTitle = this.shadowRoot.querySelector(`#section-title`);
226
244
  this.sdContentsContainer = this.shadowRoot.querySelector(`#container`);
227
245
 
228
- this.sdSectionTitle.appendChild(document.createTextNode(this.state.sTitle));
246
+ if(!this.state.bHideHeader) {
247
+ this.sdSectionTitle.appendChild(document.createTextNode(this.state.sTitle));
248
+ }
229
249
 
230
- if (!this.state.bHideControls) {
250
+ if (!this.state.bHideControls && !this.state.bHideHeader) {
231
251
 
232
252
  this.sdSectionCollapseControl = this.shadowRoot.querySelector(`#collapse-control`);
233
253
 
@@ -299,6 +319,136 @@ export default class CUI_SECTION extends HTMLElement {
299
319
  this.appendChild(dSectionInstructions);
300
320
  }
301
321
 
322
+ let dSectionMenuSlot = this.querySelector('[slot="section-menu"]');
323
+ if(dSectionMenuSlot && dSectionMenuSlot.hasChildNodes()) {
324
+ this.generateSectionMenuButton(dSectionMenuSlot);
325
+ }
326
+ }
327
+
328
+ generateSectionMenuButton(dSectionMenuSlot) {
329
+ let dMenuItems = dSectionMenuSlot.querySelectorAll('cui-item');
330
+
331
+ if(dMenuItems) {
332
+ let dHeaderControls = this.shadowRoot.querySelector('.header-controls');
333
+ let dCollapseControl = dHeaderControls.querySelector('#collapse-control');
334
+
335
+ let dMenuButton = document.createElement('button');
336
+ dMenuButton.classList.add('section-menu-btn');
337
+ dMenuButton.setAttribute('part', 'section-menu-btn');
338
+ dMenuButton.setAttribute('id', 'section-menu-btn');
339
+ dMenuButton.addEventListener('click', this.sectionMenuClick.bind(this, dMenuItems));
340
+
341
+ let dMenuIcon = document.createElement('cui-icon');
342
+ dMenuIcon.setAttribute('src', 'ellipsis-vertical');
343
+ dMenuIcon.setAttribute('part', 'section-menu-icon');
344
+
345
+ dMenuButton.appendChild(dMenuIcon);
346
+ dCollapseControl.before(dMenuButton);
347
+ }
348
+ }
349
+
350
+ sectionMenuClick(dMenuItems, evt) {
351
+ evt.stopImmediatePropagation();
352
+ let dActiveBody = this.closest('body, dialog, modal');
353
+ if(this.bSectionMenuDisplayed) {
354
+ this.hideSectionMenu();
355
+ dActiveBody.removeEventListener('click', this.fGlobalBodyClick, false);
356
+ dActiveBody.removeEventListener('screenUnloadComplete', this.fGlobalBodyClick, false);
357
+ }
358
+ else {
359
+ this.displaySectionMenu(dMenuItems, dActiveBody);
360
+ dActiveBody.addEventListener('click', this.fGlobalBodyClick, false);
361
+ dActiveBody.addEventListener('screenUnloadComplete', this.fGlobalBodyClick, false);
362
+ }
363
+ }
364
+
365
+ displaySectionMenu(dMenuItems) {
366
+ if(this.dSectionMenu) {
367
+ if(this.bMobile) {
368
+ this.dSectionMenuDialog.showModal();
369
+ }
370
+ else {
371
+ this.shadowRoot.appendChild(this.dSectionMenu);
372
+ }
373
+ }
374
+ else {
375
+ let dMenu = document.createElement('div');
376
+ dMenu.classList.add('section-menu-popover');
377
+ dMenu.setAttribute('part', 'section-menu-popover');
378
+ let bFirstMenuItem = true;
379
+ for(let dMenuItem of dMenuItems) {
380
+ let dMenuOption = document.createElement('button');
381
+ dMenuOption.innerHTML = dMenuItem.getAttribute('text');
382
+ if(bFirstMenuItem) {
383
+ dMenuOption.classList.add('section-menu-item');
384
+ dMenuOption.setAttribute('part', 'section-menu-item');
385
+ bFirstMenuItem = false;
386
+ }
387
+ else {
388
+ dMenuOption.classList.add(['section-menu-item', 'not-first']);
389
+ dMenuOption.setAttribute('part', 'section-menu-item not-first');
390
+ }
391
+ dMenuOption.addEventListener('click', this.emitActionEvent.bind(this, dMenuItem.getAttribute('action')));
392
+ dMenu.appendChild(dMenuOption);
393
+ }
394
+ this.dSectionMenu = dMenu;
395
+ if(this.bMobile) {
396
+ let dMenuDialog = document.createElement('dialog');
397
+ dMenuDialog.setAttribute('id', 'section-menu-dialog');
398
+ dMenuDialog.classList.add('section-menu-dialog');
399
+ dMenuDialog.setAttribute('part', 'section-menu-dialog');
400
+ dMenuDialog.setAttribute('closedby', 'any');
401
+
402
+ dMenu.classList.add('dialog');
403
+ dMenu.setAttribute('part', dMenu.getAttribute('part') + ' dialog');
404
+
405
+ dMenuDialog.appendChild(dMenu);
406
+ this.shadowRoot.appendChild(dMenuDialog);
407
+ dMenuDialog.showModal();
408
+ this.dSectionMenuDialog = dMenuDialog;
409
+ }
410
+ else {
411
+ this.shadowRoot.appendChild(dMenu);
412
+ }
413
+ }
414
+ this.bSectionMenuDisplayed = true;
415
+ }
416
+
417
+ emitActionEvent(sAction) {
418
+ const E_CONTROL_EVENT = new CustomEvent("eventAction", {
419
+ bubbles: true,
420
+ composed: true,
421
+ detail: {
422
+ target: this,
423
+ name: sAction
424
+ }
425
+ });
426
+
427
+ this.dispatchEvent(E_CONTROL_EVENT);
428
+ this.hideSectionMenu();
429
+ }
430
+
431
+ hideSectionMenu() {
432
+ if(this.bSectionMenuDisplayed === false) {
433
+ return;
434
+ }
435
+
436
+ if(this.bMobile) {
437
+ this.dSectionMenuDialog.close();
438
+ }
439
+ else {
440
+ this.shadowRoot.removeChild(this.dSectionMenu);
441
+ }
442
+
443
+ this.bSectionMenuDisplayed = false;
444
+ }
445
+
446
+ globalBodyClick(evt) {
447
+ if(this.bSectionMenuDisplayed && !this.dSectionMenu.contains(evt.target)) {
448
+ this.hideSectionMenu();
449
+ evt.currentTarget.removeEventListener('click', this.fGlobalBodyClick, false);
450
+ evt.currentTarget.removeEventListener('screenUnloadComplete', this.fGlobalBodyClick, false);
451
+ }
302
452
  }
303
453
 
304
454
  get messages() {
package/src/section.scss CHANGED
@@ -33,16 +33,16 @@
33
33
  }
34
34
 
35
35
  .header-controls {
36
- flex: 0 1 0;
36
+ flex: 0 1 auto;
37
37
  margin: 10px 0 3px;
38
38
 
39
- button {
39
+ button#collapse-control,
40
+ button#section-menu-btn {
40
41
  background: transparent;
41
42
  border: 0;
42
43
  border-radius: 32px;
43
44
  cursor: pointer;
44
45
  height: 32px;
45
- margin-left: 0.5em;
46
46
  margin-top: -7px;
47
47
  transform: rotate(180deg);
48
48
  width: 32px;
@@ -66,6 +66,19 @@
66
66
  transform: rotate(0);
67
67
  }
68
68
  }
69
+
70
+ button#collapse-control {
71
+ margin-left: 0.5em;
72
+ }
73
+
74
+ button#section-menu-btn {
75
+ anchor-name: --section-menu;
76
+ }
77
+ }
78
+
79
+ &.headless {
80
+ border-bottom: none;
81
+ margin: 0.75em 0.75em;
69
82
  }
70
83
 
71
84
  }
@@ -91,6 +104,19 @@
91
104
  }
92
105
  }
93
106
 
107
+ .section-menu-popover {
108
+ display: flex;
109
+ flex-direction: column;
110
+ align-items: flex-end;
111
+
112
+ &:not(.dialog) {
113
+ position: fixed;
114
+ position-anchor: --section-menu;
115
+ top: anchor(bottom);
116
+ right: anchor(right);
117
+ }
118
+ }
119
+
94
120
  }
95
121
 
96
122
  :host([subsection]) {