secure-ui-components 0.2.3 → 0.2.5
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.js +1 -867
- 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.js +1 -491
- package/dist/core/base-component.js +1 -500
- 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
|
@@ -1,329 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export class SecurePasswordConfirm extends SecureBaseComponent {
|
|
3
|
-
#passwordInput = null;
|
|
4
|
-
#confirmInput = null;
|
|
5
|
-
#passwordError = null;
|
|
6
|
-
#confirmError = null;
|
|
7
|
-
#matchIndicator = null;
|
|
8
|
-
#passwordToggle = null;
|
|
9
|
-
#confirmToggle = null;
|
|
10
|
-
#passwordValue = '';
|
|
11
|
-
#confirmValue = '';
|
|
12
|
-
#confirmTouched = false;
|
|
13
|
-
#passwordVisible = false;
|
|
14
|
-
#confirmVisible = false;
|
|
15
|
-
#hiddenInput = null;
|
|
16
|
-
#instanceId = `secure-password-confirm-${Math.random().toString(36).substring(2, 11)}`;
|
|
17
|
-
// Strip any pre-set security-tier before the base reads it,
|
|
18
|
-
// guaranteeing this component is always CRITICAL.
|
|
19
|
-
connectedCallback() {
|
|
20
|
-
this.removeAttribute('security-tier');
|
|
21
|
-
super.connectedCallback();
|
|
22
|
-
}
|
|
23
|
-
static get observedAttributes() {
|
|
24
|
-
return [
|
|
25
|
-
...super.observedAttributes,
|
|
26
|
-
'name',
|
|
27
|
-
'label',
|
|
28
|
-
'password-label',
|
|
29
|
-
'confirm-label',
|
|
30
|
-
'required',
|
|
31
|
-
'minlength',
|
|
32
|
-
];
|
|
33
|
-
}
|
|
34
|
-
render() {
|
|
35
|
-
const fragment = document.createDocumentFragment();
|
|
36
|
-
const container = document.createElement('div');
|
|
37
|
-
container.className = 'container';
|
|
38
|
-
container.setAttribute('part', 'container');
|
|
39
|
-
// Optional group label
|
|
40
|
-
const groupLabelText = this.getAttribute('label');
|
|
41
|
-
if (groupLabelText) {
|
|
42
|
-
const groupLabel = document.createElement('div');
|
|
43
|
-
groupLabel.className = 'group-label';
|
|
44
|
-
groupLabel.textContent = this.sanitizeValue(groupLabelText);
|
|
45
|
-
container.appendChild(groupLabel);
|
|
46
|
-
}
|
|
47
|
-
const isRequired = this.hasAttribute('required');
|
|
48
|
-
// ── Password field ──────────────────────────────────────────────────────
|
|
49
|
-
const passwordSection = this.#buildField({
|
|
50
|
-
inputPart: 'password-input',
|
|
51
|
-
wrapperPart: 'password-wrapper',
|
|
52
|
-
labelPart: 'password-label',
|
|
53
|
-
errorPart: 'password-error',
|
|
54
|
-
togglePart: 'password-toggle',
|
|
55
|
-
labelText: this.getAttribute('password-label') ?? 'New Password',
|
|
56
|
-
inputId: `${this.#instanceId}-password`,
|
|
57
|
-
errorId: `${this.#instanceId}-password-error`,
|
|
58
|
-
isRequired,
|
|
59
|
-
});
|
|
60
|
-
this.#passwordInput = passwordSection.querySelector('[part="password-input"]');
|
|
61
|
-
this.#passwordToggle = passwordSection.querySelector('[part="password-toggle"]');
|
|
62
|
-
this.#passwordError = passwordSection.querySelector('[part="password-error"]');
|
|
63
|
-
// ── Confirm field ───────────────────────────────────────────────────────
|
|
64
|
-
const confirmSection = this.#buildField({
|
|
65
|
-
inputPart: 'confirm-input',
|
|
66
|
-
wrapperPart: 'confirm-wrapper',
|
|
67
|
-
labelPart: 'confirm-label',
|
|
68
|
-
errorPart: 'confirm-error',
|
|
69
|
-
togglePart: 'confirm-toggle',
|
|
70
|
-
labelText: this.getAttribute('confirm-label') ?? 'Confirm Password',
|
|
71
|
-
inputId: `${this.#instanceId}-confirm`,
|
|
72
|
-
errorId: `${this.#instanceId}-confirm-error`,
|
|
73
|
-
isRequired,
|
|
74
|
-
});
|
|
75
|
-
this.#confirmInput = confirmSection.querySelector('[part="confirm-input"]');
|
|
76
|
-
this.#confirmToggle = confirmSection.querySelector('[part="confirm-toggle"]');
|
|
77
|
-
this.#confirmError = confirmSection.querySelector('[part="confirm-error"]');
|
|
78
|
-
// ── Match indicator ─────────────────────────────────────────────────────
|
|
79
|
-
this.#matchIndicator = document.createElement('div');
|
|
80
|
-
this.#matchIndicator.setAttribute('part', 'match-indicator');
|
|
81
|
-
this.#matchIndicator.className = 'match-indicator';
|
|
82
|
-
this.#matchIndicator.setAttribute('aria-hidden', 'true');
|
|
83
|
-
container.appendChild(passwordSection);
|
|
84
|
-
container.appendChild(confirmSection);
|
|
85
|
-
container.appendChild(this.#matchIndicator);
|
|
86
|
-
this.#attachPasswordListeners();
|
|
87
|
-
this.#attachConfirmListeners();
|
|
88
|
-
this.#attachToggleListeners();
|
|
89
|
-
this.#createHiddenInput();
|
|
90
|
-
this.addComponentStyles(new URL('./secure-password-confirm.css', import.meta.url).href);
|
|
91
|
-
fragment.appendChild(container);
|
|
92
|
-
return fragment;
|
|
93
|
-
}
|
|
94
|
-
// ── DOM builders ──────────────────────────────────────────────────────────
|
|
95
|
-
#buildField(opts) {
|
|
96
|
-
const section = document.createElement('div');
|
|
97
|
-
section.className = 'field-section';
|
|
98
|
-
const label = document.createElement('label');
|
|
99
|
-
label.setAttribute('part', opts.labelPart);
|
|
100
|
-
label.htmlFor = opts.inputId;
|
|
101
|
-
label.textContent = this.sanitizeValue(opts.labelText);
|
|
102
|
-
const wrapper = document.createElement('div');
|
|
103
|
-
wrapper.className = 'input-wrapper';
|
|
104
|
-
wrapper.setAttribute('part', opts.wrapperPart);
|
|
105
|
-
const input = document.createElement('input');
|
|
106
|
-
input.id = opts.inputId;
|
|
107
|
-
input.type = 'password';
|
|
108
|
-
input.autocomplete = 'new-password';
|
|
109
|
-
input.setAttribute('part', opts.inputPart);
|
|
110
|
-
input.setAttribute('aria-describedby', opts.errorId);
|
|
111
|
-
if (opts.isRequired) {
|
|
112
|
-
input.required = true;
|
|
113
|
-
input.setAttribute('aria-required', 'true');
|
|
114
|
-
}
|
|
115
|
-
const toggle = document.createElement('button');
|
|
116
|
-
toggle.type = 'button';
|
|
117
|
-
toggle.className = 'toggle-btn';
|
|
118
|
-
toggle.setAttribute('part', opts.togglePart);
|
|
119
|
-
toggle.setAttribute('aria-label', 'Show password');
|
|
120
|
-
toggle.appendChild(this.#createEyeIcon());
|
|
121
|
-
wrapper.appendChild(input);
|
|
122
|
-
wrapper.appendChild(toggle);
|
|
123
|
-
const error = document.createElement('div');
|
|
124
|
-
error.id = opts.errorId;
|
|
125
|
-
error.setAttribute('part', opts.errorPart);
|
|
126
|
-
error.setAttribute('role', 'alert');
|
|
127
|
-
error.className = 'error-container hidden';
|
|
128
|
-
section.appendChild(label);
|
|
129
|
-
section.appendChild(wrapper);
|
|
130
|
-
section.appendChild(error);
|
|
131
|
-
return section;
|
|
132
|
-
}
|
|
133
|
-
// ── Event wiring ──────────────────────────────────────────────────────────
|
|
134
|
-
#attachPasswordListeners() {
|
|
135
|
-
this.#passwordInput.addEventListener('focus', () => {
|
|
136
|
-
this.recordTelemetryFocus();
|
|
137
|
-
});
|
|
138
|
-
this.#passwordInput.addEventListener('input', (e) => {
|
|
139
|
-
this.recordTelemetryInput(e);
|
|
140
|
-
this.#passwordValue = this.#passwordInput.value;
|
|
141
|
-
this.detectInjection(this.#passwordValue, this.getAttribute('name') ?? '');
|
|
142
|
-
if (this.#confirmTouched) {
|
|
143
|
-
this.#checkMatch();
|
|
144
|
-
}
|
|
145
|
-
this.dispatchEvent(new CustomEvent('secure-input', {
|
|
146
|
-
detail: { name: this.getAttribute('name') ?? '', field: 'password' },
|
|
147
|
-
bubbles: true,
|
|
148
|
-
composed: true,
|
|
149
|
-
}));
|
|
150
|
-
});
|
|
151
|
-
this.#passwordInput.addEventListener('blur', () => {
|
|
152
|
-
this.recordTelemetryBlur();
|
|
153
|
-
this.#validateStrength();
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
#attachConfirmListeners() {
|
|
157
|
-
this.#confirmInput.addEventListener('input', () => {
|
|
158
|
-
this.#confirmValue = this.#confirmInput.value;
|
|
159
|
-
this.detectInjection(this.#confirmValue, this.getAttribute('name') ?? '');
|
|
160
|
-
if (this.#confirmTouched) {
|
|
161
|
-
this.#checkMatch();
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
this.#confirmInput.addEventListener('blur', () => {
|
|
165
|
-
this.#confirmTouched = true;
|
|
166
|
-
this.#confirmValue = this.#confirmInput.value;
|
|
167
|
-
this.#checkMatch();
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
#attachToggleListeners() {
|
|
171
|
-
this.#passwordToggle.addEventListener('click', () => {
|
|
172
|
-
this.#passwordVisible = !this.#passwordVisible;
|
|
173
|
-
this.#passwordInput.type = this.#passwordVisible ? 'text' : 'password';
|
|
174
|
-
this.#passwordToggle.classList.toggle('is-visible', this.#passwordVisible);
|
|
175
|
-
this.#passwordToggle.setAttribute('aria-label', this.#passwordVisible ? 'Hide password' : 'Show password');
|
|
176
|
-
});
|
|
177
|
-
this.#confirmToggle.addEventListener('click', () => {
|
|
178
|
-
this.#confirmVisible = !this.#confirmVisible;
|
|
179
|
-
this.#confirmInput.type = this.#confirmVisible ? 'text' : 'password';
|
|
180
|
-
this.#confirmToggle.classList.toggle('is-visible', this.#confirmVisible);
|
|
181
|
-
this.#confirmToggle.setAttribute('aria-label', this.#confirmVisible ? 'Hide confirm password' : 'Show confirm password');
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
// ── SVG icon ──────────────────────────────────────────────────────────────
|
|
185
|
-
#createEyeIcon() {
|
|
186
|
-
const ns = 'http://www.w3.org/2000/svg';
|
|
187
|
-
const svg = document.createElementNS(ns, 'svg');
|
|
188
|
-
svg.setAttribute('viewBox', '0 0 24 24');
|
|
189
|
-
svg.setAttribute('aria-hidden', 'true');
|
|
190
|
-
svg.setAttribute('class', 'eye-icon');
|
|
191
|
-
svg.setAttribute('width', '18');
|
|
192
|
-
svg.setAttribute('height', '18');
|
|
193
|
-
// Outer eye shape
|
|
194
|
-
const outline = document.createElementNS(ns, 'path');
|
|
195
|
-
outline.setAttribute('class', 'eye-outline');
|
|
196
|
-
outline.setAttribute('d', 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z');
|
|
197
|
-
// Pupil
|
|
198
|
-
const pupil = document.createElementNS(ns, 'circle');
|
|
199
|
-
pupil.setAttribute('class', 'eye-pupil');
|
|
200
|
-
pupil.setAttribute('cx', '12');
|
|
201
|
-
pupil.setAttribute('cy', '12');
|
|
202
|
-
pupil.setAttribute('r', '3');
|
|
203
|
-
// Slash — drawn in when password is visible (eye-off state)
|
|
204
|
-
// Line (3,3)→(21,21): length = √(18²+18²) ≈ 25.46 → dasharray 26
|
|
205
|
-
const slash = document.createElementNS(ns, 'line');
|
|
206
|
-
slash.setAttribute('class', 'eye-slash');
|
|
207
|
-
slash.setAttribute('x1', '3');
|
|
208
|
-
slash.setAttribute('y1', '3');
|
|
209
|
-
slash.setAttribute('x2', '21');
|
|
210
|
-
slash.setAttribute('y2', '21');
|
|
211
|
-
svg.append(outline, pupil, slash);
|
|
212
|
-
return svg;
|
|
213
|
-
}
|
|
214
|
-
// ── Validation ────────────────────────────────────────────────────────────
|
|
215
|
-
#validateStrength() {
|
|
216
|
-
const err = this.#strengthError(this.#passwordValue);
|
|
217
|
-
if (err) {
|
|
218
|
-
this.#showError(this.#passwordError, this.#passwordInput, err);
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
this.#clearError(this.#passwordError, this.#passwordInput);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
#strengthError(value) {
|
|
225
|
-
if (!value)
|
|
226
|
-
return null;
|
|
227
|
-
if (value.length < 8)
|
|
228
|
-
return 'Password must be at least 8 characters';
|
|
229
|
-
if (!/[a-z]/.test(value))
|
|
230
|
-
return 'Password must include a lowercase letter';
|
|
231
|
-
if (!/[A-Z]/.test(value))
|
|
232
|
-
return 'Password must include an uppercase letter';
|
|
233
|
-
if (!/[0-9]/.test(value))
|
|
234
|
-
return 'Password must include a number';
|
|
235
|
-
if (!/[^a-zA-Z0-9]/.test(value))
|
|
236
|
-
return 'Password must include a special character';
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
#checkMatch() {
|
|
240
|
-
const matched = this.#passwordValue.length > 0 && this.#passwordValue === this.#confirmValue;
|
|
241
|
-
this.#syncHiddenInput(matched);
|
|
242
|
-
this.#updateMatchIndicator(matched);
|
|
243
|
-
if (matched) {
|
|
244
|
-
this.#clearError(this.#confirmError, this.#confirmInput);
|
|
245
|
-
this.dispatchEvent(new CustomEvent('secure-password-match', {
|
|
246
|
-
detail: { name: this.getAttribute('name') ?? '', matched: true },
|
|
247
|
-
bubbles: true,
|
|
248
|
-
composed: true,
|
|
249
|
-
}));
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
const msg = this.#confirmValue.length > 0
|
|
253
|
-
? 'Passwords do not match'
|
|
254
|
-
: 'Please confirm your password';
|
|
255
|
-
this.#showError(this.#confirmError, this.#confirmInput, msg);
|
|
256
|
-
this.dispatchEvent(new CustomEvent('secure-password-mismatch', {
|
|
257
|
-
detail: { name: this.getAttribute('name') ?? '', matched: false },
|
|
258
|
-
bubbles: true,
|
|
259
|
-
composed: true,
|
|
260
|
-
}));
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
// ── UI helpers ────────────────────────────────────────────────────────────
|
|
264
|
-
#showError(container, input, message) {
|
|
265
|
-
container.textContent = message;
|
|
266
|
-
container.classList.remove('hidden');
|
|
267
|
-
input.setAttribute('aria-invalid', 'true');
|
|
268
|
-
}
|
|
269
|
-
#clearError(container, input) {
|
|
270
|
-
container.classList.add('hidden');
|
|
271
|
-
container.textContent = '';
|
|
272
|
-
input.removeAttribute('aria-invalid');
|
|
273
|
-
}
|
|
274
|
-
#updateMatchIndicator(matched) {
|
|
275
|
-
if (!this.#matchIndicator)
|
|
276
|
-
return;
|
|
277
|
-
this.#matchIndicator.className = `match-indicator ${matched ? 'matched' : 'mismatched'}`;
|
|
278
|
-
this.#matchIndicator.textContent = matched ? '✓ Passwords match' : '✗ Passwords do not match';
|
|
279
|
-
}
|
|
280
|
-
// ── Form participation ────────────────────────────────────────────────────
|
|
281
|
-
#createHiddenInput() {
|
|
282
|
-
const name = this.getAttribute('name');
|
|
283
|
-
if (!name || this.closest('secure-form'))
|
|
284
|
-
return;
|
|
285
|
-
this.#hiddenInput = document.createElement('input');
|
|
286
|
-
this.#hiddenInput.type = 'hidden';
|
|
287
|
-
this.#hiddenInput.name = name;
|
|
288
|
-
this.#hiddenInput.value = '';
|
|
289
|
-
this.appendChild(this.#hiddenInput);
|
|
290
|
-
}
|
|
291
|
-
#syncHiddenInput(matched) {
|
|
292
|
-
if (!this.#hiddenInput)
|
|
293
|
-
return;
|
|
294
|
-
this.#hiddenInput.value = matched ? this.#passwordValue : '';
|
|
295
|
-
}
|
|
296
|
-
// ── Public API ────────────────────────────────────────────────────────────
|
|
297
|
-
getPasswordValue() {
|
|
298
|
-
if (!this.#passwordValue || !this.#confirmValue)
|
|
299
|
-
return null;
|
|
300
|
-
if (this.#passwordValue !== this.#confirmValue)
|
|
301
|
-
return null;
|
|
302
|
-
return this.#passwordValue;
|
|
303
|
-
}
|
|
304
|
-
get valid() {
|
|
305
|
-
if (!this.#passwordValue || !this.#confirmValue)
|
|
306
|
-
return false;
|
|
307
|
-
if (this.#passwordValue !== this.#confirmValue)
|
|
308
|
-
return false;
|
|
309
|
-
return this.#strengthError(this.#passwordValue) === null;
|
|
310
|
-
}
|
|
311
|
-
get name() {
|
|
312
|
-
return this.getAttribute('name') ?? '';
|
|
313
|
-
}
|
|
314
|
-
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
|
315
|
-
disconnectedCallback() {
|
|
316
|
-
super.disconnectedCallback();
|
|
317
|
-
this.#passwordValue = '';
|
|
318
|
-
this.#confirmValue = '';
|
|
319
|
-
if (this.#passwordInput)
|
|
320
|
-
this.#passwordInput.value = '';
|
|
321
|
-
if (this.#confirmInput)
|
|
322
|
-
this.#confirmInput.value = '';
|
|
323
|
-
if (this.#hiddenInput)
|
|
324
|
-
this.#hiddenInput.value = '';
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
customElements.define('secure-password-confirm', SecurePasswordConfirm);
|
|
328
|
-
export default SecurePasswordConfirm;
|
|
329
|
-
//# sourceMappingURL=secure-password-confirm.js.map
|
|
1
|
+
import{SecureBaseComponent as l}from"../../core/base-component.js";class o extends l{#e=null;#s=null;#c=null;#h=null;#a=null;#l=null;#u=null;#t="";#r="";#p=!1;#n=!1;#o=!1;#i=null;#d=`secure-password-confirm-${Math.random().toString(36).substring(2,11)}`;connectedCallback(){this.removeAttribute("security-tier"),super.connectedCallback()}static get observedAttributes(){return[...super.observedAttributes,"name","label","password-label","confirm-label","required","minlength"]}render(){const t=document.createDocumentFragment(),e=document.createElement("div");e.className="container",e.setAttribute("part","container");const i=this.getAttribute("label");if(i){const n=document.createElement("div");n.className="group-label",n.textContent=this.sanitizeValue(i),e.appendChild(n)}const r=this.hasAttribute("required"),s=this.#b({inputPart:"password-input",wrapperPart:"password-wrapper",labelPart:"password-label",errorPart:"password-error",togglePart:"password-toggle",labelText:this.getAttribute("password-label")??"New Password",inputId:`${this.#d}-password`,errorId:`${this.#d}-password-error`,isRequired:r});this.#e=s.querySelector('[part="password-input"]'),this.#l=s.querySelector('[part="password-toggle"]'),this.#c=s.querySelector('[part="password-error"]');const a=this.#b({inputPart:"confirm-input",wrapperPart:"confirm-wrapper",labelPart:"confirm-label",errorPart:"confirm-error",togglePart:"confirm-toggle",labelText:this.getAttribute("confirm-label")??"Confirm Password",inputId:`${this.#d}-confirm`,errorId:`${this.#d}-confirm-error`,isRequired:r});return this.#s=a.querySelector('[part="confirm-input"]'),this.#u=a.querySelector('[part="confirm-toggle"]'),this.#h=a.querySelector('[part="confirm-error"]'),this.#a=document.createElement("div"),this.#a.setAttribute("part","match-indicator"),this.#a.className="match-indicator",this.#a.setAttribute("aria-hidden","true"),e.appendChild(s),e.appendChild(a),e.appendChild(this.#a),this.#A(),this.#v(),this.#E(),this.#I(),this.addComponentStyles(new URL("./secure-password-confirm.css",import.meta.url).href),t.appendChild(e),t}#b(t){const e=document.createElement("div");e.className="field-section";const i=document.createElement("label");i.setAttribute("part",t.labelPart),i.htmlFor=t.inputId,i.textContent=this.sanitizeValue(t.labelText);const r=document.createElement("div");r.className="input-wrapper",r.setAttribute("part",t.wrapperPart);const s=document.createElement("input");s.id=t.inputId,s.type="password",s.autocomplete="new-password",s.setAttribute("part",t.inputPart),s.setAttribute("aria-describedby",t.errorId),t.isRequired&&(s.required=!0,s.setAttribute("aria-required","true"));const a=document.createElement("button");a.type="button",a.className="toggle-btn",a.setAttribute("part",t.togglePart),a.setAttribute("aria-label","Show password"),a.appendChild(this.#C()),r.appendChild(s),r.appendChild(a);const n=document.createElement("div");return n.id=t.errorId,n.setAttribute("part",t.errorPart),n.setAttribute("role","alert"),n.className="error-container hidden",e.appendChild(i),e.appendChild(r),e.appendChild(n),e}#A(){this.#e.addEventListener("focus",()=>{this.recordTelemetryFocus()}),this.#e.addEventListener("input",t=>{this.recordTelemetryInput(t),this.#t=this.#e.value,this.detectInjection(this.#t,this.getAttribute("name")??""),this.#p&&this.#m(),this.dispatchEvent(new CustomEvent("secure-input",{detail:{name:this.getAttribute("name")??"",field:"password"},bubbles:!0,composed:!0}))}),this.#e.addEventListener("blur",()=>{this.recordTelemetryBlur(),this.#P()})}#v(){this.#s.addEventListener("input",()=>{this.#r=this.#s.value,this.detectInjection(this.#r,this.getAttribute("name")??""),this.#p&&this.#m()}),this.#s.addEventListener("blur",()=>{this.#p=!0,this.#r=this.#s.value,this.#m()})}#E(){this.#l.addEventListener("click",()=>{this.#n=!this.#n,this.#e.type=this.#n?"text":"password",this.#l.classList.toggle("is-visible",this.#n),this.#l.setAttribute("aria-label",this.#n?"Hide password":"Show password")}),this.#u.addEventListener("click",()=>{this.#o=!this.#o,this.#s.type=this.#o?"text":"password",this.#u.classList.toggle("is-visible",this.#o),this.#u.setAttribute("aria-label",this.#o?"Hide confirm password":"Show confirm password")})}#C(){const t="http://www.w3.org/2000/svg",e=document.createElementNS(t,"svg");e.setAttribute("viewBox","0 0 24 24"),e.setAttribute("aria-hidden","true"),e.setAttribute("class","eye-icon"),e.setAttribute("width","18"),e.setAttribute("height","18");const i=document.createElementNS(t,"path");i.setAttribute("class","eye-outline"),i.setAttribute("d","M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z");const r=document.createElementNS(t,"circle");r.setAttribute("class","eye-pupil"),r.setAttribute("cx","12"),r.setAttribute("cy","12"),r.setAttribute("r","3");const s=document.createElementNS(t,"line");return s.setAttribute("class","eye-slash"),s.setAttribute("x1","3"),s.setAttribute("y1","3"),s.setAttribute("x2","21"),s.setAttribute("y2","21"),e.append(i,r,s),e}#P(){const t=this.#w(this.#t);t?this.#f(this.#c,this.#e,t):this.#g(this.#c,this.#e)}#w(t){return t?t.length<8?"Password must be at least 8 characters":/[a-z]/.test(t)?/[A-Z]/.test(t)?/[0-9]/.test(t)?/[^a-zA-Z0-9]/.test(t)?null:"Password must include a special character":"Password must include a number":"Password must include an uppercase letter":"Password must include a lowercase letter":null}#m(){const t=this.#t.length>0&&this.#t===this.#r;if(this.#S(t),this.#y(t),t)this.#g(this.#h,this.#s),this.dispatchEvent(new CustomEvent("secure-password-match",{detail:{name:this.getAttribute("name")??"",matched:!0},bubbles:!0,composed:!0}));else{const e=this.#r.length>0?"Passwords do not match":"Please confirm your password";this.#f(this.#h,this.#s,e),this.dispatchEvent(new CustomEvent("secure-password-mismatch",{detail:{name:this.getAttribute("name")??"",matched:!1},bubbles:!0,composed:!0}))}}#f(t,e,i){t.textContent=i,t.classList.remove("hidden"),e.setAttribute("aria-invalid","true")}#g(t,e){t.classList.add("hidden"),t.textContent="",e.removeAttribute("aria-invalid")}#y(t){this.#a&&(this.#a.className=`match-indicator ${t?"matched":"mismatched"}`,this.#a.textContent=t?"\u2713 Passwords match":"\u2717 Passwords do not match")}#I(){const t=this.getAttribute("name");!t||this.closest("secure-form")||(this.#i=document.createElement("input"),this.#i.type="hidden",this.#i.name=t,this.#i.value="",this.appendChild(this.#i))}#S(t){this.#i&&(this.#i.value=t?this.#t:"")}getPasswordValue(){return!this.#t||!this.#r||this.#t!==this.#r?null:this.#t}get valid(){return!this.#t||!this.#r||this.#t!==this.#r?!1:this.#w(this.#t)===null}get name(){return this.getAttribute("name")??""}disconnectedCallback(){super.disconnectedCallback(),this.#t="",this.#r="",this.#e&&(this.#e.value=""),this.#s&&(this.#s.value=""),this.#i&&(this.#i.value="")}}customElements.define("secure-password-confirm",o);var c=o;export{o as SecurePasswordConfirm,c as default};
|