@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.
- package/.cursorrules +250 -2
- package/.github/copilot-instructions.md +391 -2
- package/dist/types/pds.d.ts +109 -408
- package/dist/types/src/js/common/toast.d.ts +93 -0
- package/dist/types/src/js/common/toast.d.ts.map +1 -0
- package/dist/types/src/js/pds.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/js/pds.d.ts +62 -0
- package/src/js/pds.js +2 -0
|
@@ -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
|