@wral/my-wral-settings 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { M as a } from "./my-wral-settings-CZEj0day.js";
2
+ export {
3
+ a as MyWralSettings
4
+ };
@@ -0,0 +1,468 @@
1
+ import { LitElement as $, css as F, html as r } from "lit";
2
+ const N = {
3
+ md: 1024
4
+ }, c = {
5
+ preferredName: ["preferred_name"],
6
+ zipCode: ["zipcode", "zip_code"],
7
+ dateOfBirth: ["date_of_birth"]
8
+ }, B = (n) => n?.facts || n?.data?.facts || {}, p = (n, e) => {
9
+ for (const s of e) {
10
+ const t = n?.[s];
11
+ if (t) {
12
+ if (t?.item?.value != null) return String(t.item.value);
13
+ if (Array.isArray(t?.items) && t.items[0]?.value != null)
14
+ return String(t.items[0].value);
15
+ }
16
+ }
17
+ return "";
18
+ }, h = (n) => {
19
+ const e = String(n || "").trim();
20
+ return e ? /^\d{4}-\d{2}-\d{2}/.test(e) ? e.slice(0, 10) : e : "";
21
+ };
22
+ class C extends $ {
23
+ static properties = {
24
+ getUserFacts: { attribute: !1 },
25
+ updateUserFact: { attribute: !1 },
26
+ updateUserFacts: { attribute: !1 },
27
+ loading: { state: !0 },
28
+ saving: { state: !0 },
29
+ error: { state: !0 },
30
+ success: { state: !0 },
31
+ _values: { state: !0 },
32
+ _dirty: { state: !0 },
33
+ _fieldErrors: { state: !0 }
34
+ };
35
+ constructor() {
36
+ super(), this.getUserFacts = null, this.updateUserFact = null, this.updateUserFacts = null, this.loading = !1, this.saving = !1, this.error = "", this.success = "", this._values = {
37
+ preferredName: "",
38
+ zipCode: "",
39
+ dateOfBirth: ""
40
+ }, this._originalValues = { ...this._values }, this._dirty = {
41
+ preferredName: !1,
42
+ zipCode: !1,
43
+ dateOfBirth: !1
44
+ }, this._fieldErrors = {
45
+ preferredName: "",
46
+ zipCode: "",
47
+ dateOfBirth: ""
48
+ }, this._handlePreferredNameInput = (e) => {
49
+ this._handleInput("preferredName", e);
50
+ }, this._handleZipCodeInput = (e) => {
51
+ this._handleInput("zipCode", e);
52
+ }, this._handleDateOfBirthInput = (e) => {
53
+ this._handleInput("dateOfBirth", e);
54
+ };
55
+ }
56
+ static styles = F`
57
+ :host {
58
+ display: block;
59
+ color: var(--mw-settings-ink, #1b1a17);
60
+ font-family: var(--mw-settings-font-family, "Sora",
61
+ "Trebuchet MS", sans-serif);
62
+ }
63
+ .card {
64
+ position: relative;
65
+ overflow: hidden;
66
+ border-radius: 20px;
67
+ border: 1px solid var(--mw-settings-border, #e3dbd1);
68
+ background: var(--mw-settings-surface, #fbf7f2);
69
+ padding: 1.75rem;
70
+ box-shadow: 0 12px 24px rgba(34, 28, 20, 0.08);
71
+ animation: riseIn 0.4s ease-out;
72
+ }
73
+ .card::before {
74
+ content: '';
75
+ position: absolute;
76
+ inset: -60% -20% auto -20%;
77
+ height: 160px;
78
+ background:
79
+ radial-gradient(circle at 20% 20%, rgba(255, 194, 128, 0.55),
80
+ transparent 60%),
81
+ radial-gradient(circle at 85% 0%, rgba(129, 191, 255, 0.4),
82
+ transparent 55%);
83
+ pointer-events: none;
84
+ }
85
+ .header {
86
+ display: flex;
87
+ align-items: flex-start;
88
+ justify-content: space-between;
89
+ gap: 1.5rem;
90
+ position: relative;
91
+ z-index: 1;
92
+ }
93
+ .eyebrow {
94
+ text-transform: uppercase;
95
+ letter-spacing: 0.2em;
96
+ font-size: 0.7rem;
97
+ color: var(--mw-settings-muted, #6b6259);
98
+ margin: 0 0 0.35rem;
99
+ }
100
+ h2 {
101
+ margin: 0 0 0.25rem;
102
+ font-size: 1.5rem;
103
+ line-height: 1.2;
104
+ }
105
+ .subtitle {
106
+ margin: 0;
107
+ color: var(--mw-settings-muted, #6b6259);
108
+ font-size: 0.95rem;
109
+ }
110
+ .status-row {
111
+ display: flex;
112
+ gap: 0.5rem;
113
+ flex-wrap: wrap;
114
+ justify-content: flex-end;
115
+ }
116
+ .pill {
117
+ display: inline-flex;
118
+ align-items: center;
119
+ gap: 0.35rem;
120
+ padding: 0.35rem 0.75rem;
121
+ border-radius: 999px;
122
+ font-size: 0.8rem;
123
+ font-weight: 600;
124
+ background: #ffffff;
125
+ border: 1px solid var(--mw-settings-border, #e3dbd1);
126
+ color: var(--mw-settings-muted, #6b6259);
127
+ }
128
+ .status {
129
+ margin: 1rem 0 0;
130
+ font-size: 0.9rem;
131
+ color: var(--mw-settings-muted, #6b6259);
132
+ }
133
+ .status.error {
134
+ color: #9b2c2c;
135
+ }
136
+ .status.success {
137
+ color: #2f7c3d;
138
+ }
139
+ form {
140
+ margin-top: 1.5rem;
141
+ display: grid;
142
+ gap: 1.25rem;
143
+ position: relative;
144
+ z-index: 1;
145
+ }
146
+ .fields {
147
+ display: grid;
148
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
149
+ gap: 1rem;
150
+ }
151
+ .field {
152
+ background: var(--mw-settings-panel, #ffffff);
153
+ border: 1px solid var(--mw-settings-border, #e3dbd1);
154
+ border-radius: 14px;
155
+ padding: 0.85rem 0.9rem 0.95rem;
156
+ display: grid;
157
+ gap: 0.5rem;
158
+ animation: fieldIn 0.35s ease-out;
159
+ }
160
+ .field:nth-child(2) { animation-delay: 0.04s; }
161
+ .field:nth-child(3) { animation-delay: 0.08s; }
162
+ .label {
163
+ font-weight: 600;
164
+ font-size: 0.9rem;
165
+ }
166
+ .input {
167
+ border: 1px solid #d5cbc0;
168
+ border-radius: 10px;
169
+ padding: 0.55rem 0.7rem;
170
+ font-size: 0.95rem;
171
+ font-family: inherit;
172
+ color: inherit;
173
+ background: #fff;
174
+ }
175
+ .input:focus {
176
+ outline: 2px solid var(--mw-settings-focus, #2563eb);
177
+ outline-offset: 1px;
178
+ border-color: transparent;
179
+ }
180
+ .helper {
181
+ color: var(--mw-settings-muted, #6b6259);
182
+ font-size: 0.8rem;
183
+ }
184
+ .error-text {
185
+ color: #9b2c2c;
186
+ font-size: 0.8rem;
187
+ }
188
+ .action-row {
189
+ display: flex;
190
+ align-items: center;
191
+ justify-content: space-between;
192
+ gap: 0.75rem;
193
+ flex-wrap: wrap;
194
+ }
195
+ .buttons {
196
+ display: flex;
197
+ align-items: center;
198
+ gap: 0.75rem;
199
+ flex-wrap: wrap;
200
+ }
201
+ .button {
202
+ border-radius: 999px;
203
+ border: 1px solid transparent;
204
+ padding: 0.6rem 1.4rem;
205
+ font-size: 0.9rem;
206
+ font-weight: 600;
207
+ cursor: pointer;
208
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
209
+ }
210
+ .button.primary {
211
+ background: var(--mw-settings-accent, #b24b1c);
212
+ color: #fff;
213
+ box-shadow: 0 10px 18px rgba(178, 75, 28, 0.2);
214
+ }
215
+ .button.secondary {
216
+ background: #fff;
217
+ color: var(--mw-settings-ink, #1b1a17);
218
+ border-color: var(--mw-settings-border, #e3dbd1);
219
+ }
220
+ .button:disabled {
221
+ opacity: 0.6;
222
+ cursor: not-allowed;
223
+ transform: none;
224
+ box-shadow: none;
225
+ }
226
+ .meta-note {
227
+ font-size: 0.8rem;
228
+ color: var(--mw-settings-muted, #6b6259);
229
+ }
230
+ @keyframes riseIn {
231
+ from { opacity: 0; transform: translateY(12px); }
232
+ to { opacity: 1; transform: translateY(0); }
233
+ }
234
+ @keyframes fieldIn {
235
+ from { opacity: 0; transform: translateY(6px); }
236
+ to { opacity: 1; transform: translateY(0); }
237
+ }
238
+ @media (prefers-reduced-motion: reduce) {
239
+ .card, .field, .button { animation: none; transition: none; }
240
+ }
241
+ @media (max-width: ${N.md}px) {
242
+ .card { padding: 1.4rem; }
243
+ .header { flex-direction: column; align-items: flex-start; }
244
+ .status-row { justify-content: flex-start; }
245
+ .action-row { flex-direction: column; align-items: stretch; }
246
+ .buttons { width: 100%; }
247
+ }
248
+ `;
249
+ firstUpdated() {
250
+ typeof this.getUserFacts == "function" && this._loadFacts();
251
+ }
252
+ updated(e) {
253
+ e.has("getUserFacts") && typeof this.getUserFacts == "function" && this._loadFacts();
254
+ }
255
+ async _loadFacts() {
256
+ if (typeof this.getUserFacts != "function") {
257
+ this.error = "Missing getUserFacts callback.";
258
+ return;
259
+ }
260
+ this.loading = !0, this.error = "", this.success = "";
261
+ try {
262
+ const e = await this.getUserFacts(), s = B(e), t = {
263
+ preferredName: p(s, c.preferredName),
264
+ zipCode: p(s, c.zipCode),
265
+ dateOfBirth: h(
266
+ p(s, c.dateOfBirth)
267
+ )
268
+ };
269
+ this._values = { ...t }, this._originalValues = { ...t }, this._dirty = {
270
+ preferredName: !1,
271
+ zipCode: !1,
272
+ dateOfBirth: !1
273
+ }, this._fieldErrors = {
274
+ preferredName: "",
275
+ zipCode: "",
276
+ dateOfBirth: ""
277
+ };
278
+ } catch (e) {
279
+ console.error("[my-wral-settings]", e), this.error = "Unable to load user facts.";
280
+ } finally {
281
+ this.loading = !1;
282
+ }
283
+ }
284
+ _handleInput(e, s) {
285
+ const t = s.target.value;
286
+ this._values = { ...this._values, [e]: t }, this._dirty = {
287
+ ...this._dirty,
288
+ [e]: t !== this._originalValues[e]
289
+ }, this._fieldErrors = { ...this._fieldErrors, [e]: "" }, this.error = "", this.success = "";
290
+ }
291
+ _validate(e) {
292
+ const s = {
293
+ preferredName: "",
294
+ zipCode: "",
295
+ dateOfBirth: ""
296
+ };
297
+ return e.zipCode && !/^\d{5}$/.test(e.zipCode) && (s.zipCode = "Zip code must be 5 digits."), e.dateOfBirth && !/^\d{4}-\d{2}-\d{2}$/.test(e.dateOfBirth) && (s.dateOfBirth = "Use YYYY-MM-DD."), s;
298
+ }
299
+ async _handleSubmit(e) {
300
+ if (e.preventDefault(), this.saving) return;
301
+ const s = typeof this.updateUserFact == "function" ? this.updateUserFact : this.updateUserFacts;
302
+ if (typeof s != "function") {
303
+ this.error = "Missing updateUserFact callback.";
304
+ return;
305
+ }
306
+ const t = {
307
+ preferredName: (this._values.preferredName || "").trim(),
308
+ zipCode: (this._values.zipCode || "").trim(),
309
+ dateOfBirth: h(this._values.dateOfBirth)
310
+ }, o = this._validate(t);
311
+ if (Object.values(o).some(Boolean)) {
312
+ this._fieldErrors = o;
313
+ return;
314
+ }
315
+ const a = [];
316
+ if (this._dirty.preferredName && a.push({ factName: "preferred_name", value: t.preferredName }), this._dirty.zipCode && a.push({ factName: "zipcode", value: t.zipCode }), this._dirty.dateOfBirth && a.push({ factName: "date_of_birth", value: t.dateOfBirth }), !!a.length) {
317
+ this.saving = !0, this.error = "", this.success = "", this._values = { ...t };
318
+ try {
319
+ for (const i of a)
320
+ await s(i.factName, i.value);
321
+ this._originalValues = { ...t }, this._dirty = {
322
+ preferredName: !1,
323
+ zipCode: !1,
324
+ dateOfBirth: !1
325
+ }, this.success = "Saved changes.";
326
+ } catch (i) {
327
+ console.error("[my-wral-settings]", i), this.error = "Unable to save changes.";
328
+ } finally {
329
+ this.saving = !1;
330
+ }
331
+ }
332
+ }
333
+ _handleReset() {
334
+ this._values = { ...this._originalValues }, this._dirty = {
335
+ preferredName: !1,
336
+ zipCode: !1,
337
+ dateOfBirth: !1
338
+ }, this._fieldErrors = {
339
+ preferredName: "",
340
+ zipCode: "",
341
+ dateOfBirth: ""
342
+ }, this.error = "", this.success = "";
343
+ }
344
+ render() {
345
+ const { loading: e, saving: s, error: t, success: o, _values: d, _fieldErrors: a } = this, i = Object.values(this._dirty).some(Boolean), f = typeof this.getUserFacts == "function", u = typeof this.updateUserFact == "function" || typeof this.updateUserFacts == "function", l = /* @__PURE__ */ new Date(), m = `${l.getFullYear()}-${String(l.getMonth() + 1).padStart(2, "0")}-${String(l.getDate()).padStart(2, "0")}`, g = t ? r`<div class="status error" role="status">${t}</div>` : "", b = o ? r`<div class="status success" role="status">${o}</div>` : "", v = f ? "" : r`
346
+ <div class="status" role="status">
347
+ Provide a getUserFacts callback to load values.
348
+ </div>
349
+ `, y = f && !u ? r`
350
+ <div class="status" role="status">
351
+ Provide an updateUserFact callback to save changes.
352
+ </div>
353
+ ` : "", _ = a.preferredName ? r`
354
+ <span class="error-text">
355
+ ${a.preferredName}
356
+ </span>
357
+ ` : "", x = a.zipCode ? r`
358
+ <span class="error-text">
359
+ ${a.zipCode}
360
+ </span>
361
+ ` : "", w = a.dateOfBirth ? r`
362
+ <span class="error-text">
363
+ ${a.dateOfBirth}
364
+ </span>
365
+ ` : "", z = i ? "You have unsaved changes." : "All changes are saved.";
366
+ return r`
367
+ <section class="card" aria-busy=${e ? "true" : "false"}>
368
+ <div class="header">
369
+ <div>
370
+ <p class="eyebrow">My WRAL</p>
371
+ <h2>Settings</h2>
372
+ <p class="subtitle">Update the basics tied to your account.</p>
373
+ </div>
374
+ <div class="status-row">
375
+ ${e ? r`<span class="pill">Loading</span>` : ""}
376
+ ${s ? r`<span class="pill">Saving</span>` : ""}
377
+ </div>
378
+ </div>
379
+
380
+ ${g}
381
+ ${b}
382
+ ${v}
383
+ ${y}
384
+
385
+ <form @submit=${this._handleSubmit} novalidate>
386
+ <div class="fields">
387
+ <label class="field">
388
+ <span class="label">Preferred name</span>
389
+ <input
390
+ class="input"
391
+ type="text"
392
+ name="preferred-name"
393
+ autocomplete="name"
394
+ placeholder="e.g. Dominick"
395
+ .value=${d.preferredName}
396
+ @input=${this._handlePreferredNameInput}
397
+ ?disabled=${e || s}
398
+ />
399
+ <span class="helper">What should we call you?</span>
400
+ ${_}
401
+ </label>
402
+
403
+ <label class="field">
404
+ <span class="label">Zip code</span>
405
+ <input
406
+ class="input"
407
+ type="text"
408
+ name="zip-code"
409
+ inputmode="numeric"
410
+ autocomplete="postal-code"
411
+ placeholder="5 digit zip"
412
+ .value=${d.zipCode}
413
+ @input=${this._handleZipCodeInput}
414
+ ?disabled=${e || s}
415
+ pattern="\\d{5}"
416
+ />
417
+ <span class="helper">Used for local weather and alerts.</span>
418
+ ${x}
419
+ </label>
420
+
421
+ <label class="field">
422
+ <span class="label">Date of birth</span>
423
+ <input
424
+ class="input"
425
+ type="date"
426
+ name="date-of-birth"
427
+ autocomplete="bday"
428
+ .value=${d.dateOfBirth}
429
+ @input=${this._handleDateOfBirthInput}
430
+ ?disabled=${e || s}
431
+ max=${m}
432
+ />
433
+ <span class="helper">Format: YYYY-MM-DD</span>
434
+ ${w}
435
+ </label>
436
+ </div>
437
+
438
+ <div class="action-row">
439
+ <span class="meta-note">
440
+ ${z}
441
+ </span>
442
+ <div class="buttons">
443
+ <button
444
+ class="button secondary"
445
+ type="button"
446
+ @click=${this._handleReset}
447
+ ?disabled=${e || s || !i}
448
+ >
449
+ Reset
450
+ </button>
451
+ <button
452
+ class="button primary"
453
+ type="submit"
454
+ ?disabled=${e || s || !i || !u}
455
+ >
456
+ Save changes
457
+ </button>
458
+ </div>
459
+ </div>
460
+ </form>
461
+ </section>
462
+ `;
463
+ }
464
+ }
465
+ globalThis?.customElements && !globalThis.customElements.get("my-wral-settings") && globalThis.customElements.define("my-wral-settings", C);
466
+ export {
467
+ C as M
468
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@wral/my-wral-settings",
3
+ "version": "0.2.4",
4
+ "type": "module",
5
+ "description": "Lit web component for rendering My WRAL settings input fields.",
6
+ "author": "Dominick Jones <djones@wral.com>",
7
+ "license": "UNLICENSED",
8
+ "peerDependencies": {
9
+ "lit": "^3.0.0"
10
+ },
11
+ "scripts": {
12
+ "dev": "vite",
13
+ "lint": "eslint",
14
+ "prepublishOnly": "npm run build",
15
+ "build": "vite build && vite build --config vite.standalone.config.mjs"
16
+ },
17
+ "exports": {
18
+ ".": "./dist/index.mjs",
19
+ "./define": "./dist/define.mjs",
20
+ "./package.json": "./package.json"
21
+ },
22
+ "main": "./dist/index.mjs",
23
+ "module": "./dist/index.mjs",
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "engines": {
28
+ "node": ">=18"
29
+ },
30
+ "dependencies": {
31
+ "@wral/sdk-alerts": "^0.0.1"
32
+ },
33
+ "devDependencies": {
34
+ "eslint": "^9.36.0",
35
+ "vite": "^7.1.7",
36
+ "lit": "^3.3.1"
37
+ }
38
+ }