259 lines
9.2 KiB
HTML
259 lines
9.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Rating Stars Widget Demo</title>
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
max-width: 800px;
|
|
margin: 40px auto;
|
|
padding: 20px;
|
|
background: #f5f5f5;
|
|
}
|
|
.demo-section {
|
|
background: white;
|
|
padding: 30px;
|
|
margin-bottom: 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
h1 {
|
|
color: #333;
|
|
margin-bottom: 10px;
|
|
}
|
|
h2 {
|
|
color: #666;
|
|
margin-top: 0;
|
|
margin-bottom: 20px;
|
|
font-size: 18px;
|
|
}
|
|
.demo-item {
|
|
margin-bottom: 30px;
|
|
}
|
|
.demo-label {
|
|
font-weight: 600;
|
|
margin-bottom: 10px;
|
|
color: #555;
|
|
}
|
|
.demo-description {
|
|
font-size: 14px;
|
|
color: #777;
|
|
margin-bottom: 10px;
|
|
}
|
|
.selected-value {
|
|
margin-top: 10px;
|
|
padding: 10px;
|
|
background: #e3f2fd;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
color: #1976d2;
|
|
}
|
|
|
|
/* Include the rating stars styles inline for demo */
|
|
.rating-stars-container {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
outline: none;
|
|
}
|
|
|
|
.rating-stars-container:focus {
|
|
outline: 2px solid #007bff;
|
|
outline-offset: 4px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.rating-star {
|
|
display: inline-block;
|
|
line-height: 1;
|
|
transition: all 0.2s ease;
|
|
user-select: none;
|
|
}
|
|
|
|
.rating-star.rating-star-filled {
|
|
color: #ffc107;
|
|
}
|
|
|
|
.rating-star.rating-star-empty {
|
|
color: #e0e0e0;
|
|
}
|
|
|
|
.rating-star.rating-star-interactive {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.rating-star.rating-star-interactive:hover {
|
|
transform: scale(1.15);
|
|
filter: brightness(1.1);
|
|
}
|
|
|
|
.rating-star.rating-star-focused {
|
|
outline: 2px solid #007bff;
|
|
outline-offset: 2px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.rating-stars-small .rating-star {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.rating-stars-medium .rating-star {
|
|
font-size: 24px;
|
|
}
|
|
|
|
.rating-stars-large .rating-star {
|
|
font-size: 36px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.rating-stars-container {
|
|
gap: 8px;
|
|
}
|
|
|
|
.rating-star {
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="demo-section">
|
|
<h1>Rating Stars Widget Demo</h1>
|
|
<h2>Interactive 5-Star Rating Component for Odoo 18</h2>
|
|
</div>
|
|
|
|
<div class="demo-section">
|
|
<div class="demo-item">
|
|
<div class="demo-label">Interactive Rating (Medium Size)</div>
|
|
<div class="demo-description">Click on a star to select a rating. Hover to preview.</div>
|
|
<div class="rating-stars-container rating-stars-medium" id="demo1" tabindex="0">
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
</div>
|
|
<div class="selected-value" id="value1">Selected: 0 stars</div>
|
|
</div>
|
|
|
|
<div class="demo-item">
|
|
<div class="demo-label">Small Size</div>
|
|
<div class="demo-description">Compact version for list views.</div>
|
|
<div class="rating-stars-container rating-stars-small" id="demo2" tabindex="0">
|
|
<span class="rating-star rating-star-filled">★</span>
|
|
<span class="rating-star rating-star-filled">★</span>
|
|
<span class="rating-star rating-star-filled">★</span>
|
|
<span class="rating-star rating-star-empty">☆</span>
|
|
<span class="rating-star rating-star-empty">☆</span>
|
|
</div>
|
|
<div class="selected-value">Pre-selected: 3 stars (readonly)</div>
|
|
</div>
|
|
|
|
<div class="demo-item">
|
|
<div class="demo-label">Large Size</div>
|
|
<div class="demo-description">Prominent display for rating forms.</div>
|
|
<div class="rating-stars-container rating-stars-large" id="demo3" tabindex="0">
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
<span class="rating-star rating-star-empty rating-star-interactive">☆</span>
|
|
</div>
|
|
<div class="selected-value" id="value3">Selected: 0 stars</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="demo-section">
|
|
<h2>Features</h2>
|
|
<ul>
|
|
<li><strong>Click to select:</strong> Click any star to set the rating</li>
|
|
<li><strong>Hover feedback:</strong> Hover over stars to preview the rating</li>
|
|
<li><strong>Keyboard navigation:</strong> Use arrow keys to change rating, Enter to confirm</li>
|
|
<li><strong>Accessibility:</strong> Full ARIA labels and keyboard support</li>
|
|
<li><strong>Touch-friendly:</strong> Optimized for mobile devices with larger touch targets</li>
|
|
<li><strong>Responsive:</strong> Adapts to different screen sizes</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<script>
|
|
// Simple demo JavaScript (the actual OWL component handles this automatically)
|
|
function setupDemo(containerId, valueId) {
|
|
const container = document.getElementById(containerId);
|
|
const valueDisplay = document.getElementById(valueId);
|
|
const stars = container.querySelectorAll('.rating-star');
|
|
let selectedValue = 0;
|
|
|
|
stars.forEach((star, index) => {
|
|
const starValue = index + 1;
|
|
|
|
// Click handler
|
|
star.addEventListener('click', () => {
|
|
selectedValue = starValue;
|
|
updateStars(selectedValue);
|
|
if (valueDisplay) {
|
|
valueDisplay.textContent = `Selected: ${selectedValue} stars`;
|
|
}
|
|
});
|
|
|
|
// Hover handler
|
|
star.addEventListener('mouseenter', () => {
|
|
updateStars(starValue);
|
|
});
|
|
});
|
|
|
|
// Mouse leave handler
|
|
container.addEventListener('mouseleave', () => {
|
|
updateStars(selectedValue);
|
|
});
|
|
|
|
// Keyboard handler
|
|
container.addEventListener('keydown', (e) => {
|
|
if (e.key === 'ArrowRight' || e.key === 'ArrowUp') {
|
|
if (selectedValue < 5) {
|
|
selectedValue++;
|
|
updateStars(selectedValue);
|
|
if (valueDisplay) {
|
|
valueDisplay.textContent = `Selected: ${selectedValue} stars`;
|
|
}
|
|
}
|
|
e.preventDefault();
|
|
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') {
|
|
if (selectedValue > 0) {
|
|
selectedValue--;
|
|
updateStars(selectedValue);
|
|
if (valueDisplay) {
|
|
valueDisplay.textContent = `Selected: ${selectedValue} stars`;
|
|
}
|
|
}
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
|
|
function updateStars(value) {
|
|
stars.forEach((star, index) => {
|
|
if (index < value) {
|
|
star.classList.add('rating-star-filled');
|
|
star.classList.remove('rating-star-empty');
|
|
star.textContent = '★';
|
|
} else {
|
|
star.classList.remove('rating-star-filled');
|
|
star.classList.add('rating-star-empty');
|
|
star.textContent = '☆';
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Initialize demos
|
|
setupDemo('demo1', 'value1');
|
|
setupDemo('demo3', 'value3');
|
|
</script>
|
|
</body>
|
|
</html>
|