Coverage for pyWebLayout/abstract/functional.py: 98%

144 statements  

« prev     ^ index     » next       coverage.py v7.11.2, created at 2025-11-12 12:02 +0000

1from __future__ import annotations 

2from enum import Enum 

3from typing import Callable, Dict, Any, Optional, List, Tuple 

4from pyWebLayout.core.base import Interactable 

5 

6 

7class LinkType(Enum): 

8 """Enumeration of different types of links for classification purposes""" 

9 INTERNAL = 1 # Links within the same document (e.g., chapter references, bookmarks) 

10 EXTERNAL = 2 # Links to external resources (e.g., websites, other documents) 

11 API = 3 # Links that trigger API calls (e.g., for settings management) 

12 FUNCTION = 4 # Links that execute a specific function 

13 

14 

15class Link(Interactable): 

16 """ 

17 A link that can navigate to a location or execute a function. 

18 Links can be used for navigation within a document, to external resources, 

19 or to trigger API calls for functionality like settings management. 

20 """ 

21 

22 def __init__(self, 

23 location: str, 

24 link_type: LinkType = LinkType.INTERNAL, 

25 callback: Optional[Callable] = None, 

26 params: Optional[Dict[str, Any]] = None, 

27 title: Optional[str] = None, 

28 html_id: Optional[str] = None): 

29 """ 

30 Initialize a link. 

31 

32 Args: 

33 location: The target location or identifier for this link 

34 link_type: The type of link (internal, external, API, function) 

35 callback: Optional callback function to execute when the link is activated 

36 params: Optional parameters to pass to the callback or API 

37 title: Optional title/tooltip for the link 

38 html_id: Optional HTML id attribute (from <a id="...">) for callback binding 

39 """ 

40 super().__init__(callback) 

41 self._location = location 

42 self._link_type = link_type 

43 self._params = params or {} 

44 self._title = title 

45 self._html_id = html_id 

46 

47 @property 

48 def location(self) -> str: 

49 """Get the target location of this link""" 

50 return self._location 

51 

52 @property 

53 def link_type(self) -> LinkType: 

54 """Get the type of this link""" 

55 return self._link_type 

56 

57 @property 

58 def params(self) -> Dict[str, Any]: 

59 """Get the parameters for this link""" 

60 return self._params 

61 

62 @property 

63 def title(self) -> Optional[str]: 

64 """Get the title/tooltip for this link""" 

65 return self._title 

66 

67 @property 

68 def html_id(self) -> Optional[str]: 

69 """Get the HTML id attribute for callback binding""" 

70 return self._html_id 

71 

72 def execute(self, point=None) -> Any: 

73 """ 

74 Execute the link action based on its type. 

75 

76 For internal and external links, returns the location. 

77 For API and function links, executes the callback with the provided parameters. 

78 

79 Args: 

80 point: Optional interaction point passed from the interact() method 

81 

82 Returns: 

83 The result of the link execution, which depends on the link type. 

84 """ 

85 if self._link_type in (LinkType.API, LinkType.FUNCTION) and self._callback: 

86 return self._callback(self._location, point, **self._params) 

87 else: 

88 # For INTERNAL and EXTERNAL links, return the location 

89 # The renderer/browser will handle the navigation 

90 return self._location 

91 

92 

93class Button(Interactable): 

94 """ 

95 A button that can be clicked to execute an action. 

96 Buttons are similar to function links but are rendered differently. 

97 """ 

98 

99 def __init__(self, 

100 label: str, 

101 callback: Callable, 

102 params: Optional[Dict[str, Any]] = None, 

103 enabled: bool = True, 

104 html_id: Optional[str] = None): 

105 """ 

106 Initialize a button. 

107 

108 Args: 

109 label: The text label for the button 

110 callback: The function to execute when the button is clicked 

111 params: Optional parameters to pass to the callback 

112 enabled: Whether the button is initially enabled 

113 html_id: Optional HTML id attribute (from <button id="...">) for callback binding 

114 """ 

115 super().__init__(callback) 

116 self._label = label 

117 self._params = params or {} 

118 self._enabled = enabled 

119 self._html_id = html_id 

120 

121 @property 

122 def label(self) -> str: 

123 """Get the button label""" 

124 return self._label 

125 

126 @label.setter 

127 def label(self, label: str): 

128 """Set the button label""" 

129 self._label = label 

130 

131 @property 

132 def enabled(self) -> bool: 

133 """Check if the button is enabled""" 

134 return self._enabled 

135 

136 @enabled.setter 

137 def enabled(self, enabled: bool): 

138 """Enable or disable the button""" 

139 self._enabled = enabled 

140 

141 @property 

142 def params(self) -> Dict[str, Any]: 

143 """Get the button parameters""" 

144 return self._params 

145 

146 @property 

147 def html_id(self) -> Optional[str]: 

148 """Get the HTML id attribute for callback binding""" 

149 return self._html_id 

150 

151 def execute(self, point=None) -> Any: 

152 """ 

153 Execute the button's callback function if the button is enabled. 

154 

155 Args: 

156 point: Optional interaction point passed from the interact() method 

157 

158 Returns: 

159 The result of the callback function, or None if the button is disabled. 

160 """ 

161 if self._enabled and self._callback: 

162 return self._callback(point, **self._params) 

163 return None 

164 

165 

166class Form(Interactable): 

167 """ 

168 A form that can contain input fields and be submitted. 

169 Forms can be used for user input and settings configuration. 

170 """ 

171 

172 def __init__(self, 

173 form_id: str, 

174 action: Optional[str] = None, 

175 callback: Optional[Callable] = None, 

176 html_id: Optional[str] = None): 

177 """ 

178 Initialize a form. 

179 

180 Args: 

181 form_id: The unique identifier for this form 

182 action: The action URL or endpoint for form submission 

183 callback: Optional callback function to execute on form submission 

184 html_id: Optional HTML id attribute (from <form id="...">) for callback binding 

185 """ 

186 super().__init__(callback) 

187 self._form_id = form_id 

188 self._action = action 

189 self._fields: Dict[str, FormField] = {} 

190 self._html_id = html_id 

191 

192 @property 

193 def form_id(self) -> str: 

194 """Get the form ID""" 

195 return self._form_id 

196 

197 @property 

198 def action(self) -> Optional[str]: 

199 """Get the form action""" 

200 return self._action 

201 

202 @property 

203 def html_id(self) -> Optional[str]: 

204 """Get the HTML id attribute for callback binding""" 

205 return self._html_id 

206 

207 def add_field(self, field: FormField): 

208 """ 

209 Add a field to this form. 

210 

211 Args: 

212 field: The FormField to add 

213 """ 

214 self._fields[field.name] = field 

215 field.form = self 

216 

217 def get_field(self, name: str) -> Optional[FormField]: 

218 """ 

219 Get a field by name. 

220 

221 Args: 

222 name: The name of the field to get 

223 

224 Returns: 

225 The FormField with the specified name, or None if not found 

226 """ 

227 return self._fields.get(name) 

228 

229 def get_values(self) -> Dict[str, Any]: 

230 """ 

231 Get the current values of all fields in this form. 

232 

233 Returns: 

234 A dictionary mapping field names to their current values 

235 """ 

236 return {name: field.value for name, field in self._fields.items()} 

237 

238 def execute(self) -> Any: 

239 """ 

240 Submit the form, executing the callback with the form values. 

241 

242 Returns: 

243 The result of the callback function, or the form values if no callback is provided. 

244 """ 

245 values = self.get_values() 

246 

247 if self._callback: 

248 return self._callback(self._form_id, values) 

249 

250 return values 

251 

252 

253class FormFieldType(Enum): 

254 """Enumeration of different types of form fields""" 

255 TEXT = 1 

256 PASSWORD = 2 

257 CHECKBOX = 3 

258 RADIO = 4 

259 SELECT = 5 

260 TEXTAREA = 6 

261 NUMBER = 7 

262 DATE = 8 

263 TIME = 9 

264 EMAIL = 10 

265 URL = 11 

266 COLOR = 12 

267 RANGE = 13 

268 HIDDEN = 14 

269 

270 

271class FormField: 

272 """ 

273 A field in a form that can accept user input. 

274 """ 

275 

276 def __init__(self, 

277 name: str, 

278 field_type: FormFieldType, 

279 label: Optional[str] = None, 

280 value: Any = None, 

281 required: bool = False, 

282 options: Optional[List[Tuple[str, str]]] = None): 

283 """ 

284 Initialize a form field. 

285 

286 Args: 

287 name: The name of this field 

288 field_type: The type of this field 

289 label: Optional label for this field 

290 value: Initial value for this field 

291 required: Whether this field is required 

292 options: Options for select, radio, or checkbox fields (list of (value, label) tuples) 

293 """ 

294 self._name = name 

295 self._field_type = field_type 

296 self._label = label or name 

297 self._value = value 

298 self._required = required 

299 self._options = options or [] 

300 self._form: Optional[Form] = None 

301 

302 @property 

303 def name(self) -> str: 

304 """Get the field name""" 

305 return self._name 

306 

307 @property 

308 def field_type(self) -> FormFieldType: 

309 """Get the field type""" 

310 return self._field_type 

311 

312 @property 

313 def label(self) -> str: 

314 """Get the field label""" 

315 return self._label 

316 

317 @property 

318 def value(self) -> Any: 

319 """Get the current field value""" 

320 return self._value 

321 

322 @value.setter 

323 def value(self, value: Any): 

324 """Set the field value""" 

325 self._value = value 

326 

327 @property 

328 def required(self) -> bool: 

329 """Check if the field is required""" 

330 return self._required 

331 

332 @property 

333 def options(self) -> List[Tuple[str, str]]: 

334 """Get the field options""" 

335 return self._options 

336 

337 @property 

338 def form(self) -> Optional[Form]: 

339 """Get the form containing this field""" 

340 return self._form 

341 

342 @form.setter 

343 def form(self, form: Form): 

344 """Set the form containing this field""" 

345 self._form = form