User:DannyS712/VueDemo.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/**
* Demo using Vue and WVUI onwiki
*
* @author DannyS712
*/
// <nowiki>
$(() => {
const VueDemo = {};
window.VueDemo = VueDemo;
VueDemo.run = function ( require ) {
// Don't accidentally lose your work
window.addEventListener(
'beforeunload',
function ( event ) {
event.returnValue = 'Are you sure you want to close this tab?';
},
false
);
mw.util.addCSS(`
#vue-demo-component-ace-wrapper .ace_editor,
#vue-demo-template-ace-wrapper .ace_editor,
#vue-demo-styles-ace-wrapper .ace_editor {
height: 30em;
}
`);
// Expose for client side testing
VueDemo.requireFn = require;
var $interactiveWrapper = $( '<div>' ).attr( 'id', 'vue-demo-interactive' );
$( '#mw-content-text' )
.empty()
.append(
$( '<div>' ).attr( 'id', 'vue-demo-target' ),
$interactiveWrapper
);
var Vue = require( 'vue' );
var VueCompositionAPI = require( '@vue/composition-api' );
Vue.use( VueCompositionAPI );
/**
Vue.component(
'demo-vue-component',
{
template: `<p>This text came from a component!</p>`
}
);
new Vue( {
el: '#vue-demo-target',
components: {
WvuiButton: require( 'wvui' ).WvuiButton
},
data: function () {
return {
clicks: 0
};
},
methods: {
clickHandler: function () {
console.log( 'Button was clicked!' );
this.clicks++;
}
},
template: `<div>
<h2>Stuff created in Vue:</h2>
<wvui-button v-on:click="clickHandler">WVUI button - increment clicks</wvui-button>
<wvui-button action="destructive" v-on:click="clicks = 0">WVUI button - reset clicks</wvui-button>
<demo-vue-component></demo-vue-component>
<p>The button has been clicked: {{ clicks }} time(s).</p>
</div>`,
} );
*/
$interactiveWrapper.append(
$( '<h2>' ).text( 'Live Vue testing:' )
);
var $templateInput = $( '<textarea>' )
.attr( 'id', 'vue-demo-template-input' )
.attr( 'rows', 10 );
var $componentInput = $( '<textarea>' )
.attr( 'id', 'vue-demo-component-input' )
.attr( 'rows', 10 );
var $stylesInput = $( '<textarea>' )
.attr( 'id', 'vue-demo-styles-input' )
.attr( 'rows', 10 );
var $updateButton = $( '<button>' )
.attr( 'id', 'vue-demo-update-result' )
.text( 'Update output' );
var $result = $( '<div>' ).attr( 'id', 'vue-demo-live-result' );
$interactiveWrapper.append(
$( '<label>' ).attr( 'for', 'vue-demo-template-input' ).text( 'Template input: (wrapped in a div, so no need to worry about only one root element)' ),
$( '<div>' ).attr( 'id', 'vue-demo-template-ace-wrapper' ).append(
$templateInput
),
$( '<label>' ).attr( 'for', 'vue-demo-component-input' ).text( 'Component input: (wvui components are registered globally, and the composition api can be accessed using the global `VueCompositionAPI` variable)' ),
$( '<div>' ).attr( 'id', 'vue-demo-component-ace-wrapper' ).append(
$componentInput
),
$( '<label>' ).attr( 'for', 'vue-demo-styles-input' ).text( 'Styles input (CSS):' ),
$( '<div>' ).attr( 'id', 'vue-demo-styles-ace-wrapper' ).append(
$stylesInput
),
$updateButton,
$( '<hr>' ),
$( '<label>' ).attr( 'for', 'vue-demo-live-result' ).text( 'Result:' ),
$result
);
// Initial values, for testing and demo
$templateInput.val( `<h2>Stuff created in Vue:</h2>
<h4>Options API example</h4>
<wvui-button v-on:click="optionsClickHandler">WVUI button - increment clicks (options)</wvui-button>
<wvui-button action="destructive" v-on:click="optionsClicks = 0">WVUI button - reset clicks (options)</wvui-button>
<p class="blue-text">The options button has been clicked: {{ optionsClicks }} time(s).</p>
<h4>Composition API example</h4>
<wvui-button v-on:click="compositionClickHandler">WVUI button - increment clicks</wvui-button>
<wvui-button action="destructive" v-on:click="compositionClicks = 0">WVUI button - reset clicks (composition)</wvui-button>
<p class="blue-text">The composition button has been clicked: {{ compositionClicks }} time(s).</p>` );
$componentInput.val( `module.exports = {
data: function () {
return {
optionsClicks: 0
};
},
methods: {
optionsClickHandler: function () {
console.log( 'Options button was clicked!' );
this.optionsClicks++;
}
},
setup( props ) {
const compositionClicks = VueCompositionAPI.ref( 0 );
const compositionClickHandler = function () {
console.log( 'Composition button was clicked!' );
compositionClicks.value++;
};
return { compositionClicks, compositionClickHandler };
}
};` );
$stylesInput.val( `.blue-text {
color: blue;
}` );
// Set up ACE
var ACEcomponent = ace.edit( 'vue-demo-component-input' );
ACEcomponent.session.setMode('ace/mode/javascript');
var ACEtemplate = ace.edit( 'vue-demo-template-input' );
ACEtemplate.session.setMode('ace/mode/html');
var ACEstyles = ace.edit( 'vue-demo-styles-input' );
ACEstyles.session.setMode('ace/mode/css');
// Filter out warnings about a missing DOCTYPE tag, this isn't a full HTML
// document but rather just a small part that is embedded (its actually not even
// HTML but that is the closest language ACE has)
// Attribution: https://stackoverflow.com/questions/33232632/how-can-i-remove-the-first-doctype-tooltip-of-the-ace-editor-in-my-html-editor
ACEtemplate.session.on('changeAnnotation', function () {
if ( !ACEtemplate.session.$annotations ) {
// No annotations to filter
return;
}
ACEtemplate.session.$annotations = ACEtemplate.session.$annotations.filter(function(annotation){
return !(/doctype first\. Expected/.test(annotation.text) || /Unexpected End of file\. Expected/.test(annotation.text));
});
ACEtemplate.$onChangeAnnotation();
});
const styleTag = mw.loader.addStyleTag( '' );
// Export for debugging
VueDemo.ACEcomponent = ACEcomponent;
VueDemo.ACEtemplate = ACEtemplate;
VueDemo.ACEstyles = ACEstyles;
VueDemo.styleTag = styleTag;
// Expose to testing components (wvui can be used directly, but it is
// also registered globally so you don't really need to)
window.wvui = require( 'wvui' );
window.VueCompositionAPI = VueCompositionAPI;
VueDemo.currentApp = false;
VueDemo.renderComponent = function () {
// Unmount old version
if ( VueDemo.currentApp !== false ) {
VueDemo.currentApp.unmount();
VueDemo.currentApp = false;
}
// Remove any existing rendering, and add an element to replace
$result.empty();
$result.append(
$( '<div>' ).attr( 'id', 'vue-demo-result-replace' )
);
try {
// Component input should set module.exports, set up module object
// and then return the exports
var compF = new Function( 'var module={};' + ACEcomponent.getValue() + ';return module.exports;' );
var comp = compF();
const templateField = '<div>' + ACEtemplate.getValue() + '</div>';
if ( comp.template === undefined || comp.template === '' ) {
comp.template = templateField;
}
VueDemo.currentApp = Vue.createMwApp( comp );
// register WVUI components as globally available
Object.keys( wvui ).forEach( ( componentName ) => {
VueDemo.currentApp.component( componentName, wvui[ componentName ] );
} );
VueDemo.currentInstance = VueDemo.currentApp.mount( '#vue-demo-result-replace' );
styleTag.innerText = ACEstyles.getValue();
} catch ( e ) {
$result.append(
$( '<b>' ).text( 'Something went wrong, check the console' )
);
console.error( e );
}
};
$updateButton.on( 'click', VueDemo.renderComponent );
};
});
$( document ).ready(
function () {
if ( mw.config.get( 'wgPageName' ) === "Special:BlankPage/VueDemo" ) {
mw.loader.load( '//ajaxorg.github.io/ace-builds/src-min/ace.js' );
mw.loader.using(
[ 'vue', 'wvui', 'mediawiki.util', '@vue/composition-api' ],
window.VueDemo.run
);
}
}
);
// </nowiki>