1 |
|
2 | import { h } from 'preact'
|
3 | import { connect } from 'redux-bundler-preact'
|
4 | import getComponent from '../utils/get-component'
|
5 | import { Crate } from './crate'
|
6 | const Wrapper = getComponent('relative border-box')
|
7 |
|
8 | const defaultMargin = 2
|
9 | const defaultWidth = 18
|
10 | const defaultHeight = 18
|
11 |
|
12 | const toPixels = amount => amount * 16
|
13 |
|
14 | const getStylesForIndex = (
|
15 | numColumns,
|
16 | index,
|
17 | width = defaultWidth,
|
18 | height = defaultHeight,
|
19 | margin = defaultMargin
|
20 | ) => {
|
21 | const column = index % numColumns
|
22 | const row = Math.floor(index / numColumns)
|
23 | const isSingle = numColumns === 1
|
24 | const totalHeight = height + margin
|
25 | const totalWidth = width + margin
|
26 | return {
|
27 | position: 'absolute',
|
28 | top: 0,
|
29 | left: 0,
|
30 | width: isSingle ? '100%' : `${width}rem`,
|
31 | height: `${height}rem`,
|
32 | transform: `translate(${column * totalWidth}rem, ${row * totalHeight}rem)`
|
33 | }
|
34 | }
|
35 |
|
36 | const getContainerStyles = (
|
37 | numColumns,
|
38 | numItems,
|
39 | width = defaultWidth,
|
40 | height = defaultHeight,
|
41 | margin = defaultMargin
|
42 | ) => {
|
43 | const singleColumn = numColumns === 1
|
44 | return {
|
45 | width: singleColumn
|
46 | ? '100%'
|
47 | : `${numColumns * (width + margin) - margin}rem`,
|
48 | height: `${Math.ceil(numItems / numColumns) * (width + margin) - margin}rem`
|
49 | }
|
50 | }
|
51 |
|
52 | const getTotalWidth = (count, widthInPixels, marginInPixels) =>
|
53 | widthInPixels * count + marginInPixels * (count - 1)
|
54 |
|
55 | const getColumnInfo = (
|
56 | viewportWidth,
|
57 | widthToIgnore = 0,
|
58 | width = defaultWidth,
|
59 | margin = defaultMargin,
|
60 | maxColumns = 3
|
61 | ) => {
|
62 | const marginInPixels = toPixels(margin)
|
63 | const widthInPixels = toPixels(width)
|
64 | const totalPixelWidthToIgnore = marginInPixels * 2 + toPixels(widthToIgnore)
|
65 | const maxAvailableWidth = viewportWidth - totalPixelWidthToIgnore
|
66 |
|
67 | let count = 1
|
68 | let numColumns = count
|
69 |
|
70 | while (count <= maxColumns) {
|
71 | const totalWidth = getTotalWidth(count, widthInPixels, marginInPixels)
|
72 | if (totalWidth > maxAvailableWidth) {
|
73 | break
|
74 | } else {
|
75 | numColumns = count
|
76 | }
|
77 | count++
|
78 | }
|
79 |
|
80 | return {
|
81 | numColumns,
|
82 | internalContainerWidth: getTotalWidth(
|
83 | numColumns,
|
84 | widthInPixels,
|
85 | marginInPixels
|
86 | )
|
87 | }
|
88 | }
|
89 |
|
90 | export default connect('selectViewportWidth', props => {
|
91 | const {
|
92 | children,
|
93 | className,
|
94 | height,
|
95 | introComponent,
|
96 | margin,
|
97 | viewportWidth,
|
98 | width,
|
99 | widthToIgnore
|
100 | } = props
|
101 |
|
102 | const { numColumns, internalContainerWidth } = getColumnInfo(
|
103 | viewportWidth,
|
104 | widthToIgnore,
|
105 | width,
|
106 | height,
|
107 | margin
|
108 | )
|
109 |
|
110 | const externalContainerWidth =
|
111 | internalContainerWidth + toPixels((margin || defaultMargin) * 2)
|
112 |
|
113 | const newChildren = children.map((comp, index) => (
|
114 | <Crate
|
115 | className='tr-tr'
|
116 | style={getStylesForIndex(numColumns, index, width, height, margin)}
|
117 | >
|
118 | {comp}
|
119 | </Crate>
|
120 | ))
|
121 |
|
122 | const newProps = Object.assign({}, props, {
|
123 | introComponent: null,
|
124 | children: null,
|
125 | width: null,
|
126 | height: null,
|
127 | margin: null,
|
128 | viewportWidth: null,
|
129 | numColumns: null,
|
130 | widthToIgnore: null,
|
131 | className: null
|
132 | })
|
133 |
|
134 | const containerStyles = getContainerStyles(
|
135 | numColumns,
|
136 | newChildren.length,
|
137 | width,
|
138 | height,
|
139 | margin
|
140 | )
|
141 |
|
142 | const isSingle = numColumns === 1
|
143 |
|
144 | const outsideMargin = toPixels(margin || defaultMargin)
|
145 |
|
146 | return (
|
147 | <div
|
148 | style={{
|
149 | padding: outsideMargin + 'px',
|
150 | width: isSingle ? '100%' : externalContainerWidth + 'px'
|
151 | }}
|
152 | className={`center ${className || ''}`}
|
153 | >
|
154 | {introComponent}
|
155 | <Wrapper
|
156 | {...newProps}
|
157 | style={{ width: containerStyles.width, height: containerStyles.height }}
|
158 | >
|
159 | {newChildren}
|
160 | </Wrapper>
|
161 | </div>
|
162 | )
|
163 | })
|