@nys-cui/cui-section 0.1.2
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/README.md +1 -0
- package/cui.jsonc +6 -0
- package/package.json +15 -0
- package/src/section.js +211 -0
- package/src/section.scss +106 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Prototype project being pushed to node and npm for testing
|
package/cui.jsonc
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nys-cui/cui-section",
|
|
3
|
+
"description": "A generic section component used with Core UI 2",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "New York State Department of Tax and Finance"
|
|
6
|
+
},
|
|
7
|
+
"main": "./dist/js/section.js",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"version": "0.1.2",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"clean": "rm -rf ./dist",
|
|
12
|
+
"build": "npm run clean && cui --dep",
|
|
13
|
+
"postinstall": "node -e \"console.log('Post-Installing cui-section: %s', Date())\" && npm run build"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/src/section.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import SECTION_STYLES from './section.scss';
|
|
2
|
+
|
|
3
|
+
export default class CUI_SECTION extends HTMLElement {
|
|
4
|
+
|
|
5
|
+
constructor() {
|
|
6
|
+
|
|
7
|
+
super();
|
|
8
|
+
|
|
9
|
+
this.attachShadow({mode: 'open'});
|
|
10
|
+
|
|
11
|
+
this.state = {
|
|
12
|
+
sTitle: null
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
this.dSection = this;
|
|
16
|
+
|
|
17
|
+
// Key Shadow DOM Fields
|
|
18
|
+
this.sdSection = null;
|
|
19
|
+
this.sdSectionTitle = null;
|
|
20
|
+
this.sdSectionCollapseControl = null;
|
|
21
|
+
this.sdContentsContainer = null;
|
|
22
|
+
|
|
23
|
+
this.bLock = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get style() {
|
|
27
|
+
return `<style>${SECTION_STYLES}</style>`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get template() {
|
|
31
|
+
|
|
32
|
+
let sTemplate = `<section><header>
|
|
33
|
+
<h2 id="section-title"></h2>`;
|
|
34
|
+
|
|
35
|
+
if (!this.state.bHideControls) {
|
|
36
|
+
|
|
37
|
+
sTemplate += `
|
|
38
|
+
<div class="header-controls">
|
|
39
|
+
<button type="button" id="collapse-control">
|
|
40
|
+
<cui-icon src="caret-down"></cui-icon>
|
|
41
|
+
</button>
|
|
42
|
+
</div>`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
sTemplate += `</header><div class="container" id="container">
|
|
46
|
+
<slot name="section-text"></slot>
|
|
47
|
+
<slot name="section-contents"></slot>
|
|
48
|
+
<slot name="section-footer"></slot>
|
|
49
|
+
</div></section>`;
|
|
50
|
+
|
|
51
|
+
return sTemplate;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
expand(dContainer) {
|
|
55
|
+
|
|
56
|
+
function transitionEnd() {
|
|
57
|
+
|
|
58
|
+
// remove "height" from the dContainer's inline styles, so it can return to its initial value
|
|
59
|
+
dContainer.style.height = "";
|
|
60
|
+
|
|
61
|
+
// mark the section as "currently not collapsed"
|
|
62
|
+
dContainer.setAttribute('data-collapsed', 'false');
|
|
63
|
+
|
|
64
|
+
// remove this event listener so it only gets triggered once
|
|
65
|
+
dContainer.removeEventListener('transitionend', transitionEnd, this);
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.bLock = true;
|
|
70
|
+
|
|
71
|
+
var sectionHeight = dContainer.scrollHeight;
|
|
72
|
+
|
|
73
|
+
// have the dContainer transition to the height of its inner content
|
|
74
|
+
dContainer.style.height = sectionHeight + 'px';
|
|
75
|
+
|
|
76
|
+
// when the next css transition finishes (which should be the one we just triggered)
|
|
77
|
+
dContainer.addEventListener('transitionend', transitionEnd, true);
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
collapse(dContainer) {
|
|
83
|
+
|
|
84
|
+
function transitionEnd(e) {
|
|
85
|
+
|
|
86
|
+
// mark the section as "currently collapsed"
|
|
87
|
+
dContainer.setAttribute('data-collapsed', 'true');
|
|
88
|
+
|
|
89
|
+
dContainer.style.height = "";
|
|
90
|
+
|
|
91
|
+
dContainer.removeEventListener('transitionend', transitionEnd, true);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// get the height of the dContainer's inner content, regardless of its actual size
|
|
95
|
+
var sectionHeight = dContainer.scrollHeight;
|
|
96
|
+
|
|
97
|
+
// temporarily disable all css transitions
|
|
98
|
+
var dContainerTransition = dContainer.style.transition;
|
|
99
|
+
dContainer.style.transition = '';
|
|
100
|
+
|
|
101
|
+
// on the next frame (as soon as the previous style change has taken effect),
|
|
102
|
+
// explicitly set the dContainer's height to its current pixel height, so we
|
|
103
|
+
// aren't transitioning out of 'auto'
|
|
104
|
+
requestAnimationFrame(function() {
|
|
105
|
+
dContainer.style.height = sectionHeight + 'px';
|
|
106
|
+
dContainer.style.transition = dContainerTransition;
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
// on the next frame (as soon as the previous style change has taken effect),
|
|
110
|
+
// have the dContainer transition to height: 0
|
|
111
|
+
requestAnimationFrame(function() {
|
|
112
|
+
|
|
113
|
+
dContainer.style.height = 0 + 'px';
|
|
114
|
+
|
|
115
|
+
dContainer.addEventListener('transitionend', transitionEnd, true);
|
|
116
|
+
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
connectedCallback() {
|
|
125
|
+
|
|
126
|
+
this.state = {
|
|
127
|
+
sTitle: this.getAttribute('sectiontitle') || "SECTION TITLE",
|
|
128
|
+
bHideControls: (this.getAttribute("hideControls") === "true") ? true : false,
|
|
129
|
+
bCollapsed: (this.getAttribute('collapsed') === "true") ? true : false,
|
|
130
|
+
asStyles: (this.getAttribute('styles')) ? this.getAttribute('styles').split(',') : null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.render();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
render() {
|
|
137
|
+
|
|
138
|
+
this.shadowRoot.innerHTML = `${this.style}${this.template}`;
|
|
139
|
+
|
|
140
|
+
this.sdSection = this.shadowRoot.querySelector(`:host > section`);
|
|
141
|
+
this.sdSectionTitle = this.shadowRoot.querySelector(`#section-title`);
|
|
142
|
+
this.sdContentsContainer = this.shadowRoot.querySelector(`#container`);
|
|
143
|
+
|
|
144
|
+
this.sdSectionTitle.appendChild(document.createTextNode(this.state.sTitle));
|
|
145
|
+
|
|
146
|
+
if (!this.state.bHideControls) {
|
|
147
|
+
|
|
148
|
+
this.sdSectionCollapseControl = this.shadowRoot.querySelector(`#collapse-control`);
|
|
149
|
+
|
|
150
|
+
// Bind the expand and collapse controls
|
|
151
|
+
if (this.sdSectionCollapseControl) {
|
|
152
|
+
|
|
153
|
+
// Configure the the inital collapse state
|
|
154
|
+
if (this.state.bCollapsed) {
|
|
155
|
+
|
|
156
|
+
this.sdSectionCollapseControl.classList.add('collapsed');
|
|
157
|
+
this.sdContentsContainer.setAttribute('data-collapsed', "true");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this.sdSectionCollapseControl.addEventListener('click', () => {
|
|
161
|
+
|
|
162
|
+
if (!this.bLock) {
|
|
163
|
+
|
|
164
|
+
if (this.sdContentsContainer.getAttribute('data-collapsed') === "true") {
|
|
165
|
+
|
|
166
|
+
this.sdSectionCollapseControl.classList.remove('collapsed');
|
|
167
|
+
|
|
168
|
+
this.expand(this.sdContentsContainer);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
|
|
172
|
+
this.sdSectionCollapseControl.classList.add('collapsed');
|
|
173
|
+
|
|
174
|
+
this.collapse(this.sdContentsContainer);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.bLock = false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (this.state.asStyles) {
|
|
186
|
+
|
|
187
|
+
for (let sStyle of this.state.asStyles) {
|
|
188
|
+
|
|
189
|
+
switch(sStyle.toLowerCase()) {
|
|
190
|
+
|
|
191
|
+
case "subsection":
|
|
192
|
+
|
|
193
|
+
this.sdSection.classList.add('sub-section');
|
|
194
|
+
break;
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
setTimeout(() => {
|
|
203
|
+
let dSubSections = [...this.querySelectorAll('cui-section')];
|
|
204
|
+
dSubSections.forEach((dSubSection) => {
|
|
205
|
+
dSubSection.setAttribute('subsection', true);
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
}
|
package/src/section.scss
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
margin-bottom: 20px;
|
|
4
|
+
|
|
5
|
+
* {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
section {
|
|
10
|
+
background: var(--section-background-color);
|
|
11
|
+
border-radius: 5px;
|
|
12
|
+
box-shadow: var(--section-box-shadow-color) var(--section-box-shadow-x) var(--section-box-shadow-y) var(--section-box-shadow-blur);
|
|
13
|
+
|
|
14
|
+
header {
|
|
15
|
+
border-bottom: solid 1px var(--section-header-border-color);
|
|
16
|
+
display: flex;
|
|
17
|
+
margin: 0.75em 0.75em 20px;
|
|
18
|
+
|
|
19
|
+
h2, h3, h4, h5, h6 {
|
|
20
|
+
color: var(--section-header-text-color);
|
|
21
|
+
font-family: var(--section-header-fonts);
|
|
22
|
+
font-size: 20px;
|
|
23
|
+
margin: 10px 0 3px;
|
|
24
|
+
flex: 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.header-controls {
|
|
28
|
+
flex: 0 1 0;
|
|
29
|
+
margin: 10px 0 3px;
|
|
30
|
+
|
|
31
|
+
button {
|
|
32
|
+
background: transparent;
|
|
33
|
+
border: 0;
|
|
34
|
+
border-radius: 32px;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
height: 32px;
|
|
37
|
+
margin-left: 0.5em;
|
|
38
|
+
margin-top: -7px;
|
|
39
|
+
transform: rotate(180deg);
|
|
40
|
+
width: 32px;
|
|
41
|
+
|
|
42
|
+
cui-icon {
|
|
43
|
+
|
|
44
|
+
svg {
|
|
45
|
+
fill: var(--section-header-icon-color);
|
|
46
|
+
height: 16px;
|
|
47
|
+
position: relative;
|
|
48
|
+
top: 2px;
|
|
49
|
+
width: 16px;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&:hover {
|
|
54
|
+
background-color: var(--section-header-button-hover-color);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&.collapsed {
|
|
58
|
+
transform: rotate(0);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.container {
|
|
66
|
+
//margin: 0 0.75em 20px;
|
|
67
|
+
//margin: 0 0.75em;
|
|
68
|
+
transition: height 0.5s ease;
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
|
|
71
|
+
&[data-collapsed="true"] {
|
|
72
|
+
height: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
:host([subsection]) {
|
|
82
|
+
section {
|
|
83
|
+
border: 1px solid var(--section-header-border-color);
|
|
84
|
+
box-shadow: none;
|
|
85
|
+
|
|
86
|
+
header {
|
|
87
|
+
margin: 0 0 20px;
|
|
88
|
+
|
|
89
|
+
h2, h3, h4, h5, h6 {
|
|
90
|
+
font-size: 18px;
|
|
91
|
+
margin: 10px 0 3px 0.75em;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.header-controls {
|
|
95
|
+
margin: 10px 0.75em 3px 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
:host([subsection]):first-child {
|
|
102
|
+
section {
|
|
103
|
+
margin-top: 20px;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|