@manyducks.co/dolla 2.0.0-alpha.6 → 2.0.0-alpha.61

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 (118) hide show
  1. package/README.md +86 -591
  2. package/dist/core/context.d.ts +142 -0
  3. package/dist/core/env.d.ts +3 -0
  4. package/dist/core/index.d.ts +21 -0
  5. package/dist/core/logger.d.ts +42 -0
  6. package/dist/core/logger.test.d.ts +0 -0
  7. package/dist/core/markup.d.ts +104 -0
  8. package/dist/core/markup.test.d.ts +0 -0
  9. package/dist/core/mount.d.ts +15 -0
  10. package/dist/core/mount.test.d.ts +0 -0
  11. package/dist/core/nodes/_markup.d.ts +36 -0
  12. package/dist/core/nodes/dom.d.ts +13 -0
  13. package/dist/core/nodes/dynamic.d.ts +22 -0
  14. package/dist/core/nodes/element.d.ts +25 -0
  15. package/dist/core/nodes/portal.d.ts +18 -0
  16. package/dist/core/nodes/repeat.d.ts +27 -0
  17. package/dist/core/nodes/view.d.ts +25 -0
  18. package/dist/core/ref.d.ts +18 -0
  19. package/dist/core/ref.test.d.ts +1 -0
  20. package/dist/core/signals.d.ts +58 -0
  21. package/dist/core/signals.test.d.ts +1 -0
  22. package/dist/{views → core/views}/default-crash-view.d.ts +11 -4
  23. package/dist/core/views/fragment.d.ts +7 -0
  24. package/dist/fragment-BahD_BJA.js +7 -0
  25. package/dist/fragment-BahD_BJA.js.map +1 -0
  26. package/dist/hooks/index.d.ts +64 -0
  27. package/dist/hooks/index.test.d.ts +1 -0
  28. package/dist/hooks.js +69 -0
  29. package/dist/hooks.js.map +1 -0
  30. package/dist/{modules/http.d.ts → http/index.d.ts} +3 -5
  31. package/dist/http.js +163 -0
  32. package/dist/http.js.map +1 -0
  33. package/dist/i18n/index.d.ts +134 -0
  34. package/dist/i18n.js +318 -0
  35. package/dist/i18n.js.map +1 -0
  36. package/dist/index.js +98 -1388
  37. package/dist/index.js.map +1 -1
  38. package/dist/jsx-dev-runtime.d.ts +3 -2
  39. package/dist/jsx-dev-runtime.js +5 -12
  40. package/dist/jsx-dev-runtime.js.map +1 -1
  41. package/dist/jsx-runtime.d.ts +4 -3
  42. package/dist/jsx-runtime.js +9 -15
  43. package/dist/jsx-runtime.js.map +1 -1
  44. package/dist/logger-Bl496yfY.js +91 -0
  45. package/dist/logger-Bl496yfY.js.map +1 -0
  46. package/dist/markup-CX27GJ1M.js +1030 -0
  47. package/dist/markup-CX27GJ1M.js.map +1 -0
  48. package/dist/ref-BD79iqlg.js +15 -0
  49. package/dist/ref-BD79iqlg.js.map +1 -0
  50. package/dist/router/index.d.ts +2 -0
  51. package/dist/router/router.d.ts +160 -0
  52. package/dist/{routing.d.ts → router/router.utils.d.ts} +17 -3
  53. package/dist/router/router.utils.test.d.ts +1 -0
  54. package/dist/router-CjCkk4dA.js +543 -0
  55. package/dist/router-CjCkk4dA.js.map +1 -0
  56. package/dist/router.js +8 -0
  57. package/dist/router.js.map +1 -0
  58. package/dist/signals-gCwiIe5X.js +450 -0
  59. package/dist/signals-gCwiIe5X.js.map +1 -0
  60. package/dist/typeChecking-CbltMOUt.js +71 -0
  61. package/dist/typeChecking-CbltMOUt.js.map +1 -0
  62. package/dist/typeChecking.d.ts +2 -98
  63. package/dist/typeChecking.test.d.ts +1 -0
  64. package/dist/types.d.ts +98 -25
  65. package/dist/utils.d.ts +20 -3
  66. package/docs/hooks.md +211 -0
  67. package/docs/http.md +29 -0
  68. package/docs/i18n.md +43 -0
  69. package/docs/index.md +10 -0
  70. package/docs/markup.md +16 -0
  71. package/docs/mixins.md +32 -0
  72. package/docs/ref.md +93 -0
  73. package/docs/router.md +80 -0
  74. package/docs/setup.md +31 -0
  75. package/docs/signals.md +166 -0
  76. package/docs/state.md +141 -0
  77. package/docs/stores.md +62 -0
  78. package/docs/views.md +208 -0
  79. package/examples/webcomponent/index.html +14 -0
  80. package/examples/webcomponent/main.js +165 -0
  81. package/index.d.ts +2 -2
  82. package/notes/TODO.md +6 -0
  83. package/notes/atomic.md +452 -0
  84. package/notes/context-routes.md +61 -0
  85. package/notes/custom-nodes.md +17 -0
  86. package/notes/effection-idea.md +34 -0
  87. package/notes/elimination.md +33 -0
  88. package/notes/mixins.md +22 -0
  89. package/notes/molecule.md +35 -0
  90. package/notes/observable.md +180 -0
  91. package/notes/readme-scratch.md +45 -7
  92. package/notes/route-middleware.md +42 -0
  93. package/notes/scratch.md +353 -6
  94. package/notes/splitting.md +5 -0
  95. package/notes/stores.md +79 -0
  96. package/package.json +31 -12
  97. package/vite.config.js +6 -11
  98. package/build.js +0 -34
  99. package/dist/index.d.ts +0 -21
  100. package/dist/markup.d.ts +0 -100
  101. package/dist/modules/dolla.d.ts +0 -111
  102. package/dist/modules/language.d.ts +0 -41
  103. package/dist/modules/render.d.ts +0 -17
  104. package/dist/modules/router.d.ts +0 -152
  105. package/dist/nodes/cond.d.ts +0 -26
  106. package/dist/nodes/html.d.ts +0 -31
  107. package/dist/nodes/observer.d.ts +0 -29
  108. package/dist/nodes/outlet.d.ts +0 -22
  109. package/dist/nodes/portal.d.ts +0 -19
  110. package/dist/nodes/repeat.d.ts +0 -34
  111. package/dist/nodes/text.d.ts +0 -19
  112. package/dist/passthrough-CW8Ezjg-.js +0 -1244
  113. package/dist/passthrough-CW8Ezjg-.js.map +0 -1
  114. package/dist/state.d.ts +0 -101
  115. package/dist/view.d.ts +0 -50
  116. package/dist/views/passthrough.d.ts +0 -5
  117. package/tests/state.test.js +0 -135
  118. /package/dist/{routing.test.d.ts → core/context.test.d.ts} +0 -0
package/docs/views.md ADDED
@@ -0,0 +1,208 @@
1
+ # Views
2
+
3
+ Views are one of two component types in Dolla. We call them views because they deal specifically with presenting visible things to the user. The other type of component, [Stores](./stores.md), deal with data and events.
4
+
5
+ At its most basic, a view is a function that returns markup.
6
+
7
+ ```jsx
8
+ function ExampleView() {
9
+ return <h1>Hello World!</h1>;
10
+ }
11
+ ```
12
+
13
+ ## View Props
14
+
15
+ A view function takes a `props` object as its first argument. This object contains all properties passed to the view when it's invoked.
16
+
17
+ ```jsx
18
+ function ListItemView(props) {
19
+ return <li>{props.label}</li>;
20
+ }
21
+
22
+ function ListView() {
23
+ return (
24
+ <ul>
25
+ <ListItemView label="Squirrel" />
26
+ <ListItemView label="Chipmunk" />
27
+ <ListItemView label="Groundhog" />
28
+ </ul>
29
+ );
30
+ }
31
+ ```
32
+
33
+ As you may have guessed, you can pass States as props and slot them in in exactly the same way. This is important because Views do not re-render the way you might expect from other frameworks. Whatever you pass as props is what the View gets for its entire lifecycle.
34
+
35
+ ## View Helpers
36
+
37
+ ### `cond($condition, whenTruthy, whenFalsy)`
38
+
39
+ The `cond` helper does conditional rendering. When `$condition` is truthy, the second argument is rendered. When `$condition` is falsy the third argument is rendered. Either case can be left null or undefined if you don't want to render something for that condition.
40
+
41
+ ```jsx
42
+ function ConditionalListView(props) {
43
+ return (
44
+ <div>
45
+ {cond(
46
+ props.$show,
47
+
48
+ // Visible when truthy
49
+ <ul>
50
+ <ListItemView label="Squirrel" />
51
+ <ListItemView label="Chipmunk" />
52
+ <ListItemView label="Groundhog" />
53
+ </ul>,
54
+
55
+ // Visible when falsy
56
+ <span>List is hidden</span>,
57
+ )}
58
+ </div>
59
+ );
60
+ }
61
+ ```
62
+
63
+ ### `repeat($items, keyFn, renderFn)`
64
+
65
+ The `repeat` helper repeats a render function for each item in a list. The `keyFn` takes an item's value and returns a number, string or Symbol that uniquely identifies that list item. If `$items` changes or gets reordered, all rendered items with matching keys will be reused, those no longer in the list will be removed and those that didn't previously have a matching key are created.
66
+
67
+ ```jsx
68
+ function RepeatedListView() {
69
+ const [$items, setItems] = createState(["Squirrel", "Chipmunk", "Groundhog"]);
70
+
71
+ return (
72
+ <ul>
73
+ {repeat(
74
+ $items,
75
+ (item, index) => item, // Using the string itself as the key
76
+ ($item, $index, context) => {
77
+ return <ListItemView label={$item} />;
78
+ },
79
+ )}
80
+ </ul>
81
+ );
82
+ }
83
+ ```
84
+
85
+ ### `portal(content, parentNode)`
86
+
87
+ The `portal` helper displays DOM elements from a view as children of a parent element elsewhere in the document. Portals are typically used to display modals and other content that needs to appear at the top level of a document.
88
+
89
+ ```jsx
90
+ function PortalView() {
91
+ const content = (
92
+ <div class="modal">
93
+ <p>This is a modal.</p>
94
+ </div>
95
+ );
96
+
97
+ // Content will be appended to `document.body` while this view is connected.
98
+ return portal(document.body, content);
99
+ }
100
+ ```
101
+
102
+ ## View Context
103
+
104
+ A view function takes a context object as its second argument. The context provides a set of functions you can use to respond to lifecycle events, observe dynamic data, print debug messages and display child elements among other things.
105
+
106
+ ```jsx
107
+ function ExampleView(props, ctx) {
108
+ ctx.onMount(() => {
109
+ ctx.log("HELLO!");
110
+ });
111
+
112
+ return <h1>Hello World!</h1>;
113
+ }
114
+ ```
115
+
116
+ ### Printing Debug Messages
117
+
118
+ ```jsx
119
+ function ExampleView(props, ctx) {
120
+ // Set the name of this view's context. Console messages are prefixed with name.
121
+ ctx.name = "CustomName";
122
+
123
+ // Print messages to the console. These are suppressed by default in the app's "production" mode.
124
+ // You can also change which of these are printed and filter messages from certain contexts in the `createApp` options object.
125
+ ctx.info("Verbose debugging info that might be useful to know");
126
+ ctx.log("Standard messages");
127
+ ctx.warn("Something bad might be happening");
128
+ ctx.error("Uh oh!");
129
+
130
+ // If you encounter a bad enough situation, you can halt and disconnect the entire app.
131
+ ctx.crash(new Error("BOOM"));
132
+
133
+ return <h1>Hello World!</h1>;
134
+ }
135
+ ```
136
+
137
+ ### Lifecycle Events
138
+
139
+ ```jsx
140
+ function ExampleView(props, ctx) {
141
+ ctx.beforeMount(() => {
142
+ // Do something before this view's DOM nodes are created.
143
+ });
144
+
145
+ ctx.onMount(() => {
146
+ // Do something immediately after this view is connected to the DOM.
147
+ });
148
+
149
+ ctx.beforeUnmount(() => {
150
+ // Do something before removing this view from the DOM.
151
+ });
152
+
153
+ ctx.onUnmount(() => {
154
+ // Do some cleanup after this view is disconnected from the DOM.
155
+ });
156
+
157
+ return <h1>Hello World!</h1>;
158
+ }
159
+ ```
160
+
161
+ ### Displaying Children
162
+
163
+ The context has an `outlet` function that can be used to display children at a location of your choosing.
164
+
165
+ ```js
166
+ function LayoutView(props, ctx) {
167
+ return (
168
+ <div className="layout">
169
+ <div className="content">{ctx.outlet()}</div>
170
+ </div>
171
+ );
172
+ }
173
+
174
+ function ExampleView() {
175
+ // <h1> and <p> are displayed inside LayoutView's outlet.
176
+ return (
177
+ <LayoutView>
178
+ <h1>Hello</h1>
179
+ <p>This is inside the box.</p>
180
+ </LayoutView>
181
+ );
182
+ }
183
+ ```
184
+
185
+ ### Watching States
186
+
187
+ The `watch` function starts observing when the view is connected and stops when disconnected. This takes care of cleaning up watchers so you don't have to worry about memory leaks.
188
+
189
+ ```jsx
190
+ function ExampleView(props, ctx) {
191
+ const [$count, setCount] = createState(0);
192
+
193
+ // This callback will run when any states in the dependency array receive new values.
194
+ ctx.watch([$count], (count) => {
195
+ ctx.log("count is now", count);
196
+ });
197
+
198
+ // ...
199
+ }
200
+ ```
201
+
202
+ ---
203
+
204
+ End.
205
+
206
+ - [🗂️ Docs](./index.md)
207
+ - [🏠 README](../README.md)
208
+ - [🦆 That's a lot of ducks.](https://www.manyducks.co)
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <title>Web Component Example</title>
6
+ </head>
7
+
8
+ <body>
9
+ <my-counter></my-counter>
10
+
11
+ <script type="module" src="./main.js"></script>
12
+ </body>
13
+
14
+ </html>
@@ -0,0 +1,165 @@
1
+ import { $, effect, when } from "../../dist/index.js";
2
+
3
+ class EffectScope {
4
+ #observing = false;
5
+ #observers = [];
6
+ #cleanups = [];
7
+
8
+ get observing() {
9
+ return this.#observing;
10
+ }
11
+
12
+ observe(callback) {
13
+ this.#observers.push(callback);
14
+ if (this.#observing) {
15
+ this.#cleanups.push(effect(callback));
16
+ }
17
+ }
18
+
19
+ start() {
20
+ this.#observing = true;
21
+ for (const callback of this.#observers) {
22
+ this.#cleanups.push(effect(callback));
23
+ }
24
+ }
25
+
26
+ stop() {
27
+ this.#observing = false;
28
+ for (const cleanup of this.#cleanups) {
29
+ cleanup();
30
+ }
31
+ this.#cleanups.length = 0;
32
+ }
33
+ }
34
+
35
+ function setAttribute(el, attr, value) {
36
+ // handle special case attributes
37
+ el.setAttribute(attr, value);
38
+ }
39
+
40
+ function createElement(tag, attrs, children) {
41
+ // attrs can contain signals as values
42
+ // children can contain signals as values
43
+
44
+ // signals will be observed while the element is connected.
45
+
46
+ const el = document.createElement(tag);
47
+ const scope = new EffectScope();
48
+
49
+ for (const key in attrs) {
50
+ if (typeof attrs[key] === "function") {
51
+ // determine if it's an event handler or a signal
52
+ // this is determined by the name beginning with "on"
53
+ if (key.startsWith("on")) {
54
+ el.addEventListener(key.slice(2), attrs[key]);
55
+ } else {
56
+ // if signal then add an observer on the scope.
57
+ scope.observe(() => {
58
+ setAttribute(el, key, attrs[key]());
59
+ });
60
+ }
61
+ } else {
62
+ // attr is static
63
+ setAttribute(el, key, attrs[key]);
64
+ }
65
+ }
66
+
67
+ for (const child of children) {
68
+ if (typeof child === "function") {
69
+ // transform into a dynamic element
70
+ // for now just printing it as a string
71
+ const node = document.createTextNode(String(child()));
72
+ console.log("HELLO", node, child);
73
+ scope.observe(() => {
74
+ node.textContent = String(child());
75
+ });
76
+ el.appendChild(node);
77
+ } else if (child instanceof Node) {
78
+ el.appendChild(child);
79
+ } else {
80
+ el.appendChild(document.createTextNode(String(child)));
81
+ }
82
+ }
83
+
84
+ // run custom setup logic on connect and disconnect
85
+ el.$dolla = {
86
+ connected: false,
87
+ connect() {
88
+ if (this.connected) return;
89
+ console.log(el, "$dolla connected");
90
+ scope.start();
91
+
92
+ for (const child of el.childNodes) {
93
+ if ("$dolla" in child) {
94
+ child.$dolla.connect();
95
+ }
96
+ }
97
+ },
98
+ disconnect() {
99
+ if (!this.connected) return;
100
+ console.log(el, "$dolla disconnected");
101
+ scope.stop();
102
+
103
+ for (const child of el.childNodes) {
104
+ if ("$dolla" in child) {
105
+ child.$dolla.disconnect();
106
+ }
107
+ }
108
+ },
109
+ };
110
+
111
+ return el;
112
+ }
113
+
114
+ function mount(element, parent) {
115
+ // Observe childList changes on subtree and run lifecycle callbacks.
116
+ const observer = new MutationObserver((mutations) => {
117
+ for (const mutation of mutations) {
118
+ for (const node of mutation.addedNodes) {
119
+ if ("$dolla" in node) {
120
+ node.$dolla.connect();
121
+ }
122
+ }
123
+
124
+ for (const node of mutation.removedNodes) {
125
+ if ("$dolla" in node) {
126
+ node.$dolla.disconnect();
127
+ }
128
+ }
129
+ }
130
+ });
131
+ observer.observe(parent, { childList: true, subtree: true });
132
+
133
+ parent.appendChild(element);
134
+ }
135
+
136
+ // const html = htm.bind(createElement);
137
+
138
+ // need array of all event handler names
139
+
140
+ class DollaElement extends HTMLElement {
141
+ connectedCallback() {
142
+ if (this.template) {
143
+ mount(this.template, this);
144
+ }
145
+ }
146
+ }
147
+
148
+ class MyCounter extends DollaElement {
149
+ count = $(0);
150
+ increment = () => this.count((x) => x + 1);
151
+
152
+ template = createElement("div", {}, [
153
+ createElement("span", {}, ["Clicks: ", this.count]),
154
+ createElement("button", { onclick: this.increment }, ["Click Me!"]),
155
+ () => this.count() > 10 && createElement("span", {}, ["That's a lot of clicks."]),
156
+ ]);
157
+
158
+ // template = html`
159
+ // <span>Clicks: ${this.count}</span>
160
+ // <button onclick=${this.increment}>Click Me!</button>
161
+ // ${() => this.count() > 10 && html`<span>That's a lot!</span>`}
162
+ // `;
163
+ }
164
+
165
+ customElements.define("my-counter", MyCounter);
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export * from "./lib/index";
1
+ export * from "./src/core/index";
2
2
 
3
- import type { IntrinsicElements as Elements } from "./lib/core/types";
3
+ import type { IntrinsicElements as Elements } from "./src/types";
4
4
 
5
5
  declare global {
6
6
  namespace JSX {
package/notes/TODO.md ADDED
@@ -0,0 +1,6 @@
1
+ # TO DO LIST
2
+
3
+ - Combine/refactor very similar Group, Outlet and Observer nodes.
4
+ - Group is simplest and exists to mount an array of MarkupElements as one.
5
+ - Outlet is basically the same as Group but it expects a $children state with an array of MarkupElements.
6
+ - Observer is a generic catch-all that works with a set of states and a render function. The render function can return any kind of Renderable which is then converted into a MarkupElement, but very similar update logic outside of that. Observer uses Group internally.