@pure-ds/storybook 0.7.25 → 0.7.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/.storybook/preview.js +5 -4
- package/dist/pds-reference.json +2266 -1017
- package/package.json +2 -2
- package/public/assets/js/app.js +1 -1
- package/public/assets/js/pds-ask.js +6 -6
- package/public/assets/js/pds-manager.js +104 -29
- package/public/assets/pds/components/pds-calendar.js +91 -159
- package/public/assets/pds/components/pds-daterange.js +683 -0
- package/public/assets/pds/components/pds-form.js +123 -21
- package/public/assets/pds/components/pds-rating.js +648 -0
- package/public/assets/pds/components/pds-tags.js +802 -0
- package/public/assets/pds/core/pds-ask.js +6 -6
- package/public/assets/pds/core/pds-manager.js +104 -29
- package/public/assets/pds/custom-elements.json +1099 -74
- package/public/assets/pds/pds-css-complete.json +7 -2
- package/public/assets/pds/pds-runtime-config.json +1 -1
- package/public/assets/pds/pds.css-data.json +4 -4
- package/public/assets/pds/styles/pds-components.css +96 -24
- package/public/assets/pds/styles/pds-components.css.js +192 -48
- package/public/assets/pds/styles/pds-primitives.css +6 -3
- package/public/assets/pds/styles/pds-primitives.css.js +12 -6
- package/public/assets/pds/styles/pds-styles.css +104 -29
- package/public/assets/pds/styles/pds-styles.css.js +208 -58
- package/public/assets/pds/styles/pds-utilities.css +2 -2
- package/public/assets/pds/styles/pds-utilities.css.js +4 -4
- package/public/assets/pds/vscode-custom-data.json +97 -0
- package/src/js/common/ask.js +122 -12
- package/src/js/pds-core/pds-generator.js +103 -28
- package/src/js/pds-core/pds-ontology.js +2 -2
- package/stories/components/PdsCalendar.stories.js +650 -168
- package/stories/components/PdsDaterange.stories.js +85 -0
- package/stories/components/PdsForm.Basics.stories.js +16 -0
- package/stories/components/PdsForm.ConditionalAndCalculated.stories.js +16 -0
- package/stories/components/PdsForm.CustomContent.stories.js +10 -0
- package/stories/components/PdsForm.Dialogs.stories.js +7 -0
- package/stories/components/PdsForm.Layout.stories.js +16 -0
- package/stories/components/PdsForm.SelectionAndArrays.stories.js +12 -0
- package/stories/components/PdsForm.stories.js +179 -219
- package/stories/components/PdsIcon.stories.js +17 -7
- package/stories/components/PdsOmnibox.stories.js +1 -60
- package/stories/components/PdsRating.stories.js +126 -0
- package/stories/components/PdsTags.stories.js +224 -0
- package/stories/components/PdsToaster.stories.js +1 -1
- package/stories/components/omnibox-countries-api-settings.js +63 -0
- package/stories/foundations/Colors.stories.js +6 -1
- package/stories/foundations/HTMLDefaults.stories.js +6 -6
- package/stories/foundations/Icons.stories.js +4 -4
- package/stories/foundations/MeshGradients.stories.js +13 -3
- package/stories/foundations/SmartSurfaces.stories.js +43 -31
- package/stories/foundations/Typography.stories.js +30 -108
- package/stories/foundations/ZIndex.stories.js +3 -3
- package/stories/layout/LayoutOverview.stories.js +3 -3
- package/stories/layout/LayoutSystem.stories.js +24 -18
- package/stories/patterns/BorderEffects.stories.js +28 -16
- package/stories/patterns/InteractiveStates.stories.js +39 -24
- package/stories/patterns/Utilities.stories.js +45 -23
- package/stories/primitives/ArticleLayout.stories.js +176 -0
- package/stories/primitives/Badges.stories.js +24 -8
- package/stories/primitives/Buttons.stories.js +29 -9
- package/stories/primitives/Callouts.stories.js +8 -7
- package/stories/primitives/Cards.stories.js +4 -1
- package/stories/primitives/FormElements.stories.js +8 -2
- package/stories/primitives/HtmlFormElements.stories.js +8 -2
- package/stories/primitives/HtmlFormGroups.stories.js +5 -2
- package/stories/primitives/Media.stories.js +44 -30
- package/stories/primitives/Tables.stories.js +25 -7
- package/stories/utils/PdsAsk.stories.js +17 -5
- package/stories/utils/PdsObjectApi.stories.js +16 -4
|
@@ -1,276 +1,586 @@
|
|
|
1
|
-
import { html } from
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import { PDS } from "#pds";
|
|
2
3
|
|
|
3
4
|
const docsParameters = {
|
|
4
5
|
description: {
|
|
5
|
-
component:
|
|
6
|
-
|
|
6
|
+
component:
|
|
7
|
+
"A fully featured calendar component with month navigation, event display, and keyboard-accessible date selection",
|
|
8
|
+
},
|
|
7
9
|
};
|
|
8
10
|
|
|
9
|
-
if (typeof window !==
|
|
10
|
-
import(
|
|
11
|
+
if (typeof window !== "undefined") {
|
|
12
|
+
import("../reference/reference-docs.js")
|
|
11
13
|
.then(({ createComponentDocsPage }) => {
|
|
12
|
-
docsParameters.page = createComponentDocsPage(
|
|
14
|
+
docsParameters.page = createComponentDocsPage("pds-calendar");
|
|
13
15
|
})
|
|
14
16
|
.catch((error) => {
|
|
15
|
-
console.warn(
|
|
17
|
+
console.warn(
|
|
18
|
+
"storybook: docs page failed to load for pds-calendar",
|
|
19
|
+
error,
|
|
20
|
+
);
|
|
16
21
|
});
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
export default {
|
|
20
|
-
title:
|
|
21
|
-
tags: [
|
|
25
|
+
title: "Components/pds-calendar",
|
|
26
|
+
tags: ["autodocs", "calendar", "date", "datepicker", "event", "schedule"],
|
|
22
27
|
parameters: {
|
|
23
28
|
pds: {
|
|
24
|
-
tags: [
|
|
29
|
+
tags: [
|
|
30
|
+
"calendar",
|
|
31
|
+
"date",
|
|
32
|
+
"datepicker",
|
|
33
|
+
"event",
|
|
34
|
+
"schedule",
|
|
35
|
+
"pds-calendar",
|
|
36
|
+
],
|
|
25
37
|
},
|
|
26
|
-
docs: docsParameters
|
|
38
|
+
docs: docsParameters,
|
|
27
39
|
},
|
|
28
40
|
argTypes: {
|
|
29
41
|
date: {
|
|
30
|
-
control:
|
|
31
|
-
description:
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
control: "text",
|
|
43
|
+
description:
|
|
44
|
+
"The date to display (defaults to current date). Accepts any valid date string",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
34
47
|
};
|
|
35
48
|
|
|
36
49
|
// Sample event data generator
|
|
37
50
|
const getEventData = () => ({
|
|
38
51
|
5: [
|
|
39
|
-
{ title:
|
|
40
|
-
{ title:
|
|
41
|
-
],
|
|
42
|
-
12: [
|
|
43
|
-
{ title: 'Sprint Planning', type: 'primary' }
|
|
52
|
+
{ title: "Team Standup", type: "primary" },
|
|
53
|
+
{ title: "Code Review Session", type: "info" },
|
|
44
54
|
],
|
|
55
|
+
12: [{ title: "Sprint Planning", type: "primary" }],
|
|
45
56
|
15: [
|
|
46
|
-
{ title:
|
|
47
|
-
{ title:
|
|
48
|
-
],
|
|
49
|
-
18: [
|
|
50
|
-
{ title: 'Deployment Window', type: 'danger' }
|
|
57
|
+
{ title: "Client Meeting", type: "warning" },
|
|
58
|
+
{ title: "Design Review", type: "info" },
|
|
51
59
|
],
|
|
60
|
+
18: [{ title: "Deployment Window", type: "danger" }],
|
|
52
61
|
20: [
|
|
53
|
-
{ title:
|
|
54
|
-
{ title:
|
|
55
|
-
],
|
|
56
|
-
25: [
|
|
57
|
-
{ title: 'Holiday Event', type: 'warning' }
|
|
62
|
+
{ title: "Team Retrospective", type: "primary" },
|
|
63
|
+
{ title: "Documentation Review", type: "info" },
|
|
58
64
|
],
|
|
65
|
+
25: [{ title: "Holiday Event", type: "warning" }],
|
|
59
66
|
28: [
|
|
60
|
-
{ title:
|
|
61
|
-
{ title:
|
|
62
|
-
]
|
|
67
|
+
{ title: "Release Candidate Review", type: "danger" },
|
|
68
|
+
{ title: "Performance Testing", type: "info" },
|
|
69
|
+
],
|
|
63
70
|
});
|
|
64
71
|
|
|
65
72
|
export const Default = {
|
|
66
73
|
render: (args) => {
|
|
67
74
|
setTimeout(() => {
|
|
68
|
-
const calendar = document.querySelector(
|
|
75
|
+
const calendar = document.querySelector("#default-calendar");
|
|
69
76
|
if (calendar) {
|
|
70
|
-
calendar.addEventListener(
|
|
77
|
+
calendar.addEventListener("month-rendered", (e) => {
|
|
71
78
|
e.detail.fill(getEventData());
|
|
72
79
|
});
|
|
73
80
|
}
|
|
74
81
|
}, 0);
|
|
75
|
-
|
|
76
|
-
return html`
|
|
77
|
-
<pds-calendar id="default-calendar"></pds-calendar>
|
|
78
|
-
`;
|
|
82
|
+
|
|
83
|
+
return html` <pds-calendar id="default-calendar"></pds-calendar> `;
|
|
79
84
|
},
|
|
80
85
|
args: {
|
|
81
|
-
date:
|
|
82
|
-
}
|
|
86
|
+
date: "",
|
|
87
|
+
},
|
|
83
88
|
};
|
|
84
89
|
|
|
85
90
|
export const WithSpecificDate = {
|
|
86
91
|
render: () => {
|
|
87
92
|
setTimeout(() => {
|
|
88
|
-
const calendar = document.querySelector(
|
|
93
|
+
const calendar = document.querySelector("#specific-date-calendar");
|
|
89
94
|
if (calendar) {
|
|
90
|
-
calendar.addEventListener(
|
|
95
|
+
calendar.addEventListener("month-rendered", (e) => {
|
|
91
96
|
e.detail.fill({
|
|
92
|
-
10: [
|
|
93
|
-
|
|
94
|
-
],
|
|
95
|
-
15: [
|
|
96
|
-
{ title: 'Project Kickoff', type: 'primary' }
|
|
97
|
-
],
|
|
98
|
-
25: [
|
|
99
|
-
{ title: 'Holiday', type: 'warning' }
|
|
100
|
-
]
|
|
97
|
+
10: [{ title: "Important Meeting", type: "danger" }],
|
|
98
|
+
15: [{ title: "Project Kickoff", type: "primary" }],
|
|
99
|
+
25: [{ title: "Holiday", type: "warning" }],
|
|
101
100
|
});
|
|
102
101
|
});
|
|
103
102
|
}
|
|
104
103
|
}, 0);
|
|
105
|
-
|
|
104
|
+
|
|
106
105
|
return html`
|
|
107
|
-
<pds-calendar
|
|
106
|
+
<pds-calendar
|
|
107
|
+
id="specific-date-calendar"
|
|
108
|
+
date="2024-12-01"
|
|
109
|
+
></pds-calendar>
|
|
108
110
|
`;
|
|
109
|
-
}
|
|
111
|
+
},
|
|
110
112
|
};
|
|
111
113
|
|
|
112
114
|
export const WithManyEvents = {
|
|
113
115
|
render: () => {
|
|
114
116
|
setTimeout(() => {
|
|
115
|
-
const calendar = document.querySelector(
|
|
117
|
+
const calendar = document.querySelector("#many-events-calendar");
|
|
116
118
|
if (calendar) {
|
|
117
|
-
calendar.addEventListener(
|
|
119
|
+
calendar.addEventListener("month-rendered", (e) => {
|
|
118
120
|
const events = {};
|
|
119
|
-
|
|
121
|
+
|
|
120
122
|
// Generate events for every other day
|
|
121
123
|
for (let i = 1; i <= 30; i += 2) {
|
|
122
|
-
const types = [
|
|
124
|
+
const types = ["primary", "info", "warning", "danger"];
|
|
123
125
|
const eventCount = Math.floor(Math.random() * 3) + 1;
|
|
124
126
|
events[i] = [];
|
|
125
|
-
|
|
127
|
+
|
|
126
128
|
for (let j = 0; j < eventCount; j++) {
|
|
127
129
|
events[i].push({
|
|
128
130
|
title: `Event ${j + 1} on Day ${i}`,
|
|
129
|
-
type: types[Math.floor(Math.random() * types.length)]
|
|
131
|
+
type: types[Math.floor(Math.random() * types.length)],
|
|
130
132
|
});
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
|
-
|
|
135
|
+
|
|
134
136
|
e.detail.fill(events);
|
|
135
137
|
});
|
|
136
138
|
}
|
|
137
139
|
}, 0);
|
|
138
|
-
|
|
139
|
-
return html`
|
|
140
|
-
|
|
141
|
-
`;
|
|
142
|
-
}
|
|
140
|
+
|
|
141
|
+
return html` <pds-calendar id="many-events-calendar"></pds-calendar> `;
|
|
142
|
+
},
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
export const EventTypes = {
|
|
146
146
|
render: () => {
|
|
147
147
|
setTimeout(() => {
|
|
148
|
-
const calendar = document.querySelector(
|
|
148
|
+
const calendar = document.querySelector("#event-types-calendar");
|
|
149
149
|
if (calendar) {
|
|
150
|
-
calendar.addEventListener(
|
|
150
|
+
calendar.addEventListener("month-rendered", (e) => {
|
|
151
151
|
e.detail.fill({
|
|
152
|
-
5: [
|
|
153
|
-
|
|
154
|
-
],
|
|
155
|
-
|
|
156
|
-
{ title: 'Info Event', type: 'info' }
|
|
157
|
-
],
|
|
158
|
-
15: [
|
|
159
|
-
{ title: 'Warning Event', type: 'warning' }
|
|
160
|
-
],
|
|
161
|
-
20: [
|
|
162
|
-
{ title: 'Danger Event', type: 'danger' }
|
|
163
|
-
],
|
|
152
|
+
5: [{ title: "Primary Event", type: "primary" }],
|
|
153
|
+
10: [{ title: "Info Event", type: "info" }],
|
|
154
|
+
15: [{ title: "Warning Event", type: "warning" }],
|
|
155
|
+
20: [{ title: "Danger Event", type: "danger" }],
|
|
164
156
|
25: [
|
|
165
|
-
{ title:
|
|
166
|
-
{ title:
|
|
167
|
-
{ title:
|
|
168
|
-
{ title:
|
|
169
|
-
]
|
|
157
|
+
{ title: "Primary Event", type: "primary" },
|
|
158
|
+
{ title: "Info Event", type: "info" },
|
|
159
|
+
{ title: "Warning Event", type: "warning" },
|
|
160
|
+
{ title: "Danger Event", type: "danger" },
|
|
161
|
+
],
|
|
170
162
|
});
|
|
171
163
|
});
|
|
172
164
|
}
|
|
173
165
|
}, 0);
|
|
174
|
-
|
|
166
|
+
|
|
175
167
|
return html`
|
|
176
168
|
<div class="stack-lg">
|
|
177
169
|
<pds-calendar id="event-types-calendar"></pds-calendar>
|
|
178
|
-
|
|
170
|
+
|
|
179
171
|
<div class="card">
|
|
180
172
|
<h3>Event Types</h3>
|
|
181
173
|
<ul>
|
|
182
|
-
<li
|
|
183
|
-
|
|
174
|
+
<li>
|
|
175
|
+
<strong>Primary:</strong> Blue - Main events and important tasks
|
|
176
|
+
</li>
|
|
177
|
+
<li>
|
|
178
|
+
<strong>Info:</strong> Light blue - Informational items and notes
|
|
179
|
+
</li>
|
|
184
180
|
<li><strong>Warning:</strong> Yellow - Attention required items</li>
|
|
185
|
-
<li
|
|
181
|
+
<li>
|
|
182
|
+
<strong>Danger:</strong> Red - Critical deadlines and alerts
|
|
183
|
+
</li>
|
|
186
184
|
</ul>
|
|
187
185
|
</div>
|
|
188
186
|
</div>
|
|
189
187
|
`;
|
|
190
|
-
}
|
|
188
|
+
},
|
|
191
189
|
};
|
|
192
190
|
|
|
193
191
|
export const DynamicEvents = {
|
|
194
192
|
render: () => {
|
|
195
|
-
setTimeout(() => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
193
|
+
setTimeout(async () => {
|
|
194
|
+
if (typeof customElements?.whenDefined === "function") {
|
|
195
|
+
await customElements.whenDefined("pds-calendar");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const resolveCalendar = async () => {
|
|
199
|
+
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
200
|
+
const candidate = document.querySelector("#dynamic-calendar");
|
|
201
|
+
if (candidate?.shadowRoot) return candidate;
|
|
202
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
203
|
+
}
|
|
204
|
+
return document.querySelector("#dynamic-calendar");
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const calendar = await resolveCalendar();
|
|
208
|
+
if (!calendar || calendar.__dynamicEventSetup) return;
|
|
209
|
+
calendar.__dynamicEventSetup = true;
|
|
210
|
+
|
|
202
211
|
let currentEvents = {
|
|
203
|
-
10: [
|
|
212
|
+
10: [
|
|
213
|
+
{
|
|
214
|
+
title: "Existing Event",
|
|
215
|
+
type: "primary",
|
|
216
|
+
time: "09:00",
|
|
217
|
+
duration: 30,
|
|
218
|
+
},
|
|
219
|
+
],
|
|
204
220
|
};
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
221
|
+
|
|
222
|
+
const eventTypes = ["primary", "info", "warning", "danger"];
|
|
223
|
+
|
|
224
|
+
const normalizeEvent = (event = {}) => {
|
|
225
|
+
const rawTitle = String(event.title || "").trim();
|
|
226
|
+
const rawTime = String(event.time || "").trim();
|
|
227
|
+
const parsedDuration = Number(event.duration);
|
|
228
|
+
const normalizedDuration =
|
|
229
|
+
Number.isFinite(parsedDuration) && parsedDuration > 0
|
|
230
|
+
? Math.round(parsedDuration)
|
|
231
|
+
: null;
|
|
232
|
+
const rawType = String(event.type || "info").trim().toLowerCase();
|
|
233
|
+
const normalizedType = eventTypes.includes(rawType) ? rawType : "info";
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
title: rawTitle,
|
|
237
|
+
time: rawTime,
|
|
238
|
+
duration: normalizedDuration,
|
|
239
|
+
type: normalizedType,
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const toCalendarEvent = (event = {}) => {
|
|
244
|
+
const normalized = normalizeEvent(event);
|
|
245
|
+
const hasDuration =
|
|
246
|
+
Number.isFinite(normalized.duration) && normalized.duration > 0;
|
|
247
|
+
const titlePrefix = normalized.time
|
|
248
|
+
? hasDuration
|
|
249
|
+
? `${normalized.time} (${normalized.duration}m) · `
|
|
250
|
+
: `${normalized.time} · `
|
|
251
|
+
: "";
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
title: `${titlePrefix}${normalized.title || "Untitled"}`,
|
|
255
|
+
type: normalized.type,
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const getDayEvents = (day) => {
|
|
260
|
+
if (!currentEvents[day]) {
|
|
261
|
+
currentEvents[day] = [];
|
|
262
|
+
}
|
|
263
|
+
return currentEvents[day];
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const getTaskContextFromEvent = (event) => {
|
|
267
|
+
const task = event.target?.closest?.(".task");
|
|
268
|
+
if (!task) return null;
|
|
269
|
+
|
|
270
|
+
const dayCell = task.closest(".day[data-day]");
|
|
271
|
+
const day = Number.parseInt(dayCell?.dataset?.day || "", 10);
|
|
272
|
+
if (!Number.isInteger(day) || day < 1) return null;
|
|
273
|
+
|
|
274
|
+
const taskItems = [...dayCell.querySelectorAll(".task")];
|
|
275
|
+
const eventIndex = taskItems.indexOf(task);
|
|
276
|
+
if (eventIndex < 0) return null;
|
|
277
|
+
|
|
278
|
+
return { day, eventIndex };
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const getDayFromEvent = (event) => {
|
|
282
|
+
const path = event.composedPath?.() || [];
|
|
283
|
+
for (const node of path) {
|
|
284
|
+
const dayValue = node?.dataset?.day;
|
|
285
|
+
const dayNumber = Number.parseInt(dayValue || "", 10);
|
|
286
|
+
if (Number.isInteger(dayNumber) && dayNumber > 0) return dayNumber;
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const openEventDialog = async ({ day, eventIndex = null }) => {
|
|
292
|
+
const existingEvents = getDayEvents(day);
|
|
293
|
+
const hasExistingEvent =
|
|
294
|
+
Number.isInteger(eventIndex)
|
|
295
|
+
&& eventIndex >= 0
|
|
296
|
+
&& eventIndex < existingEvents.length;
|
|
297
|
+
const existingEvent = hasExistingEvent
|
|
298
|
+
? normalizeEvent(existingEvents[eventIndex])
|
|
299
|
+
: null;
|
|
300
|
+
|
|
301
|
+
const selectedDate = new Date(calendar.year, calendar.month, day);
|
|
302
|
+
const selectedDateLabel = selectedDate.toLocaleDateString(undefined, {
|
|
303
|
+
weekday: "short",
|
|
304
|
+
month: "short",
|
|
305
|
+
day: "numeric",
|
|
306
|
+
year: "numeric",
|
|
210
307
|
});
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (!currentEvents[day]) {
|
|
221
|
-
currentEvents[day] = [];
|
|
222
|
-
}
|
|
223
|
-
currentEvents[day].push({ title, type });
|
|
224
|
-
|
|
225
|
-
// Trigger re-render
|
|
226
|
-
calendar.refresh();
|
|
227
|
-
|
|
228
|
-
// Clear inputs
|
|
229
|
-
titleInput.value = '';
|
|
230
|
-
dayInput.value = '';
|
|
308
|
+
|
|
309
|
+
const buttons = hasExistingEvent
|
|
310
|
+
? {
|
|
311
|
+
delete: { name: "Delete event" },
|
|
312
|
+
ok: {
|
|
313
|
+
name: "Save changes",
|
|
314
|
+
primary: true,
|
|
315
|
+
},
|
|
316
|
+
cancel: { name: "Cancel", cancel: true },
|
|
231
317
|
}
|
|
318
|
+
: {
|
|
319
|
+
ok: {
|
|
320
|
+
name: "Add event",
|
|
321
|
+
primary: true,
|
|
322
|
+
},
|
|
323
|
+
cancel: { name: "Cancel", cancel: true },
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const result = await PDS.ask(
|
|
327
|
+
html`
|
|
328
|
+
<form method="dialog" class="stack-md min-w-sm">
|
|
329
|
+
<pds-form id="calendar-event-form" data-required hide-actions></pds-form>
|
|
330
|
+
<input type="hidden" name="eventPayload" value="" />
|
|
331
|
+
<input type="hidden" name="eventAction" value="upsert" />
|
|
332
|
+
</form>
|
|
333
|
+
`,
|
|
334
|
+
{
|
|
335
|
+
title: hasExistingEvent
|
|
336
|
+
? `Edit event for ${selectedDateLabel}`
|
|
337
|
+
: `Add event for ${selectedDateLabel}`,
|
|
338
|
+
useForm: true,
|
|
339
|
+
buttons,
|
|
340
|
+
rendered(dialog) {
|
|
341
|
+
const hostForm = dialog.querySelector("form");
|
|
342
|
+
const pdsForm = dialog.querySelector("#calendar-event-form");
|
|
343
|
+
const hidden = dialog.querySelector('input[name="eventPayload"]');
|
|
344
|
+
const actionField = dialog.querySelector('input[name="eventAction"]');
|
|
345
|
+
const deleteButton = dialog.querySelector('button[value="delete"]');
|
|
346
|
+
|
|
347
|
+
if (!pdsForm || !hidden) return;
|
|
348
|
+
|
|
349
|
+
pdsForm.jsonSchema = {
|
|
350
|
+
type: "object",
|
|
351
|
+
required: ["title"],
|
|
352
|
+
properties: {
|
|
353
|
+
title: {
|
|
354
|
+
type: "string",
|
|
355
|
+
title: "Title",
|
|
356
|
+
minLength: 1,
|
|
357
|
+
examples: ["Sprint planning"],
|
|
358
|
+
},
|
|
359
|
+
time: {
|
|
360
|
+
type: "string",
|
|
361
|
+
format: "time",
|
|
362
|
+
title: "Time",
|
|
363
|
+
examples: ["09:30"],
|
|
364
|
+
},
|
|
365
|
+
duration: {
|
|
366
|
+
type: "number",
|
|
367
|
+
title: "Duration (minutes)",
|
|
368
|
+
minimum: 1,
|
|
369
|
+
examples: ["30"],
|
|
370
|
+
},
|
|
371
|
+
type: {
|
|
372
|
+
type: "string",
|
|
373
|
+
title: "Type",
|
|
374
|
+
oneOf: [
|
|
375
|
+
{ const: "primary", title: "Primary" },
|
|
376
|
+
{ const: "info", title: "Info" },
|
|
377
|
+
{ const: "warning", title: "Warning" },
|
|
378
|
+
{ const: "danger", title: "Danger" },
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
pdsForm.uiSchema = {
|
|
385
|
+
"/title": { "ui:icon": "calendar" },
|
|
386
|
+
"/time": { "ui:autocomplete": "off" },
|
|
387
|
+
"/duration": { "ui:icon": "timer" },
|
|
388
|
+
"/type": { "ui:widget": "radio", "ui:class": "buttons" },
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
pdsForm.values = {
|
|
392
|
+
title: existingEvent?.title || "",
|
|
393
|
+
time: existingEvent?.time || "",
|
|
394
|
+
duration: existingEvent?.duration ?? null,
|
|
395
|
+
type: existingEvent?.type || "info",
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
const syncHidden = (detail) => {
|
|
399
|
+
let payload = detail?.json;
|
|
400
|
+
if (!payload && typeof pdsForm.serialize === "function") {
|
|
401
|
+
payload = pdsForm.serialize()?.json;
|
|
402
|
+
}
|
|
403
|
+
hidden.value = JSON.stringify(payload || {});
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
if (deleteButton && hostForm) {
|
|
407
|
+
deleteButton.className = "btn-danger btn-outline btn-sm";
|
|
408
|
+
deleteButton.addEventListener("click", (event) => {
|
|
409
|
+
event.preventDefault();
|
|
410
|
+
if (actionField) {
|
|
411
|
+
actionField.value = "delete";
|
|
412
|
+
}
|
|
413
|
+
const submitter =
|
|
414
|
+
hostForm.querySelector('button[value="ok"]') || undefined;
|
|
415
|
+
hostForm.requestSubmit(submitter);
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
pdsForm.addEventListener("pw:value-change", (event) => {
|
|
420
|
+
syncHidden(event.detail);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
pdsForm.addEventListener("pw:submit", (event) => {
|
|
424
|
+
event.preventDefault();
|
|
425
|
+
if (actionField) {
|
|
426
|
+
actionField.value = "upsert";
|
|
427
|
+
}
|
|
428
|
+
syncHidden(event.detail);
|
|
429
|
+
const submitter =
|
|
430
|
+
hostForm?.querySelector('button[value="ok"]') || undefined;
|
|
431
|
+
hostForm?.requestSubmit(submitter);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
hostForm?.addEventListener(
|
|
435
|
+
"submit",
|
|
436
|
+
(event) => {
|
|
437
|
+
if (event.submitter?.value === "ok") {
|
|
438
|
+
syncHidden();
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
true
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
customElements.whenDefined("pds-form").then(() => {
|
|
445
|
+
if (typeof pdsForm.serialize === "function") {
|
|
446
|
+
const initial = pdsForm.serialize()?.json || {};
|
|
447
|
+
hidden.value = JSON.stringify(initial);
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
},
|
|
451
|
+
}
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
if (!(result instanceof FormData)) return;
|
|
455
|
+
|
|
456
|
+
const action = String(result.get("eventAction") || "upsert");
|
|
457
|
+
|
|
458
|
+
if (action === "delete" && hasExistingEvent) {
|
|
459
|
+
const eventsForDay = getDayEvents(day);
|
|
460
|
+
eventsForDay.splice(eventIndex, 1);
|
|
461
|
+
if (!eventsForDay.length) {
|
|
462
|
+
delete currentEvents[day];
|
|
463
|
+
}
|
|
464
|
+
calendar.refresh();
|
|
465
|
+
await PDS.toast(`Deleted event on ${selectedDateLabel}.`, {
|
|
466
|
+
type: "warning",
|
|
467
|
+
});
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const raw = result.get("eventPayload");
|
|
472
|
+
let payload = null;
|
|
473
|
+
try {
|
|
474
|
+
payload = raw ? JSON.parse(String(raw)) : null;
|
|
475
|
+
} catch {
|
|
476
|
+
payload = null;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const normalized = normalizeEvent(payload || {});
|
|
480
|
+
|
|
481
|
+
if (hasExistingEvent) {
|
|
482
|
+
currentEvents[day][eventIndex] = normalized;
|
|
483
|
+
} else {
|
|
484
|
+
getDayEvents(day).push(normalized);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
calendar.refresh();
|
|
488
|
+
await PDS.toast(
|
|
489
|
+
hasExistingEvent
|
|
490
|
+
? `Updated event on ${selectedDateLabel}.`
|
|
491
|
+
: `Added event on ${selectedDateLabel}.`,
|
|
492
|
+
{
|
|
493
|
+
type: "success",
|
|
494
|
+
}
|
|
495
|
+
);
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
calendar.addEventListener("month-rendered", (e) => {
|
|
499
|
+
const filledEvents = Object.fromEntries(
|
|
500
|
+
Object.entries(currentEvents).map(([day, events]) => [
|
|
501
|
+
day,
|
|
502
|
+
events.map((item) => toCalendarEvent(item)),
|
|
503
|
+
])
|
|
504
|
+
);
|
|
505
|
+
e.detail.fill(filledEvents);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
const bindInteractions = () => {
|
|
509
|
+
if (!calendar.isConnected) return;
|
|
510
|
+
|
|
511
|
+
const shadow = calendar.shadowRoot;
|
|
512
|
+
if (!shadow) {
|
|
513
|
+
requestAnimationFrame(bindInteractions);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (shadow.__dynamicEventInteractionsSetup) return;
|
|
518
|
+
shadow.__dynamicEventInteractionsSetup = true;
|
|
519
|
+
|
|
520
|
+
shadow.addEventListener("dblclick", (event) => {
|
|
521
|
+
const taskContext = getTaskContextFromEvent(event);
|
|
522
|
+
if (taskContext) {
|
|
523
|
+
openEventDialog(taskContext);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const day = getDayFromEvent(event);
|
|
528
|
+
if (!day) return;
|
|
529
|
+
openEventDialog({ day });
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
let longPressTimer = null;
|
|
533
|
+
const clearLongPress = () => {
|
|
534
|
+
clearTimeout(longPressTimer);
|
|
535
|
+
longPressTimer = null;
|
|
232
536
|
};
|
|
233
|
-
|
|
537
|
+
|
|
538
|
+
shadow.addEventListener("pointerdown", (event) => {
|
|
539
|
+
const taskContext = getTaskContextFromEvent(event);
|
|
540
|
+
const day = taskContext?.day || getDayFromEvent(event);
|
|
541
|
+
if (!day) return;
|
|
542
|
+
clearLongPress();
|
|
543
|
+
longPressTimer = setTimeout(() => {
|
|
544
|
+
if (taskContext) {
|
|
545
|
+
openEventDialog(taskContext);
|
|
546
|
+
} else {
|
|
547
|
+
openEventDialog({ day });
|
|
548
|
+
}
|
|
549
|
+
clearLongPress();
|
|
550
|
+
}, 550);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
shadow.addEventListener("pointerup", clearLongPress);
|
|
554
|
+
shadow.addEventListener("pointercancel", clearLongPress);
|
|
555
|
+
shadow.addEventListener("pointerleave", clearLongPress);
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
bindInteractions();
|
|
234
559
|
}, 0);
|
|
235
|
-
|
|
560
|
+
|
|
236
561
|
return html`
|
|
237
562
|
<div class="stack-lg">
|
|
238
563
|
<div class="card">
|
|
239
564
|
<h3>Add Events Dynamically</h3>
|
|
240
|
-
<
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
</div>
|
|
245
|
-
<div class="form-group flex-1">
|
|
246
|
-
<label for="event-title-input">Title</label>
|
|
247
|
-
<input id="event-title-input" type="text" placeholder="Event title" class="input" />
|
|
248
|
-
</div>
|
|
249
|
-
<div class="form-group">
|
|
250
|
-
<label for="event-type-select">Type</label>
|
|
251
|
-
<select id="event-type-select" class="input">
|
|
252
|
-
<option value="primary">Primary</option>
|
|
253
|
-
<option value="info">Info</option>
|
|
254
|
-
<option value="warning">Warning</option>
|
|
255
|
-
<option value="danger">Danger</option>
|
|
256
|
-
</select>
|
|
257
|
-
</div>
|
|
258
|
-
<button id="add-event-btn" class="btn-primary">Add Event</button>
|
|
259
|
-
</div>
|
|
565
|
+
<p class="text-muted">
|
|
566
|
+
Double-click a day (or long-press on touch) to add an event.
|
|
567
|
+
Double-click an existing event to edit or delete it.
|
|
568
|
+
</p>
|
|
260
569
|
</div>
|
|
261
|
-
|
|
570
|
+
|
|
262
571
|
<pds-calendar id="dynamic-calendar"></pds-calendar>
|
|
263
572
|
</div>
|
|
264
573
|
`;
|
|
265
|
-
}
|
|
574
|
+
},
|
|
266
575
|
};
|
|
267
576
|
|
|
577
|
+
|
|
268
578
|
export const FormParticipation = {
|
|
269
579
|
render: () => {
|
|
270
580
|
setTimeout(() => {
|
|
271
|
-
const form = document.querySelector(
|
|
272
|
-
const calendar = document.querySelector(
|
|
273
|
-
const output = document.querySelector(
|
|
581
|
+
const form = document.querySelector("#calendar-form-example");
|
|
582
|
+
const calendar = document.querySelector("#form-calendar");
|
|
583
|
+
const output = document.querySelector("#calendar-form-output");
|
|
274
584
|
|
|
275
585
|
if (!form || !calendar || !output) return;
|
|
276
586
|
|
|
@@ -278,22 +588,22 @@ export const FormParticipation = {
|
|
|
278
588
|
output.textContent = value;
|
|
279
589
|
};
|
|
280
590
|
|
|
281
|
-
calendar.addEventListener(
|
|
591
|
+
calendar.addEventListener("month-rendered", (event) => {
|
|
282
592
|
event.detail.fill({
|
|
283
|
-
4: [{ title:
|
|
284
|
-
9: [{ title:
|
|
285
|
-
16: [{ title:
|
|
286
|
-
24: [{ title:
|
|
593
|
+
4: [{ title: "Planning", type: "primary" }],
|
|
594
|
+
9: [{ title: "Architecture", type: "info" }],
|
|
595
|
+
16: [{ title: "Review", type: "warning" }],
|
|
596
|
+
24: [{ title: "Deadline", type: "danger" }],
|
|
287
597
|
});
|
|
288
598
|
});
|
|
289
599
|
|
|
290
|
-
calendar.value =
|
|
600
|
+
calendar.value = "";
|
|
291
601
|
|
|
292
|
-
form.addEventListener(
|
|
602
|
+
form.addEventListener("submit", (event) => {
|
|
293
603
|
event.preventDefault();
|
|
294
604
|
|
|
295
605
|
if (!form.reportValidity()) {
|
|
296
|
-
writeOutput(
|
|
606
|
+
writeOutput("Form invalid: pds-calendar requires a date.");
|
|
297
607
|
return;
|
|
298
608
|
}
|
|
299
609
|
|
|
@@ -302,10 +612,10 @@ export const FormParticipation = {
|
|
|
302
612
|
writeOutput(JSON.stringify(payload, null, 2));
|
|
303
613
|
});
|
|
304
614
|
|
|
305
|
-
form.addEventListener(
|
|
615
|
+
form.addEventListener("reset", () => {
|
|
306
616
|
setTimeout(() => {
|
|
307
|
-
calendar.value =
|
|
308
|
-
writeOutput(
|
|
617
|
+
calendar.value = "";
|
|
618
|
+
writeOutput("Form reset complete.");
|
|
309
619
|
}, 0);
|
|
310
620
|
});
|
|
311
621
|
}, 0);
|
|
@@ -314,11 +624,12 @@ export const FormParticipation = {
|
|
|
314
624
|
<section class="stack-lg card">
|
|
315
625
|
<h3>Form Participation</h3>
|
|
316
626
|
<p class="text-muted">
|
|
317
|
-
Pick a meeting date directly in <code><pds-calendar></code>.
|
|
318
|
-
|
|
627
|
+
Pick a meeting date directly in <code><pds-calendar></code>.
|
|
628
|
+
This compact mode shows only day numbers; event days are color-coded
|
|
629
|
+
and no event text is rendered.
|
|
319
630
|
</p>
|
|
320
631
|
|
|
321
|
-
<form id="calendar-form-example"
|
|
632
|
+
<form id="calendar-form-example">
|
|
322
633
|
<pds-calendar
|
|
323
634
|
id="form-calendar"
|
|
324
635
|
compact
|
|
@@ -333,8 +644,179 @@ export const FormParticipation = {
|
|
|
333
644
|
</div>
|
|
334
645
|
</form>
|
|
335
646
|
|
|
336
|
-
<pre id="calendar-form-output" class="card surface-subtle">
|
|
647
|
+
<pre id="calendar-form-output" class="card surface-subtle">
|
|
648
|
+
Submit the form to inspect payload.</pre
|
|
649
|
+
>
|
|
650
|
+
</section>
|
|
651
|
+
`;
|
|
652
|
+
},
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
export const EventDetailsPanel = {
|
|
656
|
+
render: () => {
|
|
657
|
+
setTimeout(() => {
|
|
658
|
+
const calendar = document.querySelector("#details-panel-calendar");
|
|
659
|
+
const detailsHost = document.querySelector("#event-details-panel");
|
|
660
|
+
if (!calendar || !detailsHost) return;
|
|
661
|
+
|
|
662
|
+
const eventsByDay = {
|
|
663
|
+
3: [
|
|
664
|
+
{
|
|
665
|
+
title: "Sprint Planning",
|
|
666
|
+
type: "primary",
|
|
667
|
+
time: "09:00",
|
|
668
|
+
description: "Plan sprint scope and assign owners.",
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
title: "Design Sync",
|
|
672
|
+
type: "info",
|
|
673
|
+
time: "14:00",
|
|
674
|
+
description: "Review UI updates and interaction details.",
|
|
675
|
+
},
|
|
676
|
+
],
|
|
677
|
+
8: [
|
|
678
|
+
{
|
|
679
|
+
title: "Dependency Audit",
|
|
680
|
+
type: "warning",
|
|
681
|
+
time: "11:00",
|
|
682
|
+
description: "Audit package updates and pending migrations.",
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
12: [
|
|
686
|
+
{
|
|
687
|
+
title: "Release Gate",
|
|
688
|
+
type: "danger",
|
|
689
|
+
time: "16:30",
|
|
690
|
+
description: "Final go/no-go check for release readiness.",
|
|
691
|
+
},
|
|
692
|
+
],
|
|
693
|
+
19: [
|
|
694
|
+
{
|
|
695
|
+
title: "Customer Feedback Review",
|
|
696
|
+
type: "info",
|
|
697
|
+
time: "10:30",
|
|
698
|
+
description: "Prioritize recent feature requests and defects.",
|
|
699
|
+
},
|
|
700
|
+
],
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
const renderDetails = (entry, day) => {
|
|
704
|
+
if (!entry) {
|
|
705
|
+
detailsHost.innerHTML = "";
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const selectedDate = new Date(calendar.year, calendar.month, day);
|
|
710
|
+
const selectedDateLabel = selectedDate.toLocaleDateString(undefined, {
|
|
711
|
+
weekday: "short",
|
|
712
|
+
month: "short",
|
|
713
|
+
day: "numeric",
|
|
714
|
+
year: "numeric",
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
detailsHost.innerHTML = `
|
|
718
|
+
<div class="stack-sm">
|
|
719
|
+
<p>${selectedDateLabel}</p>
|
|
720
|
+
<h3>${entry.title}</h3>
|
|
721
|
+
<p class="text-muted">${entry.time}</p>
|
|
722
|
+
<p>${entry.description}</p>
|
|
723
|
+
</div>
|
|
724
|
+
`;
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
const renderDayDetails = (day) => {
|
|
728
|
+
const selectedDate = new Date(calendar.year, calendar.month, day);
|
|
729
|
+
const selectedDateLabel = selectedDate.toLocaleDateString(undefined, {
|
|
730
|
+
weekday: "short",
|
|
731
|
+
month: "short",
|
|
732
|
+
day: "numeric",
|
|
733
|
+
year: "numeric",
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
const list = eventsByDay[day] || [];
|
|
737
|
+
if (!list.length) {
|
|
738
|
+
detailsHost.innerHTML = `
|
|
739
|
+
<section class="flex flex-col items-center">
|
|
740
|
+
|
|
741
|
+
<h5>No events on ${selectedDateLabel}.</h5>
|
|
742
|
+
<small class="text-muted">Select another day in the celndar.
|
|
743
|
+
</small>
|
|
744
|
+
|
|
745
|
+
</section>`;
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
detailsHost.innerHTML = `
|
|
750
|
+
<div class="stack-sm">
|
|
751
|
+
<p>${selectedDateLabel}</p>
|
|
752
|
+
${list
|
|
753
|
+
.map(
|
|
754
|
+
(entry) => `
|
|
755
|
+
<article>
|
|
756
|
+
<header>
|
|
757
|
+
<h4>${entry.title}</h4>
|
|
758
|
+
<small class="text-muted">${entry.time}</small>
|
|
759
|
+
</header>
|
|
760
|
+
<p>${entry.description}</p>
|
|
761
|
+
</article>
|
|
762
|
+
`,
|
|
763
|
+
)
|
|
764
|
+
.join("")}
|
|
765
|
+
</div>
|
|
766
|
+
`;
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
calendar.addEventListener("month-rendered", (event) => {
|
|
770
|
+
event.detail.fill(eventsByDay);
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
if (!calendar.dataset.detailsClickBound) {
|
|
774
|
+
calendar.dataset.detailsClickBound = "true";
|
|
775
|
+
|
|
776
|
+
calendar.shadowRoot?.addEventListener("change", (event) => {
|
|
777
|
+
const radio = event.target?.closest?.(".day-radio-input[data-day]");
|
|
778
|
+
if (!radio) return;
|
|
779
|
+
|
|
780
|
+
const day = Number.parseInt(radio.dataset.day || "", 10);
|
|
781
|
+
if (!Number.isInteger(day)) return;
|
|
782
|
+
|
|
783
|
+
renderDayDetails(day);
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
calendar.shadowRoot?.addEventListener("click", (event) => {
|
|
787
|
+
const task = event.target?.closest?.(".task");
|
|
788
|
+
if (!task) return;
|
|
789
|
+
|
|
790
|
+
const dayCell = task.closest(".day[data-day]");
|
|
791
|
+
const day = Number.parseInt(dayCell?.dataset?.day || "", 10);
|
|
792
|
+
if (!Number.isInteger(day)) return;
|
|
793
|
+
|
|
794
|
+
const taskItems = [...dayCell.querySelectorAll(".task")];
|
|
795
|
+
const taskIndex = taskItems.indexOf(task);
|
|
796
|
+
if (taskIndex < 0) return;
|
|
797
|
+
|
|
798
|
+
const details = eventsByDay[day]?.[taskIndex] || null;
|
|
799
|
+
renderDetails(details, day);
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
renderDetails(null);
|
|
804
|
+
}, 0);
|
|
805
|
+
|
|
806
|
+
return html`
|
|
807
|
+
<section>
|
|
808
|
+
<header>
|
|
809
|
+
<h3>Event Calendar</h3>
|
|
810
|
+
<small class="text-muted"
|
|
811
|
+
>Click calendar to show event details.</small
|
|
812
|
+
>
|
|
813
|
+
</header>
|
|
814
|
+
<article class="flex gap-lg flex-wrap card max-w-md">
|
|
815
|
+
<pds-calendar id="details-panel-calendar" compact></pds-calendar>
|
|
816
|
+
<article id="event-details-panel"></article>
|
|
817
|
+
</article>
|
|
818
|
+
|
|
337
819
|
</section>
|
|
338
820
|
`;
|
|
339
|
-
}
|
|
821
|
+
},
|
|
340
822
|
};
|