@manyducks.co/dolla 2.0.0-alpha.27 → 2.0.0-alpha.29

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.
@@ -0,0 +1,88 @@
1
+ import type { Emitter } from "@manyducks.co/emitter";
2
+ import type { Store, StoreFactory } from "./store";
3
+ import type { Dolla } from "./dolla";
4
+ interface ContextEmitterEvents {
5
+ [eventName: string | symbol]: [ContextEvent<any>];
6
+ }
7
+ export interface ElementContext {
8
+ /**
9
+ * The root Dolla instance this element belongs to.
10
+ */
11
+ root: Dolla;
12
+ /**
13
+ * Storage for context variables.
14
+ */
15
+ data: Record<string | symbol, unknown>;
16
+ /**
17
+ * Event emitter for this context.
18
+ */
19
+ emitter: Emitter<ContextEmitterEvents>;
20
+ /**
21
+ * Stores attached to this context.
22
+ */
23
+ stores: Map<string, Store<any, any>>;
24
+ /**
25
+ * A reference to the parent context.
26
+ */
27
+ parent?: ElementContext;
28
+ /**
29
+ * Whether to create DOM nodes in the SVG namespace. An `<svg>` element will set this to true and pass it down to children.
30
+ */
31
+ isSVG?: boolean;
32
+ /**
33
+ * The name of the nearest parent view.
34
+ */
35
+ viewName?: string;
36
+ }
37
+ export interface ComponentContext {
38
+ /**
39
+ * Sets a context variable and returns its value.
40
+ */
41
+ set<T>(key: string | symbol, value: T): T;
42
+ /**
43
+ * Gets the value of a context variable. Returns null if the variable is not set.
44
+ */
45
+ get<T>(key: string | symbol): T | null;
46
+ /**
47
+ * Adds a listener to be called when `eventName` is emitted.
48
+ */
49
+ on<T = unknown>(eventName: string, listener: (event: ContextEvent<T>) => void): void;
50
+ /**
51
+ * Removes a listener from the list to be called when `eventName` is emitted.
52
+ */
53
+ off<T = unknown>(eventName: string, listener: (event: ContextEvent<T>) => void): void;
54
+ /**
55
+ * Adds a listener to be called when `eventName` is emitted. The listener is immediately removed after being called once.
56
+ */
57
+ once<T = unknown>(eventName: string, listener: (event: ContextEvent<T>) => void): void;
58
+ /**
59
+ * Emits a new event to all listeners.
60
+ */
61
+ emit<T = unknown>(eventName: string, detail: T): boolean;
62
+ }
63
+ /**
64
+ * A context capable of hosting stores.
65
+ */
66
+ export interface StorableContext extends ComponentContext {
67
+ /**
68
+ * Attaches a new store to this context.
69
+ */
70
+ attachStore(store: Store<any, any>): void;
71
+ /**
72
+ * Gets the closest instance of a store. Throws an error if the store isn't provided higher in the tree.
73
+ */
74
+ useStore<Value>(factory: StoreFactory<any, Value>): Value;
75
+ }
76
+ /**
77
+ * An event emitted from and received by a Dolla context. These are separate from DOM events.
78
+ */
79
+ export declare class ContextEvent<T> {
80
+ #private;
81
+ type: string;
82
+ detail: T;
83
+ get propagationStopped(): boolean;
84
+ constructor(type: string, detail: T);
85
+ stopPropagation(): void;
86
+ get [Symbol.toStringTag](): string;
87
+ }
88
+ export {};
@@ -1,12 +1,14 @@
1
+ import { HTTP } from "../modules/http.js";
2
+ import { I18n } from "../modules/i18n.js";
3
+ import { Router } from "../modules/router.js";
1
4
  import { type CrashViewProps } from "../views/default-crash-view.js";
2
5
  import { Batch } from "./batch.js";
6
+ import { ContextEvent, type StorableContext } from "./context.js";
3
7
  import { type Markup, type MarkupElement } from "./markup.js";
4
8
  import { type ViewElement, type ViewFunction } from "./nodes/view.js";
5
9
  import { createRef, isRef } from "./ref.js";
6
10
  import { createState, createWatcher, derive, isState, toState, toValue } from "./state.js";
7
- import { HTTP } from "../modules/http.js";
8
- import { I18n } from "../modules/i18n.js";
9
- import { Router } from "../modules/router.js";
11
+ import { type Store, type StoreFactory } from "./store.js";
10
12
  export type Environment = "development" | "production";
11
13
  /**
12
14
  * Log type toggles. Each message category can be turned on or off or enabled only in a specific environment.
@@ -40,11 +42,7 @@ export type LoggerOptions = {
40
42
  */
41
43
  uid?: string;
42
44
  };
43
- export interface DollaModuleConfig<Options = never> {
44
- root: Dolla;
45
- options: Options;
46
- }
47
- export declare class Dolla {
45
+ export declare class Dolla implements StorableContext {
48
46
  #private;
49
47
  readonly batch: Batch;
50
48
  private readonly stats;
@@ -88,9 +86,6 @@ export declare class Dolla {
88
86
  * Returns the top level view Dolla is rendering inside the root element. This will return undefined until Dolla.mount() is called.
89
87
  */
90
88
  getRootView(): ViewElement | undefined;
91
- /**
92
- * Registers a Dolla module.
93
- */
94
89
  /**
95
90
  * Sets a context variable and returns its value. Context variables are accessible on the app and in child views.
96
91
  */
@@ -102,7 +97,30 @@ export declare class Dolla {
102
97
  /**
103
98
  * Returns an object of all context variables stored at the app level.
104
99
  */
105
- getAll(): Record<string | symbol, unknown>;
100
+ /**
101
+ * Adds a listener to be called when `eventName` is emitted.
102
+ */
103
+ on<T = unknown>(eventName: string, listener: (event: ContextEvent<T>) => void): void;
104
+ /**
105
+ * Removes a listener from the list to be called when `eventName` is emitted.
106
+ */
107
+ off<T = unknown>(eventName: string, listener: (event: ContextEvent<T>) => void): void;
108
+ /**
109
+ * Adds a listener to be called when `eventName` is emitted. The listener is immediately removed after being called once.
110
+ */
111
+ once<T = unknown>(eventName: string, listener: (event: ContextEvent<T>) => void): void;
112
+ /**
113
+ * Emits a new event to all listeners.
114
+ */
115
+ emit<T = unknown>(eventName: string, detail: T): boolean;
116
+ /**
117
+ * Attaches a new store to this context.
118
+ */
119
+ attachStore(store: Store<any, any>): void;
120
+ /**
121
+ * Gets the nearest instance of a store. Throws an error if the store isn't provided higher in the tree.
122
+ */
123
+ useStore<Value>(factory: StoreFactory<any, Value>): Value;
106
124
  mount(selector: string, view?: ViewFunction<any>): Promise<void>;
107
125
  mount(element: HTMLElement, view?: ViewFunction<any>): Promise<void>;
108
126
  unmount(): Promise<void>;
@@ -1,29 +1,7 @@
1
1
  import type { Renderable } from "../types.js";
2
- import type { Dolla } from "./dolla.js";
2
+ import type { ElementContext } from "./context.js";
3
3
  import { type ViewContext, type ViewFunction, type ViewResult } from "./nodes/view.js";
4
4
  import { type MaybeState, type State } from "./state.js";
5
- export interface ElementContext {
6
- /**
7
- * The root Dolla instance this element belongs to.
8
- */
9
- root: Dolla;
10
- /**
11
- * Storage for context variables.
12
- */
13
- data: Record<string | symbol, unknown>;
14
- /**
15
- * A reference to the parent context.
16
- */
17
- parent?: ElementContext;
18
- /**
19
- * Whether to create DOM nodes in the SVG namespace. An `<svg>` element will set this to true and pass it down to children.
20
- */
21
- isSVG?: boolean;
22
- /**
23
- * The name of the nearest parent view.
24
- */
25
- viewName?: string;
26
- }
27
5
  /**
28
6
  * Markup is a set of element metadata that hasn't been constructed into a MarkupElement yet.
29
7
  */
@@ -1,4 +1,5 @@
1
- import { type ElementContext, type Markup, type MarkupElement } from "../markup.js";
1
+ import { type ElementContext } from "../context.js";
2
+ import { type Markup, type MarkupElement } from "../markup.js";
2
3
  import { type Ref } from "../ref.js";
3
4
  import { type State, type StopFunction } from "../state.js";
4
5
  import { IS_MARKUP_ELEMENT } from "../symbols.js";
@@ -1,5 +1,6 @@
1
1
  import type { Renderable } from "../../types.js";
2
- import { type ElementContext, type MarkupElement } from "../markup.js";
2
+ import type { ElementContext } from "../context.js";
3
+ import { type MarkupElement } from "../markup.js";
3
4
  import { type MaybeState } from "../state.js";
4
5
  import { IS_MARKUP_ELEMENT } from "../symbols.js";
5
6
  interface ObserverOptions {
@@ -1,5 +1,6 @@
1
- import { type Renderable } from "../../types.js";
2
- import { type ElementContext, type MarkupElement } from "../markup.js";
1
+ import type { Renderable } from "../../types.js";
2
+ import type { ElementContext } from "../context.js";
3
+ import { type MarkupElement } from "../markup.js";
3
4
  import { IS_MARKUP_ELEMENT } from "../symbols.js";
4
5
  interface PortalConfig {
5
6
  content: Renderable;
@@ -1,4 +1,5 @@
1
- import { type ElementContext, type MarkupElement } from "../markup.js";
1
+ import { type ElementContext } from "../context.js";
2
+ import { type MarkupElement } from "../markup.js";
2
3
  import { type Setter, type State, type StopFunction } from "../state.js";
3
4
  import { IS_MARKUP_ELEMENT } from "../symbols.js";
4
5
  import { type ViewContext, type ViewResult } from "./view.js";
@@ -23,7 +24,7 @@ export declare class Repeat<T> implements MarkupElement {
23
24
  stopCallback?: StopFunction;
24
25
  connectedItems: ConnectedItem<T>[];
25
26
  elementContext: ElementContext;
26
- renderFn: ($value: State<T>, $index: State<number>, ctx: ViewContext) => ViewResult;
27
+ renderFn: (this: ViewContext, $value: State<T>, $index: State<number>, context: ViewContext) => ViewResult;
27
28
  keyFn: (value: T, index: number) => string | number | symbol;
28
29
  get isMounted(): boolean;
29
30
  constructor({ elementContext, $items, renderFn, keyFn }: RepeatOptions<T>);
@@ -1,13 +1,14 @@
1
+ import { Emitter } from "@manyducks.co/emitter";
2
+ import { type ElementContext, type StorableContext } from "../context.js";
1
3
  import type { Logger } from "../dolla.js";
2
- import { type ElementContext, type Markup, type MarkupElement } from "../markup.js";
4
+ import { type Markup, type MarkupElement } from "../markup.js";
3
5
  import { type MaybeState, type State, type StateValues, type StopFunction } from "../state.js";
4
6
  import { IS_MARKUP_ELEMENT } from "../symbols.js";
5
- import { Emitter } from "@manyducks.co/emitter";
6
7
  /**
7
8
  * Any valid value that a View can return.
8
9
  */
9
10
  export type ViewResult = Node | State<any> | Markup | Markup[] | null;
10
- export type ViewFunction<P> = (props: P, context: ViewContext) => ViewResult;
11
+ export type ViewFunction<P> = (this: ViewContext, props: P, context: ViewContext) => ViewResult;
11
12
  /**
12
13
  * A view that has been constructed into DOM nodes.
13
14
  */
@@ -17,27 +18,22 @@ export interface ViewElement extends MarkupElement {
17
18
  */
18
19
  setChildView(view: ViewFunction<{}>): ViewElement;
19
20
  }
20
- export interface ViewContext extends Logger {
21
+ export interface ViewContext extends Logger, StorableContext {
21
22
  /**
22
23
  * An ID unique to this view.
23
24
  */
24
25
  readonly uid: string;
25
- /**
26
- * Sets a context variable and returns its value. Context variables are accessible on the same context and from those of child views.
27
- */
28
- set<T>(key: string | symbol, value: T): T;
29
- /**
30
- * Gets the value of a context variable. Returns null if the variable is not set.
31
- */
32
- get<T>(key: string | symbol): T | null;
33
26
  /**
34
27
  * Returns an object of all variables stored on this context.
35
28
  */
36
- getAll(): Record<string | symbol, unknown>;
37
29
  /**
38
30
  * Sets the name of the view's built in logger.
39
31
  */
40
32
  setName(name: string): ViewContext;
33
+ /**
34
+ * True while this view is connected to the DOM.
35
+ */
36
+ readonly isMounted: boolean;
41
37
  /**
42
38
  * Registers a callback to run just before this view is mounted. DOM nodes are not yet attached to the page.
43
39
  */
@@ -64,6 +60,7 @@ export interface ViewContext extends Logger {
64
60
  */
65
61
  outlet(): Markup;
66
62
  }
63
+ export declare function createView<Props extends Record<string, any> = Record<string, unknown>>(fn: ViewFunction<Props>): ViewFunction<Props>;
67
64
  type ViewEvents = {
68
65
  beforeMount: [];
69
66
  mounted: [];
@@ -0,0 +1,61 @@
1
+ import { Emitter } from "@manyducks.co/emitter";
2
+ import { type ComponentContext, type ElementContext } from "./context.js";
3
+ import type { Logger } from "./dolla.js";
4
+ import { type MaybeState, type StateValues, type StopFunction } from "./state.js";
5
+ export type StoreFunction<Options, Value> = (this: StoreContext, options: Options, context: StoreContext) => Value;
6
+ export type StoreFactory<Options, Value> = Options extends undefined ? () => Store<Options, Value> : (options: Options) => Store<Options, Value>;
7
+ export interface StoreContext extends Logger, ComponentContext {
8
+ /**
9
+ * True while this store is attached to a context that is currently mounted in the view tree.
10
+ */
11
+ readonly isMounted: boolean;
12
+ /**
13
+ * Registers a callback to run just after this store is mounted.
14
+ */
15
+ onMount(callback: () => void): void;
16
+ /**
17
+ * Registers a callback to run just after this store is unmounted.
18
+ */
19
+ onUnmount(callback: () => void): void;
20
+ /**
21
+ * Watch a set of states. The callback is called when any of the states receive a new value.
22
+ * Watchers will be automatically stopped when this store is unmounted.
23
+ */
24
+ watch<T extends MaybeState<any>[]>(states: [...T], callback: (...values: StateValues<T>) => void): StopFunction;
25
+ }
26
+ type StoreEvents = {
27
+ mounted: [];
28
+ unmounted: [];
29
+ };
30
+ export declare class Store<Options, Value> {
31
+ readonly key: string;
32
+ private _fn;
33
+ private _options;
34
+ /**
35
+ * Value is guaranteed to be set after `attach` is called.
36
+ */
37
+ value: Value;
38
+ isMounted: boolean;
39
+ _elementContext: ElementContext;
40
+ _emitter: Emitter<StoreEvents>;
41
+ _logger: Logger;
42
+ _watcher: import("./state.js").StateWatcher;
43
+ get name(): string;
44
+ constructor(key: string, fn: StoreFunction<Options, Value>, options: Options);
45
+ /**
46
+ * Attaches this Store to the elementContext.
47
+ * Returns false if there was already an instance attached, and true otherwise.
48
+ */
49
+ attach(elementContext: ElementContext): boolean;
50
+ handleMount(): void;
51
+ handleUnmount(): void;
52
+ }
53
+ export declare function isStoreFactory<Options, Value>(value: any): value is StoreFactory<Options, Value>;
54
+ export declare function isStore<Options, Value>(value: any): value is Store<Options, Value>;
55
+ /**
56
+ * Defines a new store.
57
+ */
58
+ export declare function createStore<Options = undefined, Value = unknown>(fn: StoreFunction<Options, Value>): StoreFactory<Options, Value>;
59
+ export declare class StoreError extends Error {
60
+ }
61
+ export {};
@@ -2,3 +2,5 @@ export declare const IS_STATE: unique symbol;
2
2
  export declare const IS_REF: unique symbol;
3
3
  export declare const IS_MARKUP: unique symbol;
4
4
  export declare const IS_MARKUP_ELEMENT: unique symbol;
5
+ export declare const IS_STORE: unique symbol;
6
+ export declare const IS_STORE_FACTORY: unique symbol;
package/dist/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export { createState, derive, isState, toState, toValue } from "./core/state.js";
2
2
  export type { MaybeState, Setter, State, StopFunction } from "./core/state.js";
3
3
  export { createRef, isRef, type Ref } from "./core/ref.js";
4
+ export { createStore, type Store, type StoreFunction, type StoreFactory } from "./core/store.js";
4
5
  export { deepEqual, shallowEqual, strictEqual } from "./utils.js";
5
6
  export { cond, createMarkup, html, portal, repeat } from "./core/markup.js";
6
7
  export type { Markup, MarkupElement } from "./core/markup.js";
8
+ export { createView } from "./core/nodes/view.js";
7
9
  import { Dolla } from "./core/dolla.js";
8
10
  declare const dolla: Dolla;
9
11
  export default dolla;