@pure-ds/core 0.4.24 β†’ 0.4.26

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.
@@ -41,6 +41,356 @@ PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browse
41
41
  - `public/assets/pds/vscode-custom-data.json`
42
42
  - `src/js/pds-core/pds-ontology.js`
43
43
 
44
+ **Path resolution helper:** When looking up SSoT files:
45
+ 1. First check if `node_modules/@pure-ds/core/` exists (consuming project)
46
+ 2. Otherwise use workspace root paths (pure-ds development)
47
+ 3. Prefer reading actual files over guessing - the data is authoritative
48
+
49
+ ---
50
+
51
+ ## πŸ“‹ pds-form Best Practices
52
+
53
+ **When generating pds-form code, ALWAYS follow these patterns:**
54
+
55
+ ### 1. Event Handling - Use `pw:submit`, NOT `submit`
56
+
57
+ ```javascript
58
+ // βœ… CORRECT: Listen to pw:submit custom event
59
+ form.addEventListener('pw:submit', async (e) => {
60
+ const { json, formData, valid, issues } = e.detail;
61
+ if (valid) {
62
+ // Handle submission with json or formData
63
+ }
64
+ });
65
+
66
+ // ❌ WRONG: Native submit event
67
+ form.addEventListener('submit', (e) => { /* Won't work */ });
68
+ ```
69
+
70
+ ### 2. Submit Button Progress - Add `btn-working` automatically
71
+
72
+ **When user requests a form with async submission, ALWAYS:**
73
+ - Add `btn-working` class to submit button during processing
74
+ - Remove it when done (PDS automatically shows spinner icon)
75
+ - Use a realistic 2-3 second delay for demos
76
+
77
+ ```javascript
78
+ // βœ… CORRECT: Auto-add progress state
79
+ form.addEventListener('pw:submit', async (e) => {
80
+ const submitBtn = form.querySelector('button[type="submit"]');
81
+ submitBtn?.classList.add('btn-working');
82
+
83
+ try {
84
+ await simulateSubmit(e.detail.json); // 2-3 second promise
85
+ await PDS.toast('Submitted successfully!', { type: 'success' });
86
+ } finally {
87
+ submitBtn?.classList.remove('btn-working');
88
+ }
89
+ });
90
+
91
+ async function simulateSubmit(data) {
92
+ await new Promise(resolve => setTimeout(resolve, 2000));
93
+ console.log('Submitted:', data);
94
+ }
95
+ ```
96
+
97
+ ### 3. Progressive Enhancement - Add `data-required` to form wrapper
98
+
99
+ ```html
100
+ <!-- βœ… CORRECT: Wrap pds-form in form[data-required] -->
101
+ <form data-required>
102
+ <pds-form id="myForm" hide-actions></pds-form>
103
+ <div class="form-actions">
104
+ <button type="submit" class="btn-primary">Submit</button>
105
+ </div>
106
+ </form>
107
+
108
+ <!-- ❌ WRONG: No data-required enhancement -->
109
+ <pds-form id="myForm"></pds-form>
110
+ ```
111
+
112
+ ### 4. Placeholders - ALWAYS include examples
113
+
114
+ **Placeholders improve UX significantly. ALWAYS add examples to schema properties:**
115
+
116
+ ```javascript
117
+ // βœ… CORRECT: Every field has an example (becomes placeholder)
118
+ const schema = {
119
+ type: "object",
120
+ properties: {
121
+ name: {
122
+ type: "string",
123
+ title: "Full Name",
124
+ examples: ["John Doe"] // First example becomes placeholder
125
+ },
126
+ email: {
127
+ type: "string",
128
+ format: "email",
129
+ title: "Email Address",
130
+ examples: ["john@example.com"] // Shows expected format
131
+ },
132
+ phone: {
133
+ type: "string",
134
+ title: "Phone Number",
135
+ examples: ["+1 (555) 123-4567"] // Pattern example
136
+ },
137
+ age: {
138
+ type: "integer",
139
+ title: "Age",
140
+ examples: [25] // Works for numbers too
141
+ },
142
+ bio: {
143
+ type: "string",
144
+ title: "About You",
145
+ examples: ["Tell us about yourself..."] // Long text hint
146
+ }
147
+ }
148
+ };
149
+
150
+ // ❌ WRONG: Missing examples - fields will have no placeholders
151
+ const badSchema = {
152
+ type: "object",
153
+ properties: {
154
+ email: { type: "string", format: "email" } // No placeholder!
155
+ }
156
+ };
157
+ ```
158
+
159
+ **Rule: When generating a form, infer appropriate placeholders based on field name/type if not specified.**
160
+
161
+ ### 5. Smart Icons - Auto-infer from field names
162
+
163
+ **When generating forms, automatically add icons for common field types. Match field names (or semantic meaning) to these icons:**
164
+
165
+ ```javascript
166
+ // Icon inference map - use these patterns by default
167
+ const iconInference = {
168
+ // Contact & Identity
169
+ 'email': 'envelope',
170
+ 'phone': 'phone',
171
+ 'mobile': 'phone',
172
+ 'name': 'user',
173
+ 'username': 'user',
174
+ 'firstname': 'user',
175
+ 'lastname': 'user',
176
+ 'password': 'lock',
177
+
178
+ // Location
179
+ 'address': 'map-pin',
180
+ 'street': 'map-pin',
181
+ 'city': 'building',
182
+ 'location': 'map-marker',
183
+ 'country': 'globe',
184
+
185
+ // Communication
186
+ 'message': 'message',
187
+ 'comment': 'comment',
188
+ 'feedback': 'comment',
189
+
190
+ // Web & Links
191
+ 'website': 'link',
192
+ 'url': 'link',
193
+ 'link': 'link',
194
+
195
+ // Time & Dates
196
+ 'date': 'calendar',
197
+ 'birthday': 'calendar',
198
+ 'time': 'clock',
199
+
200
+ // Categorization
201
+ 'subject': 'tag',
202
+ 'tag': 'tag',
203
+ 'category': 'folder',
204
+ 'priority': 'flag',
205
+
206
+ // Files
207
+ 'file': 'file',
208
+ 'upload': 'upload',
209
+ 'image': 'image',
210
+ 'photo': 'image',
211
+
212
+ // Search
213
+ 'search': 'search',
214
+ 'query': 'search'
215
+ };
216
+
217
+ // βœ… CORRECT: Auto-generate uiSchema with icons
218
+ const uiSchema = {
219
+ "/email": { 'ui:icon': 'envelope', 'ui:autocomplete': 'email' },
220
+ "/phone": { 'ui:icon': 'phone', 'ui:autocomplete': 'tel' },
221
+ "/name": { 'ui:icon': 'user', 'ui:autocomplete': 'name' },
222
+ "/password": { 'ui:icon': 'lock', 'ui:widget': 'password' },
223
+ "/website": { 'ui:icon': 'link' },
224
+ "/message": { 'ui:widget': 'textarea', 'ui:icon': 'message' }
225
+ };
226
+ ```
227
+
228
+ **Rule: When generating a contact/signup/profile form, infer and add appropriate icons automatically.**
229
+
230
+ ### 6. Submit Handler Pattern - ALWAYS provide working async handler
231
+
232
+ **When generating a pds-form, ALWAYS include a complete, iteration-ready submit handler with:**
233
+ - `pw:submit` event (NOT native submit)
234
+ - `btn-working` class for loading state
235
+ - `PDS.toast()` for user feedback
236
+ - Error handling
237
+ - Realistic async simulation
238
+
239
+ ```javascript
240
+ // βœ… CORRECT: Complete submit handler pattern
241
+ form.addEventListener('pw:submit', async (e) => {
242
+ const submitBtn = form.querySelector('button[type="submit"]');
243
+ submitBtn?.classList.add('btn-working');
244
+
245
+ try {
246
+ // Simulate async operation (replace with real API call)
247
+ await new Promise(resolve => setTimeout(resolve, 2000));
248
+
249
+ // Log the data for debugging
250
+ console.log('Submitted data:', e.detail.json);
251
+
252
+ // Show success toast
253
+ await PDS.toast('Form submitted successfully!', { type: 'success' });
254
+
255
+ // Optionally reset form
256
+ form.reset();
257
+ } catch (error) {
258
+ // Show error toast
259
+ await PDS.toast('Submission failed: ' + error.message, { type: 'error' });
260
+ } finally {
261
+ // Always remove loading state
262
+ submitBtn?.classList.remove('btn-working');
263
+ }
264
+ });
265
+
266
+ // ❌ WRONG: Native submit event
267
+ form.addEventListener('submit', (e) => { /* Won't work */ });
268
+
269
+ // ❌ WRONG: No loading state
270
+ form.addEventListener('pw:submit', async (e) => {
271
+ await fetch('/api'); // No visual feedback!
272
+ });
273
+
274
+ // ❌ WRONG: Browser dialogs
275
+ form.addEventListener('pw:submit', async (e) => {
276
+ alert('Submitted!'); // Use PDS.toast() instead
277
+ });
278
+ ```
279
+
280
+ **PDS.toast() is available globally via window.PDS:**
281
+
282
+ ```javascript
283
+ // All toast types
284
+ await PDS.toast('Success message', { type: 'success' });
285
+ await PDS.toast('Error occurred', { type: 'error' });
286
+ await PDS.toast('Warning message', { type: 'warning' });
287
+ await PDS.toast('Info message', { type: 'information' });
288
+
289
+ // Custom duration (auto-calculated by default based on message length)
290
+ await PDS.toast('Quick message', { type: 'info', duration: 3000 });
291
+
292
+ // Persistent (requires manual close)
293
+ await PDS.toast('Important notice', { type: 'warning', persistent: true });
294
+ ```
295
+
296
+ ### 7. Conditional "Other" Fields - Auto-generate ui:visibleWhen
297
+
298
+ **When a schema has an "Other" enum option, ALWAYS auto-generate a conditional text field:**
299
+
300
+ ```javascript
301
+ const schema = {
302
+ type: "object",
303
+ properties: {
304
+ reason: {
305
+ type: "string",
306
+ title: "How did you hear about us?",
307
+ oneOf: [
308
+ { const: "search", title: "Search Engine" },
309
+ { const: "social", title: "Social Media" },
310
+ { const: "friend", title: "Friend Referral" },
311
+ { const: "other", title: "Other... (please specify)" }, // ← "Other" option
312
+ ],
313
+ },
314
+ otherReason: { // ← Conditional field for "Other"
315
+ type: "string",
316
+ title: "Please specify",
317
+ examples: ["Tell us more..."],
318
+ },
319
+ },
320
+ };
321
+
322
+ const uiSchema = {
323
+ // βœ… ALWAYS add these when "other" enum exists
324
+ "/otherReason": {
325
+ "ui:visibleWhen": { "/reason": "other" },
326
+ "ui:requiredWhen": { "/reason": "other" },
327
+ },
328
+ };
329
+ ```
330
+
331
+ ### 8. Complete Working Example
332
+
333
+ ```javascript
334
+ // Schema with examples for placeholders
335
+ const contactSchema = {
336
+ type: "object",
337
+ required: ["name", "email", "message"],
338
+ properties: {
339
+ name: {
340
+ type: "string",
341
+ title: "Name",
342
+ minLength: 2,
343
+ examples: ["John Doe"]
344
+ },
345
+ email: {
346
+ type: "string",
347
+ format: "email",
348
+ title: "Email",
349
+ examples: ["user@example.com"]
350
+ },
351
+ message: {
352
+ type: "string",
353
+ title: "Message",
354
+ minLength: 10,
355
+ examples: ["Your message here..."]
356
+ }
357
+ }
358
+ };
359
+
360
+ // UI schema with smart icons
361
+ const uiSchema = {
362
+ "/name": { 'ui:icon': 'user' },
363
+ "/email": { 'ui:icon': 'envelope' },
364
+ "/message": {
365
+ 'ui:widget': 'textarea',
366
+ 'ui:rows': 4,
367
+ 'ui:icon': 'message'
368
+ }
369
+ };
370
+
371
+ // Setup with pw:submit and btn-working
372
+ const form = document.getElementById('contactForm');
373
+ form.jsonSchema = contactSchema;
374
+ form.uiSchema = uiSchema;
375
+
376
+ form.addEventListener('pw:submit', async (e) => {
377
+ const submitBtn = form.querySelector('button[type="submit"]');
378
+ submitBtn?.classList.add('btn-working');
379
+
380
+ try {
381
+ // Simulate 2s async operation
382
+ await new Promise(resolve => setTimeout(resolve, 2000));
383
+ console.log('Submitted:', e.detail.json);
384
+ await PDS.toast('Message sent!', { type: 'success' });
385
+ form.reset();
386
+ } catch (error) {
387
+ await PDS.toast('Failed to send', { type: 'error' });
388
+ } finally {
389
+ submitBtn?.classList.remove('btn-working');
390
+ }
391
+ });
392
+ ```
393
+
44
394
  ---
45
395
 
46
396
  ## 🚫 Critical Anti-Patterns (NEVER DO THIS)
@@ -61,7 +411,7 @@ PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browse
61
411
 
62
412
  ```javascript
63
413
  // ❌ NEVER: Browser dialogs - Use PDS.ask() and PDS.toast()
64
- alert("message"); // β†’ PDS.toast("message", { type: "info" })
414
+ alert("message"); // β†’ await PDS.toast("message", { type: "info" })
65
415
  confirm("sure?"); // β†’ await PDS.ask("sure?", { type: "confirm" })
66
416
  prompt("name?"); // β†’ await PDS.ask("name?", { type: "prompt" })
67
417
 
@@ -71,6 +421,23 @@ prompt("name?"); // β†’ await PDS.ask("name?", { type: "prompt" })
71
421
  // ❌ NEVER: Access lazy-loaded component APIs before they're defined
72
422
  const form = document.querySelector('pds-form');
73
423
  form.getFormData(); // May fail - component not loaded yet
424
+
425
+ // ❌ NEVER: Use native 'submit' event with pds-form
426
+ form.addEventListener('submit', (e) => { }); // β†’ Use 'pw:submit'
427
+
428
+ // ❌ NEVER: Forget btn-working class for async operations
429
+ button.onclick = async () => {
430
+ await fetch('/api'); // No loading indicator!
431
+ };
432
+ // β†’ Add button.classList.add('btn-working') before, remove after
433
+
434
+ // ❌ NEVER: Hardcode placeholders instead of using schema examples
435
+ const schema = {
436
+ properties: {
437
+ email: { type: "string" } // Missing examples!
438
+ }
439
+ };
440
+ // β†’ Add examples: ["user@example.com"]
74
441
  ```
75
442
 
76
443
  ---
@@ -173,7 +540,7 @@ const confirmed = await PDS.ask("Delete this item?", {
173
540
  buttons: { ok: { name: "Delete", variant: "danger" } }
174
541
  });
175
542
 
176
- PDS.toast("Saved successfully!", { type: "success" });
543
+ await PDS.toast("Saved successfully!", { type: "success" });
177
544
 
178
545
  // Theme management
179
546
  PDS.theme = 'dark'; // 'light' | 'dark' | 'system'
@@ -184,6 +551,20 @@ const results = await PDS.query("border gradient classes");
184
551
 
185
552
  ---
186
553
 
554
+ ## πŸ“š Additional Resources
555
+
556
+ **For comprehensive pds-form documentation:**
557
+ - Read [pds-form-docs.md](../pds-form-docs.md) for complete API reference
558
+ - See [packages/pds-storybook/stories/components/PdsForm.stories.js](../packages/pds-storybook/stories/components/PdsForm.stories.js) for real examples
559
+ - Check [custom-elements.json](../custom-elements.json) for component API details
560
+
561
+ **For toast notifications:**
562
+ - Use `PDS.toast()` method (see [src/js/common/toast.js](../src/js/common/toast.js) for implementation)
563
+ - Automatically ensures pds-toaster exists and is loaded before displaying
564
+ - See pds-toaster component API in [custom-elements.json](../custom-elements.json)
565
+
566
+ ---
567
+
187
568
  ## How to Look Things Up
188
569
 
189
570
  | Question | Action |
@@ -211,3 +592,11 @@ Before generating code:
211
592
  8. βœ… **Prefer primitives** β€” `.card`, `.badge`, `.alert` over custom components
212
593
  9. βœ… **Wait for lazy components** β€” Use `await customElements.whenDefined()` before accessing APIs
213
594
  10. βœ… **Include import map** β€” When using `pds-form` or `pds-drawer`, ensure `#pds/lit` is mapped
595
+
596
+ **For pds-form specifically:**
597
+
598
+ 11. βœ… **Use `pw:submit` event** β€” NOT native `submit` event
599
+ 12. βœ… **Add `btn-working` class** β€” For async submit operations, add during processing
600
+ 13. βœ… **Use `examples` in JSON schema** β€” First example becomes placeholder
601
+ 14. βœ… **Add smart icons** β€” Infer icons based on field names (emailβ†’envelope, phoneβ†’phone)
602
+ 15. βœ… **Wrap in `form[data-required]`** β€” For asterisk enhancement on required fields