secure-ui-components 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +310 -0
- package/dist/components/secure-datetime/secure-datetime.css +263 -0
- package/dist/components/secure-datetime/secure-datetime.d.ts +124 -0
- package/dist/components/secure-datetime/secure-datetime.d.ts.map +1 -0
- package/dist/components/secure-datetime/secure-datetime.js +610 -0
- package/dist/components/secure-datetime/secure-datetime.js.map +1 -0
- package/dist/components/secure-file-upload/secure-file-upload.css +334 -0
- package/dist/components/secure-file-upload/secure-file-upload.d.ts +150 -0
- package/dist/components/secure-file-upload/secure-file-upload.d.ts.map +1 -0
- package/dist/components/secure-file-upload/secure-file-upload.js +911 -0
- package/dist/components/secure-file-upload/secure-file-upload.js.map +1 -0
- package/dist/components/secure-form/secure-form.css +62 -0
- package/dist/components/secure-form/secure-form.d.ts +128 -0
- package/dist/components/secure-form/secure-form.d.ts.map +1 -0
- package/dist/components/secure-form/secure-form.js +697 -0
- package/dist/components/secure-form/secure-form.js.map +1 -0
- package/dist/components/secure-input/secure-input.css +168 -0
- package/dist/components/secure-input/secure-input.d.ts +114 -0
- package/dist/components/secure-input/secure-input.d.ts.map +1 -0
- package/dist/components/secure-input/secure-input.js +785 -0
- package/dist/components/secure-input/secure-input.js.map +1 -0
- package/dist/components/secure-select/secure-select.css +195 -0
- package/dist/components/secure-select/secure-select.d.ts +149 -0
- package/dist/components/secure-select/secure-select.d.ts.map +1 -0
- package/dist/components/secure-select/secure-select.js +634 -0
- package/dist/components/secure-select/secure-select.js.map +1 -0
- package/dist/components/secure-submit-button/secure-submit-button.css +135 -0
- package/dist/components/secure-submit-button/secure-submit-button.d.ts +61 -0
- package/dist/components/secure-submit-button/secure-submit-button.d.ts.map +1 -0
- package/dist/components/secure-submit-button/secure-submit-button.js +399 -0
- package/dist/components/secure-submit-button/secure-submit-button.js.map +1 -0
- package/dist/components/secure-table/secure-table.css +341 -0
- package/dist/components/secure-table/secure-table.d.ts +64 -0
- package/dist/components/secure-table/secure-table.d.ts.map +1 -0
- package/dist/components/secure-table/secure-table.js +567 -0
- package/dist/components/secure-table/secure-table.js.map +1 -0
- package/dist/components/secure-textarea/secure-textarea.css +153 -0
- package/dist/components/secure-textarea/secure-textarea.d.ts +111 -0
- package/dist/components/secure-textarea/secure-textarea.d.ts.map +1 -0
- package/dist/components/secure-textarea/secure-textarea.js +477 -0
- package/dist/components/secure-textarea/secure-textarea.js.map +1 -0
- package/dist/core/base-component.d.ts +134 -0
- package/dist/core/base-component.d.ts.map +1 -0
- package/dist/core/base-component.js +303 -0
- package/dist/core/base-component.js.map +1 -0
- package/dist/core/base.css +37 -0
- package/dist/core/security-config.d.ts +89 -0
- package/dist/core/security-config.d.ts.map +1 -0
- package/dist/core/security-config.js +273 -0
- package/dist/core/security-config.js.map +1 -0
- package/dist/core/types.d.ts +212 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +7 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/package.json +89 -0
- package/dist/styles/tokens.css +257 -0
- package/package.json +118 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Secure-UI Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# secure-ui-components
|
|
2
|
+
|
|
3
|
+
Security-first web component library with zero dependencies.
|
|
4
|
+
|
|
5
|
+
**[Live Demo](https://barryprender.github.io/Secure-UI/)** — Try all components in your browser.
|
|
6
|
+
|
|
7
|
+
## 🔒 Features
|
|
8
|
+
|
|
9
|
+
- **8 Secure Components**: Input, Textarea, Select, Form, File Upload, DateTime, Table, Submit Button
|
|
10
|
+
- **4-Tier Security System**: PUBLIC, AUTHENTICATED, SENSITIVE, CRITICAL
|
|
11
|
+
- **Progressive Enhancement**: Works without JavaScript
|
|
12
|
+
- **Zero Dependencies**: Pure TypeScript, no runtime dependencies
|
|
13
|
+
- **Fully Customizable**: CSS Design Tokens + CSS Parts API
|
|
14
|
+
- **SSR Friendly**: Components render meaningful markup without JavaScript
|
|
15
|
+
- **Comprehensive Testing**: Unit tests and security tests included
|
|
16
|
+
|
|
17
|
+
## 📦 Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install secure-ui-components
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 🚀 Quick Start
|
|
24
|
+
|
|
25
|
+
### Bundler (Vite, Webpack, Rollup, etc.)
|
|
26
|
+
|
|
27
|
+
Import the components you need — each import auto-registers its custom element:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import 'secure-ui-components/secure-input';
|
|
31
|
+
import 'secure-ui-components/secure-form';
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then use them in your HTML:
|
|
35
|
+
|
|
36
|
+
```html
|
|
37
|
+
<secure-input
|
|
38
|
+
label="Email Address"
|
|
39
|
+
name="email"
|
|
40
|
+
type="email"
|
|
41
|
+
required
|
|
42
|
+
security-tier="authenticated"
|
|
43
|
+
></secure-input>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### CDN / Vanilla HTML (no bundler)
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<!DOCTYPE html>
|
|
50
|
+
<html>
|
|
51
|
+
<head>
|
|
52
|
+
<link rel="stylesheet" href="https://unpkg.com/secure-ui-components/dist/styles/tokens.css">
|
|
53
|
+
</head>
|
|
54
|
+
<body>
|
|
55
|
+
<secure-input
|
|
56
|
+
label="Email Address"
|
|
57
|
+
name="email"
|
|
58
|
+
type="email"
|
|
59
|
+
required
|
|
60
|
+
security-tier="authenticated"
|
|
61
|
+
></secure-input>
|
|
62
|
+
|
|
63
|
+
<script type="module">
|
|
64
|
+
import 'https://unpkg.com/secure-ui-components/dist/index.js';
|
|
65
|
+
</script>
|
|
66
|
+
</body>
|
|
67
|
+
</html>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 🧩 Available Components
|
|
71
|
+
|
|
72
|
+
### SecureInput
|
|
73
|
+
Text input with security features and validation.
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<secure-input
|
|
77
|
+
label="Password"
|
|
78
|
+
name="password"
|
|
79
|
+
type="password"
|
|
80
|
+
required
|
|
81
|
+
security-tier="critical"
|
|
82
|
+
></secure-input>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### SecureTextarea
|
|
86
|
+
Multi-line text input with character counting.
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<secure-textarea
|
|
90
|
+
label="Description"
|
|
91
|
+
name="description"
|
|
92
|
+
rows="5"
|
|
93
|
+
maxlength="500"
|
|
94
|
+
></secure-textarea>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### SecureSelect
|
|
98
|
+
Dropdown select with XSS prevention.
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<secure-select
|
|
102
|
+
label="Country"
|
|
103
|
+
name="country"
|
|
104
|
+
required
|
|
105
|
+
>
|
|
106
|
+
<option value="us">United States</option>
|
|
107
|
+
<option value="uk">United Kingdom</option>
|
|
108
|
+
</secure-select>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### SecureForm
|
|
112
|
+
Form container with CSRF protection.
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<secure-form
|
|
116
|
+
action="/api/submit"
|
|
117
|
+
method="post"
|
|
118
|
+
csrf-token="your-token"
|
|
119
|
+
security-tier="sensitive"
|
|
120
|
+
>
|
|
121
|
+
<!-- Your form fields here -->
|
|
122
|
+
</secure-form>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### SecureFileUpload
|
|
126
|
+
File upload with drag-drop and validation.
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<secure-file-upload
|
|
130
|
+
label="Upload Document"
|
|
131
|
+
name="document"
|
|
132
|
+
accept="image/*,.pdf"
|
|
133
|
+
max-size="5242880"
|
|
134
|
+
></secure-file-upload>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### SecureDateTime
|
|
138
|
+
Date and time picker with range validation.
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<secure-datetime
|
|
142
|
+
label="Birth Date"
|
|
143
|
+
name="birthdate"
|
|
144
|
+
type="date"
|
|
145
|
+
min="1900-01-01"
|
|
146
|
+
max="2025-12-31"
|
|
147
|
+
></secure-datetime>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### SecureSubmitButton
|
|
151
|
+
Accessible submit button with loading state and security integration.
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<secure-submit-button label="Submit"></secure-submit-button>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### SecureTable
|
|
158
|
+
Data table with sorting, filtering, and pagination.
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
const table = document.querySelector('secure-table');
|
|
162
|
+
table.data = [
|
|
163
|
+
{ id: 1, name: 'John', email: 'john@example.com' },
|
|
164
|
+
{ id: 2, name: 'Jane', email: 'jane@example.com' }
|
|
165
|
+
];
|
|
166
|
+
table.columns = [
|
|
167
|
+
{ key: 'id', label: 'ID', sortable: true },
|
|
168
|
+
{ key: 'name', label: 'Name', sortable: true, filterable: true },
|
|
169
|
+
{ key: 'email', label: 'Email', sortable: true, tier: 'sensitive' }
|
|
170
|
+
];
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## 🎨 Customization
|
|
174
|
+
|
|
175
|
+
### Using Design Tokens
|
|
176
|
+
|
|
177
|
+
```css
|
|
178
|
+
:root {
|
|
179
|
+
/* Override global colors */
|
|
180
|
+
--secure-ui-color-primary: #your-brand-color;
|
|
181
|
+
--secure-ui-input-border-radius: 8px;
|
|
182
|
+
--secure-ui-font-family-base: 'Your Font', sans-serif;
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Using CSS Parts API
|
|
187
|
+
|
|
188
|
+
```css
|
|
189
|
+
/* Style internal elements */
|
|
190
|
+
secure-input::part(label) {
|
|
191
|
+
font-weight: 700;
|
|
192
|
+
text-transform: uppercase;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
secure-input::part(input) {
|
|
196
|
+
font-family: monospace;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
secure-input::part(error) {
|
|
200
|
+
background: #ffe0e0;
|
|
201
|
+
padding: 0.5rem;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
See the [Customization Guide](https://github.com/Barryprender/Secure-UI/blob/main/secure-ui-components/docs/customization.md) for a complete styling guide with examples.
|
|
206
|
+
|
|
207
|
+
## 🔐 Security Tiers
|
|
208
|
+
|
|
209
|
+
```html
|
|
210
|
+
<!-- PUBLIC: Basic validation -->
|
|
211
|
+
<secure-input security-tier="public"></secure-input>
|
|
212
|
+
|
|
213
|
+
<!-- AUTHENTICATED: Enhanced validation, detailed logging -->
|
|
214
|
+
<secure-input security-tier="authenticated"></secure-input>
|
|
215
|
+
|
|
216
|
+
<!-- SENSITIVE: Strict validation, autocomplete disabled -->
|
|
217
|
+
<secure-input security-tier="sensitive"></secure-input>
|
|
218
|
+
|
|
219
|
+
<!-- CRITICAL: Maximum security, masking, rate limiting -->
|
|
220
|
+
<secure-input security-tier="critical" type="password"></secure-input>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## 🧪 Testing
|
|
224
|
+
|
|
225
|
+
All components include comprehensive tests:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# Run all tests
|
|
229
|
+
npm test
|
|
230
|
+
|
|
231
|
+
# Run tests in watch mode
|
|
232
|
+
npm run test:watch
|
|
233
|
+
|
|
234
|
+
# Run tests with coverage report
|
|
235
|
+
npm run test:coverage
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## 📖 Documentation
|
|
239
|
+
|
|
240
|
+
- [Customization Guide](https://github.com/Barryprender/Secure-UI/blob/main/secure-ui-components/docs/customization.md) - Complete styling guide
|
|
241
|
+
- [Architecture](https://github.com/Barryprender/Secure-UI/blob/main/secure-ui-components/docs/ARCHITECTURE.md) - Technical architecture details
|
|
242
|
+
|
|
243
|
+
## 📝 API Reference
|
|
244
|
+
|
|
245
|
+
### Common Attributes
|
|
246
|
+
|
|
247
|
+
All components support:
|
|
248
|
+
|
|
249
|
+
| Attribute | Type | Description |
|
|
250
|
+
|-----------|------|-------------|
|
|
251
|
+
| `label` | `string` | Visible field label |
|
|
252
|
+
| `name` | `string` | Form field name |
|
|
253
|
+
| `required` | `boolean` | Mark field as required |
|
|
254
|
+
| `disabled` | `boolean` | Disable the field |
|
|
255
|
+
| `readonly` | `boolean` | Make the field read-only |
|
|
256
|
+
| `security-tier` | `string` | Security level: `public`, `authenticated`, `sensitive`, `critical` (default: `critical`) |
|
|
257
|
+
|
|
258
|
+
### Common Properties / Methods
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
const el = document.querySelector('secure-input');
|
|
262
|
+
|
|
263
|
+
el.value // get current value (unmasked)
|
|
264
|
+
el.value = 'foo' // set value programmatically
|
|
265
|
+
el.valid // boolean — passes all validation rules
|
|
266
|
+
el.name // field name string
|
|
267
|
+
el.getAuditLog() // returns array of security audit log entries
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
`secure-file-upload` also exposes:
|
|
271
|
+
|
|
272
|
+
```js
|
|
273
|
+
el.files // FileList | null
|
|
274
|
+
el.clear() // clear selected files
|
|
275
|
+
el.hasScanHook // boolean
|
|
276
|
+
el.scanning // boolean — true while scan hook is running
|
|
277
|
+
el.setScanHook(async (file) => { return { valid: true } })
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Common Events
|
|
281
|
+
|
|
282
|
+
| Event | Fired by | Detail |
|
|
283
|
+
|-------|----------|--------|
|
|
284
|
+
| `secure-input` | `secure-input` | `{ name, value, masked, tier }` |
|
|
285
|
+
| `secure-textarea` | `secure-textarea` | `{ name, value, tier }` |
|
|
286
|
+
| `secure-select` | `secure-select` | `{ name, value, tier }` |
|
|
287
|
+
| `secure-datetime` | `secure-datetime` | `{ name, value, type, tier }` |
|
|
288
|
+
| `secure-file-upload` | `secure-file-upload` | `{ name, files, tier }` |
|
|
289
|
+
| `secure-form-submit` | `secure-form` | `{ formData, formElement, preventDefault }` |
|
|
290
|
+
| `secure-form-success` | `secure-form` | `{ formData, response }` |
|
|
291
|
+
| `secure-audit` | all components | `{ event, tier, timestamp, … }` |
|
|
292
|
+
| `table-action` | `secure-table` | `{ action, row }` |
|
|
293
|
+
|
|
294
|
+
## 🤝 Contributing
|
|
295
|
+
|
|
296
|
+
Contributions are welcome! Please see the main repository for guidelines.
|
|
297
|
+
|
|
298
|
+
## 📄 License
|
|
299
|
+
|
|
300
|
+
MIT License - see LICENSE file for details.
|
|
301
|
+
|
|
302
|
+
## 🔗 Links
|
|
303
|
+
|
|
304
|
+
- [GitHub Repository](https://github.com/Barryprender/Secure-UI)
|
|
305
|
+
- [Live Demo](https://barryprender.github.io/Secure-UI/)
|
|
306
|
+
|
|
307
|
+
## 🆘 Support
|
|
308
|
+
|
|
309
|
+
- [Issue Tracker](https://github.com/Barryprender/Secure-UI/issues)
|
|
310
|
+
- [Discussions](https://github.com/Barryprender/Secure-UI/discussions)
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure DateTime Component Styles
|
|
3
|
+
* Uses design tokens for full customizability
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* Container */
|
|
7
|
+
.datetime-container {
|
|
8
|
+
margin-bottom: var(--secure-ui-form-gap);
|
|
9
|
+
font-family: var(--secure-ui-font-family-base);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Label */
|
|
13
|
+
label {
|
|
14
|
+
display: block;
|
|
15
|
+
margin-bottom: var(--secure-ui-form-label-margin-bottom);
|
|
16
|
+
font-size: var(--secure-ui-label-font-size);
|
|
17
|
+
font-weight: var(--secure-ui-label-font-weight);
|
|
18
|
+
color: var(--secure-ui-label-color);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.label-suffix {
|
|
22
|
+
font-weight: var(--secure-ui-font-weight-normal);
|
|
23
|
+
color: var(--secure-ui-color-text-secondary);
|
|
24
|
+
font-size: var(--secure-ui-font-size-xs);
|
|
25
|
+
margin-left: var(--secure-ui-space-1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.security-badge {
|
|
29
|
+
display: inline-block;
|
|
30
|
+
padding: var(--secure-ui-badge-padding);
|
|
31
|
+
margin-left: var(--secure-ui-space-2);
|
|
32
|
+
font-size: var(--secure-ui-badge-font-size);
|
|
33
|
+
font-weight: var(--secure-ui-font-weight-semibold);
|
|
34
|
+
border-radius: var(--secure-ui-badge-border-radius);
|
|
35
|
+
text-transform: uppercase;
|
|
36
|
+
background-color: var(--secure-ui-color-bg-tertiary);
|
|
37
|
+
color: var(--secure-ui-color-text-secondary);
|
|
38
|
+
border: var(--secure-ui-border-width-thin) solid var(--secure-ui-color-border);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Datetime Wrapper */
|
|
42
|
+
.input-wrapper {
|
|
43
|
+
position: relative;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Datetime Input */
|
|
47
|
+
.datetime-field {
|
|
48
|
+
width: 100%;
|
|
49
|
+
height: var(--secure-ui-input-height);
|
|
50
|
+
padding: var(--secure-ui-input-padding-y) var(--secure-ui-input-padding-x);
|
|
51
|
+
|
|
52
|
+
font-family: var(--secure-ui-font-family-base);
|
|
53
|
+
font-size: var(--secure-ui-input-font-size);
|
|
54
|
+
line-height: var(--secure-ui-line-height-normal);
|
|
55
|
+
color: var(--secure-ui-input-text-color);
|
|
56
|
+
|
|
57
|
+
background-color: var(--secure-ui-input-bg);
|
|
58
|
+
border: var(--secure-ui-input-border-width) solid var(--secure-ui-input-border-color);
|
|
59
|
+
border-radius: var(--secure-ui-input-border-radius);
|
|
60
|
+
|
|
61
|
+
transition: all var(--secure-ui-transition-base) var(--secure-ui-transition-ease-in-out);
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
box-sizing: border-box;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.datetime-field::placeholder {
|
|
67
|
+
color: var(--secure-ui-input-placeholder-color);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.datetime-field:hover:not(:disabled):not([readonly]) {
|
|
71
|
+
border-color: var(--secure-ui-input-border-color-hover);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.datetime-field:focus {
|
|
75
|
+
outline: none;
|
|
76
|
+
border-color: var(--secure-ui-input-border-color-focus);
|
|
77
|
+
box-shadow: var(--secure-ui-shadow-focus);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.datetime-field.error {
|
|
81
|
+
border-color: var(--secure-ui-color-error);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.datetime-field.error:focus {
|
|
85
|
+
box-shadow: var(--secure-ui-shadow-focus-error);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.datetime-field:disabled {
|
|
89
|
+
background-color: var(--secure-ui-input-disabled-bg);
|
|
90
|
+
cursor: not-allowed;
|
|
91
|
+
opacity: var(--secure-ui-input-disabled-opacity);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.datetime-field[readonly] {
|
|
95
|
+
background-color: var(--secure-ui-color-bg-secondary);
|
|
96
|
+
cursor: default;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Calendar Icon Indicator
|
|
100
|
+
CSS alt-text syntax (content: 'emoji' / '') marks the pseudo-element as
|
|
101
|
+
presentational (no accessible name) in browsers that support it (Chrome 94+,
|
|
102
|
+
Firefox 92+, Safari 15.4+). This prevents screen readers from reading out
|
|
103
|
+
the verbose Unicode emoji name. */
|
|
104
|
+
.input-wrapper::after {
|
|
105
|
+
content: '📅' / '';
|
|
106
|
+
position: absolute;
|
|
107
|
+
right: var(--secure-ui-space-3);
|
|
108
|
+
top: 50%;
|
|
109
|
+
transform: translateY(-50%);
|
|
110
|
+
pointer-events: none;
|
|
111
|
+
font-size: var(--secure-ui-font-size-lg);
|
|
112
|
+
color: var(--secure-ui-color-text-secondary);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.input-wrapper[data-type="time"]::after {
|
|
116
|
+
content: '🕐' / '';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.input-wrapper[data-type="datetime-local"]::after {
|
|
120
|
+
content: '📅' / '';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.input-wrapper[data-type="month"]::after {
|
|
124
|
+
content: '📆' / '';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.input-wrapper[data-type="week"]::after {
|
|
128
|
+
content: '📅' / '';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.input-wrapper .timezone-display {
|
|
132
|
+
position: absolute;
|
|
133
|
+
right: 0;
|
|
134
|
+
bottom: -1.25rem;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Range Display */
|
|
138
|
+
.range-display {
|
|
139
|
+
display: none;
|
|
140
|
+
margin-top: var(--secure-ui-space-2);
|
|
141
|
+
padding: var(--secure-ui-space-2) var(--secure-ui-space-3);
|
|
142
|
+
background-color: var(--secure-ui-color-bg-secondary);
|
|
143
|
+
border-radius: var(--secure-ui-border-radius-sm);
|
|
144
|
+
font-size: var(--secure-ui-font-size-xs);
|
|
145
|
+
color: var(--secure-ui-color-text-secondary);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.range-display.visible {
|
|
149
|
+
display: block;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.range-item {
|
|
153
|
+
margin-bottom: var(--secure-ui-space-1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.range-item:last-child {
|
|
157
|
+
margin-bottom: 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.range-label {
|
|
161
|
+
font-weight: var(--secure-ui-font-weight-medium);
|
|
162
|
+
margin-right: var(--secure-ui-space-1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Helper Text */
|
|
166
|
+
.helper-text {
|
|
167
|
+
margin-top: var(--secure-ui-space-2);
|
|
168
|
+
font-size: var(--secure-ui-font-size-xs);
|
|
169
|
+
color: var(--secure-ui-color-text-secondary);
|
|
170
|
+
line-height: var(--secure-ui-line-height-normal);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Error Container */
|
|
174
|
+
.error-container {
|
|
175
|
+
position: absolute;
|
|
176
|
+
margin-top: var(--secure-ui-form-error-margin-top);
|
|
177
|
+
font-size: var(--secure-ui-error-font-size);
|
|
178
|
+
color: var(--secure-ui-error-color);
|
|
179
|
+
line-height: var(--secure-ui-line-height-normal);
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
max-height: 40px;
|
|
182
|
+
opacity: 1;
|
|
183
|
+
transform: translateY(0);
|
|
184
|
+
transition: opacity 0.2s ease-out, transform 0.2s ease-out, max-height 0.2s ease-out, margin-top 0.2s ease-out;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.error-container.hidden {
|
|
188
|
+
max-height: 0;
|
|
189
|
+
opacity: 0;
|
|
190
|
+
transform: translateY(-4px);
|
|
191
|
+
margin-top: 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@media (prefers-reduced-motion: reduce) {
|
|
195
|
+
.datetime-field,
|
|
196
|
+
.error-container {
|
|
197
|
+
transition: none !important;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Security Tier Styles */
|
|
202
|
+
/* :host([security-tier="authenticated"]) .datetime-field {
|
|
203
|
+
border-color: var(--secure-ui-tier-authenticated);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
:host([security-tier="sensitive"]) .datetime-field {
|
|
207
|
+
border-color: var(--secure-ui-tier-sensitive);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
:host([security-tier="critical"]) .datetime-field {
|
|
211
|
+
border-color: var(--secure-ui-tier-critical);
|
|
212
|
+
} */
|
|
213
|
+
|
|
214
|
+
/* Browser-specific Adjustments */
|
|
215
|
+
/* Chrome/Safari date picker styling */
|
|
216
|
+
.datetime-field::-webkit-calendar-picker-indicator {
|
|
217
|
+
display: inline-block;
|
|
218
|
+
cursor: pointer;
|
|
219
|
+
opacity: 1;
|
|
220
|
+
padding: 4px;
|
|
221
|
+
margin-left: 4px;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.datetime-field:disabled::-webkit-calendar-picker-indicator {
|
|
225
|
+
cursor: not-allowed;
|
|
226
|
+
opacity: 0.3;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* Inner spin button in Chrome/Safari */
|
|
230
|
+
.datetime-field::-webkit-inner-spin-button {
|
|
231
|
+
height: auto;
|
|
232
|
+
opacity: 0.6;
|
|
233
|
+
cursor: pointer;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* Validation States */
|
|
237
|
+
.datetime-field:valid:not(:placeholder-shown):not(:focus) {
|
|
238
|
+
border-color: var(--secure-ui-color-success);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.datetime-field:invalid:not(:placeholder-shown):not(:focus) {
|
|
242
|
+
border-color: var(--secure-ui-color-error);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Focus Within Container */
|
|
246
|
+
.datetime-container:focus-within label {
|
|
247
|
+
color: var(--secure-ui-color-primary);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Disabled State */
|
|
251
|
+
.datetime-container.disabled {
|
|
252
|
+
opacity: var(--secure-ui-input-disabled-opacity);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.datetime-container.disabled label {
|
|
256
|
+
color: var(--secure-ui-color-text-disabled);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* Required Indicator */
|
|
260
|
+
label .required {
|
|
261
|
+
color: var(--secure-ui-color-error);
|
|
262
|
+
margin-left: var(--secure-ui-space-1);
|
|
263
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Secure Date/Time Picker Component
|
|
3
|
+
*
|
|
4
|
+
* A security-first date/time picker component that implements progressive enhancement,
|
|
5
|
+
* validation, and audit logging.
|
|
6
|
+
*
|
|
7
|
+
* Progressive Enhancement Strategy:
|
|
8
|
+
* 1. Without JavaScript: Falls back to native HTML5 date/time inputs
|
|
9
|
+
* 2. With JavaScript: Enhances with validation, range limits, audit logging
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* <secure-datetime
|
|
13
|
+
* security-tier="authenticated"
|
|
14
|
+
* name="appointment"
|
|
15
|
+
* label="Appointment Date"
|
|
16
|
+
* type="datetime-local"
|
|
17
|
+
* min="2024-01-01T00:00"
|
|
18
|
+
* max="2024-12-31T23:59"
|
|
19
|
+
* required
|
|
20
|
+
* ></secure-datetime>
|
|
21
|
+
*
|
|
22
|
+
* Security Features:
|
|
23
|
+
* - Input sanitization and validation
|
|
24
|
+
* - Date range enforcement
|
|
25
|
+
* - Rate limiting for sensitive/critical tiers
|
|
26
|
+
* - Comprehensive audit logging
|
|
27
|
+
* - Timezone awareness
|
|
28
|
+
* - Format validation
|
|
29
|
+
*
|
|
30
|
+
* @module secure-datetime
|
|
31
|
+
* @license MIT
|
|
32
|
+
*/
|
|
33
|
+
import { SecureBaseComponent } from '../../core/base-component.js';
|
|
34
|
+
/**
|
|
35
|
+
* Secure DateTime Web Component
|
|
36
|
+
*
|
|
37
|
+
* Provides a security-hardened date/time picker with progressive enhancement.
|
|
38
|
+
* The component works as a standard HTML5 date/time input without JavaScript and
|
|
39
|
+
* enhances with security features when JavaScript is available.
|
|
40
|
+
*
|
|
41
|
+
* @extends SecureBaseComponent
|
|
42
|
+
*/
|
|
43
|
+
export declare class SecureDateTime extends SecureBaseComponent {
|
|
44
|
+
#private;
|
|
45
|
+
/**
|
|
46
|
+
* Observed attributes for this component
|
|
47
|
+
*
|
|
48
|
+
* @static
|
|
49
|
+
*/
|
|
50
|
+
static get observedAttributes(): string[];
|
|
51
|
+
/**
|
|
52
|
+
* Constructor
|
|
53
|
+
*/
|
|
54
|
+
constructor();
|
|
55
|
+
/**
|
|
56
|
+
* Render the datetime component
|
|
57
|
+
*
|
|
58
|
+
* Security Note: We use native HTML5 date/time inputs wrapped in our web component
|
|
59
|
+
* to ensure progressive enhancement and browser-native date validation.
|
|
60
|
+
*
|
|
61
|
+
* @protected
|
|
62
|
+
*/
|
|
63
|
+
protected render(): DocumentFragment | HTMLElement | null;
|
|
64
|
+
/**
|
|
65
|
+
* Handle attribute changes
|
|
66
|
+
*
|
|
67
|
+
* @protected
|
|
68
|
+
*/
|
|
69
|
+
protected handleAttributeChange(name: string, _oldValue: string | null, newValue: string | null): void;
|
|
70
|
+
/**
|
|
71
|
+
* Get the current value
|
|
72
|
+
*
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
get value(): string;
|
|
76
|
+
/**
|
|
77
|
+
* Set the value
|
|
78
|
+
*
|
|
79
|
+
* @public
|
|
80
|
+
*/
|
|
81
|
+
set value(value: string);
|
|
82
|
+
/**
|
|
83
|
+
* Get the input name
|
|
84
|
+
*
|
|
85
|
+
* @public
|
|
86
|
+
*/
|
|
87
|
+
get name(): string;
|
|
88
|
+
/**
|
|
89
|
+
* Get value as Date object
|
|
90
|
+
*
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
93
|
+
getValueAsDate(): Date | null;
|
|
94
|
+
/**
|
|
95
|
+
* Set value from Date object
|
|
96
|
+
*
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
setValueFromDate(date: Date): void;
|
|
100
|
+
/**
|
|
101
|
+
* Check if the datetime is valid
|
|
102
|
+
*
|
|
103
|
+
* @public
|
|
104
|
+
*/
|
|
105
|
+
get valid(): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Focus the input
|
|
108
|
+
*
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
focus(): void;
|
|
112
|
+
/**
|
|
113
|
+
* Blur the input
|
|
114
|
+
*
|
|
115
|
+
* @public
|
|
116
|
+
*/
|
|
117
|
+
blur(): void;
|
|
118
|
+
/**
|
|
119
|
+
* Cleanup on disconnect
|
|
120
|
+
*/
|
|
121
|
+
disconnectedCallback(): void;
|
|
122
|
+
}
|
|
123
|
+
export default SecureDateTime;
|
|
124
|
+
//# sourceMappingURL=secure-datetime.d.ts.map
|