fixing merge conflicts
This commit is contained in:
commit
bceee7ecf1
3 changed files with 95 additions and 56 deletions
134
README.md
134
README.md
|
|
@ -1,11 +1,87 @@
|
|||
# zulip
|
||||
|
||||
A minimal, no-build web app kit using only Lit-based web components via importmaps.
|
||||
A modern, no-build web application using Lit web components with Zustand state management.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
## Quick Start Example
|
||||
* **Lit-based web components** - Standards-based, framework-agnostic UI
|
||||
* **No-build tooling** - Import maps only, no bundler required
|
||||
* **Zustand state management** - Lightweight, vanilla JS store
|
||||
* **StoreController** - Reactive controller that bridges Zustand and Lit
|
||||
|
||||
### 1. Define a Component
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
components/ -> standard lit components, including top-level "app-root"
|
||||
controllers/ -> lit reactive controllers (such as Zustand<->Lit bridge)
|
||||
store/ -> defines specific accessor sets
|
||||
index.html -> entry point with inline importmap and app-root
|
||||
README.md
|
||||
```
|
||||
|
||||
## Data Flow Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant LitComponent
|
||||
participant StoreController
|
||||
participant ZustandStore
|
||||
|
||||
User->>LitComponent: Click button
|
||||
LitComponent->>ZustandStore: Call action (e.g., addItem)
|
||||
ZustandStore->>ZustandStore: Update state
|
||||
ZustandStore->>StoreController: Notify subscribers
|
||||
StoreController->>LitComponent: requestUpdate()
|
||||
LitComponent->>LitComponent: Re-render
|
||||
LitComponent->>User: Show updated UI
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
### 1. Define Store (Zustand Vanilla)
|
||||
|
||||
```javascript
|
||||
// store/index.js
|
||||
import { createStore } from 'zustand/vanilla'
|
||||
|
||||
export const store = createStore((set, get) => ({
|
||||
count: 0,
|
||||
increment: () => set(s => ({ count: s.count + 1 })),
|
||||
decrement: () => set(s => ({ count: s.count - 1 })),
|
||||
}))
|
||||
```
|
||||
|
||||
### 2. Create StoreController (Bridge)
|
||||
|
||||
```javascript
|
||||
// controllers/store.js
|
||||
export class StoreController {
|
||||
constructor(host, store, selector) {
|
||||
this.host = host
|
||||
this.store = store
|
||||
this.selector = selector
|
||||
host.addController(this)
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
this._unsub = this.store.subscribe((state) => {
|
||||
const next = this.selector(state)
|
||||
if (next !== this.value) {
|
||||
this.value = next
|
||||
this.host.requestUpdate()
|
||||
}
|
||||
})
|
||||
this.value = this.selector(this.store.getState())
|
||||
}
|
||||
|
||||
hostDisconnected() {
|
||||
this._unsub?.()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Create Component (Lit + StoreController)
|
||||
|
||||
```javascript
|
||||
// components/counter.js
|
||||
|
|
@ -22,16 +98,15 @@ class Counter extends LitElement {
|
|||
}
|
||||
|
||||
static styles = css`
|
||||
button { padding: 1rem; font-size: 1.2rem; }
|
||||
button { padding: 1rem; font-size: 1.2rem; margin: 0.5rem; }
|
||||
`
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div>
|
||||
<p>Count: ${this.count}</p>
|
||||
<button @click=${() => this.count++}>
|
||||
Increment
|
||||
</button>
|
||||
<p>Count: ${this.#count.value}</p>
|
||||
<button @click=${() => store.getState().decrement()}>-</button>
|
||||
<button @click=${() => store.getState().increment()}>+</button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
|
@ -40,53 +115,24 @@ class Counter extends LitElement {
|
|||
customElements.define('my-counter', Counter)
|
||||
```
|
||||
|
||||
### 2. Use in App root (or other composition node)
|
||||
### 4. Instantiate in parent (such as app-root)
|
||||
|
||||
```js
|
||||
import "./my-counter.js";
|
||||
import counter from "./counter.js":
|
||||
|
||||
class AppRoot extends LitElement {
|
||||
...
|
||||
render() {
|
||||
return html`<my-counter .count=${0}></my-counter>`;
|
||||
return html`
|
||||
<counter></counter>
|
||||
`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Define Data Flows
|
||||
|
||||
#### Parent-to-Child (Props)
|
||||
|
||||
```javascript
|
||||
// Parent passes data down
|
||||
html`<child-component .items=${this.items}></child-component>`
|
||||
|
||||
// Child receives via properties
|
||||
static properties = {
|
||||
items: { type: Array }
|
||||
}
|
||||
```
|
||||
|
||||
#### Child-to-Parent (Events)
|
||||
|
||||
```javascript
|
||||
// Child emits event
|
||||
this.dispatchEvent(new CustomEvent('add-item', {
|
||||
detail: { item: newItem },
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}))
|
||||
|
||||
// Parent listens
|
||||
html`<child-component @add-item=${(e) => this.handleAdd(e.detail.item)}></child-component>`
|
||||
```
|
||||
|
||||
## Benefits Over TSX
|
||||
|
||||
1. **No build step** - Edit and refresh, instant feedback
|
||||
|
||||
1. **Minimal dependencies** - Just Lit, nothing else
|
||||
|
||||
1. **Smaller bundle** - No framework runtime, just standards
|
||||
|
||||
1. **Better encapsulation** - Shadow DOM, scoped styles
|
||||
|
|
@ -95,4 +141,6 @@ html`<child-component @add-item=${(e) => this.handleAdd(e.detail.item)}></child-
|
|||
|
||||
1. **Future-proof** - Built on web standards
|
||||
|
||||
1. **Easy to learn** - Simple mental model for state, with natural evolution into Zustand/IndexedDB or similar extensions
|
||||
1. **Familiar patterns** - Zustand works like Redux/Context
|
||||
|
||||
1. **Type-safe** - Can add JSDoc or TypeScript without build step
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue