UNPKG

canner

Version:

Build CMS in few lines of code for different data sources

212 lines (197 loc) 6.16 kB
// @flow import * as React from 'react'; import {Spin, Icon} from 'antd'; import RefId from 'canner-ref-id'; import Toolbar from '../components/toolbar/index'; import {mapValues} from 'lodash'; import type {HOCProps} from './types'; import {parseConnectionToNormal, getValue, defaultValue} from './utils'; import {withApollo} from 'react-apollo'; import gql from 'graphql-tag'; import {Query} from '../query'; import {List} from 'react-content-loader'; type State = { originRootValue: any, isFetching: boolean, current: number } type Props = HOCProps & { client: any } @withApollo export default function withQuery(Com: React.ComponentType<*>) { // this hoc will fetch data; return class ComponentWithQuery extends React.PureComponent<Props, State> { query: Query; constructor(props: Props) { super(props); this.state = { originRootValue: null, isFetching: true, current: 0 }; if (props.relation) { this.query = new Query({schema: props.schema}); } } componentDidMount() { const {relation, toolbar} = this.props; if (!relation) { return; } let args = this.getArgs(); if (toolbar && toolbar.async) { args = {...args, first: 10} } if (!toolbar || !toolbar.async) { args.first = undefined; delete args.last; delete args.after; delete args.before; } if (toolbar && toolbar.async && toolbar.filter && toolbar.filter.permanentFilter) { args = {...args, where: toolbar.filter.permanentFilter}; } // this method will also query data this.updateQuery([relation.to], args); } UNSAFE_componentWillReceiveProps(props: Props) { const {refId, relation} = this.props; if (!relation) { return; } if (refId.toString() !== props.refId.toString()) { // refetch when route change this.queryData(props); } } queryData = (props?: Props): Promise<*> => { const {relation, client, type, graphql, variables, fetchPolicy, beforeFetch} = props || this.props; if (!relation) { return Promise.resolve(); } this.setState({ isFetching: true, }); if (type === 'relation' && graphql) { let finalQuery = graphql; let finalVariables = variables || this.query.getVairables(); if (beforeFetch) { const updated = beforeFetch(relation.to, { relation, query: finalQuery, variables: finalVariables }); finalQuery = updated.query; finalVariables = updated.variables; } return client.query({ query: gql`${finalQuery}`, variables: finalVariables, fetchPolicy }).then(({data, error, errors}) => { if (error) { throw new Error(errors); } return data }).then(this.updateData); } let finalQuery = this.query.toGQL(relation.to); let finalVariables = this.query.getVairables(); if (beforeFetch) { const updated = beforeFetch(relation.to, { relation, query: finalQuery, variables: finalVariables }); finalQuery = updated.query; finalVariables = updated.variables; } return client.query({ query: gql`${finalQuery}`, variables: finalVariables, fetchPolicy }).then(({data}) => { this.updateData(data); }) .catch(() => { this.setState({ isFetching: false }) }); } updateData = (data: Object) => { this.setState({ originRootValue: data, isFetching: false, }); } getArgs = () => { const {relation} = this.props; const queries = this.query.getQueries([relation.to]).args || {pagination: {first: 10}}; const variables = this.query.getVairables(); const args = mapValues(queries, v => variables[v.substr(1)]); return args; } updateQuery = (paths: Array<string>, args: Object) => { this.query.updateQueries(paths, 'args', args); this.queryData(); } render() { let {originRootValue, isFetching} = this.state; const {toolbar, relation, schema, refId} = this.props; if (!relation) { return <Com {...this.props}/>; } if (!originRootValue) { return <List style={{maxWidth: 500}} />; } const args = this.getArgs(); const removeSelfRootValue = {[relation.to]: removeSelf(originRootValue[relation.to], refId, relation.to)}; let parsedRootValue = removeSelfRootValue; const tb = ({children, ...restProps}) => <Toolbar {...restProps} items={schema[relation.to].items.items} toolbar={toolbar || {pagination: {type: 'pagination'}}} args={args} query={this.query} keyName={relation.to} refId={new RefId(relation.to)} originRootValue={parsedRootValue} updateQuery={this.updateQuery} parseConnectionToNormal={parseConnectionToNormal} getValue={getValue} defaultValue={defaultValue} > {/* $FlowFixMe */} <SpinWrapper isFetching={isFetching}> {children} </SpinWrapper> </Toolbar>; return <Com {...this.props} isRelationFetching={isFetching} relationArgs={args} updateRelationQuery={this.updateQuery} Toolbar={tb} relationValue={removeSelfRootValue[relation.to]}/>; } }; } export function removeSelf(value: any, refId: RefId, relationTo: string) { const [key, index] = refId.getPathArr().slice(0, 2); if (key !== relationTo) { return value; } return {...value, edges: value.edges.filter((v, i) => i !== Number(index))}; } const antIcon = <Icon type="loading" style={{fontSize: 24}} spin />; function SpinWrapper({ isFetching, children, value }: { isFetching: boolean, children: Function, value: any }): React.Element<*> { return ( <Spin indicator={antIcon} spinning={isFetching}> {children(value)} </Spin> ) }