- Author:
- OgliariNatan
- Posted:
- September 6, 2025
- Language:
- Python
- Version:
- 5.2
- Score:
- 0 (after 0 ratings)
Implementation Suggestions
use:
.......
widgets = {
......
}),
'solicitada_mpu': ToggleSwitchWidget(size='sm',
active_color='#9333ea',
inactive_color='#ccc',
active_text='YES',
inactive_text='NO'
),
...............
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | from django.forms.widgets import CheckboxInput from django.utils.safestring import mark_safe from django.utils.html import format_html import json import uuid class ToggleSwitchWidget(CheckboxInput): """ A customizable toggle switch widget for Django forms. Author: Ogliari Natan Date: 11-08-2025 Proposal for Django Foundation Args: size (str): Size of the toggle - 'xs', 'sm', 'md', 'lg', 'xl' or custom tuple (width, height) active_color (str): Color when toggle is active (default: '#10B981') inactive_color (str): Color when toggle is inactive (default: '#9CA3AF') active_text (str): Text to display when active (default: 'Active') inactive_text (str): Text to display when inactive (default: 'Inactive') show_label (bool): Whether to show the text label (default: True) animation_speed (float): Animation speed in seconds (default: 0.3) """ # Predefined sizes with maintained proportions SIZES = { 'xs': (32, 16), # width, height in pixels 'sm': (40, 20), 'md': (48, 24), 'lg': (56, 28), 'xl': (64, 32), } def __init__(self, size='md', active_color="#B910B9", inactive_color='#9CA3AF', active_text='Active', inactive_text='Inactive', show_label=True, animation_speed=0.3, attrs=None): super().__init__(attrs) # Handle size parameter if isinstance(size, str) and size in self.SIZES: self.width, self.height = self.SIZES[size] elif isinstance(size, (tuple, list)) and len(size) == 2: self.width, self.height = size else: self.width, self.height = self.SIZES['md'] # Calculate proportional dimensions self.button_size = int(self.height * 0.75) # Button is 75% of height self.padding = int(self.height * 0.125) # Padding is 12.5% of height self.translate_x = self.width - self.button_size - (self.padding * 2) # Set colors and text self.active_color = active_color self.inactive_color = inactive_color self.active_text = active_text self.inactive_text = inactive_text self.show_label = show_label self.animation_speed = animation_speed def render(self, name, value, attrs=None, renderer=None): if attrs is None: attrs = {} # Generate unique ID for this instance widget_id = attrs.get('id', f'toggle_{uuid.uuid4().hex[:8]}') is_checked = bool(value) checked_attr = 'checked' if is_checked else '' # Build the widget HTML context = { 'widget_id': widget_id, 'name': name, 'checked': checked_attr, 'width': self.width, 'height': self.height, 'button_size': self.button_size, 'padding': self.padding, 'translate_x': self.translate_x, 'active_color': self.active_color, 'inactive_color': self.inactive_color, 'active_text': self.active_text, 'inactive_text': self.inactive_text, 'current_text': self.active_text if is_checked else self.inactive_text, 'current_color': self.active_color if is_checked else self.inactive_color, 'show_label': 'inline-block' if self.show_label else 'none', 'animation_speed': self.animation_speed, 'font_size': max(12, int(self.height * 0.6)), # Proportional font size } html = ''' <div class="django-toggle-switch-wrapper" id="wrapper_%(widget_id)s"> <style> #wrapper_%(widget_id)s { display: inline-flex; align-items: center; gap: 8px; } #wrapper_%(widget_id)s .django-toggle-switch { position: relative; display: inline-block; width: %(width)spx; height: %(height)spx; cursor: pointer; } #wrapper_%(widget_id)s .django-toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; } #wrapper_%(widget_id)s .django-toggle-slider { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: %(inactive_color)s; transition: background-color %(animation_speed)ss; border-radius: %(height)spx; } #wrapper_%(widget_id)s .django-toggle-slider:before { position: absolute; content: ""; height: %(button_size)spx; width: %(button_size)spx; left: %(padding)spx; bottom: %(padding)spx; background-color: white; transition: transform %(animation_speed)ss; border-radius: 50%%; box-shadow: 0 2px 4px rgba(0,0,0,0.2); } #wrapper_%(widget_id)s input:checked + .django-toggle-slider { background-color: %(active_color)s; } #wrapper_%(widget_id)s input:checked + .django-toggle-slider:before { transform: translateX(%(translate_x)spx); } #wrapper_%(widget_id)s input:focus + .django-toggle-slider { box-shadow: 0 0 0 2px rgba(0,0,0,0.1); } #wrapper_%(widget_id)s .django-toggle-label { display: %(show_label)s; font-size: %(font_size)spx; user-select: none; transition: color %(animation_speed)ss; color: %(current_color)s; } /* Accessibility improvements */ #wrapper_%(widget_id)s input:focus-visible + .django-toggle-slider { outline: 2px solid %(active_color)s; outline-offset: 2px; } /* Hover effect */ #wrapper_%(widget_id)s .django-toggle-switch:hover .django-toggle-slider { opacity: 0.8; } /* Disabled state */ #wrapper_%(widget_id)s input:disabled + .django-toggle-slider { opacity: 0.5; cursor: not-allowed; } </style> <label class="django-toggle-switch" for="%(widget_id)s"> <input type="checkbox" name="%(name)s" id="%(widget_id)s" %(checked)s aria-checked="%(checked)s" aria-label="%(current_text)s" role="switch"> <span class="django-toggle-slider"></span> </label> <span class="django-toggle-label" id="%(widget_id)s_label"> %(current_text)s </span> <script> (function() { const toggle = document.getElementById('%(widget_id)s'); const label = document.getElementById('%(widget_id)s_label'); const config = { activeText: '%(active_text)s', inactiveText: '%(inactive_text)s', activeColor: '%(active_color)s', inactiveColor: '%(inactive_color)s' }; if (toggle && label) { toggle.addEventListener('change', function() { if (this.checked) { label.textContent = config.activeText; label.style.color = config.activeColor; this.setAttribute('aria-checked', 'true'); this.setAttribute('aria-label', config.activeText); } else { label.textContent = config.inactiveText; label.style.color = config.inactiveColor; this.setAttribute('aria-checked', 'false'); this.setAttribute('aria-label', config.inactiveText); } }); } })(); </script> </div> ''' % context return mark_safe(html) |
More like this
- get_object_or_none by azwdevops 3 months, 3 weeks ago
- Mask sensitive data from logger by agusmakmun 5 months, 2 weeks ago
- Template tag - list punctuation for a list of items by shapiromatron 1 year, 7 months ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 1 year, 8 months ago
- Serializer factory with Django Rest Framework by julio 2 years, 2 months ago
Comments
Please login first before commenting.