@pure-ds/storybook 0.1.0
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/.storybook/addons/description/preview.js +15 -0
- package/.storybook/addons/description/register.js +60 -0
- package/.storybook/addons/html-preview/Panel.jsx +327 -0
- package/.storybook/addons/html-preview/constants.js +6 -0
- package/.storybook/addons/html-preview/preview.js +178 -0
- package/.storybook/addons/html-preview/register.js +16 -0
- package/.storybook/addons/pds-configurator/SearchTool.js +44 -0
- package/.storybook/addons/pds-configurator/Tool.js +30 -0
- package/.storybook/addons/pds-configurator/constants.js +9 -0
- package/.storybook/addons/pds-configurator/preview.js +159 -0
- package/.storybook/addons/pds-configurator/register.js +24 -0
- package/.storybook/docs.css +35 -0
- package/.storybook/htmlPreview.css +103 -0
- package/.storybook/htmlPreview.js +271 -0
- package/.storybook/main.js +160 -0
- package/.storybook/preview-body.html +48 -0
- package/.storybook/preview-head.html +11 -0
- package/.storybook/preview.js +1563 -0
- package/README.md +266 -0
- package/bin/index.js +40 -0
- package/dist/pds-reference.json +2101 -0
- package/package.json +45 -0
- package/pds.config.js +6 -0
- package/public/assets/css/app.css +1216 -0
- package/public/assets/data/auto-design-advanced.json +704 -0
- package/public/assets/data/auto-design-simple.json +123 -0
- package/public/assets/img/icon-512x512.png +0 -0
- package/public/assets/img/logo-trans.png +0 -0
- package/public/assets/img/logo.png +0 -0
- package/public/assets/js/app.js +15088 -0
- package/public/assets/js/app.js.map +7 -0
- package/public/assets/js/lit.js +1176 -0
- package/public/assets/js/lit.js.map +7 -0
- package/public/assets/js/pds.js +9801 -0
- package/public/assets/js/pds.js.map +7 -0
- package/public/assets/pds/components/pds-calendar.js +837 -0
- package/public/assets/pds/components/pds-drawer.js +857 -0
- package/public/assets/pds/components/pds-icon.js +338 -0
- package/public/assets/pds/components/pds-jsonform.js +1775 -0
- package/public/assets/pds/components/pds-richtext.js +1035 -0
- package/public/assets/pds/components/pds-scrollrow.js +331 -0
- package/public/assets/pds/components/pds-splitpanel.js +401 -0
- package/public/assets/pds/components/pds-tabstrip.js +251 -0
- package/public/assets/pds/components/pds-toaster.js +446 -0
- package/public/assets/pds/components/pds-upload.js +657 -0
- package/public/assets/pds/custom-elements.json +2003 -0
- package/public/assets/pds/icons/pds-icons.svg +498 -0
- package/public/assets/pds/pds-css-complete.json +1861 -0
- package/public/assets/pds/pds-runtime-config.json +11 -0
- package/public/assets/pds/pds.css-data.json +2152 -0
- package/public/assets/pds/styles/pds-components.css +1944 -0
- package/public/assets/pds/styles/pds-components.css.js +3895 -0
- package/public/assets/pds/styles/pds-primitives.css +352 -0
- package/public/assets/pds/styles/pds-primitives.css.js +711 -0
- package/public/assets/pds/styles/pds-styles.css +3761 -0
- package/public/assets/pds/styles/pds-styles.css.js +7529 -0
- package/public/assets/pds/styles/pds-tokens.css +699 -0
- package/public/assets/pds/styles/pds-tokens.css.js +1405 -0
- package/public/assets/pds/styles/pds-utilities.css +763 -0
- package/public/assets/pds/styles/pds-utilities.css.js +1533 -0
- package/public/assets/pds/vscode-custom-data.json +824 -0
- package/scripts/build-pds-reference.mjs +807 -0
- package/scripts/generate-stories.js +542 -0
- package/scripts/package-build.js +86 -0
- package/src/js/app.js +17 -0
- package/src/js/common/ask.js +208 -0
- package/src/js/common/common.js +20 -0
- package/src/js/common/font-loader.js +200 -0
- package/src/js/common/msg.js +90 -0
- package/src/js/lit.js +40 -0
- package/src/js/pds-core/pds-config.js +1162 -0
- package/src/js/pds-core/pds-enhancer-metadata.js +75 -0
- package/src/js/pds-core/pds-enhancers.js +357 -0
- package/src/js/pds-core/pds-enums.js +86 -0
- package/src/js/pds-core/pds-generator.js +5317 -0
- package/src/js/pds-core/pds-ontology.js +256 -0
- package/src/js/pds-core/pds-paths.js +109 -0
- package/src/js/pds-core/pds-query.js +571 -0
- package/src/js/pds-core/pds-registry.js +129 -0
- package/src/js/pds-core/pds.d.ts +129 -0
- package/src/js/pds.d.ts +408 -0
- package/src/js/pds.js +1579 -0
- package/src/pds-core/pds-api.js +105 -0
- package/stories/GettingStarted.md +96 -0
- package/stories/GettingStarted.stories.js +144 -0
- package/stories/WhatIsPDS.md +194 -0
- package/stories/WhatIsPDS.stories.js +144 -0
- package/stories/components/PdsCalendar.stories.js +263 -0
- package/stories/components/PdsDrawer.stories.js +623 -0
- package/stories/components/PdsIcon.stories.js +78 -0
- package/stories/components/PdsJsonform.stories.js +1444 -0
- package/stories/components/PdsRichtext.stories.js +367 -0
- package/stories/components/PdsScrollrow.stories.js +140 -0
- package/stories/components/PdsSplitpanel.stories.js +502 -0
- package/stories/components/PdsTabstrip.stories.js +442 -0
- package/stories/components/PdsToaster.stories.js +186 -0
- package/stories/components/PdsUpload.stories.js +66 -0
- package/stories/enhancements/Dropdowns.stories.js +185 -0
- package/stories/enhancements/InteractiveStates.stories.js +625 -0
- package/stories/enhancements/MeshGradients.stories.js +320 -0
- package/stories/enhancements/OpenGroups.stories.js +227 -0
- package/stories/enhancements/RangeSliders.stories.js +232 -0
- package/stories/enhancements/RequiredFields.stories.js +189 -0
- package/stories/enhancements/Toggles.stories.js +167 -0
- package/stories/foundations/Colors.stories.js +283 -0
- package/stories/foundations/Icons.stories.js +305 -0
- package/stories/foundations/SmartSurfaces.stories.js +367 -0
- package/stories/foundations/Spacing.stories.js +175 -0
- package/stories/foundations/Typography.stories.js +960 -0
- package/stories/foundations/ZIndex.stories.js +325 -0
- package/stories/patterns/BorderEffects.stories.js +72 -0
- package/stories/patterns/Layout.stories.js +99 -0
- package/stories/patterns/Utilities.stories.js +107 -0
- package/stories/primitives/Accordion.stories.js +359 -0
- package/stories/primitives/Alerts.stories.js +64 -0
- package/stories/primitives/Badges.stories.js +183 -0
- package/stories/primitives/Buttons.stories.js +229 -0
- package/stories/primitives/Cards.stories.js +353 -0
- package/stories/primitives/FormGroups.stories.js +569 -0
- package/stories/primitives/Forms.stories.js +131 -0
- package/stories/primitives/Media.stories.js +203 -0
- package/stories/primitives/Tables.stories.js +232 -0
- package/stories/reference/ReferenceCatalog.stories.js +28 -0
- package/stories/reference/reference-catalog.js +413 -0
- package/stories/reference/reference-docs.js +302 -0
- package/stories/reference/reference-helpers.js +310 -0
- package/stories/utilities/GridSystem.stories.js +208 -0
- package/stories/utils/PdsAsk.stories.js +420 -0
- package/stories/utils/toast-utils.js +148 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Utilities/Grid System',
|
|
5
|
+
tags: ['layout', 'grid', 'spacing', 'utilities', 'gap'],
|
|
6
|
+
parameters: {
|
|
7
|
+
pds: {
|
|
8
|
+
tags: ['grid', 'layout', 'utilities', 'spacing', 'gap']
|
|
9
|
+
},
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: 'Modern, config-driven grid system with fixed column grids and auto-fit responsive layouts. All utilities are generated from layout.gridSystem configuration.'
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const gridSystemStoryStyles = html`
|
|
19
|
+
<style>
|
|
20
|
+
.grid-system-card-title {
|
|
21
|
+
margin-top: var(--spacing-3);
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export const FixedColumnGrids = () => html`
|
|
27
|
+
<div class="card">
|
|
28
|
+
<h3>Two Column Grid (.grid-cols-2)</h3>
|
|
29
|
+
<div class="grid grid-cols-2 gap-md">
|
|
30
|
+
<div class="card text-center">
|
|
31
|
+
<pds-icon icon="square" size="lg" class="icon-primary"></pds-icon>
|
|
32
|
+
<h4>Column 1</h4>
|
|
33
|
+
<p>Equal width columns</p>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="card text-center">
|
|
36
|
+
<pds-icon icon="square" size="lg" class="icon-secondary"></pds-icon>
|
|
37
|
+
<h4>Column 2</h4>
|
|
38
|
+
<p>Auto responsive</p>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div class="card">
|
|
44
|
+
<h3>Three Column Grid (.grid-cols-3)</h3>
|
|
45
|
+
<div class="grid grid-cols-3 gap-sm">
|
|
46
|
+
<div class="card text-center">
|
|
47
|
+
<pds-icon icon="circle" size="md" class="icon-success"></pds-icon>
|
|
48
|
+
<p>Column 1</p>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="card text-center">
|
|
51
|
+
<pds-icon icon="circle" size="md" class="icon-warning"></pds-icon>
|
|
52
|
+
<p>Column 2</p>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="card text-center">
|
|
55
|
+
<pds-icon icon="circle" size="md" class="icon-danger"></pds-icon>
|
|
56
|
+
<p>Column 3</p>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="card">
|
|
62
|
+
<h3>Four Column Grid (.grid-cols-4)</h3>
|
|
63
|
+
<div class="grid grid-cols-4 gap-xs">
|
|
64
|
+
${Array.from({ length: 4 }, (_, i) => html`
|
|
65
|
+
<div class="card text-center">
|
|
66
|
+
<strong>${i + 1}</strong>
|
|
67
|
+
</div>
|
|
68
|
+
`)}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
FixedColumnGrids.storyName = 'Fixed Column Grids';
|
|
74
|
+
|
|
75
|
+
export const AutoFitGrids = () => html`
|
|
76
|
+
<div class="card">
|
|
77
|
+
<h3>.grid-auto-sm (min 150px)</h3>
|
|
78
|
+
<p class="text-muted">
|
|
79
|
+
<em>Resize the window to see columns automatically adjust</em>
|
|
80
|
+
</p>
|
|
81
|
+
<div class="grid grid-auto-sm gap-md">
|
|
82
|
+
<div class="card text-center">
|
|
83
|
+
<pds-icon icon="desktop" size="lg" class="icon-info"></pds-icon>
|
|
84
|
+
<h5>Responsive</h5>
|
|
85
|
+
<p>Automatically wraps</p>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="card text-center">
|
|
88
|
+
<pds-icon icon="device-mobile" size="lg" class="icon-info"></pds-icon>
|
|
89
|
+
<h5>Adaptive</h5>
|
|
90
|
+
<p>Based on space</p>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="card text-center">
|
|
93
|
+
<pds-icon icon="globe" size="lg" class="icon-info"></pds-icon>
|
|
94
|
+
<h5>Flexible</h5>
|
|
95
|
+
<p>Resize the window</p>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="card text-center">
|
|
98
|
+
<pds-icon icon="feather" size="lg" class="icon-info"></pds-icon>
|
|
99
|
+
<h5>Dynamic</h5>
|
|
100
|
+
<p>No breakpoints needed</p>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div class="card">
|
|
106
|
+
<h3>.grid-auto-md (min 250px)</h3>
|
|
107
|
+
<div class="grid grid-auto-md gap-lg">
|
|
108
|
+
<div class="card surface-elevated text-center">
|
|
109
|
+
<pds-icon icon="rocket" size="xl" class="icon-accent"></pds-icon>
|
|
110
|
+
<h5>Card 1</h5>
|
|
111
|
+
<p>Larger minimum width means fewer columns on small screens</p>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="card surface-elevated text-center">
|
|
114
|
+
<pds-icon icon="palette" size="xl" class="icon-accent"></pds-icon>
|
|
115
|
+
<h5>Card 2</h5>
|
|
116
|
+
<p>Smart surface tokens apply automatically</p>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="card surface-elevated text-center">
|
|
119
|
+
<pds-icon icon="heart" size="xl" class="icon-accent"></pds-icon>
|
|
120
|
+
<h5>Card 3</h5>
|
|
121
|
+
<p>Consistent spacing with gap utilities</p>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
AutoFitGrids.storyName = 'Auto-Fit Responsive Grids';
|
|
128
|
+
|
|
129
|
+
export const GapUtilities = () => html`
|
|
130
|
+
<div class="card">
|
|
131
|
+
<h2>Gap Utilities</h2>
|
|
132
|
+
<p>Control spacing between grid items with <code>.gap-{size}</code> classes</p>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<h3>.gap-xs (spacing-1)</h3>
|
|
136
|
+
<div class="card">
|
|
137
|
+
<div class="grid grid-cols-3 gap-xs">
|
|
138
|
+
${Array.from({ length: 3 }, (_, i) => html`
|
|
139
|
+
<div class="card text-center">
|
|
140
|
+
<strong>${String.fromCharCode(65 + i)}</strong>
|
|
141
|
+
</div>
|
|
142
|
+
`)}
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<h3>.gap-sm (spacing-2)</h3>
|
|
147
|
+
<div class="card">
|
|
148
|
+
<div class="grid grid-cols-3 gap-sm">
|
|
149
|
+
${Array.from({ length: 3 }, (_, i) => html`
|
|
150
|
+
<div class="card text-center">
|
|
151
|
+
<strong>${String.fromCharCode(65 + i)}</strong>
|
|
152
|
+
</div>
|
|
153
|
+
`)}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<h3>.gap-md (spacing-4)</h3>
|
|
158
|
+
<div class="card">
|
|
159
|
+
<div class="grid grid-cols-3 gap-md">
|
|
160
|
+
${Array.from({ length: 3 }, (_, i) => html`
|
|
161
|
+
<div class="card text-center">
|
|
162
|
+
<strong>${String.fromCharCode(65 + i)}</strong>
|
|
163
|
+
</div>
|
|
164
|
+
`)}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<h3>.gap-lg (spacing-6)</h3>
|
|
169
|
+
<div class="card">
|
|
170
|
+
<div class="grid grid-cols-3 gap-lg">
|
|
171
|
+
${Array.from({ length: 3 }, (_, i) => html`
|
|
172
|
+
<div class="card text-center">
|
|
173
|
+
<strong>${String.fromCharCode(65 + i)}</strong>
|
|
174
|
+
</div>
|
|
175
|
+
`)}
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
`;
|
|
179
|
+
|
|
180
|
+
GapUtilities.storyName = 'Gap Utilities';
|
|
181
|
+
|
|
182
|
+
export const CardGridLayouts = () => html`
|
|
183
|
+
${gridSystemStoryStyles}
|
|
184
|
+
<div class="card">
|
|
185
|
+
<h2>Card Grid Layouts</h2>
|
|
186
|
+
<p>Practical examples using grid utilities for card-based layouts</p>
|
|
187
|
+
|
|
188
|
+
<div class="grid grid-cols-3 gap-md">
|
|
189
|
+
<div class="card">
|
|
190
|
+
<pds-icon icon="palette" size="xl" class="icon-primary"></pds-icon>
|
|
191
|
+
<h5 class="grid-system-card-title">Design</h5>
|
|
192
|
+
<p>Smart surfaces handle theming automatically</p>
|
|
193
|
+
</div>
|
|
194
|
+
<div class="card">
|
|
195
|
+
<pds-icon icon="code" size="xl" class="icon-success"></pds-icon>
|
|
196
|
+
<h5 class="grid-system-card-title">Development</h5>
|
|
197
|
+
<p>Zero manual color overrides needed</p>
|
|
198
|
+
</div>
|
|
199
|
+
<div class="card">
|
|
200
|
+
<pds-icon icon="rocket" size="xl" class="icon-info"></pds-icon>
|
|
201
|
+
<h5 class="grid-system-card-title">Performance</h5>
|
|
202
|
+
<p>CSS custom properties are fast</p>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
`;
|
|
207
|
+
|
|
208
|
+
CardGridLayouts.storyName = 'Card Grid Layouts';
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import { ask as askFallback } from '../../../../src/js/common/ask.js';
|
|
3
|
+
import { toastFormData } from './toast-utils.js';
|
|
4
|
+
|
|
5
|
+
const ensureAsk = () => {
|
|
6
|
+
if (typeof window !== 'undefined' && window.PDS?.ask) {
|
|
7
|
+
return window.PDS.ask;
|
|
8
|
+
}
|
|
9
|
+
return askFallback;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const marketingSchema = {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
headline: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
title: 'Headline',
|
|
18
|
+
minLength: 10,
|
|
19
|
+
examples: ['Supercharge collaboration across your org']
|
|
20
|
+
},
|
|
21
|
+
description: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
title: 'Supporting copy',
|
|
24
|
+
minLength: 20,
|
|
25
|
+
examples: ['Surface key metrics, share insights instantly, and keep every team aligned.']
|
|
26
|
+
},
|
|
27
|
+
ctaLabel: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
title: 'CTA label',
|
|
30
|
+
default: 'Schedule a demo'
|
|
31
|
+
},
|
|
32
|
+
emphasis: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
title: 'Visual style',
|
|
35
|
+
enum: ['balanced', 'quiet', 'loud'],
|
|
36
|
+
default: 'balanced'
|
|
37
|
+
},
|
|
38
|
+
audiences: {
|
|
39
|
+
type: 'array',
|
|
40
|
+
title: 'Target audiences',
|
|
41
|
+
items: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
enum: ['Design Leadership', 'Product Teams', 'Engineering', 'Marketing']
|
|
44
|
+
},
|
|
45
|
+
uniqueItems: true
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
required: ['headline', 'description', 'ctaLabel']
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const marketingUiSchema = {
|
|
52
|
+
description: {
|
|
53
|
+
'ui:widget': 'textarea',
|
|
54
|
+
'ui:options': {
|
|
55
|
+
rows: 3
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
audiences: {
|
|
59
|
+
'ui:widget': 'checkboxes'
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const marketingInitialValues = {
|
|
64
|
+
headline: 'Supercharge collaboration across your org',
|
|
65
|
+
description: 'Surface key metrics, share insights instantly, and keep every team aligned.',
|
|
66
|
+
ctaLabel: 'Schedule a demo',
|
|
67
|
+
emphasis: 'balanced',
|
|
68
|
+
audiences: ['Design Leadership', 'Product Teams']
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const formDataToObject = (formData) => {
|
|
72
|
+
const entries = Object.fromEntries(formData.entries());
|
|
73
|
+
return entries;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const askStoryStyles = html`
|
|
77
|
+
<style>
|
|
78
|
+
.ask-card {
|
|
79
|
+
padding: var(--spacing-6, 1.5rem);
|
|
80
|
+
border: 1px solid var(--surface-border, rgba(0, 0, 0, 0.08));
|
|
81
|
+
border-radius: var(--radius-lg, 16px);
|
|
82
|
+
background: var(--surface-base, #ffffff);
|
|
83
|
+
}
|
|
84
|
+
.ask-card--sm {
|
|
85
|
+
max-width: 26.25rem;
|
|
86
|
+
}
|
|
87
|
+
.ask-card--md {
|
|
88
|
+
max-width: 30rem;
|
|
89
|
+
}
|
|
90
|
+
.ask-card--lg {
|
|
91
|
+
max-width: 32.5rem;
|
|
92
|
+
}
|
|
93
|
+
.ask-card--xl {
|
|
94
|
+
max-width: 38.75rem;
|
|
95
|
+
}
|
|
96
|
+
.ask-card-heading {
|
|
97
|
+
margin: 0;
|
|
98
|
+
}
|
|
99
|
+
.ask-card-status {
|
|
100
|
+
margin: 0;
|
|
101
|
+
color: var(--surface-muted-text, rgba(0, 0, 0, 0.6));
|
|
102
|
+
}
|
|
103
|
+
.ask-card-list {
|
|
104
|
+
padding-left: var(--spacing-6, 1.5rem);
|
|
105
|
+
}
|
|
106
|
+
.ask-dialog-form {
|
|
107
|
+
min-width: 22.5rem;
|
|
108
|
+
}
|
|
109
|
+
.ask-dialog-form--wide {
|
|
110
|
+
min-width: 30rem;
|
|
111
|
+
}
|
|
112
|
+
</style>
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
export default {
|
|
116
|
+
title: 'Utilities/PDS ask',
|
|
117
|
+
tags: ['interaction', 'dialogs', 'forms'],
|
|
118
|
+
parameters: {
|
|
119
|
+
pds: {
|
|
120
|
+
tags: ['interaction', 'dialogs', 'forms', 'pds-ask']
|
|
121
|
+
},
|
|
122
|
+
docs: {
|
|
123
|
+
description: {
|
|
124
|
+
component: `The \`PDS.ask()\` helper wraps the native \`<dialog>\` element with PDS styling and a promise-based API.
|
|
125
|
+
Use it to collect quick confirmations or to stage entire forms before committing data back to the host application.`
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const BareConfirm = {
|
|
132
|
+
name: 'Are you sure?',
|
|
133
|
+
render: () => {
|
|
134
|
+
const handleClick = async (event) => {
|
|
135
|
+
const ask = ensureAsk();
|
|
136
|
+
const container = event.currentTarget.closest('[data-ask-example]');
|
|
137
|
+
const status = container?.querySelector('[data-status]');
|
|
138
|
+
|
|
139
|
+
if (status) {
|
|
140
|
+
status.textContent = 'Asking…';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const confirmed = await ask('This uses the default buttons and styling.');
|
|
144
|
+
|
|
145
|
+
if (status) {
|
|
146
|
+
status.textContent = confirmed
|
|
147
|
+
? '✅ User confirmed the action.'
|
|
148
|
+
: '❎ User cancelled.';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
await toastFormData({ confirmed, scenario: 'bare-confirm' });
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return html`
|
|
155
|
+
${askStoryStyles}
|
|
156
|
+
<section
|
|
157
|
+
data-ask-example
|
|
158
|
+
class="ask-card ask-card--sm flex flex-col gap-md"
|
|
159
|
+
>
|
|
160
|
+
<h3 class="ask-card-heading">Default confirm dialog</h3>
|
|
161
|
+
<p>
|
|
162
|
+
No custom options supplied—just a message string. The helper wires up buttons, focus, and the promise result for you.
|
|
163
|
+
</p>
|
|
164
|
+
<button class="btn" @click=${handleClick}>Ask “Are you sure?”</button>
|
|
165
|
+
<p data-status class="ask-card-status">Waiting…</p>
|
|
166
|
+
</section>
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export const SimpleForm = {
|
|
172
|
+
name: 'Mini form submission',
|
|
173
|
+
render: () => {
|
|
174
|
+
const handleClick = async (event) => {
|
|
175
|
+
const ask = ensureAsk();
|
|
176
|
+
const container = event.currentTarget.closest('[data-ask-example]');
|
|
177
|
+
const status = container?.querySelector('[data-status]');
|
|
178
|
+
|
|
179
|
+
if (status) {
|
|
180
|
+
status.textContent = 'Opening form…';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const result = await ask(
|
|
184
|
+
html`
|
|
185
|
+
<form method="dialog" class="ask-dialog-form flex flex-col gap-sm">
|
|
186
|
+
<label class="flex flex-col gap-xs">
|
|
187
|
+
<span>Name</span>
|
|
188
|
+
<input name="name" required placeholder="Alex Rivera" />
|
|
189
|
+
</label>
|
|
190
|
+
<label class="flex flex-col gap-xs">
|
|
191
|
+
<span>Email</span>
|
|
192
|
+
<input type="email" name="email" required placeholder="alex@example.com" />
|
|
193
|
+
</label>
|
|
194
|
+
<label class="flex flex-col gap-xs">
|
|
195
|
+
<span>Team size</span>
|
|
196
|
+
<select class="select" name="teamSize">
|
|
197
|
+
<option value="1-5">1-5</option>
|
|
198
|
+
<option value="6-20">6-20</option>
|
|
199
|
+
<option value="21-100">21-100</option>
|
|
200
|
+
<option value="101+">101+</option>
|
|
201
|
+
</select>
|
|
202
|
+
</label>
|
|
203
|
+
</form>
|
|
204
|
+
`,
|
|
205
|
+
{
|
|
206
|
+
title: 'Share your details',
|
|
207
|
+
useForm: true,
|
|
208
|
+
buttons: {
|
|
209
|
+
ok: { name: 'Submit', primary: true },
|
|
210
|
+
cancel: { name: 'Cancel', cancel: true }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
if (result instanceof FormData) {
|
|
216
|
+
if (status) {
|
|
217
|
+
status.textContent = '✅ Submitted! (check toast for values)';
|
|
218
|
+
}
|
|
219
|
+
await toastFormData(result);
|
|
220
|
+
} else {
|
|
221
|
+
if (status) {
|
|
222
|
+
status.textContent = 'Submission cancelled.';
|
|
223
|
+
}
|
|
224
|
+
await toastFormData({ cancelled: true, scenario: 'simple-form' });
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
return html`
|
|
229
|
+
${askStoryStyles}
|
|
230
|
+
<section
|
|
231
|
+
data-ask-example
|
|
232
|
+
class="ask-card ask-card--md flex flex-col gap-md"
|
|
233
|
+
>
|
|
234
|
+
<h3 class="ask-card-heading">Collect a few fields</h3>
|
|
235
|
+
<p>
|
|
236
|
+
A plain HTML form inside the dialog captures values through <code>FormData</code> when the OK button fires.
|
|
237
|
+
</p>
|
|
238
|
+
<button class="btn btn-primary" @click=${handleClick}>Open mini form</button>
|
|
239
|
+
<p data-status class="ask-card-status">No submission yet.</p>
|
|
240
|
+
</section>
|
|
241
|
+
`;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export const QuickConfirm = {
|
|
246
|
+
name: 'Boolean confirm flow',
|
|
247
|
+
render: () => {
|
|
248
|
+
const handleClick = async (event) => {
|
|
249
|
+
const ask = ensureAsk();
|
|
250
|
+
const container = event.currentTarget.closest('[data-ask-example]');
|
|
251
|
+
const status = container?.querySelector('[data-status]');
|
|
252
|
+
|
|
253
|
+
if (status) {
|
|
254
|
+
status.textContent = 'Waiting for a decision…';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const decision = await ask(
|
|
258
|
+
html`
|
|
259
|
+
<div class="flex flex-col gap-sm">
|
|
260
|
+
<p>This dialog comes from <code>PDS.ask()</code> and resolves with a simple boolean.</p>
|
|
261
|
+
<ul class="ask-card-list list-disc">
|
|
262
|
+
<li>Handles focus trapping and ESC cancel automatically.</li>
|
|
263
|
+
<li>Resolves to <code>true</code> when the primary button is pressed.</li>
|
|
264
|
+
<li>Returns <code>false</code> if the user cancels or closes the dialog.</li>
|
|
265
|
+
</ul>
|
|
266
|
+
</div>
|
|
267
|
+
`,
|
|
268
|
+
{
|
|
269
|
+
title: 'Archive analytics project?',
|
|
270
|
+
buttons: {
|
|
271
|
+
ok: { name: 'Archive project', primary: true },
|
|
272
|
+
cancel: { name: 'Keep active', cancel: true }
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
if (decision) {
|
|
278
|
+
if (status) {
|
|
279
|
+
status.textContent = '✅ Archived via PDS.ask()';
|
|
280
|
+
}
|
|
281
|
+
await toastFormData({ confirmed: true, action: 'archive-project' });
|
|
282
|
+
} else {
|
|
283
|
+
if (status) {
|
|
284
|
+
status.textContent = '✋ Cancelled — nothing changed';
|
|
285
|
+
}
|
|
286
|
+
await toastFormData({ confirmed: false, action: 'archive-project' });
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
return html`
|
|
291
|
+
${askStoryStyles}
|
|
292
|
+
<section
|
|
293
|
+
data-ask-example
|
|
294
|
+
class="ask-card ask-card--lg flex flex-col gap-md"
|
|
295
|
+
>
|
|
296
|
+
<h3 class="ask-card-heading">Lightweight confirmations</h3>
|
|
297
|
+
<p>
|
|
298
|
+
Click the button to open a fully styled modal dialog. When it resolves, the promise gives you a
|
|
299
|
+
boolean — ideal for gating destructive actions without wiring any extra event listeners.
|
|
300
|
+
</p>
|
|
301
|
+
<button class="btn btn-primary" @click=${handleClick}>Archive project</button>
|
|
302
|
+
<p data-status class="ask-card-status">No dialog shown yet.</p>
|
|
303
|
+
</section>
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
export const JsonformSubdialog = {
|
|
309
|
+
name: 'Embed a pds-jsonform subform',
|
|
310
|
+
render: () => {
|
|
311
|
+
const handleClick = async (event) => {
|
|
312
|
+
const ask = ensureAsk();
|
|
313
|
+
const container = event.currentTarget.closest('[data-ask-example]');
|
|
314
|
+
const status = container?.querySelector('[data-status]');
|
|
315
|
+
|
|
316
|
+
if (status) {
|
|
317
|
+
status.textContent = 'Opening editor…';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const dialogResult = await ask(
|
|
321
|
+
html`
|
|
322
|
+
<form method="dialog" class="ask-dialog-form ask-dialog-form--wide flex flex-col gap-md">
|
|
323
|
+
<pds-jsonform
|
|
324
|
+
id="spotlight-form"
|
|
325
|
+
.jsonSchema=${marketingSchema}
|
|
326
|
+
.uiSchema=${marketingUiSchema}
|
|
327
|
+
.values=${marketingInitialValues}
|
|
328
|
+
hide-actions
|
|
329
|
+
></pds-jsonform>
|
|
330
|
+
<input type="hidden" name="spotlight" value="" />
|
|
331
|
+
</form>
|
|
332
|
+
`,
|
|
333
|
+
{
|
|
334
|
+
title: 'Marketing spotlight',
|
|
335
|
+
useForm: true,
|
|
336
|
+
buttons: {
|
|
337
|
+
ok: { name: 'Save changes', primary: true },
|
|
338
|
+
cancel: { name: 'Cancel', cancel: true }
|
|
339
|
+
},
|
|
340
|
+
rendered(dialog) {
|
|
341
|
+
const form = dialog.querySelector('form');
|
|
342
|
+
const subform = dialog.querySelector('#spotlight-form');
|
|
343
|
+
const hidden = dialog.querySelector('input[name="spotlight"]');
|
|
344
|
+
|
|
345
|
+
const syncHidden = (payload) => {
|
|
346
|
+
if (!hidden) return;
|
|
347
|
+
const nextValue =
|
|
348
|
+
payload?.json ??
|
|
349
|
+
payload?.value ??
|
|
350
|
+
(() => {
|
|
351
|
+
if (!subform?.serialize) return undefined;
|
|
352
|
+
try {
|
|
353
|
+
const { json } = subform.serialize();
|
|
354
|
+
return json;
|
|
355
|
+
} catch (error) {
|
|
356
|
+
return { error: error?.message || 'Failed to serialize' };
|
|
357
|
+
}
|
|
358
|
+
})();
|
|
359
|
+
|
|
360
|
+
hidden.value = JSON.stringify(nextValue ?? {});
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
if (subform) {
|
|
364
|
+
const handleValueChange = (evt) => syncHidden(evt.detail);
|
|
365
|
+
subform.addEventListener('pw:value-change', handleValueChange);
|
|
366
|
+
subform.addEventListener('pw:submit', (evt) => {
|
|
367
|
+
evt.preventDefault();
|
|
368
|
+
syncHidden(evt.detail);
|
|
369
|
+
const submitter = form?.querySelector('button[value="ok"]') || undefined;
|
|
370
|
+
form?.requestSubmit(submitter);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
if (typeof customElements?.whenDefined === 'function') {
|
|
374
|
+
customElements.whenDefined('pds-jsonform').then(() => syncHidden());
|
|
375
|
+
} else {
|
|
376
|
+
syncHidden();
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
if (dialogResult instanceof FormData) {
|
|
384
|
+
await toastFormData(dialogResult);
|
|
385
|
+
const asObject = formDataToObject(dialogResult);
|
|
386
|
+
let parsed;
|
|
387
|
+
try {
|
|
388
|
+
parsed = asObject.spotlight ? JSON.parse(asObject.spotlight) : null;
|
|
389
|
+
} catch (error) {
|
|
390
|
+
parsed = null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (status) {
|
|
394
|
+
const headline = parsed?.headline || 'Draft saved';
|
|
395
|
+
status.textContent = `✅ Saved: ${headline}`;
|
|
396
|
+
}
|
|
397
|
+
} else {
|
|
398
|
+
if (status) {
|
|
399
|
+
status.textContent = 'Dialog cancelled — existing content preserved';
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
return html`
|
|
405
|
+
${askStoryStyles}
|
|
406
|
+
<section
|
|
407
|
+
data-ask-example
|
|
408
|
+
class="ask-card ask-card--xl flex flex-col gap-md"
|
|
409
|
+
>
|
|
410
|
+
<h3 class="ask-card-heading">Deep editing workflows</h3>
|
|
411
|
+
<p>
|
|
412
|
+
Use <code>PDS.ask()</code> with <code>useForm: true</code> to mount an entire <code><pds-jsonform></code>
|
|
413
|
+
inside the dialog. The promise resolves with the form data, letting you merge the changes into the parent flow.
|
|
414
|
+
</p>
|
|
415
|
+
<button class="btn btn-outline" @click=${handleClick}>Edit spotlight copy</button>
|
|
416
|
+
<p data-status class="ask-card-status">No edits made yet.</p>
|
|
417
|
+
</section>
|
|
418
|
+
`;
|
|
419
|
+
}
|
|
420
|
+
};
|