updated examples
This commit is contained in:
parent
991c3c3764
commit
5caf7644a1
298
app/apptiler.js
298
app/apptiler.js
|
@ -1,298 +0,0 @@
|
||||||
//
|
|
||||||
// this is just an AFRAME wrapper for golden-layout v2 (docs: https://golden-layout.github.io/golden-layout/)
|
|
||||||
//
|
|
||||||
//
|
|
||||||
AFRAME.registerComponent('apptiler', {
|
|
||||||
schema: {
|
|
||||||
},
|
|
||||||
|
|
||||||
requires:{
|
|
||||||
goldenlayout_css1: "https://unpkg.com/golden-layout@2.6.0/dist/css/goldenlayout-base.css",
|
|
||||||
goldenlayout_css2: "https://unpkg.com/golden-layout@2.6.0/dist/css/themes/goldenlayout-dark-theme.css",
|
|
||||||
},
|
|
||||||
|
|
||||||
dom: {
|
|
||||||
events: [],
|
|
||||||
|
|
||||||
// for stylesheet see bottom of file
|
|
||||||
html: (me) => `
|
|
||||||
<div id="windowmanager">
|
|
||||||
<div class="modals"></div>
|
|
||||||
</div>`,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
events:{
|
|
||||||
DOMready: async function(){
|
|
||||||
await this.initLayout(this)
|
|
||||||
AFRAME.app.foreach( (opts) => {
|
|
||||||
this.add( opts.component, opts.app.el.dom)
|
|
||||||
if( opts.component != 'apptiler' ) opts.app.el.dom.classList.add(['tile'])
|
|
||||||
})
|
|
||||||
setTimeout( () => document.querySelector('#overlay').classList.add(['apptiler']), 100 )
|
|
||||||
// surf to entrypoint of other xrsh worlds
|
|
||||||
//<script src="https://xrfragment.org/feat/multiparty/dist/xrfragment.aframe.js"></script>
|
|
||||||
//AFRAME.XRF.navigator.to("https://coderofsalvation.github.io/xrsh-media/assets/background.glb")
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function () {
|
|
||||||
this.require( this.requires )
|
|
||||||
},
|
|
||||||
|
|
||||||
initLayout: async function(){
|
|
||||||
if( this.goldenLayout !== undefined || !this.el.dom.querySelector(".modals")) return console.warn("TODO: fix duplicate ready-events")
|
|
||||||
|
|
||||||
let { GoldenLayout } = await import("https://cdn.skypack.dev/pin/golden-layout@v2.5.0-dAz3xMzxIRpbnbfEAik0/mode=imports/optimized/golden-layout.js");
|
|
||||||
|
|
||||||
class Modal {
|
|
||||||
constructor(container) {
|
|
||||||
this.container = container;
|
|
||||||
this.rootElement = container.element;
|
|
||||||
this.rootElement.innerHTML = ''
|
|
||||||
this.resizeWithContainerAutomatically = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const myLayout = {
|
|
||||||
root: {
|
|
||||||
type: 'row',
|
|
||||||
content: []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.goldenLayout = new GoldenLayout( this.el.dom.querySelector('.modals'));
|
|
||||||
this.goldenLayout.registerComponent('Modal', Modal);
|
|
||||||
this.goldenLayout.loadLayout(myLayout);
|
|
||||||
},
|
|
||||||
|
|
||||||
add: function(title,el){
|
|
||||||
if( title == 'apptiler' ) return // dont add yourself to yourself please
|
|
||||||
let item = this.goldenLayout.addComponent('Modal', undefined, title )
|
|
||||||
try{
|
|
||||||
item.parentItem.contentItems[ item.parentItem.contentItems.length-1 ].element.querySelector('.lm_content').appendChild(el)
|
|
||||||
}catch(e){} // ignore elements which are already appended
|
|
||||||
},
|
|
||||||
|
|
||||||
manifest: { // HTML5 manifest to identify app to xrsh
|
|
||||||
"short_name": "windowmanager",
|
|
||||||
"name": "Window Manager",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/images/icons-vector.svg",
|
|
||||||
"type": "image/svg+xml",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "/?source=pwa",
|
|
||||||
"start_url": "/?source=pwa",
|
|
||||||
"background_color": "#3367D6",
|
|
||||||
"display": "standalone",
|
|
||||||
"scope": "/",
|
|
||||||
"theme_color": "#3367D6",
|
|
||||||
"shortcuts": [],
|
|
||||||
"description": "2D/3D management of windows",
|
|
||||||
"screenshots": [
|
|
||||||
{
|
|
||||||
"src": "/images/screenshot1.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "540x720",
|
|
||||||
"form_factor": "narrow"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"help":`
|
|
||||||
Window Manager
|
|
||||||
|
|
||||||
The window manager manages all the windows in 2D/XR.
|
|
||||||
This is a core XRSH system application
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
document.head.innerHTML += `
|
|
||||||
<style type="text/css">
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--xrsh-primary: #3aacff;
|
|
||||||
--xrsh-primary-fg: #FFF;
|
|
||||||
--xrsh-light-primary: #00a3Ff;
|
|
||||||
--xrsh-secondary: #872eff;
|
|
||||||
--xrsh-third: #ce7df2;
|
|
||||||
--xrsh-box-shadow: #0005;
|
|
||||||
--xrsh-dark-gray: #343334;
|
|
||||||
--xrsh-gray: #424280;
|
|
||||||
--xrsh-white: #fdfdfd;
|
|
||||||
--xrsh-light-gray: #efefef;
|
|
||||||
--xrsh-lighter-gray: #e4e2fb96;
|
|
||||||
--xrsh-modal: #000B;
|
|
||||||
--xrsh-font-sans-serif: system-ui, -apple-system, segoe ui, roboto, ubuntu, helvetica, cantarell, noto sans, sans-serif;
|
|
||||||
--xrsh-font-monospace: menlo, monaco, lucida console, liberation mono, dejavu sans mono, bitstream vera sans mono, courier new, monospace, serif;
|
|
||||||
--xrsh-font-size-0: 12px;
|
|
||||||
--xrsh-font-size-1: 14px;
|
|
||||||
--xrsh-font-size-2: 17px;
|
|
||||||
--xrsh-font-size-3: 21px;
|
|
||||||
--xrsh-modal-button-bg: #CCC;
|
|
||||||
--xrsh-modal-button-fg: #FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
body,
|
|
||||||
html.a-fullscreen body{
|
|
||||||
color: var(--xrsh-light-gray);
|
|
||||||
font-size: var(--xrsh-font-size-1);
|
|
||||||
font-family: var(--xrsh-font-sans-serif);
|
|
||||||
accent-color: var(--xrsh-light-primary);
|
|
||||||
line-height:22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#overlay{
|
|
||||||
opacity:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#overlay.apptiler{
|
|
||||||
transition:1s;
|
|
||||||
opacity:1;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,h2,h3,h4,h5{
|
|
||||||
color: var(--xrsh-light-gray);
|
|
||||||
}
|
|
||||||
h1 { font-size: var(--xrsh-font-size-3); }
|
|
||||||
h2,h3,h4{ font-size: var(--xrsh-font-size-2); }
|
|
||||||
|
|
||||||
a,a:visited,a:active{
|
|
||||||
color: var(--xrsh-light-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
button,.btn,input[type=submit]{
|
|
||||||
border-radius:7px;
|
|
||||||
background: var(--xrsh-primary);
|
|
||||||
color: var(--xrsh-primary-fg);
|
|
||||||
transition:0.3s;
|
|
||||||
padding: 10px;
|
|
||||||
font-weight: bold;
|
|
||||||
border: none;
|
|
||||||
cursor:pointer;
|
|
||||||
}
|
|
||||||
button:hover,.btn:hover,input[type=submit]:hover{
|
|
||||||
filter: brightness(1.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal{
|
|
||||||
background: var(--xrsh-modal);
|
|
||||||
padding: 20px 20px 10px 20px;
|
|
||||||
border-radius: 15px;
|
|
||||||
display:inline-block;
|
|
||||||
position:relative;
|
|
||||||
z-index:50;
|
|
||||||
height:unset;
|
|
||||||
overflow:hidden;
|
|
||||||
margin-left:0px;
|
|
||||||
margin-top:-41px;
|
|
||||||
color: var(--xrsh-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal.tile{
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .top{
|
|
||||||
background: var(--xrsh-light-primary);
|
|
||||||
border-radius:15px;
|
|
||||||
position: absolute;
|
|
||||||
z-index:1;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
height: 25px;
|
|
||||||
padding: 13px 10px 1px 14px;
|
|
||||||
color: #FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remove this to see why this is a workaround for border-radius bug */
|
|
||||||
.modal .top .title{
|
|
||||||
position: absolute;
|
|
||||||
background: var(--xrsh-light-primary);
|
|
||||||
display: block;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 27px;
|
|
||||||
padding: 0px 0px 0px 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .top button{
|
|
||||||
padding: 5px 7px;
|
|
||||||
background: var(--xrsh-modal-button-bg);
|
|
||||||
color: var(--xrsh-modal-button-fg);
|
|
||||||
font-weight: bold;
|
|
||||||
float:right;
|
|
||||||
}
|
|
||||||
.modal .top button:hover{
|
|
||||||
filter: brightness(1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .top button.close{
|
|
||||||
background: transparent;
|
|
||||||
display: block;
|
|
||||||
font-size: 20px;
|
|
||||||
padding: 0;
|
|
||||||
transform: translate(-4px,-3px);
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
legend{
|
|
||||||
font-size: var(--xrsh-font-size-0);
|
|
||||||
margin-bottom: 15px;
|
|
||||||
border-bottom: 1px solid var(--xrsh-light-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset{
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
label{
|
|
||||||
margin-left:10px;
|
|
||||||
}
|
|
||||||
button,input,.btn{
|
|
||||||
margin-bottom:10px;
|
|
||||||
}
|
|
||||||
[type="checkbox"], [type="radio"]{
|
|
||||||
transform: scale(1.4);
|
|
||||||
margin-left:3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#windowmanager{
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
position:fixed;
|
|
||||||
top:0;
|
|
||||||
left:0;
|
|
||||||
bottom:0;
|
|
||||||
right:0;
|
|
||||||
}
|
|
||||||
.modals{
|
|
||||||
flex: 0 0 auto;
|
|
||||||
height:100%;
|
|
||||||
width:100%;
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
#windowmanager .lm_content{
|
|
||||||
overflow-y: auto;
|
|
||||||
box-sizing:content-box;
|
|
||||||
border:1px solid #444;
|
|
||||||
background:transparent;
|
|
||||||
}
|
|
||||||
#windowmanager .lm_goldenlayout{
|
|
||||||
background: var(--xrsh-modal);
|
|
||||||
}
|
|
||||||
#windowmanager .lm_title{
|
|
||||||
font-family: var(--xrsh-font-sans-serif);
|
|
||||||
font-size: var(--xrsh-font-size-1);
|
|
||||||
font-weight:bold;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
`
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
AFRAME.registerComponent('helloworld-html', {
|
||||||
|
schema: {
|
||||||
|
foo: { type:"string"}
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
requires:{
|
||||||
|
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
|
},
|
||||||
|
|
||||||
|
dom: {
|
||||||
|
scale: 3,
|
||||||
|
events: ['click'],
|
||||||
|
html: (me) => `<div>
|
||||||
|
<div class="pad"> helloworld-html: ${me.data.foo} <b>${me.data.myvalue}</b></span>
|
||||||
|
</div>`,
|
||||||
|
|
||||||
|
css: `.helloworld-html {
|
||||||
|
color: var(--xrsh-dark-gray); /* see index.css */
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
events:{
|
||||||
|
|
||||||
|
// component events
|
||||||
|
html: function( ){ console.log("html-mesh requirement mounted") },
|
||||||
|
ready: function(e){ console.log("requires are loaded") },
|
||||||
|
|
||||||
|
// combined AFRAME+DOM reactive events
|
||||||
|
keydown: function(e){ }, //
|
||||||
|
click: function(e){
|
||||||
|
console.dir(e)
|
||||||
|
}, //
|
||||||
|
|
||||||
|
// reactive events for this.data updates
|
||||||
|
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
|
||||||
|
|
||||||
|
|
||||||
|
DOMready: function( ){
|
||||||
|
console.log("this.el.dom has been added to DOM")
|
||||||
|
this.el.dom.children[0].id = this.el.uid // important hint for html-mesh
|
||||||
|
this.data.myvalue = 1
|
||||||
|
setInterval( () => this.data.myvalue++, 100 )
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
|
"short_name": "Hello world",
|
||||||
|
"name": "Hello world",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/images/icons-vector.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "/?source=pwa",
|
||||||
|
"start_url": "/?source=pwa",
|
||||||
|
"background_color": "#3367D6",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"theme_color": "#3367D6",
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "What is the latest news?",
|
||||||
|
"cli":{
|
||||||
|
"usage": "helloworld <type> [options]",
|
||||||
|
"example": "helloworld news",
|
||||||
|
"args":{
|
||||||
|
"--latest": {type:"string"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"short_name": "Today",
|
||||||
|
"description": "View weather information for today",
|
||||||
|
"url": "/today?source=pwa",
|
||||||
|
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Hello world information",
|
||||||
|
"screenshots": [
|
||||||
|
{
|
||||||
|
"src": "/images/screenshot1.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "540x720",
|
||||||
|
"form_factor": "narrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"help":`
|
||||||
|
Helloworld application
|
||||||
|
|
||||||
|
This is a help file which describes the application.
|
||||||
|
It will be rendered thru troika text, and will contain
|
||||||
|
headers based on non-punctualized lines separated by linebreaks,
|
||||||
|
in above's case "\nHelloworld application\n" will qualify as header.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
AFRAME.registerComponent('helloworld-htmlform', {
|
||||||
|
schema: {
|
||||||
|
foo: { type:"string"}
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
requires:{
|
||||||
|
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
|
winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
|
||||||
|
winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", // deadsimple windows: https://nextapps-de.github.io/winbox
|
||||||
|
stylis: "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js", // modern CSS (https://stylis.js.org)
|
||||||
|
},
|
||||||
|
|
||||||
|
dom: {
|
||||||
|
scale: 3,
|
||||||
|
events: ['click','input'],
|
||||||
|
html: (me) => `<div class="light">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Colour</legend>
|
||||||
|
<input type="radio" id="color-red" name="color" value="red" checked><label for="color-red"> Red</label><br>
|
||||||
|
<input type="radio" id="color-blue" name="color" value="blue"><label for="color-blue"> Blue</label><br>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Material:</legend>
|
||||||
|
<input id="material-wireframe" type="checkbox" name="wireframe"><label for="material-wireframe"> Wireframe</label><br>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Size: <span id="myvalue"></span></legend>
|
||||||
|
<input type="range" min="0.1" max="2" value="1" step="0.01" id="myRange" style="background-color: transparent;">
|
||||||
|
</fieldset>
|
||||||
|
<button>hello</button>
|
||||||
|
</div>`,
|
||||||
|
css: `.helloworld-htmlform {
|
||||||
|
div {
|
||||||
|
padding:11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
events:{
|
||||||
|
|
||||||
|
// component events
|
||||||
|
html: function( ){ console.log("html-mesh requirement mounted") },
|
||||||
|
stylis: function( ){ console.log("stylis requirement mounted") },
|
||||||
|
|
||||||
|
// combined AFRAME+DOM reactive events
|
||||||
|
click: function(e){ }, //
|
||||||
|
input: function(e){
|
||||||
|
if( !e.detail.target ) return
|
||||||
|
if( e.detail.target.id == 'myRange' ) this.data.myvalue = e.detail.target.value // reactive demonstration
|
||||||
|
},
|
||||||
|
|
||||||
|
// reactive events for this.data updates
|
||||||
|
myvalue: function(e){ this.el.dom.querySelector('#myvalue').innerText = this.data.myvalue },
|
||||||
|
|
||||||
|
// requires are loaded
|
||||||
|
ready: function(e){
|
||||||
|
setTimeout( () => {
|
||||||
|
new WinBox("Hello World",{
|
||||||
|
width: 250,
|
||||||
|
height: 315,
|
||||||
|
minwidth:250,
|
||||||
|
maxwidth:250,
|
||||||
|
maxheight:315,
|
||||||
|
minheight:315,
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
id: this.el.uid, // important hint for html-mesh
|
||||||
|
root: document.querySelector("#overlay"),
|
||||||
|
mount: this.el.dom
|
||||||
|
});
|
||||||
|
},500) /*FIXME*/
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
DOMready: function( ){
|
||||||
|
console.log("this.el.dom has been added to DOM")
|
||||||
|
this.data.myvalue = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
|
"short_name": "Hello world",
|
||||||
|
"name": "Hello world",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/images/icons-vector.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "/?source=pwa",
|
||||||
|
"start_url": "/?source=pwa",
|
||||||
|
"background_color": "#3367D6",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"theme_color": "#3367D6",
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "What is the latest news?",
|
||||||
|
"cli":{
|
||||||
|
"usage": "helloworld <type> [options]",
|
||||||
|
"example": "helloworld news",
|
||||||
|
"args":{
|
||||||
|
"--latest": {type:"string"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"short_name": "Today",
|
||||||
|
"description": "View weather information for today",
|
||||||
|
"url": "/today?source=pwa",
|
||||||
|
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Hello world information",
|
||||||
|
"screenshots": [
|
||||||
|
{
|
||||||
|
"src": "/images/screenshot1.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "540x720",
|
||||||
|
"form_factor": "narrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"help":`
|
||||||
|
Helloworld application
|
||||||
|
|
||||||
|
This is a help file which describes the application.
|
||||||
|
It will be rendered thru troika text, and will contain
|
||||||
|
headers based on non-punctualized lines separated by linebreaks,
|
||||||
|
in above's case "\nHelloworld application\n" will qualify as header.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
AFRAME.registerComponent('helloworld-window', {
|
||||||
|
schema: {
|
||||||
|
foo: { type:"string"}
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
requires:{
|
||||||
|
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
|
winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
|
||||||
|
winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", // deadsimple windows: https://nextapps-de.github.io/winbox
|
||||||
|
stylis: "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js", // modern CSS (https://stylis.js.org)
|
||||||
|
},
|
||||||
|
|
||||||
|
dom: {
|
||||||
|
scale: 3,
|
||||||
|
events: ['click','keydown'],
|
||||||
|
html: (me) => `<div>
|
||||||
|
<div class="pad"> ${me.data.foo} <b>${me.data.myvalue}</b></span>
|
||||||
|
</div>`,
|
||||||
|
css: `.helloworld-window {
|
||||||
|
div {
|
||||||
|
.pad { padding:11px; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
events:{
|
||||||
|
|
||||||
|
// component events
|
||||||
|
html: function( ){ console.log("html-mesh requirement mounted") },
|
||||||
|
stylis: function( ){ console.log("stylis requirement mounted") },
|
||||||
|
|
||||||
|
// combined AFRAME+DOM reactive events
|
||||||
|
click: function(e){ }, //
|
||||||
|
keydown: function(e){ }, //
|
||||||
|
|
||||||
|
// reactive events for this.data updates
|
||||||
|
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
|
||||||
|
|
||||||
|
// requires are loaded
|
||||||
|
ready: function(e){
|
||||||
|
|
||||||
|
setTimeout( () => {
|
||||||
|
new WinBox("Hello World",{
|
||||||
|
width: 250,
|
||||||
|
height: 150,
|
||||||
|
x:"center",
|
||||||
|
y:"center",
|
||||||
|
id: this.el.uid, // important hint for html-mesh
|
||||||
|
root: document.querySelector("#overlay"),
|
||||||
|
mount: this.el.dom
|
||||||
|
});
|
||||||
|
}, 500 )
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
DOMready: function( ){
|
||||||
|
console.log("this.el.dom has been added to DOM")
|
||||||
|
this.data.myvalue = 1
|
||||||
|
setInterval( () => this.data.myvalue++, 100 )
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
|
"short_name": "Hello world",
|
||||||
|
"name": "Hello world",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/images/icons-vector.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "/?source=pwa",
|
||||||
|
"start_url": "/?source=pwa",
|
||||||
|
"background_color": "#3367D6",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"theme_color": "#3367D6",
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "What is the latest news?",
|
||||||
|
"cli":{
|
||||||
|
"usage": "helloworld <type> [options]",
|
||||||
|
"example": "helloworld news",
|
||||||
|
"args":{
|
||||||
|
"--latest": {type:"string"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"short_name": "Today",
|
||||||
|
"description": "View weather information for today",
|
||||||
|
"url": "/today?source=pwa",
|
||||||
|
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Hello world information",
|
||||||
|
"screenshots": [
|
||||||
|
{
|
||||||
|
"src": "/images/screenshot1.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "540x720",
|
||||||
|
"form_factor": "narrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"help":`
|
||||||
|
Helloworld application
|
||||||
|
|
||||||
|
This is a help file which describes the application.
|
||||||
|
It will be rendered thru troika text, and will contain
|
||||||
|
headers based on non-punctualized lines separated by linebreaks,
|
||||||
|
in above's case "\nHelloworld application\n" will qualify as header.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -3,76 +3,20 @@ AFRAME.registerComponent('helloworld', {
|
||||||
foo: { type:"string"}
|
foo: { type:"string"}
|
||||||
},
|
},
|
||||||
|
|
||||||
requires:{
|
init: function () {
|
||||||
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
this.el.setAttribute("geometry","primitive: octahedron")
|
||||||
stylis: "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js", // modern CSS (https://stylis.js.org)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
dom: {
|
requires:{
|
||||||
scale: 3,
|
// somecomponent: "https://unpkg.com/some-aframe-component/mycom.min.js"
|
||||||
events: ['click','input'],
|
|
||||||
html: (me) => `
|
|
||||||
<div id="${me.el.uid}" class="modal hello">
|
|
||||||
<div class="top">
|
|
||||||
<div class="title">Hello world</div>
|
|
||||||
</div>
|
|
||||||
<br><br>
|
|
||||||
<fieldset>
|
|
||||||
<legend>Colour</legend>
|
|
||||||
<input type="radio" id="color-red" name="color" value="red" checked><label for="color-red"> Red</label><br>
|
|
||||||
<input type="radio" id="color-blue" name="color" value="blue"><label for="color-blue"> Blue</label><br>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<legend>Material:</legend>
|
|
||||||
<input id="material-wireframe" type="checkbox" name="wireframe"><label for="material-wireframe"> Wireframe</label><br>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<legend>Size: <span id="value"></span></legend>
|
|
||||||
<input type="range" min="0.1" max="2" value="1" step="0.01" id="myRange" style="background-color: transparent;">
|
|
||||||
</fieldset>
|
|
||||||
<button>hello</button>
|
|
||||||
</div>`,
|
|
||||||
css: `.modal.hello {
|
|
||||||
position:relative;
|
|
||||||
top:0;
|
|
||||||
width:200px;
|
|
||||||
.title { font-weight:bold; } /* modern nested buildless css thanks to stylis */
|
|
||||||
}`
|
|
||||||
},
|
},
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
||||||
html: function( ){ console.log("html-mesh requirement mounted") },
|
// component events
|
||||||
stylis: function( ){ console.log("stylis requirement mounted") },
|
somecomponent: function( ){ console.log("component requirement mounted") },
|
||||||
|
ready: function(e){ console.log("requires are loaded") },
|
||||||
|
|
||||||
DOMready: function(e){
|
|
||||||
// our reactive dom element has been added to the dom (DOMElement = this.el.dom)
|
|
||||||
},
|
|
||||||
|
|
||||||
click: function(e){ // a click was detected on this.el.dom or AFRAME entity
|
|
||||||
let el = e.detail.target || e.detail.srcElement
|
|
||||||
if( !el ) return
|
|
||||||
if( el.className.match("fold") ) this.el.toggleFold()
|
|
||||||
if( el.className.match("close") ) this.el.close()
|
|
||||||
},
|
|
||||||
|
|
||||||
input: function(e){
|
|
||||||
if( !e.detail.target ) return
|
|
||||||
if( e.detail.target.id == 'myRange' ) this.data.value = e.detail.target.value // reactive demonstration
|
|
||||||
},
|
|
||||||
|
|
||||||
value: function(e){ this.el.dom.querySelector("#value").innerHTML = e.detail.v }, // auto-emitted when this.data.value gets updated
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function () {
|
|
||||||
this.require( this.requires )
|
|
||||||
|
|
||||||
this.scene.addEventListener('apps:2D', () => this.el.setAttribute('visible', false) )
|
|
||||||
this.scene.addEventListener('apps:XR', () => {
|
|
||||||
this.el.setAttribute('visible', true)
|
|
||||||
this.el.setAttribute("html",`html:#${this.el.uid}; cursor:#cursor`)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
manifest: { // HTML5 manifest to identify app to xrsh
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
AFRAME.registerComponent('helloworld', {
|
||||||
|
schema: {
|
||||||
|
foo: { type:"string"}
|
||||||
|
},
|
||||||
|
|
||||||
|
requires:{
|
||||||
|
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
|
stylis: "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js", // modern CSS (https://stylis.js.org)
|
||||||
|
},
|
||||||
|
|
||||||
|
dom: {
|
||||||
|
scale: 3,
|
||||||
|
events: ['click','input'],
|
||||||
|
html: (me) => `
|
||||||
|
<div id="${me.el.uid}" class="modal hello">
|
||||||
|
<div class="top">
|
||||||
|
<div class="title">Hello world</div>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Colour</legend>
|
||||||
|
<input type="radio" id="color-red" name="color" value="red" checked><label for="color-red"> Red</label><br>
|
||||||
|
<input type="radio" id="color-blue" name="color" value="blue"><label for="color-blue"> Blue</label><br>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Material:</legend>
|
||||||
|
<input id="material-wireframe" type="checkbox" name="wireframe"><label for="material-wireframe"> Wireframe</label><br>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Size: <span id="value"></span></legend>
|
||||||
|
<input type="range" min="0.1" max="2" value="1" step="0.01" id="myRange" style="background-color: transparent;">
|
||||||
|
</fieldset>
|
||||||
|
<button>hello</button>
|
||||||
|
</div>`,
|
||||||
|
css: `
|
||||||
|
/*
|
||||||
|
* * HTML-2-WebGL limitations / guidelines for html-mesh compatibility:
|
||||||
|
* no icon libraries (favicon e.g.)
|
||||||
|
* in case of 'border-radius: 2px 3px 4px 5px' (2px will apply to all corners)
|
||||||
|
* dont use transform: scale(1.2) e.g.
|
||||||
|
* */
|
||||||
|
.modal.hello {
|
||||||
|
position:relative;
|
||||||
|
top:0;
|
||||||
|
width:200px;
|
||||||
|
.title { font-weight:bold; } /* modern nested buildless css thanks to stylis */
|
||||||
|
}`
|
||||||
|
},
|
||||||
|
|
||||||
|
events:{
|
||||||
|
|
||||||
|
html: function( ){ console.log("html-mesh requirement mounted") },
|
||||||
|
stylis: function( ){ console.log("stylis requirement mounted") },
|
||||||
|
|
||||||
|
DOMready: function(e){
|
||||||
|
// our reactive dom element has been added to the dom (DOMElement = this.el.dom)
|
||||||
|
},
|
||||||
|
|
||||||
|
click: function(e){ // a click was detected on this.el.dom or AFRAME entity
|
||||||
|
let el = e.detail.target || e.detail.srcElement
|
||||||
|
if( !el ) return
|
||||||
|
if( el.className.match("fold") ) this.el.toggleFold()
|
||||||
|
if( el.className.match("close") ) this.el.close()
|
||||||
|
},
|
||||||
|
|
||||||
|
input: function(e){
|
||||||
|
if( !e.detail.target ) return
|
||||||
|
if( e.detail.target.id == 'myRange' ) this.data.value = e.detail.target.value // reactive demonstration
|
||||||
|
},
|
||||||
|
|
||||||
|
value: function(e){ this.el.dom.querySelector("#value").innerHTML = e.detail.v }, // auto-emitted when this.data.value gets updated
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
this.require( this.requires )
|
||||||
|
|
||||||
|
this.scene.addEventListener('apps:2D', () => this.el.setAttribute('visible', false) )
|
||||||
|
this.scene.addEventListener('apps:XR', () => {
|
||||||
|
this.el.setAttribute('visible', true)
|
||||||
|
this.el.setAttribute("html",`html:#${this.el.uid}; cursor:#cursor`)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
|
"short_name": "Hello world",
|
||||||
|
"name": "Hello world",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/images/icons-vector.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "/?source=pwa",
|
||||||
|
"start_url": "/?source=pwa",
|
||||||
|
"background_color": "#3367D6",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"theme_color": "#3367D6",
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "What is the latest news?",
|
||||||
|
"cli":{
|
||||||
|
"usage": "helloworld <type> [options]",
|
||||||
|
"example": "helloworld news",
|
||||||
|
"args":{
|
||||||
|
"--latest": {type:"string"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"short_name": "Today",
|
||||||
|
"description": "View weather information for today",
|
||||||
|
"url": "/today?source=pwa",
|
||||||
|
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Hello world information",
|
||||||
|
"screenshots": [
|
||||||
|
{
|
||||||
|
"src": "/images/screenshot1.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "540x720",
|
||||||
|
"form_factor": "narrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"help":`
|
||||||
|
Helloworld application
|
||||||
|
|
||||||
|
This is a help file which describes the application.
|
||||||
|
It will be rendered thru troika text, and will contain
|
||||||
|
headers based on non-punctualized lines separated by linebreaks,
|
||||||
|
in above's case "\nHelloworld application\n" will qualify as header.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
112
app/manual.js
112
app/manual.js
|
@ -3,53 +3,87 @@ AFRAME.registerComponent('manual', {
|
||||||
foo: { type:"string"}
|
foo: { type:"string"}
|
||||||
},
|
},
|
||||||
|
|
||||||
requires:{},
|
init: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
requires:{
|
||||||
|
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
|
||||||
|
winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
|
||||||
|
winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", // deadsimple windows: https://nextapps-de.github.io/winbox
|
||||||
|
stylis: "https://unpkg.com/stylis@4.3.1/dist/umd/stylis.js", // modern CSS (https://stylis.js.org)
|
||||||
|
},
|
||||||
|
|
||||||
dom: {
|
dom: {
|
||||||
events: ['click'],
|
scale: 3,
|
||||||
html: (me) => `
|
events: ['click','keydown'],
|
||||||
<div id="manual">
|
html: (me) => `<div>
|
||||||
<h1>Welcome to XRSHell</h1>
|
|
||||||
<br>
|
|
||||||
<img src="https://i.imgur.com/BW22wrb.png"/>
|
|
||||||
<br><br>
|
|
||||||
The <b>xrsh</b> (xrshell) brings the <a href="https://en.wikipedia.org/wiki/Free_and_open-source_software" target="_blank">FOSS</a>- and <a href="https://en.wikipedia.org/wiki/Linux" target="_blank">Linux</a>-soul to <a href="https://en.wikipedia.org/wiki/WebXR" target="_blank">WebXR</a>, promoting the use of (interactive text) terminal and user-provided operating systems inside WebXR.
|
|
||||||
<br><br>Technically, <b>xrsh</b> is a bundle of freshly created re-usable FOSS WebXR components.<br>These provide a common filesystem interface for interacting with WebXR, offering the well-known linux/unix toolchain including a commandline to invoke, store, edit and run WebXR utilities - regardless of their implementation.
|
|
||||||
<br><br>Think of it as termux for the VR/AR headset browser, which can be used to e.g. livecode (using terminal auto-completion!) for XR component (registries).
|
|
||||||
|
|
||||||
<br>
|
<h1>Welcome to XRSHell</h1>
|
||||||
<ul>
|
<br>
|
||||||
<li><a href="https://forgejo.isvery.ninja/xrsh" target="_blank">source xrsh</a></li>
|
<!-- <img src="https://i.imgur.com/BW22wrb.png"/> -->
|
||||||
<li><a href="https://forgejo.isvery.ninja/xrsh-apps" target="_blank">source xrsh apps</a></li>
|
<br><br>
|
||||||
<li><a href="https://forgejo.isvery.ninja/xrsh-media" target="_blank">roadmap meeting recordings</a></li>
|
The <b>xrsh</b> (xrshell) brings the <a href="https://en.wikipedia.org/wiki/Free_and_open-source_software" target="_blank">FOSS</a>- and <a href="https://en.wikipedia.org/wiki/Linux" target="_blank">Linux</a>-soul to <a href="https://en.wikipedia.org/wiki/WebXR" target="_blank">WebXR</a>, promoting the use of (interactive text) terminal and user-provided operating systems inside WebXR.
|
||||||
</ul>
|
<br><br>Technically, <b>xrsh</b> is a bundle of freshly created re-usable FOSS WebXR components.<br>These provide a common filesystem interface for interacting with WebXR, offering the well-known linux/unix toolchain including a commandline to invoke, store, edit and run WebXR utilities - regardless of their implementation.
|
||||||
|
<br><br>Think of it as termux for the VR/AR headset browser, which can be used to e.g. livecode (using terminal auto-completion!) for XR component (registries).
|
||||||
|
|
||||||
</div>`,
|
<br>
|
||||||
css: `
|
<ul>
|
||||||
#manual {
|
<li><a href="https://forgejo.isvery.ninja/xrsh" target="_blank">source xrsh</a></li>
|
||||||
padding:15px;
|
<li><a href="https://forgejo.isvery.ninja/xrsh-apps" target="_blank">source xrsh apps</a></li>
|
||||||
|
<li><a href="https://forgejo.isvery.ninja/xrsh-media" target="_blank">roadmap meeting recordings</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>`,
|
||||||
|
css: `.manual {
|
||||||
|
div {
|
||||||
|
padding:11px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#manual img{
|
`,
|
||||||
width: 100%;
|
|
||||||
max-width: 550px;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
`
|
|
||||||
},
|
},
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
DOMready: function(e){},
|
|
||||||
click: function(e){}, // click was detected on this.el.dom or AFRAME entity
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function () {
|
// component events
|
||||||
this.require( this.requires )
|
html: function( ){ console.log("html-mesh requirement mounted") },
|
||||||
|
stylis: function( ){ console.log("stylis requirement mounted") },
|
||||||
|
|
||||||
|
// combined AFRAME+DOM reactive events
|
||||||
|
click: function(e){ }, //
|
||||||
|
keydown: function(e){ }, //
|
||||||
|
|
||||||
|
// reactive events for this.data updates
|
||||||
|
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
|
||||||
|
|
||||||
|
// requires are loaded
|
||||||
|
ready: function(e){
|
||||||
|
|
||||||
|
setTimeout( () => {
|
||||||
|
new WinBox("XRSH manual",{
|
||||||
|
width:300,
|
||||||
|
height:300,
|
||||||
|
x:"center",
|
||||||
|
y:"center",
|
||||||
|
id: this.el.uid, // important hint for html-mesh
|
||||||
|
root: document.querySelector("#overlay"),
|
||||||
|
mount: this.el.dom
|
||||||
|
});
|
||||||
|
}, 500 )
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
DOMready: function( ){
|
||||||
|
console.log("this.el.dom has been added to DOM")
|
||||||
|
this.data.myvalue = 1
|
||||||
|
setInterval( () => this.data.myvalue++, 100 )
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
manifest: { // HTML5 manifest to identify app to xrsh
|
manifest: { // HTML5 manifest to identify app to xrsh
|
||||||
"short_name": "XRSH Manual",
|
"short_name": "Hello world",
|
||||||
"name": "XRSH Manual",
|
"name": "Hello world",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/images/icons-vector.svg",
|
"src": "/images/icons-vector.svg",
|
||||||
|
@ -67,10 +101,10 @@ AFRAME.registerComponent('manual', {
|
||||||
{
|
{
|
||||||
"name": "What is the latest news?",
|
"name": "What is the latest news?",
|
||||||
"cli":{
|
"cli":{
|
||||||
"usage": "man xrsh",
|
"usage": "helloworld <type> [options]",
|
||||||
"example": "",
|
"example": "helloworld news",
|
||||||
"args":{
|
"args":{
|
||||||
"topic": {type:"string"}
|
"--latest": {type:"string"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"short_name": "Today",
|
"short_name": "Today",
|
||||||
|
@ -79,7 +113,7 @@ AFRAME.registerComponent('manual', {
|
||||||
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "XRSH Manual information",
|
"description": "Hello world information",
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
{
|
{
|
||||||
"src": "/images/screenshot1.png",
|
"src": "/images/screenshot1.png",
|
||||||
|
|
119
com/app.js
119
com/app.js
|
@ -53,33 +53,35 @@ AFRAME.registerComponent('app', {
|
||||||
let deps = []
|
let deps = []
|
||||||
if( !packages.map ) packages = Object.values(packages)
|
if( !packages.map ) packages = Object.values(packages)
|
||||||
packages.map( (package) => {
|
packages.map( (package) => {
|
||||||
let id = package.split("/").pop()
|
try{
|
||||||
// prevent duplicate requests
|
let id = package.split("/").pop()
|
||||||
if( AFRAME.required[id] ) return
|
// prevent duplicate requests
|
||||||
AFRAME.required[id] = true
|
if( AFRAME.required[id] ) return
|
||||||
|
AFRAME.required[id] = true
|
||||||
|
|
||||||
if( !document.head.querySelector(`script#${id}`) ){
|
if( !document.head.querySelector(`script#${id}`) ){
|
||||||
let {id,component,type} = this.parseAppURI(package)
|
let {component,type} = this.parseAppURI(package)
|
||||||
let p = new Promise( (resolve,reject) => {
|
let p = new Promise( (resolve,reject) => {
|
||||||
switch(type){
|
switch(type){
|
||||||
case "js": let script = document.createElement("script")
|
case "js": let script = document.createElement("script")
|
||||||
script.id = id
|
script.id = id
|
||||||
script.src = package
|
script.src = package
|
||||||
script.onload = () => resolve()
|
script.onload = () => resolve()
|
||||||
script.onerror = (e) => reject(e)
|
script.onerror = (e) => reject(e)
|
||||||
document.head.appendChild(script)
|
document.head.appendChild(script)
|
||||||
break;
|
break;
|
||||||
case "css": let link = document.createElement("link")
|
case "css": let link = document.createElement("link")
|
||||||
link.id = id
|
link.id = id
|
||||||
link.href = package
|
link.href = package
|
||||||
link.rel = 'stylesheet'
|
link.rel = 'stylesheet'
|
||||||
document.head.appendChild(link)
|
document.head.appendChild(link)
|
||||||
resolve()
|
resolve()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
deps.push(p)
|
deps.push(p)
|
||||||
}
|
}
|
||||||
|
}catch(e){ console.error(`package ${package} could not be retrieved..aborting :(`); throw e; }
|
||||||
})
|
})
|
||||||
Promise.all(deps).then( () => this.el.emit( readyEvent||'ready', packages) )
|
Promise.all(deps).then( () => this.el.emit( readyEvent||'ready', packages) )
|
||||||
}
|
}
|
||||||
|
@ -106,15 +108,24 @@ AFRAME.AComponent.prototype.initComponent = function(initComponent){
|
||||||
AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
return function(){
|
return function(){
|
||||||
updateProperties.apply(this,arguments)
|
updateProperties.apply(this,arguments)
|
||||||
if( this.dom && this.data && this.data.uri ){
|
|
||||||
|
|
||||||
|
const reactify = (el,aframe) => new Proxy(this.data,{
|
||||||
|
get(me,k,v) { return me[k]
|
||||||
|
},
|
||||||
|
set(me,k,v){
|
||||||
|
me[k] = v
|
||||||
|
aframe.emit(k,{el,k,v})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if( !this.data ) return
|
||||||
|
|
||||||
|
// reactify components with dom-definition
|
||||||
|
if( this.data.uri && this.dom && !this.el.dom ){
|
||||||
|
|
||||||
tasks = {
|
tasks = {
|
||||||
|
|
||||||
generateUniqueId: () => {
|
|
||||||
this.el.uid = String(Math.random()).substr(10)
|
|
||||||
return tasks
|
|
||||||
},
|
|
||||||
|
|
||||||
ensureOverlay: () => {
|
ensureOverlay: () => {
|
||||||
let overlay = document.querySelector('#overlay')
|
let overlay = document.querySelector('#overlay')
|
||||||
if( !overlay ){
|
if( !overlay ){
|
||||||
|
@ -128,13 +139,6 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
},
|
},
|
||||||
|
|
||||||
createReactiveDOMElement: () => {
|
createReactiveDOMElement: () => {
|
||||||
const reactify = (el,aframe) => new Proxy(this.data,{
|
|
||||||
get(me,k,v) { return me[k] },
|
|
||||||
set(me,k,v){
|
|
||||||
me[k] = v
|
|
||||||
aframe.emit(k,{el,k,v})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.el.dom = document.createElement('div')
|
this.el.dom = document.createElement('div')
|
||||||
this.el.dom.className = this.parseAppURI(this.data.uri).component
|
this.el.dom.className = this.parseAppURI(this.data.uri).component
|
||||||
this.el.dom.innerHTML = this.dom.html(this)
|
this.el.dom.innerHTML = this.dom.html(this)
|
||||||
|
@ -155,35 +159,38 @@ AFRAME.AComponent.prototype.updateProperties = function(updateProperties){
|
||||||
return tasks
|
return tasks
|
||||||
},
|
},
|
||||||
|
|
||||||
addModalFunctions: () => {
|
requireDependencies: () => {
|
||||||
this.el.close = () => {
|
this.require( this.requires )
|
||||||
this.el.dom.remove()
|
return tasks
|
||||||
this.el.removeAttribute("html")
|
},
|
||||||
}
|
|
||||||
|
setupListeners: () => {
|
||||||
|
this.scene.addEventListener('apps:2D', () => this.el.setAttribute('visible', false) )
|
||||||
|
this.scene.addEventListener('apps:XR', () => {
|
||||||
|
this.el.setAttribute('visible', true)
|
||||||
|
this.el.setAttribute("html",`html:#${this.el.uid}; cursor:#cursor`)
|
||||||
|
})
|
||||||
return tasks
|
return tasks
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks
|
tasks
|
||||||
.generateUniqueId()
|
|
||||||
.ensureOverlay()
|
.ensureOverlay()
|
||||||
.addCSS()
|
.addCSS()
|
||||||
.createReactiveDOMElement()
|
.createReactiveDOMElement()
|
||||||
.scaleDOMvsXR()
|
.scaleDOMvsXR()
|
||||||
.addModalFunctions()
|
.requireDependencies()
|
||||||
|
.setupListeners()
|
||||||
|
|
||||||
tasks.overlay.appendChild(this.el.dom)
|
tasks.overlay.appendChild(this.el.dom)
|
||||||
this.el.emit('DOMready',{el: this.el.dom})
|
this.el.emit('DOMready',{el: this.el.dom})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// assign unique app id
|
||||||
|
if( !this.el.uid ) this.el.uid = '_'+String(Math.random()).substr(10)
|
||||||
}
|
}
|
||||||
}( AFRAME.AComponent.prototype.updateProperties)
|
}( AFRAME.AComponent.prototype.updateProperties)
|
||||||
//
|
|
||||||
// base CSS for XRSH apps
|
|
||||||
//
|
|
||||||
// limitations / some guidelines for html-mesh compatibility:
|
|
||||||
// * no icon libraries (favicon e.g.)
|
|
||||||
// * 'border-radius: 2px 3px 4px 5px' (applies 2px to all corners)
|
|
||||||
//
|
|
||||||
|
|
||||||
document.head.innerHTML += `
|
document.head.innerHTML += `
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
@ -247,10 +254,11 @@ let toggle = (state) => {
|
||||||
document.body.classList[ state ? 'remove' : 'add'](['XR'])
|
document.body.classList[ state ? 'remove' : 'add'](['XR'])
|
||||||
AFRAME.scenes[0].emit( state ? 'apps:2D' : 'apps:XR')
|
AFRAME.scenes[0].emit( state ? 'apps:2D' : 'apps:XR')
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", (event) => {
|
document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
let btn = document.createElement("button")
|
let btn = document.createElement("button")
|
||||||
btn.id = "toggle_overlay"
|
btn.id = "toggle_overlay"
|
||||||
btn.innerText = 'XRSH'
|
btn.innerHTML = "<i class='gg-stack'></i>"
|
||||||
btn.addEventListener('click', (e) => toggle() )
|
btn.addEventListener('click', (e) => toggle() )
|
||||||
document.body.appendChild(btn)
|
document.body.appendChild(btn)
|
||||||
|
|
||||||
|
@ -262,5 +270,6 @@ document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
if( VRbtn ) document.body.appendChild(VRbtn) // move to body
|
if( VRbtn ) document.body.appendChild(VRbtn) // move to body
|
||||||
if( ARbtn ) document.body.appendChild(ARbtn) // so they will always be visible
|
if( ARbtn ) document.body.appendChild(ARbtn) // so they will always be visible
|
||||||
})
|
})
|
||||||
|
// toggle immersive with ESCAPE
|
||||||
|
document.body.addEventListener('keydown', (e) => e.key == 'Escape' && toggle() )
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue