Components¶
A component is a re-usable snippet of HTML.
Defining components¶
You create components with the @component
decorator on a function.
The function always receives a Slots
objects as its first positional
argument, but can have other arguments as well.
Here is a simple component that returns a div with a configurable “name” attribute:
>>> from minihtml import component
>>> from minihtml.tags import div
>>>
>>> @component()
... def my_component(slots, name):
... return div["my-component"](name=name)
You create an instance of the component by calling it. Like elements, a component called inside an element context will add content to the parent element:
>>> comp = my_component("my-name")
>>> print(comp)
<div class="my-component" name="my-name"></div>
>>> with div["container"] as elem:
... my_component("my-name")
<...>
>>> print(elem)
<div class="container">
<div class="my-component" name="my-name"></div>
</div>
Component slots¶
In addition to arguments, components can have slots that can be filled by the caller with arbitrary content. So, instead of passing elements as arguments to a component, you would use a slot. If you do not configure slots explicitly, your component has a default slot.
To refer to the contents of the slot within the component definition, use the
Slots.slot()
method inside an element context. Here is a component that
wraps it’s content in a div
with class="greeting"
:
>>> @component()
... def my_greeting(slots):
... with div["greeting"] as elem:
... slots.slot()
... return elem
To fill the slot of a component, use it as a context manager. Elements created within the context are added to the default slot.
>>> from minihtml.tags import p
>>> with my_greeting() as comp:
... p("Hello, world!")
... p("More content.")
<...>
>>> print(comp)
<div class="greeting">
<p>Hello, world!</p>
<p>More content.</p>
</div>
Named slots¶
A component can also be declared with one or more named slots:
>>> @component(slots=("header", "footer"))
... def two_slots(slots):
... with div["two-slots"] as elem:
... with div["header"]:
... slots.slot("header")
... with div["footer"]:
... slots.slot("footer")
... return elem
To interact with a named slot, pass the slot name to Slots.slot()
, as
shown above. To fill a slot by name, use the Component.slot()
method of the component returned by the context manager:
>>> with two_slots() as comp:
... with comp.slot("header"):
... p("header content")
... with comp.slot("footer"):
... p("footer content")
<...>
>>> print(comp)
<div class="two-slots">
<div class="header">
<p>header content</p>
</div>
<div class="footer">
<p>footer content</p>
</div>
</div>
Specifying a default slot¶
If your component has multiple slots, it’s a good idea to define one to be the default slot:
>>> from minihtml.tags import h4
>>>
>>> @component(slots=("title", "content"), default="content")
... def my_card(slots):
... with div["my-card"] as elem:
... with h4:
... slots.slot("title")
... with div["content"]:
... slots.slot("content")
... return elem
The default slot can always be referred to (both inside and outside of the component definition) via it’s name (as shown above), or as the unnamed default slot:
>>> from minihtml import text
>>> with my_card() as comp:
... with comp.slot("title"):
... text("card title")
... p("card content") # this goes into the default slot ("content")
<...>
>>> print(comp)
<div class="my-card">
<h4>card title</h4>
<div class="content">
<p>card content</p>
</div>
</div>
Checking if a slot is filled¶
Inside a component, you can find out whether or not a slot has been filled
using Slots.is_filled()
:
>>> from minihtml.tags import span, img
>>>
>>> @component(slots=("icon", "message"), default="message")
... def my_message(slots):
... with div["my-message"] as elem:
... if slots.is_filled("icon"):
... with span["icon"]:
... slots.slot("icon")
... slots.slot("message")
... return elem
As a result, the span
element will only be present if the image
slot
has been filled:
>>> with my_message() as comp:
... p("the message")
<...>
>>> print(comp)
<div class="my-message">
<p>the message</p>
</div>
>>> with my_message() as comp:
... with comp.slot("icon"):
... img(src="warning.png", alt="warning icon")
... p("the message")
<...>
>>> print(comp)
<div class="my-message">
<span class="icon"><img src="warning.png" alt="warning icon"></span>
<p>the message</p>
</div>
Default content for slots¶
You can provide default content that will be inserted if a slot has not been
filled. To do hat, use Slots.slot()
as a context manager. Elements
created within the context will be used as the default content if the slot was
not filled. If the slot was filled, the elements will be ignored and the slot
content inserted in their place.
>>> @component(slots=("title", "icon"), default="title")
... def my_warning(slots):
... with div["my-warning"] as elem:
... with slots.slot("icon"):
... img(src="warning.png", alt="warning icon")
... with slots.slot("title"):
... h4("Warning")
... return elem
>>> comp = my_warning()
>>> print(comp)
<div class="my-warning">
<img src="warning.png" alt="warning icon">
<h4>Warning</h4>
</div>
>>> with my_warning() as comp:
... with comp.slot("icon"):
... img(src="error.png", alt="error icon")
... h4("Error")
<...>
>>> print(comp)
<div class="my-warning">
<img src="error.png" alt="error icon">
<h4>Error</h4>
</div>
Component styles and scripts¶
A component can define style and/or script resources that should be included in the page where a component is used. This only has an effect when the component is used inside a template.
To associate a style with a component, pass an element or list of elements to
the style
parameter of the @component
decorator. Typically, this
will be one or more style
or link
elements. For scripts, use the
script
parameter.
>>> from minihtml.tags import link, script, style
>>>
>>> @component(
... style=[
... style(".my-component { background #ccc }"),
... link(rel="stylesheet", href="/path/to/stylesheet.css"),
... ],
... script=script("alert('hello, world!);")
... )
... def my_component(slots):
... return div["my-component"]
See Collecting component styles and scripts for details on how to use component styles and scripts in a template.