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.
Files changed (134) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/ab-test.d.ts +14 -0
  4. package/dist/ab-test.js +116 -0
  5. package/dist/babylon-3d.d.ts +53 -0
  6. package/dist/babylon-3d.js +292 -0
  7. package/dist/bodymovin-player.d.ts +32 -0
  8. package/dist/bodymovin-player.js +172 -0
  9. package/dist/bp-loader.d.ts +1 -0
  10. package/dist/bp-loader.js +26 -0
  11. package/dist/carousel.d.ts +113 -0
  12. package/dist/carousel.js +308 -0
  13. package/dist/code-editor.d.ts +27 -0
  14. package/dist/code-editor.js +102 -0
  15. package/dist/color-input.d.ts +41 -0
  16. package/dist/color-input.js +112 -0
  17. package/dist/data-table.d.ts +79 -0
  18. package/dist/data-table.js +774 -0
  19. package/dist/drag-and-drop.d.ts +2 -0
  20. package/dist/drag-and-drop.js +386 -0
  21. package/dist/editable-rect.d.ts +97 -0
  22. package/dist/editable-rect.js +450 -0
  23. package/dist/filter-builder.d.ts +64 -0
  24. package/dist/filter-builder.js +468 -0
  25. package/dist/float.d.ts +18 -0
  26. package/dist/float.js +170 -0
  27. package/dist/form.d.ts +68 -0
  28. package/dist/form.js +466 -0
  29. package/dist/gamepad.d.ts +34 -0
  30. package/dist/gamepad.js +115 -0
  31. package/dist/icon-data.d.ts +312 -0
  32. package/dist/icon-data.js +308 -0
  33. package/dist/icon-types.d.ts +7 -0
  34. package/dist/icon-types.js +1 -0
  35. package/dist/icons.d.ts +17 -0
  36. package/dist/icons.js +374 -0
  37. package/dist/iife.js +69 -0
  38. package/dist/iife.js.map +49 -0
  39. package/dist/index-iife.d.ts +1 -0
  40. package/dist/index-iife.js +4 -0
  41. package/dist/index.d.ts +37 -0
  42. package/dist/index.js +37 -0
  43. package/dist/index.js.map +47 -0
  44. package/dist/live-example.d.ts +63 -0
  45. package/dist/live-example.js +611 -0
  46. package/dist/localize.d.ts +46 -0
  47. package/dist/localize.js +381 -0
  48. package/dist/make-sorter.d.ts +3 -0
  49. package/dist/make-sorter.js +119 -0
  50. package/dist/make-sorter.test.d.ts +1 -0
  51. package/dist/make-sorter.test.js +48 -0
  52. package/dist/mapbox.d.ts +24 -0
  53. package/dist/mapbox.js +161 -0
  54. package/dist/markdown-viewer.d.ts +17 -0
  55. package/dist/markdown-viewer.js +173 -0
  56. package/dist/match-shortcut.d.ts +9 -0
  57. package/dist/match-shortcut.js +13 -0
  58. package/dist/match-shortcut.test.d.ts +1 -0
  59. package/dist/match-shortcut.test.js +194 -0
  60. package/dist/menu.d.ts +60 -0
  61. package/dist/menu.js +614 -0
  62. package/dist/notifications.d.ts +106 -0
  63. package/dist/notifications.js +308 -0
  64. package/dist/password-strength.d.ts +35 -0
  65. package/dist/password-strength.js +302 -0
  66. package/dist/playwright.config.d.ts +9 -0
  67. package/dist/playwright.config.js +73 -0
  68. package/dist/pop-float.d.ts +10 -0
  69. package/dist/pop-float.js +231 -0
  70. package/dist/rating.d.ts +62 -0
  71. package/dist/rating.js +192 -0
  72. package/dist/rich-text.d.ts +35 -0
  73. package/dist/rich-text.js +296 -0
  74. package/dist/segmented.d.ts +80 -0
  75. package/dist/segmented.js +298 -0
  76. package/dist/select.d.ts +43 -0
  77. package/dist/select.js +427 -0
  78. package/dist/side-nav.d.ts +36 -0
  79. package/dist/side-nav.js +106 -0
  80. package/dist/size-break.d.ts +18 -0
  81. package/dist/size-break.js +118 -0
  82. package/dist/sizer.d.ts +34 -0
  83. package/dist/sizer.js +92 -0
  84. package/dist/src/ab-test.d.ts +14 -0
  85. package/dist/src/babylon-3d.d.ts +53 -0
  86. package/dist/src/bodymovin-player.d.ts +32 -0
  87. package/dist/src/bp-loader.d.ts +0 -0
  88. package/dist/src/carousel.d.ts +113 -0
  89. package/dist/src/code-editor.d.ts +27 -0
  90. package/dist/src/color-input.d.ts +41 -0
  91. package/dist/src/data-table.d.ts +79 -0
  92. package/dist/src/drag-and-drop.d.ts +2 -0
  93. package/dist/src/editable-rect.d.ts +97 -0
  94. package/dist/src/filter-builder.d.ts +64 -0
  95. package/dist/src/float.d.ts +18 -0
  96. package/dist/src/form.d.ts +68 -0
  97. package/dist/src/gamepad.d.ts +34 -0
  98. package/dist/src/icon-data.d.ts +309 -0
  99. package/dist/src/icon-types.d.ts +7 -0
  100. package/dist/src/icons.d.ts +17 -0
  101. package/dist/src/index.d.ts +37 -0
  102. package/dist/src/live-example.d.ts +51 -0
  103. package/dist/src/localize.d.ts +30 -0
  104. package/dist/src/make-sorter.d.ts +3 -0
  105. package/dist/src/mapbox.d.ts +24 -0
  106. package/dist/src/markdown-viewer.d.ts +15 -0
  107. package/dist/src/match-shortcut.d.ts +9 -0
  108. package/dist/src/menu.d.ts +60 -0
  109. package/dist/src/notifications.d.ts +106 -0
  110. package/dist/src/password-strength.d.ts +35 -0
  111. package/dist/src/pop-float.d.ts +10 -0
  112. package/dist/src/rating.d.ts +62 -0
  113. package/dist/src/rich-text.d.ts +28 -0
  114. package/dist/src/segmented.d.ts +80 -0
  115. package/dist/src/select.d.ts +43 -0
  116. package/dist/src/side-nav.d.ts +36 -0
  117. package/dist/src/size-break.d.ts +18 -0
  118. package/dist/src/sizer.d.ts +34 -0
  119. package/dist/src/tab-selector.d.ts +91 -0
  120. package/dist/src/tag-list.d.ts +37 -0
  121. package/dist/src/track-drag.d.ts +5 -0
  122. package/dist/src/version.d.ts +1 -0
  123. package/dist/src/via-tag.d.ts +2 -0
  124. package/dist/tab-selector.d.ts +91 -0
  125. package/dist/tab-selector.js +326 -0
  126. package/dist/tag-list.d.ts +37 -0
  127. package/dist/tag-list.js +375 -0
  128. package/dist/track-drag.d.ts +5 -0
  129. package/dist/track-drag.js +143 -0
  130. package/dist/version.d.ts +1 -0
  131. package/dist/version.js +1 -0
  132. package/dist/via-tag.d.ts +2 -0
  133. package/dist/via-tag.js +102 -0
  134. 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>>;
@@ -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>;