secure-ui-components 0.2.2 → 0.2.4
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/dist/components/secure-card/secure-card.js +1 -766
- package/dist/components/secure-datetime/secure-datetime.js +1 -570
- package/dist/components/secure-file-upload/secure-file-upload.js +1 -868
- package/dist/components/secure-form/secure-form.js +1 -797
- package/dist/components/secure-input/secure-input.css +67 -1
- package/dist/components/secure-input/secure-input.d.ts +14 -0
- package/dist/components/secure-input/secure-input.d.ts.map +1 -1
- package/dist/components/secure-input/secure-input.js +1 -805
- package/dist/components/secure-input/secure-input.js.map +1 -1
- package/dist/components/secure-password-confirm/secure-password-confirm.js +1 -329
- package/dist/components/secure-select/secure-select.js +1 -589
- package/dist/components/secure-submit-button/secure-submit-button.js +1 -378
- package/dist/components/secure-table/secure-table.js +33 -528
- package/dist/components/secure-telemetry-provider/secure-telemetry-provider.js +1 -201
- package/dist/components/secure-textarea/secure-textarea.css +66 -1
- package/dist/components/secure-textarea/secure-textarea.d.ts +11 -0
- package/dist/components/secure-textarea/secure-textarea.d.ts.map +1 -1
- package/dist/components/secure-textarea/secure-textarea.js +1 -436
- package/dist/components/secure-textarea/secure-textarea.js.map +1 -1
- package/dist/core/base-component.d.ts +18 -0
- package/dist/core/base-component.d.ts.map +1 -1
- package/dist/core/base-component.js +1 -455
- package/dist/core/base-component.js.map +1 -1
- package/dist/core/security-config.js +1 -242
- package/dist/core/types.js +0 -2
- package/dist/index.js +1 -17
- package/dist/package.json +4 -2
- package/package.json +4 -2
|
@@ -19,381 +19,4 @@
|
|
|
19
19
|
*
|
|
20
20
|
* @module secure-submit-button
|
|
21
21
|
* @license MIT
|
|
22
|
-
*/
|
|
23
|
-
import { SecureBaseComponent } from '../../core/base-component.js';
|
|
24
|
-
import { getTierConfig } from '../../core/security-config.js';
|
|
25
|
-
/**
|
|
26
|
-
* Secure Submit Button Web Component
|
|
27
|
-
*
|
|
28
|
-
* Provides a security-aware submit button that monitors parent form validity
|
|
29
|
-
* and enables/disables based on the form's security tier requirements.
|
|
30
|
-
*
|
|
31
|
-
* @extends SecureBaseComponent
|
|
32
|
-
*/
|
|
33
|
-
export class SecureSubmitButton extends SecureBaseComponent {
|
|
34
|
-
/**
|
|
35
|
-
* Button element reference inside shadow DOM
|
|
36
|
-
* @private
|
|
37
|
-
*/
|
|
38
|
-
#buttonElement = null;
|
|
39
|
-
/**
|
|
40
|
-
* Label span element
|
|
41
|
-
* @private
|
|
42
|
-
*/
|
|
43
|
-
#labelElement = null;
|
|
44
|
-
/**
|
|
45
|
-
* Loading indicator element
|
|
46
|
-
* @private
|
|
47
|
-
*/
|
|
48
|
-
#loadingElement = null;
|
|
49
|
-
/**
|
|
50
|
-
* Reference to the parent <secure-form> element
|
|
51
|
-
* @private
|
|
52
|
-
*/
|
|
53
|
-
#parentForm = null;
|
|
54
|
-
/**
|
|
55
|
-
* Whether the parent form is currently valid
|
|
56
|
-
* @private
|
|
57
|
-
*/
|
|
58
|
-
#isFormValid = false;
|
|
59
|
-
/**
|
|
60
|
-
* Whether a submission is in progress
|
|
61
|
-
* @private
|
|
62
|
-
*/
|
|
63
|
-
#isSubmitting = false;
|
|
64
|
-
/**
|
|
65
|
-
* Effective security tier (inherited from form or explicit)
|
|
66
|
-
* @private
|
|
67
|
-
*/
|
|
68
|
-
#effectiveTier = 'critical';
|
|
69
|
-
/**
|
|
70
|
-
* Effective tier config
|
|
71
|
-
* @private
|
|
72
|
-
*/
|
|
73
|
-
#effectiveConfig;
|
|
74
|
-
/**
|
|
75
|
-
* Unique instance ID for aria attribute association
|
|
76
|
-
* @private
|
|
77
|
-
*/
|
|
78
|
-
#instanceId = `secure-submit-button-${Math.random().toString(36).substring(2, 11)}`;
|
|
79
|
-
/**
|
|
80
|
-
* Bound event handler for field change events
|
|
81
|
-
* @private
|
|
82
|
-
*/
|
|
83
|
-
#boundHandleFieldChange;
|
|
84
|
-
/**
|
|
85
|
-
* Bound click handler
|
|
86
|
-
* @private
|
|
87
|
-
*/
|
|
88
|
-
#boundHandleClick;
|
|
89
|
-
/**
|
|
90
|
-
* Observed attributes
|
|
91
|
-
*/
|
|
92
|
-
static get observedAttributes() {
|
|
93
|
-
return [
|
|
94
|
-
...super.observedAttributes,
|
|
95
|
-
'label',
|
|
96
|
-
'loading-label',
|
|
97
|
-
'disabled'
|
|
98
|
-
];
|
|
99
|
-
}
|
|
100
|
-
constructor() {
|
|
101
|
-
super();
|
|
102
|
-
this.#effectiveConfig = getTierConfig(this.#effectiveTier);
|
|
103
|
-
this.#boundHandleFieldChange = this.#handleFieldChange.bind(this);
|
|
104
|
-
this.#boundHandleClick = this.#handleClick.bind(this);
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Connected to DOM — discover form, attach listeners, evaluate state
|
|
108
|
-
*/
|
|
109
|
-
connectedCallback() {
|
|
110
|
-
super.connectedCallback();
|
|
111
|
-
// Defer form discovery to ensure parent secure-form has initialized
|
|
112
|
-
// (secure-form creates its <form> element in its own connectedCallback)
|
|
113
|
-
queueMicrotask(() => {
|
|
114
|
-
this.#discoverParentForm();
|
|
115
|
-
this.#resolveEffectiveTier();
|
|
116
|
-
this.#attachFormListeners();
|
|
117
|
-
this.#evaluateValidity();
|
|
118
|
-
this.audit('submit_button_initialized', {
|
|
119
|
-
tier: this.#effectiveTier,
|
|
120
|
-
hasParentForm: !!this.#parentForm
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Render the button inside shadow DOM
|
|
126
|
-
*/
|
|
127
|
-
render() {
|
|
128
|
-
const fragment = document.createDocumentFragment();
|
|
129
|
-
const container = document.createElement('div');
|
|
130
|
-
container.className = 'submit-container';
|
|
131
|
-
// Visually hidden hint explains why the button may be disabled (WCAG 1.3.5, 4.1.2)
|
|
132
|
-
const hintId = `${this.#instanceId}-hint`;
|
|
133
|
-
const hint = document.createElement('p');
|
|
134
|
-
hint.id = hintId;
|
|
135
|
-
hint.className = 'sr-only';
|
|
136
|
-
hint.textContent = 'Complete all required fields to enable this button';
|
|
137
|
-
container.appendChild(hint);
|
|
138
|
-
// Create button (type="button" — cannot use type="submit" in shadow DOM)
|
|
139
|
-
this.#buttonElement = document.createElement('button');
|
|
140
|
-
this.#buttonElement.type = 'button';
|
|
141
|
-
this.#buttonElement.className = 'submit-btn';
|
|
142
|
-
this.#buttonElement.disabled = true; // Disabled by default until validity is evaluated
|
|
143
|
-
this.#buttonElement.setAttribute('aria-disabled', 'true');
|
|
144
|
-
this.#buttonElement.setAttribute('aria-describedby', hintId);
|
|
145
|
-
// Label span
|
|
146
|
-
this.#labelElement = document.createElement('span');
|
|
147
|
-
this.#labelElement.className = 'btn-label';
|
|
148
|
-
this.#labelElement.textContent = this.sanitizeValue(this.getAttribute('label') || 'Submit');
|
|
149
|
-
// Loading indicator span (hidden by default)
|
|
150
|
-
this.#loadingElement = document.createElement('span');
|
|
151
|
-
this.#loadingElement.className = 'btn-loading hidden';
|
|
152
|
-
this.#loadingElement.setAttribute('aria-hidden', 'true');
|
|
153
|
-
const spinner = document.createElement('span');
|
|
154
|
-
spinner.className = 'spinner';
|
|
155
|
-
const loadingText = document.createElement('span');
|
|
156
|
-
loadingText.textContent = this.sanitizeValue(this.getAttribute('loading-label') || 'Submitting...');
|
|
157
|
-
this.#loadingElement.appendChild(spinner);
|
|
158
|
-
this.#loadingElement.appendChild(loadingText);
|
|
159
|
-
this.#buttonElement.appendChild(this.#labelElement);
|
|
160
|
-
this.#buttonElement.appendChild(this.#loadingElement);
|
|
161
|
-
// Click handler
|
|
162
|
-
this.#buttonElement.addEventListener('click', this.#boundHandleClick);
|
|
163
|
-
container.appendChild(this.#buttonElement);
|
|
164
|
-
fragment.appendChild(container);
|
|
165
|
-
this.addComponentStyles(this.#getComponentStyles());
|
|
166
|
-
return fragment;
|
|
167
|
-
}
|
|
168
|
-
// ---------------------------------------------------------------------------
|
|
169
|
-
// Form discovery & tier resolution
|
|
170
|
-
// ---------------------------------------------------------------------------
|
|
171
|
-
/**
|
|
172
|
-
* Find the parent <secure-form> element
|
|
173
|
-
* @private
|
|
174
|
-
*/
|
|
175
|
-
#discoverParentForm() {
|
|
176
|
-
this.#parentForm = this.closest('secure-form');
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Determine the effective security tier.
|
|
180
|
-
* If the button has an explicit security-tier attribute, use it.
|
|
181
|
-
* Otherwise, inherit from the parent form.
|
|
182
|
-
* @private
|
|
183
|
-
*/
|
|
184
|
-
#resolveEffectiveTier() {
|
|
185
|
-
const ownTier = this.getAttribute('security-tier');
|
|
186
|
-
if (!ownTier && this.#parentForm) {
|
|
187
|
-
// Inherit from parent form
|
|
188
|
-
this.#effectiveTier = this.#parentForm.securityTier;
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
this.#effectiveTier = this.securityTier;
|
|
192
|
-
}
|
|
193
|
-
this.#effectiveConfig = getTierConfig(this.#effectiveTier);
|
|
194
|
-
}
|
|
195
|
-
// ---------------------------------------------------------------------------
|
|
196
|
-
// Validation monitoring
|
|
197
|
-
// ---------------------------------------------------------------------------
|
|
198
|
-
/**
|
|
199
|
-
* Attach event listeners on the parent form to monitor field changes
|
|
200
|
-
* @private
|
|
201
|
-
*/
|
|
202
|
-
#attachFormListeners() {
|
|
203
|
-
const target = this.#parentForm || this.parentElement;
|
|
204
|
-
if (!target)
|
|
205
|
-
return;
|
|
206
|
-
target.addEventListener('secure-input', this.#boundHandleFieldChange);
|
|
207
|
-
target.addEventListener('secure-textarea', this.#boundHandleFieldChange);
|
|
208
|
-
target.addEventListener('secure-select', this.#boundHandleFieldChange);
|
|
209
|
-
target.addEventListener('secure-datetime', this.#boundHandleFieldChange);
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Handle a field change event — re-evaluate form validity
|
|
213
|
-
* @private
|
|
214
|
-
*/
|
|
215
|
-
#handleFieldChange(_event) {
|
|
216
|
-
this.#evaluateValidity();
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Evaluate form validity and enable/disable the button accordingly
|
|
220
|
-
* @private
|
|
221
|
-
*/
|
|
222
|
-
#evaluateValidity() {
|
|
223
|
-
// If manually disabled via attribute, stay disabled
|
|
224
|
-
if (this.hasAttribute('disabled')) {
|
|
225
|
-
this.#setButtonDisabled(true);
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
// If currently submitting, stay disabled
|
|
229
|
-
if (this.#isSubmitting) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
// Public tier: validation not required, button always enabled
|
|
233
|
-
if (!this.#effectiveConfig.validation.required) {
|
|
234
|
-
this.#isFormValid = true;
|
|
235
|
-
this.#setButtonDisabled(false);
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
// Authenticated / Sensitive / Critical: check form validity
|
|
239
|
-
if (this.#parentForm && typeof this.#parentForm.valid === 'boolean') {
|
|
240
|
-
this.#isFormValid = this.#parentForm.valid;
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
// Fallback: manually query fields
|
|
244
|
-
this.#isFormValid = this.#checkFieldsValid();
|
|
245
|
-
}
|
|
246
|
-
this.#setButtonDisabled(!this.#isFormValid);
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Fallback field validation when no parent form is found
|
|
250
|
-
* @private
|
|
251
|
-
*/
|
|
252
|
-
#checkFieldsValid() {
|
|
253
|
-
const container = this.#parentForm || this.parentElement;
|
|
254
|
-
if (!container)
|
|
255
|
-
return false;
|
|
256
|
-
const fields = container.querySelectorAll('secure-input, secure-textarea, secure-select, secure-datetime, secure-file-upload');
|
|
257
|
-
for (const field of fields) {
|
|
258
|
-
const typedField = field;
|
|
259
|
-
if (typeof typedField.valid === 'boolean' && !typedField.valid) {
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
// If there are no fields and validation is required, stay disabled
|
|
264
|
-
return fields.length > 0;
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Update the button's disabled state
|
|
268
|
-
* @private
|
|
269
|
-
*/
|
|
270
|
-
#setButtonDisabled(disabled) {
|
|
271
|
-
if (!this.#buttonElement)
|
|
272
|
-
return;
|
|
273
|
-
this.#buttonElement.disabled = disabled;
|
|
274
|
-
this.#buttonElement.setAttribute('aria-disabled', String(disabled));
|
|
275
|
-
}
|
|
276
|
-
// ---------------------------------------------------------------------------
|
|
277
|
-
// Click & submission
|
|
278
|
-
// ---------------------------------------------------------------------------
|
|
279
|
-
/**
|
|
280
|
-
* Handle button click — rate limit, audit, trigger form submission
|
|
281
|
-
* @private
|
|
282
|
-
*/
|
|
283
|
-
#handleClick() {
|
|
284
|
-
if (this.#isSubmitting || this.#buttonElement?.disabled)
|
|
285
|
-
return;
|
|
286
|
-
// Rate limit check
|
|
287
|
-
const rateLimitCheck = this.checkRateLimit();
|
|
288
|
-
if (!rateLimitCheck.allowed) {
|
|
289
|
-
this.audit('submit_button_rate_limited', {
|
|
290
|
-
retryAfter: rateLimitCheck.retryAfter
|
|
291
|
-
});
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
this.audit('submit_button_clicked', {
|
|
295
|
-
tier: this.#effectiveTier,
|
|
296
|
-
formValid: this.#isFormValid
|
|
297
|
-
});
|
|
298
|
-
// Show loading state
|
|
299
|
-
this.#setLoading(true);
|
|
300
|
-
// Trigger form submission via parent secure-form
|
|
301
|
-
if (this.#parentForm) {
|
|
302
|
-
this.#parentForm.submit();
|
|
303
|
-
}
|
|
304
|
-
this.#setLoading(false);
|
|
305
|
-
// Loading state remains until explicitly reset via setLoading(false)
|
|
306
|
-
// or the form's success/error response handler resets it.
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Toggle loading/submitting state
|
|
310
|
-
* @private
|
|
311
|
-
*/
|
|
312
|
-
#setLoading(loading) {
|
|
313
|
-
this.#isSubmitting = loading;
|
|
314
|
-
if (this.#buttonElement) {
|
|
315
|
-
this.#buttonElement.disabled = loading;
|
|
316
|
-
this.#buttonElement.setAttribute('aria-disabled', String(loading));
|
|
317
|
-
}
|
|
318
|
-
if (this.#labelElement) {
|
|
319
|
-
this.#labelElement.classList.toggle('hidden', loading);
|
|
320
|
-
}
|
|
321
|
-
if (this.#loadingElement) {
|
|
322
|
-
this.#loadingElement.classList.toggle('hidden', !loading);
|
|
323
|
-
this.#loadingElement.setAttribute('aria-hidden', String(!loading));
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
// ---------------------------------------------------------------------------
|
|
327
|
-
// Attribute changes
|
|
328
|
-
// ---------------------------------------------------------------------------
|
|
329
|
-
handleAttributeChange(name, _oldValue, newValue) {
|
|
330
|
-
switch (name) {
|
|
331
|
-
case 'label':
|
|
332
|
-
if (this.#labelElement) {
|
|
333
|
-
this.#labelElement.textContent = this.sanitizeValue(newValue || 'Submit');
|
|
334
|
-
}
|
|
335
|
-
break;
|
|
336
|
-
case 'loading-label':
|
|
337
|
-
if (this.#loadingElement) {
|
|
338
|
-
const textSpan = this.#loadingElement.querySelector('span:last-child');
|
|
339
|
-
if (textSpan) {
|
|
340
|
-
textSpan.textContent = this.sanitizeValue(newValue || 'Submitting...');
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
break;
|
|
344
|
-
case 'disabled':
|
|
345
|
-
this.#evaluateValidity();
|
|
346
|
-
break;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
// ---------------------------------------------------------------------------
|
|
350
|
-
// Public API
|
|
351
|
-
// ---------------------------------------------------------------------------
|
|
352
|
-
/**
|
|
353
|
-
* Whether the button is disabled
|
|
354
|
-
*/
|
|
355
|
-
get disabled() {
|
|
356
|
-
return this.#buttonElement ? this.#buttonElement.disabled : true;
|
|
357
|
-
}
|
|
358
|
-
set disabled(value) {
|
|
359
|
-
if (value) {
|
|
360
|
-
this.setAttribute('disabled', '');
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
this.removeAttribute('disabled');
|
|
364
|
-
}
|
|
365
|
-
this.#evaluateValidity();
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* The button label text
|
|
369
|
-
*/
|
|
370
|
-
get label() {
|
|
371
|
-
return this.getAttribute('label') || 'Submit';
|
|
372
|
-
}
|
|
373
|
-
set label(value) {
|
|
374
|
-
this.setAttribute('label', value);
|
|
375
|
-
}
|
|
376
|
-
// ---------------------------------------------------------------------------
|
|
377
|
-
// Styles
|
|
378
|
-
// ---------------------------------------------------------------------------
|
|
379
|
-
#getComponentStyles() {
|
|
380
|
-
return new URL('./secure-submit-button.css', import.meta.url).href;
|
|
381
|
-
}
|
|
382
|
-
// ---------------------------------------------------------------------------
|
|
383
|
-
// Cleanup
|
|
384
|
-
// ---------------------------------------------------------------------------
|
|
385
|
-
disconnectedCallback() {
|
|
386
|
-
super.disconnectedCallback();
|
|
387
|
-
const target = this.#parentForm || this.parentElement;
|
|
388
|
-
if (target) {
|
|
389
|
-
target.removeEventListener('secure-input', this.#boundHandleFieldChange);
|
|
390
|
-
target.removeEventListener('secure-textarea', this.#boundHandleFieldChange);
|
|
391
|
-
target.removeEventListener('secure-select', this.#boundHandleFieldChange);
|
|
392
|
-
target.removeEventListener('secure-datetime', this.#boundHandleFieldChange);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
// Register the custom element
|
|
397
|
-
customElements.define('secure-submit-button', SecureSubmitButton);
|
|
398
|
-
export default SecureSubmitButton;
|
|
399
|
-
//# sourceMappingURL=secure-submit-button.js.map
|
|
22
|
+
*/import{SecureBaseComponent as h}from"../../core/base-component.js";import{getTierConfig as a}from"../../core/security-config.js";class l extends h{#t=null;#n=null;#i=null;#e=null;#a=!1;#h=!1;#r="critical";#d;#b=`secure-submit-button-${Math.random().toString(36).substring(2,11)}`;#s;#c;static get observedAttributes(){return[...super.observedAttributes,"label","loading-label","disabled"]}constructor(){super(),this.#d=a(this.#r),this.#s=this.#g.bind(this),this.#c=this.#C.bind(this)}connectedCallback(){super.connectedCallback(),queueMicrotask(()=>{this.#m(),this.#f(),this.#p(),this.#l(),this.audit("submit_button_initialized",{tier:this.#r,hasParentForm:!!this.#e})})}render(){const t=document.createDocumentFragment(),i=document.createElement("div");i.className="submit-container";const s=`${this.#b}-hint`,e=document.createElement("p");e.id=s,e.className="sr-only",e.textContent="Complete all required fields to enable this button",i.appendChild(e),this.#t=document.createElement("button"),this.#t.type="button",this.#t.className="submit-btn",this.#t.disabled=!0,this.#t.setAttribute("aria-disabled","true"),this.#t.setAttribute("aria-describedby",s),this.#n=document.createElement("span"),this.#n.className="btn-label",this.#n.textContent=this.sanitizeValue(this.getAttribute("label")||"Submit"),this.#i=document.createElement("span"),this.#i.className="btn-loading hidden",this.#i.setAttribute("aria-hidden","true");const n=document.createElement("span");n.className="spinner";const r=document.createElement("span");return r.textContent=this.sanitizeValue(this.getAttribute("loading-label")||"Submitting..."),this.#i.appendChild(n),this.#i.appendChild(r),this.#t.appendChild(this.#n),this.#t.appendChild(this.#i),this.#t.addEventListener("click",this.#c),i.appendChild(this.#t),t.appendChild(i),this.addComponentStyles(this.#E()),t}#m(){this.#e=this.closest("secure-form")}#f(){!this.getAttribute("security-tier")&&this.#e?this.#r=this.#e.securityTier:this.#r=this.securityTier,this.#d=a(this.#r)}#p(){const t=this.#e||this.parentElement;t&&(t.addEventListener("secure-input",this.#s),t.addEventListener("secure-textarea",this.#s),t.addEventListener("secure-select",this.#s),t.addEventListener("secure-datetime",this.#s))}#g(t){this.#l()}#l(){if(this.hasAttribute("disabled")){this.#u(!0);return}if(!this.#h){if(!this.#d.validation.required){this.#a=!0,this.#u(!1);return}this.#e&&typeof this.#e.valid=="boolean"?this.#a=this.#e.valid:this.#a=this.#v(),this.#u(!this.#a)}}#v(){const t=this.#e||this.parentElement;if(!t)return!1;const i=t.querySelectorAll("secure-input, secure-textarea, secure-select, secure-datetime, secure-file-upload");for(const s of i){const e=s;if(typeof e.valid=="boolean"&&!e.valid)return!1}return i.length>0}#u(t){this.#t&&(this.#t.disabled=t,this.#t.setAttribute("aria-disabled",String(t)))}#C(){if(this.#h||this.#t?.disabled)return;const t=this.checkRateLimit();if(!t.allowed){this.audit("submit_button_rate_limited",{retryAfter:t.retryAfter});return}this.audit("submit_button_clicked",{tier:this.#r,formValid:this.#a}),this.#o(!0),this.#e&&this.#e.submit(),this.#o(!1)}#o(t){this.#h=t,this.#t&&(this.#t.disabled=t,this.#t.setAttribute("aria-disabled",String(t))),this.#n&&this.#n.classList.toggle("hidden",t),this.#i&&(this.#i.classList.toggle("hidden",!t),this.#i.setAttribute("aria-hidden",String(!t)))}handleAttributeChange(t,i,s){switch(t){case"label":this.#n&&(this.#n.textContent=this.sanitizeValue(s||"Submit"));break;case"loading-label":if(this.#i){const e=this.#i.querySelector("span:last-child");e&&(e.textContent=this.sanitizeValue(s||"Submitting..."))}break;case"disabled":this.#l();break}}get disabled(){return this.#t?this.#t.disabled:!0}set disabled(t){t?this.setAttribute("disabled",""):this.removeAttribute("disabled"),this.#l()}get label(){return this.getAttribute("label")||"Submit"}set label(t){this.setAttribute("label",t)}#E(){return new URL("./secure-submit-button.css",import.meta.url).href}disconnectedCallback(){super.disconnectedCallback();const t=this.#e||this.parentElement;t&&(t.removeEventListener("secure-input",this.#s),t.removeEventListener("secure-textarea",this.#s),t.removeEventListener("secure-select",this.#s),t.removeEventListener("secure-datetime",this.#s))}}customElements.define("secure-submit-button",l);var o=l;export{l as SecureSubmitButton,o as default};
|