Skip to content

Instantly share code, notes, and snippets.

@rqbazan
Last active July 30, 2018 21:23
Show Gist options
  • Save rqbazan/f7aa157bba98248b79ed9699e41a0e6d to your computer and use it in GitHub Desktop.
Save rqbazan/f7aa157bba98248b79ed9699e41a0e6d to your computer and use it in GitHub Desktop.
import React, { Component } from 'react'
import styled, { withTheme } from 'styled-components'
import { Label } from '@riqra/truck'
import { StickyTree } from 'react-virtualized-sticky-tree'
import Measure from 'react-measure'
import { map } from 'lodash'
import { GroupedTree } from 'utils'
const resolveHeight = props => {
const { theme: { dimensions } } = props
const { headerHeight, footerHeight } = dimensions
return `calc(100vh - ${headerHeight}px - ${footerHeight}px)`
}
const Container = styled.div`
display: flex;
width: 100%;
height: ${resolveHeight};
`
const TITLE_HEIGHT = 42
const Title = styled(Label)`
display: flex;
background: ${({ theme }) => theme.colors.grayLight};
padding-bottom: ${({ theme }) => theme.spacing.cozy}px;
padding-left: ${({ theme }) => theme.spacing.comfortable}px;
padding-top: ${({ theme }) => theme.spacing.comfortable}px;
`
class GroupedList extends Component {
state = {
tree: new GroupedTree(),
bounds: {}
}
static getDerivedStateFromProps(props, state) {
const { data, grouper } = props
const newTree = new GroupedTree(data, grouper)
return {
tree: state.tree.concat(newTree)
}
}
getChildren = nodeId => {
const { theme, rowHeight } = this.props
const { tree } = this.state
const node = tree.childAt(nodeId)
if (!node || !node.children) { return }
const mapper = childId => {
const child = tree.childAt(childId)
const isTitle = !!child.children
let height = rowHeight
let zIndex = theme.stack.groupedItem
if (isTitle) {
height = TITLE_HEIGHT
zIndex = theme.stack.stickyTitle
}
return {
id: childId,
isSticky: isTitle,
zIndex,
height
}
}
return map(node.children, mapper)
}
handleOnResize = rectangle => {
this.setState({ bounds: rectangle.bounds })
}
renderRow = options => {
const { renderItem } = this.props
const { tree } = this.state
const { id, style, nodeInfo } = options
const node = tree.childAt(id)
if (nodeInfo.isSticky) {
return (
<Title
key={id}
textStyle="h6Semibold"
style={style}
>
{node.name}
</Title>
)
}
return renderItem(node, nodeInfo)
}
render() {
return (
<Measure
bounds
onResize={this.handleOnResize}
>
{({ measureRef }) => {
const { tree, bounds } = this.state
return (
<Container innerRef={measureRef}>
<StickyTree
height={bounds.height}
width={bounds.width}
renderRoot={false}
root={tree.childAt('root')}
getChildren={this.getChildren}
rowRenderer={this.renderRow}
overscanRowCount={20}
/>
</Container>
)
}}
</Measure>
)
}
}
export default withTheme(GroupedList)
import {
groupBy, keys, last, each, assign, map, pick, drop, mergeWith
} from 'lodash'
export const getRoot = children => {
return {
id: 'root', children, depth: 0, height: 0
}
}
const mergeTrees = (treeA, treeB) => {
const customizer = (current, source) => {
if (Array.isArray(current)) {
return current.concat(source)
}
}
return mergeWith({}, treeA, treeB, customizer)
}
class GroupedTree {
constructor(data, grouper) {
if (!data || !grouper) {
this.tree = { root: getRoot([]) }
return
}
const groups = groupBy(data, grouper)
this.initTree(groups)
}
get lastGroupKey() {
const { root } = this.tree
return last(root.children)
}
initTree = groups => {
const groupKeys = keys(groups)
const tree = { root: getRoot(groupKeys) }
const nodes = map(groups, this.mapGroupToNode)
assign(tree, ...nodes)
this.tree = tree
}
mapGroupToNode = (items, groupKey) => {
const itemKeys = []
const nodes = []
each(items, item => {
const itemKey = item.id
const node = {
[itemKey]: { item, depth: 2 }
}
nodes.push(node)
itemKeys.push(itemKey)
})
const groupNode = {
[groupKey]: {
name: groupKey,
children: itemKeys,
depth: 1
}
}
return assign(groupNode, ...nodes)
}
concat = another => {
if (this.lastGroupKey in another.tree) {
const { root } = another.tree
const children = drop(root.children)
root.children = children
}
const newGroupedTree = new GroupedTree()
const newTree = mergeTrees(this.tree, another.tree)
newGroupedTree.setState(newTree)
return newGroupedTree
}
childAt = nodeId => {
return this.tree[nodeId]
}
getState = () => {
return pick(this, ['tree', 'lastGroupKey'])
}
setState = tree => {
this.tree = tree
}
}
export default GroupedTree
import React from 'react'
import { distanceInWordsToNow } from 'utils'
import GroupedList from '../grouped-list'
import OrderCard from '../order-card'
const grouper = order => {
return distanceInWordsToNow(order.createdAt)
}
const renderItem = (node, nodeInfo) => {
const { id, style } = nodeInfo
return (
<OrderCard
key={id}
style={style}
{...node.item}
/>
)
}
export default props => {
const { data } = props
return (
<GroupedList
data={data}
grouper={grouper}
renderItem={renderItem}
/>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment