oak-frontend-base/lib/page.react.js

748 lines
29 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createComponent = void 0;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const assert_1 = require("oak-domain/lib/utils/assert");
const react_1 = tslib_1.__importDefault(require("react"));
const lodash_1 = require("oak-domain/lib/utils/lodash");
const types_1 = require("oak-domain/lib/types");
const page_common_1 = require("./page.common");
class OakComponentBase extends react_1.default.PureComponent {
oakFullpath = '';
featuresSubscribed = [];
addFeatureSub(name, callback) {
const unsubHandler = this.features[name].subscribe(callback);
this.featuresSubscribed.push({
name,
callback,
unsubHandler,
});
}
removeFeatureSub(name, callback) {
const f = this.featuresSubscribed.find(ele => ele.callback === callback && ele.name === name);
(0, lodash_1.pull)(this.featuresSubscribed, f);
f.unsubHandler && f.unsubHandler();
}
unsubscribeAll() {
this.featuresSubscribed.forEach(ele => {
if (ele.unsubHandler) {
ele.unsubHandler();
ele.unsubHandler = undefined;
}
});
}
subscribeAll() {
this.featuresSubscribed.forEach(ele => {
if (!ele.unsubHandler) {
ele.unsubHandler = this.features[ele.name].subscribe(ele.callback);
}
});
}
save(key, item) {
return this.features.localStorage.save(key, item);
}
load(key) {
return this.features.localStorage.load(key);
}
clear(key) {
if (key) {
return this.features.localStorage.remove(key);
}
return this.features.localStorage.clear();
}
setNotification(data) {
this.features.notification.setNotification(data);
}
consumeNotification() {
return this.features.notification.consumeNotification();
}
setMessage(data) {
return this.features.message.setMessage(data);
}
consumeMessage() {
return this.features.message.consumeMessage();
}
reRender(extra) {
return page_common_1.reRender.call(this, this.oakOption, extra);
}
navigateTo(options, state, disableNamespace) {
// 路由传入namespace
return this.features.navigator.navigateTo(options, state, disableNamespace);
}
navigateBack(delta) {
return this.features.navigator.navigateBack(delta);
}
redirectTo(options, state, disableNamespace) {
return this.features.navigator.redirectTo(options, state, disableNamespace);
}
addItem(data, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
return this.features.runningTree.addItem(path2, data);
}
addItems(data, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
return this.features.runningTree.addItems(path2, data);
}
removeItem(id, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.removeItem(path2, id);
}
removeItems(ids, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.removeItems(path2, ids);
}
updateItem(data, id, action, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.updateItem(path2, data, id, action);
}
updateItems(data, ids, action, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.updateItems(path2, data, ids, action);
}
recoverItem(id, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.recoverItem(path2, id);
}
recoverItems(ids, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.recoverItems(path2, ids);
}
resetItem(id, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.resetItem(path2, id);
}
update(data, action, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.update(path2, data, action);
}
create(data, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.create(path2, data);
}
remove(path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.remove(path2);
}
isCreation(path) {
const value = this.getFreshValue(path);
(0, assert_1.assert)(!(value instanceof Array));
return value?.$$createAt$$ === 1;
}
clean(lsn, dontPublish, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.clean(path2, lsn, dontPublish);
}
savePoint() {
return this.features.runningTree.savePoint();
}
t(key, params) {
return this.features.locales.t(key, params);
}
execute(action, messageProps, path, opers) {
return page_common_1.execute.call(this, action, path, messageProps, opers);
}
isDirty(path) {
return this.features.runningTree.isDirty(path || this.state.oakFullpath);
}
getFreshValue(path) {
return page_common_1.getFreshValue.call(this, path);
}
select(entity, selection) {
return page_common_1.select.call(this, entity, selection);
}
checkOperation(entity, operation, checkerTypes, cacheInsensative) {
return this.features.cache.checkOperation(entity, operation, checkerTypes, cacheInsensative);
}
tryExecute(path, action) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
const operations = this.features.runningTree.getOperations(path2);
if (operations?.length) {
for (const oper of operations) {
const { entity, operation } = oper;
const operation2 = action ? {
...operation,
action,
} : operation;
const result = this.checkOperation(entity, operation2);
if (result !== true) {
return result;
}
}
return true;
}
return false;
}
getOperations(path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
return this.features.runningTree.getOperations(path2);
}
refresh(pageNumber, resetTotal) {
return page_common_1.refresh.call(this, pageNumber, resetTotal);
}
loadMore() {
return page_common_1.loadMore.call(this);
}
setId(id, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
return this.features.runningTree.setId(path2, id);
}
unsetId() {
return this.features.runningTree.unsetId(this.state.oakFullpath);
}
getId(path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
return this.features.runningTree.getId(path2);
}
setFilters(filters, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.setNamedFilters(path2, filters);
}
setNamedFilters(filters, refresh, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.setNamedFilters(path2, filters, refresh);
}
getFilters(path) {
if (this.state.oakFullpath) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
const namedFilters = this.features.runningTree.getNamedFilters(path2);
const filters = namedFilters.map(({ filter }) => {
if (typeof filter === 'function') {
return filter();
}
return filter;
});
return filters;
}
}
getFilterByName(name, path) {
if (this.state.oakFullpath) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
const filter = this.features.runningTree.getNamedFilterByName(path2, name);
if (filter?.filter) {
if (typeof filter.filter === 'function') {
return filter.filter();
}
return filter.filter;
}
}
}
addNamedFilter(namedFilter, refresh, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.addNamedFilter(path2, namedFilter, refresh);
}
removeNamedFilter(namedFilter, refresh, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.removeNamedFilter(path2, namedFilter, refresh);
}
removeNamedFilterByName(name, refresh, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.removeNamedFilterByName(path2, name, refresh);
}
setNamedSorters(namedSorters, refresh, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.setNamedSorters(path2, namedSorters, refresh);
}
getSorters(path) {
if (this.state.oakFullpath) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
const namedSorters = this.features.runningTree.getNamedSorters(path2);
const sorters = namedSorters
.map(({ sorter }) => {
if (typeof sorter === 'function') {
return sorter();
}
return sorter;
})
.filter((ele) => !!ele);
return sorters;
}
}
getSorterByName(name, path) {
if (this.state.oakFullpath) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
const sorter = this.features.runningTree.getNamedSorterByName(path2, name);
if (sorter?.sorter) {
if (typeof sorter.sorter === 'function') {
return sorter.sorter();
}
return sorter.sorter;
}
}
}
addNamedSorter(namedSorter, refresh, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.addNamedSorter(path2, namedSorter, refresh);
}
removeNamedSorter(namedSorter, refresh, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.removeNamedSorter(path2, namedSorter, refresh);
}
removeNamedSorterByName(name, refresh, path) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.removeNamedSorterByName(path2, name, refresh);
}
getPagination(path) {
if (this.state.oakFullpath) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
return this.features.runningTree.getPagination(path2);
}
}
setPageSize(pageSize, path) {
if (this.state.oakFullpath) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.setPageSize(path2, pageSize);
}
}
setCurrentPage(currentPage, path) {
(0, assert_1.assert)(currentPage !== 0);
if (this.state.oakEntity && this.state.oakFullpath) {
const path2 = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
this.features.runningTree.setCurrentPage(path2, currentPage);
}
}
subDataEvents(events, callback) {
return this.features.subscriber.sub(events, callback);
}
}
function translateListeners(listeners) {
return {
fn(prevProps, prevState) {
const { state, props } = this;
for (const obs in listeners) {
const keys = obs.split(',').map((ele) => ele.trim());
let changed = false;
for (const k of keys) {
if (k.includes('*')) {
throw new Error('web模式下带*的observer通配符暂不支持');
}
if ((0, lodash_1.get)(props, k) !== (0, lodash_1.get)(prevProps, k) ||
(0, lodash_1.get)(state, k) !== (0, lodash_1.get)(prevState, k)) {
changed = true;
break;
}
}
const prev = {};
const next = {};
if (changed) {
for (const k of keys) {
next[k] = (0, lodash_1.get)(props, k) === undefined
? (0, lodash_1.get)(state, k)
: (0, lodash_1.get)(props, k);
prev[k] = (0, lodash_1.get)(prevProps, k) === undefined
? (0, lodash_1.get)(prevState, k)
: (0, lodash_1.get)(prevProps, k);
}
listeners && listeners[obs] && listeners[obs].call(this, prev, next);
}
}
},
};
}
function createComponent(option, features) {
const { data, methods, lifetimes, getRender, path, listeners } = option;
const { fn } = translateListeners(listeners);
class OakComponentWrapper extends OakComponentBase {
features = features;
oakOption = option;
oakLifetime = 'born';
isReachBottom = false;
methodProps;
defaultProperties;
constructor(props) {
super(props);
const methodProps = {
t: (key, params) => this.t(key, params),
execute: (action, messageProps, path, opers) => {
return this.execute(action, messageProps, path, opers);
},
isDirty: (path) => this.isDirty(path),
aggregate: (aggregation) => {
return this.features.cache.aggregate(this.state.oakEntity, aggregation);
},
refresh: () => {
return this.refresh();
},
setNotification: (data) => {
return this.setNotification(data);
},
setMessage: (data) => {
return this.setMessage(data);
},
navigateTo: (options, state, disableNamespace) => {
return this.navigateTo(options, state, disableNamespace);
},
navigateBack: (delta) => {
return this.navigateBack(delta);
},
redirectTo: (options, state, disableNamespace) => {
return this.redirectTo(options, state, disableNamespace);
},
clean: (lsn, dontPublish, path) => {
return this.clean(lsn, dontPublish, path);
},
checkOperation: (entity, operation, checkerTypes) => {
return this.checkOperation(entity, operation, checkerTypes);
}
};
Object.assign(methodProps, {
addItem: (data, path) => {
return this.addItem(data, path);
},
addItems: (data, path) => {
return this.addItems(data, path);
},
removeItem: (id, path) => {
return this.removeItem(id, path);
},
removeItems: (ids, path) => {
return this.removeItems(ids, path);
},
updateItem: (data, id, action, path) => {
return this.updateItem(data, id, action, path);
},
updateItems: (data, ids, action, path) => {
return this.updateItems(data, ids, action, path);
},
setFilters: (filters, path) => {
return this.setFilters(filters, path);
},
setNamedFilters: (filters, refresh, path) => {
return this.setNamedFilters(filters, refresh, path);
},
addNamedFilter: (filter, refresh, path) => {
return this.addNamedFilter(filter, refresh, path);
},
removeNamedFilter: (filter, refresh, path) => {
return this.removeNamedFilter(filter, refresh, path);
},
removeNamedFilterByName: (name, refresh, path) => {
return this.removeNamedFilterByName(name, refresh, path);
},
setNamedSorters: (sorters, refresh, path) => {
return this.setNamedSorters(sorters, refresh, path);
},
addNamedSorter: (sorter, refresh, path) => {
return this.addNamedSorter(sorter, refresh, path);
},
removeNamedSorter: (sorter, refresh, path) => {
return this.removeNamedSorter(sorter, refresh, path);
},
removeNamedSorterByName: (name, refresh, path) => {
return this.removeNamedSorterByName(name, refresh, path);
},
setPageSize: (pageSize, path) => {
return this.setPageSize(pageSize, path);
},
setCurrentPage: (current, path) => {
return this.setCurrentPage(current, path);
},
loadMore: () => {
return this.loadMore();
},
recoverItem: (id, path) => {
return this.recoverItem(id, path);
},
recoverItems: (ids, path) => {
return this.recoverItems(ids, path);
},
resetItem: (id, path) => {
return this.resetItem(id, path);
},
setId: (id) => {
return this.setId(id);
},
getId: (id) => {
return this.getId();
},
unsetId: () => {
return this.unsetId();
}
});
Object.assign(methodProps, {
update: (data, action, path) => {
return this.update(data, action, path);
},
create: (data, path) => {
return this.create(data, path);
},
remove: (path) => {
return this.remove(path);
},
isCreation: (path) => {
return this.isCreation(path);
},
getId: (path) => this.getId(path),
setId: (id, path) => this.setId(id, path)
});
if (methods) {
for (const m in methods) {
Object.assign(this, {
[m]: (...args) => methods[m].call(this, ...args),
});
Object.assign(methodProps, {
[m]: (...args) => methods[m].call(this, ...args),
});
}
}
const data2 = typeof data === 'function' ? data.call(this) : data;
for (const k in data2) {
if (typeof data2[k] === 'function') {
data2[k] = data2[k].bind(this);
}
}
this.state = Object.assign({}, data2, {
oakLoading: !!option.entity && !!option.projection,
oakLoadingMore: false,
oakPullDownRefreshLoading: false,
oakExecuting: false,
oakDirty: false,
});
this.methodProps = methodProps;
// 处理默认的properties
this.defaultProperties = {};
const { properties } = option;
if (properties) {
for (const property in properties) {
this.defaultProperties[property] = properties[property];
}
}
const createResult = lifetimes?.created && lifetimes.created.call(this);
(0, assert_1.assert)(!(createResult instanceof Promise), 'created方法不能是异步');
this.oakLifetime = 'created';
}
// 编译器只会在page层注入pathcomponent依赖父亲设置的oakPath
iAmThePage() {
return !!this.oakOption.path;
}
isMobile() {
// 现按屏幕宽度判断是否为mobile
return this.props.width === 'xs';
}
supportPullDownRefresh() {
const { oakDisablePulldownRefresh = false } = this.props;
return (this.isMobile() &&
this.iAmThePage() &&
!oakDisablePulldownRefresh);
}
async componentDidMount() {
this.addFeatureSub('locales', () => this.reRender());
if (option.entity) {
this.addFeatureSub('cache', () => this.reRender());
}
lifetimes?.attached && lifetimes.attached.call(this);
if (option.features) {
option.features.forEach(ele => {
if (typeof ele === 'string') {
this.addFeatureSub(ele, () => this.reRender());
}
else {
(0, assert_1.assert)(typeof ele === 'object');
const { feature, behavior, callback } = ele;
this.addFeatureSub(feature, () => {
if (behavior) {
switch (behavior) {
case 'reRender': {
this.reRender();
return;
}
default: {
(0, assert_1.assert)(behavior === 'refresh');
this.refresh(undefined, true);
return;
}
}
}
else if (callback) {
callback.call(this);
}
else {
this.reRender();
}
});
}
});
}
this.oakLifetime = 'attached';
const { oakPath } = this.props;
if (oakPath || path) {
const pathState = page_common_1.onPathSet.call(this, this.oakOption, this.iAmThePage());
/**
* 实际中出现了setState后先调willUnmount析构而callback不被调用的情况此时willUnmount需要将runningTree上的node正确析构
* by Xc 20240806
*/
this.oakFullpath = pathState.oakFullpath;
this.setState(pathState, async () => {
if (this.oakLifetime === 'detached') {
return;
}
try {
const { oakFullpath } = this.state;
if (oakFullpath && !this.features.runningTree.isListChildOrStale(oakFullpath)) {
await this.refresh();
lifetimes?.mature && lifetimes.mature.call(this);
}
else {
this.reRender();
}
if (this.oakLifetime === 'detached') {
return;
}
lifetimes?.ready && await lifetimes.ready.call(this);
}
catch (err) {
if (err instanceof types_1.OakException) {
err.tag2 = true;
}
throw err;
}
if (this.oakLifetime === 'detached') {
return;
}
lifetimes?.show && lifetimes.show.call(this);
this.oakLifetime = 'ready';
});
}
else if (!this.oakOption.entity) {
// 如果没有entity也不需要onPathSet直接走ready
lifetimes?.ready && await lifetimes.ready.call(this);
lifetimes?.show && lifetimes.show.call(this);
this.reRender();
this.oakLifetime = 'ready';
}
}
componentWillUnmount() {
this.unsubscribeAll();
// 这里用this.oakFullpath去析构见上面didMount的注释
this.oakFullpath && page_common_1.destroyNode.call(this, this.iAmThePage(), this.oakFullpath);
lifetimes?.detached && lifetimes.detached.call(this);
this.oakLifetime = 'detached';
}
async componentDidUpdate(prevProps, prevState) {
if (prevProps.oakPath !== this.props.oakPath) {
// oakPath如果是用变量初始化在这里再执行onPathSet如果有entity的结点在此执行ready
(0, assert_1.assert)(this.props.oakPath && this.oakOption.entity);
const pathState = page_common_1.onPathSet.call(this, this.oakOption, this.iAmThePage());
if (pathState.oakFullpath) {
this.oakFullpath = pathState.oakFullpath;
}
this.setState(pathState, async () => {
if (this.oakLifetime === 'detached') {
return;
}
if (prevProps.oakPath === undefined) {
// 如果每个页面都在oakFullpath形成后再渲染子结点这个if感觉是不应该命中的
console.warn('发生了结点先形成再配置oakPath的情况请检查代码修正');
lifetimes?.ready && lifetimes.ready.call(this);
lifetimes?.show && lifetimes.show.call(this);
}
const { oakFullpath } = this.state;
if (oakFullpath && !this.features.runningTree.isListChildOrStale(oakFullpath)) {
await this.refresh();
lifetimes?.mature && lifetimes.mature.call(this);
}
else {
this.reRender();
}
});
}
else if (this.props.oakId !== prevProps.oakId) {
if (this.props.oakId) {
this.setId(this.props.oakId);
}
else {
// 组件销毁时会出现这种情况
this.unsetId();
}
}
fn && fn.call(this, prevProps, prevState);
}
render() {
// 允许复用组件的index.ts这里传入resetRender来重置render
const Render = this.props["#resetRender"] ? this.props["#resetRender"].call(this) : getRender.call(this);
// 传入oakPath或page入口页 需要等待oakFullpath初始化完成
if ((this.props.oakPath || path) && !this.state.oakFullpath) {
return null;
}
// option有entity也需要等待oakFullpath初始化完成
if (this.oakOption.entity && !this.state.oakFullpath) {
return null;
}
return ((0, jsx_runtime_1.jsx)(Render, { methods: this.methodProps, data: {
...this.defaultProperties,
...this.state,
...this.props,
} }));
}
}
;
return OakComponentWrapper;
}
exports.createComponent = createComponent;