@jay-framework/runtime 0.5.0

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,85 @@
1
+ # Context API Implementation
2
+
3
+ These functions provide a mechanism for managing and sharing context within a hierarchical structure,
4
+ used with `JayElement`s.
5
+
6
+ > The functions are intended to be an internal API for `@jay-framework/runtime` and `@jay-framework/component`.
7
+ > The `@jay-framework/component` library defines the public Jay context API in [provide-context.md](../../component/docs/provide-context.md)
8
+ > and [provide-reactive-context.md](../../component/docs/provide-reactive-context.md).
9
+
10
+ ## `createJayContext`
11
+
12
+ Creates a unique symbol (a `ContextMarker`) to identify a specific context type.
13
+
14
+ ### Returns:
15
+
16
+ A `ContextMarker` symbol.
17
+
18
+ ## `withContext`
19
+
20
+ Temporarily establishes a new context for a given block of code.
21
+
22
+ ### Parameters:
23
+
24
+ - `marker`: The `ContextMarker` identifying the context type.
25
+ - `context`: The actual context value to be provided. \* `callback`: The function to execute within the new context.
26
+
27
+ ### Returns:
28
+
29
+ The return value of the `callback` function.
30
+
31
+ ## `useContext`
32
+
33
+ Retrieves the current context value for a given `ContextMarker`.
34
+
35
+ ### Parameters:
36
+
37
+ - `marker`: The `ContextMarker` identifying the context type.
38
+
39
+ ### Returns:
40
+
41
+ The current context value.
42
+
43
+ ## `findContext`
44
+
45
+ Searches the current context stack for a context matching the given predicate.
46
+
47
+ ### Parameters:
48
+
49
+ - `predicate`: A function that takes a `ContextMarker` and returns a boolean indicating whether it's the desired context.
50
+
51
+ ### Returns:
52
+
53
+ The found context value, or `undefined` if not found.
54
+
55
+ ## `saveContext`
56
+
57
+ Saves the current context stack for later restoration. The function is used internally by JayComponent update collection
58
+ to ensure passing the right context to newly created child components.
59
+
60
+ ### Returns:
61
+
62
+ The saved context stack.
63
+
64
+ ## # `restoreContext`
65
+
66
+ Restores a previously saved context stack. The function is used internally by JayComponent update collection
67
+ to ensure passing the right context to newly created child components.
68
+
69
+ ### Parameters:
70
+
71
+ - `savedContext`: The saved context stack to restore.
72
+ - `callback`: The function to execute within the restored context.
73
+
74
+ ### Returns:
75
+
76
+ The return value of the `callback` function.
77
+
78
+ # How it Works internally:
79
+
80
+ 1. **Context Markers:** Unique symbols are created to identify different context types.
81
+ 2. **Context Stack:** A stack-like data structure is used to manage the current context and its parent contexts.
82
+ 3. **`withContext`:** Pushes a new context onto the stack, executes the callback, and then pops the context off the stack.
83
+ 4. **`useContext`:** Searches the current context stack for the specified context marker and returns its associated value.
84
+ 5. **`findContext`:** Searches the context stack for a context matching the given predicate.
85
+ 6. **`saveContext` and `restoreContext`:** Allow for saving and restoring the current context stack, enabling complex context management scenarios.
@@ -0,0 +1,81 @@
1
+ # Generated JayElement
2
+
3
+ > note: this is an "how it works doc"
4
+
5
+ The `@jay-framework/compiler` compiles `jay-html` into `JayElement<ViewState, Refs>` implementation code files, discussed here.
6
+
7
+ Taking the example from the [readme.md](../readme.md), the `jay-html` is
8
+
9
+ ```html
10
+ <html>
11
+ <head>
12
+ <script type="application/yaml-jay">
13
+ data:
14
+ count: number
15
+ </script>
16
+ </head>
17
+ <body>
18
+ <div>
19
+ <button ref="subtracter">-</button>
20
+ <span style="margin: 0 16px">{count}</span>
21
+ <button ref="adder-button">+</button>
22
+ </div>
23
+ </body>
24
+ </html>
25
+ ```
26
+
27
+ and the generated jay element is
28
+
29
+ ```typescript
30
+ import {
31
+ JayElement,
32
+ element as e,
33
+ dynamicText as dt,
34
+ RenderElement,
35
+ ReferencesManager,
36
+ ConstructContext,
37
+ HTMLElementProxy,
38
+ RenderElementOptions,
39
+ } from '@jay-framework/runtime';
40
+
41
+ export interface CounterViewState {
42
+ count: number;
43
+ }
44
+
45
+ export interface CounterElementRefs {
46
+ subtracter: HTMLElementProxy<CounterViewState, HTMLButtonElement>;
47
+ adderButton: HTMLElementProxy<CounterViewState, HTMLButtonElement>;
48
+ }
49
+
50
+ export type CounterElement = JayElement<CounterViewState, CounterElementRefs>;
51
+ export type CounterElementRender = RenderElement<
52
+ CounterViewState,
53
+ CounterElementRefs,
54
+ CounterElement
55
+ >;
56
+ export type CounterElementPreRender = [CounterElementRefs, CounterElementRender];
57
+
58
+ export function render(options?: RenderElementOptions): CounterElementPreRender {
59
+ const [refManager, [refSubtracter, refAdderButton]] = ReferencesManager.for(
60
+ options,
61
+ ['subtracter', 'adderButton'],
62
+ [],
63
+ [],
64
+ [],
65
+ );
66
+ const render = (viewState: CounterViewState) =>
67
+ ConstructContext.withRootContext(viewState, refManager, () =>
68
+ e('div', {}, [
69
+ e('button', {}, ['-'], refSubtracter()),
70
+ e('span', { style: { cssText: 'margin: 0 16px' } }, [dt((vs) => vs.count)]),
71
+ e('button', {}, ['+'], refAdderButton()),
72
+ ]),
73
+ ) as CounterElement;
74
+ return [refManager.getPublicAPI() as CounterElementRefs, render];
75
+ }
76
+ ```
77
+
78
+ The generated file can use any of set of jay element creation functions - `element`, `dynamicElement`, `dynamicText`,
79
+ `dynamicAttribute`, `dynamicProperty`, `childComp`, `forEach`, `conditional`.
80
+
81
+ - See the [Runtime Implementation](./runtime.md) for the details of the jay-element constructor functions.
@@ -0,0 +1,51 @@
1
+ # The kindergarten
2
+
3
+ The kindergarten is a software library that manages the children of an HTML node.
4
+ It provides a high level abstraction over the APIs of the HTML node itself,
5
+ key to manage different groups of children, each group with a different logic.
6
+
7
+ Consider a node who has one conditional child, another set of children bound to an array
8
+ and a 3rd conditional child. The logic of binding the elements of the array to the nodes
9
+ needs to use the node indexes, which are dependent on the first conditional node.
10
+ The second conditional node depends on the first and the array.
11
+
12
+ The kindergarten manages this logic for us. It models the above as
13
+
14
+ | group | nodes | group offset |
15
+ | ----- | ----------------------------------------------- | ----------------------------------------- |
16
+ | 1 | zero or one element, depending on the condition | 0 |
17
+ | 2 | zero to N elements, depending on the array | #(group 1 elements) |
18
+ | 3 | zero or one element, depending on the condition | #(group 1 elements) + #(group 2 elements) |
19
+
20
+ The kindergarten API is
21
+
22
+ ```typescript
23
+ declare class Kindergarten {
24
+ readonly parentNode: HTMLElement;
25
+ constructor(parentNode: HTMLElement);
26
+ newGroup(): KindergartenGroup;
27
+ getOffsetFor(group: KindergartenGroup): number;
28
+ }
29
+ ```
30
+
31
+ which allows to create groups and get the index offset of a group.
32
+
33
+ The KindergartenGroup API is
34
+
35
+ ```typescript
36
+ declare class KindergartenGroup {
37
+ children: Set<Node>;
38
+ constructor(kindergarten: Kindergarten);
39
+ addListener(groupListener: KindergardenGroupListener): void;
40
+ ensureNode(node: Node, atIndex?: number): void;
41
+ removeNode(node: Node): void;
42
+ removeNodeAt(pos: number): void;
43
+ moveNode(from: number, to: number): void;
44
+ }
45
+ ```
46
+
47
+ - ensureNode - makes sure the provided node is at the provided index, relative to the group offset
48
+ - removeNode - removes the HTML node
49
+ - removeNodeAt - removes the node based on it's index relative to the group offset
50
+ - moveNode - moves the node from an index to an index, both relative to the group offset
51
+ - addListener - adds a listener to for the group mutations, to listen on adding and removing nodes from the group
package/docs/refs.md ADDED
@@ -0,0 +1,200 @@
1
+ # Refs
2
+
3
+ The `Refs` type is one of the generated types for a `jay-html`.
4
+ For each `ref` attribute in the `jay-html`, a member is created in the `refs` type.
5
+
6
+ ## Examples of Refs Types
7
+
8
+ ```typescript
9
+ export interface TodoElementRefs {
10
+ newTodo: HTMLElementProxy<TodoViewState, HTMLInputElement>;
11
+ toggleAll: HTMLElementProxy<TodoViewState, HTMLInputElement>;
12
+ items: ItemRefs<ShownTodo>;
13
+ filterAll: HTMLElementProxy<TodoViewState, HTMLAnchorElement>;
14
+ filterActive: HTMLElementProxy<TodoViewState, HTMLAnchorElement>;
15
+ filterCompleted: HTMLElementProxy<TodoViewState, HTMLAnchorElement>;
16
+ clearCompleted: HTMLElementProxy<TodoViewState, HTMLButtonElement>;
17
+ }
18
+ ```
19
+
20
+ Jay runtime includes 4 type of Refs - `HTMLElementProxy`, `HTMLElementCollectionProxy`,
21
+ `the component`, `ComponentCollectionProxy`. The first are used directly, while the latter are use indirectly via
22
+ a generated `component-refs.ts` file.
23
+
24
+ The generated `component-refs.ts` file, for example for the `items` property above is
25
+
26
+ ```typescript
27
+ import { EventEmitter, ComponentCollectionProxy, EventTypeFrom } from '@jay-framework/runtime';
28
+ import { Item } from './item';
29
+
30
+ export type ItemComponentType<ParentVS> = ReturnType<typeof Item<ParentVS>>;
31
+
32
+ export interface ItemRefs<ParentVS>
33
+ extends ComponentCollectionProxy<ParentVS, ItemComponentType<ParentVS>> {
34
+ onCompletedToggle: EventEmitter<
35
+ EventTypeFrom<ItemComponentType<ParentVS>['onCompletedToggle']>,
36
+ ParentVS
37
+ >;
38
+ onRemove: EventEmitter<EventTypeFrom<ItemComponentType<ParentVS>['onRemove']>, ParentVS>;
39
+ onTitleChanged: EventEmitter<
40
+ EventTypeFrom<ItemComponentType<ParentVS>['onTitleChanged']>,
41
+ ParentVS
42
+ >;
43
+ }
44
+ ```
45
+
46
+ And the component type is `ItemComponentType` while the `ComponentCollectionProxy` type is `ItemRefs`.
47
+
48
+ ## JayEventHandler
49
+
50
+ The `refs` enable to listen on events from elements or components.
51
+ All Jay Events are using the `JayEventHandler` type
52
+
53
+ ```typescript
54
+ export type Coordinate = string[];
55
+ export interface JayEvent<EventType, ViewState> {
56
+ event: EventType;
57
+ viewState: ViewState;
58
+ coordinate: Coordinate;
59
+ }
60
+ export type JayEventHandler<EventType, ViewState, Returns> = (
61
+ event: JayEvent<EventType, ViewState>,
62
+ ) => Returns;
63
+ ```
64
+
65
+ - `JayEventHandler`: the event callback function, called when an event is emitted.
66
+ - `event: JayEvent<EventType, ViewState>`: the parameter of the handler, holding all the event data and context
67
+ - `event.event: EventType`: the type of the event. For DOM elements, this is the native browser event
68
+ (if allowed by access security patterns). For components, this is the emitted component event.
69
+ - `event.viewState: ViewState`: the ViewState at the location of the element or component. Useful to get the context
70
+ of repeated items
71
+ - `coordinate: Coordinate`: an array of id's from nested `forEach` element structures, to get the logical location
72
+ of the element or component who triggered the event.
73
+
74
+ ## HTMLElementProxy
75
+
76
+ The `HTMLElementProxy` is a proxy for a single dom element ref.
77
+ The `HTMLElementProxy` effective type (simplified view) is
78
+
79
+ ```typescript
80
+ interface HTMLElementProxy<ViewState, ElementType extends HTMLElement>
81
+ extends GlobalJayEvents<ViewState> {
82
+ addEventListener<E extends Event>(
83
+ type: string,
84
+ handler: JayEventHandler<E, ViewState, any>,
85
+ options?: boolean | AddEventListenerOptions,
86
+ );
87
+
88
+ removeEventListener<E extends Event>(
89
+ type: string,
90
+ handler: JayEventHandler<E, ViewState, any>,
91
+ options?: EventListenerOptions | boolean,
92
+ );
93
+
94
+ exec$<ResultType>(
95
+ handler: JayNativeFunction<ElementType, ViewState, ResultType>,
96
+ ): Promise<ResultType>;
97
+ }
98
+ ```
99
+
100
+ - `onclick`, `oninput`, `on...`: named event handlers to register new event handlers.
101
+ - `addEventListener`: registers a new event handlers.
102
+ - `removeEventListener`: registers a new event handlers.
103
+ - `exec$`: runs a code function against the DOM element. The function must match compiler patterns (see the compiler security section)
104
+ to be property run in secure applications. In non-secure applications, the function just runs with the DOM element.
105
+
106
+ ## HTMLElementCollectionProxy
107
+
108
+ The `HTMLElementCollectionProxy` is a proxy for a collection of DOM elements with the same ref, normally
109
+ children of one or more `forEach` element creator functions or `jay-html` directives.
110
+ The `HTMLElementCollectionProxy` effective type (simplified view) is
111
+
112
+ ```typescript
113
+ interface HTMLElementCollectionProxy<ViewState, ElementType extends HTMLElement>
114
+ extends GlobalJayEvents<ViewState> {
115
+ addEventListener<E extends Event>(
116
+ type: string,
117
+ handler: JayEventHandler<E, ViewState, any>,
118
+ options?: boolean | AddEventListenerOptions,
119
+ );
120
+
121
+ removeEventListener<E extends Event>(
122
+ type: string,
123
+ handler: JayEventHandler<E, ViewState, any>,
124
+ options?: EventListenerOptions | boolean,
125
+ );
126
+
127
+ find(
128
+ predicate: (t: ViewState, c: Coordinate) => boolean,
129
+ ): HTMLNativeExec<ViewState, ElementType> | undefined;
130
+
131
+ map<ResultType>(
132
+ handler: (
133
+ element: HTMLNativeExec<ViewState, ElementType>,
134
+ viewState: ViewState,
135
+ coordinate: Coordinate,
136
+ ) => ResultType,
137
+ ): Array<ResultType>;
138
+ }
139
+ ```
140
+
141
+ - `onclick`, `oninput`, `on...`: named event handlers to register new event handlers for all the underlying elements.
142
+ - `addEventListener`: registers a new event handlers for all the referenced elements.
143
+ - `removeEventListener`: registers a new event handlers for all the referenced elements.
144
+ - `find`: finds the first DOM element who matches the predicate by the element view state or coordinate.
145
+ Once found, the element can be interacted with the `exec$` function of `HTMLElementProxy`.
146
+ - `map`: similar to an array map, this function runs for all the referenced DOM elements, given the
147
+ view state, coordinate and the element proxy with the `exec$` function of `HTMLElementProxy`.
148
+
149
+ > understanding nested ViewState and Coordinate:
150
+ > when using nested forEach structures, forEach mandates that each array element has an id property named by matchBy.
151
+ > the element of the array is the ViewState, and the coordinate holds the id's path.
152
+ > for a single forEach, the array item will be the view state, while the id of the item will be the coordinate.
153
+ > for nested forEach, the most nested array item will be the view state, while the id's of all parents will be the coordinate.
154
+
155
+ ## The component
156
+
157
+ For a single component (not under `forEach`), the ref is just a proxy to the component directly and has the same interface
158
+ as the component.
159
+
160
+ **One important note - while the component constructor is running, the component ref is not initialized yet as the
161
+ component was not rendered yet. It is safe to set event handlers on the component, but calling the component APIs is only
162
+ supported on later times, such as event handlers or async code.**
163
+
164
+ ## ComponentCollectionProxy
165
+
166
+ For a collection of components, components nested under `forEach`, Jay generates a type for each component based on
167
+ `ComponentCollectionProxy`, adding to it the component named event handlers.
168
+
169
+ The actual refs type is then `ComponentRefs` extending the `ComponentCollectionProxy` type
170
+
171
+ ```typescript
172
+ interface ComponentRefs<ParentVS>
173
+ extends ComponentCollectionProxy<ParentVS, ComponentType<ParentVS>> {}
174
+
175
+ interface ComponentCollectionProxy<ViewState, ComponentType extends JayComponent<any, any, any>> {
176
+ addEventListener(
177
+ type: string,
178
+ handler: JayEventHandler<any, ViewState, void>,
179
+ options?: boolean | AddEventListenerOptions,
180
+ ): void;
181
+ removeEventListener(
182
+ type: string,
183
+ handler: JayEventHandler<any, ViewState, void>,
184
+ options?: EventListenerOptions | boolean,
185
+ ): void;
186
+
187
+ map<ResultType>(
188
+ handler: (comp: ComponentType, viewState: ViewState, coordinate: Coordinate) => ResultType,
189
+ ): Array<ResultType>;
190
+ find(predicate: (t: ViewState) => boolean): ComponentType | undefined;
191
+ }
192
+ ```
193
+
194
+ - `named event handlers`: The named event handlers of the component
195
+ - `addEventListener`: registers a new event handlers for all the referenced components.
196
+ - `removeEventListener`: registers a new event handlers for all the referenced components.
197
+ - `find`: finds the first component who matches the predicate by the component view state or coordinate.
198
+ Once found, the component can be interacted with directly.
199
+ - `map`: similar to an array map, this function runs for all the referenced components , given the
200
+ view state, coordinate and the component.