@@ -22,25 +22,161 @@ def component_text(app, context, content="", url="", classes=[]):
2222 return html
2323
2424
25- def component_button (app , context , content = "" , url = "" , onclick = "" , classes = []):
25+ def component_button (
26+ app ,
27+ context ,
28+ content = "" ,
29+ title = "" ,
30+ icon = "" ,
31+ url = "" ,
32+ onclick = "" ,
33+ button_id = "" ,
34+ label_for = "" ,
35+ id = "" ,
36+ tooltip_placement = "" ,
37+ kwargs = {},
38+ classes = [],
39+ ):
40+ kwargs = kwargs .copy ()
41+ kwargs .update ({"type" : "button" , "class" : ["btn" , "icon-button" ]})
42+ kwargs ["class" ].extend (classes )
43+ btn_content = ""
2644 if url and onclick :
2745 raise Exception ("Button component cannot have both url and onclick specified." )
28- classes = " " .join (classes )
46+
47+ if not (icon or content ):
48+ raise Exception ("Button must have either icon or content specified." )
49+
2950 if onclick :
30- onclick = ' onclick="{onclick}"'
51+ kwargs ["onclick" ] = onclick
52+
53+ if id :
54+ kwargs ["id" ] = id
55+
56+ if icon :
57+ if icon .startswith ("fa" ):
58+ icon = f'<i class="{ icon } "></i>'
59+ else :
60+ if not icon .startswith ("http" ):
61+ icon = context ["pathto" ](icon , 1 )
62+ icon = f'<img src="{ icon } ">'
63+ btn_content += f'<span class="btn__icon-container">{ icon } </span>'
64+
65+ if not content :
66+ kwargs ["class" ].append ("icon-button-no-content" )
67+ else :
68+ btn_content += content
69+
70+ if button_id :
71+ kwargs ["id" ] = button_id
72+
73+ kwargs ["aria-label" ] = title
74+
75+ # Handle tooltips
76+ title = context ["translate" ](title )
77+ tooltip_placement = "bottom" if not tooltip_placement else tooltip_placement
78+
79+ # If we're already using data-toggle, wrap the button content in a span.
80+ # This lets us use another data-toggle.
81+ if "data-toggle" in kwargs :
82+ btn_content = f"""
83+ <span data-toggle="tooltip" data-placement="{ tooltip_placement } " title="{ title } ">
84+ { btn_content }
85+ </span>
86+ """ # noqa
87+ else :
88+ kwargs ["data-placement" ] = tooltip_placement
89+ kwargs ["title" ] = title
90+
91+ # Convert all the options for the button into a string of HTML kwargs
92+ kwargs ["class" ] = " " .join (kwargs ["class" ])
93+ kwargs_str = " " .join ([f'{ key } ="{ val } "' for key , val in kwargs .items ()])
94+
95+ # Generate the button HTML
96+ if label_for :
97+ html = f"""
98+ <label for="{ label_for } " { kwargs_str } >
99+ { btn_content }
100+ </label>
101+ """
102+ else :
103+ html = f"""
104+ <button { kwargs_str } >
105+ { btn_content }
106+ </button>
107+ """
31108
32- classes = " " .join (classes )
33- html = f"""
34- <button class="btn btn-outline-primary { classes } "{ onclick } type="button">
35- { content }
36- </button>
37- """
109+ # Wrap the whole thing in a link if one is specified
38110 if url :
111+ # If it doesn't look like a web URL, assume it's a local page
112+ if not url .startswith ("http" ):
113+ url = context ["pathto" ](url )
39114 html = f'<a href="{ url } ">{ html } </a>'
40115
41116 return html
42117
43118
119+ def component_dropdown (
120+ app , context , content = "" , icon = "" , side = "left" , items = [], ** kwargs
121+ ):
122+ # Items to go inside dropdown
123+ dropdown_items = []
124+ for component in items :
125+ # Pop the `button` type in case it was incorrectly given, since we force button
126+ if "type" in component :
127+ component .pop ("type" )
128+ dropdown_items .append (
129+ component_button (
130+ app ,
131+ context ,
132+ ** component ,
133+ )
134+ )
135+ dropdown_items = "\n " .join (dropdown_items )
136+
137+ # These control the look of the button
138+ button_classes = []
139+ if content :
140+ button_classes .append ("dropdown-toggle" )
141+
142+ # Unique ID to trigger the show event
143+ dropdown_id = "menu-dropdown-"
144+ dropdown_id += hashlib .md5 (dropdown_items .encode ("utf-8" )).hexdigest ()[:5 ]
145+
146+ # Generate the button HTML
147+ dropdown_kwargs = {
148+ "data-toggle" : "dropdown" ,
149+ "aria-haspopup" : "true" ,
150+ "aria-expanded" : "false" ,
151+ "type" : "button" ,
152+ }
153+ html_button = component_button (
154+ app ,
155+ context ,
156+ content = content ,
157+ icon = icon ,
158+ kwargs = dropdown_kwargs ,
159+ classes = button_classes ,
160+ button_id = dropdown_id ,
161+ ** kwargs ,
162+ )
163+
164+ dropdown_classes = ["dropdown-menu" ]
165+ if side == "right" :
166+ dropdown_classes .append ("dropdown-menu-right" )
167+ dropdown_classes = " " .join (dropdown_classes )
168+
169+ html_dropdown = f"""
170+ <div class="dropdown">
171+ { html_button }
172+ <div class="{ dropdown_classes } " aria-labelledby="{ dropdown_id } ">
173+ { dropdown_items }
174+ </div>
175+ </div>
176+ """ # noqa
177+ return html_dropdown
178+
179+
44180def component_image (app , context , src = "" , url = "" , classes = []):
45181 if not src .startswith ("http" ):
46182 src = context ["pathto" ](src , 1 )
@@ -56,29 +192,6 @@ def component_html(app, context, html=""):
56192 return html
57193
58194
59- def component_dropdown (app , context , content = "" , items = []):
60- dropdown_items = []
61- for component in items :
62- link = f"""
63- <a href="{ component ['url' ]} " class="dropdown-item">{ component ['content' ]} </a>
64- """
65- dropdown_items .append (link )
66- dropdown_items = "\n " .join (dropdown_items )
67- dropdown_id = "menu-dropdown-"
68- dropdown_id += hashlib .md5 (dropdown_items .encode ("utf-8" )).hexdigest ()[:5 ]
69- html = f"""
70- <div class="dropdown">
71- <button class="btn dropdown-toggle" type="button" id="{ dropdown_id } " data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
72- { content }
73- </button>
74- <div class="dropdown-menu" aria-labelledby="{ dropdown_id } ">
75- { dropdown_items }
76- </div>
77- </div>
78- """ # noqa
79- return html
80-
81-
82195def component_icon_links (app , context , icons , classes = []):
83196 context = {"theme_icon_links" : icons }
84197 # Add the pydata theme icon-links macro as a function we can re-use
0 commit comments