/// import { PANE_LEVELS, PANE_ATTRIBUTES, PANE_LEVEL_KEYS, loadAddressData, loadTownData, traceTownParents, defaultTextSerializer, standardValueEqual, addressTreeOfSet, delay, } from './util'; import React from 'react'; import { NodeID, AddressTree, AddressNode, AddressNodeSet, AddressDataset, NodeSelection, NodeIDStrict, NodeChain, NodeAnyValue, SelectionChain, NodeSelectionInternalData, AddressRequestParams, } from './Types'; import { last } from './lodash-alt'; // import last = require("lodash/fp/last") // import * as last from "lodash/fp/last" // import { Dictionary } from "lodash/fp"; import { ConfigProvider } from '@alifd/next'; // import LocaleProvider from "@alifd/next/lib/locale-provider"; import { ADDRTREE_ROOT_KEY } from './Constant'; export interface AddressCompProps { className?: string; style?: object; requestAddressUrl: string; requestTownUrl: string; requestAddressLevelUrl: string; level: number; hiddenData: NodeIDStrict[]; ignored: NodeIDStrict[]; disabled: boolean; dataOverride: { [key: string]: string[] }; preprocessor: (dataRaw: object) => { [key: string]: string[] }; } export interface AddressCompState { paneRoot: AddressNode | null; addressTree: AddressTree; } type AddressComp = React.Component; // type RequestParam = { // addressURL: string // townURL: string // parentURL: string // ignored: NodeIDStrict[]; // dataOverride: { [key: string]: string[] }; // preprocessor: (dataRaw: { [key: string]: string[] }) => { [key: string]: string[] }; // } export interface AddressCompE extends AddressComp { allNodesSet: AddressNodeSet; futureDatasetTem?: Promise; } export function getRequestParams( props: AddressCompProps ): AddressRequestParams { return { addressURL: props.requestAddressUrl, townURL: props.requestTownUrl, parentURL: props.requestAddressLevelUrl, ignored: props.ignored, dataOverride: props.dataOverride, preprocessor: props.preprocessor, }; } export function setStateAsync(self: AddressComp, updater: any) { return new Promise((resolve, reject) => { // @ts-ignore self.setState(updater, () => resolve()); }); } export async function loadAndStoreDataset( self: AddressComp, params = getRequestParams(self.props) ) { const { countryList, allNodesSet, addressTree, } = await (loadAddressData as Function)(params); const selfE = self as AddressCompE; selfE.allNodesSet = allNodesSet; await setStateAsync(self, { countryList, addressTree, paneRoot: allNodesSet['1'], }); return { addressTree, allNodesSet, }; } export async function dataset( self: AddressComp, params: AddressRequestParams = getRequestParams(self.props) ): Promise { const selfE = self as AddressCompE; if (self.state.paneRoot) { return { addressTree: self.state.addressTree, allNodesSet: (self as AddressCompE).allNodesSet, }; } else { if (!selfE.futureDatasetTem) { selfE.futureDatasetTem = loadAndStoreDataset(self, params); } return await selfE.futureDatasetTem; } } export function selectNodeFromChain( node: AddressNode, chain: NodeID[] ): AddressNode[] { const ret = [node]; if (!chain.length) { return ret; } if (!node.children || node.isLeaf) { throw new Error(`children expected for node ${node.id}, chain ${chain}`); } const nextRoot = node.children!.find(node => node.id == chain[0]); if (!nextRoot) { throw new Error( `children ${chain[0]} expected for node ${node.id}, chain ${chain}` ); } return ret.concat( selectNodeFromChain(nextRoot, chain.slice(1, chain.length)) ); } export function nodeChainFromNode(node: AddressNode): NodeChain { return (!node || !node.parentNode ? [] : nodeChainFromNode(node.parentNode) ).concat(node); } export function foldNodeChainToSelection( nodeChain: AddressNode[] ): NodeSelection { const ret: any = {}; nodeChain.forEach(node => { if (node) { ret[node.levelKey] = node.id; } }); return ret; } export function selectionToChain(value: NodeSelection): SelectionChain { // TODO: fix the dictionary type return ( PANE_LEVEL_KEYS.map(key => (value as any)[key]) // keys array, must be in order // necessary because there is cases that one or more // intermediate levels with no value .filter(v => !!v) ); } export function anyValueToSelectionId(val: NodeAnyValue): NodeIDStrict { switch (typeof val) { case 'string': case 'number': return val.toString(); default: // didn't used switch case // because TS's inference is kind of dumb ... if (typeof val == 'object') { const ret = last(selectionToChain(val as NodeSelection)); if (ret) { return ret; } } throw `invalid address anyValue ${val}`; } } export const addressEqual = (lhs: NodeAnyValue, rhs: NodeAnyValue) => anyValueToSelectionId(lhs) == anyValueToSelectionId(rhs); export const CANNOT_REACH: any = 'CANNOT_REACH'; export function maybeGet(src: T | null | undefined): T { return src as T; } export function selectionChainLastNonstrict(chain: SelectionChain): NodeID { if (chain.length) { return last(chain); } else { return ''; } } export function nodeChainLastIDNonstrict(chain: NodeChain): NodeID { if (chain.length) { return last(chain).id; } else { return ''; } } // export function decorateValueInternal( // value: NodeSelection, nodeChain: NodeChain): NodeSelection { // let lastName = ""; // let id = ""; // if (nodeChain.length) { // const l = last(nodeChain); // id = l.id; // lastName = l.nameZh; // } // Object.defineProperty(value, "__addressValueInternalData", { // get () { // return { nodeChain, lastName, id }; // } // }); // return value; // } // export function valueInternalDecorated(value: NodeSelection, id: string): boolean { // if (!value || typeof value != "object") { return false; } // const v = ((value as any)["__addressValueInternalData"]) as // NodeSelectionInternalData; // return (!!v) && (v.id == id); // } // export function valueInternalDecorated2(value: number | string | NodeSelection): boolean { // if (typeof value == "number" || typeof value == "string") { return false; } // const v = ((value as any)["__addressValueInternalData"]) as // NodeSelectionInternalData; // if (!v) { return false; } // const id = selectionToChain(value); // if (id.toString() != v.id) { return false; } // return true; // } export const id = (v: any) => v; export function getParentSetOf(node: AddressNode): Set { const nodeChain = nodeChainFromNode(node); const ret = new Set(); nodeChain.forEach(v => ret.add(v.id)); // @ts-ignore return ret; } export function isDescendantOfOne( allNodesSet: AddressNodeSet, childId: NodeIDStrict, parentIds: NodeIDStrict[] ) { // TODO: use LCA-like alg. to opt. const childNode = allNodesSet[childId.toString()]; const parents = getParentSetOf(childNode); for (const parentId of parentIds) { if (parentId != childId && parents.has(parentId)) { return true; } } return false; } export const PROVINCE_LETTERS = ['A–G', 'H–K', 'L–S', 'T–Z']; export function provinceLetterParititon(namePy: string): number { const c = namePy[0]; if (c <= 'g') { return 0; } else if (c <= 'k') { return 1; } else if (c <= 's') { return 2; } else { return 3; } } export function lastOr(src: T[], def: T): T { if (src.length) { return src[src.length - 1]; } else { return def; } } export function selectionToId(src: SelectionChain): NodeIDStrict { return lastOr(src, ADDRTREE_ROOT_KEY); } export function getLabelIntl(node: AddressNode): string { if (node.parent == '0') { // return node.label; return ((ConfigProvider as any).getLanguage() == 'zh-cn' ? node.nameZh! : node.label) as any; } else { return node.label; } } export function getNodeLevelInTree(node: AddressNode): number { if ('_cachedLevelInTree' in node) { return (node)._cachedLevelInTree; } function f(node: AddressNode, i: number): number { return node.parentNode ? f(node.parentNode, i + 1) : i; } const ret = f(node, 0); (node)._cachedLevelInTree = ret; return ret; }