@wsxjs/wsx-base-components 0.0.8 → 0.0.9
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/index.cjs +2 -2
- package/dist/index.js +1882 -935
- package/dist/style.css +1 -0
- package/package.json +4 -4
- package/src/TodoList.css +197 -0
- package/src/TodoList.wsx +264 -0
- package/src/TodoListLight.css +198 -0
- package/src/TodoListLight.wsx +263 -0
- package/src/UserProfile.css +146 -0
- package/src/UserProfile.wsx +247 -0
- package/src/UserProfileLight.css +146 -0
- package/src/UserProfileLight.wsx +256 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
.user-profile {
|
|
2
|
+
padding: 1.5rem;
|
|
3
|
+
max-width: 800px;
|
|
4
|
+
margin: 0 auto;
|
|
5
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.profile-header {
|
|
9
|
+
margin-bottom: 2rem;
|
|
10
|
+
text-align: center;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.profile-header h2 {
|
|
14
|
+
margin: 0 0 0.5rem 0;
|
|
15
|
+
color: #333;
|
|
16
|
+
font-size: 1.75rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.profile-header .subtitle {
|
|
20
|
+
color: #666;
|
|
21
|
+
font-size: 0.9rem;
|
|
22
|
+
margin: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.profile-content {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
gap: 2rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.profile-section {
|
|
32
|
+
background: #f8f9fa;
|
|
33
|
+
padding: 1.5rem;
|
|
34
|
+
border-radius: 8px;
|
|
35
|
+
border: 1px solid #e0e0e0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.profile-section h3 {
|
|
39
|
+
margin: 0 0 1rem 0;
|
|
40
|
+
color: #333;
|
|
41
|
+
font-size: 1.25rem;
|
|
42
|
+
border-bottom: 2px solid #007bff;
|
|
43
|
+
padding-bottom: 0.5rem;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.form-group {
|
|
47
|
+
margin-bottom: 1rem;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.form-group label {
|
|
51
|
+
display: block;
|
|
52
|
+
margin-bottom: 0.5rem;
|
|
53
|
+
color: #555;
|
|
54
|
+
font-weight: 500;
|
|
55
|
+
font-size: 0.9rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.form-group input[type="checkbox"] {
|
|
59
|
+
margin-right: 0.5rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.input-field {
|
|
63
|
+
width: 100%;
|
|
64
|
+
padding: 0.75rem;
|
|
65
|
+
border: 1px solid #ddd;
|
|
66
|
+
border-radius: 4px;
|
|
67
|
+
font-size: 1rem;
|
|
68
|
+
transition: border-color 0.2s;
|
|
69
|
+
box-sizing: border-box;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.input-field:focus {
|
|
73
|
+
outline: none;
|
|
74
|
+
border-color: #007bff;
|
|
75
|
+
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.profile-actions {
|
|
79
|
+
display: flex;
|
|
80
|
+
gap: 1rem;
|
|
81
|
+
flex-wrap: wrap;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.btn {
|
|
85
|
+
padding: 0.75rem 1.5rem;
|
|
86
|
+
border: none;
|
|
87
|
+
border-radius: 4px;
|
|
88
|
+
font-size: 1rem;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
transition: all 0.2s;
|
|
91
|
+
font-weight: 500;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.btn-primary {
|
|
95
|
+
background: #007bff;
|
|
96
|
+
color: white;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.btn-primary:hover {
|
|
100
|
+
background: #0056b3;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.btn-secondary {
|
|
104
|
+
background: #6c757d;
|
|
105
|
+
color: white;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.btn-secondary:hover {
|
|
109
|
+
background: #545b62;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.btn-warning {
|
|
113
|
+
background: #ffc107;
|
|
114
|
+
color: #212529;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.btn-warning:hover {
|
|
118
|
+
background: #e0a800;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.profile-display {
|
|
122
|
+
background: #f8f9fa;
|
|
123
|
+
padding: 1.5rem;
|
|
124
|
+
border-radius: 8px;
|
|
125
|
+
border: 1px solid #e0e0e0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.profile-display h3 {
|
|
129
|
+
margin: 0 0 1rem 0;
|
|
130
|
+
color: #333;
|
|
131
|
+
font-size: 1.25rem;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.json-display {
|
|
135
|
+
background: #2d2d2d;
|
|
136
|
+
color: #f8f8f2;
|
|
137
|
+
padding: 1rem;
|
|
138
|
+
border-radius: 4px;
|
|
139
|
+
overflow-x: auto;
|
|
140
|
+
font-family: "Courier New", monospace;
|
|
141
|
+
font-size: 0.875rem;
|
|
142
|
+
line-height: 1.5;
|
|
143
|
+
margin: 0;
|
|
144
|
+
white-space: pre-wrap;
|
|
145
|
+
word-wrap: break-word;
|
|
146
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/** @jsxImportSource @wsxjs/wsx-core */
|
|
2
|
+
/**
|
|
3
|
+
* UserProfileLight Component - LightComponent with @state object support
|
|
4
|
+
*
|
|
5
|
+
* Demonstrates:
|
|
6
|
+
* - Using @state decorator with objects in LightComponent
|
|
7
|
+
* - Object property updates trigger automatic rerender
|
|
8
|
+
* - Object replacement (entire object replacement)
|
|
9
|
+
* - Nested object updates
|
|
10
|
+
* - Light DOM (no Shadow DOM) - styles can be inherited from parent
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { LightComponent, state, autoRegister, createLogger } from "@wsxjs/wsx-core";
|
|
14
|
+
import "./UserProfileLight.css";
|
|
15
|
+
|
|
16
|
+
const logger = createLogger("UserProfileLight");
|
|
17
|
+
|
|
18
|
+
interface UserPreferences {
|
|
19
|
+
theme: "light" | "dark";
|
|
20
|
+
language: string;
|
|
21
|
+
notifications: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface UserProfile {
|
|
25
|
+
name: string;
|
|
26
|
+
email: string;
|
|
27
|
+
age: number;
|
|
28
|
+
preferences: UserPreferences;
|
|
29
|
+
lastLogin: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@autoRegister({ tagName: "user-profile-light" })
|
|
33
|
+
export default class UserProfileLight extends LightComponent {
|
|
34
|
+
// @state decorator with object - automatically reactive
|
|
35
|
+
@state private profile: UserProfile = {
|
|
36
|
+
name: "John Doe",
|
|
37
|
+
email: "john@example.com",
|
|
38
|
+
age: 30,
|
|
39
|
+
preferences: {
|
|
40
|
+
theme: "light",
|
|
41
|
+
language: "en",
|
|
42
|
+
notifications: true,
|
|
43
|
+
},
|
|
44
|
+
lastLogin: Date.now(),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
constructor() {
|
|
48
|
+
super();
|
|
49
|
+
logger.info("UserProfileLight initialized");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
render() {
|
|
53
|
+
return (
|
|
54
|
+
<div class="user-profile">
|
|
55
|
+
<div class="profile-header">
|
|
56
|
+
<h2>👤 User Profile (LightComponent)</h2>
|
|
57
|
+
<p class="subtitle">Demonstrates @state decorator with objects</p>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="profile-content">
|
|
61
|
+
<div class="profile-section">
|
|
62
|
+
<h3>Basic Information</h3>
|
|
63
|
+
<div class="form-group">
|
|
64
|
+
<label>Name:</label>
|
|
65
|
+
<input
|
|
66
|
+
type="text"
|
|
67
|
+
value={this.profile.name}
|
|
68
|
+
onInput={this.handleNameChange}
|
|
69
|
+
onBlur={this.handleNameBlur}
|
|
70
|
+
class="input-field"
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="form-group">
|
|
74
|
+
<label>Email:</label>
|
|
75
|
+
<input
|
|
76
|
+
type="email"
|
|
77
|
+
value={this.profile.email}
|
|
78
|
+
onInput={this.handleEmailChange}
|
|
79
|
+
onBlur={this.handleEmailBlur}
|
|
80
|
+
class="input-field"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="form-group">
|
|
84
|
+
<label>Age:</label>
|
|
85
|
+
<input
|
|
86
|
+
type="number"
|
|
87
|
+
value={this.profile.age.toString()}
|
|
88
|
+
onInput={this.handleAgeChange}
|
|
89
|
+
onBlur={this.handleAgeBlur}
|
|
90
|
+
class="input-field"
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div class="profile-section">
|
|
96
|
+
<h3>Preferences</h3>
|
|
97
|
+
<div class="form-group">
|
|
98
|
+
<label>Theme:</label>
|
|
99
|
+
<select
|
|
100
|
+
value={this.profile.preferences.theme}
|
|
101
|
+
onChange={this.handleThemeChange}
|
|
102
|
+
class="input-field"
|
|
103
|
+
>
|
|
104
|
+
<option value="light">Light</option>
|
|
105
|
+
<option value="dark">Dark</option>
|
|
106
|
+
</select>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="form-group">
|
|
109
|
+
<label>Language:</label>
|
|
110
|
+
<select
|
|
111
|
+
value={this.profile.preferences.language}
|
|
112
|
+
onChange={this.handleLanguageChange}
|
|
113
|
+
class="input-field"
|
|
114
|
+
>
|
|
115
|
+
<option value="en">English</option>
|
|
116
|
+
<option value="zh">中文</option>
|
|
117
|
+
<option value="es">Español</option>
|
|
118
|
+
<option value="fr">Français</option>
|
|
119
|
+
</select>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="form-group">
|
|
122
|
+
<label>
|
|
123
|
+
<input
|
|
124
|
+
type="checkbox"
|
|
125
|
+
checked={this.profile.preferences.notifications}
|
|
126
|
+
onChange={this.handleNotificationsChange}
|
|
127
|
+
/>
|
|
128
|
+
Enable Notifications
|
|
129
|
+
</label>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<div class="profile-actions">
|
|
134
|
+
<button class="btn btn-primary" onClick={this.resetProfile}>
|
|
135
|
+
Reset to Default
|
|
136
|
+
</button>
|
|
137
|
+
<button class="btn btn-secondary" onClick={this.updateLastLogin}>
|
|
138
|
+
Update Last Login
|
|
139
|
+
</button>
|
|
140
|
+
<button class="btn btn-warning" onClick={this.replaceEntireProfile}>
|
|
141
|
+
Replace Entire Profile
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div class="profile-display">
|
|
146
|
+
<h3>Current Profile Data</h3>
|
|
147
|
+
<pre class="json-display">{JSON.stringify(this.profile, null, 2)}</pre>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private handleNameChange = (event: Event) => {
|
|
155
|
+
const input = event.target as HTMLInputElement;
|
|
156
|
+
// Update non-reactive property - no rerender
|
|
157
|
+
this.profile.name = input.value;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
private handleNameBlur = () => {
|
|
161
|
+
// Update reactive profile when input loses focus
|
|
162
|
+
this.profile = { ...this.profile, name: this.profile.name };
|
|
163
|
+
logger.debug("Name updated", { name: this.profile.name });
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
private handleEmailChange = (event: Event) => {
|
|
167
|
+
const input = event.target as HTMLInputElement;
|
|
168
|
+
// Update non-reactive property - no rerender
|
|
169
|
+
this.profile.email = input.value;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
private handleEmailBlur = () => {
|
|
173
|
+
// Update reactive profile when input loses focus
|
|
174
|
+
this.profile = { ...this.profile, email: this.profile.email };
|
|
175
|
+
logger.debug("Email updated", { email: this.profile.email });
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
private handleAgeChange = (event: Event) => {
|
|
179
|
+
const input = event.target as HTMLInputElement;
|
|
180
|
+
// Update non-reactive property - no rerender
|
|
181
|
+
this.profile.age = parseInt(input.value, 10) || 0;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
private handleAgeBlur = () => {
|
|
185
|
+
// Update reactive profile when input loses focus
|
|
186
|
+
this.profile = { ...this.profile, age: this.profile.age };
|
|
187
|
+
logger.debug("Age updated", { age: this.profile.age });
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
private handleThemeChange = (event: Event) => {
|
|
191
|
+
const select = event.target as HTMLSelectElement;
|
|
192
|
+
// Direct nested property update - automatically reactive
|
|
193
|
+
this.profile.preferences.theme = select.value as "light" | "dark";
|
|
194
|
+
logger.debug("Theme updated", { theme: select.value });
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
private handleLanguageChange = (event: Event) => {
|
|
198
|
+
const select = event.target as HTMLSelectElement;
|
|
199
|
+
// Direct nested property update - automatically reactive
|
|
200
|
+
this.profile.preferences.language = select.value;
|
|
201
|
+
logger.debug("Language updated", { language: select.value });
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
private handleNotificationsChange = (event: Event) => {
|
|
205
|
+
const checkbox = event.target as HTMLInputElement;
|
|
206
|
+
// Direct nested property update - automatically reactive
|
|
207
|
+
this.profile.preferences.notifications = checkbox.checked;
|
|
208
|
+
logger.debug("Notifications updated", { notifications: checkbox.checked });
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
private resetProfile = () => {
|
|
212
|
+
// Replace entire object - automatically wrapped in reactive
|
|
213
|
+
this.profile = {
|
|
214
|
+
name: "John Doe",
|
|
215
|
+
email: "john@example.com",
|
|
216
|
+
age: 30,
|
|
217
|
+
preferences: {
|
|
218
|
+
theme: "light",
|
|
219
|
+
language: "en",
|
|
220
|
+
notifications: true,
|
|
221
|
+
},
|
|
222
|
+
lastLogin: Date.now(),
|
|
223
|
+
};
|
|
224
|
+
// Sync non-reactive input values
|
|
225
|
+
this._name = "John Doe";
|
|
226
|
+
this._email = "john@example.com";
|
|
227
|
+
this._age = 30;
|
|
228
|
+
logger.debug("Profile reset to default");
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
private updateLastLogin = () => {
|
|
232
|
+
// Update object property - automatically reactive
|
|
233
|
+
this.profile = { ...this.profile, lastLogin: Date.now() };
|
|
234
|
+
logger.debug("Last login updated", { lastLogin: this.profile.lastLogin });
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
private replaceEntireProfile = () => {
|
|
238
|
+
// Replace entire object - automatically wrapped in reactive
|
|
239
|
+
this.profile = {
|
|
240
|
+
name: "Jane Smith",
|
|
241
|
+
email: "jane@example.com",
|
|
242
|
+
age: 25,
|
|
243
|
+
preferences: {
|
|
244
|
+
theme: "dark",
|
|
245
|
+
language: "zh",
|
|
246
|
+
notifications: false,
|
|
247
|
+
},
|
|
248
|
+
lastLogin: Date.now(),
|
|
249
|
+
};
|
|
250
|
+
// Sync non-reactive input values
|
|
251
|
+
this._name = "Jane Smith";
|
|
252
|
+
this._email = "jane@example.com";
|
|
253
|
+
this._age = 25;
|
|
254
|
+
logger.debug("Entire profile replaced");
|
|
255
|
+
};
|
|
256
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,10 @@ export { default as ThemeSwitcher } from "./ThemeSwitcher.wsx";
|
|
|
9
9
|
export { default as SvgIcon } from "./SvgIcon.wsx";
|
|
10
10
|
export { default as SvgDemo } from "./SvgDemo.wsx";
|
|
11
11
|
export { default as SimpleReactiveDemo } from "./SimpleReactiveDemo.wsx";
|
|
12
|
+
export { default as TodoList } from "./TodoList.wsx";
|
|
13
|
+
export { default as TodoListLight } from "./TodoListLight.wsx";
|
|
14
|
+
export { default as UserProfile } from "./UserProfile.wsx";
|
|
15
|
+
export { default as UserProfileLight } from "./UserProfileLight.wsx";
|
|
12
16
|
|
|
13
17
|
// Export utilities
|
|
14
18
|
export * from "./ColorPickerUtils";
|