22A module implementing a channel-environment tab HTML interface.
33"""
44
5+ # built-in
6+ from io import StringIO
7+ from json import dumps
8+ from typing import NamedTuple
9+
510# third-party
11+ from svgen .element import Element
12+ from svgen .element .html import div
13+ from vcorelib .dict import GenericStrDict
614from vcorelib .io .markdown import MarkdownMixin
715from vcorelib .logging import LoggerMixin
816from vcorelib .math import RateLimiter
1321 ChannelCommandProcessor ,
1422)
1523from runtimepy .net .arbiter .info import AppInfo
24+ from runtimepy .net .html .bootstrap import icon_str
1625from runtimepy .net .html .bootstrap .tabs import TabbedContent
1726from runtimepy .net .server .app .tab import Tab
1827
1928
29+ class ActionButton (NamedTuple ):
30+ """A class implementing an interface for action buttons."""
31+
32+ key : str
33+ payload : GenericStrDict
34+ text : str
35+ icon : str
36+ variant : str
37+ outline : bool
38+
39+ @staticmethod
40+ def from_dict (data : GenericStrDict ) -> "ActionButton" :
41+ """Create an action button from dictionary data."""
42+
43+ return ActionButton (
44+ data ["key" ],
45+ data ["payload" ],
46+ data .get ("text" , "" ),
47+ data .get ("icon" , "" ),
48+ data .get ("variant" , "primary" ),
49+ data .get ("outline" , True ),
50+ )
51+
52+ def element (self ) -> Element :
53+ """Create an action button element."""
54+
55+ payload = dumps (self .payload ).replace ('"' , """ )
56+
57+ text_parts = []
58+ if self .icon :
59+ text_parts .append (
60+ icon_str (
61+ self .icon ,
62+ [f"text-{ self .variant } -emphasis" ] if self .text else [],
63+ )
64+ )
65+ if self .text :
66+ text_parts .append (self .text )
67+
68+ return div (
69+ tag = "button" ,
70+ type = "button" ,
71+ onclick = f"tabs[shown_tab].worker.bus('{ self .key } ', { payload } )" ,
72+ class_str = f"btn btn{ '-outline' if self .outline else '' } "
73+ f"-{ self .variant } m-2 ms-1 me-0" ,
74+ text = " " .join (text_parts ),
75+ )
76+
77+
2078class ChannelEnvironmentTabBase (Tab , LoggerMixin , MarkdownMixin ):
2179 """A channel-environment tab interface."""
2280
@@ -28,14 +86,61 @@ def __init__(
2886 tabs : TabbedContent ,
2987 icon : str = "alarm" ,
3088 markdown : str = None ,
89+ buttons : list [ActionButton ] = None ,
3190 ** kwargs ,
3291 ) -> None :
3392 """Initialize this instance."""
3493
94+ # Action buttons.
95+ if buttons is None :
96+ buttons = [
97+ ###############################################################
98+ # REMOVE THESE
99+ ###############################################################
100+ ActionButton .from_dict (
101+ {"icon" : "tux" , "key" : "test" , "payload" : {}}
102+ ),
103+ ActionButton .from_dict (
104+ {
105+ "icon" : "tux" ,
106+ "key" : "test" ,
107+ "payload" : {},
108+ "text" : "asdf" ,
109+ }
110+ ),
111+ ActionButton .from_dict (
112+ {
113+ "icon" : "tux" ,
114+ "key" : "test" ,
115+ "payload" : {},
116+ "outline" : False ,
117+ }
118+ ),
119+ ActionButton .from_dict (
120+ {
121+ "icon" : "tux" ,
122+ "key" : "test" ,
123+ "payload" : {},
124+ "text" : "asdf" ,
125+ "outline" : False ,
126+ }
127+ ),
128+ ###############################################################
129+ ]
130+ self .buttons : list [ActionButton ] = buttons
131+
35132 self .command = command
36133 self .set_markdown (markdown = markdown , package = PKG_NAME )
37134 super ().__init__ (name , app , tabs , source = "env" , icon = icon , ** kwargs )
38135
39136 # Logging.
40137 LoggerMixin .__init__ (self , logger = self .command .logger )
41138 self .log_limiter = RateLimiter .from_s (1.0 )
139+
140+ def _action_markdown (self ) -> str :
141+ """Get action-button markdown."""
142+
143+ with StringIO () as stream :
144+ for button in self .buttons :
145+ button .element ().encode (stream )
146+ return stream .getvalue ()
0 commit comments