add basic module

This commit is contained in:
Bowen Tan 2021-10-28 15:46:39 +08:00
parent 8363143459
commit c682e3b360
5 changed files with 250 additions and 7 deletions

View File

@ -3,3 +3,4 @@ export * from './trait';
export * from './scope';
export * from './application';
export * from './method';
export * from './module';

View File

@ -0,0 +1,33 @@
import { JSONSchema7Object } from 'json-schema';
import { parseVersion } from './version';
import { Metadata } from './metadata';
import { Version } from './version';
import { ApplicationComponent } from './application';
// spec
export type Module = {
version: string;
kind: 'Module';
metadata: Metadata;
spec: ModuleSpec;
};
type ModuleSpec = {
components: ApplicationComponent[];
properties: JSONSchema7Object;
events: string[];
};
// extended runtime
export type RuntimeModule = Module & {
parsedVersion: Version;
};
export function createModule(options: Omit<Module, 'kind'>): RuntimeModule {
return {
...options,
kind: 'Module',
parsedVersion: parseVersion(options.version),
};
}

View File

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>meta-ui runtime example: list component</title>
</head>
<body>
<div id="root"></div>
<script type="module">
import renderApp from '../../src/main.tsx';
const listdata = [
{
id: 1,
name: '马云',
email: 'jack.ma@deck.com',
},
{
id: 2,
name: '马化腾',
email: 'pony.ma@conversation.com',
},
{
id: 3,
name: '李彦宏',
email: 'robin.li@response.com',
},
{
id: 4,
name: '张一鸣',
email: 'yiming.zhang@example.com',
},
{
id: 5,
name: '王兴',
email: 'xing.wang@widget.org',
},
];
renderApp({
version: 'example/v1',
metadata: {
name: 'list_component',
description: 'list component example',
},
spec: {
components: [
{
id: 'root',
type: 'chakra_ui/v1/root',
properties: {},
traits: [
{
type: 'core/v1/state',
properties: {
key: 'listTitle',
initialValue: '客户列表',
},
},
{
type: 'core/v1/arrayState',
properties: {
key: 'listData',
initialValue: listdata,
},
},
],
},
{
id: 'list',
type: 'chakra_ui/v1/list',
properties: {
listData: '{{ root.listData }}',
template: {
type: 'core/v1/littleItem',
properties: {
$id: '{{$listItem.id}}{{$i}}',
value: '{{$listItem.name}}',
value2: '{{$listItem.email}}',
},
},
},
traits: [
{
type: 'core/v1/slot',
properties: {
container: {
id: 'root',
slot: 'root',
},
},
},
],
},
],
},
});
</script>
</body>
</html>

View File

@ -34,7 +34,8 @@ const List: ComponentImplementation<Static<typeof PropsSchema>> = ({
return null;
}
const itemElementMemo = useRef(new Map());
const parsedtemplete = template.map(parseTypeComponents);
const moduleTemplate = services.registry.getModuleByType(template.type);
const parsedtemplete = moduleTemplate.spec.components.map(parseTypeComponents);
const listItems = listData.map((listItem, i) => {
// this memo only diff listItem, dosen't compare expressions
@ -44,8 +45,8 @@ const List: ComponentImplementation<Static<typeof PropsSchema>> = ({
}
}
const evaledTemplate = services.stateManager.mapValuesDeep(
{ parsedtemplete },
const evaledModuleProperties = services.stateManager.mapValuesDeep(
template.properties,
({ value }) => {
if (typeof value === 'string') {
return services.stateManager.maskedEval(value, true, {
@ -55,10 +56,20 @@ const List: ComponentImplementation<Static<typeof PropsSchema>> = ({
}
return value;
}
).parsedtemplete;
);
const eventModuleTemplate = services.stateManager.mapValuesDeep(
{ template: parsedtemplete },
({ value }) => {
if (typeof value === 'string') {
return services.stateManager.maskedEval(value, true, evaledModuleProperties);
}
return value;
}
).template;
const { topLevelComponents, slotComponentsMap } = resolveAppComponents(
evaledTemplate,
eventModuleTemplate,
{
services,
app,
@ -96,7 +107,10 @@ const List: ComponentImplementation<Static<typeof PropsSchema>> = ({
const PropsSchema = Type.Object({
listData: Type.Array(Type.Record(Type.String(), Type.String())),
template: Type.Array(Type.Any()),
template: Type.Object({
type: Type.String(),
properties: Type.Object({}),
}),
});
const exampleProperties = {

View File

@ -1,4 +1,4 @@
import { RuntimeComponent, RuntimeTrait } from '@meta-ui/core';
import { RuntimeComponent, RuntimeTrait, RuntimeModule } from '@meta-ui/core';
// components
/* --- plain --- */
import PlainButton from '../components/plain/Button';
@ -56,9 +56,77 @@ type ImplementedRuntimeTrait = RuntimeTrait & {
impl: TraitImplementation;
};
const exampleModule: RuntimeModule = {
version: 'core/v1',
kind: 'Module',
parsedVersion: {
category: 'core/v1',
value: 'littleItem',
},
metadata: {
name: 'littleItem',
},
// name: 'littleItem',
spec: {
components: [
{
id: '{{$id}}hstack',
type: 'chakra_ui/v1/hstack',
properties: {},
traits: [],
},
{
id: '{{$id}}1',
type: 'core/v1/text',
properties: {
value: {
raw: '**{{value}}**',
format: 'md',
},
},
traits: [
{
type: 'core/v1/slot',
properties: {
container: {
id: '{{$id}}hstack',
slot: 'content',
},
},
},
],
},
{
id: '{{$id}}2',
type: 'core/v1/text',
properties: {
value: {
raw: '**{{value2}}**',
format: 'md',
},
},
traits: [
{
type: 'core/v1/slot',
properties: {
container: {
id: '{{$id}}hstack',
slot: 'content',
},
},
},
],
},
],
properties: {},
events: [],
},
};
export class Registry {
components: Map<string, Map<string, ImplementedRuntimeComponent>> = new Map();
traits: Map<string, Map<string, ImplementedRuntimeTrait>> = new Map();
modules: Map<string, Map<string, RuntimeModule>> = new Map();
registerComponent(c: ImplementedRuntimeComponent) {
if (this.components.get(c.version)?.has(c.metadata.name)) {
@ -119,6 +187,31 @@ export class Registry {
}
return res;
}
registerModule(c: RuntimeModule) {
if (this.modules.get(c.version)?.has(c.metadata.name)) {
throw new Error(
`Already has module ${c.version}/${c.metadata.name} in this registry.`
);
}
if (!this.modules.has(c.version)) {
this.modules.set(c.version, new Map());
}
this.modules.get(c.version)?.set(c.metadata.name, c);
}
getModule(version: string, name: string): RuntimeModule {
const m = this.modules.get(version)?.get(name);
if (!m) {
throw new Error(`Module ${version}/${name} has not registered yet.`);
}
return m;
}
getModuleByType(type: string): RuntimeModule {
const { version, name } = parseType(type);
return this.getModule(version, name);
}
}
export function initRegistry(): Registry {
@ -162,5 +255,7 @@ export function initRegistry(): Registry {
registry.registerTrait(CoreFetch);
registry.registerTrait(CoreValidation);
registry.registerModule(exampleModule);
return registry;
}