tosijs-ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/ab-test.d.ts +14 -0
- package/dist/ab-test.js +116 -0
- package/dist/babylon-3d.d.ts +53 -0
- package/dist/babylon-3d.js +292 -0
- package/dist/bodymovin-player.d.ts +32 -0
- package/dist/bodymovin-player.js +172 -0
- package/dist/bp-loader.d.ts +1 -0
- package/dist/bp-loader.js +26 -0
- package/dist/carousel.d.ts +113 -0
- package/dist/carousel.js +308 -0
- package/dist/code-editor.d.ts +27 -0
- package/dist/code-editor.js +102 -0
- package/dist/color-input.d.ts +41 -0
- package/dist/color-input.js +112 -0
- package/dist/data-table.d.ts +79 -0
- package/dist/data-table.js +774 -0
- package/dist/drag-and-drop.d.ts +2 -0
- package/dist/drag-and-drop.js +386 -0
- package/dist/editable-rect.d.ts +97 -0
- package/dist/editable-rect.js +450 -0
- package/dist/filter-builder.d.ts +64 -0
- package/dist/filter-builder.js +468 -0
- package/dist/float.d.ts +18 -0
- package/dist/float.js +170 -0
- package/dist/form.d.ts +68 -0
- package/dist/form.js +466 -0
- package/dist/gamepad.d.ts +34 -0
- package/dist/gamepad.js +115 -0
- package/dist/icon-data.d.ts +312 -0
- package/dist/icon-data.js +308 -0
- package/dist/icon-types.d.ts +7 -0
- package/dist/icon-types.js +1 -0
- package/dist/icons.d.ts +17 -0
- package/dist/icons.js +374 -0
- package/dist/iife.js +69 -0
- package/dist/iife.js.map +49 -0
- package/dist/index-iife.d.ts +1 -0
- package/dist/index-iife.js +4 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +47 -0
- package/dist/live-example.d.ts +63 -0
- package/dist/live-example.js +611 -0
- package/dist/localize.d.ts +46 -0
- package/dist/localize.js +381 -0
- package/dist/make-sorter.d.ts +3 -0
- package/dist/make-sorter.js +119 -0
- package/dist/make-sorter.test.d.ts +1 -0
- package/dist/make-sorter.test.js +48 -0
- package/dist/mapbox.d.ts +24 -0
- package/dist/mapbox.js +161 -0
- package/dist/markdown-viewer.d.ts +17 -0
- package/dist/markdown-viewer.js +173 -0
- package/dist/match-shortcut.d.ts +9 -0
- package/dist/match-shortcut.js +13 -0
- package/dist/match-shortcut.test.d.ts +1 -0
- package/dist/match-shortcut.test.js +194 -0
- package/dist/menu.d.ts +60 -0
- package/dist/menu.js +614 -0
- package/dist/notifications.d.ts +106 -0
- package/dist/notifications.js +308 -0
- package/dist/password-strength.d.ts +35 -0
- package/dist/password-strength.js +302 -0
- package/dist/playwright.config.d.ts +9 -0
- package/dist/playwright.config.js +73 -0
- package/dist/pop-float.d.ts +10 -0
- package/dist/pop-float.js +231 -0
- package/dist/rating.d.ts +62 -0
- package/dist/rating.js +192 -0
- package/dist/rich-text.d.ts +35 -0
- package/dist/rich-text.js +296 -0
- package/dist/segmented.d.ts +80 -0
- package/dist/segmented.js +298 -0
- package/dist/select.d.ts +43 -0
- package/dist/select.js +427 -0
- package/dist/side-nav.d.ts +36 -0
- package/dist/side-nav.js +106 -0
- package/dist/size-break.d.ts +18 -0
- package/dist/size-break.js +118 -0
- package/dist/sizer.d.ts +34 -0
- package/dist/sizer.js +92 -0
- package/dist/src/ab-test.d.ts +14 -0
- package/dist/src/babylon-3d.d.ts +53 -0
- package/dist/src/bodymovin-player.d.ts +32 -0
- package/dist/src/bp-loader.d.ts +0 -0
- package/dist/src/carousel.d.ts +113 -0
- package/dist/src/code-editor.d.ts +27 -0
- package/dist/src/color-input.d.ts +41 -0
- package/dist/src/data-table.d.ts +79 -0
- package/dist/src/drag-and-drop.d.ts +2 -0
- package/dist/src/editable-rect.d.ts +97 -0
- package/dist/src/filter-builder.d.ts +64 -0
- package/dist/src/float.d.ts +18 -0
- package/dist/src/form.d.ts +68 -0
- package/dist/src/gamepad.d.ts +34 -0
- package/dist/src/icon-data.d.ts +309 -0
- package/dist/src/icon-types.d.ts +7 -0
- package/dist/src/icons.d.ts +17 -0
- package/dist/src/index.d.ts +37 -0
- package/dist/src/live-example.d.ts +51 -0
- package/dist/src/localize.d.ts +30 -0
- package/dist/src/make-sorter.d.ts +3 -0
- package/dist/src/mapbox.d.ts +24 -0
- package/dist/src/markdown-viewer.d.ts +15 -0
- package/dist/src/match-shortcut.d.ts +9 -0
- package/dist/src/menu.d.ts +60 -0
- package/dist/src/notifications.d.ts +106 -0
- package/dist/src/password-strength.d.ts +35 -0
- package/dist/src/pop-float.d.ts +10 -0
- package/dist/src/rating.d.ts +62 -0
- package/dist/src/rich-text.d.ts +28 -0
- package/dist/src/segmented.d.ts +80 -0
- package/dist/src/select.d.ts +43 -0
- package/dist/src/side-nav.d.ts +36 -0
- package/dist/src/size-break.d.ts +18 -0
- package/dist/src/sizer.d.ts +34 -0
- package/dist/src/tab-selector.d.ts +91 -0
- package/dist/src/tag-list.d.ts +37 -0
- package/dist/src/track-drag.d.ts +5 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/via-tag.d.ts +2 -0
- package/dist/tab-selector.d.ts +91 -0
- package/dist/tab-selector.js +326 -0
- package/dist/tag-list.d.ts +37 -0
- package/dist/tag-list.js +375 -0
- package/dist/track-drag.d.ts +5 -0
- package/dist/track-drag.js +143 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/dist/via-tag.d.ts +2 -0
- package/dist/via-tag.js +102 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Tonio Loewald
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# tosi-ui
|
|
2
|
+
|
|
3
|
+
<!--{ "pin": "top" }-->
|
|
4
|
+
|
|
5
|
+
[ui.xinjs.net live demo](https://ui.xinjs.net) | [xinjs](https://xinjs.net) | [discord](https://discord.gg/ramJ9rgky5) | [github](https://github.com/tonioloewald/xinjs-ui#readme) | [npm](https://www.npmjs.com/package/xinjs-ui)
|
|
6
|
+
|
|
7
|
+
<center>
|
|
8
|
+
<xin-icon class="logo" icon="tosiUi" size=300></xin-icon>
|
|
9
|
+
</center>
|
|
10
|
+
|
|
11
|
+
Copyright ©2023-2025 Tonio Loewald
|
|
12
|
+
|
|
13
|
+
## the xinjs ui library
|
|
14
|
+
|
|
15
|
+
A set of [web-components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
|
|
16
|
+
created with [xinjs](https://xinjs.net), designed to augment what the browser gives you
|
|
17
|
+
for free rather than replace it.
|
|
18
|
+
|
|
19
|
+
It works beautifully with other web-component libraries, such as [shoelace.style](https://shoelace.style/).
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Using npm and a bundler
|
|
24
|
+
|
|
25
|
+
Add xinjs-ui to your project, e.g.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
npm add xinjs-ui
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then you can import the component `elementCreator` and create the element any way you
|
|
32
|
+
like, the easiest way being to use the `elementCreator` itself. A `tosijs` `elementCreator`
|
|
33
|
+
is syntax sugar around `document.createElement()`.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
import { dataTable } from 'xinjs-ui'
|
|
37
|
+
|
|
38
|
+
document.body.append(dataTable())
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Using the iife via cdn
|
|
42
|
+
|
|
43
|
+
The `tosijs-ui` iife build bundles `tosijs`, `tosijs-ui`, and `marked` into
|
|
44
|
+
a single minified javascript source file. You can access `tosijs` and `xinjsui`
|
|
45
|
+
as globals which contain all the things exported by `tosijs` and `tosijs-ui`.
|
|
46
|
+
|
|
47
|
+
> iife support is new so it may not have propagated to the cdn yet. This
|
|
48
|
+
> example loads the library from ui.xinjs.net for now.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
<script src="https://ui.xinjs.net/iife.js"></script>
|
|
52
|
+
<button id="menu">Menu <xin-icon icon="chevronDown"></xin-icon></button>
|
|
53
|
+
<script>
|
|
54
|
+
const { elements } = tosijs
|
|
55
|
+
const { popMenu, icons } = tosijsui
|
|
56
|
+
|
|
57
|
+
const button = { elements }
|
|
58
|
+
|
|
59
|
+
const showMenu = (target) => {
|
|
60
|
+
popMenu({
|
|
61
|
+
target,
|
|
62
|
+
menuItems: [
|
|
63
|
+
{
|
|
64
|
+
caption: 'Say hello',
|
|
65
|
+
action() {
|
|
66
|
+
alert('hello')
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
null,
|
|
70
|
+
{
|
|
71
|
+
caption: 'Version',
|
|
72
|
+
action() {
|
|
73
|
+
alert(`xinjs ${xinjs.version}\nxinjs-ui ${xinjsui.version}`)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
document.body.append(
|
|
81
|
+
button(
|
|
82
|
+
{
|
|
83
|
+
onClick(event) {
|
|
84
|
+
showMenu(event.target)
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
'Menu',
|
|
88
|
+
icons.chevronDown()
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
</script>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
[Click here to see a simple iife demo](https://ui.xinjs.net/iife.html)
|
|
95
|
+
|
|
96
|
+
## custom-elements
|
|
97
|
+
|
|
98
|
+
The simplest way to use these elements is to simply import the element and then either
|
|
99
|
+
use HTML or the `ElementCreator` function exported.
|
|
100
|
+
|
|
101
|
+
E.g. to use the markdown viewer:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
import { markdownViewer } from 'xinjs-ui'
|
|
105
|
+
document.body.append(markdownViewer('# hello world\nthis is a test'))
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
const { markdownViewer } = tosijsui
|
|
110
|
+
|
|
111
|
+
preview.append(
|
|
112
|
+
markdownViewer(`
|
|
113
|
+
## hello world
|
|
114
|
+
here is some markdown
|
|
115
|
+
`)
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Assuming you import the module somewhere, the HTML will work as well.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
<xin-md>
|
|
123
|
+
## hello world
|
|
124
|
+
here is some markdown
|
|
125
|
+
</xin-md>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The big difference with using the `markdownViewer()` function is that the `tosijs` `Component`
|
|
129
|
+
class will automatically pick a new tag if the expected tag is taken (e.g. by a previously
|
|
130
|
+
defined custom-element from another library). `markdownViewer()` will create an element of
|
|
131
|
+
the correct type.
|
|
132
|
+
|
|
133
|
+
The other thing is that `tosijs` `ElementCreator` functions are convenient and composable,
|
|
134
|
+
allowing you to build DOM elements with less code than pretty much any other option, including
|
|
135
|
+
JSX, TSX, or HTML.
|
|
136
|
+
|
|
137
|
+
## Philosophy
|
|
138
|
+
|
|
139
|
+
In general, `tosijs` strives to work _with_ the browser rather than trying to _replace_ it.
|
|
140
|
+
|
|
141
|
+
In a similar vein, `tosijs-ui` comprises a collection of [web-components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
|
|
142
|
+
with the goal of augmenting what _already_ works well, and the components are intended be interoperable as
|
|
143
|
+
similar as possible to things that you already use, such as `<input>` or `<select>` elements.
|
|
144
|
+
E.g. where appropriate, the `value` of an element is its malleable `state`, and when this changes,
|
|
145
|
+
the element emits a `change` event.
|
|
146
|
+
|
|
147
|
+
Similarly, the xinjs base `Component` class and the components in this collection strive to
|
|
148
|
+
be as similar in operation as possible to DOM elements as makes sense. E.g. binary attributes
|
|
149
|
+
work as expected. Adding the `hidden` attribute makes them disappear. If a component subclass
|
|
150
|
+
has a `value` property then it will be rendered if the value changes (similarly it will be
|
|
151
|
+
rendered if an initialized attribute is changed). Intinsic properties of components will
|
|
152
|
+
default to `null` rather than `undefined`.
|
|
153
|
+
|
|
154
|
+
Similarly, because web-components are highly interoperable, there's no reason to reinvent
|
|
155
|
+
wheels. In particular, this library won't try to replace existing, excellent libraries
|
|
156
|
+
such as [shoelace.style](https://shoelace.style/) or wrap perfectly functional HTML
|
|
157
|
+
elements, like the venerable `<input>` or `<form>` elements that are already capable
|
|
158
|
+
and accessible.
|
|
159
|
+
|
|
160
|
+
The goal here is to provide useful components and other utilities that add to what's built
|
|
161
|
+
into HTML5 and CSS3 and to make custom-elements work as much as possible like drop-in replacements
|
|
162
|
+
for an `<input>` or `<textarea>` (while mitigating the historical pathologies of things like
|
|
163
|
+
`<select>` and `<input type="radio">`). E.g. the `<xin-select>` does not suffer from a
|
|
164
|
+
race-condition between having its value set and being given an `<option>` with the intended value
|
|
165
|
+
and you can differentiate between the user picking a value (`action`) and the value changing (`change`).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Component } from 'tosijs';
|
|
2
|
+
export declare class AbTest extends Component {
|
|
3
|
+
static set conditions(context: {
|
|
4
|
+
[key: string]: any;
|
|
5
|
+
});
|
|
6
|
+
condition: string;
|
|
7
|
+
not: boolean;
|
|
8
|
+
static instances: Set<AbTest>;
|
|
9
|
+
constructor();
|
|
10
|
+
connectedCallback(): void;
|
|
11
|
+
disconnectedCallback(): void;
|
|
12
|
+
render(): void;
|
|
13
|
+
}
|
|
14
|
+
export declare const abTest: import("tosijs").ElementCreator<Component<import("tosijs").PartsMap>>;
|
package/dist/ab-test.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/*#
|
|
2
|
+
# ab-test
|
|
3
|
+
|
|
4
|
+
`<xin-ab>` provides a simple method for implementing A|B-testing.
|
|
5
|
+
|
|
6
|
+
```js
|
|
7
|
+
const { AbTest } = xinjsui
|
|
8
|
+
|
|
9
|
+
function randomize() {
|
|
10
|
+
const conditions = {
|
|
11
|
+
testA: Math.random() < 0.5,
|
|
12
|
+
testB: Math.random() < 0.5,
|
|
13
|
+
testC: Math.random() < 0.5
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
AbTest.conditions = conditions
|
|
17
|
+
|
|
18
|
+
preview.querySelector('pre').innerText = JSON.stringify(conditions, null, 2)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
preview.querySelector('.randomize-conditions').addEventListener('click', randomize)
|
|
22
|
+
|
|
23
|
+
randomize()
|
|
24
|
+
```
|
|
25
|
+
```html
|
|
26
|
+
<div style="display: flex; gap: 10px; align-items: center;">
|
|
27
|
+
<div style="display: flex; flex-direction: column; gap: 10px;">
|
|
28
|
+
<xin-ab class="a" condition="testA">
|
|
29
|
+
<p>testA</p>
|
|
30
|
+
</xin-ab>
|
|
31
|
+
<xin-ab class="not-a" not condition="testA">
|
|
32
|
+
<p>not testA</p>
|
|
33
|
+
</xin-ab>
|
|
34
|
+
<xin-ab class="b" condition="testB">
|
|
35
|
+
<p>testB</p>
|
|
36
|
+
</xin-ab>
|
|
37
|
+
<xin-ab class="not-b" not condition="testB">
|
|
38
|
+
<p>not testB</p>
|
|
39
|
+
</xin-ab>
|
|
40
|
+
<xin-ab class="c" condition="testC">
|
|
41
|
+
<p>testC</p>
|
|
42
|
+
</xin-ab>
|
|
43
|
+
<xin-ab class="not-c" not condition="testC">
|
|
44
|
+
<p>not testC</p>
|
|
45
|
+
</xin-ab>
|
|
46
|
+
</div>
|
|
47
|
+
<pre>
|
|
48
|
+
</pre>
|
|
49
|
+
</div>
|
|
50
|
+
<button class="randomize-conditions">Randomize</button>
|
|
51
|
+
```
|
|
52
|
+
```css
|
|
53
|
+
.preview {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
gap: 4px;
|
|
57
|
+
align-items: flex-start;
|
|
58
|
+
}
|
|
59
|
+
.preview p {
|
|
60
|
+
background: #44c;
|
|
61
|
+
color: white;
|
|
62
|
+
display: block;
|
|
63
|
+
border-radius: 99px;
|
|
64
|
+
padding: 4px 10px;
|
|
65
|
+
margin: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.preview xin-ab[not] p {
|
|
69
|
+
background: red;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
- Set `AbTest.conditions` to anything you like.
|
|
74
|
+
- Use `<xin-ab>` elements to display conditional content.
|
|
75
|
+
- `condition` attribute determines which value in `AbTest.conditions` controls the element
|
|
76
|
+
- `not` reverses the condition (so `<xin-ab not condition="foo">` will be visible if `conditions.foo` is `false`)
|
|
77
|
+
*/
|
|
78
|
+
import { Component, xinProxy } from 'xinjs';
|
|
79
|
+
const { abTestConditions } = xinProxy({
|
|
80
|
+
abTestConditions: {},
|
|
81
|
+
});
|
|
82
|
+
export class AbTest extends Component {
|
|
83
|
+
static set conditions(context) {
|
|
84
|
+
Object.assign(abTestConditions, context);
|
|
85
|
+
for (const abTest of [...AbTest.instances]) {
|
|
86
|
+
abTest.queueRender();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
condition = '';
|
|
90
|
+
not = false;
|
|
91
|
+
static instances = new Set();
|
|
92
|
+
constructor() {
|
|
93
|
+
super();
|
|
94
|
+
this.initAttributes('condition', 'not');
|
|
95
|
+
}
|
|
96
|
+
connectedCallback() {
|
|
97
|
+
super.connectedCallback();
|
|
98
|
+
AbTest.instances.add(this);
|
|
99
|
+
}
|
|
100
|
+
disconnectedCallback() {
|
|
101
|
+
super.disconnectedCallback();
|
|
102
|
+
AbTest.instances.delete(this);
|
|
103
|
+
}
|
|
104
|
+
render() {
|
|
105
|
+
if (this.condition !== '' &&
|
|
106
|
+
(this.not
|
|
107
|
+
? abTestConditions[this.condition] !== true
|
|
108
|
+
: abTestConditions[this.condition] === true)) {
|
|
109
|
+
this.toggleAttribute('hidden', false);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.toggleAttribute('hidden', true);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export const abTest = AbTest.elementCreator({ tag: 'xin-ab' });
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Component as WebComponent, ElementCreator } from 'tosijs';
|
|
2
|
+
type B3dCallback = ((element: B3d, BABYLON: any) => void) | ((element: B3d, BABYLON: any) => Promise<void>);
|
|
3
|
+
interface B3dUIOptions {
|
|
4
|
+
snippetId?: string;
|
|
5
|
+
jsonUrl?: string;
|
|
6
|
+
data?: any;
|
|
7
|
+
size?: number;
|
|
8
|
+
}
|
|
9
|
+
type MeshProcessCallback = (meshes: any[]) => void;
|
|
10
|
+
export declare class B3d extends WebComponent {
|
|
11
|
+
babylonReady: Promise<any>;
|
|
12
|
+
BABYLON?: any;
|
|
13
|
+
static styleSpec: {
|
|
14
|
+
':host': {
|
|
15
|
+
display: string;
|
|
16
|
+
position: string;
|
|
17
|
+
};
|
|
18
|
+
':host canvas': {
|
|
19
|
+
width: string;
|
|
20
|
+
height: string;
|
|
21
|
+
};
|
|
22
|
+
':host .babylonVRicon': {
|
|
23
|
+
height: number;
|
|
24
|
+
width: number;
|
|
25
|
+
backgroundColor: string;
|
|
26
|
+
filter: string;
|
|
27
|
+
backgroundImage: string;
|
|
28
|
+
backgroundPosition: string;
|
|
29
|
+
backgroundRepeat: string;
|
|
30
|
+
border: string;
|
|
31
|
+
borderRadius: number;
|
|
32
|
+
borderStyle: string;
|
|
33
|
+
outline: string;
|
|
34
|
+
transition: string;
|
|
35
|
+
};
|
|
36
|
+
':host .babylonVRicon:hover': {
|
|
37
|
+
transform: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
content: HTMLCanvasElement;
|
|
41
|
+
constructor();
|
|
42
|
+
scene: any;
|
|
43
|
+
engine: any;
|
|
44
|
+
sceneCreated: B3dCallback;
|
|
45
|
+
update: B3dCallback;
|
|
46
|
+
private _update;
|
|
47
|
+
onResize(): void;
|
|
48
|
+
loadScene: (path: string, file: string, processCallback?: MeshProcessCallback) => Promise<void>;
|
|
49
|
+
loadUI: (options: B3dUIOptions) => Promise<any>;
|
|
50
|
+
connectedCallback(): void;
|
|
51
|
+
}
|
|
52
|
+
export declare const b3d: ElementCreator<B3d>;
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/*#
|
|
2
|
+
# 3d
|
|
3
|
+
|
|
4
|
+
A [babylonjs](https://www.babylonjs.com/) wrapper.
|
|
5
|
+
|
|
6
|
+
A `<xin-3d>` element is initialized with an `engine`, `canvas`, `scene`, and an update-loop.
|
|
7
|
+
|
|
8
|
+
If you view this example with an XR-enabled device, such as the
|
|
9
|
+
[Meta Quest 3](https://www.meta.com/quest/quest-3/), then you should be able to view this
|
|
10
|
+
as an AR scene.
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
const { b3d, gamepadText, xrControllers, xrControllersText } = xinjsui
|
|
14
|
+
|
|
15
|
+
preview.append(b3d({
|
|
16
|
+
async sceneCreated(element, BABYLON) {
|
|
17
|
+
const camera = new BABYLON.FreeCamera(
|
|
18
|
+
'camera',
|
|
19
|
+
new BABYLON.Vector3(0, 1, -4),
|
|
20
|
+
element.scene
|
|
21
|
+
)
|
|
22
|
+
camera.attachControl(element.parts.canvas, true)
|
|
23
|
+
|
|
24
|
+
new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0.25, 1, -0.5))
|
|
25
|
+
|
|
26
|
+
this.loadScene('/', 'xin3d.glb')
|
|
27
|
+
|
|
28
|
+
const size = 1024
|
|
29
|
+
const textTexture = new BABYLON.DynamicTexture('Text', size, element.scene)
|
|
30
|
+
const textContext = textTexture.getContext()
|
|
31
|
+
textTexture.update()
|
|
32
|
+
|
|
33
|
+
const textMaterial = new BABYLON.StandardMaterial('Text', element.scene)
|
|
34
|
+
textMaterial.diffuseTexture = textTexture
|
|
35
|
+
textMaterial.emissiveTexture = textTexture
|
|
36
|
+
textMaterial.backfaceCulling = false
|
|
37
|
+
|
|
38
|
+
const plaque = BABYLON.MeshBuilder.CreatePlane('Plaque', {size: 1}, element.scene)
|
|
39
|
+
plaque.position.x = 0
|
|
40
|
+
plaque.position.y = 2
|
|
41
|
+
plaque.billboardMode = BABYLON.Mesh.BILLBOARDMODE_ALL
|
|
42
|
+
plaque.material = textMaterial
|
|
43
|
+
|
|
44
|
+
let controllers
|
|
45
|
+
if (navigator.xr) {
|
|
46
|
+
const xrHelper = await element.scene.createDefaultXRExperienceAsync({
|
|
47
|
+
uiOptions: {
|
|
48
|
+
sessionMode: 'immersive-ar'
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
controllers = xrControllers(xrHelper)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const interval = setInterval(() => {
|
|
55
|
+
if (document.body.contains(element)) {
|
|
56
|
+
textContext.fillStyle = '#204020'
|
|
57
|
+
textContext.fillRect(0, 0, size, size)
|
|
58
|
+
const text = gamepadText() + '\n' + xrControllersText(controllers)
|
|
59
|
+
const lines = text.split('\n')
|
|
60
|
+
textContext.fillStyle = '#afa'
|
|
61
|
+
textContext.font = '32px monospace'
|
|
62
|
+
for(let i = 0; i < lines.length; i++) {
|
|
63
|
+
const line = lines[i]
|
|
64
|
+
textContext.fillText(line, 40, 70 + i * 40)
|
|
65
|
+
}
|
|
66
|
+
textContext.fillStyle = '#bbb'
|
|
67
|
+
textContext.fillText('xinjs-xr — debug info', 40, 984)
|
|
68
|
+
textTexture.update()
|
|
69
|
+
} else {
|
|
70
|
+
clearInterval(interval)
|
|
71
|
+
}
|
|
72
|
+
}, 100)
|
|
73
|
+
},
|
|
74
|
+
}))
|
|
75
|
+
```
|
|
76
|
+
```css
|
|
77
|
+
.preview xin-3d {
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 100%;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
You can access the `scene` and `engine` properties. You can also assign `sceneCreated`
|
|
84
|
+
and `update` callbacks that will be executed when the scene is first initialized and
|
|
85
|
+
before each update, respectively. (See the example, it does both.)
|
|
86
|
+
|
|
87
|
+
Both `sceneCreated` and `update` may be `async`. The component will `await` `sceneCreated`
|
|
88
|
+
before starting the renderLoop, but `update` is simply passed to babylon, so be careful.
|
|
89
|
+
|
|
90
|
+
By default, this component loads `babylon.js` from the [babylonjs CDN](https://doc.babylonjs.com/setup/frameworkPackages/CDN),
|
|
91
|
+
but if `BABYLON` is already defined (e.g. if you've bundled it) then it will use that instead.
|
|
92
|
+
|
|
93
|
+
If you need additional libraries, e.g. `https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js` for loading models such as `gltf` and `glb` files, you should load those in `sceneCreated`.
|
|
94
|
+
|
|
95
|
+
Here's a simple example of a terrain mesh comprising 125k triangles, 50% of which is being scaled using a `profileScale` function that
|
|
96
|
+
takes an array of numbers that use a linear profile to change the landform.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
const { b3d } = xinjsui
|
|
100
|
+
const { MoreMath } = xinjs
|
|
101
|
+
|
|
102
|
+
const debugCutoff = 0.5
|
|
103
|
+
const defaultProfile = [0, 1, 5, 8, 10].map(x => x/10)
|
|
104
|
+
|
|
105
|
+
const { clamp } = MoreMath
|
|
106
|
+
function profileScale(t = 0, bypass = false, profile = defaultProfile) {
|
|
107
|
+
if (bypass) {
|
|
108
|
+
return t
|
|
109
|
+
}
|
|
110
|
+
const count = profile.length - 1
|
|
111
|
+
if (count < 1) {
|
|
112
|
+
throw new Error('profile must be of length ≥ 2')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const s = clamp(0, (t + 1) / 2, 1)
|
|
116
|
+
const index = Math.floor(s * count)
|
|
117
|
+
const dt = (s - index / count) * count
|
|
118
|
+
const min = profile[index]
|
|
119
|
+
const max = profile[index + 1]
|
|
120
|
+
const p = dt * (max - min) + min
|
|
121
|
+
return 2 * p - 1
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
preview.append(b3d({
|
|
125
|
+
async sceneCreated(element, BABYLON) {
|
|
126
|
+
const { scene } = element
|
|
127
|
+
const { createNoise2D } = await import('https://cdn.jsdelivr.net/npm/simplex-noise@4.0.1/+esm')
|
|
128
|
+
|
|
129
|
+
new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0.25, 1, 2))
|
|
130
|
+
|
|
131
|
+
const terrain = new BABYLON.Mesh('terrain', scene)
|
|
132
|
+
const vertexData = new BABYLON.VertexData()
|
|
133
|
+
|
|
134
|
+
const noise2D = createNoise2D()
|
|
135
|
+
const positions = []
|
|
136
|
+
const indices = []
|
|
137
|
+
const gridSize = 100
|
|
138
|
+
const gridResolution = 250
|
|
139
|
+
const gridPoints = gridResolution + 1
|
|
140
|
+
const noiseScale = 0.03
|
|
141
|
+
const heightScale = 4.5
|
|
142
|
+
terrain.position.y = -5
|
|
143
|
+
const scale = t => t * gridSize / gridResolution - gridSize * 0.5
|
|
144
|
+
for(let x = 0; x <= gridResolution; x++) {
|
|
145
|
+
for(let z = 0; z <= gridResolution; z++) {
|
|
146
|
+
const y = profileScale(noise2D(scale(x) * noiseScale, scale(z) * noiseScale), x < gridResolution * debugCutoff)
|
|
147
|
+
positions.push(scale(x), y * heightScale, scale(z))
|
|
148
|
+
if (x > 0 && z > 0) {
|
|
149
|
+
const i = x * gridPoints + z
|
|
150
|
+
indices.push(
|
|
151
|
+
i, i - gridPoints - 1, i - 1,
|
|
152
|
+
i, i - gridPoints, i - gridPoints - 1,
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const normals = []
|
|
158
|
+
BABYLON.VertexData.ComputeNormals(positions, indices, normals);
|
|
159
|
+
|
|
160
|
+
vertexData.positions = positions
|
|
161
|
+
vertexData.indices = indices
|
|
162
|
+
vertexData.normals = normals
|
|
163
|
+
vertexData.applyToMesh(terrain)
|
|
164
|
+
},
|
|
165
|
+
}))
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## loadScene
|
|
169
|
+
|
|
170
|
+
`<xin-3d>.loadScene(path: string, file: string, callBack(meshes: any[]): void)` can
|
|
171
|
+
be used to load `.glb` files.
|
|
172
|
+
|
|
173
|
+
## loadUI
|
|
174
|
+
|
|
175
|
+
`<xin-3d>.loadUI(options: B3dUIOptions)` loads babylonjs guis, which you can create programmatically or using the [babylonjs gui tool](https://gui.babylonjs.com/).
|
|
176
|
+
*/
|
|
177
|
+
import { Component as WebComponent, elements } from 'xinjs';
|
|
178
|
+
import { scriptTag } from './via-tag';
|
|
179
|
+
import { icons, svg2DataUrl } from './icons';
|
|
180
|
+
const noop = () => {
|
|
181
|
+
/* do not care */
|
|
182
|
+
};
|
|
183
|
+
export class B3d extends WebComponent {
|
|
184
|
+
babylonReady;
|
|
185
|
+
BABYLON;
|
|
186
|
+
static styleSpec = {
|
|
187
|
+
':host': {
|
|
188
|
+
display: 'block',
|
|
189
|
+
position: 'relative',
|
|
190
|
+
},
|
|
191
|
+
':host canvas': {
|
|
192
|
+
width: '100%',
|
|
193
|
+
height: '100%',
|
|
194
|
+
},
|
|
195
|
+
':host .babylonVRicon': {
|
|
196
|
+
height: 50,
|
|
197
|
+
width: 80,
|
|
198
|
+
backgroundColor: 'transparent',
|
|
199
|
+
filter: 'drop-shadow(0 0 4px #000c)',
|
|
200
|
+
backgroundImage: svg2DataUrl(icons.xrColor()),
|
|
201
|
+
backgroundPosition: 'center',
|
|
202
|
+
backgroundRepeat: 'no-repeat',
|
|
203
|
+
border: 'none',
|
|
204
|
+
borderRadius: 5,
|
|
205
|
+
borderStyle: 'none',
|
|
206
|
+
outline: 'none',
|
|
207
|
+
transition: 'transform 0.125s ease-out',
|
|
208
|
+
},
|
|
209
|
+
':host .babylonVRicon:hover': {
|
|
210
|
+
transform: 'scale(1.1)',
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
content = elements.canvas({ part: 'canvas' });
|
|
214
|
+
constructor() {
|
|
215
|
+
super();
|
|
216
|
+
this.babylonReady = (async () => {
|
|
217
|
+
const { BABYLON } = await scriptTag('https://cdn.babylonjs.com/babylon.js', 'BABYLON');
|
|
218
|
+
return BABYLON;
|
|
219
|
+
})();
|
|
220
|
+
}
|
|
221
|
+
scene;
|
|
222
|
+
engine;
|
|
223
|
+
sceneCreated = noop;
|
|
224
|
+
update = noop;
|
|
225
|
+
_update = () => {
|
|
226
|
+
if (this.scene) {
|
|
227
|
+
if (this.update !== undefined) {
|
|
228
|
+
this.update(this, this.BABYLON);
|
|
229
|
+
}
|
|
230
|
+
if (this.scene.activeCamera !== undefined) {
|
|
231
|
+
this.scene.render();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
onResize() {
|
|
236
|
+
if (this.engine) {
|
|
237
|
+
this.engine.resize();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
loadScene = async (path, file, processCallback) => {
|
|
241
|
+
const { BABYLON } = await scriptTag('https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js', 'BABYLON');
|
|
242
|
+
BABYLON.SceneLoader.Append(path, file, this.scene, processCallback);
|
|
243
|
+
};
|
|
244
|
+
loadUI = async (options) => {
|
|
245
|
+
const { BABYLON } = await scriptTag('https://cdn.babylonjs.com/gui/babylon.gui.min.js', 'BABYLON');
|
|
246
|
+
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('GUI', true, this.scene);
|
|
247
|
+
const { snippetId, jsonUrl, data, size } = options;
|
|
248
|
+
if (size) {
|
|
249
|
+
advancedTexture.idealWidth = size;
|
|
250
|
+
advancedTexture.renderAtIdealSize = true;
|
|
251
|
+
}
|
|
252
|
+
// edit or create your own snippet here
|
|
253
|
+
// https://gui.babylonjs.com/
|
|
254
|
+
let gui;
|
|
255
|
+
if (snippetId) {
|
|
256
|
+
gui = await advancedTexture.parseFromSnippetAsync(snippetId);
|
|
257
|
+
}
|
|
258
|
+
else if (jsonUrl) {
|
|
259
|
+
gui = await advancedTexture.parseFromURLAsync(jsonUrl);
|
|
260
|
+
}
|
|
261
|
+
else if (data) {
|
|
262
|
+
gui = advancedTexture.parseContent(data);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const root = advancedTexture.getChildren()[0];
|
|
268
|
+
const widgets = root.children.reduce((map, widget) => {
|
|
269
|
+
map[widget.name] = widget;
|
|
270
|
+
return map;
|
|
271
|
+
}, {});
|
|
272
|
+
return { advancedTexture, gui, root, widgets };
|
|
273
|
+
};
|
|
274
|
+
connectedCallback() {
|
|
275
|
+
super.connectedCallback();
|
|
276
|
+
const { canvas } = this.parts;
|
|
277
|
+
this.babylonReady.then(async (BABYLON) => {
|
|
278
|
+
this.BABYLON = BABYLON;
|
|
279
|
+
this.engine = new BABYLON.Engine(canvas, true);
|
|
280
|
+
this.scene = new BABYLON.Scene(this.engine);
|
|
281
|
+
if (this.sceneCreated) {
|
|
282
|
+
await this.sceneCreated(this, BABYLON);
|
|
283
|
+
}
|
|
284
|
+
if (this.scene.activeCamera === undefined) {
|
|
285
|
+
const camera = new BABYLON.ArcRotateCamera('default-camera', -Math.PI / 2, Math.PI / 2.5, 3, new BABYLON.Vector3(0, 0, 0));
|
|
286
|
+
camera.attachControl(this.parts.canvas, true);
|
|
287
|
+
}
|
|
288
|
+
this.engine.runRenderLoop(this._update);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
export const b3d = B3d.elementCreator({ tag: 'xin-3d' });
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Component as WebComponent, ElementCreator } from 'tosijs';
|
|
2
|
+
export interface LottieConfig {
|
|
3
|
+
container?: HTMLElement | ShadowRoot;
|
|
4
|
+
renderer: 'svg' | 'canvas' | 'html';
|
|
5
|
+
loop: boolean;
|
|
6
|
+
autoplay: boolean;
|
|
7
|
+
animationData?: string;
|
|
8
|
+
path?: string;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
}
|
|
11
|
+
export declare class BodymovinPlayer extends WebComponent {
|
|
12
|
+
content: null;
|
|
13
|
+
src: string;
|
|
14
|
+
json: string;
|
|
15
|
+
config: LottieConfig;
|
|
16
|
+
static bodymovinAvailable?: Promise<any>;
|
|
17
|
+
animation: any;
|
|
18
|
+
static styleSpec: {
|
|
19
|
+
':host': {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
display: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
private _loading;
|
|
26
|
+
get loading(): boolean;
|
|
27
|
+
constructor();
|
|
28
|
+
private readonly doneLoading;
|
|
29
|
+
private readonly load;
|
|
30
|
+
render(): void;
|
|
31
|
+
}
|
|
32
|
+
export declare const bodymovinPlayer: ElementCreator<BodymovinPlayer>;
|