@ticatec/uniface-element 0.2.0 → 0.2.1
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 +273 -1
- package/README_CN.md +341 -0
- package/dist/common/MouseClickHandler.d.ts +1 -1
- package/dist/dialog/CommonDialog.svelte +6 -3
- package/dist/dialog/CommonDialog.svelte.d.ts +2 -1
- package/dist/dialog/Dialog.svelte +36 -10
- package/dist/dialog/Dialog.svelte.d.ts +4 -3
- package/dist/dialog/DialogBoard.svelte +11 -8
- package/dist/dialog/DialogWrapper.svelte +10 -0
- package/dist/dialog/DialogWrapper.svelte.d.ts +22 -0
- package/dist/dialog/Dialogs.js +1 -1
- package/dist/dialog/ModalResult.d.ts +5 -0
- package/dist/dialog/ModalResult.js +5 -0
- package/docs/buttons/cn/ActionBar.md +459 -0
- package/docs/buttons/cn/Button.md +228 -0
- package/docs/buttons/cn/IconButton.md +294 -0
- package/docs/buttons/cn/README.md +600 -0
- package/docs/buttons/cn/TextButton.md +163 -0
- package/docs/buttons/en/ActionBar.md +459 -0
- package/docs/buttons/en/Button.md +228 -0
- package/docs/buttons/en/IconButton.md +294 -0
- package/docs/buttons/en/README.md +600 -0
- package/docs/buttons/en/TextButton.md +163 -0
- package/docs/containers/cn/accordion.md +622 -0
- package/docs/containers/cn/box.md +445 -0
- package/docs/containers/cn/card.md +404 -0
- package/docs/containers/cn/page.md +576 -0
- package/docs/containers/cn/tabs.md +686 -0
- package/docs/containers/en/accordion.md +622 -0
- package/docs/containers/en/box.md +445 -0
- package/docs/containers/en/card.md +404 -0
- package/docs/containers/en/page.md +576 -0
- package/docs/containers/en/tabs.md +686 -0
- package/docs/data-display/cn/concise-data-table.md +731 -0
- package/docs/data-display/cn/listbox.md +786 -0
- package/docs/data-display/cn/treeview.md +863 -0
- package/docs/data-display/en/concise-data-table.md +732 -0
- package/docs/data-display/en/listbox.md +786 -0
- package/docs/data-display/en/treeview.md +863 -0
- package/docs/data-table/cn/README.md +596 -0
- package/docs/data-table/en/README.md +596 -0
- package/docs/dialog/cn/README.md +767 -0
- package/docs/dialog/en/README.md +767 -0
- package/docs/form/cn/README.md +847 -0
- package/docs/form/en/README.md +847 -0
- package/docs/form-components/cn/README.md +495 -0
- package/docs/form-components/cn/attachmentfilesfield.md +757 -0
- package/docs/form-components/cn/cascade-options-select.md +374 -0
- package/docs/form-components/cn/checkbox.md +58 -0
- package/docs/form-components/cn/date-picker.md +138 -0
- package/docs/form-components/cn/datetime-picker.md +228 -0
- package/docs/form-components/cn/group-checkbox.md +405 -0
- package/docs/form-components/cn/group-radiobox.md +525 -0
- package/docs/form-components/cn/imagefilesfield.md +651 -0
- package/docs/form-components/cn/input-options-select.md +250 -0
- package/docs/form-components/cn/lookup-editor.md +406 -0
- package/docs/form-components/cn/memoeditor.md +409 -0
- package/docs/form-components/cn/number-editor.md +261 -0
- package/docs/form-components/cn/options-multi-select.md +177 -0
- package/docs/form-components/cn/options-select.md +83 -0
- package/docs/form-components/cn/passwordeditor.md +538 -0
- package/docs/form-components/cn/prompts-text-editor.md +241 -0
- package/docs/form-components/cn/radiobutton.md +84 -0
- package/docs/form-components/cn/switch.md +178 -0
- package/docs/form-components/cn/text-editor.md +142 -0
- package/docs/form-components/cn/time-editor.md +159 -0
- package/docs/form-components/cn/unit-number-editor.md +164 -0
- package/docs/form-components/en/README.md +463 -0
- package/docs/form-components/en/attachmentfilesfield.md +757 -0
- package/docs/form-components/en/cascade-options-select.md +358 -0
- package/docs/form-components/en/checkbox.md +59 -0
- package/docs/form-components/en/date-picker.md +138 -0
- package/docs/form-components/en/datetime-picker.md +228 -0
- package/docs/form-components/en/group-checkbox.md +395 -0
- package/docs/form-components/en/group-radiobox.md +498 -0
- package/docs/form-components/en/imagefilesfield.md +651 -0
- package/docs/form-components/en/input-options-select.md +239 -0
- package/docs/form-components/en/lookup-editor.md +389 -0
- package/docs/form-components/en/memoeditor.md +409 -0
- package/docs/form-components/en/number-editor.md +249 -0
- package/docs/form-components/en/options-multi-select.md +177 -0
- package/docs/form-components/en/options-select.md +83 -0
- package/docs/form-components/en/passwordeditor.md +538 -0
- package/docs/form-components/en/prompts-text-editor.md +241 -0
- package/docs/form-components/en/radiobutton.md +84 -0
- package/docs/form-components/en/switch.md +180 -0
- package/docs/form-components/en/text-editor.md +142 -0
- package/docs/form-components/en/time-editor.md +159 -0
- package/docs/form-components/en/unit-number-editor.md +164 -0
- package/docs/form-layout/cn/criteria-field.md +652 -0
- package/docs/form-layout/cn/flex-form.md +454 -0
- package/docs/form-layout/cn/flex-row-form.md +542 -0
- package/docs/form-layout/cn/form-field.md +923 -0
- package/docs/form-layout/cn/grid-form.md +676 -0
- package/docs/form-layout/en/criteria-field.md +652 -0
- package/docs/form-layout/en/flex-form.md +454 -0
- package/docs/form-layout/en/flex-row-form.md +542 -0
- package/docs/form-layout/en/form-field.md +923 -0
- package/docs/form-layout/en/grid-form.md +676 -0
- package/docs/global/cn/README.md +534 -0
- package/docs/global/en/README.md +534 -0
- package/docs/layout/cn/classiclayout.md +460 -0
- package/docs/layout/cn/headerlayout.md +524 -0
- package/docs/layout/cn/sidebarlayout.md +263 -0
- package/docs/layout/en/classiclayout.md +951 -0
- package/docs/layout/en/headerlayout.md +524 -0
- package/docs/layout/en/sidebarlayout.md +262 -0
- package/docs/misc/cn/README.md +1217 -0
- package/docs/misc/en/README.md +1217 -0
- package/docs/navigation/cn/breadcrumb.md +291 -0
- package/docs/navigation/cn/navigator.md +322 -0
- package/docs/navigation/cn/navigatormenu.md +512 -0
- package/docs/navigation/cn/progressbar.md +199 -0
- package/docs/navigation/cn/progressstepbar.md +299 -0
- package/docs/navigation/en/breadcrumb.md +291 -0
- package/docs/navigation/en/navigator.md +322 -0
- package/docs/navigation/en/navigatormenu.md +512 -0
- package/docs/navigation/en/progressbar.md +199 -0
- package/docs/navigation/en/progressstepbar.md +299 -0
- package/docs/search-filters/cn/daterange.md +1156 -0
- package/docs/search-filters/cn/numberrange.md +688 -0
- package/docs/search-filters/cn/searchbox.md +1582 -0
- package/docs/search-filters/en/daterange.md +1156 -0
- package/docs/search-filters/en/numberrange.md +688 -0
- package/docs/search-filters/en/searchbox.md +1582 -0
- package/package.json +38 -5
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
# PasswordEditor Component
|
|
2
|
+
|
|
3
|
+
The PasswordEditor component provides a secure password input field with built-in visibility toggle functionality, allowing users to show/hide password content for better usability while maintaining security.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Secure Password Input**: Standard password field with masked input
|
|
8
|
+
- **Visibility Toggle**: Eye icon to show/hide password text
|
|
9
|
+
- **CommonEditor Integration**: Built on the unified CommonEditor component
|
|
10
|
+
- **Variant Support**: Multiple styling variants (plain, outlined, filled)
|
|
11
|
+
- **State Management**: Handles readonly and disabled states appropriately
|
|
12
|
+
- **Event Forwarding**: Standard input events (blur, focus) are forwarded
|
|
13
|
+
- **Compact Mode**: Optional compact display mode
|
|
14
|
+
|
|
15
|
+
## Import
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import PasswordEditor from '@ticatec/uniface-element/PasswordEditor';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Basic Usage
|
|
22
|
+
|
|
23
|
+
### Simple Password Field
|
|
24
|
+
|
|
25
|
+
```svelte
|
|
26
|
+
<script>
|
|
27
|
+
import PasswordEditor from '@ticatec/uniface-element/PasswordEditor';
|
|
28
|
+
|
|
29
|
+
let password = '';
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<PasswordEditor
|
|
33
|
+
bind:value={password}
|
|
34
|
+
placeholder="Enter your password"
|
|
35
|
+
/>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Styled Password Field
|
|
39
|
+
|
|
40
|
+
```svelte
|
|
41
|
+
<script>
|
|
42
|
+
import PasswordEditor from '@ticatec/uniface-element/PasswordEditor';
|
|
43
|
+
|
|
44
|
+
let userPassword = '';
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<PasswordEditor
|
|
48
|
+
bind:value={userPassword}
|
|
49
|
+
variant="outlined"
|
|
50
|
+
placeholder="Password"
|
|
51
|
+
style="width: 300px;"
|
|
52
|
+
/>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Compact Password Field
|
|
56
|
+
|
|
57
|
+
```svelte
|
|
58
|
+
<script>
|
|
59
|
+
import PasswordEditor from '@ticatec/uniface-element/PasswordEditor';
|
|
60
|
+
|
|
61
|
+
let pin = '';
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<PasswordEditor
|
|
65
|
+
bind:value={pin}
|
|
66
|
+
compact={true}
|
|
67
|
+
variant="filled"
|
|
68
|
+
placeholder="PIN"
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API Reference
|
|
73
|
+
|
|
74
|
+
### Props
|
|
75
|
+
|
|
76
|
+
| Property | Type | Default | Description |
|
|
77
|
+
|----------|------|---------|-------------|
|
|
78
|
+
| `value` | `string` | `''` | The password value |
|
|
79
|
+
| `disabled` | `boolean` | `false` | Whether the input is disabled |
|
|
80
|
+
| `readonly` | `boolean` | `false` | Whether the input is read-only |
|
|
81
|
+
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | Visual variant |
|
|
82
|
+
| `compact` | `boolean` | `false` | Compact display mode |
|
|
83
|
+
| `style` | `string` | `''` | Additional CSS styles |
|
|
84
|
+
|
|
85
|
+
### Events
|
|
86
|
+
|
|
87
|
+
- `on:blur` - Fired when input loses focus
|
|
88
|
+
- `on:focus` - Fired when input gains focus
|
|
89
|
+
|
|
90
|
+
## Advanced Examples
|
|
91
|
+
|
|
92
|
+
### Login Form
|
|
93
|
+
|
|
94
|
+
```svelte
|
|
95
|
+
<script>
|
|
96
|
+
import PasswordEditor from '@ticatec/uniface-element/PasswordEditor';
|
|
97
|
+
|
|
98
|
+
let credentials = {
|
|
99
|
+
username: '',
|
|
100
|
+
password: ''
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
let loginError = '';
|
|
104
|
+
let isLoading = false;
|
|
105
|
+
|
|
106
|
+
const handleLogin = async () => {
|
|
107
|
+
if (!credentials.username || !credentials.password) {
|
|
108
|
+
loginError = 'Please enter both username and password';
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
isLoading = true;
|
|
113
|
+
loginError = '';
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
// Simulate login API call
|
|
117
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
118
|
+
|
|
119
|
+
// Mock successful login
|
|
120
|
+
if (credentials.username === 'admin' && credentials.password === 'password123') {
|
|
121
|
+
alert('Login successful!');
|
|
122
|
+
credentials = { username: '', password: '' };
|
|
123
|
+
} else {
|
|
124
|
+
loginError = 'Invalid username or password';
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
loginError = 'Login failed. Please try again.';
|
|
128
|
+
} finally {
|
|
129
|
+
isLoading = false;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const handleKeyDown = (event) => {
|
|
134
|
+
if (event.key === 'Enter' && !isLoading) {
|
|
135
|
+
handleLogin();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
</script>
|
|
139
|
+
|
|
140
|
+
<div class="login-form" style="max-width: 400px; margin: 50px auto; padding: 20px; border: 1px solid #ddd; border-radius: 8px;">
|
|
141
|
+
<h2 style="text-align: center; margin-bottom: 30px;">Login</h2>
|
|
142
|
+
|
|
143
|
+
<div class="form-field" style="margin-bottom: 20px;">
|
|
144
|
+
<label for="username" style="display: block; margin-bottom: 5px; font-weight: bold;">Username</label>
|
|
145
|
+
<input
|
|
146
|
+
id="username"
|
|
147
|
+
type="text"
|
|
148
|
+
bind:value={credentials.username}
|
|
149
|
+
placeholder="Enter username"
|
|
150
|
+
disabled={isLoading}
|
|
151
|
+
on:keydown={handleKeyDown}
|
|
152
|
+
style="width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 4px;"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<div class="form-field" style="margin-bottom: 20px;">
|
|
157
|
+
<label for="password" style="display: block; margin-bottom: 5px; font-weight: bold;">Password</label>
|
|
158
|
+
<PasswordEditor
|
|
159
|
+
bind:value={credentials.password}
|
|
160
|
+
variant="outlined"
|
|
161
|
+
disabled={isLoading}
|
|
162
|
+
placeholder="Enter password"
|
|
163
|
+
style="width: 100%;"
|
|
164
|
+
on:keydown={handleKeyDown}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
{#if loginError}
|
|
169
|
+
<div class="error-message" style="color: #d32f2f; margin-bottom: 15px; font-size: 0.875em;">
|
|
170
|
+
{loginError}
|
|
171
|
+
</div>
|
|
172
|
+
{/if}
|
|
173
|
+
|
|
174
|
+
<button
|
|
175
|
+
on:click={handleLogin}
|
|
176
|
+
disabled={isLoading}
|
|
177
|
+
style="width: 100%; padding: 12px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: {isLoading ? 'not-allowed' : 'pointer'};"
|
|
178
|
+
>
|
|
179
|
+
{isLoading ? 'Logging in...' : 'Login'}
|
|
180
|
+
</button>
|
|
181
|
+
</div>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Password Change Form
|
|
185
|
+
|
|
186
|
+
```svelte
|
|
187
|
+
<script>
|
|
188
|
+
import PasswordEditor from '@ticatec/uniface-element/PasswordEditor';
|
|
189
|
+
|
|
190
|
+
let passwordForm = {
|
|
191
|
+
currentPassword: '',
|
|
192
|
+
newPassword: '',
|
|
193
|
+
confirmPassword: ''
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
let errors = {};
|
|
197
|
+
let showSuccess = false;
|
|
198
|
+
|
|
199
|
+
const validatePassword = (password) => {
|
|
200
|
+
if (password.length < 8) return 'Password must be at least 8 characters long';
|
|
201
|
+
if (!/(?=.*[a-z])/.test(password)) return 'Password must contain at least one lowercase letter';
|
|
202
|
+
if (!/(?=.*[A-Z])/.test(password)) return 'Password must contain at least one uppercase letter';
|
|
203
|
+
if (!/(?=.*\d)/.test(password)) return 'Password must contain at least one number';
|
|
204
|
+
if (!/(?=.*[@$!%*?&])/.test(password)) return 'Password must contain at least one special character';
|
|
205
|
+
return '';
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const validateForm = () => {
|
|
209
|
+
errors = {};
|
|
210
|
+
|
|
211
|
+
if (!passwordForm.currentPassword) {
|
|
212
|
+
errors.currentPassword = 'Current password is required';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!passwordForm.newPassword) {
|
|
216
|
+
errors.newPassword = 'New password is required';
|
|
217
|
+
} else {
|
|
218
|
+
const passwordError = validatePassword(passwordForm.newPassword);
|
|
219
|
+
if (passwordError) errors.newPassword = passwordError;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!passwordForm.confirmPassword) {
|
|
223
|
+
errors.confirmPassword = 'Password confirmation is required';
|
|
224
|
+
} else if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
|
225
|
+
errors.confirmPassword = 'Passwords do not match';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return Object.keys(errors).length === 0;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const handleSubmit = () => {
|
|
232
|
+
if (validateForm()) {
|
|
233
|
+
// Simulate API call
|
|
234
|
+
setTimeout(() => {
|
|
235
|
+
showSuccess = true;
|
|
236
|
+
passwordForm = { currentPassword: '', newPassword: '', confirmPassword: '' };
|
|
237
|
+
setTimeout(() => showSuccess = false, 3000);
|
|
238
|
+
}, 500);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const handleFieldBlur = (field) => () => {
|
|
243
|
+
if (field === 'newPassword' && passwordForm.newPassword) {
|
|
244
|
+
const error = validatePassword(passwordForm.newPassword);
|
|
245
|
+
if (error) errors.newPassword = error;
|
|
246
|
+
else delete errors.newPassword;
|
|
247
|
+
errors = {...errors};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (field === 'confirmPassword' && passwordForm.confirmPassword && passwordForm.newPassword) {
|
|
251
|
+
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
|
252
|
+
errors.confirmPassword = 'Passwords do not match';
|
|
253
|
+
} else {
|
|
254
|
+
delete errors.confirmPassword;
|
|
255
|
+
}
|
|
256
|
+
errors = {...errors};
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
</script>
|
|
260
|
+
|
|
261
|
+
<div class="password-change-form" style="max-width: 500px; margin: 50px auto; padding: 30px; border: 1px solid #e0e0e0; border-radius: 8px; background: #fafafa;">
|
|
262
|
+
<h2 style="text-align: center; margin-bottom: 30px; color: #333;">Change Password</h2>
|
|
263
|
+
|
|
264
|
+
{#if showSuccess}
|
|
265
|
+
<div class="success-message" style="background: #d4edda; color: #155724; padding: 10px; border-radius: 4px; margin-bottom: 20px; text-align: center;">
|
|
266
|
+
Password changed successfully!
|
|
267
|
+
</div>
|
|
268
|
+
{/if}
|
|
269
|
+
|
|
270
|
+
<div class="form-field" style="margin-bottom: 25px;">
|
|
271
|
+
<label style="display: block; margin-bottom: 8px; font-weight: 600; color: #555;">Current Password</label>
|
|
272
|
+
<PasswordEditor
|
|
273
|
+
bind:value={passwordForm.currentPassword}
|
|
274
|
+
variant="outlined"
|
|
275
|
+
placeholder="Enter current password"
|
|
276
|
+
style="width: 100%;"
|
|
277
|
+
/>
|
|
278
|
+
{#if errors.currentPassword}
|
|
279
|
+
<div class="field-error" style="color: #d32f2f; font-size: 0.875em; margin-top: 5px;">
|
|
280
|
+
{errors.currentPassword}
|
|
281
|
+
</div>
|
|
282
|
+
{/if}
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<div class="form-field" style="margin-bottom: 25px;">
|
|
286
|
+
<label style="display: block; margin-bottom: 8px; font-weight: 600; color: #555;">New Password</label>
|
|
287
|
+
<PasswordEditor
|
|
288
|
+
bind:value={passwordForm.newPassword}
|
|
289
|
+
variant="outlined"
|
|
290
|
+
placeholder="Enter new password"
|
|
291
|
+
style="width: 100%;"
|
|
292
|
+
on:blur={handleFieldBlur('newPassword')}
|
|
293
|
+
/>
|
|
294
|
+
{#if errors.newPassword}
|
|
295
|
+
<div class="field-error" style="color: #d32f2f; font-size: 0.875em; margin-top: 5px;">
|
|
296
|
+
{errors.newPassword}
|
|
297
|
+
</div>
|
|
298
|
+
{:else if passwordForm.newPassword}
|
|
299
|
+
<div class="password-requirements" style="font-size: 0.75em; color: #666; margin-top: 5px;">
|
|
300
|
+
Password must contain: 8+ characters, uppercase, lowercase, number, special character
|
|
301
|
+
</div>
|
|
302
|
+
{/if}
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<div class="form-field" style="margin-bottom: 30px;">
|
|
306
|
+
<label style="display: block; margin-bottom: 8px; font-weight: 600; color: #555;">Confirm New Password</label>
|
|
307
|
+
<PasswordEditor
|
|
308
|
+
bind:value={passwordForm.confirmPassword}
|
|
309
|
+
variant="outlined"
|
|
310
|
+
placeholder="Confirm new password"
|
|
311
|
+
style="width: 100%;"
|
|
312
|
+
on:blur={handleFieldBlur('confirmPassword')}
|
|
313
|
+
/>
|
|
314
|
+
{#if errors.confirmPassword}
|
|
315
|
+
<div class="field-error" style="color: #d32f2f; font-size: 0.875em; margin-top: 5px;">
|
|
316
|
+
{errors.confirmPassword}
|
|
317
|
+
</div>
|
|
318
|
+
{/if}
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<div class="form-actions" style="display: flex; gap: 10px;">
|
|
322
|
+
<button
|
|
323
|
+
type="button"
|
|
324
|
+
on:click={() => passwordForm = { currentPassword: '', newPassword: '', confirmPassword: '' }}
|
|
325
|
+
style="flex: 1; padding: 12px; background: #f5f5f5; color: #333; border: 1px solid #ddd; border-radius: 4px; cursor: pointer;"
|
|
326
|
+
>
|
|
327
|
+
Cancel
|
|
328
|
+
</button>
|
|
329
|
+
<button
|
|
330
|
+
type="button"
|
|
331
|
+
on:click={handleSubmit}
|
|
332
|
+
style="flex: 2; padding: 12px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer;"
|
|
333
|
+
>
|
|
334
|
+
Change Password
|
|
335
|
+
</button>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Registration Form with Password Confirmation
|
|
341
|
+
|
|
342
|
+
```svelte
|
|
343
|
+
<script>
|
|
344
|
+
import PasswordEditor from '@ticatec/uniface-element/PasswordEditor';
|
|
345
|
+
|
|
346
|
+
let registrationForm = {
|
|
347
|
+
email: '',
|
|
348
|
+
password: '',
|
|
349
|
+
confirmPassword: '',
|
|
350
|
+
acceptTerms: false
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
let fieldErrors = {};
|
|
354
|
+
let passwordStrength = '';
|
|
355
|
+
|
|
356
|
+
$: {
|
|
357
|
+
// Real-time password strength calculation
|
|
358
|
+
if (registrationForm.password) {
|
|
359
|
+
let strength = 0;
|
|
360
|
+
if (registrationForm.password.length >= 8) strength++;
|
|
361
|
+
if (/[A-Z]/.test(registrationForm.password)) strength++;
|
|
362
|
+
if (/[a-z]/.test(registrationForm.password)) strength++;
|
|
363
|
+
if (/\d/.test(registrationForm.password)) strength++;
|
|
364
|
+
if (/[@$!%*?&]/.test(registrationForm.password)) strength++;
|
|
365
|
+
|
|
366
|
+
switch (strength) {
|
|
367
|
+
case 0:
|
|
368
|
+
case 1: passwordStrength = 'Very Weak'; break;
|
|
369
|
+
case 2: passwordStrength = 'Weak'; break;
|
|
370
|
+
case 3: passwordStrength = 'Fair'; break;
|
|
371
|
+
case 4: passwordStrength = 'Good'; break;
|
|
372
|
+
case 5: passwordStrength = 'Strong'; break;
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
passwordStrength = '';
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const getStrengthColor = (strength) => {
|
|
380
|
+
switch (strength) {
|
|
381
|
+
case 'Very Weak': return '#d32f2f';
|
|
382
|
+
case 'Weak': return '#f57c00';
|
|
383
|
+
case 'Fair': return '#fbc02d';
|
|
384
|
+
case 'Good': return '#388e3c';
|
|
385
|
+
case 'Strong': return '#1976d2';
|
|
386
|
+
default: return '#666';
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const validateEmail = (email) => {
|
|
391
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
392
|
+
return emailRegex.test(email);
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const handleSubmit = () => {
|
|
396
|
+
fieldErrors = {};
|
|
397
|
+
|
|
398
|
+
if (!registrationForm.email) {
|
|
399
|
+
fieldErrors.email = 'Email is required';
|
|
400
|
+
} else if (!validateEmail(registrationForm.email)) {
|
|
401
|
+
fieldErrors.email = 'Please enter a valid email address';
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (!registrationForm.password) {
|
|
405
|
+
fieldErrors.password = 'Password is required';
|
|
406
|
+
} else if (passwordStrength === 'Very Weak' || passwordStrength === 'Weak') {
|
|
407
|
+
fieldErrors.password = 'Password is too weak';
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (!registrationForm.confirmPassword) {
|
|
411
|
+
fieldErrors.confirmPassword = 'Password confirmation is required';
|
|
412
|
+
} else if (registrationForm.password !== registrationForm.confirmPassword) {
|
|
413
|
+
fieldErrors.confirmPassword = 'Passwords do not match';
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (!registrationForm.acceptTerms) {
|
|
417
|
+
fieldErrors.terms = 'You must accept the terms and conditions';
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (Object.keys(fieldErrors).length === 0) {
|
|
421
|
+
alert('Registration successful!');
|
|
422
|
+
registrationForm = { email: '', password: '', confirmPassword: '', acceptTerms: false };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
fieldErrors = {...fieldErrors};
|
|
426
|
+
};
|
|
427
|
+
</script>
|
|
428
|
+
|
|
429
|
+
<div class="registration-form" style="max-width: 450px; margin: 50px auto; padding: 30px; border: 1px solid #e0e0e0; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
|
430
|
+
<h2 style="text-align: center; margin-bottom: 30px; color: #1976d2;">Create Account</h2>
|
|
431
|
+
|
|
432
|
+
<div class="form-field" style="margin-bottom: 20px;">
|
|
433
|
+
<label style="display: block; margin-bottom: 8px; font-weight: 600;">Email Address</label>
|
|
434
|
+
<input
|
|
435
|
+
type="email"
|
|
436
|
+
bind:value={registrationForm.email}
|
|
437
|
+
placeholder="your.email@example.com"
|
|
438
|
+
style="width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;"
|
|
439
|
+
class:error={fieldErrors.email}
|
|
440
|
+
/>
|
|
441
|
+
{#if fieldErrors.email}
|
|
442
|
+
<div class="error" style="color: #d32f2f; font-size: 0.875em; margin-top: 4px;">
|
|
443
|
+
{fieldErrors.email}
|
|
444
|
+
</div>
|
|
445
|
+
{/if}
|
|
446
|
+
</div>
|
|
447
|
+
|
|
448
|
+
<div class="form-field" style="margin-bottom: 20px;">
|
|
449
|
+
<label style="display: block; margin-bottom: 8px; font-weight: 600;">Password</label>
|
|
450
|
+
<PasswordEditor
|
|
451
|
+
bind:value={registrationForm.password}
|
|
452
|
+
variant="outlined"
|
|
453
|
+
placeholder="Create a strong password"
|
|
454
|
+
style="width: 100%;"
|
|
455
|
+
/>
|
|
456
|
+
{#if passwordStrength}
|
|
457
|
+
<div class="password-strength" style="margin-top: 5px; font-size: 0.875em;">
|
|
458
|
+
Strength: <span style="color: {getStrengthColor(passwordStrength)}; font-weight: bold;">{passwordStrength}</span>
|
|
459
|
+
</div>
|
|
460
|
+
{/if}
|
|
461
|
+
{#if fieldErrors.password}
|
|
462
|
+
<div class="error" style="color: #d32f2f; font-size: 0.875em; margin-top: 4px;">
|
|
463
|
+
{fieldErrors.password}
|
|
464
|
+
</div>
|
|
465
|
+
{/if}
|
|
466
|
+
</div>
|
|
467
|
+
|
|
468
|
+
<div class="form-field" style="margin-bottom: 25px;">
|
|
469
|
+
<label style="display: block; margin-bottom: 8px; font-weight: 600;">Confirm Password</label>
|
|
470
|
+
<PasswordEditor
|
|
471
|
+
bind:value={registrationForm.confirmPassword}
|
|
472
|
+
variant="outlined"
|
|
473
|
+
placeholder="Confirm your password"
|
|
474
|
+
style="width: 100%;"
|
|
475
|
+
/>
|
|
476
|
+
{#if fieldErrors.confirmPassword}
|
|
477
|
+
<div class="error" style="color: #d32f2f; font-size: 0.875em; margin-top: 4px;">
|
|
478
|
+
{fieldErrors.confirmPassword}
|
|
479
|
+
</div>
|
|
480
|
+
{/if}
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
<div class="form-field" style="margin-bottom: 25px;">
|
|
484
|
+
<label style="display: flex; align-items: center; font-size: 0.9em; color: #555;">
|
|
485
|
+
<input
|
|
486
|
+
type="checkbox"
|
|
487
|
+
bind:checked={registrationForm.acceptTerms}
|
|
488
|
+
style="margin-right: 8px;"
|
|
489
|
+
/>
|
|
490
|
+
I agree to the <a href="#" style="color: #1976d2; text-decoration: none;">Terms and Conditions</a>
|
|
491
|
+
</label>
|
|
492
|
+
{#if fieldErrors.terms}
|
|
493
|
+
<div class="error" style="color: #d32f2f; font-size: 0.875em; margin-top: 4px;">
|
|
494
|
+
{fieldErrors.terms}
|
|
495
|
+
</div>
|
|
496
|
+
{/if}
|
|
497
|
+
</div>
|
|
498
|
+
|
|
499
|
+
<button
|
|
500
|
+
type="button"
|
|
501
|
+
on:click={handleSubmit}
|
|
502
|
+
style="width: 100%; padding: 14px; background: #1976d2; color: white; border: none; border-radius: 6px; font-size: 16px; font-weight: 600; cursor: pointer;"
|
|
503
|
+
>
|
|
504
|
+
Create Account
|
|
505
|
+
</button>
|
|
506
|
+
</div>
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## Best Practices
|
|
510
|
+
|
|
511
|
+
1. **Security First**: Never store or transmit passwords in plain text
|
|
512
|
+
2. **Visibility Toggle**: The eye icon helps users verify their password entry
|
|
513
|
+
3. **Validation**: Implement proper password strength validation
|
|
514
|
+
4. **Accessibility**: Provide clear labels and appropriate ARIA attributes
|
|
515
|
+
5. **Confirmation Fields**: Always include password confirmation in registration forms
|
|
516
|
+
6. **Readonly States**: Handle readonly state appropriately to prevent toggle when not editable
|
|
517
|
+
7. **Form Integration**: Use with proper form validation and submission handling
|
|
518
|
+
|
|
519
|
+
## Styling Notes
|
|
520
|
+
|
|
521
|
+
- The component inherits styling from CommonEditor
|
|
522
|
+
- Eye icon changes based on current visibility state (eye/eye-off)
|
|
523
|
+
- Supports all CommonEditor variants and styling options
|
|
524
|
+
- Custom styles can be applied via the `style` prop
|
|
525
|
+
|
|
526
|
+
## Security Considerations
|
|
527
|
+
|
|
528
|
+
- Password masking is handled automatically
|
|
529
|
+
- Visibility toggle is disabled in readonly mode
|
|
530
|
+
- Component doesn't store password history or implement auto-complete
|
|
531
|
+
- Use HTTPS in production for secure transmission
|
|
532
|
+
|
|
533
|
+
## Accessibility Features
|
|
534
|
+
|
|
535
|
+
- Proper input type switching between "password" and "text"
|
|
536
|
+
- Focus management preserved during visibility toggle
|
|
537
|
+
- Screen reader compatible with standard input behaviors
|
|
538
|
+
- Clear visual indicators for interactive elements
|