Coverage for src / lexigram / ui / __init__.py: 94%
17 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-10 04:11 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-10 04:11 +0800
1"""Lexigram UI — HTMX/htpy component library for Lexigram web applications.
3Provides general-purpose UI components, layouts, and utilities for building
4server-rendered HTMX applications.
5"""
7from __future__ import annotations
9__path__ = __import__("pkgutil").extend_path(__path__, __name__)
11import importlib.metadata
12from typing import TYPE_CHECKING, Any
14from lexigram.ui.constants import __version__ as __version__
16if TYPE_CHECKING:
17 from lexigram.ui.accessibility import (
18 AriaAttrs,
19 AriaLive,
20 AriaRole,
21 SkipLink,
22 announce,
23 announce_action_complete,
24 announce_selection_change,
25 announce_table_update,
26 button_aria,
27 cell_aria,
28 dialog_aria,
29 header_aria,
30 keyboard_navigation_script,
31 live_region_aria,
32 row_aria,
33 search_aria,
34 tab_aria,
35 table_aria,
36 tabpanel_aria,
37 )
38 from lexigram.ui.atoms.badge import Badge
39 from lexigram.ui.atoms.button import Button, SubmitButton
40 from lexigram.ui.atoms.divider import Divider
41 from lexigram.ui.atoms.editors import MarkdownEditor, RichEditor
42 from lexigram.ui.atoms.fieldset import Fieldset
43 from lexigram.ui.atoms.file_upload import FileUpload
44 from lexigram.ui.atoms.icon import Icon
45 from lexigram.ui.atoms.inputs import (
46 AbstractInput,
47 AvatarUpload,
48 BelongsTo,
49 Checkbox,
50 CheckboxList,
51 ColorPicker,
52 DateInput,
53 EmailInput,
54 Hidden,
55 Input,
56 KeyValueField,
57 LazySelect,
58 MorphTo,
59 MultiFileUpload,
60 MultiSelect,
61 NativeMultiSelect,
62 NumberInput,
63 PasswordInput,
64 Radio,
65 Rating,
66 Select,
67 Slider,
68 TagsInput,
69 TextArea,
70 TextInput,
71 TimePicker,
72 Toggle,
73 )
74 from lexigram.ui.atoms.label import Label
75 from lexigram.ui.atoms.layout import Aside, Col, Container, Grid, Row
76 from lexigram.ui.atoms.link import Link
77 from lexigram.ui.atoms.progress_bar import ProgressBar
78 from lexigram.ui.atoms.skeleton import Skeleton
79 from lexigram.ui.atoms.spinner import Spinner
80 from lexigram.ui.atoms.switch import Switch
81 from lexigram.ui.atoms.tooltip import Tooltip
82 from lexigram.ui.charts import (
83 AreaChart,
84 BarChart,
85 ChartConfig,
86 ChartDataPoint,
87 ChartType,
88 LineChart,
89 MiniBar,
90 PieChart,
91 Sparkline,
92 )
93 from lexigram.ui.config import (
94 BaseLayoutConfig,
95 DebounceConfig,
96 FooterConfig,
97 HeadConfig,
98 HTMLDocumentConfig,
99 ToastConfig,
100 UIConfig,
101 )
102 from lexigram.ui.core.base import (
103 Component,
104 Element,
105 RawHTML,
106 el,
107 raw,
108 render_to_string,
109 )
110 from lexigram.ui.core.context import (
111 UIContext,
112 get_ui_context,
113 reset_ui_context,
114 set_ui_context,
115 )
116 from lexigram.ui.core.zones import SwapMode, Zone, Zones
117 from lexigram.ui.decorators import component
118 from lexigram.ui.di.provider import UIProvider
119 from lexigram.ui.exceptions import (
120 ErrorCategory,
121 ErrorResponse,
122 FieldError,
123 UIError,
124 htmx_error_response,
125 not_found_error,
126 permission_error,
127 render_validation_errors,
128 server_error,
129 timeout_error,
130 validation_error,
131 )
132 from lexigram.ui.htmx import helpers, htmx, sse
133 from lexigram.ui.layouts import (
134 BaseLayoutContext,
135 CSSManager,
136 HTMLDocument,
137 JSManager,
138 LayoutBase,
139 )
140 from lexigram.ui.layouts.footer import FooterLink, FooterRenderer
141 from lexigram.ui.layouts.head import HeadRenderer
142 from lexigram.ui.module import UIModule
143 from lexigram.ui.molecules.action_button import ActionButton
144 from lexigram.ui.molecules.alert import Alert
145 from lexigram.ui.molecules.breadcrumbs import Breadcrumbs
146 from lexigram.ui.molecules.builder import Builder
147 from lexigram.ui.molecules.card import Card
148 from lexigram.ui.molecules.dropdown import Dropdown
149 from lexigram.ui.molecules.empty_state import EmptyState
150 from lexigram.ui.molecules.error_state import ErrorState
151 from lexigram.ui.molecules.form_actions import FormActions
152 from lexigram.ui.molecules.form_field import FormField
153 from lexigram.ui.molecules.input_group import InputGroup
154 from lexigram.ui.molecules.loading_overlay import LoadingOverlay
155 from lexigram.ui.molecules.metric_card import MetricCard
156 from lexigram.ui.molecules.modal import Modal
157 from lexigram.ui.molecules.popover import Popover
158 from lexigram.ui.molecules.realtime import LiveCounter, RealTimeFeed
159 from lexigram.ui.molecules.rich_select import RichSelect
160 from lexigram.ui.molecules.section import Section
161 from lexigram.ui.molecules.simple_alert import SimpleAlert
162 from lexigram.ui.molecules.stack import Stack
163 from lexigram.ui.molecules.stat_card import StatCard
164 from lexigram.ui.molecules.tabs import TabPanel, Tabs
165 from lexigram.ui.molecules.toast import (
166 InlineToast,
167 ServerToastChannel,
168 Toast,
169 ToastData,
170 ToastRenderer,
171 ToastType,
172 flash_to_toast,
173 )
174 from lexigram.ui.molecules.toggle import ToggleIcon
175 from lexigram.ui.molecules.virtual_scroll import (
176 InfiniteScrollTrigger,
177 VirtualScroll,
178 )
179 from lexigram.ui.organisms.activity_feed import ActivityFeed
180 from lexigram.ui.organisms.forms import Form
181 from lexigram.ui.organisms.repeater import Repeater
182 from lexigram.ui.organisms.slide_over import SlideOver
183 from lexigram.ui.performance.observability import (
184 MetricProtocol,
185 MetricsCollector,
186 MetricType,
187 )
188 from lexigram.ui.performance.performance import (
189 RenderCache,
190 RequestCoalescer,
191 ResponseOptimizer,
192 add_htmx_timing_header,
193 cached_render,
194 debounced_search_attrs,
195 infinite_scroll_trigger,
196 lazy_load_placeholder,
197 measure_render_time,
198 optimize_htmx_response,
199 )
200 from lexigram.ui.protocols import RenderableProtocol
202_LAZY_IMPORTS: dict[str, str] = {
203 # ---- Core primitives ----
204 "Component": "lexigram.ui.core.base",
205 "Element": "lexigram.ui.core.base",
206 "RawHTML": "lexigram.ui.core.base",
207 "el": "lexigram.ui.core.base",
208 "raw": "lexigram.ui.core.base",
209 "render_to_string": "lexigram.ui.core.base",
210 # ---- Context ----
211 "UIContext": "lexigram.ui.core.context",
212 "get_ui_context": "lexigram.ui.core.context",
213 "reset_ui_context": "lexigram.ui.core.context",
214 "set_ui_context": "lexigram.ui.core.context",
215 # ---- Zones ----
216 "Zone": "lexigram.ui.core.zones",
217 "Zones": "lexigram.ui.core.zones",
218 "SwapMode": "lexigram.ui.core.zones",
219 # ---- Icons ----
220 "get_icon": "lexigram.ui.atoms.icons",
221 "IconDefinition": "lexigram.ui.atoms.icons",
222 "IconLibrary": "lexigram.ui.atoms.icons",
223 # ---- HTMX namespaces ----
224 "htmx": "lexigram.ui.htmx",
225 "helpers": "lexigram.ui.htmx",
226 "sse": "lexigram.ui.htmx",
227 "HtmxActionResponse": "lexigram.ui.htmx.action_response",
228 # ---- Exceptions & errors ----
229 "UIError": "lexigram.ui.exceptions",
230 "ErrorCategory": "lexigram.ui.exceptions",
231 "FieldError": "lexigram.ui.exceptions",
232 "ErrorResponse": "lexigram.ui.exceptions",
233 "validation_error": "lexigram.ui.exceptions",
234 "not_found_error": "lexigram.ui.exceptions",
235 "permission_error": "lexigram.ui.exceptions",
236 "server_error": "lexigram.ui.exceptions",
237 "timeout_error": "lexigram.ui.exceptions",
238 "render_validation_errors": "lexigram.ui.exceptions",
239 "htmx_error_response": "lexigram.ui.exceptions",
240 # ---- DI ----
241 "UIModule": "lexigram.ui.module",
242 "UIProvider": "lexigram.ui.di.provider",
243 # ---- Protocols ----
244 "RenderableProtocol": "lexigram.ui.protocols",
245 # ---- Decorators ----
246 "component": "lexigram.ui.decorators",
247 # ---- Hooks ----
248 "UIComponentRenderedHook": "lexigram.ui.hooks",
249 "UITemplateRenderedHook": "lexigram.ui.hooks",
250 # ---- Accessibility ----
251 "AriaAttrs": "lexigram.ui.accessibility",
252 "AriaLive": "lexigram.ui.accessibility",
253 "AriaRole": "lexigram.ui.accessibility",
254 "SkipLink": "lexigram.ui.accessibility",
255 "announce": "lexigram.ui.accessibility",
256 "announce_action_complete": "lexigram.ui.accessibility",
257 "announce_selection_change": "lexigram.ui.accessibility",
258 "announce_table_update": "lexigram.ui.accessibility",
259 "button_aria": "lexigram.ui.accessibility",
260 "cell_aria": "lexigram.ui.accessibility",
261 "dialog_aria": "lexigram.ui.accessibility",
262 "header_aria": "lexigram.ui.accessibility",
263 "keyboard_navigation_script": "lexigram.ui.accessibility",
264 "live_region_aria": "lexigram.ui.accessibility",
265 "row_aria": "lexigram.ui.accessibility",
266 "search_aria": "lexigram.ui.accessibility",
267 "tab_aria": "lexigram.ui.accessibility",
268 "tabpanel_aria": "lexigram.ui.accessibility",
269 "table_aria": "lexigram.ui.accessibility",
270 # ---- Config ----
271 "UIConfig": "lexigram.ui.config",
272 "DebounceConfig": "lexigram.ui.config",
273 "HTMLDocumentConfig": "lexigram.ui.config",
274 "BaseLayoutConfig": "lexigram.ui.config",
275 "HeadConfig": "lexigram.ui.config",
276 "FooterConfig": "lexigram.ui.config",
277 "ToastConfig": "lexigram.ui.config",
278 # ---- Atoms — primitives ----
279 "Button": "lexigram.ui.atoms.button",
280 "SubmitButton": "lexigram.ui.atoms.button",
281 "Badge": "lexigram.ui.atoms.badge",
282 "Spinner": "lexigram.ui.atoms.spinner",
283 "Icon": "lexigram.ui.atoms.icon",
284 "Divider": "lexigram.ui.atoms.divider",
285 "Link": "lexigram.ui.atoms.link",
286 "Label": "lexigram.ui.atoms.label",
287 "Fieldset": "lexigram.ui.atoms.fieldset",
288 "FileUpload": "lexigram.ui.atoms.file_upload",
289 "ProgressBar": "lexigram.ui.atoms.progress_bar",
290 "Skeleton": "lexigram.ui.atoms.skeleton",
291 "Switch": "lexigram.ui.atoms.switch",
292 "Tooltip": "lexigram.ui.atoms.tooltip",
293 "MarkdownEditor": "lexigram.ui.atoms.editors",
294 "RichEditor": "lexigram.ui.atoms.editors",
295 # ---- Atoms — layout primitives ----
296 "Aside": "lexigram.ui.atoms.layout",
297 "Col": "lexigram.ui.atoms.layout",
298 "Container": "lexigram.ui.atoms.layout",
299 "Grid": "lexigram.ui.atoms.layout",
300 "Row": "lexigram.ui.atoms.layout",
301 "Stack": "lexigram.ui.molecules.stack",
302 # ---- Atoms — inputs (base + text) ----
303 "AbstractInput": "lexigram.ui.atoms.inputs",
304 "Input": "lexigram.ui.atoms.inputs",
305 "TextInput": "lexigram.ui.atoms.inputs",
306 "PasswordInput": "lexigram.ui.atoms.inputs",
307 "EmailInput": "lexigram.ui.atoms.inputs",
308 "TextArea": "lexigram.ui.atoms.inputs",
309 # ---- Atoms — inputs (numeric & date) ----
310 "NumberInput": "lexigram.ui.atoms.inputs",
311 "Slider": "lexigram.ui.atoms.inputs",
312 "DateInput": "lexigram.ui.atoms.inputs",
313 "TimePicker": "lexigram.ui.atoms.inputs",
314 # ---- Atoms — inputs (selection) ----
315 "Select": "lexigram.ui.atoms.inputs",
316 "MultiSelect": "lexigram.ui.atoms.inputs",
317 "NativeMultiSelect": "lexigram.ui.atoms.inputs",
318 "LazySelect": "lexigram.ui.atoms.inputs",
319 "CheckboxList": "lexigram.ui.atoms.inputs",
320 "Radio": "lexigram.ui.atoms.inputs",
321 "BelongsTo": "lexigram.ui.atoms.inputs",
322 "MorphTo": "lexigram.ui.atoms.inputs",
323 # ---- Atoms — inputs (toggle) ----
324 "Checkbox": "lexigram.ui.atoms.inputs",
325 "Toggle": "lexigram.ui.atoms.inputs",
326 # ---- Atoms — inputs (special) ----
327 "ColorPicker": "lexigram.ui.atoms.inputs",
328 "Hidden": "lexigram.ui.atoms.inputs",
329 "KeyValueField": "lexigram.ui.atoms.inputs",
330 "Rating": "lexigram.ui.atoms.inputs",
331 "TagsInput": "lexigram.ui.atoms.inputs",
332 # ---- Atoms — inputs (file) ----
333 "AvatarUpload": "lexigram.ui.atoms.inputs",
334 "MultiFileUpload": "lexigram.ui.atoms.inputs",
335 # ---- Action button ----
336 "ActionButton": "lexigram.ui.molecules.action_button",
337 # ---- Builder ----
338 "Builder": "lexigram.ui.molecules.builder",
339 # ---- Molecules ----
340 "Alert": "lexigram.ui.molecules.alert",
341 "SimpleAlert": "lexigram.ui.molecules.simple_alert",
342 "Breadcrumbs": "lexigram.ui.molecules.breadcrumbs",
343 "Card": "lexigram.ui.molecules.card",
344 "Dropdown": "lexigram.ui.molecules.dropdown",
345 "EmptyState": "lexigram.ui.molecules.empty_state",
346 "ErrorState": "lexigram.ui.molecules.error_state",
347 "FormField": "lexigram.ui.molecules.form_field",
348 "FieldSchema": "lexigram.ui.molecules.form_field",
349 "FormActions": "lexigram.ui.molecules.form_actions",
350 "InputGroup": "lexigram.ui.molecules.input_group",
351 "LoadingOverlay": "lexigram.ui.molecules.loading_overlay",
352 "MetricCard": "lexigram.ui.molecules.metric_card",
353 "Modal": "lexigram.ui.molecules.modal",
354 "Popover": "lexigram.ui.molecules.popover",
355 "RichSelect": "lexigram.ui.molecules.rich_select",
356 "Section": "lexigram.ui.molecules.section",
357 "StatCard": "lexigram.ui.molecules.stat_card",
358 "Tabs": "lexigram.ui.molecules.tabs",
359 "TabPanel": "lexigram.ui.molecules.tabs",
360 # ---- Molecules — toggle ----
361 "ToggleIcon": "lexigram.ui.molecules.toggle",
362 # ---- Molecules — toasts ----
363 "InlineToast": "lexigram.ui.molecules.toast",
364 "Toast": "lexigram.ui.molecules.toast", # deprecated alias for InlineToast
365 "ToastData": "lexigram.ui.molecules.toast",
366 "ServerToastChannel": "lexigram.ui.molecules.toast",
367 "ToastRenderer": "lexigram.ui.molecules.toast", # deprecated alias for ServerToastChannel
368 "ToastType": "lexigram.ui.molecules.toast",
369 "flash_to_toast": "lexigram.ui.molecules.toast",
370 # ---- Molecules — realtime / scroll ----
371 "InfiniteScrollTrigger": "lexigram.ui.molecules.virtual_scroll",
372 "VirtualScroll": "lexigram.ui.molecules.virtual_scroll",
373 "RealTimeFeed": "lexigram.ui.molecules.realtime",
374 "LiveCounter": "lexigram.ui.molecules.realtime",
375 # ---- Organisms ----
376 "Form": "lexigram.ui.organisms.forms",
377 "Repeater": "lexigram.ui.organisms.repeater",
378 "AreaChart": "lexigram.ui.charts",
379 "BarChart": "lexigram.ui.charts",
380 "ChartConfig": "lexigram.ui.charts",
381 "ChartDataPoint": "lexigram.ui.charts",
382 "ChartType": "lexigram.ui.charts",
383 "LineChart": "lexigram.ui.charts",
384 "MiniBar": "lexigram.ui.charts",
385 "PieChart": "lexigram.ui.charts",
386 "Sparkline": "lexigram.ui.charts",
387 "ActivityFeed": "lexigram.ui.organisms.activity_feed",
388 "AdminCard": "lexigram.ui.organisms.admin",
389 "PageLayout": "lexigram.ui.organisms.admin",
390 "SlideOver": "lexigram.ui.organisms.slide_over",
391 # ---- Layouts ----
392 "LayoutBase": "lexigram.ui.layouts",
393 "BaseLayoutContext": "lexigram.ui.layouts.base_layout",
394 "CSSManager": "lexigram.ui.layouts",
395 "JSManager": "lexigram.ui.layouts",
396 "HTMLDocument": "lexigram.ui.layouts",
397 "FooterLink": "lexigram.ui.layouts.footer",
398 "FooterRenderer": "lexigram.ui.layouts.footer",
399 "HeadRenderer": "lexigram.ui.layouts.head",
400 # ---- Performance ----
401 "RenderCache": "lexigram.ui.performance.performance",
402 "RequestCoalescer": "lexigram.ui.performance.performance",
403 "ResponseOptimizer": "lexigram.ui.performance.performance",
404 "add_htmx_timing_header": "lexigram.ui.performance.performance",
405 "cached_render": "lexigram.ui.performance.performance",
406 "debounced_search_attrs": "lexigram.ui.performance.performance",
407 "infinite_scroll_trigger": "lexigram.ui.performance.performance",
408 "lazy_load_placeholder": "lexigram.ui.performance.performance",
409 "measure_render_time": "lexigram.ui.performance.performance",
410 "optimize_htmx_response": "lexigram.ui.performance.performance",
411 # ---- Observability ----
412 "MetricsCollector": "lexigram.ui.performance.observability",
413 "MetricProtocol": "lexigram.ui.performance.observability",
414 "MetricType": "lexigram.ui.performance.observability",
415 # ---- CLI / Registry ----
416 "COMPONENT_REGISTRY": "lexigram.ui.cli.registry",
417}
420def __getattr__(name: str) -> Any:
421 if name in _LAZY_IMPORTS:
422 import importlib
424 module = importlib.import_module(_LAZY_IMPORTS[name])
425 value = getattr(module, name)
426 globals()[name] = value
427 return value
428 raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
431def __dir__() -> list[str]:
432 return sorted(set(__all__) | set(_LAZY_IMPORTS.keys()))
435__all__ = list(_LAZY_IMPORTS.keys())