Éléments .jhs et vues réactives, compilés en PHP, sans node ni bundler.
4.0.1
stable
MMO\client
Ce que c'est
The front-end half of the framework: .jhs custom elements and views bound to a reactive template runtime, served by the theme and its Sass, SimpleCSS and JavaScript compilers; pages with display levels (simple, standard, expert), guided tours (MMO.guide, a page's {page}.tour.json), long jobs followed live (<mmo-job>) and the developer console.
Le replay de la page d'accueil est un fichier .jhs de ce site : son balisage, sa classe et son style. mmo.client le compile en PHP dans le bundle que cette page charge : ni node, ni bundler, ni watcher.
src/customElements/mmo-replay.jhs
<mmo-replay>
<div id="frame" part="frame">
<div id="head" part="head"><slot name="title"></slot><span id="counter"></span></div>
<div id="steps" part="steps"><slot></slot></div>
<div id="done" part="done"><slot name="done"></slot></div>
<div id="controls" part="controls"><slot name="controls"></slot></div>
</div>
<script>
/**
* Replays a recorded session one step at a time: an agent's tool calls, a deployment's log.
*
* The steps are the element's own children, written by the page (so they are text the page escaped, and
* translated where the page is): the first `step` show, the next ones appear as the reader goes on. What
* follows the last step goes in the `done` slot. The controls are the page's buttons too, in the `controls`
* slot, each saying what it does with `data-replay`: `next`, `all` or `reset`.
*
* @attr step - how many steps show at first (1 by default)
* @slot - the steps, in order
* @slot title - what is replayed, above the steps
* @slot done - shown once every step is
* @slot controls - the buttons, each with `data-replay="next|all|reset"`
* @part frame - the box around it all
* @example <mmo-replay step="2"><span slot="title">session</span><p>one</p><p>two</p><p>three</p><button slot="controls" data-replay="next">Next</button></mmo-replay>
*/
class Replay {
init() {
super.init();
this.step = Math.max(1, parseInt(this.getAttribute('step') || '1', 10) || 1);
//the buttons are the page's: a click on one reaches the host
this.addEventListener('click', (e) => {
const button = e.target.closest('[data-replay]');
if (!button) {return;}
const action = button.getAttribute('data-replay');
if (action === 'next') {this.next();}
else if (action === 'all') {this.show(this.steps.length);}
else if (action === 'reset') {this.show(1);}
});
//the steps may be written after the element is upgraded
this.queryShadow('#steps slot').addEventListener('slotchange', () => this.show(this.step));
this.show(this.step);
}
/** @return {HTMLElement[]} the steps: the children in no named slot */
get steps() {
return [...this.children].filter((child) => !child.slot);
}
/** Show one more step. */
next() {
this.show(this.step + 1);
}
/**
* Show the first `count` steps, and the `done` slot when they are all shown.
* @param {number} count - between 1 and the number of steps
*/
show(count) {
const steps = this.steps;
//no step written yet (the element is upgraded before its children): keep what was asked for them
if (!steps.length) {this.step = Math.max(1, count); return;}
this.step = Math.min(Math.max(1, count), steps.length);
steps.forEach((step, i) => {step.hidden = i >= this.step;});
this.toggleAttribute('done', this.step >= steps.length);
this.queryShadow('#counter').textContent = this.step + ' / ' + steps.length;
for (const button of this.querySelectorAll('[data-replay="next"], [data-replay="all"]')) {button.disabled = this.step >= steps.length;}
}
}
</script>
<style>
:host {display: block;}
#frame {background: $color.surface-2; border: 1px solid $color.line;}
#head {display: flex; justify-content: space-between; gap: $space.md; padding: $space.sm $space.lg; border-bottom: 1px solid $color.line; font-family: $font.mono; font-size: $text.xs; color: $color.text-3;}
#counter {white-space: nowrap;}
#steps {padding: $space.lg; min-height: 260px; font-family: $font.mono; font-size: $text.sm; line-height: 1.9;}
#done {display: none; padding: 0 $space.lg $space.lg;}
:host([done]) #done {display: block;}
#controls {display: flex; flex-wrap: wrap; gap: $space.sm; padding: $space.md $space.lg; border-top: 1px solid $color.line;}
::slotted([hidden]) {display: none;}
</style>
</mmo-replay>
l'élément compilé
1Les étapes sont les enfants de la page.
2L'élément les affiche une à une.
3Son style est du Simple CSS, sur les tokens du design system.
Des éléments livrés avec le framework
Changez leurs attributs : ce sont les éléments du framework, qui tournent dans cette page.
La documentation du framework est rédigée en anglais.