@pure-ds/core 0.4.24 → 0.4.25
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 +245 -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
package/.cursorrules
CHANGED
|
@@ -41,6 +41,210 @@ 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. JSON Schema - Use `examples` for placeholders
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// ✅ CORRECT: Use examples array for placeholders
|
|
116
|
+
const schema = {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
name: {
|
|
120
|
+
type: "string",
|
|
121
|
+
title: "Full Name",
|
|
122
|
+
examples: ["John Doe"] // First example becomes placeholder
|
|
123
|
+
},
|
|
124
|
+
email: {
|
|
125
|
+
type: "string",
|
|
126
|
+
format: "email",
|
|
127
|
+
title: "Email",
|
|
128
|
+
examples: ["user@example.com"]
|
|
129
|
+
},
|
|
130
|
+
age: {
|
|
131
|
+
type: "integer",
|
|
132
|
+
title: "Age",
|
|
133
|
+
examples: [25] // Works for numbers too
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// ❌ WRONG: No placeholders or using wrong property
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 5. UI Schema - Infer smart icons automatically
|
|
142
|
+
|
|
143
|
+
**Common field → icon mappings (use these by default):**
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
const smartIcons = {
|
|
147
|
+
email: 'envelope',
|
|
148
|
+
phone: 'phone',
|
|
149
|
+
name: 'user',
|
|
150
|
+
password: 'lock',
|
|
151
|
+
search: 'search',
|
|
152
|
+
message: 'message',
|
|
153
|
+
comment: 'comment',
|
|
154
|
+
address: 'map-marker',
|
|
155
|
+
website: 'link',
|
|
156
|
+
date: 'calendar',
|
|
157
|
+
time: 'clock',
|
|
158
|
+
subject: 'tag',
|
|
159
|
+
priority: 'flag',
|
|
160
|
+
category: 'folder',
|
|
161
|
+
file: 'file',
|
|
162
|
+
image: 'image'
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const uiSchema = {
|
|
166
|
+
email: {
|
|
167
|
+
'ui:widget': 'email',
|
|
168
|
+
'ui:placeholder': 'user@example.com',
|
|
169
|
+
'ui:icon': 'envelope', // Auto-infer
|
|
170
|
+
'ui:autocomplete': 'email'
|
|
171
|
+
},
|
|
172
|
+
phone: {
|
|
173
|
+
'ui:widget': 'tel',
|
|
174
|
+
'ui:icon': 'phone',
|
|
175
|
+
'ui:autocomplete': 'tel'
|
|
176
|
+
},
|
|
177
|
+
message: {
|
|
178
|
+
'ui:widget': 'textarea',
|
|
179
|
+
'ui:rows': 4,
|
|
180
|
+
'ui:icon': 'message'
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 6. Complete Working Example
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
// Schema with examples for placeholders
|
|
189
|
+
const contactSchema = {
|
|
190
|
+
type: "object",
|
|
191
|
+
required: ["name", "email", "message"],
|
|
192
|
+
properties: {
|
|
193
|
+
name: {
|
|
194
|
+
type: "string",
|
|
195
|
+
title: "Name",
|
|
196
|
+
minLength: 2,
|
|
197
|
+
examples: ["John Doe"]
|
|
198
|
+
},
|
|
199
|
+
email: {
|
|
200
|
+
type: "string",
|
|
201
|
+
format: "email",
|
|
202
|
+
title: "Email",
|
|
203
|
+
examples: ["user@example.com"]
|
|
204
|
+
},
|
|
205
|
+
message: {
|
|
206
|
+
type: "string",
|
|
207
|
+
title: "Message",
|
|
208
|
+
minLength: 10,
|
|
209
|
+
examples: ["Your message here..."]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// UI schema with smart icons
|
|
215
|
+
const uiSchema = {
|
|
216
|
+
name: { 'ui:icon': 'user' },
|
|
217
|
+
email: { 'ui:icon': 'envelope' },
|
|
218
|
+
message: {
|
|
219
|
+
'ui:widget': 'textarea',
|
|
220
|
+
'ui:rows': 4,
|
|
221
|
+
'ui:icon': 'message'
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Setup with pw:submit and btn-working
|
|
226
|
+
const form = document.getElementById('contactForm');
|
|
227
|
+
form.jsonSchema = contactSchema;
|
|
228
|
+
form.uiSchema = uiSchema;
|
|
229
|
+
|
|
230
|
+
form.addEventListener('pw:submit', async (e) => {
|
|
231
|
+
const submitBtn = form.querySelector('button[type="submit"]');
|
|
232
|
+
submitBtn?.classList.add('btn-working');
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
// Simulate 2s async operation
|
|
236
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
237
|
+
console.log('Submitted:', e.detail.json);
|
|
238
|
+
await PDS.toast('Message sent!', { type: 'success' });
|
|
239
|
+
form.reset();
|
|
240
|
+
} catch (error) {
|
|
241
|
+
await PDS.toast('Failed to send', { type: 'error' });
|
|
242
|
+
} finally {
|
|
243
|
+
submitBtn?.classList.remove('btn-working');
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
44
248
|
---
|
|
45
249
|
|
|
46
250
|
## 🚫 Critical Anti-Patterns (NEVER DO THIS)
|
|
@@ -61,12 +265,33 @@ PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browse
|
|
|
61
265
|
|
|
62
266
|
```javascript
|
|
63
267
|
// ❌ NEVER: Browser dialogs - Use PDS.ask() and PDS.toast()
|
|
64
|
-
alert("message"); // → PDS.toast("message", { type: "info" })
|
|
268
|
+
alert("message"); // → await PDS.toast("message", { type: "info" })
|
|
65
269
|
confirm("sure?"); // → await PDS.ask("sure?", { type: "confirm" })
|
|
66
270
|
prompt("name?"); // → await PDS.ask("name?", { type: "prompt" })
|
|
67
271
|
|
|
68
272
|
// ❌ NEVER: Manual dropdown/modal/tab implementations
|
|
69
273
|
// → Use <nav data-dropdown>, PDS.ask(), <pds-tabstrip>
|
|
274
|
+
|
|
275
|
+
// ❌ NEVER: Access lazy-loaded component APIs before they're defined
|
|
276
|
+
const form = document.querySelector('pds-form');
|
|
277
|
+
form.getFormData(); // May fail - component not loaded yet
|
|
278
|
+
|
|
279
|
+
// ❌ NEVER: Use native 'submit' event with pds-form
|
|
280
|
+
form.addEventListener('submit', (e) => { }); // → Use 'pw:submit'
|
|
281
|
+
|
|
282
|
+
// ❌ NEVER: Forget btn-working class for async operations
|
|
283
|
+
button.onclick = async () => {
|
|
284
|
+
await fetch('/api'); // No loading indicator!
|
|
285
|
+
};
|
|
286
|
+
// → Add button.classList.add('btn-working') before, remove after
|
|
287
|
+
|
|
288
|
+
// ❌ NEVER: Hardcode placeholders instead of using schema examples
|
|
289
|
+
const schema = {
|
|
290
|
+
properties: {
|
|
291
|
+
email: { type: "string" } // Missing examples!
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
// → Add examples: ["user@example.com"]
|
|
70
295
|
```
|
|
71
296
|
|
|
72
297
|
---
|
|
@@ -130,7 +355,7 @@ const confirmed = await PDS.ask("Delete this item?", {
|
|
|
130
355
|
buttons: { ok: { name: "Delete", variant: "danger" } }
|
|
131
356
|
});
|
|
132
357
|
|
|
133
|
-
PDS.toast("Saved successfully!", { type: "success" });
|
|
358
|
+
await PDS.toast("Saved successfully!", { type: "success" });
|
|
134
359
|
|
|
135
360
|
// Theme management
|
|
136
361
|
PDS.theme = 'dark'; // 'light' | 'dark' | 'system'
|
|
@@ -141,6 +366,19 @@ const results = await PDS.query("border gradient classes");
|
|
|
141
366
|
|
|
142
367
|
---
|
|
143
368
|
|
|
369
|
+
## 📚 Additional Resources
|
|
370
|
+
|
|
371
|
+
**For comprehensive pds-form documentation:**
|
|
372
|
+
- Read [pds-form-docs.md](pds-form-docs.md) for complete API reference
|
|
373
|
+
- See [packages/pds-storybook/stories/components/PdsForm.stories.js](packages/pds-storybook/stories/components/PdsForm.stories.js) for real examples
|
|
374
|
+
- Check [custom-elements.json](custom-elements.json) for component API details
|
|
375
|
+
|
|
376
|
+
**For toast notifications:**
|
|
377
|
+
- Use `PDS.toast()` method (see [src/js/common/toast.js](src/js/common/toast.js) for implementation)
|
|
378
|
+
- Automatically ensures pds-toaster exists and is loaded before displaying
|
|
379
|
+
- See pds-toaster component API in [custom-elements.json](custom-elements.json)
|
|
380
|
+
---
|
|
381
|
+
|
|
144
382
|
## How to Look Things Up
|
|
145
383
|
|
|
146
384
|
| Question | Action |
|
|
@@ -166,3 +404,13 @@ Before generating code:
|
|
|
166
404
|
6. ✅ **Apply enhancements via data-* attributes** — See `pds-enhancer-metadata.js`
|
|
167
405
|
7. ✅ **Components as last resort** — Only when native HTML can't achieve it
|
|
168
406
|
8. ✅ **Prefer primitives** — `.card`, `.badge`, `.alert` over custom components
|
|
407
|
+
9. ✅ **Wait for lazy components** — Use `await customElements.whenDefined()` before accessing APIs
|
|
408
|
+
10. ✅ **Include import map** — When using `pds-form` or `pds-drawer`, ensure `#pds/lit` is mapped
|
|
409
|
+
|
|
410
|
+
**For pds-form specifically:**
|
|
411
|
+
|
|
412
|
+
11. ✅ **Use `pw:submit` event** — NOT native `submit` event
|
|
413
|
+
12. ✅ **Add `btn-working` class** — For async submit operations, add during processing
|
|
414
|
+
13. ✅ **Use `examples` in JSON schema** — First example becomes placeholder
|
|
415
|
+
14. ✅ **Add smart icons** — Infer icons based on field names (email→envelope, phone→phone)
|
|
416
|
+
15. ✅ **Wrap in `form[data-required]`** — For asterisk enhancement on required fields
|
|
@@ -41,6 +41,210 @@ 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. JSON Schema - Use `examples` for placeholders
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// ✅ CORRECT: Use examples array for placeholders
|
|
116
|
+
const schema = {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
name: {
|
|
120
|
+
type: "string",
|
|
121
|
+
title: "Full Name",
|
|
122
|
+
examples: ["John Doe"] // First example becomes placeholder
|
|
123
|
+
},
|
|
124
|
+
email: {
|
|
125
|
+
type: "string",
|
|
126
|
+
format: "email",
|
|
127
|
+
title: "Email",
|
|
128
|
+
examples: ["user@example.com"]
|
|
129
|
+
},
|
|
130
|
+
age: {
|
|
131
|
+
type: "integer",
|
|
132
|
+
title: "Age",
|
|
133
|
+
examples: [25] // Works for numbers too
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// ❌ WRONG: No placeholders or using wrong property
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 5. UI Schema - Infer smart icons automatically
|
|
142
|
+
|
|
143
|
+
**Common field → icon mappings (use these by default):**
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
const smartIcons = {
|
|
147
|
+
email: 'envelope',
|
|
148
|
+
phone: 'phone',
|
|
149
|
+
name: 'user',
|
|
150
|
+
password: 'lock',
|
|
151
|
+
search: 'search',
|
|
152
|
+
message: 'message',
|
|
153
|
+
comment: 'comment',
|
|
154
|
+
address: 'map-marker',
|
|
155
|
+
website: 'link',
|
|
156
|
+
date: 'calendar',
|
|
157
|
+
time: 'clock',
|
|
158
|
+
subject: 'tag',
|
|
159
|
+
priority: 'flag',
|
|
160
|
+
category: 'folder',
|
|
161
|
+
file: 'file',
|
|
162
|
+
image: 'image'
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const uiSchema = {
|
|
166
|
+
email: {
|
|
167
|
+
'ui:widget': 'email',
|
|
168
|
+
'ui:placeholder': 'user@example.com',
|
|
169
|
+
'ui:icon': 'envelope', // Auto-infer
|
|
170
|
+
'ui:autocomplete': 'email'
|
|
171
|
+
},
|
|
172
|
+
phone: {
|
|
173
|
+
'ui:widget': 'tel',
|
|
174
|
+
'ui:icon': 'phone',
|
|
175
|
+
'ui:autocomplete': 'tel'
|
|
176
|
+
},
|
|
177
|
+
message: {
|
|
178
|
+
'ui:widget': 'textarea',
|
|
179
|
+
'ui:rows': 4,
|
|
180
|
+
'ui:icon': 'message'
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 6. Complete Working Example
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
// Schema with examples for placeholders
|
|
189
|
+
const contactSchema = {
|
|
190
|
+
type: "object",
|
|
191
|
+
required: ["name", "email", "message"],
|
|
192
|
+
properties: {
|
|
193
|
+
name: {
|
|
194
|
+
type: "string",
|
|
195
|
+
title: "Name",
|
|
196
|
+
minLength: 2,
|
|
197
|
+
examples: ["John Doe"]
|
|
198
|
+
},
|
|
199
|
+
email: {
|
|
200
|
+
type: "string",
|
|
201
|
+
format: "email",
|
|
202
|
+
title: "Email",
|
|
203
|
+
examples: ["user@example.com"]
|
|
204
|
+
},
|
|
205
|
+
message: {
|
|
206
|
+
type: "string",
|
|
207
|
+
title: "Message",
|
|
208
|
+
minLength: 10,
|
|
209
|
+
examples: ["Your message here..."]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// UI schema with smart icons
|
|
215
|
+
const uiSchema = {
|
|
216
|
+
name: { 'ui:icon': 'user' },
|
|
217
|
+
email: { 'ui:icon': 'envelope' },
|
|
218
|
+
message: {
|
|
219
|
+
'ui:widget': 'textarea',
|
|
220
|
+
'ui:rows': 4,
|
|
221
|
+
'ui:icon': 'message'
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Setup with pw:submit and btn-working
|
|
226
|
+
const form = document.getElementById('contactForm');
|
|
227
|
+
form.jsonSchema = contactSchema;
|
|
228
|
+
form.uiSchema = uiSchema;
|
|
229
|
+
|
|
230
|
+
form.addEventListener('pw:submit', async (e) => {
|
|
231
|
+
const submitBtn = form.querySelector('button[type="submit"]');
|
|
232
|
+
submitBtn?.classList.add('btn-working');
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
// Simulate 2s async operation
|
|
236
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
237
|
+
console.log('Submitted:', e.detail.json);
|
|
238
|
+
await PDS.toast('Message sent!', { type: 'success' });
|
|
239
|
+
form.reset();
|
|
240
|
+
} catch (error) {
|
|
241
|
+
await PDS.toast('Failed to send', { type: 'error' });
|
|
242
|
+
} finally {
|
|
243
|
+
submitBtn?.classList.remove('btn-working');
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
44
248
|
---
|
|
45
249
|
|
|
46
250
|
## 🚫 Critical Anti-Patterns (NEVER DO THIS)
|
|
@@ -61,7 +265,7 @@ PDS follows the [Pure Web Manifesto](https://pureweb.dev/manifesto): "The browse
|
|
|
61
265
|
|
|
62
266
|
```javascript
|
|
63
267
|
// ❌ NEVER: Browser dialogs - Use PDS.ask() and PDS.toast()
|
|
64
|
-
alert("message"); // → PDS.toast("message", { type: "info" })
|
|
268
|
+
alert("message"); // → await PDS.toast("message", { type: "info" })
|
|
65
269
|
confirm("sure?"); // → await PDS.ask("sure?", { type: "confirm" })
|
|
66
270
|
prompt("name?"); // → await PDS.ask("name?", { type: "prompt" })
|
|
67
271
|
|
|
@@ -71,6 +275,23 @@ prompt("name?"); // → await PDS.ask("name?", { type: "prompt" })
|
|
|
71
275
|
// ❌ NEVER: Access lazy-loaded component APIs before they're defined
|
|
72
276
|
const form = document.querySelector('pds-form');
|
|
73
277
|
form.getFormData(); // May fail - component not loaded yet
|
|
278
|
+
|
|
279
|
+
// ❌ NEVER: Use native 'submit' event with pds-form
|
|
280
|
+
form.addEventListener('submit', (e) => { }); // → Use 'pw:submit'
|
|
281
|
+
|
|
282
|
+
// ❌ NEVER: Forget btn-working class for async operations
|
|
283
|
+
button.onclick = async () => {
|
|
284
|
+
await fetch('/api'); // No loading indicator!
|
|
285
|
+
};
|
|
286
|
+
// → Add button.classList.add('btn-working') before, remove after
|
|
287
|
+
|
|
288
|
+
// ❌ NEVER: Hardcode placeholders instead of using schema examples
|
|
289
|
+
const schema = {
|
|
290
|
+
properties: {
|
|
291
|
+
email: { type: "string" } // Missing examples!
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
// → Add examples: ["user@example.com"]
|
|
74
295
|
```
|
|
75
296
|
|
|
76
297
|
---
|
|
@@ -173,7 +394,7 @@ const confirmed = await PDS.ask("Delete this item?", {
|
|
|
173
394
|
buttons: { ok: { name: "Delete", variant: "danger" } }
|
|
174
395
|
});
|
|
175
396
|
|
|
176
|
-
PDS.toast("Saved successfully!", { type: "success" });
|
|
397
|
+
await PDS.toast("Saved successfully!", { type: "success" });
|
|
177
398
|
|
|
178
399
|
// Theme management
|
|
179
400
|
PDS.theme = 'dark'; // 'light' | 'dark' | 'system'
|
|
@@ -184,6 +405,20 @@ const results = await PDS.query("border gradient classes");
|
|
|
184
405
|
|
|
185
406
|
---
|
|
186
407
|
|
|
408
|
+
## 📚 Additional Resources
|
|
409
|
+
|
|
410
|
+
**For comprehensive pds-form documentation:**
|
|
411
|
+
- Read [pds-form-docs.md](../pds-form-docs.md) for complete API reference
|
|
412
|
+
- See [packages/pds-storybook/stories/components/PdsForm.stories.js](../packages/pds-storybook/stories/components/PdsForm.stories.js) for real examples
|
|
413
|
+
- Check [custom-elements.json](../custom-elements.json) for component API details
|
|
414
|
+
|
|
415
|
+
**For toast notifications:**
|
|
416
|
+
- Use `PDS.toast()` method (see [src/js/common/toast.js](../src/js/common/toast.js) for implementation)
|
|
417
|
+
- Automatically ensures pds-toaster exists and is loaded before displaying
|
|
418
|
+
- See pds-toaster component API in [custom-elements.json](../custom-elements.json)
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
187
422
|
## How to Look Things Up
|
|
188
423
|
|
|
189
424
|
| Question | Action |
|
|
@@ -211,3 +446,11 @@ Before generating code:
|
|
|
211
446
|
8. ✅ **Prefer primitives** — `.card`, `.badge`, `.alert` over custom components
|
|
212
447
|
9. ✅ **Wait for lazy components** — Use `await customElements.whenDefined()` before accessing APIs
|
|
213
448
|
10. ✅ **Include import map** — When using `pds-form` or `pds-drawer`, ensure `#pds/lit` is mapped
|
|
449
|
+
|
|
450
|
+
**For pds-form specifically:**
|
|
451
|
+
|
|
452
|
+
11. ✅ **Use `pw:submit` event** — NOT native `submit` event
|
|
453
|
+
12. ✅ **Add `btn-working` class** — For async submit operations, add during processing
|
|
454
|
+
13. ✅ **Use `examples` in JSON schema** — First example becomes placeholder
|
|
455
|
+
14. ✅ **Add smart icons** — Infer icons based on field names (email→envelope, phone→phone)
|
|
456
|
+
15. ✅ **Wrap in `form[data-required]`** — For asterisk enhancement on required fields
|
package/dist/types/pds.d.ts
CHANGED
|
@@ -1,408 +1,109 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
* const g = new PDS.Generator({ example: true });
|
|
111
|
-
* // tokens may be returned synchronously or via a Promise
|
|
112
|
-
* const tokens = await g.generateTokens?.();
|
|
113
|
-
* const css = await g.generateCSS?.();
|
|
114
|
-
*/
|
|
115
|
-
export class Generator {
|
|
116
|
-
constructor(options?: any);
|
|
117
|
-
/** runtime options / config passed to the generator */
|
|
118
|
-
options: any;
|
|
119
|
-
/** generated token object */
|
|
120
|
-
tokens: Record<string, any>;
|
|
121
|
-
/** concatenated CSS (layered) */
|
|
122
|
-
css: string;
|
|
123
|
-
/** Complete compiled representation of the design system state */
|
|
124
|
-
readonly compiled: CompiledState;
|
|
125
|
-
/** Generate the token map (may be async in some implementations) */
|
|
126
|
-
generateTokens?(): Record<string, any> | Promise<Record<string, any>>;
|
|
127
|
-
/** Generate the CSS string (may be async) */
|
|
128
|
-
generateCSS?(): string | Promise<string>;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** Public runtime surface exported as `PDS` */
|
|
132
|
-
export interface PDSEventMap {
|
|
133
|
-
'pds:ready': CustomEvent<{ mode: 'live' | 'static'; generator?: Generator; config: any; theme: string; autoDefiner?: any }>;
|
|
134
|
-
'pds:error': CustomEvent<{ error: any }>;
|
|
135
|
-
'pds:theme:changed': CustomEvent<{ theme: string; requested?: string; source: 'system' | 'programmatic' }>;
|
|
136
|
-
'pds:design:updated': CustomEvent<{ config: any; designer?: any }>;
|
|
137
|
-
'pds:design:field:changed': CustomEvent<{ field: string; config: any }>;
|
|
138
|
-
'pds:inspector:mode:changed': CustomEvent<{ active: boolean }>;
|
|
139
|
-
'pds:inspector:deactivate': CustomEvent<{}>;
|
|
140
|
-
'pds:docs:view': CustomEvent<{ file: string }>;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export class PDS extends EventTarget {
|
|
144
|
-
// Static surface
|
|
145
|
-
static Generator: typeof Generator;
|
|
146
|
-
static registry: PDSRegistry;
|
|
147
|
-
static presets: Record<string, any>;
|
|
148
|
-
static ontology: any;
|
|
149
|
-
static adoptLayers: (shadowRoot: ShadowRoot, layers?: string[], additionalSheets?: CSSStyleSheet[]) => Promise<void>;
|
|
150
|
-
static adoptPrimitives: (shadowRoot: ShadowRoot, additionalSheets?: CSSStyleSheet[]) => Promise<void>;
|
|
151
|
-
static createStylesheet: (css: string) => CSSStyleSheet;
|
|
152
|
-
static isLiveMode: () => boolean;
|
|
153
|
-
static findComponentForElement: (el: Element) => ComponentDef | null;
|
|
154
|
-
static validateDesign: (designConfig: any, options?: { minContrast?: number }) => { ok: boolean; issues: Array<{ path: string; message: string; ratio: number; min: number; context?: string }> };
|
|
155
|
-
static validateDesigns: (designs: Array<any> | Record<string, any>, options?: { minContrast?: number }) => { ok: boolean; results: Array<{ name?: string; ok: boolean; issues: Array<{ path: string; message: string; ratio: number; min: number; context?: string }> }> };
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Current configuration after PDS.start() completes - read-only, frozen after initialization.
|
|
159
|
-
* Contains the complete configuration used to initialize PDS, including mode, design, preset, and theme.
|
|
160
|
-
*/
|
|
161
|
-
static readonly currentConfig: any;
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Compiled design system state - provides structured access to all generated tokens,
|
|
165
|
-
* layers, and metadata. Available in live mode when a generator is active.
|
|
166
|
-
* Returns null if not in live mode or if no generator is present.
|
|
167
|
-
*/
|
|
168
|
-
static readonly compiled: CompiledState | null;
|
|
169
|
-
|
|
170
|
-
// Static EventTarget-like facade for default instance
|
|
171
|
-
static addEventListener<K extends keyof PDSEventMap>(type: K, listener: (ev: PDSEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
172
|
-
static removeEventListener<K extends keyof PDSEventMap>(type: K, listener: (ev: PDSEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
173
|
-
static dispatchEvent(event: PDSEventMap[keyof PDSEventMap] | Event): boolean;
|
|
174
|
-
|
|
175
|
-
// Static entry point
|
|
176
|
-
static start(config?: {
|
|
177
|
-
mode?: 'live' | 'static';
|
|
178
|
-
preset?: string;
|
|
179
|
-
design?: any;
|
|
180
|
-
autoDefine?: {
|
|
181
|
-
baseURL?: string;
|
|
182
|
-
predefine?: string[];
|
|
183
|
-
mapper?: (tag: string) => string | undefined | null | false;
|
|
184
|
-
scanExisting?: boolean;
|
|
185
|
-
observeShadows?: boolean;
|
|
186
|
-
patchAttachShadow?: boolean;
|
|
187
|
-
debounceMs?: number;
|
|
188
|
-
onError?: (tag: string, err: any) => void;
|
|
189
|
-
};
|
|
190
|
-
applyGlobalStyles?: boolean;
|
|
191
|
-
manageTheme?: boolean;
|
|
192
|
-
themeStorageKey?: string;
|
|
193
|
-
public?: {
|
|
194
|
-
root?: string;
|
|
195
|
-
};
|
|
196
|
-
// live-only
|
|
197
|
-
preloadStyles?: boolean;
|
|
198
|
-
criticalLayers?: string[];
|
|
199
|
-
// static-only
|
|
200
|
-
staticPaths?: Record<string, string>;
|
|
201
|
-
enhancers?: Array<any>;
|
|
202
|
-
}): Promise<PDS>;
|
|
203
|
-
|
|
204
|
-
constructor();
|
|
205
|
-
|
|
206
|
-
// Instance state
|
|
207
|
-
readonly mode: 'live' | 'static';
|
|
208
|
-
readonly generator?: Generator;
|
|
209
|
-
readonly config: any;
|
|
210
|
-
readonly theme: string;
|
|
211
|
-
readonly autoDefiner?: any;
|
|
212
|
-
|
|
213
|
-
// Instance helpers
|
|
214
|
-
setTheme(theme: 'light' | 'dark' | 'system', options?: { storageKey?: string; persist?: boolean }): Promise<string>;
|
|
215
|
-
preloadCritical(config: any, options?: { theme?: string; layers?: string[] }): void;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export { PDS as default };
|
|
219
|
-
|
|
220
|
-
// ===== pds-form Types =====
|
|
221
|
-
|
|
222
|
-
export interface JsonFormOptions {
|
|
223
|
-
widgets?: {
|
|
224
|
-
booleans?: 'toggle' | 'checkbox';
|
|
225
|
-
numbers?: 'input' | 'range';
|
|
226
|
-
selects?: 'standard' | 'dropdown';
|
|
227
|
-
};
|
|
228
|
-
layouts?: {
|
|
229
|
-
fieldsets?: 'default' | 'flex' | 'grid' | 'accordion' | 'tabs' | 'card';
|
|
230
|
-
arrays?: 'default' | 'compact';
|
|
231
|
-
};
|
|
232
|
-
enhancements?: {
|
|
233
|
-
icons?: boolean;
|
|
234
|
-
datalists?: boolean;
|
|
235
|
-
rangeOutput?: boolean;
|
|
236
|
-
};
|
|
237
|
-
validation?: {
|
|
238
|
-
showErrors?: boolean;
|
|
239
|
-
validateOnChange?: boolean;
|
|
240
|
-
};
|
|
241
|
-
// Path-specific options (JSON Pointer paths)
|
|
242
|
-
[path: string]: any;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export interface UISchema {
|
|
246
|
-
// Field-level UI customization (JSON Pointer paths as keys)
|
|
247
|
-
[path: string]: {
|
|
248
|
-
'ui:widget'?: string;
|
|
249
|
-
'ui:layout'?: 'default' | 'flex' | 'grid' | 'accordion' | 'tabs';
|
|
250
|
-
'ui:columns'?: number | 'auto';
|
|
251
|
-
'ui:autoSize'?: 'sm' | 'md' | 'lg' | 'xl';
|
|
252
|
-
'ui:gap'?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
253
|
-
'ui:wrap'?: boolean;
|
|
254
|
-
'ui:direction'?: 'row' | 'column';
|
|
255
|
-
'ui:surface'?: 'card' | 'elevated' | 'dialog';
|
|
256
|
-
'ui:accordion'?: boolean;
|
|
257
|
-
'ui:tabs'?: boolean;
|
|
258
|
-
'ui:defaultOpen'?: number[];
|
|
259
|
-
'ui:dialog'?: boolean;
|
|
260
|
-
'ui:dialogButton'?: string;
|
|
261
|
-
'ui:dialogSize'?: 'sm' | 'lg' | 'xl' | 'full';
|
|
262
|
-
'ui:submitLabel'?: string;
|
|
263
|
-
'ui:cancelLabel'?: string;
|
|
264
|
-
'ui:help'?: string;
|
|
265
|
-
'ui:placeholder'?: string;
|
|
266
|
-
'ui:icon'?: string;
|
|
267
|
-
'ui:iconPosition'?: 'start' | 'end';
|
|
268
|
-
'ui:datalist'?: string[];
|
|
269
|
-
'ui:rows'?: number;
|
|
270
|
-
'ui:min'?: number;
|
|
271
|
-
'ui:max'?: number;
|
|
272
|
-
'ui:dropdown'?: boolean;
|
|
273
|
-
'ui:autocomplete'?: string;
|
|
274
|
-
'ui:accept'?: string;
|
|
275
|
-
'ui:multiple'?: boolean;
|
|
276
|
-
'ui:maxFiles'?: number;
|
|
277
|
-
'ui:maxSize'?: number;
|
|
278
|
-
'ui:submitOnEnter'?: boolean;
|
|
279
|
-
'ui:spellcheck'?: boolean;
|
|
280
|
-
'ui:order'?: string[];
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export interface JsonFormEvent<T = any> extends CustomEvent {
|
|
285
|
-
detail: T;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export interface JsonFormSubmitDetail {
|
|
289
|
-
json: any;
|
|
290
|
-
formData: FormData;
|
|
291
|
-
valid: boolean;
|
|
292
|
-
issues: Array<{ path?: string; message: string }>;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export interface JsonFormValueChangeDetail {
|
|
296
|
-
name: string;
|
|
297
|
-
value: any;
|
|
298
|
-
validity: { valid: boolean };
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
export interface JsonFormArrayEventDetail {
|
|
302
|
-
path: string;
|
|
303
|
-
index?: number;
|
|
304
|
-
from?: number;
|
|
305
|
-
to?: number;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
export interface JsonFormDialogEventDetail {
|
|
309
|
-
path: string;
|
|
310
|
-
schema?: any;
|
|
311
|
-
value?: any;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
export interface JsonFormRendererContext {
|
|
315
|
-
id: string;
|
|
316
|
-
path: string;
|
|
317
|
-
label: string;
|
|
318
|
-
value: any;
|
|
319
|
-
required: boolean;
|
|
320
|
-
ui: any;
|
|
321
|
-
schema: any;
|
|
322
|
-
get: (path?: string) => any;
|
|
323
|
-
set: (value: any, path?: string) => void;
|
|
324
|
-
attrs: {
|
|
325
|
-
placeholder?: string;
|
|
326
|
-
minLength?: number;
|
|
327
|
-
maxLength?: number;
|
|
328
|
-
min?: number;
|
|
329
|
-
max?: number;
|
|
330
|
-
step?: number;
|
|
331
|
-
pattern?: string;
|
|
332
|
-
readOnly?: boolean;
|
|
333
|
-
required?: boolean;
|
|
334
|
-
autocomplete?: string;
|
|
335
|
-
list?: string;
|
|
336
|
-
};
|
|
337
|
-
host: any;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
export type JsonFormRenderer = (context: JsonFormRendererContext) => any;
|
|
341
|
-
|
|
342
|
-
export interface JsonFormEventMap {
|
|
343
|
-
'pw:schema-resolve': JsonFormEvent<{ schema: any }>;
|
|
344
|
-
'pw:compile-node': JsonFormEvent<{ path: string; schema: any; ui: any; node?: any }>;
|
|
345
|
-
'pw:choose-widget': JsonFormEvent<{ path: string; schema: any; ui: any; widget: string | null }>;
|
|
346
|
-
'pw:before-render-field': JsonFormEvent<{ path: string; schema: any; ui: any; mount?: any; render?: () => any }>;
|
|
347
|
-
'pw:render-field': JsonFormEvent<{ path: string; schema: any; node: any }>;
|
|
348
|
-
'pw:after-render-field': JsonFormEvent<{ path: string; schema: any }>;
|
|
349
|
-
'pw:value-change': JsonFormEvent<JsonFormValueChangeDetail>;
|
|
350
|
-
'pw:array-add': JsonFormEvent<JsonFormArrayEventDetail>;
|
|
351
|
-
'pw:array-remove': JsonFormEvent<JsonFormArrayEventDetail>;
|
|
352
|
-
'pw:array-reorder': JsonFormEvent<JsonFormArrayEventDetail>;
|
|
353
|
-
'pw:serialize': JsonFormEvent<JsonFormSubmitDetail>;
|
|
354
|
-
'pw:submit': JsonFormEvent<JsonFormSubmitDetail>;
|
|
355
|
-
'pw:dialog-open': JsonFormEvent<JsonFormDialogEventDetail>;
|
|
356
|
-
'pw:dialog-submit': JsonFormEvent<JsonFormDialogEventDetail>;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
declare global {
|
|
360
|
-
interface HTMLElementTagNameMap {
|
|
361
|
-
'pds-form': SchemaForm;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
export class SchemaForm extends HTMLElement {
|
|
366
|
-
// Properties
|
|
367
|
-
jsonSchema: any;
|
|
368
|
-
uiSchema: UISchema;
|
|
369
|
-
options: JsonFormOptions;
|
|
370
|
-
values: any;
|
|
371
|
-
action?: string;
|
|
372
|
-
method: 'get' | 'post' | 'dialog';
|
|
373
|
-
disabled: boolean;
|
|
374
|
-
hideActions: boolean;
|
|
375
|
-
submitLabel: string;
|
|
376
|
-
resetLabel: string;
|
|
377
|
-
hideReset: boolean;
|
|
378
|
-
hideSubmit: boolean;
|
|
379
|
-
|
|
380
|
-
// Methods
|
|
381
|
-
defineRenderer(widgetKey: string, renderer: JsonFormRenderer): void;
|
|
382
|
-
useValidator(validator: (data: any, schema: any) => Promise<{ valid: boolean; errors?: Array<{ path?: string; message: string }> }>): void;
|
|
383
|
-
getValuesFlat(): Record<string, any>;
|
|
384
|
-
serialize(): { json: any; formData: FormData };
|
|
385
|
-
submit(): Promise<JsonFormSubmitDetail>;
|
|
386
|
-
|
|
387
|
-
// Event listeners
|
|
388
|
-
addEventListener<K extends keyof JsonFormEventMap>(
|
|
389
|
-
type: K,
|
|
390
|
-
listener: (ev: JsonFormEventMap[K]) => any,
|
|
391
|
-
options?: boolean | AddEventListenerOptions
|
|
392
|
-
): void;
|
|
393
|
-
addEventListener(
|
|
394
|
-
type: string,
|
|
395
|
-
listener: EventListenerOrEventListenerObject,
|
|
396
|
-
options?: boolean | AddEventListenerOptions
|
|
397
|
-
): void;
|
|
398
|
-
removeEventListener<K extends keyof JsonFormEventMap>(
|
|
399
|
-
type: K,
|
|
400
|
-
listener: (ev: JsonFormEventMap[K]) => any,
|
|
401
|
-
options?: boolean | EventListenerOptions
|
|
402
|
-
): void;
|
|
403
|
-
removeEventListener(
|
|
404
|
-
type: string,
|
|
405
|
-
listener: EventListenerOrEventListenerObject,
|
|
406
|
-
options?: boolean | EventListenerOptions
|
|
407
|
-
): void;
|
|
408
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Public PDS runtime object exported to consumers.
|
|
3
|
+
*
|
|
4
|
+
* This object exposes the core runtime building blocks for the Pure Design System.
|
|
5
|
+
* It intentionally provides a small, stable surface area so consuming apps can:
|
|
6
|
+
* - programmatically generate design system artifacts (via `Generator`),
|
|
7
|
+
* - adopt styles into Shadow DOM (via `adoptLayers` / `adoptPrimitives`),
|
|
8
|
+
* - query runtime mode and obtain constructable stylesheets (via `registry`).
|
|
9
|
+
*
|
|
10
|
+
* Common events in the PDS ecosystem (emitted by other packages/components):
|
|
11
|
+
* - `design-updated` — emitted by the designer component when the in-memory design changes (detail: { config }).
|
|
12
|
+
* - `pds-generated` — emitted by the PDS configurator when generation completes (detail: { modules, meta }).
|
|
13
|
+
* - `pds-error` — emitted by the PDS configurator when generation fails (detail: Error).
|
|
14
|
+
*
|
|
15
|
+
* Error handling notes:
|
|
16
|
+
* - Methods that perform dynamic imports (e.g. `adoptLayers` via the registry) may log and return fallbacks
|
|
17
|
+
* rather than throwing; consumers should check return values (e.g. null) and listen for `pds-error` in UI flows.
|
|
18
|
+
* - `createStylesheet(css)` will throw synchronously if the provided CSS cannot be parsed by the browser's
|
|
19
|
+
* `CSSStyleSheet.replaceSync()` — callers should guard invalid input or wrap calls in try/catch.
|
|
20
|
+
*/
|
|
21
|
+
export type PDSAPI = {
|
|
22
|
+
/**
|
|
23
|
+
* - Generator class to produce design system assets
|
|
24
|
+
*/
|
|
25
|
+
Generator: typeof import("./pds-core/pds-generator.js").Generator;
|
|
26
|
+
/**
|
|
27
|
+
* - Singleton runtime registry for live/static mode
|
|
28
|
+
*/
|
|
29
|
+
registry: import("./pds-core/pds-registry.js").PDSRegistry;
|
|
30
|
+
/**
|
|
31
|
+
* - Ontology helpers and metadata for components
|
|
32
|
+
*/
|
|
33
|
+
ontology: any;
|
|
34
|
+
/**
|
|
35
|
+
* - Adopt multiple layers into a ShadowRoot. May log errors and fallback to additionalSheets when static imports fail.
|
|
36
|
+
*/
|
|
37
|
+
adoptLayers: (shadowRoot: ShadowRoot, layers?: string[], additionalSheets?: CSSStyleSheet[]) => Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* - Adopt primitives layer into a ShadowRoot. Designed as a convenience for components.
|
|
40
|
+
*/
|
|
41
|
+
adoptPrimitives: (shadowRoot: ShadowRoot, additionalSheets?: CSSStyleSheet[]) => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* - Create a constructable stylesheet from CSS text.
|
|
44
|
+
*/
|
|
45
|
+
createStylesheet: (css: string) => CSSStyleSheet;
|
|
46
|
+
};
|
|
47
|
+
/** @type {PDSAPI & PDSBase} */
|
|
48
|
+
export const PDS: PDSAPI & PDSBase;
|
|
49
|
+
/**
|
|
50
|
+
* Validate a design configuration for accessibility sanity checks.
|
|
51
|
+
* Currently validates color contrast for primary buttons and base surface text
|
|
52
|
+
* in both light and dark themes.
|
|
53
|
+
*
|
|
54
|
+
* @param {object} designConfig - A full or partial PDS config object
|
|
55
|
+
* @param {object} [options]
|
|
56
|
+
* @param {number} [options.minContrast=4.5] - Minimum contrast ratio for normal text
|
|
57
|
+
* @returns {{ ok: boolean, issues: Array<{path:string, message:string, ratio:number, min:number, context?:string}> }}
|
|
58
|
+
*/
|
|
59
|
+
export function validateDesign(designConfig?: object, options?: {
|
|
60
|
+
minContrast?: number;
|
|
61
|
+
}): {
|
|
62
|
+
ok: boolean;
|
|
63
|
+
issues: Array<{
|
|
64
|
+
path: string;
|
|
65
|
+
message: string;
|
|
66
|
+
ratio: number;
|
|
67
|
+
min: number;
|
|
68
|
+
context?: string;
|
|
69
|
+
}>;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Public PDS runtime object exported to consumers.
|
|
73
|
+
*
|
|
74
|
+
* This object exposes the core runtime building blocks for the Pure Design System.
|
|
75
|
+
* It intentionally provides a small, stable surface area so consuming apps can:
|
|
76
|
+
* - programmatically generate design system artifacts (via `Generator`),
|
|
77
|
+
* - adopt styles into Shadow DOM (via `adoptLayers` / `adoptPrimitives`),
|
|
78
|
+
* - query runtime mode and obtain constructable stylesheets (via `registry`).
|
|
79
|
+
*
|
|
80
|
+
* Common events in the PDS ecosystem (emitted by other packages/components):
|
|
81
|
+
* - `design-updated` — emitted by the designer component when the in-memory design changes (detail: { config }).
|
|
82
|
+
* - `pds-generated` — emitted by the PDS configurator when generation completes (detail: { modules, meta }).
|
|
83
|
+
* - `pds-error` — emitted by the PDS configurator when generation fails (detail: Error).
|
|
84
|
+
*
|
|
85
|
+
* Error handling notes:
|
|
86
|
+
* - Methods that perform dynamic imports (e.g. `adoptLayers` via the registry) may log and return fallbacks
|
|
87
|
+
* rather than throwing; consumers should check return values (e.g. null) and listen for `pds-error` in UI flows.
|
|
88
|
+
* - `createStylesheet(css)` will throw synchronously if the provided CSS cannot be parsed by the browser's
|
|
89
|
+
* `CSSStyleSheet.replaceSync()` — callers should guard invalid input or wrap calls in try/catch.
|
|
90
|
+
*
|
|
91
|
+
* @typedef {Object} PDSAPI
|
|
92
|
+
* @property {typeof import("./pds-core/pds-generator.js").Generator} Generator - Generator class to produce design system assets
|
|
93
|
+
* @property {import("./pds-core/pds-registry.js").PDSRegistry} registry - Singleton runtime registry for live/static mode
|
|
94
|
+
* @property {any} ontology - Ontology helpers and metadata for components
|
|
95
|
+
* @property {(shadowRoot: ShadowRoot, layers?: string[], additionalSheets?: CSSStyleSheet[]) => Promise<void>} adoptLayers - Adopt multiple layers into a ShadowRoot. May log errors and fallback to additionalSheets when static imports fail.
|
|
96
|
+
* @property {(shadowRoot: ShadowRoot, additionalSheets?: CSSStyleSheet[]) => Promise<void>} adoptPrimitives - Adopt primitives layer into a ShadowRoot. Designed as a convenience for components.
|
|
97
|
+
* @property {(css:string) => CSSStyleSheet} createStylesheet - Create a constructable stylesheet from CSS text. @throws {DOMException} on invalid CSS in some browsers.
|
|
98
|
+
* @property {() => boolean} isLiveMode - Returns true when running in live/designer-backed mode
|
|
99
|
+
* @property {(el: Element) => import("./pds-core/pds-ontology.js").ComponentDef | null} findComponentForElement - Helper to find a component definition for a DOM element
|
|
100
|
+
*/
|
|
101
|
+
/**
|
|
102
|
+
* Workspace for the Pure Design System runtime API
|
|
103
|
+
* PDS is now an EventTarget so consumers can subscribe to a single, consistent
|
|
104
|
+
* event bus instead of listening on window/document or individual elements.
|
|
105
|
+
*/
|
|
106
|
+
declare class PDSBase extends EventTarget {
|
|
107
|
+
}
|
|
108
|
+
export {};
|
|
109
|
+
//# sourceMappingURL=pds.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display a toast notification
|
|
3
|
+
*
|
|
4
|
+
* This method automatically ensures the pds-toaster component exists and is loaded
|
|
5
|
+
* before displaying the notification. The toaster element is appended to document.body
|
|
6
|
+
* if not already present.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} message - The message to display
|
|
9
|
+
* @param {Object} [options={}] - Toast configuration
|
|
10
|
+
* @param {"information"|"success"|"warning"|"error"} [options.type="information"] - Toast type/severity
|
|
11
|
+
* @param {number} [options.duration] - Duration in milliseconds (auto-calculated if not provided based on message length)
|
|
12
|
+
* @param {boolean} [options.closable=true] - Whether the toast can be manually closed
|
|
13
|
+
* @param {boolean} [options.persistent=false] - If true, toast won't auto-dismiss (requires manual close)
|
|
14
|
+
* @returns {Promise<string>} Toast ID (can be used to dismiss programmatically)
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Simple success toast
|
|
18
|
+
* await PDS.toast('Changes saved successfully!', { type: 'success' });
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Error with custom duration
|
|
22
|
+
* await PDS.toast('Failed to save changes', {
|
|
23
|
+
* type: 'error',
|
|
24
|
+
* duration: 8000
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Persistent warning (must be manually closed)
|
|
29
|
+
* await PDS.toast('This action cannot be undone', {
|
|
30
|
+
* type: 'warning',
|
|
31
|
+
* persistent: true
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Get toast ID to dismiss later
|
|
36
|
+
* const toastId = await PDS.toast('Processing...', { persistent: true });
|
|
37
|
+
* // ... later
|
|
38
|
+
* const toaster = document.querySelector('pds-toaster');
|
|
39
|
+
* toaster.dismissToast(toastId);
|
|
40
|
+
*/
|
|
41
|
+
export function toast(message: string, options?: {
|
|
42
|
+
type?: "information" | "success" | "warning" | "error";
|
|
43
|
+
duration?: number;
|
|
44
|
+
closable?: boolean;
|
|
45
|
+
persistent?: boolean;
|
|
46
|
+
}): Promise<string>;
|
|
47
|
+
export namespace toast {
|
|
48
|
+
/**
|
|
49
|
+
* Display a success toast (convenience method)
|
|
50
|
+
*
|
|
51
|
+
* @param {string} message - The success message
|
|
52
|
+
* @param {Object} [options={}] - Additional toast options (type is preset to 'success')
|
|
53
|
+
* @returns {Promise<string>} Toast ID
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* await PDS.toast.success('Profile updated!');
|
|
57
|
+
*/
|
|
58
|
+
function success(message: string, options?: any): Promise<string>;
|
|
59
|
+
/**
|
|
60
|
+
* Display an error toast (convenience method)
|
|
61
|
+
*
|
|
62
|
+
* @param {string} message - The error message
|
|
63
|
+
* @param {Object} [options={}] - Additional toast options (type is preset to 'error')
|
|
64
|
+
* @returns {Promise<string>} Toast ID
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* await PDS.toast.error('Failed to connect to server');
|
|
68
|
+
*/
|
|
69
|
+
function error(message: string, options?: any): Promise<string>;
|
|
70
|
+
/**
|
|
71
|
+
* Display a warning toast (convenience method)
|
|
72
|
+
*
|
|
73
|
+
* @param {string} message - The warning message
|
|
74
|
+
* @param {Object} [options={}] - Additional toast options (type is preset to 'warning')
|
|
75
|
+
* @returns {Promise<string>} Toast ID
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* await PDS.toast.warning('Session will expire in 5 minutes');
|
|
79
|
+
*/
|
|
80
|
+
function warning(message: string, options?: any): Promise<string>;
|
|
81
|
+
/**
|
|
82
|
+
* Display an information toast (convenience method)
|
|
83
|
+
*
|
|
84
|
+
* @param {string} message - The information message
|
|
85
|
+
* @param {Object} [options={}] - Additional toast options (type is preset to 'information')
|
|
86
|
+
* @returns {Promise<string>} Toast ID
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* await PDS.toast.info('New features available!');
|
|
90
|
+
*/
|
|
91
|
+
function info(message: string, options?: any): Promise<string>;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=toast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../../../../src/js/common/toast.js"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,+BAjCW,MAAM,YAEd;IAA4D,IAAI,GAAxD,aAAa,GAAC,SAAS,GAAC,SAAS,GAAC,OAAO;IACxB,QAAQ,GAAzB,MAAM;IACY,QAAQ,GAA1B,OAAO;IACW,UAAU,GAA5B,OAAO;CACf,GAAU,OAAO,CAAC,MAAM,CAAC,CA8B3B;;IAED;;;;;;;;;OASG;IACH,0BAPW,MAAM,kBAEJ,OAAO,CAAC,MAAM,CAAC,CAO3B;IAED;;;;;;;;;OASG;IACH,wBAPW,MAAM,kBAEJ,OAAO,CAAC,MAAM,CAAC,CAO3B;IAED;;;;;;;;;OASG;IACH,0BAPW,MAAM,kBAEJ,OAAO,CAAC,MAAM,CAAC,CAO3B;IAED;;;;;;;;;OASG;IACH,uBAPW,MAAM,kBAEJ,OAAO,CAAC,MAAM,CAAC,CAO3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pds.d.ts","sourceRoot":"","sources":["../../../../src/js/pds.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;eAuBc,cAAc,6BAA6B,EAAE,SAAS;;;;cACtD,OAAO,4BAA4B,EAAE,WAAW;;;;cAChD,GAAG;;;;iBACH,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,gBAAgB,CAAC,EAAE,aAAa,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC;;;;qBAChG,CAAC,UAAU,EAAE,UAAU,EAAE,gBAAgB,CAAC,EAAE,aAAa,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC;;;;sBAC7E,CAAC,GAAG,EAAC,MAAM,KAAK,aAAa;;AAW3C,+BAA+B;AAC/B,kBADW,MAAM,GAAG,OAAO,CACD;
|
|
1
|
+
{"version":3,"file":"pds.d.ts","sourceRoot":"","sources":["../../../../src/js/pds.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;eAuBc,cAAc,6BAA6B,EAAE,SAAS;;;;cACtD,OAAO,4BAA4B,EAAE,WAAW;;;;cAChD,GAAG;;;;iBACH,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,gBAAgB,CAAC,EAAE,aAAa,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC;;;;qBAChG,CAAC,UAAU,EAAE,UAAU,EAAE,gBAAgB,CAAC,EAAE,aAAa,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC;;;;sBAC7E,CAAC,GAAG,EAAC,MAAM,KAAK,aAAa;;AAW3C,+BAA+B;AAC/B,kBADW,MAAM,GAAG,OAAO,CACD;AA6R1B;;;;;;;;;GASG;AACH,8CALW,MAAM,YAEd;IAAyB,WAAW,GAA5B,MAAM;CACd,GAAU;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC;QAAC,IAAI,EAAC,MAAM,CAAC;QAAC,OAAO,EAAC,MAAM,CAAC;QAAC,KAAK,EAAC,MAAM,CAAC;QAAC,GAAG,EAAC,MAAM,CAAC;QAAC,OAAO,CAAC,EAAC,MAAM,CAAA;KAAC,CAAC,CAAA;CAAE,CAyIpH;AApdD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH;;;;GAIG;AACH;CAAoC"}
|
package/package.json
CHANGED
package/src/js/pds.d.ts
CHANGED
|
@@ -154,6 +154,68 @@ export class PDS extends EventTarget {
|
|
|
154
154
|
static validateDesign: (designConfig: any, options?: { minContrast?: number }) => { ok: boolean; issues: Array<{ path: string; message: string; ratio: number; min: number; context?: string }> };
|
|
155
155
|
static validateDesigns: (designs: Array<any> | Record<string, any>, options?: { minContrast?: number }) => { ok: boolean; results: Array<{ name?: string; ok: boolean; issues: Array<{ path: string; message: string; ratio: number; min: number; context?: string }> }> };
|
|
156
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Display a toast notification.
|
|
159
|
+
*
|
|
160
|
+
* Automatically ensures the pds-toaster component exists and is loaded before displaying.
|
|
161
|
+
*
|
|
162
|
+
* @param message - The message to display
|
|
163
|
+
* @param options - Toast configuration
|
|
164
|
+
* @param options.type - Toast type/severity ("information" | "success" | "warning" | "error")
|
|
165
|
+
* @param options.duration - Duration in milliseconds (auto-calculated if not provided)
|
|
166
|
+
* @param options.closable - Whether toast can be manually closed (default: true)
|
|
167
|
+
* @param options.persistent - If true, toast won't auto-dismiss (default: false)
|
|
168
|
+
* @returns Toast ID (can be used to dismiss programmatically)
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* await PDS.toast('Changes saved!', { type: 'success' });
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* await PDS.toast('Error occurred', { type: 'error', persistent: true });
|
|
175
|
+
*/
|
|
176
|
+
static toast(
|
|
177
|
+
message: string,
|
|
178
|
+
options?: {
|
|
179
|
+
type?: 'information' | 'success' | 'warning' | 'error';
|
|
180
|
+
duration?: number;
|
|
181
|
+
closable?: boolean;
|
|
182
|
+
persistent?: boolean;
|
|
183
|
+
}
|
|
184
|
+
): Promise<string>;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Display a success toast (convenience method).
|
|
188
|
+
*
|
|
189
|
+
* @param message - The success message
|
|
190
|
+
* @param options - Additional toast options (type is preset to 'success')
|
|
191
|
+
* @returns Toast ID
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* await PDS.toast.success('Profile updated!');
|
|
195
|
+
*/
|
|
196
|
+
static toast: {
|
|
197
|
+
(message: string, options?: { type?: 'information' | 'success' | 'warning' | 'error'; duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
|
|
198
|
+
success(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
|
|
199
|
+
error(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
|
|
200
|
+
warning(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
|
|
201
|
+
info(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Display a modal dialog for user input or confirmation.
|
|
206
|
+
*
|
|
207
|
+
* @param message - The message or prompt to display
|
|
208
|
+
* @param options - Dialog configuration
|
|
209
|
+
* @returns User's response (string for prompt, boolean for confirm, true for alert)
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* const confirmed = await PDS.ask('Delete this item?', { type: 'confirm' });
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* const name = await PDS.ask('Enter your name:', { type: 'prompt' });
|
|
216
|
+
*/
|
|
217
|
+
static ask(message: string, options?: any): Promise<any>;
|
|
218
|
+
|
|
157
219
|
/**
|
|
158
220
|
* Current configuration after PDS.start() completes - read-only, frozen after initialization.
|
|
159
221
|
* Contains the complete configuration used to initialize PDS, including mode, design, preset, and theme.
|
package/src/js/pds.js
CHANGED
|
@@ -56,6 +56,7 @@ import { findComponentForElement } from "./pds-core/pds-ontology.js";
|
|
|
56
56
|
import { presets, defaultLog } from "./pds-core/pds-config.js";
|
|
57
57
|
import { enums } from "./pds-core/pds-enums.js";
|
|
58
58
|
import { ask } from "./common/ask.js";
|
|
59
|
+
import { toast } from "./common/toast.js";
|
|
59
60
|
import { PDSQuery } from "./pds-core/pds-query.js";
|
|
60
61
|
import * as common from "./common/common.js";
|
|
61
62
|
import { defaultPDSEnhancers } from "./pds-core/pds-enhancers.js";
|
|
@@ -87,6 +88,7 @@ PDS.isLiveMode = () => registry.isLive;
|
|
|
87
88
|
PDS.enums = enums;
|
|
88
89
|
|
|
89
90
|
PDS.ask = ask;
|
|
91
|
+
PDS.toast = toast;
|
|
90
92
|
|
|
91
93
|
// Expose common utilities (deepMerge, isObject, etc.)
|
|
92
94
|
PDS.common = common;
|