@pure-ds/core 0.5.56 → 0.5.58
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/.cursorrules +520 -512
- package/.github/copilot-instructions.md +520 -512
- package/custom-elements.json +258 -98
- package/dist/types/public/assets/js/pds-manager.d.ts +141 -426
- package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -1
- package/dist/types/public/assets/js/pds.d.ts +3 -4
- package/dist/types/public/assets/js/pds.d.ts.map +1 -1
- package/dist/types/public/assets/pds/components/pds-omnibox.d.ts.map +1 -1
- package/dist/types/src/js/common/common.d.ts +6 -0
- package/dist/types/src/js/common/common.d.ts.map +1 -1
- package/dist/types/src/js/pds.d.ts.map +1 -1
- package/package.json +1 -1
- package/packages/pds-cli/bin/templates/bootstrap/src/js/app.js +1 -10
- package/public/assets/js/app.js +19 -19
- package/public/assets/js/pds-manager.js +4 -4
- package/public/assets/js/pds.js +6 -6
- package/public/assets/pds/components/pds-omnibox.js +2 -10
- package/public/assets/pds/custom-elements.json +471 -0
- package/public/assets/pds/pds-css-complete.json +590 -179
- package/public/assets/pds/pds.css-data.json +201 -165
- package/public/assets/pds/vscode-custom-data.json +42 -25
- package/src/js/pds.js +6 -15
package/.cursorrules
CHANGED
|
@@ -1,512 +1,520 @@
|
|
|
1
|
-
# PDS (Pure Design System) - AI Generation Instructions
|
|
2
|
-
|
|
3
|
-
> **CRITICAL**: This workspace uses **Pure Design System (PDS)**. All code generation MUST follow PDS and vanilla Web Platform patterns. Never use 3rd party framework patterns, non-PDS utility classes, inline styles, or hardcoded CSS values.
|
|
4
|
-
|
|
5
|
-
## Philosophy
|
|
6
|
-
|
|
7
|
-
PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browser is the framework."
|
|
8
|
-
|
|
9
|
-
1. **Standards-first**: Web Platform APIs only (no framework dependencies)
|
|
10
|
-
2. **Configuration-driven**: `pds.config.js` generates everything
|
|
11
|
-
3. **Progressive Enhancement**: Semantic HTML first, enhance where needed
|
|
12
|
-
4. **Components as Last Resort**: Web Components only when native HTML cannot achieve it
|
|
13
|
-
|
|
14
|
-
### The
|
|
15
|
-
|
|
16
|
-
**Layer 1 — Styles**: From minimal config, PDS generates complete CSS: tokens, scales, semantics, surfaces, states. Zero specificity via `:where()`.
|
|
17
|
-
|
|
18
|
-
**Layer 2 — Enhancements**: Behavior added to semantic HTML via selector-based upgrades (`data-dropdown`, `data-toggle`, etc.).
|
|
19
|
-
|
|
20
|
-
**Layer 3 — Web Components**: `<pds-tabstrip>`, `<pds-drawer>`, etc. only when native HTML has no equivalent.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
|
31
|
-
|
|
32
|
-
| **
|
|
33
|
-
| **
|
|
34
|
-
| **
|
|
35
|
-
| **
|
|
36
|
-
| **
|
|
37
|
-
|
|
38
|
-
**
|
|
39
|
-
|
|
40
|
-
- `
|
|
41
|
-
- `
|
|
42
|
-
- `
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
**
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
**
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
"/
|
|
126
|
-
"/
|
|
127
|
-
"/
|
|
128
|
-
"/
|
|
129
|
-
"/
|
|
130
|
-
"/
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
-
|
|
141
|
-
- `
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
await PDS.toast('
|
|
193
|
-
await PDS.toast('
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
{ const: "
|
|
217
|
-
{ const: "
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
"/
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
'ui:
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
form
|
|
281
|
-
|
|
282
|
-
form.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
await
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
<button class="btn-
|
|
399
|
-
<button class="btn-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
<div class="
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
</
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
</
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
</
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
//
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
**
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
1
|
+
# PDS (Pure Design System) - AI Generation Instructions
|
|
2
|
+
|
|
3
|
+
> **CRITICAL**: This workspace uses **Pure Design System (PDS)**. All code generation MUST follow PDS and vanilla Web Platform patterns. Never use 3rd party framework patterns, non-PDS utility classes, inline styles, or hardcoded CSS values.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browser is the framework."
|
|
8
|
+
|
|
9
|
+
1. **Standards-first**: Web Platform APIs only (no framework dependencies)
|
|
10
|
+
2. **Configuration-driven**: `pds.config.js` generates everything
|
|
11
|
+
3. **Progressive Enhancement**: Semantic HTML first, enhance where needed
|
|
12
|
+
4. **Components as Last Resort**: Web Components only when native HTML cannot achieve it
|
|
13
|
+
|
|
14
|
+
### The Four Layers
|
|
15
|
+
|
|
16
|
+
**Layer 1 — Styles**: From minimal config, PDS generates complete CSS: tokens, scales, semantics, surfaces, states. Zero specificity via `:where()`.
|
|
17
|
+
|
|
18
|
+
**Layer 2 — Enhancements**: Behavior added to semantic HTML via selector-based upgrades (`data-dropdown`, `data-toggle`, etc.).
|
|
19
|
+
|
|
20
|
+
**Layer 3 — Web Components**: `<pds-tabstrip>`, `<pds-drawer>`, etc. only when native HTML has no equivalent.
|
|
21
|
+
|
|
22
|
+
**Layer 4: LLM Support**: Install `@pure-ds/core` and get instant PDS AI Coding Instrucions at your fingertips (GitHub Copilot & Cursor support built in)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🔍 Single Sources of Truth (ALWAYS CONSULT THESE FIRST)
|
|
27
|
+
|
|
28
|
+
**Before generating code, read the relevant SSoT file to get accurate class names, tokens, and APIs.**
|
|
29
|
+
|
|
30
|
+
| Need | SSoT File | What It Contains |
|
|
31
|
+
|------|-----------|------------------|
|
|
32
|
+
| **CSS Tokens** | `public/assets/pds/pds.css-data.json` | All `--color-*`, `--spacing-*`, `--radius-*`, `--shadow-*`, `--font-*` |
|
|
33
|
+
| **Web Components** | `custom-elements.json` | Complete component APIs, attributes, methods, events, slots |
|
|
34
|
+
| **HTML Tags** | `public/assets/pds/vscode-custom-data.json` | Component HTML structure, attribute values |
|
|
35
|
+
| **Primitives & Utilities** | `src/js/pds-core/pds-ontology.js` | `.card`, `.badge`, `.btn-*`, `.flex`, `.gap-*`, `.surface-*` |
|
|
36
|
+
| **Enhancements** | `src/js/pds-core/pds-enhancers.js` | Enhancement metadata (`defaultPDSEnhancerMetadata`) + runtime (`defaultPDSEnhancers`) |
|
|
37
|
+
| **Generator Logic** | `src/js/pds-core/pds-generator.js` | How CSS is generated, token naming conventions |
|
|
38
|
+
| **Config** | `pds.config.js` | What's enabled in this workspace |
|
|
39
|
+
|
|
40
|
+
**For consuming projects** using `@pure-ds/core`, files are in `node_modules/@pure-ds/core/`:
|
|
41
|
+
- `custom-elements.json`
|
|
42
|
+
- `public/assets/pds/pds.css-data.json`
|
|
43
|
+
- `public/assets/pds/vscode-custom-data.json`
|
|
44
|
+
- `src/js/pds-core/pds-ontology.js`
|
|
45
|
+
|
|
46
|
+
**Path resolution helper:** When looking up SSoT files:
|
|
47
|
+
1. First check if `node_modules/@pure-ds/core/` exists (consuming project)
|
|
48
|
+
2. Otherwise use workspace root paths (pure-ds development)
|
|
49
|
+
3. Prefer reading actual files over guessing - the data is authoritative
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 📋 pds-form Best Practices
|
|
54
|
+
|
|
55
|
+
**When generating pds-form code, ALWAYS follow these patterns:**
|
|
56
|
+
|
|
57
|
+
### 1. Event Handling - Use `pw:submit`, NOT `submit`
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// ✅ CORRECT: Listen to pw:submit custom event
|
|
61
|
+
form.addEventListener('pw:submit', async (e) => {
|
|
62
|
+
const { json, formData, valid, issues } = e.detail;
|
|
63
|
+
if (valid) {
|
|
64
|
+
// Handle submission with json or formData
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ❌ WRONG: Native submit event
|
|
69
|
+
form.addEventListener('submit', (e) => { /* Won't work */ });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 2. Submit Button Progress - Add `btn-working` automatically
|
|
73
|
+
|
|
74
|
+
**When user requests a form with async submission, ALWAYS:**
|
|
75
|
+
- Add `btn-working` class to submit button during processing
|
|
76
|
+
- Remove it when done (PDS automatically shows spinner icon)
|
|
77
|
+
- Use a realistic 2-3 second delay for demos
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
// ✅ CORRECT: Auto-add progress state
|
|
81
|
+
form.addEventListener('pw:submit', async (e) => {
|
|
82
|
+
const submitBtn = form.querySelector('button[type="submit"]');
|
|
83
|
+
submitBtn?.classList.add('btn-working');
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await simulateSubmit(e.detail.json); // 2-3 second promise
|
|
87
|
+
await PDS.toast('Submitted successfully!', { type: 'success' });
|
|
88
|
+
} finally {
|
|
89
|
+
submitBtn?.classList.remove('btn-working');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
async function simulateSubmit(data) {
|
|
94
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
95
|
+
console.log('Submitted:', data);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 3. Adding `data-required` to pds-form generated form: simply add the attribute to the pds-form tag
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<pds-form data-required id="myForm" hide-actions></pds-form>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 4. Placeholders - ALWAYS include examples
|
|
106
|
+
|
|
107
|
+
**Placeholders improve UX significantly. Try to add 'examples' array to schema properties:**
|
|
108
|
+
|
|
109
|
+
**Rule: When generating a form, infer appropriate placeholders based on field name/type if not specified.**
|
|
110
|
+
|
|
111
|
+
### 5. Smart Icons - Infer from field semantics
|
|
112
|
+
|
|
113
|
+
**When generating forms, automatically add appropriate icons based on field names and semantics.**
|
|
114
|
+
|
|
115
|
+
**Sources of truth for available icons:**
|
|
116
|
+
- Check `pds.config.js` for project-specific icon configuration
|
|
117
|
+
- Consult icon sprite at `public/assets/img/icons/pds-icons.svg` for available icons
|
|
118
|
+
- See `public/assets/pds/vscode-custom-data.json` for icon attribute values
|
|
119
|
+
|
|
120
|
+
**Use semantic reasoning to match field names to appropriate icons:**
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
// ✅ CORRECT: Infer icons based on field semantics
|
|
124
|
+
const uiSchema = {
|
|
125
|
+
"/email": { 'ui:icon': 'envelope', 'ui:autocomplete': 'email' },
|
|
126
|
+
"/phone": { 'ui:icon': 'phone', 'ui:autocomplete': 'tel' },
|
|
127
|
+
"/name": { 'ui:icon': 'user', 'ui:autocomplete': 'name' },
|
|
128
|
+
"/password": { 'ui:icon': 'lock', 'ui:widget': 'password' },
|
|
129
|
+
"/website": { 'ui:icon': 'link' },
|
|
130
|
+
"/address": { 'ui:icon': 'map-pin' },
|
|
131
|
+
"/date": { 'ui:icon': 'calendar' },
|
|
132
|
+
"/message": { 'ui:widget': 'textarea', 'ui:icon': 'message' }
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Rule: When generating forms, analyze field names/types and select semantically appropriate icons from the available icon set.**
|
|
137
|
+
|
|
138
|
+
### 6. Submit Handler Pattern - ALWAYS provide working async handler
|
|
139
|
+
|
|
140
|
+
**When generating a pds-form, ALWAYS include a complete, iteration-ready submit handler with:**
|
|
141
|
+
- `pw:submit` event (NOT native submit)
|
|
142
|
+
- `btn-working` class for loading state
|
|
143
|
+
- `PDS.toast()` for user feedback
|
|
144
|
+
- Error handling
|
|
145
|
+
- Realistic async simulation
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
// ✅ CORRECT: Complete submit handler pattern
|
|
149
|
+
form.addEventListener('pw:submit', async (e) => {
|
|
150
|
+
const submitBtn = form.querySelector('button[type="submit"]');
|
|
151
|
+
submitBtn?.classList.add('btn-working');
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
// Simulate async operation (replace with real API call)
|
|
155
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
156
|
+
|
|
157
|
+
// Log the data for debugging
|
|
158
|
+
console.log('Submitted data:', e.detail.json);
|
|
159
|
+
|
|
160
|
+
// Show success toast
|
|
161
|
+
await PDS.toast('Form submitted successfully!', { type: 'success' });
|
|
162
|
+
|
|
163
|
+
// Optionally reset form
|
|
164
|
+
form.reset();
|
|
165
|
+
} catch (error) {
|
|
166
|
+
// Show error toast
|
|
167
|
+
await PDS.toast('Submission failed: ' + error.message, { type: 'error' });
|
|
168
|
+
} finally {
|
|
169
|
+
// Always remove loading state
|
|
170
|
+
submitBtn?.classList.remove('btn-working');
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// ❌ WRONG: Native submit event
|
|
175
|
+
form.addEventListener('submit', (e) => { /* Won't work */ });
|
|
176
|
+
|
|
177
|
+
// ❌ WRONG: No loading state
|
|
178
|
+
form.addEventListener('pw:submit', async (e) => {
|
|
179
|
+
await fetch('/api'); // No visual feedback!
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ❌ WRONG: Browser dialogs
|
|
183
|
+
form.addEventListener('pw:submit', async (e) => {
|
|
184
|
+
alert('Submitted!'); // Use PDS.toast() instead
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**PDS.toast() is available globally via window.PDS:**
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
// All toast types
|
|
192
|
+
await PDS.toast('Success message', { type: 'success' });
|
|
193
|
+
await PDS.toast('Error occurred', { type: 'error' });
|
|
194
|
+
await PDS.toast('Warning message', { type: 'warning' });
|
|
195
|
+
await PDS.toast('Info message', { type: 'information' });
|
|
196
|
+
|
|
197
|
+
// Custom duration (auto-calculated by default based on message length)
|
|
198
|
+
await PDS.toast('Quick message', { type: 'info', duration: 3000 });
|
|
199
|
+
|
|
200
|
+
// Persistent (requires manual close)
|
|
201
|
+
await PDS.toast('Important notice', { type: 'warning', persistent: true });
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 7. Conditional "Other" Fields - Auto-generate ui:visibleWhen
|
|
205
|
+
|
|
206
|
+
**When a schema has an "Other" enum option, ALWAYS auto-generate a conditional text field:**
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
const schema = {
|
|
210
|
+
type: "object",
|
|
211
|
+
properties: {
|
|
212
|
+
reason: {
|
|
213
|
+
type: "string",
|
|
214
|
+
title: "How did you hear about us?",
|
|
215
|
+
oneOf: [
|
|
216
|
+
{ const: "search", title: "Search Engine" },
|
|
217
|
+
{ const: "social", title: "Social Media" },
|
|
218
|
+
{ const: "friend", title: "Friend Referral" },
|
|
219
|
+
{ const: "other", title: "Other... (please specify)" }, // ← "Other" option
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
otherReason: { // ← Conditional field for "Other"
|
|
223
|
+
type: "string",
|
|
224
|
+
title: "Please specify",
|
|
225
|
+
examples: ["Tell us more..."],
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const uiSchema = {
|
|
231
|
+
// ✅ ALWAYS add these when "other" enum exists
|
|
232
|
+
"/otherReason": {
|
|
233
|
+
"ui:visibleWhen": { "/reason": "other" },
|
|
234
|
+
"ui:requiredWhen": { "/reason": "other" },
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 8. Complete Working Example
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
// Schema with examples for placeholders
|
|
243
|
+
const contactSchema = {
|
|
244
|
+
type: "object",
|
|
245
|
+
required: ["name", "email", "message"],
|
|
246
|
+
properties: {
|
|
247
|
+
name: {
|
|
248
|
+
type: "string",
|
|
249
|
+
title: "Name",
|
|
250
|
+
minLength: 2,
|
|
251
|
+
examples: ["John Doe"]
|
|
252
|
+
},
|
|
253
|
+
email: {
|
|
254
|
+
type: "string",
|
|
255
|
+
format: "email",
|
|
256
|
+
title: "Email",
|
|
257
|
+
examples: ["user@example.com"]
|
|
258
|
+
},
|
|
259
|
+
message: {
|
|
260
|
+
type: "string",
|
|
261
|
+
title: "Message",
|
|
262
|
+
minLength: 10,
|
|
263
|
+
examples: ["Your message here..."]
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// UI schema with smart icons
|
|
269
|
+
const uiSchema = {
|
|
270
|
+
"/name": { 'ui:icon': 'user' },
|
|
271
|
+
"/email": { 'ui:icon': 'envelope' },
|
|
272
|
+
"/message": {
|
|
273
|
+
'ui:widget': 'textarea',
|
|
274
|
+
'ui:rows': 4,
|
|
275
|
+
'ui:icon': 'message'
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Setup with pw:submit and btn-working
|
|
280
|
+
const form = document.getElementById('contactForm');
|
|
281
|
+
form.jsonSchema = contactSchema;
|
|
282
|
+
form.uiSchema = uiSchema;
|
|
283
|
+
|
|
284
|
+
form.addEventListener('pw:submit', async (e) => {
|
|
285
|
+
const submitBtn = form.querySelector('button[type="submit"]');
|
|
286
|
+
submitBtn?.classList.add('btn-working');
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
// Simulate 2s async operation
|
|
290
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
291
|
+
console.log('Submitted:', e.detail.json);
|
|
292
|
+
await PDS.toast('Message sent!', { type: 'success' });
|
|
293
|
+
form.reset();
|
|
294
|
+
} catch (error) {
|
|
295
|
+
await PDS.toast('Failed to send', { type: 'error' });
|
|
296
|
+
} finally {
|
|
297
|
+
submitBtn?.classList.remove('btn-working');
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## 🚫 Critical Anti-Patterns (NEVER DO THIS)
|
|
305
|
+
|
|
306
|
+
```html
|
|
307
|
+
<!-- ❌ NEVER: Inline styles -->
|
|
308
|
+
<div style="display: flex; gap: 16px; padding: 20px;">
|
|
309
|
+
|
|
310
|
+
<!-- ❌ NEVER: Hardcoded colors -->
|
|
311
|
+
<button style="background: #007acc; color: white;">
|
|
312
|
+
|
|
313
|
+
<!-- ❌ NEVER: Non-semantic HTML -->
|
|
314
|
+
<div class="button" onclick="handleClick()">Click me</div>
|
|
315
|
+
|
|
316
|
+
<!-- ❌ NEVER: Custom CSS when primitives exist -->
|
|
317
|
+
<style>.my-card { border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }</style>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
// ❌ NEVER: Browser dialogs - Use PDS.ask() and PDS.toast()
|
|
322
|
+
alert("message"); // → await PDS.toast("message", { type: "info" })
|
|
323
|
+
confirm("sure?"); // → await PDS.ask("sure?", { type: "confirm" })
|
|
324
|
+
prompt("name?"); // → await PDS.ask("name?", { type: "prompt" })
|
|
325
|
+
|
|
326
|
+
// ❌ NEVER: Manual dropdown/modal/tab implementations
|
|
327
|
+
// → Use <nav data-dropdown>, PDS.ask(), <pds-tabstrip>
|
|
328
|
+
|
|
329
|
+
// ❌ NEVER: Access lazy-loaded component APIs before they're defined
|
|
330
|
+
const form = document.querySelector('pds-form');
|
|
331
|
+
form.getFormData(); // May fail - component not loaded yet
|
|
332
|
+
|
|
333
|
+
// ❌ NEVER: Use native 'submit' event with pds-form
|
|
334
|
+
form.addEventListener('submit', (e) => { }); // → Use 'pw:submit'
|
|
335
|
+
|
|
336
|
+
// ❌ NEVER: Forget btn-working class for async operations
|
|
337
|
+
button.onclick = async () => {
|
|
338
|
+
await fetch('/api'); // No loading indicator!
|
|
339
|
+
};
|
|
340
|
+
// → Add button.classList.add('btn-working') before, remove after
|
|
341
|
+
|
|
342
|
+
// ❌ NEVER: Hardcode placeholders instead of using schema examples
|
|
343
|
+
const schema = {
|
|
344
|
+
properties: {
|
|
345
|
+
email: { type: "string" } // Missing examples!
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
// → Add examples: ["user@example.com"]
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## ⚡ Lit Components & Import Maps
|
|
354
|
+
|
|
355
|
+
**Components that require Lit:** `pds-form`
|
|
356
|
+
|
|
357
|
+
This component uses `import { ... } from "#pds/lit"` and **requires** an import map:
|
|
358
|
+
|
|
359
|
+
```html
|
|
360
|
+
<!-- REQUIRED in HTML <head> for Lit components -->
|
|
361
|
+
<script type="importmap">
|
|
362
|
+
{
|
|
363
|
+
"imports": {
|
|
364
|
+
"#pds/lit": "/assets/pds/external/lit.js"
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
</script>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Note:** `#pds/lit` is a convenience bundle that re-exports Lit and adds PDS helpers (`lazyProps`, `msg()`, `loadLocale()`). If a project prefers its own Lit bundle, it must provide equivalents and map `#pds/lit` accordingly.
|
|
371
|
+
|
|
372
|
+
**When generating code with lazy-loaded components, ALWAYS wait for definition:**
|
|
373
|
+
|
|
374
|
+
```javascript
|
|
375
|
+
// ✅ CORRECT: Wait for component to load
|
|
376
|
+
await customElements.whenDefined('pds-form');
|
|
377
|
+
const form = document.querySelector('pds-form');
|
|
378
|
+
form.getFormData(); // Safe
|
|
379
|
+
|
|
380
|
+
// ✅ CORRECT: Alternative pattern
|
|
381
|
+
const FormClass = await customElements.get('pds-form');
|
|
382
|
+
if (FormClass) {
|
|
383
|
+
const form = document.createElement('pds-form');
|
|
384
|
+
// ...
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ❌ WRONG: Direct access without waiting
|
|
388
|
+
const form = document.querySelector('pds-form');
|
|
389
|
+
form.getFormData(); // May throw error
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## ✅ Quick Reference Patterns
|
|
395
|
+
|
|
396
|
+
```html
|
|
397
|
+
<!-- Buttons: semantic HTML + PDS classes (see pds-ontology.js → primitives) -->
|
|
398
|
+
<button class="btn-primary">Save</button>
|
|
399
|
+
<button class="btn-secondary">Cancel</button>
|
|
400
|
+
<button class="btn-outline">Details</button>
|
|
401
|
+
<button class="btn-primary icon-only" aria-label="Settings">
|
|
402
|
+
<pds-icon icon="gear"></pds-icon>
|
|
403
|
+
</button>
|
|
404
|
+
|
|
405
|
+
<!-- Layout: utility classes (see pds-ontology.js → layoutPatterns, utilities) -->
|
|
406
|
+
<div class="flex gap-md items-center">
|
|
407
|
+
<div class="grid grid-cols-3 gap-lg">
|
|
408
|
+
<div class="stack-md">
|
|
409
|
+
|
|
410
|
+
<!-- Cards & Surfaces: primitives -->
|
|
411
|
+
<article class="card surface-elevated">
|
|
412
|
+
<header class="flex justify-between items-center">
|
|
413
|
+
<h3>Title</h3>
|
|
414
|
+
</header>
|
|
415
|
+
<p class="text-muted">Content</p>
|
|
416
|
+
</article>
|
|
417
|
+
|
|
418
|
+
<!-- Icons: web component (see custom-elements.json) -->
|
|
419
|
+
<pds-icon icon="heart" size="sm"></pds-icon>
|
|
420
|
+
<pds-icon icon="check" size="lg" color="var(--color-success-500)"></pds-icon>
|
|
421
|
+
|
|
422
|
+
<!-- Enhancements: data attributes (see pds-enhancers.js → defaultPDSEnhancerMetadata) -->
|
|
423
|
+
<nav data-dropdown>
|
|
424
|
+
<button>Menu</button>
|
|
425
|
+
<menu><li><a href="#">Item</a></li></menu>
|
|
426
|
+
</nav>
|
|
427
|
+
|
|
428
|
+
<label data-toggle>
|
|
429
|
+
<input type="checkbox">
|
|
430
|
+
<span data-label>Enable feature</span>
|
|
431
|
+
</label>
|
|
432
|
+
|
|
433
|
+
<form data-required>
|
|
434
|
+
<label><span>Email</span><input type="email" required></label>
|
|
435
|
+
</form>
|
|
436
|
+
|
|
437
|
+
<!-- Tabs: web component -->
|
|
438
|
+
<pds-tabstrip>
|
|
439
|
+
<pds-tabpanel label="Tab 1">
|
|
440
|
+
<p>Content for Tab 1</p>
|
|
441
|
+
</pds-tabpanel>
|
|
442
|
+
<pds-tabpanel label="Tab 2">
|
|
443
|
+
<p>Content for Tab 2</p>
|
|
444
|
+
</pds-tabpanel>
|
|
445
|
+
</pds-tabstrip>
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Empty State Pattern
|
|
449
|
+
|
|
450
|
+
- Use the `.empty-state` primitive for empty or onboarding states.
|
|
451
|
+
- Structure: heading + supporting text, an icon, then primary/secondary actions.
|
|
452
|
+
- Keep actions as buttons or links with PDS button classes, and include a meaningful icon when available.
|
|
453
|
+
|
|
454
|
+
```javascript
|
|
455
|
+
// Dialogs & Toasts: PDS API
|
|
456
|
+
const confirmed = await PDS.ask("Delete this item?", {
|
|
457
|
+
type: "confirm",
|
|
458
|
+
buttons: { ok: { name: "Delete", variant: "danger" } }
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
await PDS.toast("Saved successfully!", { type: "success" });
|
|
462
|
+
|
|
463
|
+
// Theme management
|
|
464
|
+
PDS.theme = 'dark'; // 'light' | 'dark' | 'system'
|
|
465
|
+
|
|
466
|
+
// Query the design system
|
|
467
|
+
const results = await PDS.query("border gradient classes");
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## 📚 Additional Resources
|
|
473
|
+
|
|
474
|
+
**For comprehensive pds-form documentation:**
|
|
475
|
+
- Read [pds-form-docs.md](../pds-form-docs.md) for complete API reference
|
|
476
|
+
- See [packages/pds-storybook/stories/components/PdsForm.stories.js](../packages/pds-storybook/stories/components/PdsForm.stories.js) for real examples
|
|
477
|
+
- Check [custom-elements.json](../custom-elements.json) for component API details
|
|
478
|
+
|
|
479
|
+
**For toast notifications:**
|
|
480
|
+
- Use `PDS.toast()` method (see [src/js/common/toast.js](../src/js/common/toast.js) for implementation)
|
|
481
|
+
- Automatically ensures pds-toaster exists and is loaded before displaying
|
|
482
|
+
- See pds-toaster component API in [custom-elements.json](../custom-elements.json)
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## How to Look Things Up
|
|
487
|
+
|
|
488
|
+
| Question | Action |
|
|
489
|
+
|----------|--------|
|
|
490
|
+
| "What CSS tokens exist?" | Read `pds.css-data.json` |
|
|
491
|
+
| "What components are available?" | Read `custom-elements.json` |
|
|
492
|
+
| "What utility classes exist?" | Read `pds-ontology.js` → `layoutPatterns`, `utilities` |
|
|
493
|
+
| "What primitives exist?" | Read `pds-ontology.js` → `primitives` |
|
|
494
|
+
| "How do I enhance HTML?" | Read `pds-enhancers.js` → `defaultPDSEnhancerMetadata` → `demoHtml` |
|
|
495
|
+
| "How are tokens named?" | Read `pds-generator.js` or `pds.css-data.json` |
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## Summary Checklist
|
|
500
|
+
|
|
501
|
+
Before generating code:
|
|
502
|
+
|
|
503
|
+
1. ✅ **Consult SSoT files** — Don't guess class names or token names
|
|
504
|
+
2. ✅ **No inline styles** — Use CSS tokens via custom properties
|
|
505
|
+
3. ✅ **No hardcoded values** — Colors, spacing, radii all have tokens
|
|
506
|
+
4. ✅ **No alert/confirm/prompt** — Use `PDS.ask()` and `PDS.toast()`
|
|
507
|
+
5. ✅ **Use semantic HTML** — `<button>`, `<nav>`, `<article>`, `<label>`, `<details>`
|
|
508
|
+
6. ✅ **Apply enhancements via data-* attributes** — See `pds-enhancers.js` → `defaultPDSEnhancerMetadata`
|
|
509
|
+
7. ✅ **Components as last resort** — Only when native HTML can't achieve it
|
|
510
|
+
8. ✅ **Prefer primitives** — `.card`, `.badge`, `.callout` over custom components
|
|
511
|
+
9. ✅ **Wait for lazy components** — Use `await customElements.whenDefined()` before accessing APIs
|
|
512
|
+
10. ✅ **Include import map** — When using `pds-form` or `pds-drawer`, ensure `#pds/lit` is mapped
|
|
513
|
+
|
|
514
|
+
**For pds-form specifically:**
|
|
515
|
+
|
|
516
|
+
11. ✅ **Use `pw:submit` event** — NOT native `submit` event
|
|
517
|
+
12. ✅ **Add `btn-working` class** — For async submit operations, add during processing
|
|
518
|
+
13. ✅ **Use `examples` in JSON schema** — First example becomes placeholder
|
|
519
|
+
14. ✅ **Add smart icons** — Infer icons based on field names (email→envelope, phone→phone)
|
|
520
|
+
15. ✅ **Wrap in `form[data-required]`** — For asterisk enhancement on required fields
|