tts-html-utils 0.5.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tts_html_utils/__init__.py +0 -0
- tts_html_utils/core/__init__.py +0 -0
- tts_html_utils/core/compiler.py +215 -0
- tts_html_utils/core/components/__init__.py +6 -0
- tts_html_utils/core/components/base.py +300 -0
- tts_html_utils/core/components/favicon.py +17 -0
- tts_html_utils/core/components/flexbox.py +133 -0
- tts_html_utils/core/components/list.py +168 -0
- tts_html_utils/core/components/misc.py +237 -0
- tts_html_utils/core/components/navigation.py +55 -0
- tts_html_utils/core/components/plot.py +166 -0
- tts_html_utils/core/components/structure.py +159 -0
- tts_html_utils/core/components/table.py +392 -0
- tts_html_utils/core/components/text.py +105 -0
- tts_html_utils/css/gantt_chart/gantt_chart.css +330 -0
- tts_html_utils/css/pane_container/pane_container.css +8 -0
- tts_html_utils/django/__init__.py +0 -0
- tts_html_utils/django/forms/__init__.py +0 -0
- tts_html_utils/django/forms/time_range_form.py +56 -0
- tts_html_utils/django/tables/__init__.py +0 -0
- tts_html_utils/django/tables/filter_table.py +201 -0
- tts_html_utils/django/templates/filter_table/filter_table.html +39 -0
- tts_html_utils/django/templates/filter_table/filter_table_rows.html +22 -0
- tts_html_utils/django/templates/filter_table/javascript.html +108 -0
- tts_html_utils/django/templates/filter_table/pagination.html +18 -0
- tts_html_utils/gantt_chart/gantt_chart.py +167 -0
- tts_html_utils/javascript/filter_table/django_html_filter_table.js +85 -0
- tts_html_utils/javascript/filter_table/django_html_table_sort.js +78 -0
- tts_html_utils/javascript/filter_table/show_hide_details.js +24 -0
- tts_html_utils/javascript/filter_table/static_html_filter_table.js +72 -0
- tts_html_utils/javascript/filter_table/static_html_table_sort.js +122 -0
- tts_html_utils/javascript/gantt_chart/gantt_chart.js +908 -0
- tts_html_utils/javascript/pane_container/pane_container.js +141 -0
- tts_html_utils/javascript/pane_container.js +43 -0
- tts_html_utils/javascript/plotlyv1.58.5.js +62 -0
- tts_html_utils/javascript/visdiff_tables/hide_columns_without_mismatch.js +35 -0
- tts_html_utils/javascript/visdiff_tables/row_height_match.js +55 -0
- tts_html_utils/javascript/visdiff_tables/scroll_sync.js +29 -0
- tts_html_utils/jupyter/__init__.py +0 -0
- tts_html_utils/jupyter/grid.py +465 -0
- tts_html_utils/resource.py +225 -0
- tts_html_utils/resources/css/navbar.css +39 -0
- tts_html_utils/resources/html_templates/full_report.html +56 -0
- tts_html_utils/resources/html_templates/generic.html +2 -0
- tts_html_utils/resources/html_templates/generic_singleton.html +2 -0
- tts_html_utils/resources/html_templates/helpers.html +31 -0
- tts_html_utils/resources/html_templates/plotly_config.html +6 -0
- tts_html_utils/resources/html_templates/script_general.html +3 -0
- tts_html_utils/resources/html_templates/table.html +21 -0
- tts_html_utils/resources/html_templates/test.html +4 -0
- tts_html_utils/resources/img/favicon.png +0 -0
- tts_html_utils/resources/report_base.css +474 -0
- tts_html_utils/resources/rule_result_table.css +40 -0
- tts_html_utils/resources/rule_summary.css +79 -0
- tts_html_utils/resources/scripts/collapsible.js +40 -0
- tts_html_utils/resources/scripts/link_functions.js +13 -0
- tts_html_utils/resources/scripts/open_tab_function.js +29 -0
- tts_html_utils/resources/scripts/resize_bodyheight.js +7 -0
- tts_html_utils/resources/scripts/sorttable.js +489 -0
- tts_html_utils/resources/scripts/sticky_headers.js +11 -0
- tts_html_utils/test/__init__.py +0 -0
- tts_html_utils/test/test_base.py +110 -0
- tts_html_utils/test/test_compiler.py +227 -0
- tts_html_utils/test/test_list.py +127 -0
- tts_html_utils/test/test_misc.py +145 -0
- tts_html_utils/test/test_navigation.py +59 -0
- tts_html_utils/test/test_plot.py +150 -0
- tts_html_utils/test/test_resource.py +133 -0
- tts_html_utils/test/test_structure.py +176 -0
- tts_html_utils/test/test_table.py +256 -0
- tts_html_utils/test/test_text.py +62 -0
- tts_html_utils/visdiff/visdiff.py +98 -0
- tts_html_utils-0.5.4.dist-info/METADATA +72 -0
- tts_html_utils-0.5.4.dist-info/RECORD +76 -0
- tts_html_utils-0.5.4.dist-info/WHEEL +5 -0
- tts_html_utils-0.5.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#Standard Library Imports
|
|
2
|
+
import pdb
|
|
3
|
+
|
|
4
|
+
#Installed dependency imports
|
|
5
|
+
#None yet
|
|
6
|
+
|
|
7
|
+
#Teamtools Studio Imports
|
|
8
|
+
from tts_utilities.logger import create_logger
|
|
9
|
+
|
|
10
|
+
#This Library Imports
|
|
11
|
+
from tts_html_utils.core.components.base import HtmlComponent, HtmlComponentSimple
|
|
12
|
+
|
|
13
|
+
logger = create_logger(name='html_utils.core.components.base')
|
|
14
|
+
|
|
15
|
+
class UnorderedList(HtmlComponentSimple):
|
|
16
|
+
"""
|
|
17
|
+
A standard bulleted list (<ul>).
|
|
18
|
+
|
|
19
|
+
**Concept:**
|
|
20
|
+
Used for items where the order does not matter. Browsers typically
|
|
21
|
+
render these with bullet points. This is also a common structural
|
|
22
|
+
element that is used without looking like a literal list.
|
|
23
|
+
|
|
24
|
+
**Alias:** `UL`
|
|
25
|
+
"""
|
|
26
|
+
TAG = 'ul'
|
|
27
|
+
|
|
28
|
+
UL = UnorderedList
|
|
29
|
+
|
|
30
|
+
class OrderedList(HtmlComponentSimple):
|
|
31
|
+
"""
|
|
32
|
+
A numbered list (<ol>).
|
|
33
|
+
|
|
34
|
+
**Concept:**
|
|
35
|
+
Used for items where the sequence is important. Browsers typically render
|
|
36
|
+
these with numbers (1, 2, 3...) or letters (a, b, c...). Much less common
|
|
37
|
+
than an unordered list.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
**Alias:** `OL`
|
|
41
|
+
"""
|
|
42
|
+
TAG = 'ol'
|
|
43
|
+
OL = OrderedList
|
|
44
|
+
|
|
45
|
+
class ListItem(HtmlComponentSimple):
|
|
46
|
+
"""
|
|
47
|
+
A single row within a list (<li>).
|
|
48
|
+
|
|
49
|
+
**Concept:**
|
|
50
|
+
Neither `<ul>` nor `<ol>` can directly contain text. They can only contain `<li>` tags.
|
|
51
|
+
This component wraps the actual content of the list entry.
|
|
52
|
+
|
|
53
|
+
**Alias:** `LI`
|
|
54
|
+
"""
|
|
55
|
+
TAG = 'li'
|
|
56
|
+
LI = ListItem
|
|
57
|
+
|
|
58
|
+
class PowerList(HtmlComponent):
|
|
59
|
+
"""
|
|
60
|
+
A smart builder for creating complex, nested lists without the headache.
|
|
61
|
+
|
|
62
|
+
**The Problem:**
|
|
63
|
+
Creating nested HTML lists is very repetative. You have to remember to open a `<ul>`,
|
|
64
|
+
add an `<li>`, open a new `<ul>` *inside* that `<li>` for the sub-items, and then
|
|
65
|
+
close everything in the exact reverse order.
|
|
66
|
+
|
|
67
|
+
**The Solution:**
|
|
68
|
+
`PowerList` works like a text editor's outline mode. You simply add a line, and
|
|
69
|
+
command it to "indent" (`up_level`) or "outdent" (`down_level`). It tracks
|
|
70
|
+
where you are in the hierarchy and handles all the tag nesting for you automatically.
|
|
71
|
+
|
|
72
|
+
:param children: Initial list items to populate. (Usually better to use `.line()` after init).
|
|
73
|
+
:type children: List, optional
|
|
74
|
+
:param class_name: CSS classes for the root list element.
|
|
75
|
+
:type class_name: str or list of str, optional
|
|
76
|
+
:param extra_class_name: Additional CSS classes to append.
|
|
77
|
+
:type extra_class_name: str or list of str, optional
|
|
78
|
+
:param style: Inline CSS styles for the root list element.
|
|
79
|
+
:type style: dict, optional
|
|
80
|
+
:param ordered: If True, renders as a numbered list (`<ol>`). If False, renders as bullets (`<ul>`). Defaults to False.
|
|
81
|
+
:type ordered: bool
|
|
82
|
+
"""
|
|
83
|
+
def __init__(self, children=None, class_name=None, extra_class_name=None, style=None, ordered=False):
|
|
84
|
+
super().__init__(children=children, class_name=class_name, extra_class_name=extra_class_name, style=style)
|
|
85
|
+
self.ordered = ordered
|
|
86
|
+
|
|
87
|
+
# 'target' acts as a cursor. It points to the specific list (root or nested)
|
|
88
|
+
# where the next item should be added.
|
|
89
|
+
self.target = [self]
|
|
90
|
+
|
|
91
|
+
def line(self, line, class_name=None, extra_class_name=None, style=None):
|
|
92
|
+
"""
|
|
93
|
+
Appends a new item to the current level of the list.
|
|
94
|
+
|
|
95
|
+
:param line: The content of the item (text or another HtmlComponent).
|
|
96
|
+
:type line: str or HtmlComponent
|
|
97
|
+
:param class_name: CSS classes for this specific list item (`<li>`).
|
|
98
|
+
:type class_name: str or list of str, optional
|
|
99
|
+
:param extra_class_name: Additional CSS classes to append.
|
|
100
|
+
:type extra_class_name: str or list of str, optional
|
|
101
|
+
:param style: Inline CSS styles for this specific list item.
|
|
102
|
+
:type style: dict, optional
|
|
103
|
+
"""
|
|
104
|
+
if not isinstance(line, ListItem):
|
|
105
|
+
line = ListItem(children=line, class_name=class_name, extra_class_name=extra_class_name, style=style)
|
|
106
|
+
|
|
107
|
+
self.target[-1].add_child(line)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def up_level(self, label=None, new_list=None, class_name=None, extra_class_name=None, style=None, ordered=None):
|
|
111
|
+
"""
|
|
112
|
+
Indents the list (creates a sub-list).
|
|
113
|
+
|
|
114
|
+
**How it works:**
|
|
115
|
+
This creates a new list (container) and nests it inside the *last item* you added.
|
|
116
|
+
The internal "cursor" moves to this new list, so subsequent `.line()` calls
|
|
117
|
+
will appear as sub-items.
|
|
118
|
+
|
|
119
|
+
:param label: Optional text to place on the parent item *before* the sub-list starts.
|
|
120
|
+
:type label: str or HtmlComponent, optional
|
|
121
|
+
:param new_list: If you have already built a `UL` or `OL` object, you can pass it here to insert it.
|
|
122
|
+
:type new_list: UL or OL, optional
|
|
123
|
+
:param class_name: CSS classes for the new sub-list container.
|
|
124
|
+
:type class_name: str or list of str, optional
|
|
125
|
+
:param extra_class_name: Additional CSS classes to append.
|
|
126
|
+
:type extra_class_name: str or list of str, optional
|
|
127
|
+
:param style: Inline CSS styles for the new sub-list container.
|
|
128
|
+
:type style: dict, optional
|
|
129
|
+
:param ordered: If True, the sub-list will be numbered. If False, bulleted. Defaults to the parent list's type.
|
|
130
|
+
:type ordered: bool, optional
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
if not new_list:
|
|
134
|
+
if ordered is None:
|
|
135
|
+
ordered = self.ordered
|
|
136
|
+
|
|
137
|
+
# FIX: Pass the styling arguments to the new PowerList
|
|
138
|
+
new_list = PowerList(
|
|
139
|
+
ordered=ordered,
|
|
140
|
+
class_name=class_name,
|
|
141
|
+
extra_class_name=extra_class_name,
|
|
142
|
+
style=style
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if label is not None:
|
|
146
|
+
self.target[-1].add_child(ListItem(children=[label, new_list]))
|
|
147
|
+
else:
|
|
148
|
+
self.target[-1].add_child(ListItem(children=new_list))
|
|
149
|
+
self.target.append(new_list)
|
|
150
|
+
|
|
151
|
+
def down_level(self):
|
|
152
|
+
"""
|
|
153
|
+
Outdents the list (returns to the parent level).
|
|
154
|
+
|
|
155
|
+
**How it works:**
|
|
156
|
+
Moves the internal "cursor" back up to the parent list.
|
|
157
|
+
"""
|
|
158
|
+
if len(self.target) <= 1:
|
|
159
|
+
return # Do nothing if we're at the root level already
|
|
160
|
+
self.target.pop()
|
|
161
|
+
|
|
162
|
+
def render(self):
|
|
163
|
+
"""
|
|
164
|
+
Converts the internal PowerList structure into standard HTML components
|
|
165
|
+
(`UL`/`OL` and `LI`) and renders them to a string.
|
|
166
|
+
"""
|
|
167
|
+
top_list = OrderedList if self.ordered else UnorderedList
|
|
168
|
+
return top_list(children=self.children, class_name=self.class_name, style=self.style).rendered
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#Standard Library Imports
|
|
2
|
+
import pdb
|
|
3
|
+
|
|
4
|
+
#Installed dependency imports
|
|
5
|
+
#None yet
|
|
6
|
+
|
|
7
|
+
#Teamtools Studio Imports
|
|
8
|
+
from tts_utilities.logger import create_logger
|
|
9
|
+
|
|
10
|
+
#This Library Imports
|
|
11
|
+
from tts_html_utils.core.components.base import HtmlComponentSimple, HtmlComponentSingleSimple
|
|
12
|
+
from tts_html_utils.core.components.favicon import favicon_data
|
|
13
|
+
|
|
14
|
+
logger = create_logger(name='html_utils.core.components.base')
|
|
15
|
+
|
|
16
|
+
class HTML(HtmlComponentSimple):
|
|
17
|
+
"""
|
|
18
|
+
The root container for the entire document.
|
|
19
|
+
|
|
20
|
+
**Concept:**
|
|
21
|
+
This is the outermost shell of a webpage. Every other element (Head, Body, etc.)
|
|
22
|
+
lives inside this tag.
|
|
23
|
+
|
|
24
|
+
**Usage Note:**
|
|
25
|
+
You rarely need to instantiate this yourself. The `HtmlCompiler` typically
|
|
26
|
+
creates this automatically when you build a page.
|
|
27
|
+
"""
|
|
28
|
+
TAG = 'html'
|
|
29
|
+
|
|
30
|
+
class Head(HtmlComponentSimple):
|
|
31
|
+
"""
|
|
32
|
+
The "Settings" section of the document.
|
|
33
|
+
|
|
34
|
+
**Concept:**
|
|
35
|
+
The `<head>` contains metadata—information *about* the page rather than content
|
|
36
|
+
*on* the page. It holds the title, CSS links, scripts, and browser instructions.
|
|
37
|
+
Nothing put here is displayed in the main browser window.
|
|
38
|
+
|
|
39
|
+
**Usage Note:**
|
|
40
|
+
Like `HTML`, this is usually managed automatically by the `HtmlCompiler`.
|
|
41
|
+
"""
|
|
42
|
+
TAG = 'head'
|
|
43
|
+
|
|
44
|
+
class Body(HtmlComponentSimple):
|
|
45
|
+
"""
|
|
46
|
+
The "Canvas" of the document.
|
|
47
|
+
|
|
48
|
+
**Concept:**
|
|
49
|
+
The `<body>` contains everything the user actually sees: text, images, buttons,
|
|
50
|
+
and tables. If you are building a component to display data, it eventually
|
|
51
|
+
lives inside here.
|
|
52
|
+
|
|
53
|
+
**Usage Note:**
|
|
54
|
+
Managed automatically by the `HtmlCompiler`. You typically just add children
|
|
55
|
+
to the compiler, and it places them in the Body for you.
|
|
56
|
+
"""
|
|
57
|
+
TAG = 'body'
|
|
58
|
+
|
|
59
|
+
class Header(HtmlComponentSimple):
|
|
60
|
+
"""
|
|
61
|
+
A semantic container for introductory content.
|
|
62
|
+
|
|
63
|
+
**Concept:**
|
|
64
|
+
Not to be confused with `<head>`. The `<header>` is a visible section of the page,
|
|
65
|
+
usually containing the logo, navigation bar, and page title. It helps screen readers
|
|
66
|
+
and search engines understand the structure of your content.
|
|
67
|
+
"""
|
|
68
|
+
TAG = 'header'
|
|
69
|
+
|
|
70
|
+
class Footer(HtmlComponentSimple):
|
|
71
|
+
"""
|
|
72
|
+
A semantic container for the bottom of a page or section.
|
|
73
|
+
|
|
74
|
+
**Concept:**
|
|
75
|
+
Usually contains copyright notices, contact info, or links to privacy policies.
|
|
76
|
+
Like `<header>`, this is a "semantic" tag—it behaves exactly like a `Div`
|
|
77
|
+
visually, but it tells search engines "this is the end of the content."
|
|
78
|
+
"""
|
|
79
|
+
TAG = 'footer'
|
|
80
|
+
|
|
81
|
+
class Style(HtmlComponentSimple):
|
|
82
|
+
"""
|
|
83
|
+
A container for raw CSS code.
|
|
84
|
+
|
|
85
|
+
**Concept:**
|
|
86
|
+
Allows you to write CSS rules directly within the HTML file rather than linking
|
|
87
|
+
to an external `.css` file.
|
|
88
|
+
|
|
89
|
+
**Usage Note:**
|
|
90
|
+
The `HtmlCompiler` uses this to inject CSS dependencies. You generally shouldn't
|
|
91
|
+
need to use this manually; prefer creating `.css` files and linking them.
|
|
92
|
+
"""
|
|
93
|
+
TAG = 'style'
|
|
94
|
+
|
|
95
|
+
class Script(HtmlComponentSimple):
|
|
96
|
+
"""
|
|
97
|
+
A container for raw JavaScript code.
|
|
98
|
+
|
|
99
|
+
**Concept:**
|
|
100
|
+
Allows you to write JavaScript logic directly within the HTML file.
|
|
101
|
+
|
|
102
|
+
**Usage Note:**
|
|
103
|
+
The `HtmlCompiler` uses this to inject JS dependencies. For complex logic,
|
|
104
|
+
prefer creating `.js` files and letting the compiler link them.
|
|
105
|
+
"""
|
|
106
|
+
TAG = 'script'
|
|
107
|
+
|
|
108
|
+
class Title(HtmlComponentSimple):
|
|
109
|
+
"""
|
|
110
|
+
The browser tab name.
|
|
111
|
+
|
|
112
|
+
**Concept:**
|
|
113
|
+
Defines the text that appears in the browser tab, bookmarks, and search engine results.
|
|
114
|
+
It does not appear in the page content itself.
|
|
115
|
+
"""
|
|
116
|
+
TAG = 'title'
|
|
117
|
+
|
|
118
|
+
class Div(HtmlComponentSimple):
|
|
119
|
+
"""
|
|
120
|
+
The generic "Lego Brick" of the web.
|
|
121
|
+
|
|
122
|
+
**Concept:**
|
|
123
|
+
The `<div>` (Division) is a generic container used to group other elements together
|
|
124
|
+
for styling or layout purposes. It has no semantic meaning (unlike `<header>` or `<footer>`).
|
|
125
|
+
|
|
126
|
+
Most modern web layouts are essentially nested structures of Divs.
|
|
127
|
+
"""
|
|
128
|
+
TAG = 'div'
|
|
129
|
+
|
|
130
|
+
class TtsFavicon(Script):
|
|
131
|
+
"""
|
|
132
|
+
Injects the Teamtools Studio logo into the browser tab.
|
|
133
|
+
|
|
134
|
+
**How it works:**
|
|
135
|
+
Instead of requiring you to manage a physical `.ico` or `.png` file on the server,
|
|
136
|
+
this class contains the base64-encoded image data of the logo. It uses a small
|
|
137
|
+
JavaScript snippet to create the link tag dynamically and attach it to the document head.
|
|
138
|
+
"""
|
|
139
|
+
def __init__(self):
|
|
140
|
+
super().__init__()
|
|
141
|
+
self.js_includes = [('\n'.join([
|
|
142
|
+
"const favicon = document.createElement('link');",
|
|
143
|
+
"favicon.rel = 'icon';",
|
|
144
|
+
"favicon.type = 'image/png';",
|
|
145
|
+
f"favicon.href = '{favicon_data}';",
|
|
146
|
+
"document.head.appendChild(favicon);",
|
|
147
|
+
]), {})]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class HorizontalBreak(HtmlComponentSingleSimple):
|
|
151
|
+
"""
|
|
152
|
+
A thematic break (Horizontal Rule).
|
|
153
|
+
|
|
154
|
+
**Concept:**
|
|
155
|
+
Renders a horizontal line across the container. Used to visually separate
|
|
156
|
+
different topics or sections of content.
|
|
157
|
+
|
|
158
|
+
**Alias:** `HR`
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
TAG = 'hr'
|
|
162
|
+
DEFAULT_CLASS=['faded']
|
|
163
|
+
|
|
164
|
+
HR = HorizontalBreak
|
|
165
|
+
|
|
166
|
+
class LineBreak(HtmlComponentSingleSimple):
|
|
167
|
+
"""
|
|
168
|
+
A forced line break.
|
|
169
|
+
|
|
170
|
+
**Concept:**
|
|
171
|
+
Forces the text following it to start on a new line.
|
|
172
|
+
|
|
173
|
+
**Usage Note:**
|
|
174
|
+
Use sparingly. For spacing between paragraphs, it is usually better to use
|
|
175
|
+
CSS margins or separate `<p>` (Paragraph) tags rather than multiple `<br>` tags.
|
|
176
|
+
|
|
177
|
+
**Alias:** `BR`
|
|
178
|
+
"""
|
|
179
|
+
TAG = 'br'
|
|
180
|
+
|
|
181
|
+
BR = LineBreak
|
|
182
|
+
|
|
183
|
+
class Link(HtmlComponentSimple):
|
|
184
|
+
"""
|
|
185
|
+
A hyperlink (Anchor tag).
|
|
186
|
+
|
|
187
|
+
**Concept:**
|
|
188
|
+
The fundamental connector of the web. Links one page (or section) to another.
|
|
189
|
+
|
|
190
|
+
**Alias:** `A`
|
|
191
|
+
|
|
192
|
+
:param text: The clickable text the user sees.
|
|
193
|
+
:type text: str
|
|
194
|
+
:param href: The destination URL. If None, defaults to the `text` (useful if pasting raw URLs).
|
|
195
|
+
:type href: str, optional
|
|
196
|
+
:param new_tab: If True, clicking the link opens a new browser tab (`target="_blank"`).
|
|
197
|
+
If False, it navigates the current tab. Defaults to True.
|
|
198
|
+
:type new_tab: bool, optional
|
|
199
|
+
:param kwargs: Additional keyword arguments passed to the `HtmlComponent` constructor.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
TAG='a'
|
|
203
|
+
def __init__(self, text, href=None, new_tab=True, **kwargs):
|
|
204
|
+
if href is None: href = text
|
|
205
|
+
attr = {'href': href, 'target': '_blank' if new_tab else '_self'}
|
|
206
|
+
if 'attr' in kwargs.keys():
|
|
207
|
+
attr = {**attr, **kwargs['attr']}
|
|
208
|
+
del kwargs['attr']
|
|
209
|
+
super().__init__(children=text, attr=attr, **kwargs)
|
|
210
|
+
|
|
211
|
+
A = Link
|
|
212
|
+
|
|
213
|
+
class Button(HtmlComponentSimple):
|
|
214
|
+
"""
|
|
215
|
+
A clickable button element.
|
|
216
|
+
|
|
217
|
+
**Concept:**
|
|
218
|
+
Used for actions (like "Submit", "Calculate", "Toggle"), as opposed to Links
|
|
219
|
+
which are used for navigation.
|
|
220
|
+
|
|
221
|
+
:param text: The label displayed on the button.
|
|
222
|
+
:type text: str
|
|
223
|
+
:param button_type: The HTML behavior type.
|
|
224
|
+
- 'button': A generic clickable button (requires JS to do anything).
|
|
225
|
+
- 'submit': Submits a parent form.
|
|
226
|
+
- 'reset': Resets a parent form.
|
|
227
|
+
:type button_type: str, optional
|
|
228
|
+
:param kwargs: Additional keyword arguments passed to the `HtmlComponent` constructor.
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
TAG='button'
|
|
232
|
+
def __init__(self, text, button_type='button', **kwargs):
|
|
233
|
+
attr = {'type': button_type}
|
|
234
|
+
if 'attr' in kwargs.keys():
|
|
235
|
+
attr = {**attr, **kwargs['attr']}
|
|
236
|
+
del kwargs['attr']
|
|
237
|
+
super().__init__(children=text, attr=attr, **kwargs)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#Standard Library Imports
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import pdb
|
|
4
|
+
|
|
5
|
+
#Installed dependency imports
|
|
6
|
+
#None yet
|
|
7
|
+
|
|
8
|
+
#Teamtools Studio Imports
|
|
9
|
+
from tts_utilities.logger import create_logger
|
|
10
|
+
|
|
11
|
+
#This Library Imports
|
|
12
|
+
from tts_html_utils.core.components.list import PowerList, ListItem
|
|
13
|
+
from tts_html_utils.core.components.misc import Link
|
|
14
|
+
|
|
15
|
+
logger = create_logger(name='html_utils.core.components.base')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Navbar(PowerList):
|
|
19
|
+
"""
|
|
20
|
+
Simple class to streamline creation of a navbar, which is essentially an unordered list
|
|
21
|
+
of links.
|
|
22
|
+
|
|
23
|
+
Nothing special now, but eventually this will be part of stock templates and will include out of the box CSS
|
|
24
|
+
|
|
25
|
+
:param mapping: Dictionary where keys are labels and values are URLs
|
|
26
|
+
:type style: dict
|
|
27
|
+
|
|
28
|
+
:param new_tab: Should links open to a new tab or in the current one?
|
|
29
|
+
:type new_tab: bool
|
|
30
|
+
|
|
31
|
+
:param kwargs: Additional keyword arguments passed to the `HtmlComponent` constructor.
|
|
32
|
+
"""
|
|
33
|
+
def __init__(self, mapping, new_tab=False, **kwargs):
|
|
34
|
+
super().__init__(class_name='navbar', **kwargs)
|
|
35
|
+
self.add_items(mapping, new_tab=new_tab)
|
|
36
|
+
self.css_includes = [(Path(__file__).parent.parent.parent.joinpath('resources/css/navbar.css'),{})]
|
|
37
|
+
|
|
38
|
+
def add_items(self, mapping, new_tab=False):
|
|
39
|
+
"""
|
|
40
|
+
Add an item to an existing Navbar. This is equivalent to including the item in the mapping parameter of __init__()
|
|
41
|
+
|
|
42
|
+
:param mapping: Dictionary where keys are labels and values are URLs
|
|
43
|
+
:type style: dict
|
|
44
|
+
|
|
45
|
+
:param new_tab: Should links open to a new tab or in the current one?
|
|
46
|
+
:type new_tab: bool
|
|
47
|
+
"""
|
|
48
|
+
for label, href in mapping.items():
|
|
49
|
+
if isinstance(href, dict):
|
|
50
|
+
self.up_level(label=label)
|
|
51
|
+
self.add_items(href)
|
|
52
|
+
self.down_level()
|
|
53
|
+
else:
|
|
54
|
+
self.line(Link(label, href=href, new_tab=new_tab), class_name='navbartab')
|
|
55
|
+
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#Standard Library Imports
|
|
2
|
+
import pdb
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
#Installed dependency imports
|
|
6
|
+
import plotly.graph_objects as go
|
|
7
|
+
import plotly.express as px
|
|
8
|
+
import plotly.io as pio
|
|
9
|
+
|
|
10
|
+
#Teamtools Studio Imports
|
|
11
|
+
from tts_utilities.logger import create_logger
|
|
12
|
+
|
|
13
|
+
#This Library Imports
|
|
14
|
+
from tts_html_utils.core.components.base import HtmlComponent
|
|
15
|
+
from tts_html_utils.core.components.misc import Div
|
|
16
|
+
|
|
17
|
+
logger = create_logger(name='html_utils.core.components.base')
|
|
18
|
+
|
|
19
|
+
class PlotBase(HtmlComponent):
|
|
20
|
+
"""
|
|
21
|
+
The base wrapper for embedding Plotly figures into the HTML document.
|
|
22
|
+
|
|
23
|
+
**Concept:**
|
|
24
|
+
Plotly produces interactive, JavaScript-based charts. To put them in an HTML file,
|
|
25
|
+
we need to convert the Python `Figure` object into a specific block of HTML/JS code.
|
|
26
|
+
|
|
27
|
+
**Important Note on Performance:**
|
|
28
|
+
This component renders with `include_plotlyjs=False`. The global `plotly.js` library
|
|
29
|
+
is automatically requested via the component's `js_includes` and managed by the
|
|
30
|
+
`HtmlCompiler` to ensure it is loaded once in the `<head>`.
|
|
31
|
+
|
|
32
|
+
:param fig: The existing Plotly figure to render. If None, creates a blank figure.
|
|
33
|
+
:type fig: plotly.graph_objects.Figure, optional
|
|
34
|
+
:param title: Sets the title of the plot layout.
|
|
35
|
+
:type title: str, optional
|
|
36
|
+
:param template: The visual theme to use (e.g., 'plotly_dark', 'seaborn', 'ggplot2').
|
|
37
|
+
Defaults to 'seaborn'.
|
|
38
|
+
:type template: str, optional
|
|
39
|
+
:param class_name: Primary CSS class for the container Div.
|
|
40
|
+
:type class_name: str, optional
|
|
41
|
+
:param extra_class_name: Additional CSS classes.
|
|
42
|
+
:type extra_class_name: str, optional
|
|
43
|
+
:param id: Optional. A custom ID for this container. If not provided, a unique ID will be generated.
|
|
44
|
+
:type id: str, optional
|
|
45
|
+
:param style: Dictionary of inline CSS styles for the container Div.
|
|
46
|
+
:type style: dict, optional
|
|
47
|
+
"""
|
|
48
|
+
DEFAULT_PLOTLY_TEMPLATE = 'seaborn'
|
|
49
|
+
def __init__(self, fig=None, title=None, template=None, class_name=None, extra_class_name=None, id=None, style=None, include_plotlyjs=False, full_html=False):
|
|
50
|
+
self.fig = fig or go.Figure()
|
|
51
|
+
self.fig.update_layout(template=(template or self.DEFAULT_PLOTLY_TEMPLATE))
|
|
52
|
+
self.include_plotlyjs = include_plotlyjs
|
|
53
|
+
self.full_html = full_html
|
|
54
|
+
|
|
55
|
+
if title:
|
|
56
|
+
self.fig.update_layout(title=title)
|
|
57
|
+
|
|
58
|
+
# Ensure every plot has a unique ID for JS scoping if one isn't provided
|
|
59
|
+
if id is None:
|
|
60
|
+
id = f"plotly-{uuid.uuid4().hex[:8]}"
|
|
61
|
+
|
|
62
|
+
super().__init__(children=None, class_name=class_name, extra_class_name=extra_class_name, id=id, style=style)
|
|
63
|
+
|
|
64
|
+
# Automatically include the Plotly global library via CDN
|
|
65
|
+
# The HtmlCompiler will deduplicate this if multiple plots are present.
|
|
66
|
+
self.js_includes.append(("https://cdn.plot.ly/plotly-3.3.1.min.js", {}))
|
|
67
|
+
|
|
68
|
+
def render(self):
|
|
69
|
+
"""
|
|
70
|
+
Converts the Python Plotly figure into an HTML Div string.
|
|
71
|
+
"""
|
|
72
|
+
# Generate the Plotly HTML snippet.
|
|
73
|
+
# This already includes a <div id="self.id"> and a <script>.
|
|
74
|
+
plotly_html = pio.to_html(
|
|
75
|
+
self.fig,
|
|
76
|
+
include_plotlyjs=self.include_plotlyjs,
|
|
77
|
+
full_html=self.full_html,
|
|
78
|
+
div_id=self.id
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return plotly_html
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class ScatterPlot(PlotBase):
|
|
85
|
+
"""
|
|
86
|
+
A helper for creating basic line/scatter plots.
|
|
87
|
+
|
|
88
|
+
**Legacy Note:** This is a legacy wrapper. For complex visualization needs, it is generally
|
|
89
|
+
recommended to use `dtat-lib` or build `go.Figure` objects directly and pass
|
|
90
|
+
them to `PlotBase`.
|
|
91
|
+
"""
|
|
92
|
+
def add_trace(self, y, x=None, name=None, lines=False, markers=True, trace_kwargs=None, scatter_kwargs=None):
|
|
93
|
+
"""
|
|
94
|
+
Adds a new data series (trace) to the plot.
|
|
95
|
+
|
|
96
|
+
:param y: The Y-axis data points.
|
|
97
|
+
:type y: list or array-like
|
|
98
|
+
:param x: The X-axis data points.
|
|
99
|
+
:type x: list or array-like, optional
|
|
100
|
+
:param name: The name of this series (appears in the legend).
|
|
101
|
+
:type name: str, optional
|
|
102
|
+
:param lines: If True, connects points with lines.
|
|
103
|
+
:type lines: bool, default False
|
|
104
|
+
:param markers: If True, draws a dot at each data point.
|
|
105
|
+
:type markers: bool, default True
|
|
106
|
+
:param trace_kwargs: A passthrough dictionary of arguments sent to `self.fig.add_trace`.
|
|
107
|
+
Useful for advanced layout options like `secondary_y=True`.
|
|
108
|
+
:type trace_kwargs: dict, optional
|
|
109
|
+
:param scatter_kwargs: A passthrough dictionary of arguments sent to `go.Scatter`.
|
|
110
|
+
Useful for styling options like `line_color`, `marker_size`, etc.
|
|
111
|
+
:type scatter_kwargs: dict, optional
|
|
112
|
+
"""
|
|
113
|
+
modes = []
|
|
114
|
+
if lines:
|
|
115
|
+
modes.append('lines')
|
|
116
|
+
if markers:
|
|
117
|
+
modes.append('markers')
|
|
118
|
+
|
|
119
|
+
trace_kwargs = trace_kwargs or {}
|
|
120
|
+
scatter_kwargs = scatter_kwargs or {}
|
|
121
|
+
|
|
122
|
+
self.fig.add_trace(
|
|
123
|
+
go.Scatter(
|
|
124
|
+
x=x,
|
|
125
|
+
y=y,
|
|
126
|
+
mode='+'.join(modes),
|
|
127
|
+
name=name,
|
|
128
|
+
**scatter_kwargs
|
|
129
|
+
),
|
|
130
|
+
**trace_kwargs
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
class TimelinePlot(PlotBase):
|
|
134
|
+
"""
|
|
135
|
+
A helper for creating Gantt-style timeline charts.
|
|
136
|
+
|
|
137
|
+
**Legacy Note:**
|
|
138
|
+
This is a legacy wrapper. Consider using `dtat-lib` for modern timeline visualization.
|
|
139
|
+
"""
|
|
140
|
+
def __init__(self, data, x_start_name, x_end_name, y_name, color=None, title=None, date_format_str=None):
|
|
141
|
+
"""
|
|
142
|
+
Initialize the timeline using `plotly.express.timeline`.
|
|
143
|
+
|
|
144
|
+
:param data: The dataset (usually a Pandas DataFrame).
|
|
145
|
+
:type data: pandas.DataFrame or list of dicts
|
|
146
|
+
:param x_start_name: The column name containing start timestamps.
|
|
147
|
+
:type x_start_name: str
|
|
148
|
+
:param x_end_name: The column name containing end timestamps.
|
|
149
|
+
:type x_end_name: str
|
|
150
|
+
:param y_name: The column name to use for the Y-axis labels (tasks/rows).
|
|
151
|
+
:type y_name: str
|
|
152
|
+
:param color: The column name to use for coloring the bars.
|
|
153
|
+
:type color: str, optional
|
|
154
|
+
:param title: The chart title.
|
|
155
|
+
:type title: str, optional
|
|
156
|
+
:param date_format_str: A d3-formatting string to control how dates appear on the X-axis
|
|
157
|
+
(e.g., '%Y-%m-%d').
|
|
158
|
+
:type date_format_str: str, optional
|
|
159
|
+
"""
|
|
160
|
+
timeline = px.timeline(data, x_start=x_start_name, x_end=x_end_name, y=y_name, color=color)
|
|
161
|
+
|
|
162
|
+
super().__init__(fig=timeline, title=title)
|
|
163
|
+
|
|
164
|
+
# optionally update the formatting of the x-axis ticks (which are datetime compliant for all timeline plots)
|
|
165
|
+
if date_format_str:
|
|
166
|
+
self.fig.update_layout(xaxis_tickformat = date_format_str)
|