lodash#isPlainObject TypeScript Examples
The following examples show how to use
lodash#isPlainObject.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts From gant-design with MIT License | 7 votes |
isEqualObj = (obj, obj2) => {
let _EqualObj = true;
if (!isPlainObject(obj) || !isPlainObject(obj2)) {
if (isEmptyObj(obj) && isEmptyObj(obj2)) return true;
return isEqual(obj, obj2);
}
if (isNull(obj) && isNull(obj2)) return true;
if (isNull(obj) || isNull(obj2)) {
return false;
}
const newObj = { ...obj, ...obj2 };
for (let i in newObj) {
let value1 = get(obj, i),
value2 = get(obj2, i);
if (
typeof value1 === 'object' &&
typeof value2 === 'object' &&
!Array.isArray(value1) &&
!Array.isArray(value2)
) {
_EqualObj = isEqualObj(value1, value2);
} else {
if (!(isEmptyObj(value1) && isEmptyObj(value2))) {
value2 = typeof value2 == 'number' ? value2 : value2 + '';
value1 = typeof value1 == 'number' ? value1 : value1 + '';
_EqualObj = isEqual(value2, value1);
}
}
if (!_EqualObj) {
return _EqualObj;
}
}
return true;
}
Example #2
Source File: request.ts From generator-earth with MIT License | 6 votes |
export function getUrlWithParamsAndUuid(url: string, params?: IAnyObject): string {
if (!params || !isPlainObject(params)) params = {}
let conn: string
if (url.indexOf('?') === -1) {
conn = '?'
} else if (/\?$/.test(url)) {
conn = ''
} else {
conn = '&'
}
params.reqid = uuidv4()
return `${url}${conn}${stringifyParams(params)}`
}
Example #3
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
ReadonlyForm = ({ fieldsList, data }: IReadonlyProps) => {
const readonlyView: JSX.Element[] = [];
forEach(fieldsList, (item, index) => {
const { itemProps, label, name, viewType, getComp } = item;
if (!(itemProps && itemProps.type === 'hidden') && label) {
const value = name === undefined && getComp ? getComp() : get(data, name || '');
if (value !== undefined && value !== null && value !== '') {
if (viewType === 'image') {
readonlyView.push(
<Form.Item label={label} key={index}>
<Avatar shape="square" src={value} size={100} />
</Form.Item>,
);
} else {
readonlyView.push(
<Form.Item label={label} key={index}>
<p style={{ wordBreak: 'break-all' }}>
{Array.isArray(value) ? (
map(value, (v: string, i: number) => (
<span key={`${i}${v}`}>
{isPlainObject(v) ? JSON.stringify(v) : v.toString()}
<br />
</span>
))
) : (
<> {value !== undefined ? value.toString() : undefined}</>
)}
</p>
</Form.Item>,
);
}
}
}
});
return <Form layout="vertical">{readonlyView}</Form>;
}
Example #4
Source File: readonly-field.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
ReadonlyField = ({ renderData, style, className, value }: IProps) => {
const realData = React.useMemo(() => {
return renderData
? isFunction(renderData)
? renderData(value)
: renderData
: isPlainObject(value)
? JSON.stringify(value)
: value
? value.toString()
: '-';
}, [renderData, value]);
return (
<div style={{ overflow: 'auto', ...style }} className={className}>
{realData}
</div>
);
}
Example #5
Source File: test-env-detail.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
transObjToList = (obj: object) => {
if (!isPlainObject(obj)) {
return obj;
}
const list: Obj[] = [];
map(obj, (v: string, k: string) => {
list.push({ key: k, value: v });
});
return list;
}
Example #6
Source File: paramsSerializer.ts From linkedin-private-api with MIT License | 6 votes |
paramsSerializer = (params: Record<string, string | Record<string, string>>): string => {
const encodedParams = mapValues(params, value => {
if (!isArray(value) && !isPlainObject(value)) {
return value.toString();
}
if (isArray(value)) {
return `List(${value.join(',')})`;
}
const encodedList = reduce(
value as Record<string, string>,
(res, filterVal, filterKey) => `${res}${res ? ',' : ''}${encodeFilter(filterVal, filterKey)}`,
'',
);
return `List(${encodedList})`;
});
return stringify(encodedParams, undefined, undefined, {
encodeURIComponent: uri => uri,
});
}
Example #7
Source File: test-env-detail.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
transGlobal = (list: any[]) => {
if (!Array.isArray(list)) {
return list;
}
const dataTemp = { type: '', value: '', desc: '' };
const obj = {};
map(list, (item) => {
const { key, value, ...rest } = item || {};
if (key) {
let reItem = {};
if (isPlainObject(value)) {
reItem = { ...dataTemp, ...value, ...rest };
} else {
reItem = {
...dataTemp,
value,
...rest,
};
}
if (!reItem.type) reItem.type = typeMap.auto.string;
obj[key] = reItem;
}
});
return obj;
}
Example #8
Source File: helper.ts From brick-design with MIT License | 6 votes |
export function setVariable(
data: { [propName: string]: any },
key: string,
value: any,
) {
data = data || {};
if (key in data) {
data[key] = value;
return;
}
const parts = keyToPath(key);
const last = parts.pop() as string;
while (parts.length) {
const key = parts.shift() as string;
if (isPlainObject(data[key])) {
data = data[key] = {
...data[key],
};
} else if (Array.isArray(data[key])) {
data[key] = data[key].concat();
data = data[key];
} else if (data[key]) {
// throw new Error(`目标路径不是纯对象,不能覆盖`);
// 强行转成对象
data[key] = {};
data = data[key];
} else {
data[key] = {};
data = data[key];
}
}
data[last] = value;
}
Example #9
Source File: handleStateFields.ts From brick-design with MIT License | 6 votes |
export function getStateFields(data: any, fieldsRet?: string[]) {
const ret = fieldsRet || [];
if (Array.isArray(data)) {
return data.map((item) => getStateFields(item));
} else if (!data) {
return ret;
}
each(data, (value, key) => {
if (value === '$$' || value === '&') {
ret.push(ALL_PROPS);
} else if (key !== '$' && key.includes('$')) {
//todo 字符串方法体
resolveFieldFromExpression(value, ret);
} else if (
typeof value === 'string' &&
/(\\)?\$(?:([a-z0-9_\.]+|&|\$)|{([^}{]+?)})/gi.test(value)
) {
value.replace(
/(\\)?\$(?:([a-z0-9_\.]+|&|\$)|{([^}{]+?)})/gi,
(_, escape) => {
!escape && resolveVariableAndFilterField(_, ret);
return '';
},
);
} else if (isPlainObject(value)) {
getStateFields(value, ret);
}
});
return [...new Set(ret.filter((field) => field !== 'funParams'))];
}
Example #10
Source File: traverse.ts From S2 with MIT License | 6 votes |
traverse = <T>(value: T, seen?: Set<T>) => {
// eslint-disable-next-line dot-notation
if (!isObject(value) || (value as any)['__v_skip']) {
return value;
}
if (!isProxy(value) && !isRef(value)) {
return value;
}
seen = seen || new Set();
if (seen.has(value)) {
return value;
}
seen.add(value);
if (isRef(value)) {
traverse(value.value, seen);
} else if (isArray(value)) {
for (let i = 0; i < value.length; i++) {
traverse(value[i], seen);
}
} else if (isSet(value) || isMap(value)) {
value.forEach((v: any) => {
traverse(v, seen);
});
} else if (isPlainObject(value)) {
for (const key in value as any) {
traverse((value as any)[key], seen);
}
}
return value;
}
Example #11
Source File: compile.ts From fect with MIT License | 6 votes |
compile = async () => {
setNodeENV('production')
const { userConfig, path: configPath } = await resolveConfig()
const { lib } = userConfig
if (!isPlainObject(lib)) {
return logErr(`[Non Error!] you can not use it when your un set library in your config at ${configPath}`)
}
const { format, name, input } = lib
const components = fs
.readdirSync(input)
.filter((v) => v !== 'utils')
.map((_) => {
_ = _.replace(/\-(\w)/g, (_, k: string) => k.toUpperCase())
_ = _.charAt(0).toUpperCase() + _.slice(1)
return _
})
await runTask('CommonJs', () => cjsTask(input, components))
await runTask('EsModule', () => esmTask(input, components))
if (format === 'umd' || format.includes('umd')) {
await runTask('UMD', () => umdTask(input, name))
}
await runTask('Declaration', () => declarationTask(input))
}
Example #12
Source File: model-test-utils.ts From ui5-language-assistant with Apache License 2.0 | 6 votes |
export function isObject(value: unknown): value is Record<string, unknown> {
return isPlainObject(value);
}
Example #13
Source File: validate.ts From ui5-language-assistant with Apache License 2.0 | 6 votes |
export function isLibraryFile(
fileName: string,
fileContent: Json,
strict: boolean,
printValidationErrors: boolean
): fileContent is SchemaForApiJsonFiles {
const valid = validate(fileContent);
// Only printing when requested because the output is very verbose so it should only used for debugging.
/* istanbul ignore if */
if (!valid && printValidationErrors) {
console.log(JSON.stringify(validate.errors, undefined, 2));
}
if (!strict) {
if (!valid) {
// We only return an error in non-strict mode if symbols is not an array
return (
isPlainObject(fileContent) &&
isArray((fileContent as Record<string, unknown>).symbols)
);
}
return true;
}
return valid as boolean;
}
Example #14
Source File: compose.ts From gant-design with MIT License | 6 votes |
objectToPath = (obj: object): Array<string> => {
const paths: Array<string> = []
const inner = (obj: object, parentKey = ''): void => {
Object.keys(obj).forEach(k => {
const combineKey = parentKey ? parentKey + '.' + k : k
const value = obj[k]
if (value && isPlainObject(value) && !moment.isMoment(value)) {
inner(value, combineKey)
} else {
paths.push(combineKey)
}
})
}
inner(obj)
return paths
}
Example #15
Source File: is-mail-server-config.ts From js-client with MIT License | 6 votes |
isMailServerConfig = (v: any): v is MailServerConfig => {
if (isPlainObject(v)) {
return (
isOfTypeOrNil(v.server, isString) &&
isOfTypeOrNil(v.password, isString) &&
isOfTypeOrNil(v.string, isString) &&
isOfTypeOrNil(v.port, isNumber) &&
isOfTypeOrNil(v.useTLS, isBoolean) &&
isOfTypeOrNil(v.insecureSkipVerify, isBoolean)
);
}
return false;
}
Example #16
Source File: fetcher.ts From po8klasie with GNU General Public License v3.0 | 6 votes |
camelCaseKeys = (o: Record<string, unknown>): Record<string, unknown> | unknown[] => {
if (isPlainObject(o)) {
const n: Record<string, unknown> = {};
Object.keys(o).forEach((k) => {
n[camelCase(k)] = camelCaseKeys(o[k] as Record<string, unknown>);
});
return n;
}
if (Array.isArray(o)) {
return o.map((i) => camelCaseKeys(i));
}
return o;
}
Example #17
Source File: test-env-detail.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
KVPairTable = (props: any) => {
const { value, disabled } = props;
const columns = [
{
title: i18n.t('Name'),
dataIndex: 'Key',
width: 200,
},
{
title: i18n.t('dop:Parameter content'),
dataIndex: 'Value',
},
{
title: i18n.t('Operations'),
key: 'op',
width: 80,
render: (_: any, record: any) => (record.isLast ? null : record.Op),
},
];
const _value: any = map(transObjToList(value), (item) => {
const newItem = {};
forEach(item, (v, k) => {
if (isPlainObject(v)) {
forEach(v as Obj, (subV: string, subK: string) => {
newItem[subK] = subV;
});
} else {
newItem[k] = v;
}
});
return newItem;
});
if (props.KeyDescComp) {
columns.splice(2, 0, {
title: i18n.t('Description'),
dataIndex: 'KeyDescComp',
width: 150,
});
}
if (props.DescComp) {
columns.splice(1, 0, {
title: i18n.t('dop:Parameter type'),
dataIndex: 'Desc',
width: 150,
});
}
return (
<KVPair {...props} value={_value} emptyHolder={!disabled} autoAppend={!disabled} compProps={{ disabled }}>
{({ CompList }: any) => {
const data = CompList.map((d: any, index: number) => ({
index,
...CompList[index],
isLast: index === CompList.length - 1,
}));
return <Table rowKey={'index'} columns={columns} pagination={false} dataSource={data} scroll={{ x: '100%' }} />;
}}
</KVPair>
);
}
Example #18
Source File: request.ts From generator-earth with MIT License | 5 votes |
export function stringifyParams(params?: IAnyObject): string {
if (!isPlainObject(params)) params = {}
const _params = formatParams(params, (value) => (typeof value === 'object' ? JSON.stringify(value) : value))
return Object.keys(_params).map((key) => (key + '=' + encodeURIComponent(_params[key]))).join('&')
}
Example #19
Source File: combiner.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
export function createCombiner<P, O>({
CombinerItem,
valueFixIn = defaultFix,
valueFixOut = defaultFix,
defaultItem,
}: IProps<P, O>) {
return (props: ICompProps<P, O>) => {
const { value, onChange, disabled, ...rest } = props;
const changeData = (val: any) => {
onChange(valueFixOut(cloneDeep(val)));
};
const updateItem = (d: any, index: number) => {
const newVal = map(valueFixIn(value), (val, idx) => {
if (isPlainObject(val)) {
if (index === idx) {
const curVal = produce(val, (draft: Obj) => {
const curKey = Object.keys(d)[0];
set(draft, curKey, d[curKey]);
});
return curVal;
}
return val;
} else {
return index === idx ? d : val;
}
});
changeData(newVal);
};
const addItem = () => {
const _defaultItem = typeof defaultItem === 'function' ? defaultItem(props) : defaultItem;
changeData([...valueFixIn(value), _defaultItem]);
};
const deleteItem = (index: number) => {
changeData(filter(value, (_, idx) => index !== idx));
};
return (
<div className="dice-form-nusi-combiner-component">
{map(valueFixIn(value), (item, index) => (
<CombinerItem
{...rest}
disabled={disabled}
className="combiner-item"
key={`${index}`}
data={item}
updateItem={(d: any) => {
updateItem(d, index);
}}
operation={
disabled ? (
<ErdaIcon type="reduce-one" className="combiner-operation not-allowed" />
) : (
<ErdaIcon type="reduce-one" className="combiner-operation" onClick={() => deleteItem(index)} />
)
}
/>
))}
{disabled ? (
<ErdaIcon type="add-one" className="combiner-operation not-allowed" />
) : (
<Tooltip title={i18n.t('common:click to add item')}>
<ErdaIcon type="add-one" className="combiner-operation" onClick={addItem} />
</Tooltip>
)}
</div>
);
};
}
Example #20
Source File: resolve.ts From ui5-language-assistant with Apache License 2.0 | 5 votes |
// Exported for testing purpose
export function resolveType({
model,
type,
typeNameFix,
strict,
}: {
model: UI5SemanticModel;
type: UI5Type | string | undefined;
typeNameFix: TypeNameFix;
strict: boolean;
}): UI5Type | undefined {
// String types are unresolved
if (typeof type === "string") {
type = {
kind: "UnresolvedType",
name: type,
};
}
// Invalid type - this can only happen if the schema validation failed and we're in non-strict mode
if (!isPlainObject(type)) {
return undefined;
}
// Before resolving all types are UnresolvedType or undefined
if (type === undefined || type.kind !== "UnresolvedType") {
return type;
}
const typeName = fixTypeName(type.name, typeNameFix);
if (typeName === undefined) {
return undefined;
}
const typeObj = findValueInMaps<UI5Type>(
typeName,
model.classes,
model.interfaces,
model.enums,
model.namespaces,
model.typedefs
);
if (typeObj !== undefined) {
return typeObj;
}
const primitiveTypeName = getPrimitiveTypeName(typeName);
if (primitiveTypeName !== undefined) {
return {
kind: "PrimitiveType",
name: primitiveTypeName,
};
}
if (typeName.endsWith("[]")) {
const innerTypeName = typeName.substring(0, typeName.length - "[]".length);
const innerType = resolveType({
model,
type: innerTypeName,
typeNameFix,
strict,
});
return {
kind: "ArrayType",
type: innerType,
};
} else {
error(`Unknown type: ${typeName}`, strict);
return {
kind: "UnresolvedType",
name: typeName,
};
}
}
Example #21
Source File: uniform.ts From GWebGPUEngine with MIT License | 5 votes |
function extractUniformsRecursively(
uniformName: string,
uniformValue: IUniform,
uniforms: {
[key: string]: IUniform;
},
prefix: string,
) {
if (
uniformValue === null ||
typeof uniformValue === 'number' || // u_A: 1
typeof uniformValue === 'boolean' || // u_A: false
(Array.isArray(uniformValue) && typeof uniformValue[0] === 'number') || // u_A: [1, 2, 3]
isTypedArray(uniformValue) || // u_A: Float32Array
// @ts-ignore
uniformValue === '' ||
// @ts-ignore
uniformValue.resize !== undefined
) {
uniforms[`${prefix && prefix + '.'}${uniformName}`] = uniformValue;
return;
}
// u_Struct.a.b.c
if (isPlainObject(uniformValue)) {
Object.keys(uniformValue).forEach((childName) => {
extractUniformsRecursively(
childName,
// @ts-ignore
uniformValue[childName],
uniforms,
`${prefix && prefix + '.'}${uniformName}`,
);
});
}
// u_Struct[0].a
if (Array.isArray(uniformValue)) {
// @ts-ignore
uniformValue.forEach((child, idx) => {
Object.keys(child).forEach((childName) => {
extractUniformsRecursively(
childName,
// @ts-ignore
child[childName],
uniforms,
`${prefix && prefix + '.'}${uniformName}[${idx}]`,
);
});
});
}
}
Example #22
Source File: pure-edit-list.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
notEmptyValue = (v: any) => {
if (isPlainObject(v) || isArray(v)) {
return !isEmpty(v);
}
return !(v === '' || v === null || v === undefined);
}
Example #23
Source File: BrickDescriptions.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
export function BrickDescriptions(
props: BrickDescriptionsProps
): React.ReactElement {
const { configProps, itemList, hideGroups } = props;
const hideGroupsSet = new Set(hideGroups);
// istanbul ignore next
const renderLegacyComponent = (
item: BrickDescriptionsItemProps
): React.ReactElement => {
// eslint-disable-next-line no-console
console.warn(
"`<presentational-bricks.brick-descriptions>.itemList[].component` are deprecated, use `useBrick` instead."
);
const { field, component } = item;
if (field && Array.isArray(props.dataSource[field])) {
return props.dataSource[field].map((data: any, i: number) => (
<component.brick
key={`${item.id}-${i}`}
ref={(el: any) => {
el &&
Object.assign(el, {
item,
dataSource: data,
...component.properties,
});
}}
/>
));
} else {
return (
<component.brick
key={item.id}
ref={(el: any) => {
el &&
Object.assign(el, {
item,
dataSource: props.dataSource,
...component.properties,
});
}}
/>
);
}
};
// istanbul ignore next
const renderBrick = (
item: BrickDescriptionsItemProps
): React.ReactElement => {
return (
<BrickAsComponent useBrick={item.useBrick} data={props.dataSource} />
);
};
return (
<Descriptions
title={props.descriptionTitle}
column={props.column}
size={props.size}
bordered={props.bordered}
layout={props.layout}
className={styles.descriptionWrapper}
{...configProps}
>
{itemList
?.filter((item) => !hideGroupsSet.has(item.group))
.map((item, idx) => {
const { text, component, useBrick, ...itemProps } = item;
return (
<Descriptions.Item
key={item.id || idx}
{...itemProps}
className={styles.descriptionItem}
>
{useBrick
? renderBrick(item)
: component
? renderLegacyComponent(item)
: isPlainObject(text)
? JSON.stringify(text)
: text}
</Descriptions.Item>
);
})}
</Descriptions>
);
}
Example #24
Source File: createApp.tsx From redux-with-domain with MIT License | 4 votes |
export default function createApp(createOpt: CreateOpt = {}) {
let app: App
const { initialReducer, onError } = createOpt
// internal map for modules
const _modules: Modules = {}
let _router = <div />
const _middleWares: Middleware<{}>[] = [...defaultMiddleWares]
const sagaMiddleware: SagaMiddleware<{}> = createSagaMiddleware()
_middleWares.push(sagaMiddleware)
function hasSubModule(module: Module) {
let flag = false
forEach(module, value => {
if (isModule(value)) {
flag = true
}
})
return flag
}
function _addModule(m: Module) {
invariant(!_modules[m.namespace], `kop nodule ${m.namespace} exists`)
if (!isEmpty(m.entities)) {
forEach(m.entities, e => {
_addModule(e)
})
}
_modules[m.namespace] = m
}
// create reducer
// http://redux.js.org/docs/recipes/reducers/RefactoringReducersExample.html
function createReducer(
initialState = {},
handlers: ReducerHandler,
defaultReducer?: DefaultReducer<any>
) {
return (state = initialState, action: Action) => {
if (
Object.prototype.hasOwnProperty.call(handlers, action.type) &&
isFunction(handlers[action.type])
) {
const handler = handlers[action.type]
return handler(state, action)
}
if (defaultReducer && isFunction(defaultReducer)) {
return defaultReducer(state, action)
}
return state
}
}
function loopCombineReducer(
tree: any,
combineRootreducer = true,
parentNode?: string | ParentNode
) {
const childReducer: any = mapValues(tree, node => {
if (!isModule(node)) {
return loopCombineReducer(node)
}
if (hasSubModule(node)) {
const subModuleMap = pickBy(node, value => isModule(value))
return loopCombineReducer(subModuleMap, true, node)
}
return createReducer(
node._initialState,
node._reducers,
node._defaultReducer
)
})
let result
if (isEmpty(parentNode)) {
result = {
...childReducer
}
} else if (parentNode === 'root') {
invariant(
!initialReducer || isPlainObject(initialReducer),
'initialReducer should be object'
)
const noDuplicatedKeys = !hasDuplicatedKeys(
initialReducer,
childReducer,
'router'
)
invariant(
noDuplicatedKeys,
'initialReducer has reduplicate keys with other reducers'
)
result = {
...initialReducer,
...childReducer
}
} else {
result = {
base: createReducer(
(parentNode as ParentNode)._initialState,
(parentNode as ParentNode)._reducers,
(parentNode as ParentNode)._defaultReducer
),
...childReducer
}
}
if (parentNode === 'root' && !combineRootreducer) return result
return combineReducers(result)
}
function addModule(module: Module | Module[]) {
if (isArray(module)) {
module.forEach(m => {
_addModule(m)
})
} else {
_addModule(module)
}
}
function initInjectModules(presenter: Presenter) {
forEach(presenter.injectModules, (name: string) => {
invariant(_modules[name], `please check the kop-module ${name} is added`)
extend(_modules[name].presenter, {
loaded: true, // 标记已被装载,在module中会注入 presentor 的 seletor
selectors: presenter.selectors,
actions: presenter.actions
})
})
}
function createRootReducer(combineRootreducer = true) {
const moduleData = cloneDeepWith(_modules)
const moduleTree = reduce(
moduleData,
(result, value, key) => {
const module = get(result, toStorePath(key))
if (isModule(value)) {
if (module) {
return set(result, `${toStorePath(key)}.base`, value)
}
return set(result, toStorePath(key), value)
}
return result
},
{}
)
return loopCombineReducer(moduleTree, combineRootreducer, 'root')
}
function addPage(pageModule: Module, opt: PageOption = {}) {
const { containers } = opt
if (containers && containers.length > 0) {
addModule(containers)
}
if (pageModule.injectModules && pageModule.injectModules.length > 0) {
initInjectModules(pageModule)
}
addModule(pageModule)
}
function addDomain(module: Module) {
addModule(module)
}
const addGlobal = _addModule
function removeModule(module: Module | Module[]) {
const _remove = (m: Module) => {
invariant(
_modules[m.namespace],
`error: the kop-module - ${m.namespace} is not existed`
)
// hack redux-devtools's bug
if (
m &&
m.actions &&
!isPresenter(m) &&
m.type !== DOMAIN_MODULE &&
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
) {
_store.dispatch(m.actions.__reset())
}
delete _modules[m.namespace]
_store.dispatch({ type: `${m.namespace}/@@KOP_CANCEL_EFFECTS` })
}
if (isArray(module)) {
module.forEach(m => {
_remove(m)
})
_store.replaceReducer(createRootReducer() as Reducer)
} else if (module) {
_remove(module)
_store.replaceReducer(createRootReducer() as Reducer)
}
}
function addRouter(r: JSX.Element) {
_router = r
}
function addMiddleWare(middleWare: Middleware) {
const add = (m: Middleware) => {
_middleWares.push(m)
}
add(middleWare)
}
function getStore() {
return _store
}
function createAppStore() {
// inject chrome redux devtools
let composeEnhancers
if (
process.env.NODE_ENV !== 'production' &&
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
) {
composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
} else {
composeEnhancers = compose
}
const enhancer = composeEnhancers(applyMiddleware(..._middleWares))
const rootReducer = createRootReducer()
return createStore(rootReducer as Reducer, enhancer)
}
function getArguments(...args: any[]) {
let domSelector: string | null = null
let callback = noop
if (args.length === 1) {
// app.start(false) means jump render phase and return early
if (args[0] === false) {
return {
domSelector: '',
callback: noop,
shouldRender: false
}
}
if (isString(args[0])) {
domSelector = args[0]
}
if (isFunction(args[0])) {
callback = args[0]
}
}
if (args.length === 2) {
domSelector = args[0]
callback = args[1]
}
return {
domSelector,
callback,
shouldRender: true
}
}
function renderAppElement(
domSelector: string | null,
callback: Function,
shouldRender: boolean
) {
const $elem = <Provider store={_store}>{_router}</Provider>
// skip render when shouldRender is false
if (shouldRender) {
if (isString(domSelector)) {
render($elem, document.querySelector(domSelector), () => {
callback()
})
} else if (isElement(domSelector)) {
render($elem, domSelector, () => {
callback()
})
} else {
callback()
}
}
return $elem
}
function _onError(e: Error) {
if (!onError) {
console.error(e)
} else {
onError(e)
}
}
// create root saga
function createSaga(module: Module) {
return function*() {
if (isArray(module.effects)) {
for (let i = 0, k = module.effects.length; i < k; i++) {
const task = yield effects.fork(function*(): any {
try {
if (module.effects) {
yield module.effects[i]()
}
} catch (e) {
_onError(e)
}
})
yield effects.fork(function*() {
yield effects.take(`${module.namespace}/@@KOP_CANCEL_EFFECTS`)
yield effects.cancel(task)
})
}
}
if (isArray(module.watchers)) {
for (let i = 0, k = module.watchers.length; i < k; i++) {
const task = yield effects.fork(function*(): any {
try {
if (module.watchers) {
yield module.watchers[i]()
}
} catch (e) {
_onError(e)
}
})
yield effects.fork(function*() {
yield effects.take(`${module.namespace}/@@KOP_CANCEL_EFFECTS`)
yield effects.cancel(task)
})
}
}
}
}
function start(...args: any[]) {
const { domSelector, callback, shouldRender } = getArguments(...args)
_store = createAppStore()
initSelectorHelper(_store)
app._store = _store
window[KOP_GLOBAL_STORE_REF] = _store
app._modules = _modules
forEach(app._modules, (m: Module) => {
if (m.type === ENTITY_MODULE) {
m._store = _store // 提供domainModel的遍历action
}
})
app._middleWares = _middleWares
app._router = _router
forEach(_modules, module => {
sagaMiddleware.run(createSaga(module))
})
return renderAppElement(domSelector, callback, shouldRender)
}
// 集成模式初始化,返回 saga,reducer 等等
function _init() {
const rootReducer = createRootReducer(false)
return {
rootReducer,
sagaMiddleware
}
}
// 集成模式启动,由外部 Redux 控制 App 渲染流程
function _run(store: Store<any, AnyAction>) {
initSelectorHelper(store)
app._store = store
window[KOP_GLOBAL_STORE_REF] = store
app._modules = _modules
forEach(app._modules, (m: Module) => {
if (m.type === ENTITY_MODULE) {
m._store = store
}
})
app._router = _router
forEach(_modules, module => {
sagaMiddleware.run(createSaga(module))
})
}
app = {
addModule, // register container module
addPage, // register page module
addDomain, // register domain module
addGlobal, // register global module
addRouter,
addMiddleWare,
start,
getStore,
removeModule,
_init,
_run
}
return app
}
Example #25
Source File: ui5-model-spec.ts From ui5-language-assistant with Apache License 2.0 | 4 votes |
describe("the UI5 language assistant ui5 model", () => {
// The default timeout is 2000ms and getSemanticModel can take ~3000-5000ms
const GET_MODEL_TIMEOUT = 10000;
const VERSION = "1.71.14";
const NO_CACHE_FOLDER = undefined;
function assertSemanticModel(ui5Model: UI5SemanticModel): void {
expect(ui5Model.version).to.equal(VERSION);
expect(Object.keys(ui5Model.classes).length).to.be.greaterThan(200);
expect(Object.keys(ui5Model.namespaces).length).to.be.greaterThan(200);
expect(Object.keys(ui5Model.interfaces).length).to.be.greaterThan(30);
expect(Object.keys(ui5Model.functions).length).to.be.greaterThan(30);
expect(Object.keys(ui5Model.enums).length).to.be.greaterThan(200);
expect(Object.keys(ui5Model.typedefs).length).to.be.greaterThan(10);
expect(Object.keys(ui5Model.classes)).to.include("sap.m.List");
expect(Object.keys(ui5Model.namespaces)).to.include("sap.m");
expect(Object.keys(ui5Model.interfaces)).to.include("sap.f.ICard");
expect(Object.keys(ui5Model.functions)).to.include(
"module:sap/base/assert"
);
expect(Object.keys(ui5Model.enums)).to.include("sap.m.ButtonType");
expect(Object.keys(ui5Model.typedefs)).to.include("sap.ui.fl.Selector");
// Dist layer
expect(Object.keys(ui5Model.classes)).to.include("sap.ui.vk.Camera");
expect(Object.keys(ui5Model.namespaces)).to.include("sap.apf.base");
expect(Object.keys(ui5Model.enums)).to.include(
"sap.ca.ui.charts.ChartSelectionMode"
);
}
it("will get UI5 semantic model", async () => {
const ui5Model = await getSemanticModel(NO_CACHE_FOLDER);
assertSemanticModel(ui5Model);
}).timeout(GET_MODEL_TIMEOUT);
it("doesn't fail if a file cannot be fetched", async () => {
const ui5Model = await getSemanticModelWithFetcher(async (url: string) => {
return {
ok: false,
json: (): never => {
throw new Error(`Cannot read from ${url}`);
},
};
}, NO_CACHE_FOLDER);
expect(ui5Model).to.exist;
});
describe("with cache", () => {
describe("cache in temp dir", () => {
let cachePath: string;
beforeEach(async () => {
({ path: cachePath } = await tempDir());
});
afterEach(async () => {
// The temp folder will contain files at the end so we remove it
// with rimraf instead of calling cleanup()
rimrafSync(cachePath);
});
it("caches the model the first time getSemanticModel is called", async () => {
const ui5Model = await getSemanticModel(cachePath);
assertSemanticModel(ui5Model);
// Check the files were created in the folder
const files = await readdir(cachePath);
expect(files).to.not.be.empty;
// Call getSemanticModel again with the same path and check it doesn't try to read from the URL
let fetcherCalled = false;
const ui5ModelFromCache = await getSemanticModelWithFetcher(
(url: string): never => {
fetcherCalled = true;
throw new Error(
`The files should be taken from the cache, got call for ${url}`
);
},
cachePath
);
expect(fetcherCalled).to.be.false;
// Make sure it's not the model itself that is cached
expect(ui5ModelFromCache).to.not.equal(ui5Model);
// Check we got the same result (we can't use deep equal so the check is shallow)
forEach(ui5Model, (value, key) => {
if (isPlainObject(value)) {
expect(
Object.keys(
ui5ModelFromCache[key as keyof UI5SemanticModel] as Record<
string,
unknown
>
)
).to.deep.equalInAnyOrder(
Object.keys(value as Record<string, unknown>)
);
}
});
assertSemanticModel(ui5ModelFromCache);
}).timeout(GET_MODEL_TIMEOUT);
it("doesn't fail when file cannot be written to the cache", async () => {
// Create a folder with the file name so the file will not be written
const cacheFilePath = getCacheFilePath(
getCacheFolder(cachePath, VERSION),
"sap.m"
);
expectExists(cacheFilePath, "cacheFilePath");
await mkdirs(cacheFilePath);
const ui5Model = await getSemanticModel(cachePath);
expect(ui5Model).to.exist;
// Check we still got the sap.m library data
expect(Object.keys(ui5Model.namespaces)).to.contain("sap.m");
expect(ui5Model.namespaces["sap.m"].library).to.equal("sap.m");
}).timeout(GET_MODEL_TIMEOUT);
it("doesn't fail when file cannot be read from the cache", async () => {
// Create a file with non-json content so the file will not be deserialized
const cacheFolder = getCacheFolder(cachePath, VERSION);
await mkdirs(cacheFolder);
const cacheFilePath = getCacheFilePath(cacheFolder, "sap.m");
expectExists(cacheFilePath, "cacheFilePath");
await writeFile(cacheFilePath, "not json");
const ui5Model = await getSemanticModel(cachePath);
expect(ui5Model).to.exist;
// Check we still got the sap.m library data
expect(Object.keys(ui5Model.namespaces)).to.contain("sap.m");
expect(ui5Model.namespaces["sap.m"].library).to.equal("sap.m");
}).timeout(GET_MODEL_TIMEOUT);
});
describe("cache path is a file", async () => {
let cachePath: string;
let cleanup: () => Promise<void>;
beforeEach(async () => {
({ path: cachePath, cleanup } = await tempFile());
});
afterEach(async () => {
await cleanup();
});
it("does not cache the model", async () => {
const ui5Model = await getSemanticModel(cachePath);
assertSemanticModel(ui5Model);
// Call getSemanticModel again with the same path and check it doesn't try to read from the URL
let fetcherCalled = false;
await getSemanticModelWithFetcher(async (): Promise<FetchResponse> => {
fetcherCalled = true;
return {
ok: true,
json: async (): Promise<unknown> => {
return {};
},
};
}, cachePath);
expect(fetcherCalled).to.be.true;
}).timeout(GET_MODEL_TIMEOUT);
});
});
});
Example #26
Source File: unit-spec.ts From ui5-language-assistant with Apache License 2.0 | 4 votes |
context("The ui5-language-assistant semantic model package unit tests", () => {
describe("resolveType", () => {
it("returns the same type if it's resolved", () => {
const model = buildUI5Model({});
const stringPrimitiveType: UI5Type = {
kind: "PrimitiveType",
name: "String",
};
expect(
resolveType({
model,
type: stringPrimitiveType,
typeNameFix: {},
strict: true,
})
).to.equal(stringPrimitiveType);
});
});
describe("setParent", () => {
it("fails when parent is not found in the model", () => {
const model = buildUI5Model({});
const classFqn = "sap.MyClass";
const classs = buildUI5Class({
name: "MyClass",
});
model.classes[classFqn] = classs;
expect(() => {
setParent(model, classFqn, classs);
}).to.throw("Symbol sap not found");
});
});
describe("getSymbolMaps", () => {
it("returns all object properties on the model", () => {
const model = buildUI5Model({});
const objectsOnModel: unknown[] = [];
// Get all plain objects in the model and check that getSymbolMaps returns them
// This is done to ensure we didn't forget any symbol maps. If a map/object is added that should
// be ignored in getSymbolMaps this loop should be updated to ignore it.
forEach(model, (property) => {
if (isPlainObject(property)) {
objectsOnModel.push(property);
}
});
expect(getSymbolMaps(model)).to.contain.members(objectsOnModel);
});
});
describe("generate", () => {
function generateSymbol(symbol: SymbolBase): UI5SemanticModel {
return generate({
version: "1.74.0",
strict: true,
typeNameFix: {},
libraries: {
testLib: {
"$schema-ref":
"http://schemas.sap.com/sapui5/designtime/api.json/1.0",
symbols: [symbol],
},
},
});
}
function generateAndVerifyClass(
partialClass: Partial<ClassSymbol>
): UI5Class {
const symbol = {
kind: "class",
basename: "rootSymbol",
name: "rootSymbol",
...partialClass,
} as ClassSymbol; // Casting is required because typescript compiler expects constructor to be a function
const result = generateSymbol(symbol);
expect(keys(result.classes), "classes").to.contain("rootSymbol");
return result.classes["rootSymbol"];
}
it("sets public visibility on constructor if visibility is not defined", () => {
const result = generateAndVerifyClass({
constructor: {
description: "constructor with undefined visibility",
},
} as Partial<ClassSymbol>); // Casting is necessary because typescript compiler expects constructor to be a function
expectExists(result.ctor, "constructor");
expect(result.ctor.visibility, "constructor visibility").to.equal(
"public"
);
});
it("sets public visibility on event if visibility is not defined", () => {
const result = generateAndVerifyClass({
events: [
{
name: "name",
},
],
});
expect(result.events, "rootSymbol events").to.have.lengthOf(1);
expect(result.events[0].visibility, "event visibility").to.equal(
"public"
);
});
it("doesn't set type when not defined on class property", () => {
const result = generateAndVerifyClass({
"ui5-metadata": {
properties: [
{
name: "name",
},
],
},
});
expect(result.properties, "rootSymbol properties").to.have.lengthOf(1);
expect(result.properties[0].type, "property type").to.be.undefined;
});
it("doesn't set type when not defined on class aggregation", () => {
const result = generateAndVerifyClass({
"ui5-metadata": {
aggregations: [
{
name: "name",
},
],
},
});
expect(result.aggregations, "rootSymbol aggregations").to.have.lengthOf(
1
);
expect(result.aggregations[0].type, "aggregation type").to.be.undefined;
});
it("sets cardinality to 0..n when not defined on class aggregation", () => {
const result = generateAndVerifyClass({
"ui5-metadata": {
aggregations: [
{
name: "name",
},
],
},
});
expect(result.aggregations, "rootSymbol aggregations").to.have.lengthOf(
1
);
expect(
result.aggregations[0].cardinality,
"aggregation cardinality"
).to.equal("0..n");
});
it("doesn't set type when not defined on class association", () => {
const result = generateAndVerifyClass({
"ui5-metadata": {
associations: [
{
name: "name",
},
],
},
});
expect(result.associations, "rootSymbol associations").to.have.lengthOf(
1
);
expect(result.associations[0].type, "association type").to.be.undefined;
});
it("sets cardinality to 0..1 when not defined on class association", () => {
const result = generateAndVerifyClass({
"ui5-metadata": {
associations: [
{
name: "name",
},
],
},
});
expect(result.associations, "rootSymbol associations").to.have.lengthOf(
1
);
expect(
result.associations[0].cardinality,
"association cardinality"
).to.equal("0..1");
});
it("doesn't set type when not defined on namespace field", () => {
const symbol: NamespaceSymbol = {
kind: "namespace",
basename: "rootNamespace",
name: "rootNamespace",
properties: [
{
name: "myField",
},
],
};
const result = generateSymbol(symbol);
expect(keys(result.namespaces), "namespaces").to.contain("rootNamespace");
const namespace = result.namespaces["rootNamespace"];
expect(namespace.fields, "rootNamespace fields").to.have.lengthOf(1);
expect(namespace.fields[0].type, "field type").to.be.undefined;
});
describe("symbols is undefined", () => {
const fileContent = {
"$schema-ref": "http://schemas.sap.com/sapui5/designtime/api.json/1.0",
version: "1.74.0",
library: "testLib",
};
it("doesn't fail in strict mode", () => {
const model = generate({
version: fileContent.version,
libraries: { testLib: fileContent },
typeNameFix: {},
strict: true,
});
expectExists(model, "model");
});
it("doesn't fail in non-strict mode", () => {
const model = generate({
version: fileContent.version,
libraries: { testLib: fileContent },
typeNameFix: {},
strict: false,
});
expectExists(model, "model");
});
});
});
context("includedLibraries", () => {
function generateFromLibraries(
libraries: Record<string, unknown>
): UI5SemanticModel {
const result = generate({
version: "1.74.0",
strict: false,
typeNameFix: {},
libraries: libraries,
printValidationErrors: false,
});
return result;
}
const libWithSymbol = {
"$schema-ref": "http://schemas.sap.com/sapui5/designtime/api.json/1.0",
symbols: [
{
kind: "class",
basename: "rootSymbol",
name: "rootSymbol",
},
],
};
it("contains a valid library", () => {
const result = generateFromLibraries({
testLib: libWithSymbol,
});
expect(result.includedLibraries, "includedLibraries").to.deep.equal([
"testLib",
]);
});
it("contains an invalid library object", () => {
const result = generateFromLibraries({
testLib: {},
});
expect(result.includedLibraries, "includedLibraries").to.deep.equal([
"testLib",
]);
});
it("contains an empty library", () => {
const result = generateFromLibraries({
testLib: "",
});
expect(result.includedLibraries, "includedLibraries").to.deep.equal([
"testLib",
]);
});
it("contains all sent libraries", () => {
const result = generateFromLibraries({
testLib: libWithSymbol,
lib2: libWithSymbol,
emptyLib: {},
});
expect(
result.includedLibraries,
"includedLibraries"
).to.deep.equalInAnyOrder(["testLib", "lib2", "emptyLib"]);
});
});
context("API JSON fixes", () => {
context("addViewDefaultAggregation", () => {
it("doesn't fail when there is a sap.ui.core.mvc.View with no ui5-metadata", () => {
addViewDefaultAggregation("sap.ui.core", {
symbols: [
{
name: "sap.ui.core.mvc.View",
},
],
});
});
});
});
});
Example #27
Source File: index.tsx From gant-design with MIT License | 4 votes |
withSelector = compose( defaultProps(defaultprop), withState('label', 'setLabel', null), // 读模式下的显示文本 withState('cacheLabel', 'setCacheLabel', ({ optionLabel }) => optionLabel), // 当前选项的文本, 在点确认的时候才更新 withState('loading', 'setLoading', false), withState('filter', 'setFilter', ''), withState('selectRef', 'setSelectRef', null), // select组件 withState('dataList', 'setDataList', ({ dataSource }) => dataSource), // 监听搜索 withPropsOnChange(['filter'], ({ filter, selectorId }) => ({ taskId: `${selectorId}:${escape(filter).replace(/\%u/g, '')}`, })), withHandlers({ //将最近选择的项的key转化为真实的key storageToReal: ({ selectorId, reg }) => value => { // 最近选择 if (value?.startsWith(selectorId)) return value.replace(reg, '$1'); return value; }, }), withHandlers({ getValue: ({ valueProp }) => data => String(valueProp && isPlainObject(data) ? data[valueProp] : data), // 获取选项的value getLabel: ({ storageToReal, valueProp, labelProp }) => data => { if (labelProp && isPlainObject(data)) return valueProp == labelProp ? storageToReal(data[labelProp]) : data[labelProp]; return data; }, // 获取选项的label setLabel: ({ setLabel: originSetLabel, splitStr = '、' }) => labels => originSetLabel(Array.isArray(labels) ? labels.filter(Boolean).join(splitStr) : labels), // 重置setlabel方法,增加格式化的功能 }), withHandlers({ // 从dataList或者storageList中找到数据 getItemLabel: ({ dataList, storageList, selectorId, getValue, storageToReal, getLabel, optionLabel, useStorage, }) => (value, index = 0) => { let list = concat(dataList, storageList); // 启用缓存的情况下执行判断 // fix: 解决当storageId恰好是value的前缀的情况 if (useStorage && value?.startsWith(selectorId)) { list = storageList; } const valueItem = list.find(item => storageToReal(getValue(item)) === value); if (valueItem) return getLabel(valueItem); const optionLabelArray = Array.isArray(optionLabel) ? optionLabel : [optionLabel]; return optionLabelArray[index]; }, }), withPropsOnChange(['multiple', 'mode'], ({ multiple, mode }) => ({ isMultiple: multiple || mode === 'multiple' || mode === 'tags', })), withPropsOnChange( ['value'], ({ dataList, storageList, value, getValue, selectorId, isMultiple }) => { if (isNil(value)) { return { value: undefined, }; } const isArray = Array.isArray(value); let cValue = isArray ? value : [value]; const transormedValue = cValue.map(cv => { const v = String(cv); const isInList = dataList.find(item => getValue(item) === v); const isInStorage = storageList.find(item => getValue(item) === v); // 选择的缓存中的数据,需要做一层转化 if (!isInList && isInStorage) { return `${selectorId}-${v}`; } return v; }); return { value: isMultiple ? transormedValue : transormedValue[0], }; }, ), withHandlers({ // 依赖转化后的value transformDataToList: ({ getLabel, getValue, renderItem, optionLabelProp, hideSelected, isMultiple, value: comValue, }) => list => list.map(item => { const transformItemInfo = item => { const value = getValue(item); const key = value || item.key; const label = getLabel(item); return { value, key, label }; }; if (renderItem) { return renderItem( isPlainObject(item) ? { ...item, ...transformItemInfo(item) } : item, Option, ); } if (isPlainObject(item)) { const { disabled, title, className } = item; const { value, key, label } = transformItemInfo(item); let show = true, style; if (hideSelected) { if (isMultiple) { show = comValue.every(v => v !== value); } else { show = comValue !== value; } } if (!show) style = { display: 'none' }; //支持 antd提供的回填到选择框的 Option 的属性值参数功能 const optionLabelPropObj = optionLabelProp && item[optionLabelProp] ? { [optionLabelProp]: item[optionLabelProp] } : {}; return ( <Option key={key} value={value} disabled={disabled} title={title} style={style} className={className} {...optionLabelPropObj} > {label} </Option> ); } return ( <Option key={item} value={item}> {item} </Option> ); }), setLabelWithValue: ({ value, setLabel, setCacheLabel, getItemLabel }) => () => { if (isNil(value)) { setLabel(null); return; } let label = null; // 从dataList找到value对应的项 // 如果没有找到就从storagelist里面找 // 如果还是没有找到,那么就要使用optionLabel参数 if (Array.isArray(value)) { // 多选 label = value.map((itemValue, index) => itemValue ? getItemLabel(itemValue, index) : null, ); } else { label = getItemLabel(value); } setLabel(label); // 设置读模式下的显示文本 setCacheLabel(label); // 设置选项的label }, }), withHandlers({ updateStorage: ({ selectorId, selectorStorageId, storageList, getValue, valueProp, setStorageList, useStorage, historyLength, }) => (data, update) => { if (!useStorage) return; // 不启用缓存 let copyList = cloneDeep(storageList); data.map(item => { const id = `${selectorId}-${getValue(item)}`; let isUpdate = update; // 为true表示从最近选择的项里面选择,只更新 if (!isUpdate) { // const existed = copyList.some(pItem => getValue(pItem) === id); isUpdate = existed; // 如果最近选择种已存在,将直接更新数据 if (!existed) { // 新增最近被选择的数据 if (valueProp && isPlainObject(item)) { copyList.unshift({ ...item, [valueProp]: id }); } else { copyList.unshift(id); } copyList = copyList.slice(0, historyLength); // 保留最近?条 } } if (isUpdate) { copyList.map(item => { if (getValue(item) === id) { // 找到被选择的那一条,更新数据 return valueProp && isPlainObject(item) ? { ...item, [valueProp]: id } : id; } return item; }); } }); setStorageList(copyList); // 更新list localStorage.setItem(selectorStorageId, JSON.stringify(copyList)); // 更新缓存 }, cleanStorage: ({ selectorId, selectorStorageId, storageList, getValue, valueProp, setStorageList, useStorage, }) => (data, update) => { setStorageList([]); // 更新list localStorage.setItem(selectorStorageId, JSON.stringify([])); // 更新缓存 }, getData: ({ taskId, useCache, loading, setLoading, query, afterQuery, filter, setDataList, }) => () => { if (!query) return; let task = null; if (!useCache) { // 不使用选择器缓存,由业务自己决定缓存 setLoading(true); task = query(filter); } else { task = selectorCache.get(taskId); if (!task) { if (loading) return; setLoading(true); task = query(filter); selectorCache.set(taskId, task); } } if (!(task.then && typeof task.then === 'function')) task = Promise.resolve(task); task.then(data => { let list = Array.isArray(data) ? data : []; setLoading(false); setDataList(list); afterQuery && afterQuery(list, setDataList); // else { // throw new Error('选择器选项列表只能是数组格式') // } }); }, }), // 更新选项列表 //#region withPropsOnChange( ['dataList', 'filter', 'storageList', 'loading'], ({ dataList, filter, storageList, cleanStorage, transformDataToList, loading, useStorage, query, labelProp, getLabel, isFilter, customNotDataContent }) => { let result = dataList; if (!query && filter && isFilter) { /** * 筛选算法 * axbxcx ---> abc true * abcabc ---> abc true * bacbcc ---> abc true * bbabdd ---> abc false 没有c */ try { result = dataList.filter(item => { const label = getLabel(item); if (!label) { throw new Error( `应用选择器的过滤功能,请确保列表数据中${labelProp}属性存在,或修改'labelProp'对应的属性名称,作为过滤的依据`, ); } return label.toLowerCase().indexOf(filter.toLowerCase()) > -1; // const LastIndex = filter.split('').reduce( // (index, char) => { // if (index === -1) return -1; // let label = getLabel(item) // if (!label) { // throw new Error(`应用选择器的过滤功能,请确保列表数据中${labelProp}属性存在,或修改'labelProp'对应的属性名称,作为过滤的依据`) // } // label = label.toUpperCase() // char = char.toUpperCase() // return label.slice(index).indexOf(char) // }, // 0 // ) // return ~LastIndex }); } catch (e) { console.error(e); } } let list = [ //'加载中...' : '没有查询到数据' <Select.Option key="none" disabled> <Receiver>{locale => <>{loading ? locale.loading : customNotDataContent||locale.noData}</>}</Receiver> </Select.Option>, ]; if (result.length) { const hasGroup = result.some(item => item.group); if (!hasGroup) { list = transformDataToList(result); } else { const everyGroup = result.every(item => item.group); const group = groupBy(result, 'group'); // 某些项没有写group if (!everyGroup) { group['其他选项'] = group['undefined']; } list = Object.entries(group).reduce((result, [key, data]) => { if (key !== 'undefined') { result.push( <Select.OptGroup key={key} label={key}> {transformDataToList(data)} </Select.OptGroup>, ); } return result; }, [] as React.ReactElement[]); } } if (useStorage && !filter) { // 搜索结果 const newItems = ( <Select.OptGroup key="result" label={<Receiver>{locale => <>{locale.searchResult}</>}</Receiver>} > {list} </Select.OptGroup> ); const selectedItems = ( <Select.OptGroup key="recent" label={ <Receiver> {locale => ( <div style={{ width: '100%', display: 'flex' }}> <span style={{ flex: 1 }}>{locale.recentSelect}</span> <Tooltip title={locale.clearRecent}> <Icon type="delete" style={{ fontSize: '12px', lineHeight: '32px', }} onClick={() => { cleanStorage(); }} /> </Tooltip> </div> )} </Receiver> } > {storageList.length ? ( transformDataToList(storageList) ) : ( <Select.Option key="empty" disabled> <Receiver>{locale => <>{locale.noRecent}</>}</Receiver> </Select.Option> )} </Select.OptGroup> ); return { renderList: [selectedItems].concat(newItems), }; } else { return { renderList: list, }; } }, ), //#endregion withPropsOnChange(['query'], ({ getData }) => getData()), // 下列属性变化的时候重新根据value值设置label withPropsOnChange(['value', 'optionLabel', 'dataList'], ({ setLabelWithValue }) => setLabelWithValue(), ), // 监听label // withPropsOnChange(['optionLabel'], ({ optionLabel }) => { // return { // cacheLabel: optionLabel // } // }), // 去支持只传递dataSource,并且希望更新dataSource的情况 withPropsOnChange(['dataSource'], ({ dataSource, setDataList }) => setDataList(dataSource)), mapProps(({ dataSource, transformDataToList, ...props }) => props), )
Example #28
Source File: test-env-detail.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
TestEnvDetail = (props: IProps) => {
const { data, disabled, visible, onCancel, envID, envType, testType } = props;
const [{ headerMode, globalMode, headerJsonValid, globalJsonValid }, updater, update] = useUpdate({
headerMode: 'form',
globalMode: 'form',
headerJsonValid: true,
globalJsonValid: true,
});
React.useEffect(() => {
if (!visible) {
update({
headerMode: 'form',
globalMode: 'form',
headerJsonValid: true,
globalJsonValid: true,
});
}
}, [update, visible]);
const formRef = React.useRef({}) as React.MutableRefObject<FormInstance>;
const HeaderKeyComp = ({ record, update: _update, ...rest }: any) => {
return <InputSelect options={headerListOption} value={record.key} disabled={disabled} onChange={_update} />;
};
const GlobalKeyComp = ({ record, update: _update, ...rest }: any) => (
<Input value={record.key} onChange={(e) => _update(e.target.value.trim())} maxLength={500} {...rest} />
);
const GlobalDescComp = ({ record, update: _update, ...rest }: any) => (
<Select value={record.type || typeMap[testType].string} allowClear onChange={_update} {...rest}>
{map(typeMap[testType], (value, key) => (
<Option key={key} value={value}>
{value}
</Option>
))}
</Select>
);
const KeyDescComp = ({ record, keyDesc, update: _update, ...rest }: any) => (
<Input value={record[keyDesc]} onChange={(e) => _update(e.target.value)} {...rest} />
);
const ValueComp = ({ record, valueName, update: _update, ...rest }: any) => (
<Input value={record[valueName]} onChange={(e) => _update(e.target.value)} {...rest} />
);
const getFieldsList = (_type: string, _headMode: string, _globalMode: string) => {
const headFieldsStatus = _headMode === 'form' ? ['', 'hidden'] : ['hidden', ''];
const globalFieldsStatus = _globalMode === 'form' ? ['', 'hidden'] : ['hidden', ''];
const fieldMap = {
auto: [
{
label: i18n.t('Name'),
name: 'displayName',
itemProps: {
maxLength: 191,
disabled,
},
},
{
label: i18n.t('Description'),
name: 'desc',
type: 'textArea',
itemProps: {
maxLength: 512,
disabled,
},
},
],
manual: [
{
label: i18n.t('dop:Environment name'),
name: 'name',
itemProps: {
maxLength: 50,
disabled,
},
},
],
};
return [
...fieldMap[_type],
{
label: i18n.t('dop:Environmental domain'),
name: 'domain',
getComp: () => <ProtocolInput disabled={disabled} />,
required: false,
},
{
getComp: () => (
<div className="flex justify-between items-center">
<div>
<span className="font-bold">Header</span>
</div>
<Radio.Group
value={headerMode}
onChange={(e: RadioChangeEvent) => {
const _mode = e.target.value;
const curForm = formRef.current;
const curFormData = curForm.getFieldsValue();
if (_mode === 'form') {
formRef.current.setFieldsValue({
header: JSON.parse(curFormData.headerStr),
});
} else {
const isObj = isPlainObject(curFormData.header);
formRef.current.setFieldsValue({
headerStr: JSON.stringify(
filter(
map(curFormData.header, (item, k) => {
let reItem = {};
if (isObj) {
reItem = { value: item, key: k };
} else {
reItem = { key: item.key, value: item.value };
}
return reItem;
}),
(item) => item.key,
),
null,
2,
),
});
}
updater.headerMode(e.target.value);
}}
>
<Radio.Button disabled={!headerJsonValid} value="form">
{disabled ? firstCharToUpper(i18n.t('common:form')) : i18n.t('common:Form Edit')}
</Radio.Button>
<Radio.Button value="code">
{disabled ? firstCharToUpper(i18n.t('common:text')) : i18n.t('common:Text Edit')}
</Radio.Button>
</Radio.Group>
</div>
),
extraProps: {
className: 'mb-2',
},
},
{
name: 'header',
required: false,
getComp: () => <KVPairTable disabled={disabled} KeyComp={HeaderKeyComp} ValueComp={ValueComp} />,
itemProps: {
type: headFieldsStatus[0],
},
},
{
name: 'headerStr',
required: false,
getComp: () => <JsonFileEditor readOnly={disabled} />,
itemProps: {
type: headFieldsStatus[1],
},
},
{
getComp: () => (
<div className="flex justify-between items-center">
<span className="font-bold">Global</span>
<Radio.Group
value={globalMode}
onChange={(e: RadioChangeEvent) => {
const _mode = e.target.value;
const curForm = formRef.current;
const curFormData = curForm.getFieldsValue();
if (_mode === 'form') {
formRef.current.setFieldsValue({
global: JSON.parse(curFormData.globalStr),
});
} else {
const isObj = isPlainObject(curFormData.global);
formRef.current.setFieldsValue({
globalStr: JSON.stringify(
filter(
map(curFormData.global, (item, k) => {
const { desc = '', value = '', type = '' } = item;
const reItem = { desc, value, type, key: isObj ? k : item.key };
if (!reItem.type) reItem.type = typeMap.auto.string;
return reItem;
}),
(item) => item.key,
),
null,
2,
),
});
}
updater.globalMode(e.target.value);
}}
>
<Radio.Button disabled={!globalJsonValid} value="form">
{disabled ? firstCharToUpper(i18n.t('common:form')) : i18n.t('common:Form Edit')}
</Radio.Button>
<Radio.Button value="code">
{disabled ? firstCharToUpper(i18n.t('common:text')) : i18n.t('common:Text Edit')}
</Radio.Button>
</Radio.Group>
</div>
),
extraProps: {
className: 'mb-2',
},
},
{
name: 'global',
required: false,
getComp: () => (
<KVPairTable
disabled={disabled}
KeyComp={GlobalKeyComp}
DescComp={GlobalDescComp}
descName="type"
KeyDescComp={KeyDescComp}
keyDesc="desc"
/>
),
itemProps: {
type: globalFieldsStatus[0],
},
},
{
name: 'globalStr',
required: false,
getComp: () => <JsonFileEditor readOnly={disabled} />,
itemProps: {
type: globalFieldsStatus[1],
},
},
];
};
const fieldsList = getFieldsList(testType, headerMode, globalMode);
const onUpdateHandle = React.useCallback(
(values, header, global) => {
if (testType === 'manual') {
testEnvStore.updateTestEnv({ ...values, id: data.id, header, global, envType, envID }, { envType, envID });
} else {
testEnvStore.updateAutoTestEnv({
apiConfig: {
domain: values.domain,
name: values.name,
header,
global,
},
scope: scopeMap.autoTest.scope,
scopeID: String(envID),
ns: data.ns,
displayName: values.displayName,
desc: values.desc,
});
}
},
[data, envID, envType, testType],
);
const onCreateHandle = React.useCallback(
(values, header, global) => {
if (testType === 'manual') {
testEnvStore.createTestEnv({ ...values, header, global, envType, envID }, { envType, envID });
} else {
testEnvStore.createAutoTestEnv(
{
apiConfig: {
...values,
header,
global,
},
scope: scopeMap.autoTest.scope,
scopeID: String(envID),
displayName: values.displayName,
desc: values.desc,
},
{ scope: scopeMap.autoTest.scope, scopeID: envID },
);
}
},
[envID, envType, testType],
);
const handleSubmit = (values: any) => {
if (!globalJsonValid || !headerJsonValid) {
return Promise.reject();
}
if (disabled) {
onCancel();
return;
}
const { headerStr, globalStr, ..._rest } = values;
const curHeader = headerMode === 'form' ? values.header : JSON.parse(headerStr || '[]');
const curGlobal = globalMode === 'form' ? values.global : JSON.parse(globalStr || '[]');
const header = transHeader(curHeader);
const global = transGlobal(curGlobal);
if (!isEmpty(data)) {
onUpdateHandle(_rest, header, global);
} else {
onCreateHandle(_rest, header, global);
}
onCancel();
};
const onValuesChange = React.useCallback(
debounce((_formRef: { form: FormInstance }, changeValues: Obj, allValues: Obj) => {
if (changeValues.headerStr) {
const curHeaderValid = isValidJsonStr(changeValues.headerStr);
updater.headerJsonValid(curHeaderValid);
}
if (changeValues.globalStr) {
const curGlobalValid = isValidJsonStr(changeValues.globalStr);
updater.globalJsonValid(curGlobalValid);
}
}, 300),
[],
);
return (
<FormModal
name={i18n.t('dop:parameter configuration')}
visible={visible}
width={900}
modalProps={{
destroyOnClose: true,
}}
formOption={{ onValuesChange }}
formData={data}
ref={formRef}
fieldsList={fieldsList}
onOk={handleSubmit}
onCancel={onCancel}
formProps={{ layout: 'vertical' }}
/>
);
}
Example #29
Source File: tpl-builtin.ts From brick-design with MIT License | 4 votes |
export function dataMapping(
to: any,
from: PlainObject,
ignoreFunction: boolean | ((key: string, value: any) => boolean) = false,
): any {
let ret = {};
if (Array.isArray(to)) {
return to.map((item) => dataMapping(item, from, ignoreFunction));
} else if (!to) {
return ret;
}
Object.keys(to).forEach((key) => {
const value = to[key];
let keys: Array<string>;
if (typeof ignoreFunction === 'function' && ignoreFunction(key, value)) {
// 如果被ignore,不做数据映射处理。
(ret as PlainObject)[key] = value;
} else if (key === '&' && value === '$$') {
ret = {
...ret,
...from,
};
} else if (key.includes('$') && typeof value === 'string') {
ret[key.substring(1)] = createStr2Function(value, from);
} else if (key === '&') {
const v =
isPlainObject(value) &&
(keys = Object.keys(value)) &&
keys.length === 1 &&
from[keys[0].substring(1)] &&
Array.isArray(from[keys[0].substring(1)])
? from[keys[0].substring(1)].map((raw: object) =>
dataMapping(
value[keys[0]],
createObject(from, raw),
ignoreFunction,
),
)
: resolveMapping(value, from);
if (Array.isArray(v) || typeof v === 'string') {
ret = v;
} else if (typeof v === 'function') {
ret = {
...ret,
...v(from),
};
} else {
ret = {
...ret,
...v,
};
}
} else if (value === '$$') {
ret[key] = from;
} else if (value && value[0] === '$') {
const v = resolveMapping(value, from);
ret[key] = v;
if (v === '__undefined') {
delete (ret as PlainObject)[key];
}
} else if (value && value[0] === '&') {
ret[key] = evalExpression(/\&{([^}{]+)}/g.exec(value)[1], from);
} else if (
isPlainObject(value) &&
(keys = Object.keys(value)) &&
keys.length === 1 &&
from[keys[0].substring(1)] &&
Array.isArray(from[keys[0].substring(1)])
) {
const arr = from[keys[0].substring(1)];
const mapping = value[keys[0]];
ret[key] = arr.map((raw: object) =>
dataMapping(mapping, createObject(from, raw), ignoreFunction),
);
} else if (isPlainObject(value)) {
ret[key] = dataMapping(value, from, ignoreFunction);
} else if (Array.isArray(value)) {
ret[key] = value.map((value: any) =>
isPlainObject(value)
? dataMapping(value, from, ignoreFunction)
: resolveMapping(value, from),
);
} else if (typeof value == 'string' && value.includes('$')) {
ret[key] = resolveMapping(value, from);
} else if (typeof value === 'function' && ignoreFunction !== true) {
ret[key] = value(from);
} else {
ret[key] = value;
if (value === '__undefined') {
delete (ret as PlainObject)[key];
}
}
});
return ret;
}