In this post, I'll be covering two things: how to override a component's static template, and how to override a method, starring the dialog log.
Working with main.js
First, where does any of this go?
The majority of the time, custom JavaScript code goes into js/main.js
. For both changes, I placed them inside the monogatari.init
function:
const { $_ready, $_ } = Monogatari;
// 1. Outside the $_ready function:
$_ready (() => {
// 2. Inside the $_ready function:
monogatari.init ('#monogatari').then (() => {
// 3. Inside the init function:
/*
* PLACE CODE HERE
*/
});
});
Overriding a static template
The dialog log's Close button doesn't appear to fully be visible on certain screens. So I went and replaced the template with one that wraps the button in a container.
How did I go about doing that?
Since Monogatari's source code is available on Github, it's a matter of finding the dialog-log
component and overriding the existing rendered template.
Here's the original dialog-log component. Note this is the develop
branch as I downloaded the nightly version of Monogatari.
render () {
return `
<div class="modal__content">
<div data-content="log">
<div class="text--center padded" data-string="NoDialogsAvailable" data-content="placeholder">No dialogs available. Dialogs will appear here as they show up.</div>
</div>
<button data-string="Close" data-action="dialog-log">Close</button>
</div>
`;
}
Since the render
method is simply outputting a string, that makes it a lot easier to change. Enter monogatari.component('COMPONENT_TAG').template()
:
monogatari.component('dialog-log').template (() => {
return `
<div class="modal__content">
<div data-content="log">
<div class="text--center padded" data-string="NoDialogsAvailable" data-content="placeholder">No dialogs available. Dialogs will appear here as they show up.</div>
</div>
<div class="row">
<button data-string="Close" data-action="dialog-log">Close</button>
</div>
</div>
`;
});
The difference: there's now a <div class="row">
wrapping the Close button, giving it more of a presence on-screen!
Overriding a method
Currently the dialog log does not scroll to the bottom automatically when it's open. So I went about finding a solution to have it scroll to the bottom automatically upon any update. This is great when you want the log to start open with the most recent, as opposed to the oldest, as it's likely the user wants to see what the last few lines that were displayed were.
Back to the dialog log's source code, I can see there is a method called onStateUpdate
. This sounds like the perfect spot for the scrolling to be called.
onStateUpdate (property, oldValue, newValue) {
if (property === 'active') {
this.classList.toggle ('modal--active');
if (newValue === true) {
this.scrollTop = this.scrollHeight;
}
}
return Promise.resolve ();
}
There is currently existing code to scroll down using this.scrollHeight
, but it's not quite doing what I need it to. The scrollHeight
sometimes outputs 0
, so it doesn't do any scrolling.
I rewrote it to force it to get the log element's scrollHeight
and use that instead.
if (newValue === true) {
// scroll down to bottom
var dialogLog = $('[data-content="log"]')[0];
var scrollHeight = dialogLog.scrollHeight;
dialogLog.scrollTop = scrollHeight;
}
A method inside a component is a bit trickier, but it can be done! I set the function directly using prototype
, ensuring that the function still has the same argument signature and the rest of its logic:
monogatari.component('dialog-log').prototype.onStateUpdate =
function(property, oldValue, newValue) {
if (property === 'active') {
this.classList.toggle ('modal--active');
if (newValue === true) {
// scroll down to bottom
var dialogLog = $('[data-content="log"]')[0];
var scrollHeight = dialogLog.scrollHeight;
dialogLog.scrollTop = scrollHeight;
}
}
return Promise.resolve ();
};
Now, whenever new text is added, the scroll element's height is fetched and the scroll to the bottom occurs. There we go!
Final code
Here is how my main.js
looks now:
const { $_ready, $_ } = Monogatari;
// 1. Outside the $_ready function:
$_ready (() => {
// 2. Inside the $_ready function:
monogatari.init ('#monogatari').then (() => {
// 3. Inside the init function:
/* Dialog log template */
monogatari.component('dialog-log').template (() => {
return `
<div class="modal__content">
<div data-content="log">
<div class="text--center padded" data-string="NoDialogsAvailable" data-content="placeholder">No dialogs available. Dialogs will appear here as they show up.</div>
</div>
<div class="row">
<button data-string="Close" data-action="dialog-log">Close</button>
</div>
</div>
`;
});
/* Dialog log scroll to bottom */
monogatari.component('dialog-log').prototype.onStateUpdate =
function(property, oldValue, newValue) {
if (property === 'active') {
this.classList.toggle ('modal--active');
if (newValue === true) {
// scroll down to bottom
var dialogLog = $('[data-content="log"]')[0];
var scrollHeight = dialogLog.scrollHeight;
dialogLog.scrollTop = scrollHeight;
}
}
return Promise.resolve ();
};
});
});