@manyducks.co/dolla 2.0.0-alpha.41 → 2.0.0-alpha.42

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/README.md CHANGED
@@ -40,22 +40,87 @@ import Dolla, { atom, html } from "@manyducks.co/dolla";
40
40
  function Counter() {
41
41
  const count = atom(0);
42
42
 
43
+ this.effect(() => {
44
+ console.log(`Count is: ${count.get()}`);
45
+ });
46
+
47
+ function increment() {
48
+ count.set(count.get() + 1);
49
+ }
50
+
51
+ function decrement() {
52
+ // alternative to `set(get() - 1)`
53
+ count.update((value) => value - 1);
54
+ }
55
+
43
56
  return html`
44
57
  <div>
45
58
  <p>Counter: ${count}</p>
46
59
  <div>
47
- <button onclick=${() => count.value--}>-1</button>
48
- <button onclick=${() => count.value++}>+1</button>
60
+ <button onclick=${increment}>+1</button>
61
+ <button onclick=${decrement}>-1</button>
49
62
  </div>
50
63
  </div>
51
64
  `;
52
- });
65
+ }
53
66
 
54
67
  Dolla.mount(document.body, Counter);
55
68
  ```
56
69
 
57
70
  > TODO: Show small examples for routing and stores.
58
71
 
72
+ ```js
73
+ function MessageStore() {
74
+ const message = atom("message", "Hello world!");
75
+
76
+ this.effect(() => {
77
+ this.log(`Message is now: ${get(message)}`);
78
+ // Calling `get()` inside an effect (or compose) function will track that reactive value as a dependency.
79
+ // Effects will re-run when a dependency updates.
80
+ });
81
+ // `this` refers to the context object; StoreContext in a store and ViewContext in a view.
82
+ // Context objects contain methods for controlling the component, logging and attaching lifecycle hooks.
83
+
84
+ return {
85
+ message: compose("message:readonly", () => get(message)),
86
+ // Creates a read-only reactive of the message value.
87
+ // Composed values update when their dependencies update.
88
+
89
+ setMessage: set(message),
90
+ // Creates a setter function to update the original message atom.
91
+ };
92
+ }
93
+
94
+ function App() {
95
+ const { message, setMessage } = this.provide(MessageStore);
96
+ // Provides a MessageStore on this context and any child contexts.
97
+ // When a store is provided its value is returned right away.
98
+
99
+ return html`
100
+ <div>
101
+ <${MessageView} />
102
+ <${MessageView} />
103
+ <${MessageView} />
104
+
105
+ <input
106
+ type="text"
107
+ value=${message}
108
+ oninput=${(e) => {
109
+ setMessage(e.currentTarget.value);
110
+ }}
111
+ />
112
+ </div>
113
+ `;
114
+ }
115
+
116
+ function MessageView() {
117
+ const { message } = this.get(MessageStore);
118
+ // Gets the nearest instance of MessageStore. In this case the one provided at the parent.
119
+
120
+ return html`<span>${message}</span>`;
121
+ }
122
+ ```
123
+
59
124
  For more detail [check out the Docs](./docs/index.md).
60
125
 
61
126
  ---
@@ -24,7 +24,7 @@ export interface Markup {
24
24
  * A DOM node that has been constructed from a Markup object.
25
25
  */
26
26
  export interface MarkupElement {
27
- readonly node?: Node;
27
+ readonly domNode?: Node;
28
28
  readonly isMounted: boolean;
29
29
  mount(parent: Node, after?: Node): void;
30
30
  /**
@@ -5,7 +5,7 @@ import { IS_MARKUP_ELEMENT } from "../symbols";
5
5
  */
6
6
  export declare class DOMNode implements MarkupElement {
7
7
  [IS_MARKUP_ELEMENT]: boolean;
8
- node: Node;
8
+ domNode: Node;
9
9
  get isMounted(): boolean;
10
10
  constructor(node: Node);
11
11
  mount(parent: Node, after?: Node): void;
@@ -13,7 +13,7 @@ interface DynamicOptions {
13
13
  */
14
14
  export declare class Dynamic implements MarkupElement {
15
15
  [IS_MARKUP_ELEMENT]: boolean;
16
- node: Text;
16
+ domNode: Text;
17
17
  private children;
18
18
  private elementContext;
19
19
  private source;
@@ -9,7 +9,7 @@ type HTMLOptions = {
9
9
  };
10
10
  export declare class HTML implements MarkupElement {
11
11
  [IS_MARKUP_ELEMENT]: boolean;
12
- node: HTMLElement | SVGElement;
12
+ domNode: HTMLElement | SVGElement;
13
13
  private props;
14
14
  private childMarkup;
15
15
  private children;
@@ -11,7 +11,7 @@ interface ListOptions<T> {
11
11
  }
12
12
  export declare class List<T> implements MarkupElement {
13
13
  [IS_MARKUP_ELEMENT]: boolean;
14
- node: Text;
14
+ domNode: Text;
15
15
  private items;
16
16
  private unsubscribe;
17
17
  private connectedItems;
@@ -6,7 +6,7 @@ import { IS_MARKUP_ELEMENT } from "../symbols.js";
6
6
  */
7
7
  export declare class Outlet implements MarkupElement {
8
8
  [IS_MARKUP_ELEMENT]: boolean;
9
- node: Text;
9
+ domNode: Text;
10
10
  isMounted: boolean;
11
11
  private source;
12
12
  private elements;
@@ -69,7 +69,7 @@ export declare class View<P> implements ViewElement {
69
69
  unmount: (() => any)[];
70
70
  };
71
71
  constructor(elementContext: ElementContext, fn: ViewFunction<P>, props: P, children?: Markup[]);
72
- get node(): Node;
72
+ get domNode(): Node;
73
73
  isMounted: boolean;
74
74
  mount(parent: Node, after?: Node): void;
75
75
  unmount(parentIsUnmounting?: boolean): void;
@@ -13,8 +13,21 @@ export interface Signal<T = any> extends Dependency {
13
13
  * A readable reactive state object.
14
14
  */
15
15
  export interface Reactive<T> {
16
+ /**
17
+ * A name provided at the creation of this reactive.
18
+ */
19
+ readonly name?: string;
20
+ /**
21
+ * Returns the current value. Tracks this reactive as a dependency if called within `effect` or `compose`.
22
+ */
23
+ get(): T;
24
+ /**
25
+ * Returns the current value without tracking this reactive as a dependency when called within `effect` or `compose`.
26
+ */
27
+ peek(): T;
16
28
  /**
17
29
  * The current value.
30
+ * @deprecated use `get()` and `set()`
18
31
  */
19
32
  readonly value: T;
20
33
  }
@@ -38,9 +51,64 @@ export interface ReactiveOptions<T> {
38
51
  }
39
52
  export declare class Atom<T> implements Reactive<T> {
40
53
  #private;
41
- name?: string;
42
- constructor(signal: Signal<T>, options?: ReactiveOptions<T>);
54
+ readonly name?: string;
55
+ constructor(value: T, options?: ReactiveOptions<T>);
56
+ /**
57
+ * Returns the latest value. The signal is tracked as a dependency if called within `effect` or `compose`.
58
+ */
59
+ get(): T;
60
+ /**
61
+ * Returns the latest value. The signal is NOT tracked if called within `effect` or `compose`.
62
+ */
63
+ peek(): T;
64
+ /**
65
+ * Replaces the current value with `next`.
66
+ *
67
+ * @example
68
+ * const count = atom(0);
69
+ * count.set(2);
70
+ * count.set(count.get() + 1);
71
+ */
72
+ set(next: T): void;
73
+ /**
74
+ * Passes the current value to `fn` and sets the return value as the next value.
75
+ *
76
+ * @example
77
+ * const count = atom(0);
78
+ * count.update((current) => current + 1);
79
+ * count.update((current) => current * 5);
80
+ *
81
+ * // Also works very well with Immer `produce` for complex objects.
82
+ * const items = atom([{ name: "Alice", age: 26 }, { name: "Bob", age: 33 }]);
83
+ *
84
+ * // Without Immer:
85
+ * items.update((current) => {
86
+ * // Return a new array with Bob's age increased by 1.
87
+ * const newItems = [...current];
88
+ * newItems[1] = {
89
+ * ...newItems[1],
90
+ * age: newItems[1].age + 1
91
+ * };
92
+ * return newItems;
93
+ * });
94
+ *
95
+ * // With Immer:
96
+ * import { produce } from "immer";
97
+ *
98
+ * items.update(produce((draft) => {
99
+ * // Mutate draft to increase Bob's age by 1.
100
+ * // Results in a new object with this patch applied.
101
+ * draft[1].age++;
102
+ * }));
103
+ */
104
+ update(fn: (current: T) => T): void;
105
+ /**
106
+ * @deprecated use `get()`
107
+ */
43
108
  get value(): T;
109
+ /**
110
+ * @deprecated use `set()`
111
+ */
44
112
  set value(next: T);
45
113
  }
46
114
  /**
@@ -52,9 +120,9 @@ export declare function isReactive<T>(value: any): value is Reactive<T>;
52
120
  * Atom values can be read and updated with the `value` property
53
121
  *
54
122
  * @example
55
- * const count = atom(1);
56
- * count.value++;
57
- * count.value; // 2
123
+ * const count = atom<number>();
124
+ * count.set(5);
125
+ * count.get(); // 5
58
126
  */
59
127
  export declare function atom<T>(): Atom<T | undefined>;
60
128
  /**
@@ -63,8 +131,8 @@ export declare function atom<T>(): Atom<T | undefined>;
63
131
  *
64
132
  * @example
65
133
  * const count = atom(1);
66
- * count.value++;
67
- * count.value; // 2
134
+ * count.set(count.get() + 1);
135
+ * count.get(); // 2
68
136
  */
69
137
  export declare function atom<T>(value: T, options?: ReactiveOptions<T>): Atom<T>;
70
138
  /**
@@ -83,7 +151,7 @@ type ComposeCallback<T> = (previousValue?: T) => MaybeReactive<T>;
83
151
  *
84
152
  * @example
85
153
  * const count = atom(1);
86
- * const doubled = compose(() => get(count) * 2);
154
+ * const doubled = compose(() => count.get() * 2);
87
155
  */
88
156
  export declare function compose<T>(fn: ComposeCallback<T>, options?: ReactiveOptions<T>): Reactive<T>;
89
157
  /**
@@ -115,6 +183,10 @@ export declare function get<T>(value: MaybeReactive<T>): T;
115
183
  * });
116
184
  */
117
185
  export declare function peek<T>(value: MaybeReactive<T>): T;
186
+ /**
187
+ * Registers a callback that will receive a list of dependencies that were tracked within the scope this function was called in.
188
+ */
189
+ export declare function getTracked(fn: (tracked: Reactive<unknown>[]) => void): void;
118
190
  export type EffectCallback = () => void;
119
191
  /**
120
192
  * Creates a tracked scope that re-runs whenever the values of any tracked reactives changes.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { atom, compose, effect, get, set, peek } from "./core/signals.js";
1
+ export { atom, compose, effect, get, set, peek, getTracked } from "./core/signals.js";
2
2
  export type { Reactive, MaybeReactive, Atom } from "./core/signals.js";
3
3
  export { deepEqual, shallowEqual, strictEqual } from "./utils.js";
4
4
  export { ref, type Ref } from "./core/ref.js";