idlize
1/*
2* Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15
16import { className, uint32 } from "@koalaui/common"17import { __context, __id } from "../internals"18import { IncrementalNode } from "../tree/IncrementalNode"19
20/**
21* @param create - the node constructor is invoked only once,
22* then the created node is attached to the hierarchy
23* @param update - the node updater is invoked right after the node constructor,
24* than it can be invoked again if the values of the used states have changed
25* @internal
26* @memo:intrinsic
27*/
28export function NodeAttach<Node extends IncrementalNode>(29create: () => Node,30/** @memo */31update: (node: Node) => void32): void {33const scope = __context().scope<void>(__id(), 0, create, undefined, undefined, undefined)34if (scope.unchanged) {35scope.cached36} else try {37update(__context().node as Node)38} finally {39scope.recache()40}41}
42
43/**
44* @param kind - expected kind of current node
45* @param name - description of a call site to use use in exception
46* @returns current node
47* @throws Error with the given name if node kind is not expected
48* @internal
49* @memo:intrinsic
50*/
51export function contextNode<T extends IncrementalNode>(kind: uint32 = 1, name?: string): T {52const node = __context().node53if (node?.isKind(kind) == true) return node as T54throw new Error(name55? (name + " cannot be used in context of " + className(node))56: ("current " + className(node) + " does not contain the specified kind: " + kind)57)58}
59
60/**
61* @internal
62*/
63export class DataNode<Data> extends IncrementalNode {64private data: Data | undefined = undefined65
66constructor(kind: uint32 = 1) {67super(kind)68}69
70/**71* @memo:intrinsic
72*/
73static attach<Data>(74kind: uint32,75data: Data,76onDataChange?: () => void77): void {78const scope = __context().scope<void>(__id(), 1, (): IncrementalNode => new DataNode<Data>(kind), undefined, undefined, undefined)79const state = scope.param(0, data, undefined, undefined, undefined)80if (scope.unchanged) {81scope.cached82} else try {83const node = __context().node as DataNode<Data>84if (node.kind != kind) throw new Error("data node kind changed unexpectedly from " + node.kind + " to " + kind)85node.data = state.value as Data // subscribe to the parameter change86onDataChange?.()87} finally {88scope.recache()89}90}91
92static extract<Data>(kind: uint32, node: IncrementalNode): Data | undefined {93return node.isKind(kind) ? (node as DataNode<Data>).data : undefined94}95}
96