Skip to content

Commit

Permalink
feat(Select): add support of function extra
Browse files Browse the repository at this point in the history
  • Loading branch information
ZxBing0066 committed Jul 19, 2021
1 parent c7d8a52 commit 835c688
Show file tree
Hide file tree
Showing 15 changed files with 604 additions and 41 deletions.
201 changes: 201 additions & 0 deletions .recodo/InteractionDemo.tsx
@@ -0,0 +1,201 @@
import React, { useState, useCallback, createContext, ReactElement, useContext, useMemo } from 'react';
import { Switch, Radio, Input, Button } from '@ucloud-fe/react-components';

import { Config, ConfigInfo } from './interface';

export const USE_DEFINE = 'USE_DEFINE';

const isIgnoreProp = (prop: { description?: { tags?: { title: string; description: string }[] } }) => {
if (prop.description?.tags?.length) {
return !!prop.description.tags.find(tag => tag.title === 'ignore');
}
return false;
};

const fixString = (str: string) => str.slice(1, -1);

const Context = createContext<any>({ props: {} });

// eslint-disable-next-line react/display-name
const Action = React.memo(
({
name,
value,
onChange,
type,
options
}: {
name: string;
value: any;
onChange?: (v: any, name: string) => void;
type: string;
options?: [];
}) => {
const handleChange = useCallback(
v => {
if (type === 'string') {
v = v.target.value;
}
onChange?.(v, name);
},
[name, onChange, type]
);
switch (type) {
case 'union': {
return (
<Radio.Group
value={value}
styleType="button"
onChange={handleChange}
options={options?.map(v => (typeof v !== 'object' ? { value: v } : v))}
/>
);
}
case 'string': {
return <Input value={value || ''} onChange={handleChange} />;
}
case 'boolean': {
return <Switch checked={!!value} onChange={handleChange} />;
}
default: {
return null;
}
}
}
);

const style = `
.interaction-demo {
display: flex;
}
.interaction-demo .demo-area {
flex: 1;
}
.interaction-demo .action-area {
width: 250px;
flex-shrink: 0;
border-left: 1px solid #EFEFF8;
padding-left: 8px;
}
.interaction-demo .action-area > div {
margin: 4px 0;
}
.interaction-demo .action-area > div > h4 {
margin: 8px 0;
}
.interaction-demo .action-area .reset {
margin-top: 8px;
border-top: 1px solid #EFEFF8;
padding-top: 8px;
}
.interaction-demo .action-title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`;

const InteractionDemo = ({
config,
initialState = {},
children
}: {
config: Config;
initialState: Record<string, any>;
children: ReactElement;
}) => {
const { props: propsInfo } = useContext(Context);
const [finalDemoPropsDefine] = useMemo(() => {
const finalProps = {};
Object.keys(config).map(name => {
let prop = config[name];
let finalProp: ConfigInfo = { type: 'unknown' };
const propInfo = propsInfo[name];
if (prop === USE_DEFINE) {
switch (propInfo.tsType.name) {
case 'boolean': {
finalProp = { type: 'boolean' };
break;
}
case 'string': {
finalProp = { type: 'string' };
break;
}
case 'union': {
finalProp = {
type: 'union',
options: propInfo.tsType.elements.map((element: { value: string }) =>
fixString(element.value)
)
};
break;
}
default: {
console.warn(name, propInfo);
}
}
} else if (typeof prop === 'string') {
finalProp = { type: prop };
} else if (Array.isArray(prop)) {
finalProp = { type: 'union', options: prop };
} else {
finalProp = prop;
}
finalProp.desc = propInfo?.description?.description;
finalProps[name] = finalProp;
});
return [finalProps];
}, []);
const [componentState, setComponentState] = useState(initialState);
const handleValueChange = useCallback(
(value, key) => {
setComponentState({ ...componentState, [key]: value });
},
[componentState]
);
const handleReset = useCallback(() => {
setComponentState(initialState);
}, [initialState]);
const componentProps = {
...componentState
};
Object.keys(finalDemoPropsDefine).map(name => {
let prop = config[name];
if (prop.optionToProps) {
componentProps[name] = prop.optionToProps(componentProps[name]);
}
});
return (
<div className="interaction-demo">
<style>{style}</style>
<div className="demo-area">{React.cloneElement(children, componentProps)}</div>
<div className="action-area">
{Object.keys(finalDemoPropsDefine).map(name => {
let prop = finalDemoPropsDefine[name];
const title = `${name} ${prop.desc ? `- ${prop.desc}` : ''}`;
return (
<div key={name}>
<h4 className="action-title" title={title}>
{title}
</h4>
<Action {...prop} name={name} value={componentState[name]} onChange={handleValueChange} />
</div>
);
})}
<div className="reset">
<Button styleType="primary" onClick={handleReset}>
Reset
</Button>
</div>
</div>
</div>
);
};

export default InteractionDemo;

const Provider = ({ props, children }: { props: any; children: any }) => {
return <Context.Provider value={{ props }}>{children}</Context.Provider>;
};

export { Provider };
4 changes: 2 additions & 2 deletions .recodo/index.html
Expand Up @@ -5,15 +5,15 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<title>recodo</title>
</head>

<body>
<div id="app"></div>
<script>
window.onload = () => {
console.log(window['react-components-docs']);
const destroy = window['react-components-docs'].renderDoc('Input', document.getElementById('app'));
const destroy = window['react-components-docs'].renderInteractionDemo('Button', document.getElementById('app'));
}
</script>
</body>
Expand Down
133 changes: 111 additions & 22 deletions .recodo/index.tsx
@@ -1,21 +1,25 @@
import mod from '@rapiop/mod';
import amdResolver from '@rapiop/mod/lib/resolver/amd';
import jsonResolver from '@rapiop/mod/lib/resolver/json';
import { moduleMap } from '@rapiop/mod/lib/module';

import './index.css';

(window as any).__recodo_module_namespace__ = moduleMap;

mod.registerModuleResolver(amdResolver);
mod.registerModuleResolver(jsonResolver);

(window as any).__recodo_module_namespace__ = moduleMap;

mod.import({
css: 'https://cdn.jsdelivr.net/npm/@ucloud-fe/react-components@1.1.6/dist/icon.min.css'
css: 'https://cdn.jsdelivr.net/npm/@ucloud-fe/react-components/dist/icon.min.css'
});

mod.export('@rapiop/mod', mod);

mod.config({
modules: {
'@ucloud-fe/react-components': {
js: 'https://cdn.jsdelivr.net/npm/@ucloud-fe/react-components@1.1.6/dist/main.min.js',
js: 'https://cdn.jsdelivr.net/npm/@ucloud-fe/react-components/dist/main.min.js',
type: 'amd',
dep: ['moment', 'react', 'react-dom']
},
Expand All @@ -37,36 +41,121 @@ mod.config({
},
'react-dom': {
js: 'https://cdn.jsdelivr.net/npm/react-dom@16.14.0/umd/react-dom.production.min.js',
type: 'amd'
type: 'amd',
dep: ['react']
},
'recodo-doc': {
js: 'https://cdn.jsdelivr.net/npm/recodo-doc@0.1.8/dist/main.min.js',
type: 'amd',
dep: ['react']
},
'@rapiop/mod': { js: [] },
'examples-data': {
file: 'https://raw.githubusercontent.com/UCloud-FE/react-components/master/.recodo/data/examples.json',
type: 'json'
},
'docs-data': {
file: 'https://raw.githubusercontent.com/UCloud-FE/react-components/master/.recodo/data/docs.json',
type: 'json'
}
}
});

const renderDoc = (name, dom: Element) => {
let destroyAction;
const renderDoc = (name: string, dom: Element) => {
let destroyAction: () => void;
let destroyed = false;
let destroy = () => {
if (destroyed) return;
destroyed = true;
if (!destroyAction) return;
destroyAction();
};
mod.import([
'@ucloud-fe/react-components',
'moment',
'lodash',
'react',
'react-dom',
'prop-types',
'examples-data',
'docs-data',
'recodo-doc'
]).then(dependences => {
if (destroyed) return;
const [components, moment, lodash, React, ReactDOM, PropTypes, examples, docs] = dependences as any;
const { Doc } = require('./run');
const demoUtil = require('shared/demoUtil').default;
console.log(examples);

ReactDOM.render(
<Doc
name={name}
examples={examples}
docs={docs}
scope={{
...components,
components,
moment,
lodash,
React,
ReactDOM,
PropTypes: PropTypes,
_: lodash,
demoUtil
}}
/>,
dom
);
destroyAction = () => ReactDOM.unmountComponentAtNode(dom);
});
return destroy;
};

const renderInteractionDemo = (name: string, dom: string) => {
let destroyAction: () => void;
let destroyed = false;
let destroy = () => {
if (destroyed) return;
destroyed = true;
if (!destroyAction) return;
destroyAction();
};
mod.import(['@ucloud-fe/react-components', 'moment', 'lodash', 'react', 'react-dom', 'prop-types']).then(
dependences => {
if (destroyed) return;
const [components, moment, lodash, React, ReactDOM, PropTypes] = dependences as any;
const { Doc } = require('./run');
ReactDOM.render(
<Doc
mod.import([
'@ucloud-fe/react-components',
'moment',
'lodash',
'react',
'react-dom',
'prop-types',
'examples-data',
'docs-data',
'recodo-doc'
]).then(dependences => {
if (destroyed) return;
const [components, moment, lodash, React, ReactDOM, PropTypes, examples, docs] = dependences as any;
const { Demo } = require('./run');
const InteractionDemo = require('./InteractionDemo');

ReactDOM.render(
<InteractionDemo.Provider props={examples[name][name].info.props}>
<Demo
name={name}
scope={{ ...components, components, moment, lodash, React, ReactDOM, PropTypes, _: lodash }}
/>,
dom
);
destroyAction = () => ReactDOM.unmountComponentAtNode(dom);
}
);
modules={{
'@ucloud-fe/react-components': components,
moment: moment,
lodash: lodash,
react: React,
'react-dom': ReactDOM,
'prop-types': PropTypes,
'interaction-demo': InteractionDemo
}}
/>
</InteractionDemo.Provider>,
dom
);
destroyAction = () => ReactDOM.unmountComponentAtNode(dom);
});
return destroy;
};

export { renderDoc };
export { renderDoc, renderInteractionDemo };
9 changes: 9 additions & 0 deletions .recodo/interface.ts
@@ -0,0 +1,9 @@
export interface ConfigInfo {
type: string;
options?: [];
optionToProps?: (option: any) => any;
}

export interface Config {
[key: string]: string | [] | ConfigInfo;
}

0 comments on commit 835c688

Please sign in to comment.