Skip to content

Commit

Permalink
✨ chore: add c2d2c implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
rdmclin2 committed Jul 31, 2023
1 parent 9239fa7 commit 15acf3d
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 16 deletions.
63 changes: 63 additions & 0 deletions src/types/c2d2c.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
export interface BoundBox {
width: number;
height: number;
}

export type SizeFollow = 'sketch' | 'self';

/**
* 组件尺寸控制
*/
export interface ComponentSize extends Partial<BoundBox> {
widthFollow?: SizeFollow;
heightFollow?: SizeFollow;
}

export type VerticalType = 'top' | 'center' | 'bottom';
export type HorizontalType = 'left' | 'center' | 'right';

export interface Alignment {
/**
* 横向位置
*/
vertical: VerticalType;
/**
* 纵向位置
*/
horizontal: HorizontalType;
/**
* 是否翻转相应的坐标系
*/
verticalFlipped?: boolean;
}

/**
* C2D 组件预设值
*/
export interface C2DPresetValue {
/**
* 组件名 componentName
*/
componentName: string;
/**
* 组件属性
*/
props: any;
/**
* 对齐方式
*/
alignment?: Alignment;
/**
* 组件大小
*/
size?: ComponentSize;
/**
* 如果存在配置属性,那么存在这里持久化保存
*/
config?: any;
}

export interface ReactNodeElement {
$$__type: 'element';
$$__body: C2DPresetValue;
}
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './c2d2c';
export * from './catogory';
export * from './field';
export * from './schema';
135 changes: 135 additions & 0 deletions src/utils/c2d2c.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { JSONSchema } from '@/types/schema';
import { isEmpty, isNil, omitBy, uniq } from 'lodash';

Check failure on line 2 in src/utils/c2d2c.tsx

View workflow job for this annotation

GitHub Actions / test

Cannot find module 'lodash' or its corresponding type declarations.

Check failure on line 2 in src/utils/c2d2c.tsx

View workflow job for this annotation

GitHub Actions / test

Cannot find module 'lodash' or its corresponding type declarations.
import { ReactNodeElement } from '../types';

/**
* 从schema 获取预设值
* @param schema
*/
export const getDefaultValueFromSchema = (schema: JSONSchema) => {
if (schema.type === 'object') {
if (!schema.properties) return;
return Object.fromEntries(
Object.entries(schema.properties).map(([key, value]) => [key, value.default]),
);
}
if (schema.type === 'null') return null;
return schema.default;
};

/**
* 获取组件库导入代码
*/
export const generateImportCode = (pkg: string, components: string[]) => {
return `import { ${uniq(components).join(', ')} } from '${pkg}';`;
};

/**
* 将 prop 转换成字符串
*/
export const createPropString = (
key: string,
// eslint-disable-next-line @typescript-eslint/ban-types
value: string | number | boolean | symbol | object | undefined | Function | any[],
) => {
switch (typeof value) {
case 'undefined':
return '';
case 'object':
// 数组
if (value instanceof Array) {
return `${key}={${JSON.stringify(value)}}`;
}

// eslint-disable-next-line no-case-declarations
const clearValue = omitBy(value, isNil);
// 如果 object 里不存在任何值,返回空
if (Object.values(clearValue).length === 0) return '';

// eslint-disable-next-line no-case-declarations
const genObjStr = () => {
// 如果包含 $$__type 属性,说明是 ReactNode 或 icon
if ((value as ReactNodeElement).$$__type) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return genChildrenJSXCode(value as ReactNodeElement);
}

return JSON.stringify(clearValue, null);
};

return `${key}={${genObjStr()}}`;
case 'boolean':
if (value) return `${key}`;

return `${key}={${value}}`;
case 'number':
return `${key}={${value}}`;
case 'string':
if (isEmpty(value)) return '';

return `${key}="${value}"`;
case 'function':
return `${key}={${value.toString()}}`;
case 'symbol':
return `${key}={Symbol.for('${value.description}')}`;
}
};

/**
* 生成 React JSX 代码
* @param component
* @param props
* @param PropStringFn
*/
export const generateJSXCode = (
component: string,
props: Record<string, any>,
PropStringFn = createPropString,
) => {
if (!props) {
return `<${component} />`;
}

const inline = !props.children;

const propsStr = Object.entries(props)
// 针对 children 有值的情况下,在 props 上过滤掉 children
.filter((v) => (inline ? v : v[0] !== 'children'))
.map((entry) => PropStringFn(entry[0], entry[1]))
// 过滤空的对象
.filter((v) => v)
.join(' ');

if (inline) return `<${component} ${propsStr}/>`;

// eslint-disable-next-line @typescript-eslint/no-use-before-define
return `<${component} ${propsStr}>${genChildrenJSXCode(props.children)}</${component}>`;
};

/**
* 生成子 JSX 代码
* @param children
*/
const genChildrenJSXCode = (children: string | ReactNodeElement | ReactNodeElement[]): string => {
// children 为字符串的场景
if (typeof children === 'string') {
return children;
}

const renderChildNode = (child: ReactNodeElement) => {
const { $$__type, $$__body } = child;

switch ($$__type) {
// children 为子组件的场景
case 'element':
return generateJSXCode($$__body.componentName, $$__body.props);
// TODO: children 为 Icon 的场景
}
};

if (children instanceof Array) {
return children.map(renderChildNode).join('\n');
}

return renderChildNode(children);
};
2 changes: 1 addition & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './autoId';
export * from './schema';
export * from './c2d2c';
15 changes: 0 additions & 15 deletions src/utils/schema.ts

This file was deleted.

0 comments on commit 15acf3d

Please sign in to comment.