@3sln/dodo 0.0.4 → 0.0.6

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 (6) hide show
  1. package/README.md +16 -106
  2. package/index.js +128 -7
  3. package/package.json +1 -1
  4. package/src/html.js +119 -117
  5. package/src/vdom.js +113 -56
  6. package/dodo.js +0 -716
package/README.md CHANGED
@@ -1,12 +1,26 @@
1
- # dodo
1
+ # Dodo
2
2
 
3
3
  > [!WARNING]
4
4
  > This is a work-in-progress project and is not yet ready for production use.
5
5
 
6
6
  A minimal, configurable virtual DOM library.
7
7
 
8
+ ## Documentation
9
+
10
+ For detailed documentation, live demos, and advanced usage examples, please check out the [Dodo Deck](https://dodo.3sln.com).
11
+
8
12
  ## Quick Start
9
13
 
14
+ Installation:
15
+ ```shell
16
+ npm install @3sln/dodo
17
+ # or
18
+ bun add @3sln/dodo
19
+ # or
20
+ yarn add @3sln/dodo
21
+ ```
22
+
23
+ Basic usage:
10
24
  ```javascript
11
25
  import { reconcile, h1, p, div } from '@3sln/dodo';
12
26
 
@@ -16,113 +30,9 @@ const container = document.getElementById('root');
16
30
  // The props object is optional.
17
31
  const myVdom = div({ id: 'app' },
18
32
  h1('Hello, dodo!'),
19
- p({ $classes: ['text-lg'] }, 'This is a paragraph.')
33
+ p('This is a paragraph.')
20
34
  );
21
35
 
22
36
  // Reconcile the virtual DOM with the real DOM.
23
37
  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
38
  ```
package/index.js CHANGED
@@ -1,10 +1,131 @@
1
- import vdom from './src/vdom.js';
1
+ import vdomFactory from './src/vdom.js';
2
+ import htmlFactory from './src/html.js';
3
+ import * as scheduler from './src/scheduler.js';
2
4
 
3
- // Create a default API instance by calling the factory with no settings.
4
- const {h, alias, special, reconcile, settings} = vdom();
5
+ function dodoFactory(userSettings) {
6
+ const vdomInstance = vdomFactory(userSettings);
7
+ const htmlInstance = htmlFactory(vdomInstance);
8
+ return {
9
+ ...vdomInstance,
10
+ ...htmlInstance,
11
+ ...scheduler,
12
+ };
13
+ }
5
14
 
6
- export {vdom, h, alias, special, reconcile, settings};
15
+ const defaultDodo = dodoFactory();
7
16
 
8
- // Export all the HTML helpers.
9
- export * from './src/html.js';
10
- export {schedule, flush, clear} from './src/scheduler.js';
17
+ export {vdomFactory as vdom, htmlFactory as html, dodoFactory as dodo};
18
+
19
+ export const {
20
+ h,
21
+ alias,
22
+ special,
23
+ reconcile,
24
+ settings,
25
+ title,
26
+ meta,
27
+ link,
28
+ style,
29
+ script,
30
+ noscript,
31
+ address,
32
+ article,
33
+ aside,
34
+ footer,
35
+ header,
36
+ h1,
37
+ h2,
38
+ h3,
39
+ h4,
40
+ h5,
41
+ h6,
42
+ main,
43
+ nav,
44
+ section,
45
+ blockquote,
46
+ dd,
47
+ div,
48
+ dl,
49
+ dt,
50
+ figcaption,
51
+ figure,
52
+ hr,
53
+ li,
54
+ ol,
55
+ p,
56
+ pre,
57
+ ul,
58
+ a,
59
+ abbr,
60
+ b,
61
+ br,
62
+ cite,
63
+ code,
64
+ data,
65
+ del,
66
+ dfn,
67
+ em,
68
+ i,
69
+ ins,
70
+ kbd,
71
+ mark,
72
+ q,
73
+ s,
74
+ samp,
75
+ small,
76
+ span,
77
+ strong,
78
+ sub,
79
+ sup,
80
+ time,
81
+ u,
82
+ wbr,
83
+ area,
84
+ audio,
85
+ img,
86
+ map,
87
+ track,
88
+ video,
89
+ embed,
90
+ iframe,
91
+ object,
92
+ param,
93
+ picture,
94
+ portal,
95
+ source,
96
+ svg,
97
+ math,
98
+ canvas,
99
+ caption,
100
+ col,
101
+ colgroup,
102
+ table,
103
+ tbody,
104
+ td,
105
+ tfoot,
106
+ th,
107
+ thead,
108
+ tr,
109
+ button,
110
+ datalist,
111
+ fieldset,
112
+ form,
113
+ input,
114
+ label,
115
+ legend,
116
+ meter,
117
+ optgroup,
118
+ option,
119
+ output,
120
+ progress,
121
+ select,
122
+ textarea,
123
+ details,
124
+ dialog,
125
+ summary,
126
+ slot,
127
+ template,
128
+ schedule,
129
+ flush,
130
+ clear,
131
+ } = defaultDodo;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3sln/dodo",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "A minimal, configurable virtual DOM library.",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/src/html.js CHANGED
@@ -1,128 +1,130 @@
1
- import {h} from '../index.js';
1
+ export default function htmlFactory({h}) {
2
+ return {
3
+ // Document metadata
4
+ title: (...args) => h('title', ...args),
5
+ meta: props => h('meta', props),
6
+ link: props => h('link', props),
7
+ style: (...args) => h('style', ...args),
8
+ script: (...args) => h('script', ...args),
9
+ noscript: (...args) => h('noscript', ...args),
2
10
 
3
- // Document metadata
4
- export const title = (...args) => h('title', ...args);
5
- export const meta = props => h('meta', props);
6
- export const link = props => h('link', props);
7
- export const style = (...args) => h('style', ...args);
8
- export const script = (...args) => h('script', ...args);
9
- export const noscript = (...args) => h('noscript', ...args);
11
+ // Content sectioning
12
+ address: (...args) => h('address', ...args),
13
+ article: (...args) => h('article', ...args),
14
+ aside: (...args) => h('aside', ...args),
15
+ footer: (...args) => h('footer', ...args),
16
+ header: (...args) => h('header', ...args),
17
+ h1: (...args) => h('h1', ...args),
18
+ h2: (...args) => h('h2', ...args),
19
+ h3: (...args) => h('h3', ...args),
20
+ h4: (...args) => h('h4', ...args),
21
+ h5: (...args) => h('h5', ...args),
22
+ h6: (...args) => h('h6', ...args),
23
+ main: (...args) => h('main', ...args),
24
+ nav: (...args) => h('nav', ...args),
25
+ section: (...args) => h('section', ...args),
10
26
 
11
- // Content sectioning
12
- export const address = (...args) => h('address', ...args);
13
- export const article = (...args) => h('article', ...args);
14
- export const aside = (...args) => h('aside', ...args);
15
- export const footer = (...args) => h('footer', ...args);
16
- export const header = (...args) => h('header', ...args);
17
- export const h1 = (...args) => h('h1', ...args);
18
- export const h2 = (...args) => h('h2', ...args);
19
- export const h3 = (...args) => h('h3', ...args);
20
- export const h4 = (...args) => h('h4', ...args);
21
- export const h5 = (...args) => h('h5', ...args);
22
- export const h6 = (...args) => h('h6', ...args);
23
- export const main = (...args) => h('main', ...args);
24
- export const nav = (...args) => h('nav', ...args);
25
- export const section = (...args) => h('section', ...args);
27
+ // Text content
28
+ blockquote: (...args) => h('blockquote', ...args),
29
+ dd: (...args) => h('dd', ...args),
30
+ div: (...args) => h('div', ...args),
31
+ dl: (...args) => h('dl', ...args),
32
+ dt: (...args) => h('dt', ...args),
33
+ figcaption: (...args) => h('figcaption', ...args),
34
+ figure: (...args) => h('figure', ...args),
35
+ hr: () => h('hr'),
36
+ li: (...args) => h('li', ...args),
37
+ ol: (...args) => h('ol', ...args),
38
+ p: (...args) => h('p', ...args),
39
+ pre: (...args) => h('pre', ...args),
40
+ ul: (...args) => h('ul', ...args),
26
41
 
27
- // Text content
28
- export const blockquote = (...args) => h('blockquote', ...args);
29
- export const dd = (...args) => h('dd', ...args);
30
- export const div = (...args) => h('div', ...args);
31
- export const dl = (...args) => h('dl', ...args);
32
- export const dt = (...args) => h('dt', ...args);
33
- export const figcaption = (...args) => h('figcaption', ...args);
34
- export const figure = (...args) => h('figure', ...args);
35
- export const hr = () => h('hr');
36
- export const li = (...args) => h('li', ...args);
37
- export const ol = (...args) => h('ol', ...args);
38
- export const p = (...args) => h('p', ...args);
39
- export const pre = (...args) => h('pre', ...args);
40
- export const ul = (...args) => h('ul', ...args);
42
+ // Inline text semantics
43
+ a: (...args) => h('a', ...args),
44
+ abbr: (...args) => h('abbr', ...args),
45
+ b: (...args) => h('b', ...args),
46
+ br: () => h('br'),
47
+ cite: (...args) => h('cite', ...args),
48
+ code: (...args) => h('code', ...args),
49
+ data: (...args) => h('data', ...args),
50
+ del: (...args) => h('del', ...args),
51
+ dfn: (...args) => h('dfn', ...args),
52
+ em: (...args) => h('em', ...args),
53
+ i: (...args) => h('i', ...args),
54
+ ins: (...args) => h('ins', ...args),
55
+ kbd: (...args) => h('kbd', ...args),
56
+ mark: (...args) => h('mark', ...args),
57
+ q: (...args) => h('q', ...args),
58
+ s: (...args) => h('s', ...args),
59
+ samp: (...args) => h('samp', ...args),
60
+ small: (...args) => h('small', ...args),
61
+ span: (...args) => h('span', ...args),
62
+ strong: (...args) => h('strong', ...args),
63
+ sub: (...args) => h('sub', ...args),
64
+ sup: (...args) => h('sup', ...args),
65
+ time: (...args) => h('time', ...args),
66
+ u: (...args) => h('u', ...args),
67
+ wbr: () => h('wbr'),
41
68
 
42
- // Inline text semantics
43
- export const a = (...args) => h('a', ...args);
44
- export const abbr = (...args) => h('abbr', ...args);
45
- export const b = (...args) => h('b', ...args);
46
- export const br = () => h('br');
47
- export const cite = (...args) => h('cite', ...args);
48
- export const code = (...args) => h('code', ...args);
49
- export const data = (...args) => h('data', ...args);
50
- export const del = (...args) => h('del', ...args);
51
- export const dfn = (...args) => h('dfn', ...args);
52
- export const em = (...args) => h('em', ...args);
53
- export const i = (...args) => h('i', ...args);
54
- export const ins = (...args) => h('ins', ...args);
55
- export const kbd = (...args) => h('kbd', ...args);
56
- export const mark = (...args) => h('mark', ...args);
57
- export const q = (...args) => h('q', ...args);
58
- export const s = (...args) => h('s', ...args);
59
- export const samp = (...args) => h('samp', ...args);
60
- export const small = (...args) => h('small', ...args);
61
- export const span = (...args) => h('span', ...args);
62
- export const strong = (...args) => h('strong', ...args);
63
- export const sub = (...args) => h('sub', ...args);
64
- export const sup = (...args) => h('sup', ...args);
65
- export const time = (...args) => h('time', ...args);
66
- export const u = (...args) => h('u', ...args);
67
- export const wbr = () => h('wbr');
69
+ // Image and multimedia
70
+ area: props => h('area', props),
71
+ audio: (...args) => h('audio', ...args),
72
+ img: props => h('img', props),
73
+ map: (...args) => h('map', ...args),
74
+ track: props => h('track', props),
75
+ video: (...args) => h('video', ...args),
68
76
 
69
- // Image and multimedia
70
- export const area = props => h('area', props);
71
- export const audio = (...args) => h('audio', ...args);
72
- export const img = props => h('img', props);
73
- export const map = (...args) => h('map', ...args);
74
- export const track = props => h('track', props);
75
- export const video = (...args) => h('video', ...args);
77
+ // Embedded content
78
+ embed: props => h('embed', props),
79
+ iframe: (...args) => h('iframe', ...args),
80
+ object: (...args) => h('object', ...args),
81
+ param: props => h('param', props),
82
+ picture: (...args) => h('picture', ...args),
83
+ portal: (...args) => h('portal', ...args),
84
+ source: props => h('source', props),
76
85
 
77
- // Embedded content
78
- export const embed = props => h('embed', props);
79
- export const iframe = (...args) => h('iframe', ...args);
80
- export const object = (...args) => h('object', ...args);
81
- export const param = props => h('param', props);
82
- export const picture = (...args) => h('picture', ...args);
83
- export const portal = (...args) => h('portal', ...args);
84
- export const source = props => h('source', props);
86
+ // SVG and MathML
87
+ svg: (...args) => h('svg', ...args),
88
+ math: (...args) => h('math', ...args),
85
89
 
86
- // SVG and MathML
87
- export const svg = (...args) => h('svg', ...args);
88
- export const math = (...args) => h('math', ...args);
90
+ // Scripting
91
+ canvas: (...args) => h('canvas', ...args),
89
92
 
90
- // Scripting
91
- export const canvas = (...args) => h('canvas', ...args);
93
+ // Table content
94
+ caption: (...args) => h('caption', ...args),
95
+ col: props => h('col', props),
96
+ colgroup: (...args) => h('colgroup', ...args),
97
+ table: (...args) => h('table', ...args),
98
+ tbody: (...args) => h('tbody', ...args),
99
+ td: (...args) => h('td', ...args),
100
+ tfoot: (...args) => h('tfoot', ...args),
101
+ th: (...args) => h('th', ...args),
102
+ thead: (...args) => h('thead', ...args),
103
+ tr: (...args) => h('tr', ...args),
92
104
 
93
- // Table content
94
- export const caption = (...args) => h('caption', ...args);
95
- export const col = props => h('col', props);
96
- export const colgroup = (...args) => h('colgroup', ...args);
97
- export const table = (...args) => h('table', ...args);
98
- export const tbody = (...args) => h('tbody', ...args);
99
- export const td = (...args) => h('td', ...args);
100
- export const tfoot = (...args) => h('tfoot', ...args);
101
- export const th = (...args) => h('th', ...args);
102
- export const thead = (...args) => h('thead', ...args);
103
- export const tr = (...args) => h('tr', ...args);
105
+ // Forms
106
+ button: (...args) => h('button', ...args),
107
+ datalist: (...args) => h('datalist', ...args),
108
+ fieldset: (...args) => h('fieldset', ...args),
109
+ form: (...args) => h('form', ...args),
110
+ input: props => h('input', props),
111
+ label: (...args) => h('label', ...args),
112
+ legend: (...args) => h('legend', ...args),
113
+ meter: (...args) => h('meter', ...args),
114
+ optgroup: (...args) => h('optgroup', ...args),
115
+ option: (...args) => h('option', ...args),
116
+ output: (...args) => h('output', ...args),
117
+ progress: (...args) => h('progress', ...args),
118
+ select: (...args) => h('select', ...args),
119
+ textarea: (...args) => h('textarea', ...args),
104
120
 
105
- // Forms
106
- export const button = (...args) => h('button', ...args);
107
- export const datalist = (...args) => h('datalist', ...args);
108
- export const fieldset = (...args) => h('fieldset', ...args);
109
- export const form = (...args) => h('form', ...args);
110
- export const input = props => h('input', props);
111
- export const label = (...args) => h('label', ...args);
112
- export const legend = (...args) => h('legend', ...args);
113
- export const meter = (...args) => h('meter', ...args);
114
- export const optgroup = (...args) => h('optgroup', ...args);
115
- export const option = (...args) => h('option', ...args);
116
- export const output = (...args) => h('output', ...args);
117
- export const progress = (...args) => h('progress', ...args);
118
- export const select = (...args) => h('select', ...args);
119
- export const textarea = (...args) => h('textarea', ...args);
121
+ // Interactive elements
122
+ details: (...args) => h('details', ...args),
123
+ dialog: (...args) => h('dialog', ...args),
124
+ summary: (...args) => h('summary', ...args),
120
125
 
121
- // Interactive elements
122
- export const details = (...args) => h('details', ...args);
123
- export const dialog = (...args) => h('dialog', ...args);
124
- export const summary = (...args) => h('summary', ...args);
125
-
126
- // Web Components
127
- export const slot = (...args) => h('slot', ...args);
128
- export const template = (...args) => h('template', ...args);
126
+ // Web Components
127
+ slot: (...args) => h('slot', ...args),
128
+ template: (...args) => h('template', ...args),
129
+ };
130
+ }