{{breadcrumbs}}
HUB / lib_html2_widgets.py
lib_html2_widgets.py
- Runtime
- Python
- Category
- HTML
- Path
- /storage/emulated/0/Projects/Management/Libraries/py/HTML/lib_html2_widgets.py
FILE // lib_html2_widgets.py
"""
Library: lib_html2_widgets.py
MFDB Version: 1.3.1
Format_Creator: Elton Boehnen
Status: OFFICIAL - v1.3.1
Date: 2026-05-06
"""
"""
Library: lib_html2_widgets.py
Family: HTML
Jurisdiction: ["PYTHON", "BEJSON_LIBRARIES"]
Status: OFFICIAL
Author: Elton Boehnen
Version: 1.3 OFFICIAL
Date: 2026-05-01
Description: UI widgets and plugins for BEJSON HTML generation.
Refactored to follow Modular CSS Policy (centralized in skeletons).
"""
import html as html_mod
import os
import json
import re
from pathlib import Path
# Widget Size Standards (Fixed Grid PX)
W_SMALL = (220, 200)
W_MEDIUM = (580, 300)
W_LARGE = (1180, 400)
def html_widget(content, title="WIDGET", size="small", container_id=None):
"""
Standardizes a self-contained, injectable widget with fixed dimensions.
Sizes: 'small', 'medium', 'large'
"""
cid = container_id or f"widget_{id(content)}"
if size == "large": width, height = W_LARGE
elif size == "medium": width, height = W_MEDIUM
else: width, height = W_SMALL # Default small/sidebar
return f"""
<div id="{cid}" class="bejson-widget" style="width: {width}px; height: {height}px; min-width: {width}px; min-height: {height}px;">
<div class="bejson-widget__header">
<span>{html_mod.escape(title)}</span>
<span style="opacity: 0.5;">[{size.upper()}]</span>
</div>
<div class="bejson-widget__body">
{content}
</div>
</div>
"""
def html_gallery(dir_path, recursive=False, container_id=None):
"""Generate an image gallery widget using .gallery-grid BEM classes."""
cid = container_id or f"gallery_{id(dir_path)}"
path = Path(dir_path)
if not path.exists(): return f"<div>Error: {dir_path} not found</div>"
extensions = ('.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg')
pattern = "**/*" if recursive else "*"
items = ""
for img in sorted(path.glob(pattern)):
if img.suffix.lower() in extensions:
items += f"""
<div class="gallery-item">
<a href="{img}" target="_blank">
<img src="{img}" alt="{img.name}" loading="lazy">
</a>
<div class="gallery-item__label">{img.name}</div>
</div>"""
return f'<div id="{cid}" class="gallery-grid">{items}</div>'
def html_video_grid(videos, container_id=None):
"""Generate a responsive grid of embedded videos using .video-grid BEM classes."""
cid = container_id or f"vgrid_{id(videos)}"
items = ""
for vid in videos:
url = vid.get('url', '')
title = vid.get('title', 'Video')
embed_url = url
if "youtube.com/watch" in url:
vid_id = re.search(r'v=([^&]+)', url)
if vid_id: embed_url = f"https://www.youtube.com/embed/{vid_id.group(1)}"
elif "youtu.be/" in url:
vid_id = url.split("youtu.be/")[1]
embed_url = f"https://www.youtube.com/embed/{vid_id}"
items += f"""
<div class="video-card">
<div class="video-card__header">{html_mod.escape(title)}</div>
<div class="video-card__embed">
<iframe src="{embed_url}" allowfullscreen loading="lazy"></iframe>
</div>
</div>"""
return f'<div id="{cid}" class="video-grid">{items}</div>'
def html_info_box(title, content, link_url=None, link_label="View More", container_id=None):
"""Generate a stylized info box using .info-box BEM classes."""
cid = container_id or f"infobox_{id(title)}"
link_html = f"""<a href="{link_url}" class="info-box__link">{link_label}</a>""" if link_url else ""
return f"""
<div id="{cid}" class="info-box">
<h3 class="info-box__title">
<span class="info-box__dot"></span>
{html_mod.escape(title)}
</h3>
<div class="info-box__content">{content}</div>
{link_html}
</div>
"""
def html_standalone_widget(html_content, title="Widget", container_id=None):
"""Wraps standalone HTML into an isolated iframe."""
cid = container_id or f"standalone_widget_{id(title)}"
escaped_content = html_mod.escape(html_content, quote=True)
return f"""
<div id="{cid}" class="standalone-widget-container" style="width: 100%; border: 1px solid var(--border-color); border-radius: 8px; overflow: hidden; background: #000;">
<div class="widget-header" style="background: #111; padding: 10px 15px; border-bottom: 1px solid var(--border-color); font-family: var(--font-family-mono); font-size: 12px; color: var(--text-color); display: flex; justify-content: space-between; align-items: center;">
<span style="color: var(--primary-color); font-weight: bold;">{html_mod.escape(title)}</span>
<span style="color: #666;">Isolated Component</span>
</div>
<iframe srcdoc="{escaped_content}" style="width: 100%; height: 600px; border: none; display: block;" sandbox="allow-scripts allow-same-origin"></iframe>
</div>
"""
def html_lightbox():
"""Returns the CSS/JS for a global lightbox system."""
return """
<div id="bejson-lightbox" class="lightbox" onclick="this.style.display='none'">
<span class="lightbox__close">×</span>
<img class="lightbox__content" id="lightbox-img">
<div id="lightbox-caption" class="lightbox__caption"></div>
</div>
<style>
.lightbox { display: none; position: fixed; z-index: 2000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.9); }
.lightbox__content { margin: auto; display: block; width: 80%; max-width: 1200px; max-height: 80%; margin-top: 5%; object-fit: contain; }
.lightbox__caption { margin: auto; display: block; width: 80%; text-align: center; color: #ccc; padding: 10px 0; font-family: var(--font-mono); }
.lightbox__close { position: absolute; top: 15px; right: 35px; color: #f1f1f1; font-size: 40px; font-weight: bold; cursor: pointer; }
</style>
<script>
function openLightbox(src, caption) {
document.getElementById('bejson-lightbox').style.display = 'block';
document.getElementById('lightbox-img').src = src;
document.getElementById('lightbox-caption').innerHTML = caption;
}
</script>
"""
def html_carousel(items, container_id=None):
"""Generate a horizontal carousel/rotator."""
cid = container_id or f"carousel_{id(items)}"
slides = ""
for i, item in enumerate(items):
slides += f'<div class="carousel__slide"> {item} </div>'
return f"""
<div id="{cid}" class="carousel">
<div class="carousel__track">{slides}</div>
<div class="carousel__controls">
<button class="carousel__btn" onclick="moveCarousel('{cid}', -1)">< PREV</button>
<button class="carousel__btn" onclick="moveCarousel('{cid}', 1)">NEXT ></button>
</div>
</div>
<style>
.carousel {{ position: relative; overflow: hidden; width: 100%; border: 1px solid var(--border); background: var(--bg-surface); }}
.carousel__track {{ display: flex; transition: transform 0.3s ease; }}
.carousel__slide {{ min-width: 100%; padding: 20px; box-sizing: border-box; }}
.carousel__controls {{ display: flex; justify-content: space-between; padding: 10px; background: rgba(0,0,0,0.05); }}
.carousel__btn {{ background: none; border: 1px solid var(--primary-red); color: var(--primary-red); font-family: var(--font-mono); padding: 5px 10px; cursor: pointer; }}
</style>
<script>
if(!window.carouselPos) window.carouselPos = {{}};
function moveCarousel(id, dir) {{
const track = document.getElementById(id).querySelector('.carousel__track');
const count = track.children.length;
if(!window.carouselPos[id]) window.carouselPos[id] = 0;
window.carouselPos[id] = (window.carouselPos[id] + dir + count) % count;
track.style.transform = `translateX(-${{window.carouselPos[id] * 100}}%)`;
}}
</script>
"""
def html_code_block(code, title="Source Code", container_id=None):
"""Stylized code block with copy button."""
cid = container_id or f"code_{id(code)}"
escaped = html_mod.escape(code)
return f"""
<div id="{cid}" class="code-block">
<div class="code-block__header">
<span>{html_mod.escape(title)}</span>
<button class="code-block__copy" onclick="copyCode('{cid}')">COPY</button>
</div>
<pre class="code-block__pre"><code>{escaped}</code></pre>
</div>
<style>
.code-block {{ border: 1px solid var(--border); background: #0a0a0a; margin: 10px 0; }}
.code-block__header {{ background: #1a1a1a; padding: 5px 15px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); font-family: var(--font-mono); font-size: 11px; }}
.code-block__copy {{ background: none; border: 1px solid #444; color: #888; cursor: pointer; font-size: 10px; padding: 2px 6px; }}
.code-block__copy:hover {{ color: #fff; border-color: #fff; }}
.code-block__pre {{ padding: 15px; margin: 0; overflow-x: auto; color: #dcdcdc; font-family: var(--font-mono); font-size: 13px; }}
</style>
<script>
function copyCode(id) {{
const code = document.getElementById(id).querySelector('code').innerText;
navigator.clipboard.writeText(code);
const btn = document.getElementById(id).querySelector('.code-block__copy');
btn.innerText = 'COPIED';
setTimeout(() => btn.innerText = 'COPY', 2000);
}}
</script>
"""
def html_dialog(dialog_id, title, content, actions_html=""):
"""
Generate a standardized modal dialog component.
:param actions_html: HTML for buttons in the footer (e.g., <button class='form__button'>OK</button>)
"""
return f"""
<div id="{dialog_id}" class="dialog-mask">
<div class="dialog">
<div class="dialog__header">
<span class="dialog__title">{html_mod.escape(title)}</span>
<button class="dialog__close" onclick="closeDialog('{dialog_id}')">×</button>
</div>
<div class="dialog__body">{content}</div>
<div class="dialog__footer">{actions_html}</div>
</div>
</div>
<style>
.dialog-mask {{
display: none; position: fixed; z-index: 4000; left: 0; top: 0; width: 100%; height: 100%;
background-color: rgba(0,0,0,0.8); backdrop-filter: blur(4px);
}}
.dialog {{
position: relative; background-color: #fff; margin: 10% auto; padding: 0;
border: 2px solid var(--primary-red); width: 80%; max-width: 600px;
box-shadow: 10px 10px 0px rgba(0,0,0,0.2);
}}
.dialog__header {{
padding: 12px 20px; background: var(--bg-surface); border-bottom: 1px solid var(--border);
display: flex; justify-content: space-between; align-items: center;
}}
.dialog__title {{ font-family: var(--font-mono); font-weight: bold; font-size: 14px; text-transform: uppercase; color: var(--primary-red); }}
.dialog__close {{ background: none; border: none; font-size: 24px; cursor: pointer; color: #888; }}
.dialog__body {{ padding: 25px 20px; font-family: var(--font-base); line-height: 1.6; color: #333; }}
.dialog__footer {{
padding: 12px 20px; background: #f8f8f8; border-top: 1px solid var(--border);
display: flex; justify-content: flex-end; gap: 10px;
}}
</style>
<script>
function openDialog(id) {{ document.getElementById(id).style.display = 'block'; }}
function closeDialog(id) {{ document.getElementById(id).style.display = 'none'; }}
</script>
"""
def html_load_widget(widget_name, components_dir=None, container_id=None):
"""Loads an external HTML component."""
if not components_dir:
components_dir = os.environ.get("CC_COMPONENTS", "{SC_ROOT}/Templates/Components")
path = Path(components_dir) / f"{widget_name}.html"
if not path.exists():
return f"<div>Error: Widget component '{widget_name}' not found in {components_dir}</div>"
try:
content = path.read_text(encoding='utf-8')
return html_standalone_widget(content, title=widget_name, container_id=container_id)
except Exception as e:
return f"<div>Error loading widget '{widget_name}': {e}</div>"