Skip to content

Commit d69f5b8

Browse files
authored
1 parent a772e20 commit d69f5b8

File tree

1 file changed

+334
-0
lines changed

1 file changed

+334
-0
lines changed

side-panel-dialog.html

+334
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Side Panel Modal with Dialog Element</title>
7+
<style>
8+
* {
9+
box-sizing: border-box;
10+
}
11+
12+
body {
13+
font-family: Helvetica, Arial, sans-serif;
14+
margin: 0;
15+
padding: 20px;
16+
background-color: #f5f5f5;
17+
}
18+
19+
h1, h2, h3 {
20+
font-weight: 500;
21+
}
22+
23+
.item-list {
24+
display: grid;
25+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
26+
gap: 20px;
27+
margin-top: 20px;
28+
}
29+
30+
.item-card {
31+
background-color: white;
32+
border-radius: 8px;
33+
padding: 20px;
34+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
35+
cursor: pointer;
36+
transition: transform 0.2s, box-shadow 0.2s;
37+
}
38+
39+
.item-card:hover {
40+
transform: translateY(-3px);
41+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
42+
}
43+
44+
.item-card h3 {
45+
margin-top: 0;
46+
margin-bottom: 8px;
47+
}
48+
49+
dialog {
50+
position: fixed;
51+
margin: 0;
52+
padding: 0;
53+
border: none;
54+
width: 80%;
55+
height: 100vh;
56+
top: 0;
57+
right: 0;
58+
left: auto;
59+
background-color: white;
60+
box-shadow: -5px 0 15px rgba(0, 0, 0, 0.1);
61+
transform: translateX(100%);
62+
transition: transform 0.1s cubic-bezier(0.2, 0, 0.38, 0.9);
63+
max-height: inherit; /* Default is calc(100% - 2em - 6px) */
64+
}
65+
66+
dialog::backdrop {
67+
background-color: rgba(0, 0, 0, 0.5);
68+
opacity: 0;
69+
transition: opacity 0.1s cubic-bezier(0.2, 0, 0.38, 0.9);
70+
}
71+
72+
/* We're handling the transform in JavaScript for better control */
73+
74+
dialog[open]::backdrop {
75+
opacity: 1;
76+
}
77+
78+
.dialog-content {
79+
padding: 24px;
80+
height: 100%;
81+
overflow-y: auto;
82+
}
83+
84+
.dialog-header {
85+
display: flex;
86+
justify-content: space-between;
87+
align-items: center;
88+
margin-bottom: 24px;
89+
}
90+
91+
.close-button {
92+
background: none;
93+
border: none;
94+
font-size: 24px;
95+
cursor: pointer;
96+
padding: 8px;
97+
margin: -8px;
98+
color: #555;
99+
}
100+
101+
.item-details h2 {
102+
margin-top: 0;
103+
}
104+
105+
.item-image {
106+
width: 100%;
107+
height: 200px;
108+
background-color: #e0e0e0;
109+
border-radius: 8px;
110+
margin-bottom: 16px;
111+
display: flex;
112+
align-items: center;
113+
justify-content: center;
114+
color: #666;
115+
}
116+
117+
input, textarea {
118+
font-size: 16px;
119+
font-family: Helvetica, Arial, sans-serif;
120+
width: 100%;
121+
padding: 12px;
122+
margin-bottom: 16px;
123+
border: 1px solid #ddd;
124+
border-radius: 4px;
125+
}
126+
127+
textarea {
128+
min-height: 120px;
129+
resize: vertical;
130+
}
131+
132+
.feedback-form h3 {
133+
margin-top: 32px;
134+
}
135+
136+
button.submit-button {
137+
background-color: #4a6cf7;
138+
color: white;
139+
border: none;
140+
border-radius: 4px;
141+
padding: 12px 24px;
142+
font-size: 16px;
143+
cursor: pointer;
144+
transition: background-color 0.2s;
145+
}
146+
147+
button.submit-button:hover {
148+
background-color: #3a5ce6;
149+
}
150+
</style>
151+
</head>
152+
<body>
153+
<header>
154+
<h1>Product catalog</h1>
155+
<p>Click on any item to view details in a side panel</p>
156+
</header>
157+
158+
<main>
159+
<div class="item-list" id="itemList">
160+
<!-- Items will be inserted here by JavaScript -->
161+
</div>
162+
</main>
163+
164+
<dialog id="sidePanel">
165+
<div class="dialog-content">
166+
<div class="dialog-header">
167+
<h2>Item details</h2>
168+
<button class="close-button" id="closeDialog">&times;</button>
169+
</div>
170+
<div class="item-details" id="itemDetails">
171+
<!-- Item details will be inserted here by JavaScript -->
172+
</div>
173+
<div class="feedback-form">
174+
<h3>Send feedback</h3>
175+
<input type="text" placeholder="Your name" id="nameInput">
176+
<textarea placeholder="Your thoughts about this item..." id="feedbackInput"></textarea>
177+
<button class="submit-button" id="submitFeedback">Submit feedback</button>
178+
</div>
179+
</div>
180+
</dialog>
181+
182+
<script type="module">
183+
// Fix animation issues with dialog element
184+
document.addEventListener('DOMContentLoaded', () => {
185+
const dialog = document.getElementById('sidePanel');
186+
// Force browser to recognize dialog animations by adding a small delay
187+
dialog.addEventListener('cancel', (event) => {
188+
event.preventDefault();
189+
});
190+
});
191+
192+
// Sample data
193+
const items = [
194+
{
195+
id: 1,
196+
title: "Wireless headphones",
197+
description: "Premium noise-cancelling wireless headphones with 30-hour battery life and superior sound quality.",
198+
category: "Electronics",
199+
price: "$249.99"
200+
},
201+
{
202+
id: 2,
203+
title: "Ergonomic office chair",
204+
description: "Adjustable office chair with lumbar support, breathable mesh back, and smooth-rolling casters.",
205+
category: "Furniture",
206+
price: "$189.99"
207+
},
208+
{
209+
id: 3,
210+
title: "Smart fitness tracker",
211+
description: "Water-resistant fitness tracker with heart rate monitoring, sleep tracking, and 7-day battery life.",
212+
category: "Wearables",
213+
price: "$79.99"
214+
},
215+
{
216+
id: 4,
217+
title: "Ceramic coffee mug",
218+
description: "Hand-crafted ceramic mug with heat-retention technology and ergonomic handle.",
219+
category: "Kitchenware",
220+
price: "$24.99"
221+
},
222+
{
223+
id: 5,
224+
title: "Portable power bank",
225+
description: "20,000mAh portable charger with fast charging and dual USB ports for multiple devices.",
226+
category: "Electronics",
227+
price: "$49.99"
228+
},
229+
{
230+
id: 6,
231+
title: "Bamboo cutting board",
232+
description: "Sustainable bamboo cutting board with juice groove and non-slip feet.",
233+
category: "Kitchenware",
234+
price: "$34.99"
235+
}
236+
];
237+
238+
// Get DOM elements
239+
const itemList = document.getElementById("itemList");
240+
const sidePanel = document.getElementById("sidePanel");
241+
const closeButton = document.getElementById("closeDialog");
242+
const itemDetails = document.getElementById("itemDetails");
243+
const submitButton = document.getElementById("submitFeedback");
244+
const nameInput = document.getElementById("nameInput");
245+
const feedbackInput = document.getElementById("feedbackInput");
246+
247+
// Render items to the list
248+
function renderItems() {
249+
itemList.innerHTML = "";
250+
items.forEach(item => {
251+
const itemCard = document.createElement("div");
252+
itemCard.className = "item-card";
253+
itemCard.innerHTML = `
254+
<h3>${item.title}</h3>
255+
<p>${item.category}</p>
256+
<p>${item.price}</p>
257+
`;
258+
itemCard.addEventListener("click", () => showItemDetails(item));
259+
itemList.appendChild(itemCard);
260+
});
261+
}
262+
263+
// Show item details in the side panel
264+
function showItemDetails(item) {
265+
itemDetails.innerHTML = `
266+
<div class="item-image">Product Image Placeholder</div>
267+
<h2>${item.title}</h2>
268+
<p><strong>Category:</strong> ${item.category}</p>
269+
<p><strong>Price:</strong> ${item.price}</p>
270+
<p>${item.description}</p>
271+
`;
272+
273+
// Reset any existing transitions
274+
sidePanel.style.transition = 'none';
275+
sidePanel.style.transform = 'translateX(100%)';
276+
277+
// Show the dialog
278+
sidePanel.showModal();
279+
280+
// Force a reflow to ensure the transition will occur
281+
void sidePanel.offsetWidth;
282+
283+
// Restore the transition and apply the transform
284+
sidePanel.style.transition = 'transform 0.1s cubic-bezier(0.2, 0, 0.38, 0.9)';
285+
sidePanel.style.transform = 'translateX(0)';
286+
287+
// Store the current item ID for feedback submission
288+
submitButton.dataset.itemId = item.id;
289+
}
290+
291+
// Close the dialog when clicking the close button
292+
closeButton.addEventListener("click", () => {
293+
animateCloseDialog();
294+
});
295+
296+
// Close the dialog when clicking on the backdrop
297+
sidePanel.addEventListener("click", (event) => {
298+
if (event.target === sidePanel) {
299+
animateCloseDialog();
300+
}
301+
});
302+
303+
// Animate the dialog closing
304+
function animateCloseDialog() {
305+
// Animate the panel sliding out
306+
sidePanel.style.transform = 'translateX(100%)';
307+
308+
// After animation completes, close the dialog
309+
setTimeout(() => {
310+
sidePanel.close();
311+
}, 100);
312+
}
313+
314+
// Handle feedback submission
315+
submitButton.addEventListener("click", () => {
316+
const itemId = submitButton.dataset.itemId;
317+
const name = nameInput.value.trim();
318+
const feedback = feedbackInput.value.trim();
319+
320+
if (name && feedback) {
321+
console.log(`Feedback for item #${itemId}:`, { name, feedback });
322+
alert("Thank you for your feedback!");
323+
nameInput.value = "";
324+
feedbackInput.value = "";
325+
} else {
326+
alert("Please fill out both fields to submit feedback.");
327+
}
328+
});
329+
330+
// Initialize the page
331+
renderItems();
332+
</script>
333+
</body>
334+
</html>

0 commit comments

Comments
 (0)