@difizen/mana-core
Version:
94 lines (80 loc) • 2.77 kB
text/typescript
import type { Contribution } from '@difizen/mana-syringe';
import { contrib, singleton, Syringe } from '@difizen/mana-syringe';
import type { JSONSchemaType } from 'ajv';
import type React from 'react';
import { ApplicationContribution } from '../application';
import type { ConfigurationNode } from './configuration-protocol';
export function notFalse(x: number | false): x is number {
return x !== false;
}
export const ConfigurationRenderContribution = Syringe.defineToken(
'ConfigurationRenderContribution',
);
export interface RenderProps<T = any> {
label: string | undefined;
value: T;
onChange: (value: T) => void;
schema: JSONSchemaType<T>;
}
export interface ConfigurationRender<T = any> {
/**
* false 不处理;
* number 表示优先级
*/
canHandle: (configuration: ConfigurationNode<T>) => false | number;
component: React.ComponentType<RenderProps<T>>;
}
export interface ConfigurationRenderContribution {
registerConfigurationRenders: () => ConfigurationRender[];
}
({ contrib: [ApplicationContribution] })
export class ConfigurationRenderRegistry implements ApplicationContribution {
protected renders: ConfigurationRender[] = [];
protected providers: Contribution.Provider<ConfigurationRenderContribution>;
constructor(
(ConfigurationRenderContribution)
providers: Contribution.Provider<ConfigurationRenderContribution>,
) {
this.providers = providers;
}
async onStart() {
await this.setupRenderContribution();
}
protected async setupRenderContribution() {
this.providers.getContributions().forEach((contribution) => {
if (contribution.registerConfigurationRenders) {
const renders = contribution.registerConfigurationRenders();
renders.forEach((render) => {
this.registerConfigurationRender(render);
});
}
});
}
registerConfigurationRender(render: ConfigurationRender) {
this.renders.push(render);
}
getConfigurationRender<T>(
configuration: ConfigurationNode<T>,
): React.ComponentType<RenderProps<T>> | null {
const validRender = this.renders
.map((render) => {
return {
...render,
priority: render.canHandle(configuration as ConfigurationNode<any>),
};
})
.filter((render) => notFalse(render.priority));
if (validRender.length === 0) {
console.warn(`found no component for ${configuration.type}`);
return null;
}
// 相比sort性能更高
let maxPriorityRender = validRender[0];
for (const render of validRender) {
if ((render.priority as number) > (maxPriorityRender.priority as number)) {
maxPriorityRender = render;
}
}
return maxPriorityRender.component;
}
}