Coverage for fastblocks / adapters / icons / materialicons.py: 22%
187 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-26 03:30 -0800
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-26 03:30 -0800
1"""Material Icons adapter for FastBlocks with multiple themes."""
3from contextlib import suppress
4from typing import Any
5from uuid import UUID
7from acb.depends import depends
9from ._base import IconsBase, IconsBaseSettings
10from ._utils import (
11 add_accessibility_attributes,
12 build_attr_string,
13 process_animations,
14 process_semantic_colors,
15 process_state_attributes,
16 process_transformations,
17)
20class MaterialIconsSettings(IconsBaseSettings):
21 """Settings for Material Icons adapter."""
23 # Required ACB 0.19.0+ metadata
24 MODULE_ID: UUID = UUID("01937d86-cf0b-e4bc-0d3e-2c3d4e5f6071") # Static UUID7
25 MODULE_STATUS: str = "stable"
27 # Material Icons configuration
28 version: str = "latest"
29 base_url: str = "https://fonts.googleapis.com"
30 default_theme: str = "filled" # filled, outlined, round, sharp, two-tone
31 default_size: str = "24px"
33 # Available themes
34 enabled_themes: list[str] = ["filled", "outlined", "round", "sharp", "two-tone"]
36 # Icon mapping for common names
37 icon_aliases: dict[str, str] = {
38 "home": "home",
39 "user": "person",
40 "settings": "settings",
41 "search": "search",
42 "menu": "menu",
43 "close": "close",
44 "check": "check",
45 "error": "error",
46 "info": "info",
47 "success": "check_circle",
48 "warning": "warning",
49 "edit": "edit",
50 "delete": "delete",
51 "save": "save",
52 "download": "download",
53 "upload": "upload",
54 "email": "email",
55 "phone": "phone",
56 "location": "location_on",
57 "calendar": "event",
58 "clock": "schedule",
59 "heart": "favorite",
60 "star": "star",
61 "share": "share",
62 "link": "link",
63 "copy": "content_copy",
64 "cut": "content_cut",
65 "paste": "content_paste",
66 "undo": "undo",
67 "redo": "redo",
68 "refresh": "refresh",
69 "logout": "logout",
70 "login": "login",
71 "plus": "add",
72 "minus": "remove",
73 "eye": "visibility",
74 "eye-off": "visibility_off",
75 "lock": "lock",
76 "unlock": "lock_open",
77 "arrow-up": "keyboard_arrow_up",
78 "arrow-down": "keyboard_arrow_down",
79 "arrow-left": "keyboard_arrow_left",
80 "arrow-right": "keyboard_arrow_right",
81 }
83 # Size presets
84 size_presets: dict[str, str] = {
85 "xs": "16px",
86 "sm": "20px",
87 "md": "24px",
88 "lg": "28px",
89 "xl": "32px",
90 "2xl": "40px",
91 "3xl": "48px",
92 "4xl": "56px",
93 "5xl": "64px",
94 }
96 # Color palette
97 material_colors: dict[str, str] = {
98 "red": "#f44336",
99 "pink": "#e91e63",
100 "purple": "#9c27b0",
101 "deep-purple": "#673ab7",
102 "indigo": "#3f51b5",
103 "blue": "#2196f3",
104 "light-blue": "#03a9f4",
105 "cyan": "#00bcd4",
106 "teal": "#009688",
107 "green": "#4caf50",
108 "light-green": "#8bc34a",
109 "lime": "#cddc39",
110 "yellow": "#ffeb3b",
111 "amber": "#ffc107",
112 "orange": "#ff9800",
113 "deep-orange": "#ff5722",
114 "brown": "#795548",
115 "grey": "#9e9e9e",
116 "blue-grey": "#607d8b",
117 }
120class MaterialIcons(IconsBase):
121 """Material Icons adapter with multiple themes and comprehensive icon set."""
123 # Required ACB 0.19.0+ metadata
124 MODULE_ID: UUID = UUID("01937d86-cf0b-e4bc-0d3e-2c3d4e5f6071") # Static UUID7
125 MODULE_STATUS: str = "stable"
127 def __init__(self) -> None:
128 """Initialize Material Icons adapter."""
129 super().__init__()
130 self.settings: MaterialIconsSettings | None = None
132 # Register with ACB dependency system
133 with suppress(Exception):
134 depends.set(self)
136 def get_stylesheet_links(self) -> list[str]:
137 """Get Material Icons stylesheet links."""
138 if not self.settings:
139 self.settings = MaterialIconsSettings()
141 links = []
143 # Material Icons CSS from Google Fonts
144 for theme in self.settings.enabled_themes:
145 if theme == "filled":
146 # Base Material Icons (filled is default)
147 css_url = f"{self.settings.base_url}/icon?family=Material+Icons"
148 else:
149 # Themed variants
150 theme_name = theme.replace("-", "+").title()
151 css_url = (
152 f"{self.settings.base_url}/icon?family=Material+Icons+{theme_name}"
153 )
155 links.append(f'<link rel="stylesheet" href="{css_url}">')
157 # Custom Material Icons CSS
158 material_css = self._generate_material_css()
159 links.append(f"<style>{material_css}</style>")
161 return links
163 def _generate_material_css(self) -> str:
164 """Generate Material Icons-specific CSS."""
165 if not self.settings:
166 self.settings = MaterialIconsSettings()
168 return f"""
169/* Material Icons Base Styles */
170.material-icons {{
171 font-family: 'Material Icons';
172 font-weight: normal;
173 font-style: normal;
174 font-size: {self.settings.default_size};
175 line-height: 1;
176 letter-spacing: normal;
177 text-transform: none;
178 display: inline-block;
179 white-space: nowrap;
180 word-wrap: normal;
181 direction: ltr;
182 -webkit-font-feature-settings: 'liga';
183 -webkit-font-smoothing: antialiased;
184 vertical-align: -0.125em;
185}}
187/* Theme-specific font families */
188.material-icons-outlined {{
189 font-family: 'Material Icons Outlined';
190}}
192.material-icons-round {{
193 font-family: 'Material Icons Round';
194}}
196.material-icons-sharp {{
197 font-family: 'Material Icons Sharp';
198}}
200.material-icons-two-tone {{
201 font-family: 'Material Icons Two Tone';
202}}
204/* Size variants */
205.material-icons-xs {{ font-size: 16px; }}
206.material-icons-sm {{ font-size: 20px; }}
207.material-icons-md {{ font-size: 24px; }}
208.material-icons-lg {{ font-size: 28px; }}
209.material-icons-xl {{ font-size: 32px; }}
210.material-icons-2xl {{ font-size: 40px; }}
211.material-icons-3xl {{ font-size: 48px; }}
212.material-icons-4xl {{ font-size: 56px; }}
213.material-icons-5xl {{ font-size: 64px; }}
215/* Density variants */
216.material-icons-dense {{
217 font-size: 20px;
218}}
220.material-icons-comfortable {{
221 font-size: 24px;
222}}
224.material-icons-compact {{
225 font-size: 18px;
226}}
228/* Rotation and transformation */
229.material-icons-rotate-90 {{ transform: rotate(90deg); }}
230.material-icons-rotate-180 {{ transform: rotate(180deg); }}
231.material-icons-rotate-270 {{ transform: rotate(270deg); }}
232.material-icons-flip-horizontal {{ transform: scaleX(-1); }}
233.material-icons-flip-vertical {{ transform: scaleY(-1); }}
235/* Animation support */
236.material-icons-spin {{
237 animation: material-spin 2s linear infinite;
238}}
240.material-icons-pulse {{
241 animation: material-pulse 2s ease-in-out infinite alternate;
242}}
244.material-icons-bounce {{
245 animation: material-bounce 1s ease-in-out infinite;
246}}
248.material-icons-shake {{
249 animation: material-shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
250}}
252.material-icons-flip {{
253 animation: material-flip 2s linear infinite;
254}}
256@keyframes material-spin {{
257 0% {{ transform: rotate(0deg); }}
258 100% {{ transform: rotate(360deg); }}
259}}
261@keyframes material-pulse {{
262 from {{ opacity: 1; }}
263 to {{ opacity: 0.25; }}
264}}
266@keyframes material-bounce {{
267 0%, 100% {{ transform: translateY(0); }}
268 50% {{ transform: translateY(-25%); }}
269}}
271@keyframes material-shake {{
272 10%, 90% {{ transform: translate3d(-1px, 0, 0); }}
273 20%, 80% {{ transform: translate3d(2px, 0, 0); }}
274 30%, 50%, 70% {{ transform: translate3d(-4px, 0, 0); }}
275 40%, 60% {{ transform: translate3d(4px, 0, 0); }}
276}}
278@keyframes material-flip {{
279 0% {{ transform: rotateY(0); }}
280 50% {{ transform: rotateY(180deg); }}
281 100% {{ transform: rotateY(360deg); }}
282}}
284/* Material Design color utilities */
285{self._generate_material_color_classes()}
287/* Interactive states */
288.material-icons-interactive {{
289 cursor: pointer;
290 transition: all 0.2s ease;
291 border-radius: 50%;
292 padding: 4px;
293}}
295.material-icons-interactive:hover {{
296 background-color: rgba(0, 0, 0, 0.04);
297 transform: scale(1.1);
298}}
300.material-icons-interactive:active {{
301 background-color: rgba(0, 0, 0, 0.08);
302 transform: scale(0.95);
303}}
305/* States */
306.material-icons-disabled {{
307 opacity: 0.38;
308 cursor: not-allowed;
309}}
311.material-icons-inactive {{
312 opacity: 0.54;
313}}
315.material-icons-loading {{
316 opacity: 0.6;
317}}
319/* Button integration */
320.btn .material-icons {{
321 margin-right: 8px;
322 vertical-align: -0.125em;
323}}
325.btn .material-icons:last-child {{
326 margin-right: 0;
327 margin-left: 8px;
328}}
330.btn .material-icons:only-child {{
331 margin: 0;
332}}
334.btn-sm .material-icons {{
335 font-size: 20px;
336}}
338.btn-lg .material-icons {{
339 font-size: 28px;
340}}
342/* Floating Action Button */
343.fab {{
344 display: inline-flex;
345 align-items: center;
346 justify-content: center;
347 width: 56px;
348 height: 56px;
349 border-radius: 50%;
350 border: none;
351 box-shadow: 0 3px 5px -1px rgba(0,0,0,.2), 0 6px 10px 0 rgba(0,0,0,.14), 0 1px 18px 0 rgba(0,0,0,.12);
352 cursor: pointer;
353 transition: all 0.3s ease;
354}}
356.fab:hover {{
357 box-shadow: 0 5px 5px -3px rgba(0,0,0,.2), 0 8px 10px 1px rgba(0,0,0,.14), 0 3px 14px 2px rgba(0,0,0,.12);
358 transform: translateY(-2px);
359}}
361.fab-mini {{
362 width: 40px;
363 height: 40px;
364}}
366.fab-extended {{
367 width: auto;
368 height: 48px;
369 border-radius: 24px;
370 padding: 0 16px;
371}}
373/* Badge integration */
374.badge .material-icons {{
375 font-size: 16px;
376 margin-right: 4px;
377 vertical-align: baseline;
378}}
380/* Navigation integration */
381.nav-link .material-icons {{
382 margin-right: 8px;
383 font-size: 20px;
384}}
386/* List integration */
387.list-group-item .material-icons {{
388 margin-right: 16px;
389 color: rgba(0, 0, 0, 0.54);
390}}
392/* Input group integration */
393.input-group-text .material-icons {{
394 color: rgba(0, 0, 0, 0.54);
395}}
397/* Alert integration */
398.alert .material-icons {{
399 margin-right: 8px;
400 font-size: 20px;
401}}
403/* Card integration */
404.card-title .material-icons {{
405 margin-right: 8px;
406}}
408/* Toolbar integration */
409.toolbar .material-icons {{
410 color: rgba(255, 255, 255, 0.87);
411}}
413/* Dark theme support */
414@media (prefers-color-scheme: dark) {{
415 .material-icons-interactive:hover {{
416 background-color: rgba(255, 255, 255, 0.08);
417 }}
419 .material-icons-interactive:active {{
420 background-color: rgba(255, 255, 255, 0.12);
421 }}
423 .list-group-item .material-icons {{
424 color: rgba(255, 255, 255, 0.7);
425 }}
427 .input-group-text .material-icons {{
428 color: rgba(255, 255, 255, 0.7);
429 }}
430}}
432/* Accessibility */
433.material-icons[aria-hidden="false"] {{
434 position: relative;
435}}
437.material-icons[aria-hidden="false"]:focus {{
438 outline: 2px solid #1976d2;
439 outline-offset: 2px;
440}}
441"""
443 def _generate_material_color_classes(self) -> str:
444 """Generate Material Design color classes."""
445 if not self.settings:
446 self.settings = MaterialIconsSettings()
448 css = "/* Material Design Colors */\n"
449 for name, color in self.settings.material_colors.items():
450 css += f".material-icons-{name} {{ color: {color}; }}\n"
452 # Add semantic colors
453 css += """
454.material-icons-primary { color: var(--primary-color, #1976d2); }
455.material-icons-secondary { color: var(--secondary-color, #424242); }
456.material-icons-success { color: var(--success-color, #4caf50); }
457.material-icons-warning { color: var(--warning-color, #ff9800); }
458.material-icons-danger { color: var(--danger-color, #f44336); }
459.material-icons-info { color: var(--info-color, #2196f3); }
460.material-icons-light { color: var(--light-color, #fafafa); }
461.material-icons-dark { color: var(--dark-color, #212121); }
462.material-icons-muted { color: var(--muted-color, #757575); }
463"""
465 return css
467 def get_icon_class(self, icon_name: str, theme: str | None = None) -> str:
468 """Get Material Icons class with theme support."""
469 if not self.settings:
470 self.settings = MaterialIconsSettings()
472 # Use default theme if not specified
473 if not theme:
474 theme = self.settings.default_theme
476 # Validate theme
477 if theme not in self.settings.enabled_themes:
478 theme = self.settings.default_theme
480 # Build class name based on theme
481 if theme == "filled":
482 return "material-icons"
484 return f"material-icons-{theme.replace('_', '-')}"
486 def get_icon_tag(
487 self,
488 icon_name: str,
489 **attributes: Any,
490 ) -> str:
491 """Generate Material Icons tag with full customization."""
492 # Extract theme and size from attributes
493 theme = attributes.pop("theme", None)
494 size = attributes.pop("size", None)
496 if not self.settings:
497 self.settings = MaterialIconsSettings()
499 # Resolve icon aliases
500 resolved_name = icon_name
501 if icon_name in self.settings.icon_aliases:
502 resolved_name = self.settings.icon_aliases[icon_name]
504 # Get base icon class
505 icon_class = self.get_icon_class(icon_name, theme)
507 # Add size class or custom size
508 if size:
509 if size in self.settings.size_presets:
510 icon_class += f" material-icons-{size}"
511 else:
512 attributes["style"] = (
513 f"font-size: {size}; {attributes.get('style', '')}"
514 )
516 # Add custom classes
517 if "class" in attributes:
518 icon_class += f" {attributes.pop('class')}"
520 # Process attributes using shared utilities
521 transform_classes, attributes = process_transformations(
522 attributes, "material-icons"
523 )
524 animation_classes, attributes = process_animations(
525 attributes, ["spin", "pulse", "bounce", "shake", "flip"], "material-icons"
526 )
528 # Extended semantic colors including material design colors
529 semantic_colors = [
530 "primary",
531 "secondary",
532 "success",
533 "warning",
534 "danger",
535 "info",
536 "light",
537 "dark",
538 "muted",
539 *list(self.settings.material_colors.keys()),
540 ]
541 color_class, attributes = process_semantic_colors(
542 attributes, semantic_colors, "material-icons"
543 )
544 state_classes, attributes = process_state_attributes(
545 attributes, "material-icons"
546 )
548 # Handle density (Material Design specific feature)
549 if "density" in attributes:
550 density = attributes.pop("density")
551 if density in ("dense", "comfortable", "compact"):
552 icon_class += f" material-icons-{density}"
554 # Combine all classes
555 icon_class += (
556 transform_classes + animation_classes + color_class + state_classes
557 )
559 # Build attributes and add accessibility
560 attrs = {"class": icon_class} | attributes
561 attrs = add_accessibility_attributes(attrs)
563 # Generate tag
564 attr_string = build_attr_string(attrs)
565 return f"<span {attr_string}>{resolved_name}</span>"
567 def get_fab_tag(
568 self,
569 icon_name: str,
570 variant: str = "regular",
571 theme: str | None = None,
572 **attributes: Any,
573 ) -> str:
574 """Generate Material Design Floating Action Button."""
575 if not self.settings:
576 self.settings = MaterialIconsSettings()
578 # Resolve icon name
579 resolved_name = icon_name
580 if icon_name in self.settings.icon_aliases:
581 resolved_name = self.settings.icon_aliases[icon_name]
583 # Get icon tag
584 icon_tag = self.get_icon_tag(resolved_name, theme=theme, size="md")
586 # Build FAB classes
587 fab_class = "fab"
588 if variant == "mini":
589 fab_class += " fab-mini"
590 elif variant == "extended":
591 fab_class += " fab-extended"
593 # Add custom classes
594 if "class" in attributes:
595 fab_class += f" {attributes.pop('class')}"
597 # Handle extended FAB with text
598 text = attributes.pop("text", "")
599 if variant == "extended" and text:
600 content = f"{icon_tag} {text}"
601 else:
602 content = icon_tag
604 # Build attributes
605 attrs = {"class": fab_class} | attributes
606 attr_string = " ".join(f'{k}="{v}"' for k, v in attrs.items())
608 return f"<button {attr_string}>{content}</button>"
610 @staticmethod
611 def get_available_icons() -> dict[str, list[str]]:
612 """Get list of available Material Icons by category."""
613 return {
614 "action": [
615 "home",
616 "search",
617 "settings",
618 "favorite",
619 "star",
620 "bookmark",
621 "help",
622 "info",
623 "check_circle",
624 "done",
625 "thumb_up",
626 "thumb_down",
627 ],
628 "communication": [
629 "email",
630 "phone",
631 "chat",
632 "message",
633 "comment",
634 "forum",
635 "contact_mail",
636 "contact_phone",
637 "textsms",
638 "call",
639 ],
640 "content": [
641 "add",
642 "remove",
643 "clear",
644 "create",
645 "edit",
646 "delete_forever",
647 "content_copy",
648 "content_cut",
649 "content_paste",
650 "save",
651 "undo",
652 "redo",
653 ],
654 "editor": [
655 "format_bold",
656 "format_italic",
657 "format_underlined",
658 "format_color_text",
659 "format_align_left",
660 "format_align_center",
661 "format_align_right",
662 "format_list_bulleted",
663 ],
664 "file": [
665 "folder",
666 "folder_open",
667 "insert_drive_file",
668 "cloud",
669 "cloud_download",
670 "cloud_upload",
671 "attachment",
672 "file_download",
673 "file_upload",
674 ],
675 "hardware": [
676 "computer",
677 "phone_android",
678 "phone_iphone",
679 "tablet",
680 "laptop",
681 "desktop_windows",
682 "keyboard",
683 "mouse",
684 "headset",
685 "speaker",
686 ],
687 "image": [
688 "image",
689 "photo",
690 "photo_camera",
691 "video_camera",
692 "movie",
693 "music_note",
694 "palette",
695 "brush",
696 "color_lens",
697 "gradient",
698 ],
699 "maps": [
700 "location_on",
701 "location_off",
702 "my_location",
703 "navigation",
704 "map",
705 "place",
706 "directions",
707 "directions_car",
708 "directions_walk",
709 "local_taxi",
710 ],
711 "navigation": [
712 "menu",
713 "close",
714 "arrow_back",
715 "arrow_forward",
716 "arrow_upward",
717 "arrow_downward",
718 "chevron_left",
719 "chevron_right",
720 "expand_less",
721 "expand_more",
722 "fullscreen",
723 ],
724 "notification": [
725 "notifications",
726 "notifications_off",
727 "notification_important",
728 "alarm",
729 "alarm_on",
730 "alarm_off",
731 "event",
732 "event_available",
733 "schedule",
734 ],
735 "social": [
736 "person",
737 "people",
738 "group",
739 "public",
740 "school",
741 "domain",
742 "cake",
743 "mood",
744 "mood_bad",
745 "sentiment_satisfied",
746 "party_mode",
747 ],
748 "toggle": [
749 "check_box",
750 "check_box_outline_blank",
751 "radio_button_checked",
752 "radio_button_unchecked",
753 "star",
754 "star_border",
755 "favorite",
756 "favorite_border",
757 "visibility",
758 "visibility_off",
759 ],
760 "av": [
761 "play_arrow",
762 "pause",
763 "stop",
764 "fast_forward",
765 "fast_rewind",
766 "skip_next",
767 "skip_previous",
768 "volume_up",
769 "volume_down",
770 "volume_off",
771 ],
772 }
775# Template filter registration for FastBlocks
776def _register_material_basic_filters(env: Any) -> None:
777 """Register basic Material Icons filters."""
779 @env.filter("material_icon") # type: ignore[misc]
780 def material_icon_filter(
781 icon_name: str,
782 theme: str | None = None,
783 size: str | None = None,
784 **attributes: Any,
785 ) -> str:
786 """Template filter for Material Icons."""
787 icons = depends.get_sync("icons")
788 if isinstance(icons, MaterialIcons):
789 return icons.get_icon_tag(icon_name, theme=theme, size=size, **attributes)
790 return f"<!-- {icon_name} -->"
792 @env.filter("material_class") # type: ignore[misc]
793 def material_class_filter(icon_name: str, theme: str | None = None) -> str:
794 """Template filter for Material Icons classes."""
795 icons = depends.get_sync("icons")
796 if isinstance(icons, MaterialIcons):
797 return icons.get_icon_class(icon_name, theme)
798 return "material-icons"
800 @env.global_("materialicons_stylesheet_links") # type: ignore[misc]
801 def materialicons_stylesheet_links() -> str:
802 """Global function for Material Icons stylesheet links."""
803 icons = depends.get_sync("icons")
804 if isinstance(icons, MaterialIcons):
805 return "\n".join(icons.get_stylesheet_links())
806 return ""
809def _register_material_fab_functions(env: Any) -> None:
810 """Register Material Design FAB functions."""
812 @env.global_("material_fab") # type: ignore[misc]
813 def material_fab(
814 icon_name: str,
815 variant: str = "regular",
816 theme: str | None = None,
817 **attributes: Any,
818 ) -> str:
819 """Generate Material Design Floating Action Button."""
820 icons = depends.get_sync("icons")
821 if isinstance(icons, MaterialIcons):
822 return icons.get_fab_tag(icon_name, variant, theme, **attributes)
823 return f"<button class='fab'>{icon_name}</button>"
826def _register_material_button_functions(env: Any) -> None:
827 """Register Material Design button functions."""
829 @env.global_("material_button") # type: ignore[misc]
830 def material_button(
831 text: str,
832 icon: str | None = None,
833 theme: str | None = None,
834 icon_position: str = "left",
835 **attributes: Any,
836 ) -> str:
837 """Generate button with Material Icon."""
838 icons = depends.get_sync("icons")
839 if not isinstance(icons, MaterialIcons):
840 return f"<button>{text}</button>"
842 btn_class = attributes.pop("class", "btn btn-primary")
844 # Build button content
845 content = ""
846 if icon:
847 icon_tag = icons.get_icon_tag(icon, theme=theme, size="sm")
849 if icon_position == "left":
850 content = f"{icon_tag} {text}"
851 elif icon_position == "right":
852 content = f"{text} {icon_tag}"
853 elif icon_position == "only":
854 content = icon_tag
855 else:
856 content = text
857 else:
858 content = text
860 # Build button attributes
861 btn_attrs = {"class": btn_class} | attributes
862 attr_string = " ".join(f'{k}="{v}"' for k, v in btn_attrs.items())
864 return f"<button {attr_string}>{content}</button>"
867def _register_material_chip_functions(env: Any) -> None:
868 """Register Material Design chip functions."""
870 @env.global_("material_chip") # type: ignore[misc]
871 def material_chip(
872 text: str,
873 icon: str | None = None,
874 theme: str | None = None,
875 deletable: bool = False,
876 **attributes: Any,
877 ) -> str:
878 """Generate Material Design chip with icon."""
879 icons = depends.get_sync("icons")
880 if not isinstance(icons, MaterialIcons):
881 return f"<div class='chip'>{text}</div>"
883 chip_class = attributes.pop("class", "chip")
885 # Build chip content
886 content = ""
887 if icon:
888 icon_tag = icons.get_icon_tag(
889 icon, theme=theme, size="sm", class_="chip-icon"
890 )
891 content += icon_tag
893 content += f"<span class='chip-text'>{text}</span>"
895 if deletable:
896 delete_icon = icons.get_icon_tag(
897 "close", theme=theme, size="sm", class_="chip-delete"
898 )
899 content += delete_icon
901 # Build chip attributes
902 chip_attrs = {"class": chip_class} | attributes
903 attr_string = " ".join(f'{k}="{v}"' for k, v in chip_attrs.items())
905 return f"<div {attr_string}>{content}</div>"
908def register_materialicons_filters(env: Any) -> None:
909 """Register Material Icons filters for Jinja2 templates."""
910 _register_material_basic_filters(env)
911 _register_material_fab_functions(env)
912 _register_material_button_functions(env)
913 _register_material_chip_functions(env)
916IconsSettings = MaterialIconsSettings
917Icons = MaterialIcons
919depends.set(Icons, "materialicons")
921# ACB 0.19.0+ compatibility
922__all__ = [
923 "MaterialIcons",
924 "MaterialIconsSettings",
925 "register_materialicons_filters",
926 "Icons",
927 "IconsSettings",
928]