Skip to content

Commit

Permalink
Merge branch 'runtime_dep_25_1' of https://github.com/tongsonbarbs/De…
Browse files Browse the repository at this point in the history
…vExtreme into runtime_dep_25_1
  • Loading branch information
tongsonbarbs committed Mar 6, 2025
2 parents 384dd37 + ebf6db5 commit 00d9775
Show file tree
Hide file tree
Showing 78 changed files with 816 additions and 106 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { hydrate, InfernoEffectHost } from '@devextreme/runtime/inferno';
import domAdapter from '@js/core/dom_adapter';
import { cleanDataRecursive } from '@js/core/element_data';
import injector from '@js/core/utils/dependency_injector';
import { hydrate, InfernoEffectHost } from '@ts/core/r1/runtime/inferno/index';
import { render } from 'inferno';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createElement } from 'inferno-create-element';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext } from '@devextreme/runtime/inferno';
import { createContext } from '@ts/core/r1/runtime/inferno/index';

export interface ConfigContextValue { rtlEnabled?: boolean }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { BaseInfernoComponent } from '@devextreme/runtime/inferno';
import { BaseInfernoComponent } from '@ts/core/r1/runtime/inferno/index';

import { ConfigContext, type ConfigContextValue } from './config_context';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-unsafe-return */

/* eslint-disable @typescript-eslint/no-explicit-any */
import type { ComponentClass } from '@js/core/dom_component';
import {
hasTemplate, InfernoComponent, InfernoEffect, renderTemplate,
} from '@devextreme/runtime/inferno';
import type { ComponentClass } from '@js/core/dom_component';
} from '@ts/core/r1/runtime/inferno/index';
import type { DisposeEffectReturn } from '@ts/core/r1/utils/effect_return';
import { getUpdatedOptions } from '@ts/core/r1/utils/get_updated_options';
import type { RefObject } from 'inferno';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable spellcheck/spell-checker */
/* eslint-disable max-classes-per-file */
import { Component, findDOMfromVNode } from 'inferno';

import type { InfernoEffect } from './effect';
import { InfernoEffectHost } from './effect_host';

const areObjectsEqual = (firstObject: any, secondObject: any) => {
const bothAreObjects = firstObject instanceof Object && secondObject instanceof Object;
if (!bothAreObjects) {
return firstObject === secondObject;
}

const firstObjectKeys = Object.keys(firstObject);
const secondObjectKeys = Object.keys(secondObject);
if (firstObjectKeys.length !== secondObjectKeys.length) {
return false;
}

const hasDifferentElement = firstObjectKeys.some(
(key) => firstObject[key] !== secondObject[key],
);
return !hasDifferentElement;
};

export class BaseInfernoComponent<
P = Record<string, unknown>,
S = Record<string, unknown>,
> extends Component<P, S> {
_pendingContext: any = this.context;

componentWillReceiveProps(_: any, context: any): void {
this._pendingContext = context ?? {};
}

shouldComponentUpdate(nextProps: P, nextState: S): boolean {
return (
!areObjectsEqual(this.props, nextProps)
|| !areObjectsEqual(this.state, nextState)
|| !areObjectsEqual(this.context, this._pendingContext)
);
}
}

export class InfernoComponent<
P = Record<string, unknown>,
S = Record<string, unknown>,
> extends BaseInfernoComponent<P, S> {
_effects: InfernoEffect[] = [];

createEffects(): InfernoEffect[] {
return [];
}

updateEffects(): void {}

componentWillMount(): void {
InfernoEffectHost.lock();
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
componentWillUpdate(_nextProps?: P, _nextState?: S, _context?: any): void {
InfernoEffectHost.lock();
}

componentDidMount(): void {
InfernoEffectHost.callbacks.push(
() => { this._effects = this.createEffects(); },
);
InfernoEffectHost.callEffects();
}

componentDidUpdate(): void {
InfernoEffectHost.callbacks.push(() => this.updateEffects());
InfernoEffectHost.callEffects();
}

destroyEffects(): void {
this._effects.forEach((e) => e.dispose());
}

componentWillUnmount(): void {
this.destroyEffects();
}
}

interface VDomCustomClassesData {
previous: string[];
removed: string[];
added: string[];
}

type ElementWithCustomClassesData = Element & {
dxClasses: VDomCustomClassesData;
};
export class InfernoWrapperComponent<
P = Record<string, unknown>,
S = Record<string, unknown>,
> extends InfernoComponent<P, S> {
vDomElement: ElementWithCustomClassesData | null = null;

vDomUpdateClasses(): void {
const el = this.vDomElement as ElementWithCustomClassesData;
const currentClasses = el.className.length
? el.className.split(' ')
: [];
const addedClasses = currentClasses.filter(
(className) => !el.dxClasses.previous.includes(className),
);
const removedClasses = el.dxClasses.previous.filter(
(className: string): boolean => !currentClasses.includes(className),
);

addedClasses.forEach((value: string): void => {
const indexInRemoved = el.dxClasses.removed.indexOf(value);
if (indexInRemoved > -1) {
el.dxClasses.removed.splice(indexInRemoved, 1);
} else if (!el.dxClasses.added.includes(value)) {
el.dxClasses.added.push(value);
}
});

removedClasses.forEach((value: string): void => {
const indexInAdded = el.dxClasses.added.indexOf(value);
if (indexInAdded > -1) {
el.dxClasses.added.splice(indexInAdded, 1);
} else if (!el.dxClasses.removed.includes(value)) {
el.dxClasses.removed.push(value);
}
});
}

componentDidMount(): void {
const el = findDOMfromVNode(this.$LI, true) as ElementWithCustomClassesData;
this.vDomElement = el;
super.componentDidMount();
el.dxClasses = el.dxClasses || {
removed: [], added: [], previous: [],
};
el.dxClasses.previous = el?.className.length
? el.className.split(' ')
: [];
}

componentDidUpdate(): void {
super.componentDidUpdate();

const el = this.vDomElement;

if (el !== null) {
el.dxClasses.added.forEach((className: string): void => el.classList.add(className));
el.dxClasses.removed.forEach((className: string): void => el.classList.remove(className));
el.dxClasses.previous = el.className.length
? el.className.split(' ')
: [];
}
}

shouldComponentUpdate(nextProps: P, nextState: S): boolean {
const shouldUpdate = super.shouldComponentUpdate(nextProps, nextState);
if (shouldUpdate) {
this.vDomUpdateClasses();
}
return shouldUpdate;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable no-plusplus */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Component } from 'inferno';

let contextId = 0;
export const createContext = function<T>(defaultValue: T): { id: number;
Provider: any;
defaultValue: unknown; } {
const id = contextId++;

return {
id,
defaultValue,
Provider: class extends Component<{ value: T }> {
getChildContext() {
return {
...this.context,
[id]: this.props.value || (defaultValue as unknown as T),
};
}

render() {
return this.props.children;
}
},

};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable @typescript-eslint/array-type */
/* eslint-disable @typescript-eslint/prefer-readonly */
/* eslint-disable @typescript-eslint/no-invalid-void-type */
export class InfernoEffect {
private destroy?: (() => void) | void;

private effect: () => (() => void) | void;

private dependency: Array<unknown>;

constructor(
effect: () => (() => void) | void,
dependency: Array<unknown>,
) {
this.dependency = dependency;
this.effect = effect;
this.destroy = effect();
}

update(dependency?: Array<unknown>): void {
const currentDependency = this.dependency;
if (dependency) {
this.dependency = dependency;
}
if (!dependency || dependency.some((d, i) => currentDependency[i] !== d)) {
this.dispose();
this.destroy = this.effect();
}
}

dispose(): void {
if (this.destroy) {
this.destroy();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable no-plusplus */
/* eslint-disable @typescript-eslint/array-type */
export const InfernoEffectHost: {
lockCount: number;
lock: () => void;
callbacks: Array<() => void>;
callEffects: () => void;
} = {
lockCount: 0,
lock() {
this.lockCount++;
},

callbacks: [],

callEffects() {
this.lockCount--;
if (this.lockCount < 0) {
throw new Error('Unexpected Effect Call');
}
if (this.lockCount === 0) {
const effects = this.callbacks;
this.callbacks = [];
effects.forEach((callback) => callback());
}
},
};
10 changes: 10 additions & 0 deletions packages/devextreme/js/__internal/core/r1/runtime/inferno/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export * from './base_component';
export * from './create_context';
export * from './effect';
export * from './effect_host';
export * from './mocked/hydrate';
export * from './normalize_styles';
export * from './portal';
export * from './re_render_effect';
export type { RefObject } from './ref_object';
export * from './render_template';
Loading

0 comments on commit 00d9775

Please sign in to comment.