Mod Creation/Reusable Components with PetiteVue: Difference between revisions

No edit summary
 
(7 intermediate revisions by 2 users not shown)
Line 21: Line 21:
'''Example'''
'''Example'''


  <nowiki><!-- templates.html -->
  <syntaxhighlight lang="html" line><!-- templates.html -->
<template id="counter-component">
<template id="counter-component">
   <span class="text-light">{{ count }}</span>
   <span class="text-light">{{ count }}</span>
   <button class="btn btn-secondary" @click="inc">+</button>
   <button class="btn btn-secondary" @click="inc">+</button>
</template></nowiki>
</template></syntaxhighlight>


  <nowiki>// manifest.json
  <syntaxhighlight lang="js" line>// manifest.json
{
{
   "load": ["templates.html"]
   "load": ["templates.html"]
}</nowiki>
}</syntaxhighlight>


  <nowiki>// setup.mjs
<small>''Comments in JSON are purely illustrative and not valid markup''</small>
 
  <syntaxhighlight lang="js" line>// setup.mjs
function Counter(props) {
function Counter(props) {
   return {
   return {
Line 48: Line 50:
     ui.create(Counter({ count: 0 }), document.getElementById('woodcutting-container'));
     ui.create(Counter({ count: 0 }), document.getElementById('woodcutting-container'));
   });
   });
}</nowiki>
}</syntaxhighlight>


=== ui.createStore(props: Record<string, unknown>): ComponentStore ===
=== ui.createStore(props: Record<string, unknown>): ComponentStore ===
Line 66: Line 68:
In the above example for <code>ui.create</code>, if you created a second <code>Counter</code> component, it would contain its own state and clicking the incrementing button on one would have no effect on the other. By using a store, you can share state in the following way:
In the above example for <code>ui.create</code>, if you created a second <code>Counter</code> component, it would contain its own state and clicking the incrementing button on one would have no effect on the other. By using a store, you can share state in the following way:


  <nowiki><!-- templates.html -->
  <syntaxhighlight lang="html" line><!-- templates.html -->
<template id="counter-component-using-store">
<template id="counter-component-using-store">
   <span class="text-light">{{ store.count }}</span>
   <span class="text-light">{{ store.count }}</span>
   <button class="btn btn-secondary" @click="store.inc">+</button>
   <button class="btn btn-secondary" @click="store.inc">+</button>
</template></nowiki>
</template></syntaxhighlight>


  <nowiki>// manifest.json
  <syntaxhighlight lang="js" line>// manifest.json
{
{
   "load": ["templates.html"]
   "load": ["templates.html"]
}</nowiki>
}</syntaxhighlight>
 
<small>''Comments in JSON are purely illustrative and not valid markup''</small>


  <nowiki>// setup.mjs
  <syntaxhighlight lang="js" line>// setup.mjs
function CounterUsingStore({ store }) {
function CounterUsingStore({ store }) {
   return {
   return {
Line 99: Line 103:
     ui.create(CounterUsingStore({ store }), document.getElementById('firemaking-container'));
     ui.create(CounterUsingStore({ store }), document.getElementById('firemaking-container'));
   });
   });
}</nowiki>
}</syntaxhighlight>


Now in this example, both the counter on the Woodcutting page and the Firemaking page should stay in sync with the current count.
Now in this example, both the counter on the Woodcutting page and the Firemaking page should stay in sync with the current count.
Line 109: Line 113:
'''Parameters'''
'''Parameters'''


<code>template: string</code> The selector string for the template you want to clone. For example, to target <code><template id="static-component"><!-- --></template></code>, you would use <code>'#static-component'</code>.
<code>template: string</code> The selector string for the template you want to clone. For example, to target <syntaxhighlight lang="html" inline><template id="static-component"><!-- --></template></syntaxhighlight>, you would use <syntaxhighlight lang="html" inline>'#static-component'</syntaxhighlight>.


<code>host: HTMLElement</code> The element that the component should be appended to.
<code>host: HTMLElement</code> The element that the component should be appended to.
Line 119: Line 123:
'''Example'''
'''Example'''


  <nowiki><!-- static-templates.html -->
  <syntaxhighlight lang="html" line><!-- static-templates.html -->
<template id="my-static-component">
<template id="my-static-component">
   <h3>Hello, this is static HTML</h3>
   <h3>Hello, this is static HTML</h3>
</template></nowiki>
</template></syntaxhighlight>


  <nowiki>// manifest.json
  <syntaxhighlight lang="js" line>// manifest.json
{
{
   "load": ["static-templates.html"]
   "load": ["static-templates.html"]
}</nowiki>
}</syntaxhighlight>
 
<small>''Comments in JSON are purely illustrative and not valid markup''</small>


  <nowiki>// setup.mjs
  <syntaxhighlight lang="js" line>// setup.mjs
export function setup({ onInterfaceReady }) {
export function setup({ onInterfaceReady }) {
   onInterfaceReady(() => {
   onInterfaceReady(() => {
Line 135: Line 141:
     ui.createStatic('#my-static-component', document.getElementById('woodcutting-container'));
     ui.createStatic('#my-static-component', document.getElementById('woodcutting-container'));
   });
   });
}</nowiki>
}</syntaxhighlight>


==== Nesting Static Components ====
==== Nesting Static Components ====
Line 143: Line 149:
For example, given the following templates:
For example, given the following templates:


  <nowiki><!-- static-templates.html -->
  <syntaxhighlight lang="html" line><!-- static-templates.html -->
<template id="static-parent">
<template id="static-parent">
   <h3>Hello, this is static HTML from the parent</h3>
   <h3>Hello, this is static HTML from the parent</h3>
Line 151: Line 157:
<template id="static-child">
<template id="static-child">
   <p>And this HTML is from a static child.</p>
   <p>And this HTML is from a static child.</p>
</template></nowiki>
</template></syntaxhighlight>


You could create the parent component using the following:
You could create the parent component using the following:


  <nowiki>// setup.mjs
  <syntaxhighlight lang="js" line>// setup.mjs
export function setup({ onInterfaceReady }) {
export function setup({ onInterfaceReady }) {
   onInterfaceReady(() => {
   onInterfaceReady(() => {
     ui.createStatic('#static-parent', document.getElementById('woodcutting-container'));
     ui.createStatic('#static-parent', document.getElementById('woodcutting-container'));
   });
   });
}</nowiki>
}</syntaxhighlight>


Which results in the following HTML being appended to the bottom of the Woodcutting page:
Which results in the following HTML being appended to the bottom of the Woodcutting page:


  <nowiki><h3>Hello, this is static HTML from the parent</h3>
  <syntaxhighlight lang="html" line><h3>Hello, this is static HTML from the parent</h3>
<div>
<div>
   <p>And this HTML is from a static child.</p>
   <p>And this HTML is from a static child.</p>
</div></nowiki>
</div></syntaxhighlight>


== Useful Patterns ==
== Useful Patterns ==
Line 177: Line 183:
Consider the following templates:
Consider the following templates:


  <nowiki><!-- templates.html -->
  <syntaxhighlight lang="html" line><!-- templates.html -->
<template id="block-component">
<template id="block-component">
   <div class="block">
   <div class="block">
Line 191: Line 197:
<template id="block-content">
<template id="block-content">
   <p v-for="line in lines">{{ line }}</p>
   <p v-for="line in lines">{{ line }}</p>
</template></nowiki>
</template></syntaxhighlight>


And defined components:
And defined components:


  <nowiki>
  <syntaxhighlight lang="js" line>
function Block(props) {
function Block(props) {
   return {
   return {
Line 218: Line 224:
     lines: props.lines
     lines: props.lines
   };
   };
}</nowiki>
}</syntaxhighlight>


A complete block component can be created with the following:
A complete block component can be created with the following:


  <nowiki>ui.create(Block({
  <syntaxhighlight lang="js" line>ui.create(Block({
   header: { title: 'My Block Component' },
   header: { title: 'My Block Component' },
   content: { lines: ['My first paragraph.', 'My second paragraph.'] }
   content: { lines: ['My first paragraph.', 'My second paragraph.'] }
}), document.getElementById('woodcutting-container'));</nowiki>
}), document.getElementById('woodcutting-container'));</syntaxhighlight>


=== Programmatically Manipulating Components ===
=== Programmatically Manipulating Components ===
Line 233: Line 239:
For example, using our <code>Counter</code> from above:
For example, using our <code>Counter</code> from above:


  <nowiki><!-- templates.html -->
  <syntaxhighlight lang="html" line><!-- templates.html -->
<template id="counter-component">
<template id="counter-component">
   <span class="text-light">{{ count }}</span>
   <span class="text-light">{{ count }}</span>
   <button class="btn btn-secondary" @click="inc">+</button>
   <button class="btn btn-secondary" @click="inc">+</button>
</template></nowiki>
</template></syntaxhighlight>


  <nowiki>// setup.mjs
  <syntaxhighlight lang="js" line>// setup.mjs
function Counter(props) {
function Counter(props) {
   return {
   return {
Line 261: Line 267:
     counter.inc();
     counter.inc();
   });
   });
}</nowiki>
}</syntaxhighlight>
 
== PetiteVue Quick Reference ==
 
This is not an exhaustive rundown of PetiteVue features, but these are likely the most common to be used and examples of each.
 
=== Text Bindings ===
 
Render text within HTML using the double-curly braces notation <code>{{ }}</code>.
 
'''Example'''
 
<syntaxhighlight lang="html" line><template id="binding-example"><h1>{{ text }}</h1></template></syntaxhighlight>
 
<syntaxhighlight lang="js" line>function BindingExample(props) {
  return {
    $template: '#binding-example',
    text: props.text
  };
}</syntaxhighlight>
 
<syntaxhighlight lang="js" line>ui.create(BindingExample({ text: 'Hello, Melvor!' }), host);
// -> <h1>Hello, Melvor!</h1></syntaxhighlight>
 
=== Attribute Binding ===
 
Bind an attribute to props using <code>v-bind</code> directive, or <code>:</code> for short.
 
'''Example'''
 
<syntaxhighlight lang="html" line><template id="attr-binding-example">
  <span v-bind:class="`text-${(warning ? 'warning' : 'info')}`">
    This message could be a warning or informational.
  </span>
</template></syntaxhighlight>
 
This notation accomplishes the same:
 
<syntaxhighlight lang="html" line><template id="attr-binding-example">
  <span :class="`text-${(warning ? 'warning' : 'info')}`">
    This message could be a warning or informational.
  </span>
</template></syntaxhighlight>
 
=== Event Binding/Handling ===
 
Bind event handlers using the <code>v-on</code> directive, or <code>@</code> for short.
 
'''Example'''
 
<syntaxhighlight lang="html" line><template id="event-binding-example">
  <button v-on:click="onClick">Click Me!</button>
</template></syntaxhighlight>
 
This notation accomplishes the same:
 
<syntaxhighlight lang="html" line><template id="event-binding-example">
  <button @click="onClick">Click Me!</button>
</template></syntaxhighlight>
 
And would be used in the component like:
 
<syntaxhighlight lang="js" line>function EventBindingExample() {
  return {
    $template: '#event-binding-template',
    onClick() {
      alert('You clicked me!');
    }
  };
}</syntaxhighlight>
 
=== Input Value Binding ===
 
Input values can be bound using the <code>v-model</code> directive.
 
'''Example'''
 
<syntaxhighlight lang="html" line><template id="input-binding-example">
  <input v-model="value" />
</template></syntaxhighlight>
 
<syntaxhighlight lang="js" line>function InputBindingExample(props) {
  return {
    value: props.initialValue,
    setValue(val) {
      this.input = val;
    }
  };
}
 
const input = InputBindingExample({ initialValue: 'this is the initial value' });
ui.create(input, host);
// -> <input value="this is the initial value" />
input.setValue('now this value');
// -> <input value="now this value" />
 
// Assume the player changes the input in the UI to "new value"
console.log(input.value); // -> "new value"</syntaxhighlight>
 
=== Conditional Rendering ===
 
You can conditionally render elements using the <code>v-if</code>, <code>v-else</code>, and <code>v-else-if</code> directives.
 
'''Example'''
 
<syntaxhighlight lang="html" line><template id="conditional-example">
  <span v-if="value % 15 === 0">FizzBuzz</span>
  <span v-else-if="value % 3 === 0">Fizz</span>
  <span v-else-if="value % 5 === 0">Buzz</span>
  <span v-else>{{ value }}</span>
</template></syntaxhighlight>
 
<syntaxhighlight lang="js" line>function ConditionalExample(props) {
  return {
    $template: 'conditional-example',
    value: props.value
  };
}
 
ui.create(ConditionalExample({ value: 6 }), host);
// -> <span>Fizz</span></syntaxhighlight>
{{ModGuideNav}}
{{Menu}}
89

edits