ComponentUI
Every component has
this.ui, an instance of
ComponentUI. It's a small helper
that performs common UI mutations (show/hide, lock/unlock, enable/disable) and
coordinates them with optional CSS animations in a safe way. The rule of thumb
is simple:
-
Use
ComponentUIwhen you want UI effects and visibility changes to be consistent and optionally animated. -
Keep business logic elsewhere —
ComponentUIis "dumb" and focused on DOM effects.
Where ComponentUI is used
BaseComponent
creates
this.ui = new ComponentUI(this)
in its constructor, so
you don't really need to worry about it. One place
this.ui
is already being
used without you knowing about it is component removal:
remove({ raw=false }={}) {
...
if (raw) {
_remove_now();
return;
}
return this.ui.hide().then(() => _remove_now());
}
By default, removing a component is a "hide, then remove" flow.
If you need immediate removal (no UI effects), pass
{ raw: true }
Targets
Most
ComponentUI
methods accept a target argument
t.
Target notation is the same as for events and display states:
-
"self"orundefined— the component root element orthis.el -
"#part"— a component part -
".field"— a component field element (bound to DOM) -
">role"or"role"— child component(s) with that role
You can also pass a raw HTMLElement, but the common usage is target strings.
show() and hide()
show(t)
and
hide(t)
are the core visibility helpers. They:
-
toggle
hiddenattribute - do a simple opacity transition as a baseline
- then wait for animations/transitions to "settle" before finishing
Both return a Promise that resolves when all targets finish their visual work. Typical usage looks like this:
await this.ui.show("#details");
await this.ui.hide(">spinner");
hide()
also adds a generic
is-hiding
class early, so CSS can animate layout
collapse/reflow in a consistent way. After hiding completes, it removes
is-hiding
and clears inline opacity styles. This class is used by Qite's
standard components CSS and you can use it in your own CSS as well. Not having
an associated CSS animation for
is-hiding
doesn't break anything though — it's
just there will be no additional smooth animation is all.
lock() and unlock()
A locked component or element in Qite terminology basically means an element that's processing something and cannot be used again until it finishes processing. Like when you click a button to submit a form — once, clicked, it becomes locked because we don't want users to accidentally click on it again and re-send the form. A locked component is distinct from a disabled one (which is discussed below).
Typical uses of
locked
include locking the whole component while an async
save is in progress, or locking a specific part of the component while
something loads. Under the hood,
lock(t)
and
unlock(t)
add/remove a
locked
class and then wait for visual settle (in case your CSS animates
lock/unlock).
Here's a small example:
this.flags.set("saving", true);
await this.ui.lock("self");
await Ajax.post("/orders", { body: this.fields.all }).ready();
await this.ui.unlock("self");
this.flags.set("saving", false);
enable() and disable()
enable(t)
and
disable(t)
are meant for form controls and clickable things.
They add/remove the
disabled
CSS class and, if the target is a form
element like
<input>,
<textarea>,
<select>
or
<button>), they also
set/remove
disabled="disabled"
attribute to them.
That means you get both: real browser disabling (no clicks, no focus) and
predictable styling (your CSS can target
.disabled). Example:
this.ui.disable(">submit");
await this.saveOrder();
this.ui.enable(">submit");
blink()
blink(t)
is a tiny attention helper. It uses the Web Animations API and only
blinks elements that are currently visible (visibility is determined with
checkVisibility). Typical uses may include drawing attention to a validation
error banner that is already visible or to a "saved" indicator.
Usage is straightforward and simple:
this.ui.blink("#saved_banner");
Animation coordination: finishAnimationWith()
Sometimes you want to run logic
after
an optional CSS animation finishes.
You do not want your logic to depend on that animation existing.
finishAnimationWith()
solves that by:
-
marking
data-animating-<name>="1"on the element - cancelling any currently running CSS animations on that element
-
waiting for the CSS animation named
<name>to finish - guaranting callback runs exactly once
If the animation does not exist (or reduced motion / duration 0), it calls the callback immediately. The helper, then, is most useful for "cleanup after close animation" flows. Example: remove an element after optional fade-out:
let el = this.part("toast");
el.classList.add("closing");
this.ui.finishAnimationWith("fade-out", el, () => {
el.remove();
});
And a corresponding CSS pattern example:
.toast.closing { opacity: 0; }
.toast.closing[data-animating-fade-out] {
animation: fade-out 200ms ease-out both;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
Always make sure the final state is defined without the animation. The animation should only decorate the transition to that state.
Usage in Qite
The most notable place in Qite that uses
ComponentUI
is
DisplayStateEngine
:
it calls
show()
and
hide()
helpers internally to apply the hide/show
transitions. Those transitions return promises because
ComponentUI.show()
/
hide()
also return promises themselves. That's why
DisplayStateEngine
can safely handle
animations — it waits for current transitions to settle, then applies only
the latest pending state during rapid changes
If we're strictly talking about showing or hiding UI targets, the idea is
that you use
display_states
for "what should be visible", and use
ComponentUI
helpers for things that aren't necessarily covered by states and are, in essence,
edge cases.
Remember that
ComponentUI
is not a styling system, but rather a small
convenience layer. Therefore, it's always recommended to prefer state rules
for visibility decisions, prefer CSS for what things look like and only use
ComponentUI
helpers via
this.ui
in your components when you need a consistent,
promise-friendly way to mutate UI and coordinate optional animations.