@salesforcedevs/docs-components 1.17.0-hack-alpha → 1.17.0-hack-alpha2
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 +1 -1
- package/src/modules/doc/commentPopup/commentPopup.css +32 -15
- package/src/modules/doc/commentPopup/commentPopup.ts +7 -0
- package/src/modules/doc/content/content.css +17 -6
- package/src/modules/doc/heading/heading.css +426 -0
- package/src/modules/doc/heading/heading.html +162 -0
- package/src/modules/doc/heading/heading.ts +227 -1
package/package.json
CHANGED
|
@@ -1,29 +1,43 @@
|
|
|
1
1
|
/* Comment Icon Container */
|
|
2
2
|
.comment-icon-container {
|
|
3
|
-
position:
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
position: relative;
|
|
4
|
+
display: inline-block;
|
|
5
|
+
vertical-align: top;
|
|
6
|
+
margin-right: var(--dx-g-spacing-xs, 4px);
|
|
7
|
+
margin-top: var(--dx-g-spacing-sm, 8px);
|
|
8
|
+
margin-bottom: calc(-1 * var(--dx-g-spacing-lg, 24px));
|
|
6
9
|
z-index: 1000;
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
.comment-icon-button {
|
|
10
|
-
background:
|
|
11
|
-
border-radius: 50%;
|
|
12
|
-
box-shadow: 0 4px 12px rgb(0 0 0 / 15%);
|
|
13
|
-
width: 56px;
|
|
14
|
-
height: 56px;
|
|
13
|
+
background: var(--dx-g-blue-vibrant-20);
|
|
15
14
|
border: none;
|
|
15
|
+
border-radius: 50%;
|
|
16
16
|
cursor: pointer;
|
|
17
17
|
transition: all 0.3s ease;
|
|
18
18
|
display: flex;
|
|
19
19
|
align-items: center;
|
|
20
20
|
justify-content: center;
|
|
21
|
+
color: white;
|
|
22
|
+
padding: 6px;
|
|
23
|
+
width: 28px;
|
|
24
|
+
height: 28px;
|
|
25
|
+
box-sizing: border-box;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.comment-icon-button lightning-icon,
|
|
29
|
+
.comment-icon-button c-primitive-icon {
|
|
30
|
+
--sds-c-icon-color-foreground: white;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.comment-icon-button:hover lightning-icon,
|
|
34
|
+
.comment-icon-button:hover c-primitive-icon {
|
|
35
|
+
--sds-c-icon-color-foreground: white;
|
|
21
36
|
}
|
|
22
37
|
|
|
23
38
|
.comment-icon-button:hover {
|
|
24
|
-
background:
|
|
25
|
-
|
|
26
|
-
box-shadow: 0 6px 16px rgb(0 0 0 / 20%);
|
|
39
|
+
background: var(--dx-g-blue-vibrant-50);
|
|
40
|
+
color: white;
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
.comment-icon-button:active {
|
|
@@ -50,6 +64,7 @@
|
|
|
50
64
|
box-sizing: border-box;
|
|
51
65
|
border: 2px solid white;
|
|
52
66
|
box-shadow: 0 2px 4px rgb(0 0 0 / 20%);
|
|
67
|
+
z-index: 1001;
|
|
53
68
|
}
|
|
54
69
|
|
|
55
70
|
/* Popup Overlay */
|
|
@@ -222,13 +237,15 @@
|
|
|
222
237
|
/* Responsive Design */
|
|
223
238
|
@media (max-width: 768px) {
|
|
224
239
|
.comment-icon-container {
|
|
225
|
-
|
|
226
|
-
|
|
240
|
+
margin-right: var(--dx-g-spacing-xs, 4px);
|
|
241
|
+
margin-top: var(--dx-g-spacing-xs, 4px);
|
|
242
|
+
margin-bottom: calc(-1 * var(--dx-g-spacing-md, 16px));
|
|
227
243
|
}
|
|
228
244
|
|
|
229
245
|
.comment-icon-button {
|
|
230
|
-
width:
|
|
231
|
-
height:
|
|
246
|
+
width: 24px;
|
|
247
|
+
height: 24px;
|
|
248
|
+
padding: 4px;
|
|
232
249
|
}
|
|
233
250
|
|
|
234
251
|
.comment-count-badge {
|
|
@@ -15,6 +15,13 @@ export default class CommentPopup extends LightningElement {
|
|
|
15
15
|
@api submitButtonLabel = "Post Comment";
|
|
16
16
|
@api emailPlaceholder = "Enter your email";
|
|
17
17
|
@api commentPlaceholder = "Enter your comment";
|
|
18
|
+
@api index?: string;
|
|
19
|
+
@api contentType?: string;
|
|
20
|
+
@api headingTitle?: string;
|
|
21
|
+
@api filePath?: string;
|
|
22
|
+
@api startLine?: string;
|
|
23
|
+
@api endLine?: string;
|
|
24
|
+
@api currentBranch?: string;
|
|
18
25
|
|
|
19
26
|
private _open = false;
|
|
20
27
|
@api get open() {
|
|
@@ -67,42 +67,53 @@ h1 {
|
|
|
67
67
|
font-size: var(--dx-g-text-3xl);
|
|
68
68
|
letter-spacing: -0.8px;
|
|
69
69
|
line-height: var(--dx-g-spacing-3xl);
|
|
70
|
-
margin: var(--
|
|
70
|
+
margin: var(--heading-top-margin, var(--dx-g-spacing-2xl)) 0
|
|
71
|
+
var(--dx-g-spacing-md) 0;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
h2 {
|
|
74
75
|
font-size: var(--dx-g-text-2xl);
|
|
75
76
|
letter-spacing: -0.4px;
|
|
76
77
|
line-height: var(--dx-g-spacing-2xl);
|
|
77
|
-
margin: var(--
|
|
78
|
+
margin: var(--heading-top-margin, var(--dx-g-spacing-2xl)) 0
|
|
79
|
+
var(--dx-g-spacing-md) 0;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
h3 {
|
|
81
83
|
font-size: var(--dx-g-text-xl);
|
|
82
84
|
letter-spacing: -0.4px;
|
|
83
85
|
line-height: var(--dx-g-spacing-xl);
|
|
84
|
-
margin: var(--
|
|
86
|
+
margin: var(--heading-top-margin, var(--dx-g-spacing-md)) 0
|
|
87
|
+
var(--dx-g-spacing-md) 0;
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
h4 {
|
|
88
91
|
font-size: var(--dx-g-text-base);
|
|
89
92
|
letter-spacing: -0.5px;
|
|
90
93
|
line-height: var(--dx-g-spacing-lg);
|
|
91
|
-
margin: var(--
|
|
94
|
+
margin: var(--heading-top-margin, var(--dx-g-spacing-lg)) 0
|
|
95
|
+
var(--dx-g-spacing-sm) 0;
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
h5 {
|
|
95
99
|
font-size: var(--dx-g-text-sm);
|
|
96
100
|
letter-spacing: -0.3px;
|
|
97
101
|
line-height: var(--dx-g-spacing-mlg);
|
|
98
|
-
margin: var(--
|
|
102
|
+
margin: var(--heading-top-margin, var(--dx-g-spacing-md)) 0
|
|
103
|
+
var(--dx-g-spacing-sm) 0;
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
h6 {
|
|
102
107
|
font-size: var(--dx-g-text-xs);
|
|
103
108
|
letter-spacing: -0.3px;
|
|
104
109
|
line-height: var(--dx-g-spacing-md);
|
|
105
|
-
margin: var(--
|
|
110
|
+
margin: var(--heading-top-margin, var(--dx-g-spacing-sm)) 0
|
|
111
|
+
var(--dx-g-spacing-sm) 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Override top margin for doc-heading preceded by doc-comment-popup */
|
|
115
|
+
doc-comment-popup + doc-heading {
|
|
116
|
+
--heading-top-margin: 10px;
|
|
106
117
|
}
|
|
107
118
|
|
|
108
119
|
img {
|
|
@@ -31,3 +31,429 @@ h4 {
|
|
|
31
31
|
h4 doc-heading-content {
|
|
32
32
|
--doc-c-heading-anchor-button-bottom: -6px;
|
|
33
33
|
}
|
|
34
|
+
|
|
35
|
+
/* Heading alignment when comment popup is present */
|
|
36
|
+
h1:has(.comment-icon-container),
|
|
37
|
+
h2:has(.comment-icon-container),
|
|
38
|
+
h3:has(.comment-icon-container),
|
|
39
|
+
h4:has(.comment-icon-container) {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
gap: var(--dx-g-spacing-xs, 4px);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Ensure heading content takes up remaining space */
|
|
46
|
+
doc-heading-content {
|
|
47
|
+
flex: 1;
|
|
48
|
+
display: inline-block;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* When no comment popup is present, reset flex to normal display */
|
|
52
|
+
h1:not(:has(.comment-icon-container)),
|
|
53
|
+
h2:not(:has(.comment-icon-container)),
|
|
54
|
+
h3:not(:has(.comment-icon-container)),
|
|
55
|
+
h4:not(:has(.comment-icon-container)) {
|
|
56
|
+
display: block;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Fallback for browsers that don't support :has() -
|
|
60
|
+
rely on JavaScript or assume all headings might have popups */
|
|
61
|
+
@supports not (selector(:has(*))) {
|
|
62
|
+
h1,
|
|
63
|
+
h2,
|
|
64
|
+
h3,
|
|
65
|
+
h4 {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
gap: var(--dx-g-spacing-xs, 4px);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Comment Icon Container */
|
|
73
|
+
.comment-icon-container {
|
|
74
|
+
position: relative;
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
flex-shrink: 0;
|
|
79
|
+
z-index: 1000;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.comment-icon-button {
|
|
83
|
+
background: var(--dx-g-blue-vibrant-20);
|
|
84
|
+
border: none;
|
|
85
|
+
border-radius: 50%;
|
|
86
|
+
cursor: pointer;
|
|
87
|
+
transition: all 0.3s ease;
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
color: white;
|
|
92
|
+
padding: 6px;
|
|
93
|
+
width: 28px;
|
|
94
|
+
height: 28px;
|
|
95
|
+
box-sizing: border-box;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.comment-icon-button dx-icon {
|
|
99
|
+
color: white;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.comment-icon-button:hover {
|
|
103
|
+
background: var(--dx-g-blue-vibrant-50);
|
|
104
|
+
color: white;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.comment-icon-button:active {
|
|
108
|
+
transform: scale(0.95);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Comment Count Badge */
|
|
112
|
+
.comment-count-badge {
|
|
113
|
+
position: absolute;
|
|
114
|
+
top: -8px;
|
|
115
|
+
right: -8px;
|
|
116
|
+
background: #f44336;
|
|
117
|
+
color: white;
|
|
118
|
+
border-radius: 50%;
|
|
119
|
+
width: 20px;
|
|
120
|
+
height: 20px;
|
|
121
|
+
font-size: 12px;
|
|
122
|
+
font-weight: bold;
|
|
123
|
+
display: flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
justify-content: center;
|
|
126
|
+
min-width: 20px;
|
|
127
|
+
padding: 0 2px;
|
|
128
|
+
box-sizing: border-box;
|
|
129
|
+
border: 2px solid white;
|
|
130
|
+
box-shadow: 0 2px 4px rgb(0 0 0 / 20%);
|
|
131
|
+
z-index: 1001;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Popup Overlay */
|
|
135
|
+
.popup-overlay {
|
|
136
|
+
position: fixed;
|
|
137
|
+
top: 0;
|
|
138
|
+
left: 0;
|
|
139
|
+
width: 100%;
|
|
140
|
+
height: 100%;
|
|
141
|
+
background: rgb(0 0 0 / 50%);
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
justify-content: center;
|
|
145
|
+
z-index: 2000;
|
|
146
|
+
padding: 20px;
|
|
147
|
+
box-sizing: border-box;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Popup Container */
|
|
151
|
+
.popup-container {
|
|
152
|
+
background: white;
|
|
153
|
+
border-radius: 12px;
|
|
154
|
+
box-shadow: 0 20px 40px rgb(0 0 0 / 10%);
|
|
155
|
+
max-width: 500px;
|
|
156
|
+
width: 100%;
|
|
157
|
+
max-height: 80vh;
|
|
158
|
+
overflow-y: auto;
|
|
159
|
+
position: relative;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Popup Header */
|
|
163
|
+
.popup-header {
|
|
164
|
+
display: flex;
|
|
165
|
+
justify-content: space-between;
|
|
166
|
+
align-items: center;
|
|
167
|
+
padding: 24px 24px 0;
|
|
168
|
+
border-bottom: 1px solid #e0e0e0;
|
|
169
|
+
margin-bottom: 24px;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.popup-title {
|
|
173
|
+
margin: 0;
|
|
174
|
+
font-size: 20px;
|
|
175
|
+
font-weight: 600;
|
|
176
|
+
color: #212121;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.close-button {
|
|
180
|
+
background: none;
|
|
181
|
+
border: none;
|
|
182
|
+
cursor: pointer;
|
|
183
|
+
padding: 8px;
|
|
184
|
+
border-radius: 50%;
|
|
185
|
+
display: flex;
|
|
186
|
+
align-items: center;
|
|
187
|
+
justify-content: center;
|
|
188
|
+
transition: background-color 0.2s ease;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.close-button:hover {
|
|
192
|
+
background: #f5f5f5;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Popup Content */
|
|
196
|
+
.popup-content {
|
|
197
|
+
padding: 0 24px 24px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Comment Form */
|
|
201
|
+
.comment-form {
|
|
202
|
+
margin-bottom: 24px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.form-group {
|
|
206
|
+
margin-bottom: 20px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.form-label {
|
|
210
|
+
display: block;
|
|
211
|
+
margin-bottom: 8px;
|
|
212
|
+
font-weight: 500;
|
|
213
|
+
color: #424242;
|
|
214
|
+
font-size: 14px;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.form-input,
|
|
218
|
+
.form-textarea {
|
|
219
|
+
width: 100%;
|
|
220
|
+
padding: 12px 16px;
|
|
221
|
+
border: 2px solid #e0e0e0;
|
|
222
|
+
border-radius: 8px;
|
|
223
|
+
font-size: 14px;
|
|
224
|
+
font-family: inherit;
|
|
225
|
+
transition: border-color 0.2s ease;
|
|
226
|
+
box-sizing: border-box;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.form-input:focus,
|
|
230
|
+
.form-textarea:focus {
|
|
231
|
+
outline: none;
|
|
232
|
+
border-color: #1976d2;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.form-textarea {
|
|
236
|
+
resize: vertical;
|
|
237
|
+
min-height: 100px;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.error-message {
|
|
241
|
+
color: #d32f2f;
|
|
242
|
+
font-size: 12px;
|
|
243
|
+
margin-top: 4px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.form-actions {
|
|
247
|
+
display: flex;
|
|
248
|
+
justify-content: flex-end;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.submit-button {
|
|
252
|
+
background: #1976d2;
|
|
253
|
+
color: white;
|
|
254
|
+
border: none;
|
|
255
|
+
border-radius: 8px;
|
|
256
|
+
padding: 12px 24px;
|
|
257
|
+
font-size: 14px;
|
|
258
|
+
font-weight: 500;
|
|
259
|
+
cursor: pointer;
|
|
260
|
+
transition: background-color 0.2s ease;
|
|
261
|
+
font-family: inherit;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.submit-button:hover:not(:disabled) {
|
|
265
|
+
background: #1565c0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.submit-button:disabled {
|
|
269
|
+
background: #ccc;
|
|
270
|
+
cursor: not-allowed;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* Comments Section */
|
|
274
|
+
.comments-section {
|
|
275
|
+
border-top: 1px solid #e0e0e0;
|
|
276
|
+
padding-top: 24px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.comments-title {
|
|
280
|
+
margin: 0 0 16px;
|
|
281
|
+
font-size: 16px;
|
|
282
|
+
font-weight: 600;
|
|
283
|
+
color: #212121;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.comments-list {
|
|
287
|
+
display: flex;
|
|
288
|
+
flex-direction: column;
|
|
289
|
+
gap: 16px;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.comment-item {
|
|
293
|
+
background: #f8f9fa;
|
|
294
|
+
border-radius: 8px;
|
|
295
|
+
padding: 16px;
|
|
296
|
+
border-left: 4px solid #1976d2;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.comment-header {
|
|
300
|
+
display: flex;
|
|
301
|
+
justify-content: space-between;
|
|
302
|
+
align-items: center;
|
|
303
|
+
margin-bottom: 8px;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.comment-email {
|
|
307
|
+
font-weight: 500;
|
|
308
|
+
color: #1976d2;
|
|
309
|
+
font-size: 14px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.comment-time {
|
|
313
|
+
font-size: 12px;
|
|
314
|
+
color: #757575;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.comment-text {
|
|
318
|
+
color: #424242;
|
|
319
|
+
line-height: 1.5;
|
|
320
|
+
font-size: 14px;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* Responsive Design */
|
|
324
|
+
@media (max-width: 768px) {
|
|
325
|
+
h1:has(.comment-icon-container),
|
|
326
|
+
h2:has(.comment-icon-container),
|
|
327
|
+
h3:has(.comment-icon-container),
|
|
328
|
+
h4:has(.comment-icon-container) {
|
|
329
|
+
gap: var(--dx-g-spacing-2xs, 2px);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@supports not (selector(:has(*))) {
|
|
333
|
+
h1,
|
|
334
|
+
h2,
|
|
335
|
+
h3,
|
|
336
|
+
h4 {
|
|
337
|
+
gap: var(--dx-g-spacing-2xs, 2px);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.comment-icon-button {
|
|
342
|
+
width: 24px;
|
|
343
|
+
height: 24px;
|
|
344
|
+
padding: 4px;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.comment-count-badge {
|
|
348
|
+
width: 18px;
|
|
349
|
+
height: 18px;
|
|
350
|
+
font-size: 11px;
|
|
351
|
+
top: -6px;
|
|
352
|
+
right: -6px;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.popup-overlay {
|
|
356
|
+
padding: 16px;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.popup-container {
|
|
360
|
+
max-height: 90vh;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.popup-header {
|
|
364
|
+
padding: 16px 16px 0;
|
|
365
|
+
margin-bottom: 16px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.popup-title {
|
|
369
|
+
font-size: 18px;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.popup-content {
|
|
373
|
+
padding: 0 16px 16px;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.comments-section {
|
|
377
|
+
padding-top: 16px;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* Dark mode support */
|
|
382
|
+
@media (prefers-color-scheme: dark) {
|
|
383
|
+
.popup-container {
|
|
384
|
+
background: #303030;
|
|
385
|
+
color: #fff;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.popup-title {
|
|
389
|
+
color: #fff;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.popup-header {
|
|
393
|
+
border-bottom-color: #424242;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.form-label {
|
|
397
|
+
color: #e0e0e0;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.form-input,
|
|
401
|
+
.form-textarea {
|
|
402
|
+
background: #424242;
|
|
403
|
+
border-color: #616161;
|
|
404
|
+
color: #fff;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.form-input:focus,
|
|
408
|
+
.form-textarea:focus {
|
|
409
|
+
border-color: #42a5f5;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.form-input::placeholder,
|
|
413
|
+
.form-textarea::placeholder {
|
|
414
|
+
color: #bdbdbd;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.close-button:hover {
|
|
418
|
+
background: #424242;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.comments-section {
|
|
422
|
+
border-top-color: #424242;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.comments-title {
|
|
426
|
+
color: #fff;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.comment-item {
|
|
430
|
+
background: #424242;
|
|
431
|
+
border-left-color: #42a5f5;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.comment-email {
|
|
435
|
+
color: #42a5f5;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.comment-text {
|
|
439
|
+
color: #e0e0e0;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.comment-count-badge {
|
|
443
|
+
border-color: #303030;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.submit-button {
|
|
447
|
+
background: #1976d2;
|
|
448
|
+
color: white;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.submit-button:hover:not(:disabled) {
|
|
452
|
+
background: #1565c0;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.submit-button:disabled {
|
|
456
|
+
background: #555;
|
|
457
|
+
color: #999;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
@@ -1,14 +1,176 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<h1 class={className} if:true={isAriaLevelOne}>
|
|
3
|
+
<!-- Comment Popup -->
|
|
4
|
+
<template if:true={hasCommentPopup}>
|
|
5
|
+
<div class="comment-icon-container">
|
|
6
|
+
<button
|
|
7
|
+
class="comment-icon-button"
|
|
8
|
+
onclick={handleIconClick}
|
|
9
|
+
aria-label="Open comment popup"
|
|
10
|
+
>
|
|
11
|
+
<dx-icon symbol={iconSymbol} size={iconSize}></dx-icon>
|
|
12
|
+
</button>
|
|
13
|
+
<div if:true={showCommentCount} class="comment-count-badge">
|
|
14
|
+
{commentCount}
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
3
18
|
<doc-heading-content header={header} hash={hash}></doc-heading-content>
|
|
4
19
|
</h1>
|
|
5
20
|
<h2 class={className} if:true={isAriaLevelTwo}>
|
|
21
|
+
<!-- Comment Popup -->
|
|
22
|
+
<template if:true={hasCommentPopup}>
|
|
23
|
+
<div class="comment-icon-container">
|
|
24
|
+
<button
|
|
25
|
+
class="comment-icon-button"
|
|
26
|
+
onclick={handleIconClick}
|
|
27
|
+
aria-label="Open comment popup"
|
|
28
|
+
>
|
|
29
|
+
<dx-icon symbol={iconSymbol} size={iconSize}></dx-icon>
|
|
30
|
+
</button>
|
|
31
|
+
<div if:true={showCommentCount} class="comment-count-badge">
|
|
32
|
+
{commentCount}
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
6
36
|
<doc-heading-content header={header} hash={hash}></doc-heading-content>
|
|
7
37
|
</h2>
|
|
8
38
|
<h3 class={className} if:true={isAriaLevelThree}>
|
|
39
|
+
<!-- Comment Popup -->
|
|
40
|
+
<template if:true={hasCommentPopup}>
|
|
41
|
+
<div class="comment-icon-container">
|
|
42
|
+
<button
|
|
43
|
+
class="comment-icon-button"
|
|
44
|
+
onclick={handleIconClick}
|
|
45
|
+
aria-label="Open comment popup"
|
|
46
|
+
>
|
|
47
|
+
<dx-icon symbol={iconSymbol} size={iconSize}></dx-icon>
|
|
48
|
+
</button>
|
|
49
|
+
<div if:true={showCommentCount} class="comment-count-badge">
|
|
50
|
+
{commentCount}
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
9
54
|
<doc-heading-content header={header} hash={hash}></doc-heading-content>
|
|
10
55
|
</h3>
|
|
11
56
|
<h4 class={className} if:true={isAriaLevelFour}>
|
|
57
|
+
<!-- Comment Popup -->
|
|
58
|
+
<template if:true={hasCommentPopup}>
|
|
59
|
+
<div class="comment-icon-container">
|
|
60
|
+
<button
|
|
61
|
+
class="comment-icon-button"
|
|
62
|
+
onclick={handleIconClick}
|
|
63
|
+
aria-label="Open comment popup"
|
|
64
|
+
>
|
|
65
|
+
<dx-icon symbol={iconSymbol} size={iconSize}></dx-icon>
|
|
66
|
+
</button>
|
|
67
|
+
<div if:true={showCommentCount} class="comment-count-badge">
|
|
68
|
+
{commentCount}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
12
72
|
<doc-heading-content header={header} hash={hash}></doc-heading-content>
|
|
13
73
|
</h4>
|
|
74
|
+
|
|
75
|
+
<!-- Popup Overlay -->
|
|
76
|
+
<div if:true={open} class="popup-overlay" onclick={handleOverlayClick}>
|
|
77
|
+
<div
|
|
78
|
+
class="popup-container"
|
|
79
|
+
role="dialog"
|
|
80
|
+
aria-modal="true"
|
|
81
|
+
aria-labelledby="popup-title"
|
|
82
|
+
>
|
|
83
|
+
<!-- Popup Header -->
|
|
84
|
+
<div class="popup-header">
|
|
85
|
+
<h3 id="popup-title" class="popup-title">{popupTitle}</h3>
|
|
86
|
+
<button
|
|
87
|
+
class="close-button"
|
|
88
|
+
onclick={handleClose}
|
|
89
|
+
aria-label="Close popup"
|
|
90
|
+
>
|
|
91
|
+
<dx-icon symbol="close" size="small"></dx-icon>
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<!-- Comment Form -->
|
|
96
|
+
<div class="popup-content">
|
|
97
|
+
<form class="comment-form" onsubmit={handleSubmit}>
|
|
98
|
+
<div class="form-group">
|
|
99
|
+
<label for="email-input" class="form-label">
|
|
100
|
+
Email
|
|
101
|
+
</label>
|
|
102
|
+
<input
|
|
103
|
+
id="email-input"
|
|
104
|
+
type="email"
|
|
105
|
+
class="form-input"
|
|
106
|
+
placeholder={emailPlaceholder}
|
|
107
|
+
value={email}
|
|
108
|
+
oninput={handleEmailChange}
|
|
109
|
+
required
|
|
110
|
+
/>
|
|
111
|
+
<div if:true={emailError} class="error-message">
|
|
112
|
+
{emailError}
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div class="form-group">
|
|
117
|
+
<label for="comment-input" class="form-label">
|
|
118
|
+
Comment
|
|
119
|
+
</label>
|
|
120
|
+
<textarea
|
|
121
|
+
id="comment-input"
|
|
122
|
+
class="form-textarea"
|
|
123
|
+
placeholder={commentPlaceholder}
|
|
124
|
+
value={comment}
|
|
125
|
+
oninput={handleCommentChange}
|
|
126
|
+
rows="4"
|
|
127
|
+
required
|
|
128
|
+
></textarea>
|
|
129
|
+
<div if:true={commentError} class="error-message">
|
|
130
|
+
{commentError}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div class="form-actions">
|
|
135
|
+
<button
|
|
136
|
+
type="submit"
|
|
137
|
+
class="submit-button"
|
|
138
|
+
disabled={submitButtonDisabled}
|
|
139
|
+
onclick={handleSubmit}
|
|
140
|
+
>
|
|
141
|
+
<span if:false={isSubmitting}>
|
|
142
|
+
{submitButtonLabel}
|
|
143
|
+
</span>
|
|
144
|
+
<span if:true={isSubmitting}>Posting...</span>
|
|
145
|
+
</button>
|
|
146
|
+
</div>
|
|
147
|
+
</form>
|
|
148
|
+
|
|
149
|
+
<!-- Comments List -->
|
|
150
|
+
<div if:true={showComments} class="comments-section">
|
|
151
|
+
<h4 class="comments-title">Comments</h4>
|
|
152
|
+
<div class="comments-list">
|
|
153
|
+
<template
|
|
154
|
+
for:each={formattedComments}
|
|
155
|
+
for:item="comment"
|
|
156
|
+
>
|
|
157
|
+
<div key={comment.email} class="comment-item">
|
|
158
|
+
<div class="comment-header">
|
|
159
|
+
<span class="comment-email">
|
|
160
|
+
{comment.maskedEmail}
|
|
161
|
+
</span>
|
|
162
|
+
<span class="comment-time">
|
|
163
|
+
{comment.formattedTimestamp}
|
|
164
|
+
</span>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="comment-text">
|
|
167
|
+
{comment.comment}
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</template>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
14
176
|
</template>
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import { LightningElement, api } from "lwc";
|
|
1
|
+
import { LightningElement, api, track } from "lwc";
|
|
2
|
+
import { normalizeBoolean } from "dxUtils/normalizers";
|
|
3
|
+
|
|
4
|
+
interface Comment {
|
|
5
|
+
email: string;
|
|
6
|
+
comment: string;
|
|
7
|
+
url: string;
|
|
8
|
+
timestamp: Date;
|
|
9
|
+
}
|
|
2
10
|
|
|
3
11
|
export const ariaDisplayLevels: { [key: string]: string } = {
|
|
4
12
|
"1": "4",
|
|
@@ -16,6 +24,38 @@ export default class Heading extends LightningElement {
|
|
|
16
24
|
@api header: string = "";
|
|
17
25
|
@api hash: string | null = null;
|
|
18
26
|
|
|
27
|
+
// Comment popup attributes
|
|
28
|
+
@api index?: string;
|
|
29
|
+
@api contentType?: string;
|
|
30
|
+
@api headingTitle?: string;
|
|
31
|
+
@api filePath?: string;
|
|
32
|
+
@api startLine?: string;
|
|
33
|
+
@api endLine?: string;
|
|
34
|
+
@api currentBranch?: string;
|
|
35
|
+
|
|
36
|
+
// Comment popup properties
|
|
37
|
+
@api iconSymbol = "chat";
|
|
38
|
+
@api iconSize = "medium";
|
|
39
|
+
@api popupTitle = "Leave a Comment";
|
|
40
|
+
@api submitButtonLabel = "Post Comment";
|
|
41
|
+
@api emailPlaceholder = "Enter your email";
|
|
42
|
+
@api commentPlaceholder = "Enter your comment";
|
|
43
|
+
|
|
44
|
+
private _open = false;
|
|
45
|
+
@api get open() {
|
|
46
|
+
return this._open;
|
|
47
|
+
}
|
|
48
|
+
set open(value) {
|
|
49
|
+
this._open = normalizeBoolean(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@track private comments: Comment[] = [];
|
|
53
|
+
@track private email = "";
|
|
54
|
+
@track private comment = "";
|
|
55
|
+
@track private emailError = "";
|
|
56
|
+
@track private commentError = "";
|
|
57
|
+
@track private isSubmitting = false;
|
|
58
|
+
|
|
19
59
|
@api
|
|
20
60
|
private get ariaLevel(): string {
|
|
21
61
|
// Really Dark Magic (TM)
|
|
@@ -64,4 +104,190 @@ export default class Heading extends LightningElement {
|
|
|
64
104
|
private get className(): string {
|
|
65
105
|
return `dx-text-display-${this.displayLevel}`;
|
|
66
106
|
}
|
|
107
|
+
|
|
108
|
+
// Comment popup getters
|
|
109
|
+
get showComments() {
|
|
110
|
+
const unwrapped = JSON.parse(JSON.stringify(this.comments));
|
|
111
|
+
console.log("Comments (JSON unwrapped):", unwrapped);
|
|
112
|
+
return this.comments.length > 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get commentCount() {
|
|
116
|
+
return this.comments.length;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get showCommentCount() {
|
|
120
|
+
return this.commentCount > 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get sortedComments() {
|
|
124
|
+
return [...this.comments].sort(
|
|
125
|
+
(a, b) => b.timestamp.getTime() - a.timestamp.getTime()
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get isFormValid() {
|
|
130
|
+
return this.email.trim() !== "" && this.comment.trim() !== "";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get submitButtonDisabled() {
|
|
134
|
+
return !this.isFormValid || this.isSubmitting;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get formattedComments() {
|
|
138
|
+
return this.sortedComments.map((comment) => ({
|
|
139
|
+
...comment,
|
|
140
|
+
formattedTimestamp: this.formatTimestamp(comment.timestamp),
|
|
141
|
+
maskedEmail: this.maskEmail(comment.email)
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
get hasCommentPopup() {
|
|
146
|
+
return this.index !== undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Comment popup handlers
|
|
150
|
+
handleIconClick() {
|
|
151
|
+
this._open = !this._open;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
handleEmailChange(event: Event) {
|
|
155
|
+
this.email = (event.target as HTMLInputElement).value;
|
|
156
|
+
this.emailError = "";
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
handleCommentChange(event: Event) {
|
|
160
|
+
this.comment = (event.target as HTMLTextAreaElement).value;
|
|
161
|
+
this.commentError = "";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
handleSubmit() {
|
|
165
|
+
if (!this.validateForm()) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.isSubmitting = true;
|
|
170
|
+
|
|
171
|
+
// Simulate API call
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
this.addComment();
|
|
174
|
+
this.resetForm();
|
|
175
|
+
this.isSubmitting = false;
|
|
176
|
+
}, 500);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private validateForm(): boolean {
|
|
180
|
+
let isValid = true;
|
|
181
|
+
|
|
182
|
+
if (!this.email.trim()) {
|
|
183
|
+
this.emailError = "Email is required";
|
|
184
|
+
isValid = false;
|
|
185
|
+
} else if (!this.isValidEmail(this.email)) {
|
|
186
|
+
this.emailError = "Please enter a valid email address";
|
|
187
|
+
isValid = false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!this.comment.trim()) {
|
|
191
|
+
this.commentError = "Comment is required";
|
|
192
|
+
isValid = false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return isValid;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private isValidEmail(email: string): boolean {
|
|
199
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
200
|
+
return emailRegex.test(email);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private addComment() {
|
|
204
|
+
const newComment: Comment = {
|
|
205
|
+
email: this.email.trim(),
|
|
206
|
+
comment: this.comment.trim(),
|
|
207
|
+
url: window.location.href,
|
|
208
|
+
timestamp: new Date()
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
this.comments = [newComment, ...this.comments];
|
|
212
|
+
|
|
213
|
+
// Dispatch custom event
|
|
214
|
+
this.dispatchEvent(
|
|
215
|
+
new CustomEvent("commentadded", {
|
|
216
|
+
detail: newComment
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private resetForm() {
|
|
222
|
+
this.email = "";
|
|
223
|
+
this.comment = "";
|
|
224
|
+
this.emailError = "";
|
|
225
|
+
this.commentError = "";
|
|
226
|
+
|
|
227
|
+
// Force DOM update by accessing the form elements
|
|
228
|
+
const emailInput = this.template.querySelector(
|
|
229
|
+
"#email-input"
|
|
230
|
+
) as HTMLInputElement;
|
|
231
|
+
const commentInput = this.template.querySelector(
|
|
232
|
+
"#comment-input"
|
|
233
|
+
) as HTMLTextAreaElement;
|
|
234
|
+
|
|
235
|
+
if (emailInput) {
|
|
236
|
+
emailInput.value = "";
|
|
237
|
+
}
|
|
238
|
+
if (commentInput) {
|
|
239
|
+
commentInput.value = "";
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
handleClose() {
|
|
244
|
+
this._open = false;
|
|
245
|
+
this.resetForm();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
handleOverlayClick(event: Event) {
|
|
249
|
+
if (event.target === event.currentTarget) {
|
|
250
|
+
this.handleClose();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
handleKeyDown(event: KeyboardEvent) {
|
|
255
|
+
if (event.key === "Escape") {
|
|
256
|
+
this.handleClose();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
connectedCallback() {
|
|
261
|
+
document.addEventListener("keydown", this.handleKeyDown.bind(this));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
disconnectedCallback() {
|
|
265
|
+
document.removeEventListener("keydown", this.handleKeyDown.bind(this));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private formatTimestamp(timestamp: Date): string {
|
|
269
|
+
const now = new Date();
|
|
270
|
+
const diff = now.getTime() - timestamp.getTime();
|
|
271
|
+
const minutes = Math.floor(diff / 60000);
|
|
272
|
+
const hours = Math.floor(diff / 3600000);
|
|
273
|
+
const days = Math.floor(diff / 86400000);
|
|
274
|
+
|
|
275
|
+
if (minutes < 1) {
|
|
276
|
+
return "Just now";
|
|
277
|
+
} else if (minutes < 60) {
|
|
278
|
+
return `${minutes} minute${minutes > 1 ? "s" : ""} ago`;
|
|
279
|
+
} else if (hours < 24) {
|
|
280
|
+
return `${hours} hour${hours > 1 ? "s" : ""} ago`;
|
|
281
|
+
}
|
|
282
|
+
return `${days} day${days > 1 ? "s" : ""} ago`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private maskEmail(email: string): string {
|
|
286
|
+
const [localPart, domain] = email.split("@");
|
|
287
|
+
const maskedLocal =
|
|
288
|
+
localPart.length > 2
|
|
289
|
+
? localPart.substring(0, 2) + "*".repeat(localPart.length - 2)
|
|
290
|
+
: localPart;
|
|
291
|
+
return `${maskedLocal}@${domain}`;
|
|
292
|
+
}
|
|
67
293
|
}
|