feat: add advanced UI component macros
Add new macro files for comprehensive UI coverage: - modals.html: modal, confirm_modal, form_modal, slide_over - dropdowns.html: dropdown, context_menu, dropdown_item, select_dropdown - avatars.html: avatar, avatar_with_status, avatar_initials, avatar_group, user_avatar_card - charts.html: chart_card, line_chart, bar_chart, doughnut_chart (Chart.js) - datepicker.html: datepicker, daterange_picker, datetime_picker, time_picker (Flatpickr) Update forms.html with: - password_input: Password field with show/hide toggle and strength indicator - input_with_icon: Input with left/right icon support - file_input: Drag & drop file upload zone Tech stack: Jinja2 + Alpine.js + Tailwind CSS External libs: Chart.js (optional), Flatpickr (optional) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
368
app/templates/shared/macros/charts.html
Normal file
368
app/templates/shared/macros/charts.html
Normal file
@@ -0,0 +1,368 @@
|
||||
{#
|
||||
Chart Macros
|
||||
============
|
||||
Reusable chart components using Chart.js with Alpine.js integration.
|
||||
|
||||
Prerequisites:
|
||||
Add Chart.js CDN to your base template:
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
||||
|
||||
Usage:
|
||||
{% from 'shared/macros/charts.html' import chart_card, line_chart, bar_chart, doughnut_chart %}
|
||||
|
||||
{# Line chart in a card #}
|
||||
{{ chart_card('salesChart', 'Monthly Sales', 'line', chart_config) }}
|
||||
|
||||
{# Standalone bar chart #}
|
||||
{{ bar_chart('ordersChart', chart_data, height='300px') }}
|
||||
#}
|
||||
|
||||
|
||||
{#
|
||||
Chart Card
|
||||
==========
|
||||
A card container with a chart and optional dropdown menu.
|
||||
|
||||
Parameters:
|
||||
- id: Unique chart ID (used for canvas element)
|
||||
- title: Card title
|
||||
- chart_type: 'line' | 'bar' | 'doughnut' | 'pie' (default: 'line')
|
||||
- height: Chart height (default: '300px')
|
||||
- show_menu: Whether to show dropdown menu (default: true)
|
||||
- subtitle: Optional subtitle text
|
||||
#}
|
||||
{% macro chart_card(id, title, chart_type='line', height='300px', show_menu=true, subtitle=none) %}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-xs border border-gray-200 dark:border-gray-700 p-4 sm:p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white">{{ title }}</h3>
|
||||
{% if subtitle %}
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">{{ subtitle }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if show_menu %}
|
||||
<div x-data="{ menuOpen: false }" class="relative">
|
||||
<button
|
||||
@click="menuOpen = !menuOpen"
|
||||
@click.outside="menuOpen = false"
|
||||
class="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div
|
||||
x-show="menuOpen"
|
||||
x-transition
|
||||
class="absolute right-0 mt-1 w-40 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-1 z-10"
|
||||
>
|
||||
{{ caller() if caller is defined else '' }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div style="height: {{ height }};">
|
||||
<canvas id="{{ id }}"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#
|
||||
Line Chart
|
||||
==========
|
||||
A standalone line chart canvas.
|
||||
|
||||
Parameters:
|
||||
- id: Unique chart ID
|
||||
- height: Chart height (default: '300px')
|
||||
- class_extra: Additional CSS classes
|
||||
#}
|
||||
{% macro line_chart(id, height='300px', class_extra='') %}
|
||||
<div class="relative {{ class_extra }}" style="height: {{ height }};">
|
||||
<canvas id="{{ id }}"></canvas>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#
|
||||
Bar Chart
|
||||
=========
|
||||
A standalone bar chart canvas.
|
||||
|
||||
Parameters:
|
||||
- id: Unique chart ID
|
||||
- height: Chart height (default: '300px')
|
||||
- class_extra: Additional CSS classes
|
||||
#}
|
||||
{% macro bar_chart(id, height='300px', class_extra='') %}
|
||||
<div class="relative {{ class_extra }}" style="height: {{ height }};">
|
||||
<canvas id="{{ id }}"></canvas>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#
|
||||
Doughnut Chart
|
||||
==============
|
||||
A standalone doughnut/pie chart canvas.
|
||||
|
||||
Parameters:
|
||||
- id: Unique chart ID
|
||||
- size: Chart size (default: '200px')
|
||||
- class_extra: Additional CSS classes
|
||||
#}
|
||||
{% macro doughnut_chart(id, size='200px', class_extra='') %}
|
||||
<div class="relative inline-block {{ class_extra }}" style="width: {{ size }}; height: {{ size }};">
|
||||
<canvas id="{{ id }}"></canvas>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#
|
||||
Stats Chart Card
|
||||
================
|
||||
A card with a stat value and small sparkline chart.
|
||||
|
||||
Parameters:
|
||||
- id: Unique chart ID
|
||||
- title: Stat title
|
||||
- value_var: Alpine.js variable for the value
|
||||
- trend_var: Alpine.js variable for trend ('up' | 'down' | 'neutral')
|
||||
- trend_value_var: Alpine.js variable for trend percentage
|
||||
- chart_height: Sparkline height (default: '60px')
|
||||
#}
|
||||
{% macro stats_chart_card(id, title, value_var, trend_var=none, trend_value_var=none, chart_height='60px') %}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-xs border border-gray-200 dark:border-gray-700 p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h4 class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ title }}</h4>
|
||||
{% if trend_var %}
|
||||
<span
|
||||
class="inline-flex items-center text-xs font-medium"
|
||||
:class="{
|
||||
'text-green-600': {{ trend_var }} === 'up',
|
||||
'text-red-600': {{ trend_var }} === 'down',
|
||||
'text-gray-500': {{ trend_var }} === 'neutral'
|
||||
}"
|
||||
>
|
||||
<svg
|
||||
x-show="{{ trend_var }} === 'up'"
|
||||
class="w-3 h-3 mr-1"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"/>
|
||||
</svg>
|
||||
<svg
|
||||
x-show="{{ trend_var }} === 'down'"
|
||||
class="w-3 h-3 mr-1"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"/>
|
||||
</svg>
|
||||
<span x-text="{{ trend_value_var }}"></span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-gray-900 dark:text-white mb-3" x-text="{{ value_var }}"></p>
|
||||
<div style="height: {{ chart_height }};">
|
||||
<canvas id="{{ id }}"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#
|
||||
Chart Legend
|
||||
============
|
||||
A custom chart legend.
|
||||
|
||||
Parameters:
|
||||
- items: List of legend items [{'label': '', 'color': ''}]
|
||||
- layout: 'horizontal' | 'vertical' (default: 'horizontal')
|
||||
#}
|
||||
{% macro chart_legend(items, layout='horizontal') %}
|
||||
<div class="flex {{ 'flex-wrap gap-4' if layout == 'horizontal' else 'flex-col gap-2' }} mt-4">
|
||||
{% for item in items %}
|
||||
<div class="flex items-center">
|
||||
<span class="w-3 h-3 rounded-full mr-2" style="background-color: {{ item.color }};"></span>
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">{{ item.label }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#
|
||||
Chart.js Configuration Helper
|
||||
=============================
|
||||
JavaScript template for common chart configurations.
|
||||
|
||||
Include this in a <script> block and customize as needed.
|
||||
#}
|
||||
{% macro chart_config_script() %}
|
||||
<script>
|
||||
// Chart.js default configuration
|
||||
Chart.defaults.font.family = "'Inter', 'Helvetica', 'Arial', sans-serif";
|
||||
Chart.defaults.color = document.documentElement.classList.contains('dark') ? '#9CA3AF' : '#6B7280';
|
||||
|
||||
// Helper function to create responsive chart
|
||||
function createChart(canvasId, type, data, options = {}) {
|
||||
const ctx = document.getElementById(canvasId);
|
||||
if (!ctx) return null;
|
||||
|
||||
const defaultOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: document.documentElement.classList.contains('dark') ? '#374151' : '#ffffff',
|
||||
titleColor: document.documentElement.classList.contains('dark') ? '#F9FAFB' : '#111827',
|
||||
bodyColor: document.documentElement.classList.contains('dark') ? '#D1D5DB' : '#4B5563',
|
||||
borderColor: document.documentElement.classList.contains('dark') ? '#4B5563' : '#E5E7EB',
|
||||
borderWidth: 1,
|
||||
padding: 12,
|
||||
cornerRadius: 8,
|
||||
displayColors: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new Chart(ctx, {
|
||||
type: type,
|
||||
data: data,
|
||||
options: { ...defaultOptions, ...options }
|
||||
});
|
||||
}
|
||||
|
||||
// Line chart preset
|
||||
function createLineChart(canvasId, labels, datasets, options = {}) {
|
||||
const defaultDatasetOptions = {
|
||||
tension: 0.4,
|
||||
borderWidth: 2,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
fill: true
|
||||
};
|
||||
|
||||
const formattedDatasets = datasets.map(ds => ({
|
||||
...defaultDatasetOptions,
|
||||
...ds
|
||||
}));
|
||||
|
||||
return createChart(canvasId, 'line', { labels, datasets: formattedDatasets }, {
|
||||
scales: {
|
||||
x: {
|
||||
grid: { display: false },
|
||||
border: { display: false }
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
color: document.documentElement.classList.contains('dark') ? '#374151' : '#F3F4F6'
|
||||
},
|
||||
border: { display: false },
|
||||
beginAtZero: true
|
||||
}
|
||||
},
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
// Bar chart preset
|
||||
function createBarChart(canvasId, labels, datasets, options = {}) {
|
||||
return createChart(canvasId, 'bar', { labels, datasets }, {
|
||||
scales: {
|
||||
x: {
|
||||
grid: { display: false },
|
||||
border: { display: false }
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
color: document.documentElement.classList.contains('dark') ? '#374151' : '#F3F4F6'
|
||||
},
|
||||
border: { display: false },
|
||||
beginAtZero: true
|
||||
}
|
||||
},
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
// Doughnut chart preset
|
||||
function createDoughnutChart(canvasId, labels, data, colors, options = {}) {
|
||||
return createChart(canvasId, 'doughnut', {
|
||||
labels,
|
||||
datasets: [{
|
||||
data,
|
||||
backgroundColor: colors,
|
||||
borderWidth: 0,
|
||||
hoverOffset: 4
|
||||
}]
|
||||
}, {
|
||||
cutout: '70%',
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
// Color presets
|
||||
const chartColors = {
|
||||
purple: {
|
||||
solid: '#9333EA',
|
||||
light: 'rgba(147, 51, 234, 0.1)'
|
||||
},
|
||||
blue: {
|
||||
solid: '#3B82F6',
|
||||
light: 'rgba(59, 130, 246, 0.1)'
|
||||
},
|
||||
green: {
|
||||
solid: '#10B981',
|
||||
light: 'rgba(16, 185, 129, 0.1)'
|
||||
},
|
||||
red: {
|
||||
solid: '#EF4444',
|
||||
light: 'rgba(239, 68, 68, 0.1)'
|
||||
},
|
||||
yellow: {
|
||||
solid: '#F59E0B',
|
||||
light: 'rgba(245, 158, 11, 0.1)'
|
||||
},
|
||||
gray: {
|
||||
solid: '#6B7280',
|
||||
light: 'rgba(107, 114, 128, 0.1)'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{#
|
||||
Example Usage Comment
|
||||
=====================
|
||||
Copy this to your page to see how to use the chart macros:
|
||||
|
||||
{% from 'shared/macros/charts.html' import chart_card, chart_config_script %}
|
||||
|
||||
{{ chart_card('monthlySales', 'Monthly Sales', 'line') }}
|
||||
|
||||
{{ chart_config_script() }}
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
createLineChart('monthlySales',
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
|
||||
[{
|
||||
label: 'Sales',
|
||||
data: [30, 40, 35, 50, 49, 60],
|
||||
borderColor: chartColors.purple.solid,
|
||||
backgroundColor: chartColors.purple.light
|
||||
}]
|
||||
);
|
||||
});
|
||||
</script>
|
||||
#}
|
||||
Reference in New Issue
Block a user