From e161a9b8ccd1a7760923dd7edb09b3ba2fe6704e Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 4 Feb 2021 19:46:09 +0000 Subject: [PATCH] fix: Renderer remove nested controls, updated types --- .github/workflows/publish.yml | 2 +- .npmrc | 1 + packages/core/types/controls.d.ts | 33 ++++---- packages/react/src/renderer.tsx | 122 +++++++++++++++++++++++++----- 4 files changed, 124 insertions(+), 34 deletions(-) create mode 100644 .npmrc diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e266dbc..e1eee69 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/setup-node@v1.4.4 with: registry-url: 'https://registry.npmjs.org' - scope: '@garment' + scope: '@pixinsight' - name: Install dependencies run: yarn diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..67f5742 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@pixinsight:registry=https://registry.npmjs.org \ No newline at end of file diff --git a/packages/core/types/controls.d.ts b/packages/core/types/controls.d.ts index e3ea908..e7e5c51 100644 --- a/packages/core/types/controls.d.ts +++ b/packages/core/types/controls.d.ts @@ -211,10 +211,10 @@ declare class Frame extends Control { lineWidth: any; } declare class GroupBox extends Control { - checked: any; - onCheck: any; - title: any; - titleCheckBox: any; + checked: boolean; + onCheck: (checked: boolean) => void; + title: string; + titleCheckBox: boolean; } declare class Label extends Frame { clear: any; @@ -453,32 +453,37 @@ declare class WebView extends Control { declare class Sizer { constructor(...args: any[]); - add: any; + add(child: Sizer | Control, stretchFactor?: number, alignment?: number): void; addScaledSpacing: any; addSpacing: any; addStretch: any; addUnscaledSpacing: any; displayPixelRatio: any; - has: any; - indexOf: any; - insert: any; + has(child: Sizer | Control): boolean; + indexOf(child: Sizer | Control): number; + insert( + index: number, + child: Sizer | Control, + stretchFactor?: number, + alignment?: number + ): void; insertScaledSpacing: any; insertSpacing: any; insertStretch: any; insertUnscaledSpacing: any; - isHorizontal: any; - isVertical: any; + isHorizontal: boolean; + isVertical: boolean; logicalPixelsToPhysical: any; - margin: any; - numberOfItems: any; + margin: number; + numberOfItems: number; parentControl: Control; physicalPixelsToLogical: any; - remove: any; + remove(child: Sizer | Control): void; scaledMargin: any; scaledSpacing: any; setAlignment: any; setStretchFactor: any; - spacing: any; + spacing: number; unscaledMargin: any; unscaledSpacing: any; } diff --git a/packages/react/src/renderer.tsx b/packages/react/src/renderer.tsx index 3fb95ab..fa604f7 100644 --- a/packages/react/src/renderer.tsx +++ b/packages/react/src/renderer.tsx @@ -24,8 +24,9 @@ const sizerPropsSet = new Set([ "unscaledSpacing", ] as const); +type SizerProps = typeof sizerPropsSet extends Set ? T : never; + jsStrictMode = false; -console.clear(); console.log = (...args: any[]) => console.writeln(args.join(" ")); console.warn = console.warning; @@ -44,6 +45,56 @@ const debug = (...args: any[]) => { }; const garbage = new Control(); +const childrenBySizerMap = new Map(); + +function addChild( + parent: Sizer, + ...[child, ...args]: Parameters +) { + if (!childrenBySizerMap.has(parent)) { + childrenBySizerMap.set(parent, []); + } + const children = childrenBySizerMap.get(parent)!; + children.push(child); + parent.add(child, ...args); +} + +function insertChild( + parent: Sizer, + ...[index, child, ...args]: Parameters +) { + if (!childrenBySizerMap.has(parent)) { + childrenBySizerMap.set(parent, []); + } + const children = childrenBySizerMap.get(parent)!; + children.splice(index, 0, child); + parent.insert(index, child, ...args); +} + +function removeChild( + parent: Sizer, + ...[childToRemove]: Parameters +) { + if (!childrenBySizerMap.has(parent)) { + childrenBySizerMap.set(parent, []); + } + const children = childrenBySizerMap.get(parent)!; + + if (childToRemove instanceof Sizer && childrenBySizerMap.has(childToRemove)) { + childrenBySizerMap + .get(childToRemove) + ?.forEach((subchild) => removeChild(childToRemove, subchild)); + childrenBySizerMap.delete(childToRemove); + } + + childrenBySizerMap.set( + parent, + children.filter((child) => child !== childToRemove) + ); + if (childToRemove instanceof Control) { + childToRemove.parent = garbage; + } +} const PixInsightReconciler = Reconciler({ supportsMutation: true, @@ -88,6 +139,7 @@ const PixInsightReconciler = Reconciler({ constructorProps, stretchFactor = 0, alignment = 0, + children, ...props }: { type: string; @@ -95,6 +147,7 @@ const PixInsightReconciler = Reconciler({ constructorProps?: any[]; stretchFactor?: number; alignment?: number; + children: any; } ) { debug("createInstance", controlType, props); @@ -113,7 +166,7 @@ const PixInsightReconciler = Reconciler({ for (const key of Object.keys(instanceProps)) { if (sizerPropsSet.has(key as any) && instance instanceof Control) { - instance.sizer[key as keyof Sizer] = instanceProps[key]; + instance.sizer[key as SizerProps] = instanceProps[key]; } else { instance[key] = instanceProps[key]; } @@ -134,70 +187,90 @@ const PixInsightReconciler = Reconciler({ return label; }, + appendInitialChild(parent: Instance, child: Instance) { debug(`appendInitialChild ${parent.__id} ${child.__id}`); if (child instanceof Dialog) { return; } - const args = [child, child.stretchFactor]; + const args: Parameters = [ + child, + child.stretchFactor, + ]; if (!(child instanceof Sizer)) { args.push(child.alignment); } const sizer = parent instanceof Sizer ? parent : parent.sizer; child.parentSizer = sizer; - sizer.add(...args); + + addChild(sizer, ...args); }, appendChild(parent: Instance, child: Instance) { debug(`appendChild ${parent.__id} ${child.__id}`); if (child instanceof Dialog) { return; } - const args = [child, child.stretchFactor]; + const args: Parameters = [ + child, + child.stretchFactor, + ]; if (!(child instanceof Sizer)) { args.push(child.alignment); } const sizer = parent instanceof Sizer ? parent : parent.sizer; child.parentSizer = sizer; - sizer.add(...args); + + addChild(sizer, ...args); }, appendChildToContainer(parent: Instance, child: Instance) { debug(`appendChildToContainer ${parent.__id} ${child.__id}`); if (child instanceof Dialog) { return; } - const args = [child, child.stretchFactor]; + const args: Parameters = [ + child, + child.stretchFactor, + ]; if (!(child instanceof Sizer)) { args.push(child.alignment); } const sizer = parent instanceof Sizer ? parent : parent.sizer; child.parentSizer = sizer; - sizer.add(...args); + + addChild(sizer, ...args); }, + finalizeInitialChildren(domElement, type, props) { debug("finalizeInitialChildren"); return false; }, + insertBefore(parent: Instance, child: Instance, beforeChild: Instance) { debug(`insertBefore ${parent.__id} ${child.__id}`); if (child instanceof Dialog) { return; } const sizer = parent instanceof Sizer ? parent : parent.sizer; - const args = [sizer.indexOf(beforeChild), child, child.stretchFactor]; + const args: Parameters = [ + sizer.indexOf(beforeChild), + child, + child.stretchFactor, + ]; if (!(child instanceof Sizer)) { args.push(child.alignment); } child.parentSizer = sizer; - sizer.insert(...args); + insertChild(sizer, ...args); }, + removeChild(parent: Extended, child: Extended) { debug(`removeChild ${parent.__id} ${child.__id}`); if (child instanceof Dialog) { return; } const sizer = parent instanceof Sizer ? parent : parent.sizer; - sizer.remove(child); - child.parent = garbage; + + removeChild(sizer, child); }, removeChildFromContainer( parent: Extended, @@ -208,8 +281,7 @@ const PixInsightReconciler = Reconciler({ return; } const sizer = parent instanceof Sizer ? parent : parent.sizer; - sizer.remove(child); - child.parent = garbage; + removeChild(sizer, child); }, prepareUpdate(control: Extended, type, oldProps, newProps) { debug(`prepareUpdate ${control.__id} ${type}`); @@ -220,17 +292,29 @@ const PixInsightReconciler = Reconciler({ updatePayload: any, type: string, oldProps: any, - newProps: any + allNewProps: any ) { + const { + type: controlType, + ctor, + constructorProps, + children, + ...newProps + } = allNewProps; + debug(`commitUpdate ${control.__id} ${type}`); Object.keys(newProps).forEach((propName) => { const propValue = newProps[propName]; if (sizerPropsSet.has(propName as any)) { const sizer = control instanceof Sizer ? control : control.sizer; - sizer[propName as keyof Sizer] = propValue; + if (sizer[propName as SizerProps] !== propValue) { + sizer[propName as SizerProps] = propValue; + }; } else { - (control as any)[propName] = propValue; + if ((control as any)[propName] !== propValue) { + (control as any)[propName] = propValue; + } } }); if (newProps.stretchFactor !== oldProps.stretchFactor) { @@ -286,11 +370,11 @@ export function render( sizer.dialog = dialog; dialog.sizer = sizer; - global.setTimeout = function (cb: () => void, ms: number) { + global.setTimeout = function(cb: () => void, ms: number) { var timer = new Timer(); timer.interval = ms / 1000; timer.periodic = false; - timer.onTimeout = function () { + timer.onTimeout = function() { cb(); }; timer.start();