@3sln/dodo 0.0.1
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 +128 -0
- package/dodo.js +698 -0
- package/index.js +10 -0
- package/package.json +31 -0
- package/src/html.js +128 -0
- package/src/scheduler.js +79 -0
- package/src/vdom.js +676 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Ray Stubbs
|
|
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,128 @@
|
|
|
1
|
+
# dodo
|
|
2
|
+
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> This is a work-in-progress project and is not yet ready for production use.
|
|
5
|
+
|
|
6
|
+
A minimal, configurable virtual DOM library.
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
import { reconcile, h1, p, div } from '@3sln/dodo';
|
|
12
|
+
|
|
13
|
+
const container = document.getElementById('root');
|
|
14
|
+
|
|
15
|
+
// Use helper functions like h1(), p(), etc. for standard elements.
|
|
16
|
+
// The props object is optional.
|
|
17
|
+
const myVdom = div({ id: 'app' },
|
|
18
|
+
h1('Hello, dodo!'),
|
|
19
|
+
p({ $classes: ['text-lg'] }, 'This is a paragraph.')
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Reconcile the virtual DOM with the real DOM.
|
|
23
|
+
reconcile(container, [myVdom]);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Core Concepts
|
|
27
|
+
|
|
28
|
+
- **VDOM Factory:** The library exports a `vdom(settings)` factory to create custom instances. The default export is a pre-configured instance.
|
|
29
|
+
- **HTML Helpers:** Simple functions like `div()`, `p()`, `span()` are exported for convenience.
|
|
30
|
+
- **`$`-Prefixed Props:** Special props that `dodo` intercepts are prefixed with a `$` to avoid conflicts with standard properties (e.g., `$classes`, `$styling`, `$attrs`, `$dataset`).
|
|
31
|
+
- **`.key()`:** Chain `.key('unique-id')` to any VNode in a list to enable efficient, keyed reconciliation.
|
|
32
|
+
- **`.on()`:** Chain `.on({ event: handler })` to any VNode to attach event listeners or lifecycle hooks (`$attach`, `$detach`, `$update`).
|
|
33
|
+
- **`.opaque()`**: Marks an element node as opaque, meaning `dodo` will manage its props but not its children.
|
|
34
|
+
|
|
35
|
+
## Keys for List Reconciliation
|
|
36
|
+
|
|
37
|
+
When rendering lists of elements, always use the `.key()` method to ensure efficient updates and reordering.
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import { reconcile, div } from '@3sln/dodo';
|
|
41
|
+
|
|
42
|
+
const data = [{id: 1, text: 'A'}, {id: 2, text: 'B'}, {id: 3, text: 'C'}];
|
|
43
|
+
const container = document.getElementById('list');
|
|
44
|
+
|
|
45
|
+
// Chain .key() to the VNode returned by the helper function.
|
|
46
|
+
reconcile(container, data.map(item => div(item.text).key(item.id)));
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Event Listeners and Lifecycle Hooks
|
|
50
|
+
|
|
51
|
+
Use the `.on()` method to attach event listeners and lifecycle hooks.
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
import { reconcile, button } from '@3sln/dodo';
|
|
55
|
+
|
|
56
|
+
const myButton = button('Hover over me').on({
|
|
57
|
+
// Simple function handler
|
|
58
|
+
mouseover: () => console.log('Mouse entered!'),
|
|
59
|
+
|
|
60
|
+
// Object handler for more options
|
|
61
|
+
mouseleave: {
|
|
62
|
+
listener: () => console.log('Mouse left!'),
|
|
63
|
+
capture: true
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Lifecycle hooks
|
|
67
|
+
$attach: (domNode) => console.log('Button node was created.'),
|
|
68
|
+
$detach: (domNode) => console.log('Button has been removed.')
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const container = document.getElementById('event-container');
|
|
72
|
+
reconcile(container, myButton);
|
|
73
|
+
reconcile(container, null); // Triggers $detach
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Understanding `reconcile()`
|
|
77
|
+
|
|
78
|
+
The `reconcile` function has two distinct modes of operation:
|
|
79
|
+
|
|
80
|
+
- **`reconcile(target, vnode)` (Onto):** Modifies the `target` element itself to match the `vnode`. This requires the `target`'s tag name to be compatible with the `vnode`'s tag (unless the `vnode` is an `alias` or `special`).
|
|
81
|
+
|
|
82
|
+
- **`reconcile(target, [vnodes...])` (Into):** Modifies the *children* of the `target` element to match the array of `vnodes`. The `target` element's own properties are not changed.
|
|
83
|
+
|
|
84
|
+
- **`reconcile(target, null)` (Cleanup):** Detaches `dodo` from the `target` and its descendants, running all necessary cleanup logic.
|
|
85
|
+
|
|
86
|
+
## Advanced: Web Components with `special`
|
|
87
|
+
|
|
88
|
+
The `special` component is a powerful tool for integrating with other libraries or, as shown here, for providing the rendering logic for a standard Web Component.
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
import { reconcile, special, p, h, alias } from '@3sln/dodo';
|
|
92
|
+
|
|
93
|
+
// 1. Create a special component to manage a shadow root.
|
|
94
|
+
const shadowComponent = special({
|
|
95
|
+
attach: (domNode) => {
|
|
96
|
+
// Create a shadow root on the host element once.
|
|
97
|
+
domNode.attachShadow({ mode: 'open' });
|
|
98
|
+
},
|
|
99
|
+
update: (domNode, newContent) => {
|
|
100
|
+
// Reconcile the provided content into the shadow root.
|
|
101
|
+
reconcile(domNode.shadowRoot, newContent);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// 2. Define a standard Web Component class.
|
|
106
|
+
class MyComponent extends HTMLElement {
|
|
107
|
+
connectedCallback() {
|
|
108
|
+
// When the element is added to the DOM, render the special component ONTO it.
|
|
109
|
+
// Pass some initial content into the special component's `update` hook.
|
|
110
|
+
reconcile(this, shadowComponent(p('Hello from inside a web component!')));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
disconnectedCallback() {
|
|
114
|
+
// When the element is removed, clean up the dodo instance.
|
|
115
|
+
reconcile(this, null);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// You can add attributeChangedCallback, properties, etc.
|
|
119
|
+
// to re-reconcile with new content.
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 3. Register the custom element.
|
|
123
|
+
customElements.define('my-component', MyComponent);
|
|
124
|
+
|
|
125
|
+
// 4. Use your new custom element with dodo!
|
|
126
|
+
const container = document.getElementById('my-component-container');
|
|
127
|
+
reconcile(container, h('my-component'));
|
|
128
|
+
```
|