@manyducks.co/dolla 2.0.0-alpha.44 → 2.0.0-alpha.46

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
@@ -13,7 +13,7 @@ Dolla is a batteries-included JavaScript frontend framework covering the needs o
13
13
  - 🔀 Client side [routing](./docs/router.md) with nested routes and middleware support (check login status, preload data, etc).
14
14
  - 🐕 Built-in [HTTP](./docs/http.md) client with middleware support (set auth headers, etc).
15
15
  - 📍 Lightweight [localization](./docs/i18n.md) system (store translated strings in JSON files and call the `t` function to get them).
16
- - 🍳 Build system optional. [Write views in JSX](./docs/setup.md) or use `html` tagged template literals.
16
+ - 🍳 Build system optional. [Write views in JSX](./docs/setup.md) or bring in [HTM](https://github.com/developit/htm) and use tagged template literals directly in the browser.
17
17
 
18
18
  Dolla's goals include:
19
19
 
@@ -35,12 +35,12 @@ Dolla's goals include:
35
35
  A basic view. Note that the view function is called exactly once when the view is first mounted. All changes to DOM nodes thereafter happen as a result of `$state` values changing.
36
36
 
37
37
  ```jsx
38
- import Dolla, { atom, html } from "@manyducks.co/dolla";
38
+ import Dolla, { atom } from "@manyducks.co/dolla";
39
39
 
40
- function Counter() {
40
+ function Counter(props, ctx) {
41
41
  const count = atom(0);
42
42
 
43
- this.effect(() => {
43
+ ctx.effect(() => {
44
44
  console.log(`Count is: ${count.get()}`);
45
45
  });
46
46
 
@@ -49,19 +49,19 @@ function Counter() {
49
49
  }
50
50
 
51
51
  function decrement() {
52
- // alternative to `set(get() - 1)`
53
52
  count.update((value) => value - 1);
53
+ // alternative to `set(get() - 1)`
54
54
  }
55
55
 
56
- return html`
56
+ return (
57
57
  <div>
58
- <p>Counter: ${count}</p>
58
+ <p>Counter: {counter}</p>
59
59
  <div>
60
- <button onclick=${increment}>+1</button>
61
- <button onclick=${decrement}>-1</button>
60
+ <button on:click={increment}>+1</button>
61
+ <button on:click={decrement}>-1</button>
62
62
  </div>
63
63
  </div>
64
- `;
64
+ );
65
65
  }
66
66
 
67
67
  Dolla.mount(document.body, Counter);
@@ -70,19 +70,19 @@ Dolla.mount(document.body, Counter);
70
70
  > TODO: Show small examples for routing and stores.
71
71
 
72
72
  ```js
73
- function MessageStore() {
74
- const message = atom("message", "Hello world!");
73
+ function MessageStore(options, ctx) {
74
+ const message = atom("Hello world!");
75
75
 
76
- this.effect(() => {
77
- this.log(`Message is now: ${get(message)}`);
76
+ ctx.effect(() => {
77
+ ctx.log(`Message is now: ${get(message)}`);
78
78
  // Calling `get()` inside an effect (or compose) function will track that reactive value as a dependency.
79
79
  // Effects will re-run when a dependency updates.
80
80
  });
81
- // `this` refers to the context object; StoreContext in a store and ViewContext in a view.
81
+ // `ctx` refers to the context object; StoreContext in a store and ViewContext in a view.
82
82
  // Context objects contain methods for controlling the component, logging and attaching lifecycle hooks.
83
83
 
84
84
  return {
85
- message: compose("message:readonly", () => get(message)),
85
+ message: compose(() => message.get()),
86
86
  // Creates a read-only reactive of the message value.
87
87
  // Composed values update when their dependencies update.
88
88
 
@@ -91,33 +91,33 @@ function MessageStore() {
91
91
  };
92
92
  }
93
93
 
94
- function App() {
95
- const { message, setMessage } = this.provide(MessageStore);
94
+ function App(props, ctx) {
95
+ const { message, setMessage } = ctx.provide(MessageStore);
96
96
  // Provides a MessageStore on this context and any child contexts.
97
97
  // When a store is provided its value is returned right away.
98
98
 
99
- return html`
99
+ return (
100
100
  <div>
101
- <${MessageView} />
102
- <${MessageView} />
103
- <${MessageView} />
101
+ <MessageView />
102
+ <MessageView />
103
+ <MessageView />
104
104
 
105
105
  <input
106
106
  type="text"
107
- value=${message}
108
- oninput=${(e) => {
107
+ value={message}
108
+ on:input={(e) => {
109
109
  setMessage(e.currentTarget.value);
110
110
  }}
111
111
  />
112
112
  </div>
113
- `;
113
+ );
114
114
  }
115
115
 
116
- function MessageView() {
117
- const { message } = this.get(MessageStore);
116
+ function MessageView(props, ctx) {
117
+ const { message } = ctx.get(MessageStore);
118
118
  // Gets the nearest instance of MessageStore. In this case the one provided at the parent.
119
119
 
120
- return html`<span>${message}</span>`;
120
+ return <span>{message}</span>;
121
121
  }
122
122
  ```
123
123
 
@@ -1,4 +1,6 @@
1
1
  import type { Dolla } from "./dolla";
2
+ import type { View } from "./nodes/view";
3
+ import type { Atom } from "./signals";
2
4
  import type { Store, StoreFunction } from "./store";
3
5
  export interface ElementContext {
4
6
  /**
@@ -21,6 +23,10 @@ export interface ElementContext {
21
23
  * The name of the nearest parent view.
22
24
  */
23
25
  viewName?: string;
26
+ /**
27
+ * Current route layer of the nearest view.
28
+ */
29
+ route?: Atom<View<{}> | undefined>;
24
30
  }
25
31
  export interface ComponentContext {
26
32
  /**
@@ -1,6 +1,6 @@
1
1
  import type { Renderable } from "../types.js";
2
2
  import type { ElementContext } from "./context.js";
3
- import { type ViewContext, type ViewFunction, type ViewResult } from "./nodes/view.js";
3
+ import { View, type ViewContext, type ViewFunction, type ViewResult } from "./nodes/view.js";
4
4
  import { MaybeReactive, type Reactive } from "./signals.js";
5
5
  /**
6
6
  * Markup is a set of element metadata that hasn't been constructed into a MarkupElement yet.
@@ -18,7 +18,7 @@ export interface Markup {
18
18
  /**
19
19
  *
20
20
  */
21
- children?: Markup[];
21
+ children?: any[];
22
22
  }
23
23
  /**
24
24
  * A DOM node that has been constructed from a Markup object.
@@ -49,6 +49,9 @@ export interface MarkupAttributes {
49
49
  source: Reactive<Renderable>;
50
50
  };
51
51
  $outlet: {
52
+ view: Reactive<View<{}> | undefined>;
53
+ };
54
+ $fragment: {
52
55
  children: MaybeReactive<MarkupElement[]>;
53
56
  };
54
57
  $node: {
@@ -60,16 +63,24 @@ export interface MarkupAttributes {
60
63
  };
61
64
  [tag: string]: Record<string, any>;
62
65
  }
63
- export declare function createMarkup<T extends keyof MarkupAttributes>(type: T, attributes: MarkupAttributes[T], ...children: Renderable[]): Markup;
64
- export declare function createMarkup<I>(type: ViewFunction<I>, attributes?: I, ...children: Renderable[]): Markup;
66
+ export declare function markup<T extends keyof MarkupAttributes>(type: T, attributes: MarkupAttributes[T], ...children: Renderable[]): Markup;
67
+ export declare function markup<I>(type: ViewFunction<I>, attributes?: I, ...children: any[]): Markup;
65
68
  /**
66
69
  * Generate markup with HTML in a tagged template literal.
67
70
  */
68
- export declare const html: (strings: TemplateStringsArray, ...values: any[]) => Markup | Markup[];
69
71
  /**
70
72
  * Displays content conditionally. When `condition` holds a truthy value, `thenContent` is displayed; when `condition` holds a falsy value, `elseContent` is displayed.
73
+ * @deprecated
71
74
  */
72
75
  export declare function cond(condition: MaybeReactive<any>, thenContent?: Renderable, elseContent?: Renderable): Markup;
76
+ /**
77
+ * Displays `thenContent` when `condition` is truthy and `elseContent` when falsy.
78
+ */
79
+ export declare function when(condition: MaybeReactive<any>, thenContent?: Renderable, elseContent?: Renderable): Markup;
80
+ /**
81
+ * Inverted `when`. Displays `thenContent` when `condition` is falsy and `elseContent` when truthy.
82
+ */
83
+ export declare function unless(condition: MaybeReactive<any>, thenContent?: Renderable, elseContent?: Renderable): Markup;
73
84
  /**
74
85
  * Calls `renderFn` for each item in `items`. Dynamically adds and removes views as items change.
75
86
  * The result of `keyFn` is used to compare items and decide if item was added, removed or updated.
@@ -10,6 +10,8 @@ interface DynamicOptions {
10
10
  /**
11
11
  * Displays dynamic children without a parent element.
12
12
  * Renders a Reactive value via a render function.
13
+ *
14
+ * This is probably the most used element type aside from HTML.
13
15
  */
14
16
  export declare class Dynamic implements MarkupElement {
15
17
  [IS_MARKUP_ELEMENT]: boolean;
@@ -0,0 +1,19 @@
1
+ import { type MarkupElement } from "../markup.js";
2
+ import { type MaybeReactive } from "../signals.js";
3
+ import { IS_MARKUP_ELEMENT } from "../symbols.js";
4
+ /**
5
+ * Manages several MarkupElements as one.
6
+ */
7
+ export declare class Fragment implements MarkupElement {
8
+ [IS_MARKUP_ELEMENT]: boolean;
9
+ domNode: Text;
10
+ isMounted: boolean;
11
+ private source;
12
+ private elements;
13
+ private unsubscribe?;
14
+ constructor(source: MaybeReactive<MarkupElement[]>);
15
+ mount(parent: Node, after?: Node | undefined): void;
16
+ unmount(parentIsUnmounting?: boolean): void;
17
+ private cleanup;
18
+ private update;
19
+ }
@@ -1,11 +1,11 @@
1
1
  import { type ElementContext } from "../context.js";
2
- import { type Markup, type MarkupElement } from "../markup.js";
2
+ import { type MarkupElement } from "../markup.js";
3
3
  import { IS_MARKUP_ELEMENT } from "../symbols.js";
4
4
  type HTMLOptions = {
5
5
  elementContext: ElementContext;
6
6
  tag: string;
7
7
  props: Record<string, any>;
8
- children?: Markup[];
8
+ children?: any[];
9
9
  };
10
10
  export declare class HTML implements MarkupElement {
11
11
  [IS_MARKUP_ELEMENT]: boolean;
@@ -1,17 +1,18 @@
1
1
  import { type MarkupElement } from "../markup.js";
2
- import { type MaybeReactive } from "../signals.js";
2
+ import { Reactive } from "../signals.js";
3
3
  import { IS_MARKUP_ELEMENT } from "../symbols.js";
4
+ import { View } from "./view.js";
4
5
  /**
5
- * Manages several MarkupElements as one.
6
+ * Renders the subroute of the nearest view.
6
7
  */
7
8
  export declare class Outlet implements MarkupElement {
8
9
  [IS_MARKUP_ELEMENT]: boolean;
9
10
  domNode: Text;
10
11
  isMounted: boolean;
11
- private source;
12
- private elements;
12
+ private view;
13
+ private mountedView?;
13
14
  private unsubscribe?;
14
- constructor(source: MaybeReactive<MarkupElement[]>);
15
+ constructor(view: Reactive<View<{}> | undefined>);
15
16
  mount(parent: Node, after?: Node | undefined): void;
16
17
  unmount(parentIsUnmounting?: boolean): void;
17
18
  private cleanup;
@@ -1,7 +1,7 @@
1
1
  import type { ComponentContext, ElementContext, StoreConsumerContext, StoreProviderContext } from "../context.js";
2
2
  import type { Logger } from "../dolla.js";
3
3
  import { type Markup, type MarkupElement } from "../markup.js";
4
- import { type EffectCallback, type Reactive, type UnsubscribeFunction } from "../signals.js";
4
+ import { type EffectCallback, type EffectOptions, type Reactive, type UnsubscribeFunction } from "../signals.js";
5
5
  import { IS_MARKUP_ELEMENT } from "../symbols.js";
6
6
  /**
7
7
  * Any valid value that a View can return.
@@ -12,10 +12,7 @@ export type ViewFunction<P> = (this: ViewContext, props: P, context: ViewContext
12
12
  * A view that has been constructed into DOM nodes.
13
13
  */
14
14
  export interface ViewElement extends MarkupElement {
15
- /**
16
- * Take a ViewFunction and render it as a child of this view.
17
- */
18
- setChildView(view: ViewFunction<{}>): ViewElement;
15
+ setRouteView(view: ViewFunction<{}>): ViewElement;
19
16
  }
20
17
  export interface ViewContext extends Omit<Logger, "setName">, ComponentContext, StoreProviderContext, StoreConsumerContext {
21
18
  /**
@@ -46,9 +43,9 @@ export interface ViewContext extends Omit<Logger, "setName">, ComponentContext,
46
43
  * Passes a getter function to `callback` that will track reactive states and return their current values.
47
44
  * Callback will be run each time a tracked state gets a new value.
48
45
  */
49
- effect(callback: EffectCallback): UnsubscribeFunction;
46
+ effect(callback: EffectCallback, options?: EffectOptions): UnsubscribeFunction;
50
47
  /**
51
- * Returns a Markup element that displays this view's children.
48
+ * Displays this view's subroutes if mounted as a router view.
52
49
  */
53
50
  outlet(): Markup;
54
51
  }
@@ -57,11 +54,11 @@ export declare class View<P> implements ViewElement {
57
54
  uniqueId: string;
58
55
  elementContext: ElementContext;
59
56
  logger: Logger;
60
- props: P;
57
+ props: P & {
58
+ children: Markup[] | undefined;
59
+ };
61
60
  fn: ViewFunction<P>;
62
61
  element?: MarkupElement;
63
- childMarkup: Markup[];
64
- children: import("../signals.js").Atom<MarkupElement[]>;
65
62
  lifecycleListeners: {
66
63
  beforeMount: (() => any)[];
67
64
  mount: (() => any)[];
@@ -73,6 +70,6 @@ export declare class View<P> implements ViewElement {
73
70
  isMounted: boolean;
74
71
  mount(parent: Node, after?: Node): void;
75
72
  unmount(parentIsUnmounting?: boolean): void;
76
- setChildView(fn: ViewFunction<{}>): View<{}>;
73
+ setRouteView(fn: ViewFunction<{}>): View<{}>;
77
74
  private _initialize;
78
75
  }
@@ -49,7 +49,8 @@ export interface ReactiveOptions<T> {
49
49
  }
50
50
  export declare class Atom<T> implements Reactive<T> {
51
51
  #private;
52
- readonly name?: string;
52
+ get name(): string;
53
+ get [Symbol.toStringTag](): string;
53
54
  constructor(value: T, options?: ReactiveOptions<T>);
54
55
  /**
55
56
  * Returns the latest value. The signal is tracked as a dependency if called within `effect` or `compose`.
@@ -187,6 +188,7 @@ export declare function untrack(fn: () => void): void;
187
188
  */
188
189
  export declare function getTracked(fn: (tracked: Reactive<unknown>[]) => void): void;
189
190
  export type EffectCallback = () => void;
191
+ export type EffectOptions = {};
190
192
  /**
191
193
  * Creates a tracked scope that re-runs whenever the values of any tracked reactives changes.
192
194
  * Reactives are tracked by accessing their `value` within the body of the function.
@@ -194,5 +196,5 @@ export type EffectCallback = () => void;
194
196
  * NOTE: You must call the unsubscribe function to stop watching for changes.
195
197
  * If you are using an effect inside a View or Store, use `ctx.effect` instead, which cleans up automatically when the component unmounts.
196
198
  */
197
- export declare function effect(fn: EffectCallback): UnsubscribeFunction;
199
+ export declare function effect(fn: EffectCallback, options?: EffectOptions): UnsubscribeFunction;
198
200
  export {};
@@ -1,6 +1,6 @@
1
- import type { StoreConsumerContext, ComponentContext, ElementContext } from "./context.js";
1
+ import type { ComponentContext, ElementContext, StoreConsumerContext } from "./context.js";
2
2
  import type { Logger } from "./dolla.js";
3
- import { EffectCallback, UnsubscribeFunction } from "./signals.js";
3
+ import { type EffectCallback, type EffectOptions, type UnsubscribeFunction } from "./signals.js";
4
4
  export type StoreFunction<Options, Value> = (this: StoreContext, options: Options, context: StoreContext) => Value;
5
5
  export type StoreFactory<Options, Value> = Options extends undefined ? () => Store<Options, Value> : (options: Options) => Store<Options, Value>;
6
6
  export interface StoreContext extends Omit<Logger, "setName">, ComponentContext, StoreConsumerContext {
@@ -20,7 +20,7 @@ export interface StoreContext extends Omit<Logger, "setName">, ComponentContext,
20
20
  * Passes a getter function to `callback` that will track reactive states and return their current values.
21
21
  * Callback will be run each time a tracked state gets a new value.
22
22
  */
23
- effect(callback: EffectCallback): UnsubscribeFunction;
23
+ effect(callback: EffectCallback, options?: EffectOptions): UnsubscribeFunction;
24
24
  }
25
25
  export declare class Store<Options, Value> {
26
26
  readonly fn: StoreFunction<Options, Value>;
@@ -15,4 +15,4 @@ export type CrashViewProps = {
15
15
  */
16
16
  uid?: string;
17
17
  };
18
- export declare function DefaultCrashView(props: CrashViewProps): import("../markup.js").Markup | import("../markup.js").Markup[];
18
+ export declare function DefaultCrashView(props: CrashViewProps): import("../markup.js").Markup;
@@ -0,0 +1,9 @@
1
+ import { ViewContext, ViewResult } from "../nodes/view";
2
+ import { MaybeReactive, Reactive } from "../signals";
3
+ interface ForProps<T> {
4
+ each: MaybeReactive<Iterable<T>>;
5
+ key: (item: T, index: number) => any;
6
+ children: (item: Reactive<T>, index: Reactive<number>, ctx: ViewContext) => ViewResult;
7
+ }
8
+ export declare function For<T = any>({ each, key, children }: ForProps<T>): import("../markup").Markup;
9
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { Renderable } from "../../types.js";
2
+ import { type ViewContext } from "../nodes/view.js";
3
+ /**
4
+ * A utility view that displays its children.
5
+ */
6
+ export declare function Fragment(props: {
7
+ children?: Renderable;
8
+ }, ctx: ViewContext): import("../markup.js").Markup;
@@ -1,5 +1,5 @@
1
1
  import { type ViewContext } from "../nodes/view.js";
2
2
  /**
3
- * A utility view that simply displays its children.
3
+ * A utility view that simply displays a route.
4
4
  */
5
5
  export declare function Passthrough(_: {}, ctx: ViewContext): import("../markup.js").Markup;
@@ -0,0 +1,8 @@
1
+ import { m, d as o } from "./markup-BJffA2Ls.js";
2
+ function e(r, a) {
3
+ return m("$dynamic", { source: o(() => r.children) });
4
+ }
5
+ export {
6
+ e as F
7
+ };
8
+ //# sourceMappingURL=fragment-CYt92o5I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragment-CYt92o5I.js","sources":["../src/core/views/fragment.ts"],"sourcesContent":["import type { Renderable } from \"../../types.js\";\nimport { markup } from \"../markup.js\";\nimport { type ViewContext } from \"../nodes/view.js\";\nimport { compose } from \"../signals.js\";\n\n/**\n * A utility view that displays its children.\n */\nexport function Fragment(props: { children?: Renderable }, ctx: ViewContext) {\n return markup(\"$dynamic\", { source: compose(() => props.children) });\n}\n"],"names":["Fragment","props","ctx","markup","compose"],"mappings":";AAQgB,SAAAA,EAASC,GAAkCC,GAAkB;AACpE,SAAAC,EAAO,YAAY,EAAE,QAAQC,EAAQ,MAAMH,EAAM,QAAQ,GAAG;AACrE;"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export { atom, compose, effect, get, set, peek, untrack, getTracked } from "./core/signals.js";
2
- export type { Reactive, MaybeReactive, Atom } from "./core/signals.js";
1
+ export { atom, compose, effect, get, getTracked, peek, set, untrack } from "./core/signals.js";
2
+ export type { Atom, MaybeReactive, Reactive } from "./core/signals.js";
3
3
  export { deepEqual, shallowEqual, strictEqual } from "./utils.js";
4
4
  export { ref, type Ref } from "./core/ref.js";
5
- export { type StoreFunction, type StoreContext } from "./core/store.js";
5
+ export { type StoreContext, type StoreFunction } from "./core/store.js";
6
6
  export { createRouter, type Router, type RouterOptions } from "./router/index.js";
7
- export { cond, createMarkup, html, portal, list } from "./core/markup.js";
7
+ export { cond, list, markup, portal, unless, when } from "./core/markup.js";
8
8
  export type { Markup, MarkupElement } from "./core/markup.js";
9
9
  import { Dolla } from "./core/dolla.js";
10
10
  declare const dolla: Dolla;
@@ -14,8 +14,8 @@ export declare const http: import("./http/index.js").HTTP;
14
14
  export declare const createLogger: (name: string, options?: import("./core/dolla.js").LoggerOptions) => import("./core/dolla.js").Logger;
15
15
  export type { Dolla, Environment, Logger, LoggerErrorContext, LoggerOptions, Loggles } from "./core/dolla.js";
16
16
  export type { ViewContext, ViewElement, ViewFunction } from "./core/nodes/view.js";
17
- export type { InputType, Renderable } from "./types.js";
18
17
  export type { CrashViewProps } from "./core/views/default-crash-view.js";
18
+ export type { InputType, Renderable } from "./types.js";
19
19
  import type { IntrinsicElements as Elements } from "./types";
20
20
  declare global {
21
21
  namespace JSX {