Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/chnicoloso/lit-jsx
A JSX runtime for LitElement
https://github.com/chnicoloso/lit-jsx
jsx jsx-runtime lit lit-element webcomponents webcomponents-framework webxr
Last synced: 3 months ago
JSON representation
A JSX runtime for LitElement
- Host: GitHub
- URL: https://github.com/chnicoloso/lit-jsx
- Owner: chnicoloso
- License: apache-2.0
- Created: 2024-06-10T19:59:42.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2024-07-12T03:34:09.000Z (6 months ago)
- Last Synced: 2024-10-02T06:44:13.426Z (3 months ago)
- Topics: jsx, jsx-runtime, lit, lit-element, webcomponents, webcomponents-framework, webxr
- Language: TypeScript
- Homepage:
- Size: 274 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# lit-jsx
`lit-jsx` is a library that provides a convenient way to build Lit components using JSX syntax. The only dependency for the library is `lit` itself.
## Why?
Personal aesthetic preference: I am on board with everything about Lit but I cannot bring myself to write components using string templates.## Installation
You can install `lit-jsx` via npm:```bash
npm install @chnicoloso/lit-jsx
```## Usage
To use `lit-jsx` simply import from `@chnicoloso/lit-jsx` whatever you would otherwise import from `lit`. You also need to configure your bundler or transpiler to use `lit-jsx` for processing JSX, instead of the default. For example in TypeScript you would add something like this to your `tsconfig.json`:```json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@chnicoloso/lit-jsx",
"types": [ "@chnicoloso/lit-jsx" ]
}
}
```
There is a similar mechanism for Babel where you would add something like this to your `babel.config.js`:
```json
{
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"throwIfNamespace": false,
"runtime": "automatic",
"importSource": "@chnicoloso/lit-jsx"
}
]
]
}
```### Example
```typescript
import { LitElement, createRoot, customElement } from '@chnicoloso/lit-jsx';@customElement('my-app')
class App extends LitElement {
render() {
return (
Click Me!
);
}
}const app = document.createElement('div');
document.body.appendChild(app);const root = createRoot(app);
root.render(new App());// Clean up.
const onUnload = () => {
window.removeEventListener('beforeunload', onUnload);
root.unmount();
};
window.addEventListener('beforeunload', onUnload);
```### Running the Example
```bash
cd ./example
npm install
npm run watch
```
Go to https://localhost:8080/### Custom Elements
`lit-jsx` allows you to customize what should be rendered for each tag name. For example, if you want JSX `` to result in ``, you would pass an override in your registry.
Here's how you would do that:
```typescript
import { assignElements, resetElements } from '@chnicoloso/lit-jsx';// Define your custom elements
const customElements = {
button: 'my-custom-button',
// Add more custom elements as needed
};// Assign your custom elements
assignElements(customElements);// Later, if you want to reset to the default elements
resetElements();
```
#### And why in the world would I want to do _that_?The original motivation behind this jsx-runtime was for a WebXR UI framework I was working on. In WebXR, any UI you make _has_ to be rendered using Canvas/webgl which is as fun as it sounds so I wanted to be able to define UI components using HTML syntax. My basic idea was to create webgl/three-js versions of each HTML element and then configure `lit-jsx` so that whenever the JSX called for say, a “button” to be rendered, the webgl equivalent would be rendered instead.
I got a fair amount of it working including divs, images, text, overflow management, scroll bars, flex-box, border-radii, background colors, etc.
Expand to see the code for this "component"
```typescript
/** @jsxImportSource src/lit-jsx */import classNames from 'classnames';
import { createRoot, state, customElement, css } from 'src/canvas-elements';
import * as CanvasElements from 'src/canvas-elements';@customElement('my-scroller')
class HorizontalScroller extends CanvasElements.Component {static styles = css`
.horizontal-scroller {
flex-direction: row;
width: 1px;
height: 1px;
align-items: center;
justify-content: flex-start;
border-radius: 0.02px;
overflow: scroll;
}.box {
width: 0.3px;
height: 0.3px;
border-width: 0.04px;
border-radius: 0.02px;
}.yellow { background-color: yellow; }
.green { background-color: green; }
.purple { background-color: purple; }
.red { background-color: darkred; }
.cyan { background-color: cyan; }
.blue { background-color: blue; }
.orange { background-color: orange; }
`;render() {
return (
);
}
}@customElement('my-app')
export default class App extends CanvasElements.Component {static styles = css`
* {
border-radius: 0.02px;
}.root-container {
width: 100%;
height: 100%;
overflow: auto;
flex-direction: row;
align-items: center;
justify-content: space-between;
}.padded-space-between-container {
flex-direction: row;
width: 1px;
height: 1px;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 0.2px;
border-radius: 0.02px;
background-opacity: 0.5;
}.vertical-scroller {
flex-direction: column;
width: 1px;
height: 1px;
align-items: center;
justify-content: flex-start;
border-width: 0.04px;
border-radius: 0.02px;
overflow: scroll;
padding: 0.05px;
}.text-container {
width: 1px;
height: 1px;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
border-width: 0.04px;
border-radius: 0.02px;
overflow: auto;
font-size: 0.1px;
color: white;
overflow-wrap: break-word;
}.small-box {
width: 0.2px;
height: 0.2px;
}.medium-box {
width: 1px;
height: 1px;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
border-width: 0.04px;
border-radius: 0.02px;
}.image {
border-width: 0.04px;
border-radius: 0.02px;
}.yellow { background-color: yellow; }
.green { background-color: green; }
.purple { background-color: purple; }
.red { background-color: darkred; }
.cyan { background-color: cyan; }
.blue { background-color: blue; }
.orange { background-color: orange; }
`;connectedCallback(): void {
super.connectedCallback();
window.addEventListener('click', this._updateWidth);
window.addEventListener('keyup', this._updateText);
}disconnectedCallback(): void {
super.disconnectedCallback();
window.removeEventListener('click', this._updateWidth);
window.removeEventListener('keyup', this._updateText);
}@state()
width = 0.2;@state()
text = '';_updateWidth = () => {
this.width += 0.1;
}_updateText = (keyEvent) => {
this.text += keyEvent.key;
}get verticalScroller() {
const colors = [ 'purple', 'green', 'blue', 'orange', 'cyan', 'red', 'green', 'purple', 'blue', 'red' ];
return (
{colors.map(color => )}
);
}render() {
return (
{this.verticalScroller}
1. ByeasfkndksnfmfdsamfksafksdByeasfkndksnfmfdsamfksaf
{`2. Type here: ${this.text}`}
= 0.5
})}/>
);
}
}const root = createRoot(document.getElementById('view'));
root.render(new App());
```![Jun-11-2024 20-09-42](https://github.com/chnicoloso/lit-jsx/assets/9637975/0e6f39a4-2a6d-48b4-8717-fd974f5feb71)
I also managed to embed these "HTML" components onto ThreeJS Scenes and I made some progress towards a "composer" based on [Exokit](https://github.com/exokitxr/exokit) that would allow multiple independent "plugin" components to be combined onto the same "host" scene
![Jun-11-2024 20-26-21](https://github.com/chnicoloso/lit-jsx/assets/9637975/dfea6932-7746-4db9-a00c-2a9e5cc2c417)Ultimately however, I decided that this simply cannot not be future of the [Immersive Web](https://immersiveweb.dev/) and that an _immersive_ (not just mobile) version of something like [DOM Overlays](https://www.w3.org/TR/webxr-dom-overlays-1/) is needed in the WebXR standard - maybe taking advantage of the existing CSS-transform's 3D capabilities, as shown by ThreeJS's [CSS3DRenderer](https://threejs.org/docs/#examples/en/renderers/CSS3DRenderer).
![Jun-11-2024 20-53-40](https://github.com/chnicoloso/lit-jsx/assets/9637975/2f3a10d5-07cf-4993-876b-00c6d0e8609e)Good times though.