html_compose.gallery

  1from collections.abc import Callable
  2from typing import Any, Literal
  3
  4from .. import resource
  5from ..base_types import Node
  6from . import impl
  7
  8
  9def showcase(
 10    *fixtures,
 11    kwargs: dict[str, Any] | None = None,
 12    name: str | None = None,
 13    category: str | None = None,
 14    tags: list[str] | None = None,
 15    env: str = "default",
 16) -> Callable:
 17    """
 18    Register example fixtures for component preview and testing.
 19
 20    You may set multiple showcases on the same component function with multiple
 21    decorator uses.
 22
 23    Args:
 24        *fixtures:
 25            Zero or more arguments to be used as example inputs for the decorated function.
 26
 27        kwargs:
 28            Optional dict of keyword arguments to be used as example inputs.
 29
 30        name:
 31            Optional human-readable name for this showcase entry.
 32            Useful for distinguishing multiple showcases on the same component.
 33
 34        category:
 35            Optional category string for grouping showcases in galleries or documentation.
 36
 37        tags:
 38            Optional list of tag strings for filtering and organizing showcase entries.
 39
 40        env:
 41            The environment in which this showcase should be active.
 42            Defaults to ``"default"``.
 43
 44    Usage:
 45    ```python
 46    @showcase(example_post)
 47    @showcase(minimal_post, name="minimal")
 48    def post(post: blog_models.Post):
 49        return section(...)[...]
 50    ```
 51    This populates a gallery that can be viewed by running `html-compose gallery`
 52    """
 53
 54    def decorator(func: Callable) -> Callable:
 55        func_fixtures: list[impl.ShowcaseEntry] = (
 56            impl.showcase_registry.setdefault(func, [])
 57        )
 58        func_fixtures.append(
 59            impl.ShowcaseEntry(fixtures, kwargs, name, category, tags, env)
 60        )
 61
 62        # Attach metadata for introspection
 63        if not hasattr(func, "__showcases__"):
 64            func.__showcases__ = []  # type: ignore[attr-defined]
 65        func.__showcases__.extend(fixtures)  # type: ignore[attr-defined]
 66
 67        return func
 68
 69    return decorator
 70
 71
 72def declare_environment(
 73    env: str = "default",
 74    css: list[resource.css_import | str] | None = None,
 75    js: list[resource.js_import | str] | None = None,
 76    fonts: list[resource.font_import_manual | resource.font_import_provider]
 77    | None = None,
 78    isolation: Literal["shadow-dom", "iframe", "none"] = "none",
 79    parent: str | None = None,
 80    body_override: Node | None = None,
 81    parent_element: Node | None = None,
 82):
 83    """
 84    Declare resources for a named environment.
 85    Environments can be referenced by showcases to control their setup.
 86
 87    Args:
 88        env: Name of the environment.
 89
 90        css: List of CSS imports to include.
 91
 92        js: List of JS imports to include.
 93
 94        fonts: List of font imports to include.
 95
 96        isolation: Isolation mode for the environment.
 97
 98        parent: Name of a parent environment to inherit resources from.
 99
100        body_override: Optional body content to replace the default.
101
102        parent_element: Optional parent element for the environment.
103                        If body_override is set, this is required and must be
104                        an element within the body_override.
105    """
106    if env in impl.env_registry:
107        raise ValueError(f"Environment '{env}' is already declared")
108
109    if isolation == "none":
110        for e in impl.env_registry.values():
111            if e.isolation == "none":
112                raise ValueError(
113                    "Only one environment can have 'none' isolation"
114                )
115    if body_override is not None and parent_element is None:
116        raise ValueError(
117            "If body_override is set, parent_element must also be provided"
118        )
119    if parent_element is not None and body_override is None:
120        raise ValueError(
121            "If parent_element is set, body_override must also be provided"
122        )
123
124    impl.env_registry[env] = impl.ShowcaseEnvironment(
125        css=css or [],
126        js=js or [],
127        fonts=fonts or [],
128        isolation=isolation,
129        parent=parent,
130        body_override=body_override,
131        parent_element=parent_element,
132    )
def showcase( *fixtures, kwargs: dict[str, typing.Any] | None = None, name: str | None = None, category: str | None = None, tags: list[str] | None = None, env: str = 'default') -> Callable:
10def showcase(
11    *fixtures,
12    kwargs: dict[str, Any] | None = None,
13    name: str | None = None,
14    category: str | None = None,
15    tags: list[str] | None = None,
16    env: str = "default",
17) -> Callable:
18    """
19    Register example fixtures for component preview and testing.
20
21    You may set multiple showcases on the same component function with multiple
22    decorator uses.
23
24    Args:
25        *fixtures:
26            Zero or more arguments to be used as example inputs for the decorated function.
27
28        kwargs:
29            Optional dict of keyword arguments to be used as example inputs.
30
31        name:
32            Optional human-readable name for this showcase entry.
33            Useful for distinguishing multiple showcases on the same component.
34
35        category:
36            Optional category string for grouping showcases in galleries or documentation.
37
38        tags:
39            Optional list of tag strings for filtering and organizing showcase entries.
40
41        env:
42            The environment in which this showcase should be active.
43            Defaults to ``"default"``.
44
45    Usage:
46    ```python
47    @showcase(example_post)
48    @showcase(minimal_post, name="minimal")
49    def post(post: blog_models.Post):
50        return section(...)[...]
51    ```
52    This populates a gallery that can be viewed by running `html-compose gallery`
53    """
54
55    def decorator(func: Callable) -> Callable:
56        func_fixtures: list[impl.ShowcaseEntry] = (
57            impl.showcase_registry.setdefault(func, [])
58        )
59        func_fixtures.append(
60            impl.ShowcaseEntry(fixtures, kwargs, name, category, tags, env)
61        )
62
63        # Attach metadata for introspection
64        if not hasattr(func, "__showcases__"):
65            func.__showcases__ = []  # type: ignore[attr-defined]
66        func.__showcases__.extend(fixtures)  # type: ignore[attr-defined]
67
68        return func
69
70    return decorator

Register example fixtures for component preview and testing.

You may set multiple showcases on the same component function with multiple decorator uses.

Args: *fixtures: Zero or more arguments to be used as example inputs for the decorated function.

kwargs:
    Optional dict of keyword arguments to be used as example inputs.

name:
    Optional human-readable name for this showcase entry.
    Useful for distinguishing multiple showcases on the same component.

category:
    Optional category string for grouping showcases in galleries or documentation.

tags:
    Optional list of tag strings for filtering and organizing showcase entries.

env:
    The environment in which this showcase should be active.
    Defaults to ``"default"``.

Usage:

@showcase(example_post)
@showcase(minimal_post, name="minimal")
def post(post: blog_models.Post):
    return section(...)[...]

This populates a gallery that can be viewed by running html-compose gallery

def declare_environment( env: str = 'default', css: list[html_compose.resource.css_import | str] | None = None, js: list[html_compose.resource.js_import | str] | None = None, fonts: list[html_compose.resource.font_import_manual | html_compose.resource.font_import_provider] | None = None, isolation: Literal['shadow-dom', 'iframe', 'none'] = 'none', parent: str | None = None, body_override: Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[html_compose.base_types.ElementBase], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]]] = None, parent_element: Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[html_compose.base_types.ElementBase], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], Union[NoneType, str, int, float, bool, html_compose.base_types.ElementBase, html_compose.base_types._HasHtml, Iterable[ForwardRef('Node')], Callable[[], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase], ForwardRef('Node')], Callable[[html_compose.base_types.ElementBase, html_compose.base_types.ElementBase], ForwardRef('Node')]]]] = None):
 73def declare_environment(
 74    env: str = "default",
 75    css: list[resource.css_import | str] | None = None,
 76    js: list[resource.js_import | str] | None = None,
 77    fonts: list[resource.font_import_manual | resource.font_import_provider]
 78    | None = None,
 79    isolation: Literal["shadow-dom", "iframe", "none"] = "none",
 80    parent: str | None = None,
 81    body_override: Node | None = None,
 82    parent_element: Node | None = None,
 83):
 84    """
 85    Declare resources for a named environment.
 86    Environments can be referenced by showcases to control their setup.
 87
 88    Args:
 89        env: Name of the environment.
 90
 91        css: List of CSS imports to include.
 92
 93        js: List of JS imports to include.
 94
 95        fonts: List of font imports to include.
 96
 97        isolation: Isolation mode for the environment.
 98
 99        parent: Name of a parent environment to inherit resources from.
100
101        body_override: Optional body content to replace the default.
102
103        parent_element: Optional parent element for the environment.
104                        If body_override is set, this is required and must be
105                        an element within the body_override.
106    """
107    if env in impl.env_registry:
108        raise ValueError(f"Environment '{env}' is already declared")
109
110    if isolation == "none":
111        for e in impl.env_registry.values():
112            if e.isolation == "none":
113                raise ValueError(
114                    "Only one environment can have 'none' isolation"
115                )
116    if body_override is not None and parent_element is None:
117        raise ValueError(
118            "If body_override is set, parent_element must also be provided"
119        )
120    if parent_element is not None and body_override is None:
121        raise ValueError(
122            "If parent_element is set, body_override must also be provided"
123        )
124
125    impl.env_registry[env] = impl.ShowcaseEnvironment(
126        css=css or [],
127        js=js or [],
128        fonts=fonts or [],
129        isolation=isolation,
130        parent=parent,
131        body_override=body_override,
132        parent_element=parent_element,
133    )

Declare resources for a named environment. Environments can be referenced by showcases to control their setup.

Args: env: Name of the environment.

css: List of CSS imports to include.

js: List of JS imports to include.

fonts: List of font imports to include.

isolation: Isolation mode for the environment.

parent: Name of a parent environment to inherit resources from.

body_override: Optional body content to replace the default.

parent_element: Optional parent element for the environment.
                If body_override is set, this is required and must be
                an element within the body_override.