@chem-po/react-web 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/index.cjs +2 -2
  2. package/dist/index.js +2 -2
  3. package/package.json +22 -20
  4. package/src/components/auth/SignIn.tsx +43 -0
  5. package/src/components/auth/index.ts +1 -0
  6. package/src/components/box/CollapseHorizontal.tsx +18 -0
  7. package/src/components/box/ContentBox.tsx +17 -0
  8. package/src/components/box/ExpandOnMount.tsx +48 -0
  9. package/src/components/box/Expandable.tsx +96 -0
  10. package/src/components/box/FullSizeContainer.tsx +50 -0
  11. package/src/components/box/MobileFrame/index.tsx +145 -0
  12. package/src/components/box/MobileFrame/styles.css +35 -0
  13. package/src/components/box/index.ts +6 -0
  14. package/src/components/button/DeleteButton.tsx +178 -0
  15. package/src/components/button/Toggle.tsx +88 -0
  16. package/src/components/button/ViewButton.tsx +30 -0
  17. package/src/components/button/index.ts +3 -0
  18. package/src/components/feed/FeedContentPane.tsx +111 -0
  19. package/src/components/feed/MediaFeed.tsx +200 -0
  20. package/src/components/feed/MediaFeedBackground.tsx +127 -0
  21. package/src/components/feed/MediaFeedRefresh.tsx +78 -0
  22. package/src/components/feed/MediaFeedSwipeUp.tsx +34 -0
  23. package/src/components/feed/constants.ts +11 -0
  24. package/src/components/feed/context.tsx +19 -0
  25. package/src/components/feed/hooks.ts +290 -0
  26. package/src/components/feed/index.ts +2 -0
  27. package/src/components/feed/types.ts +50 -0
  28. package/src/components/form/Condition.tsx +26 -0
  29. package/src/components/form/Field.tsx +39 -0
  30. package/src/components/form/Form.tsx +425 -0
  31. package/src/components/form/FormFooter.tsx +82 -0
  32. package/src/components/form/UploadProgress/index.tsx +38 -0
  33. package/src/components/form/UploadProgress/styles.css +23 -0
  34. package/src/components/form/index.ts +4 -0
  35. package/src/components/form/input/Editable.tsx +129 -0
  36. package/src/components/form/input/InputSlider.tsx +75 -0
  37. package/src/components/form/input/OptionalTag.tsx +33 -0
  38. package/src/components/form/input/StandaloneInput.tsx +41 -0
  39. package/src/components/form/input/boolean/index.tsx +53 -0
  40. package/src/components/form/input/color/index.tsx +126 -0
  41. package/src/components/form/input/date/index.tsx +122 -0
  42. package/src/components/form/input/datetime/index.tsx +93 -0
  43. package/src/components/form/input/file.tsx +379 -0
  44. package/src/components/form/input/hooks/index.ts +2 -0
  45. package/src/components/form/input/hooks/useInputImperativeHandle.ts +16 -0
  46. package/src/components/form/input/hooks/useInputStyle.ts +39 -0
  47. package/src/components/form/input/index.ts +2 -0
  48. package/src/components/form/input/input.css +44 -0
  49. package/src/components/form/input/input.tsx +130 -0
  50. package/src/components/form/input/multipleSelect/index.tsx +55 -0
  51. package/src/components/form/input/number/index.tsx +83 -0
  52. package/src/components/form/input/number/styles.css +8 -0
  53. package/src/components/form/input/select/index.tsx +80 -0
  54. package/src/components/form/input/socialMedia/index.tsx +158 -0
  55. package/src/components/form/input/text/index.tsx +72 -0
  56. package/src/components/form/input/text/textarea.tsx +44 -0
  57. package/src/components/form/input/time/index.tsx +33 -0
  58. package/src/components/form/input/type.ts +0 -0
  59. package/src/components/form/input/types.ts +4 -0
  60. package/src/components/form/view/file.tsx +45 -0
  61. package/src/components/form/view/index.tsx +61 -0
  62. package/src/components/form/view/multipleSelect.tsx +38 -0
  63. package/src/components/form/view/select.tsx +33 -0
  64. package/src/components/index.ts +14 -0
  65. package/src/components/list/Body/InfiniteScrollGridBody.tsx +177 -0
  66. package/src/components/list/Body/InfiniteScrollListBody.tsx +114 -0
  67. package/src/components/list/Body/ListBody.tsx +23 -0
  68. package/src/components/list/Body/PagedGridBody.tsx +104 -0
  69. package/src/components/list/Body/PagedListBody.tsx +92 -0
  70. package/src/components/list/Body/hooks.ts +84 -0
  71. package/src/components/list/DataList.tsx +32 -0
  72. package/src/components/list/ListContainer.tsx +20 -0
  73. package/src/components/list/ListContent.tsx +54 -0
  74. package/src/components/list/ListCreate.tsx +57 -0
  75. package/src/components/list/ListFilters.tsx +182 -0
  76. package/src/components/list/ListFooter.tsx +458 -0
  77. package/src/components/list/ListHeader.tsx +180 -0
  78. package/src/components/list/ListItem/ListCell.tsx +48 -0
  79. package/src/components/list/ListItem/ListRow.tsx +38 -0
  80. package/src/components/list/ListItemView.tsx +53 -0
  81. package/src/components/list/ListSort.tsx +84 -0
  82. package/src/components/list/NoItems.tsx +33 -0
  83. package/src/components/list/constants.ts +1 -0
  84. package/src/components/list/index.ts +4 -0
  85. package/src/components/list/types.ts +29 -0
  86. package/src/components/list/utils.ts +62 -0
  87. package/src/components/loading/CircularProgress.tsx +11 -0
  88. package/src/components/loading/Loading.tsx +160 -0
  89. package/src/components/loading/LoadingImage.tsx +123 -0
  90. package/src/components/loading/LoadingSwitch.tsx +78 -0
  91. package/src/components/loading/index.ts +4 -0
  92. package/src/components/media/PlayButton.tsx +94 -0
  93. package/src/components/media/index.ts +1 -0
  94. package/src/components/modal/DefaultModal.tsx +18 -0
  95. package/src/components/modal/DesktopModal.tsx +11 -0
  96. package/src/components/modal/ForceMobile.tsx +7 -0
  97. package/src/components/modal/MobileModal.tsx +89 -0
  98. package/src/components/modal/index.ts +3 -0
  99. package/src/components/modal/type.ts +7 -0
  100. package/src/components/nav/NavBar.tsx +101 -0
  101. package/src/components/nav/index.ts +1 -0
  102. package/src/components/overlay/ImageViewOverlay.tsx +88 -0
  103. package/src/components/overlay/MobileOverlay.tsx +22 -0
  104. package/src/components/overlay/index.ts +2 -0
  105. package/src/components/text/GradientText/index.tsx +16 -0
  106. package/src/components/text/GradientText/styles.css +5 -0
  107. package/src/components/text/NumberTicker.tsx +28 -0
  108. package/src/components/text/index.ts +1 -0
  109. package/src/components/theme/colorMode/DarkModeToggle.tsx +40 -0
  110. package/src/components/theme/colorMode/index.ts +1 -0
  111. package/src/components/theme/index.ts +1 -0
  112. package/src/components/view/ErrorView.tsx +13 -0
  113. package/src/components/view/RedirectView.tsx +42 -0
  114. package/src/components/view/index.ts +2 -0
  115. package/src/contexts/index.ts +1 -0
  116. package/src/contexts/theme.ts +316 -0
  117. package/src/custom.d.ts +4 -0
  118. package/src/hooks/index.ts +1 -0
  119. package/src/hooks/ui/index.ts +1 -0
  120. package/src/hooks/ui/useBorderColor.ts +4 -0
  121. package/src/store/index.ts +1 -0
  122. package/src/store/usePlayer.ts +75 -0
  123. package/src/store/useScreen.ts +22 -0
@@ -0,0 +1,177 @@
1
+ import { useColorMode } from '@chakra-ui/react'
2
+ import { AnyObject, WithId } from '@chem-po/core'
3
+ import { ListGridOptions, useDataList, useFullSize, usePaginatedList } from '@chem-po/react'
4
+ import { useCallback, useEffect, useMemo, useRef } from 'react'
5
+ import {
6
+ FixedSizeGrid,
7
+ GridOnItemsRenderedProps,
8
+ ListOnItemsRenderedProps,
9
+ VariableSizeGrid,
10
+ } from 'react-window'
11
+ import InfiniteLoader from 'react-window-infinite-loader'
12
+ import { GridItemProps, ListCell } from '../ListItem/ListCell'
13
+ import { ListItemProps } from '../ListItem/ListRow'
14
+ import { NoItemsRow } from '../NoItems'
15
+ import { getIsDynamicSize } from '../utils'
16
+ import { useGridDims } from './hooks'
17
+
18
+ export const InfiniteScrollGridBody = <T extends AnyObject>({
19
+ height,
20
+ options,
21
+ }: {
22
+ height: number
23
+ options: ListGridOptions<T>
24
+ }) => {
25
+ const {
26
+ totalCount,
27
+ data: { data: items },
28
+ refetchItem,
29
+ isItemLoaded,
30
+ goNext: fetchMoreData,
31
+ } = usePaginatedList<T>()
32
+ const { list, onSelectItem, mobileLayout, query } = useDataList<T>()
33
+ const { width } = useFullSize()
34
+ const { numCols, numRows, rowHeight, colWidth } = useGridDims<T>(options, width)
35
+ const { previewHeight, mobile } = list
36
+ const { colorMode } = useColorMode()
37
+ const itemData = useMemo<GridItemProps<T>>(
38
+ () => ({
39
+ list,
40
+ items,
41
+ mobileLayout,
42
+ grid: options,
43
+ colorMode,
44
+ numCols,
45
+ onSelect: (item: WithId<T>) => onSelectItem(item._id),
46
+ refetch: refetchItem,
47
+ }),
48
+ [items, list, refetchItem, onSelectItem, mobileLayout, numCols, options, colorMode],
49
+ )
50
+
51
+ const varRef = useRef<VariableSizeGrid | null>(null)
52
+
53
+ const itemHeight = useMemo(
54
+ () => (mobileLayout ? (mobile?.previewHeight ?? previewHeight) : previewHeight),
55
+ [mobile, previewHeight, mobileLayout],
56
+ )
57
+
58
+ useEffect(() => {
59
+ const varGrid = varRef.current
60
+ if (varGrid) {
61
+ varGrid.resetAfterColumnIndex(0)
62
+ varGrid.resetAfterRowIndex(0)
63
+ }
64
+ }, [query])
65
+
66
+ const infiniteLoaderRef = useRef<InfiniteLoader>(null)
67
+ useEffect(() => {
68
+ if (infiniteLoaderRef.current) {
69
+ infiniteLoaderRef.current.resetloadMoreItemsCache(true)
70
+ }
71
+ }, [totalCount])
72
+
73
+ const isDynamicHeight = useMemo(() => getIsDynamicSize(itemHeight), [itemHeight])
74
+
75
+ const itemKey = useCallback(
76
+ ({
77
+ columnIndex,
78
+ rowIndex,
79
+ data,
80
+ }: {
81
+ columnIndex: number
82
+ rowIndex: number
83
+ data: ListItemProps<T>
84
+ }) => {
85
+ const index = rowIndex * numCols + columnIndex
86
+ return data.items[index]?._id || `${index}`
87
+ },
88
+ [numCols],
89
+ )
90
+
91
+ const onRendered = useCallback(
92
+ (
93
+ {
94
+ overscanColumnStartIndex,
95
+ overscanColumnStopIndex,
96
+ overscanRowStartIndex,
97
+ overscanRowStopIndex,
98
+ visibleColumnStartIndex,
99
+ visibleColumnStopIndex,
100
+ visibleRowStartIndex,
101
+ visibleRowStopIndex,
102
+ }: GridOnItemsRenderedProps,
103
+ onItemsRendered: (p: ListOnItemsRenderedProps) => void,
104
+ ) => {
105
+ const start = visibleRowStartIndex * numCols + visibleColumnStartIndex
106
+ const stop = visibleRowStopIndex * numCols + visibleColumnStopIndex
107
+ const overscanStart = overscanRowStartIndex * numCols + overscanColumnStartIndex
108
+ const overscanStop = overscanRowStopIndex * numCols + overscanColumnStopIndex
109
+ onItemsRendered({
110
+ overscanStartIndex: overscanStart,
111
+ overscanStopIndex: overscanStop,
112
+ visibleStartIndex: start,
113
+ visibleStopIndex: stop,
114
+ })
115
+ },
116
+ [numCols],
117
+ )
118
+
119
+ if (!items.length) {
120
+ return <NoItemsRow />
121
+ }
122
+
123
+ return isDynamicHeight ? (
124
+ <InfiniteLoader
125
+ isItemLoaded={isItemLoaded}
126
+ ref={infiniteLoaderRef}
127
+ itemCount={totalCount ?? 0}
128
+ loadMoreItems={fetchMoreData}>
129
+ {({ onItemsRendered, ref }) => (
130
+ <VariableSizeGrid<GridItemProps<T>>
131
+ height={height}
132
+ width={width}
133
+ rowCount={numRows}
134
+ columnCount={numCols}
135
+ rowHeight={rowHeight as (idx: number) => number}
136
+ columnWidth={colWidth as (idx: number) => number}
137
+ itemData={itemData}
138
+ style={{ overflowX: 'hidden' }}
139
+ itemKey={itemKey}
140
+ onItemsRendered={i => onRendered(i, onItemsRendered)}
141
+ ref={r => {
142
+ ref(r)
143
+ varRef.current = r
144
+ }}>
145
+ {ListCell}
146
+ </VariableSizeGrid>
147
+ )}
148
+ </InfiniteLoader>
149
+ ) : (
150
+ <InfiniteLoader
151
+ isItemLoaded={isItemLoaded}
152
+ itemCount={totalCount ?? 0}
153
+ ref={infiniteLoaderRef}
154
+ loadMoreItems={fetchMoreData}>
155
+ {({ onItemsRendered, ref }) => (
156
+ <FixedSizeGrid<GridItemProps<T>>
157
+ height={height}
158
+ width={width}
159
+ rowCount={numRows}
160
+ columnCount={numCols}
161
+ rowHeight={rowHeight as number}
162
+ columnWidth={colWidth as number}
163
+ itemData={itemData}
164
+ onItemsRendered={i => onRendered(i, onItemsRendered)}
165
+ style={{
166
+ overflowX: 'hidden',
167
+ }}
168
+ ref={r => {
169
+ ref(r)
170
+ varRef.current = null
171
+ }}>
172
+ {ListCell}
173
+ </FixedSizeGrid>
174
+ )}
175
+ </InfiniteLoader>
176
+ )
177
+ }
@@ -0,0 +1,114 @@
1
+ import { useColorMode } from '@chakra-ui/react'
2
+ import { AnyObject, WithId } from '@chem-po/core'
3
+ import { useDataList, useFullSize, usePaginatedList } from '@chem-po/react'
4
+ import { useCallback, useEffect, useMemo, useRef } from 'react'
5
+ import { FixedSizeList, VariableSizeGrid, VariableSizeList } from 'react-window'
6
+ import InfiniteLoader from 'react-window-infinite-loader'
7
+ import { ListItemProps, ListRow } from '../ListItem/ListRow'
8
+ import { NoItemsRow } from '../NoItems'
9
+ import { getIsDynamicSize } from '../utils'
10
+
11
+ export const InfiniteScrollListBody = ({ height }: { height: number }) => {
12
+ const {
13
+ totalCount,
14
+ data: { data: items },
15
+ refetchItem,
16
+ isItemLoaded,
17
+ goNext: fetchMoreData,
18
+ } = usePaginatedList()
19
+ const { list, onSelectItem, mobileLayout, query } = useDataList()
20
+ const { previewHeight, mobile } = list
21
+ const { colorMode } = useColorMode()
22
+ const { width } = useFullSize()
23
+ const itemData = useMemo<ListItemProps>(
24
+ () => ({
25
+ list,
26
+ items,
27
+ mobileLayout,
28
+ colorMode,
29
+ onSelect: (item: WithId<AnyObject>) => onSelectItem(item._id),
30
+ refetch: refetchItem,
31
+ }),
32
+ [items, list, refetchItem, onSelectItem, mobileLayout, colorMode],
33
+ )
34
+
35
+ const varRef = useRef<VariableSizeGrid | null>(null)
36
+ const itemHeight = useMemo(
37
+ () => (mobileLayout ? (mobile?.previewHeight ?? previewHeight) : previewHeight),
38
+ [mobile, previewHeight, mobileLayout],
39
+ )
40
+
41
+ const infiniteLoaderRef = useRef<InfiniteLoader>(null)
42
+ useEffect(() => {
43
+ if (infiniteLoaderRef.current) {
44
+ infiniteLoaderRef.current.resetloadMoreItemsCache(true)
45
+ }
46
+ }, [totalCount])
47
+
48
+ useEffect(() => {
49
+ const varGrid = varRef.current
50
+ if (varGrid) {
51
+ varGrid.resetAfterRowIndex(0)
52
+ varGrid.resetAfterColumnIndex(0)
53
+ }
54
+ }, [query])
55
+
56
+ const getItemSize = useCallback(
57
+ (index: number) => (typeof itemHeight === 'function' ? itemHeight(items[index]) : itemHeight),
58
+ [items, itemHeight],
59
+ )
60
+
61
+ const isDynamicHeight = useMemo(() => getIsDynamicSize(itemHeight), [itemHeight])
62
+
63
+ if (!items.length) {
64
+ return <NoItemsRow />
65
+ }
66
+
67
+ return isDynamicHeight ? (
68
+ <InfiniteLoader
69
+ ref={infiniteLoaderRef}
70
+ isItemLoaded={isItemLoaded}
71
+ itemCount={totalCount ?? 0}
72
+ loadMoreItems={fetchMoreData}>
73
+ {({ onItemsRendered, ref }) => (
74
+ <VariableSizeList<ListItemProps>
75
+ height={height}
76
+ width={width}
77
+ itemSize={getItemSize}
78
+ itemCount={totalCount ?? 0}
79
+ itemData={itemData}
80
+ style={{ overflowX: 'hidden' }}
81
+ itemKey={index => items[index]?._id || `${index}`}
82
+ onItemsRendered={i => {
83
+ onItemsRendered(i)
84
+ }}
85
+ ref={ref}>
86
+ {ListRow}
87
+ </VariableSizeList>
88
+ )}
89
+ </InfiniteLoader>
90
+ ) : (
91
+ <InfiniteLoader
92
+ ref={infiniteLoaderRef}
93
+ isItemLoaded={isItemLoaded}
94
+ itemCount={totalCount ?? 0}
95
+ loadMoreItems={fetchMoreData}>
96
+ {({ onItemsRendered, ref }) => (
97
+ <FixedSizeList<ListItemProps>
98
+ height={height}
99
+ width={width}
100
+ itemSize={itemHeight as number}
101
+ itemCount={totalCount ?? 0}
102
+ itemData={itemData}
103
+ itemKey={index => items[index]?._id || `${index}`}
104
+ style={{
105
+ overflowX: 'hidden',
106
+ }}
107
+ onItemsRendered={onItemsRendered}
108
+ ref={ref}>
109
+ {ListRow}
110
+ </FixedSizeList>
111
+ )}
112
+ </InfiniteLoader>
113
+ )
114
+ }
@@ -0,0 +1,23 @@
1
+ import { useDataList } from '@chem-po/react'
2
+ import { InfiniteScrollGridBody } from './InfiniteScrollGridBody'
3
+ import { InfiniteScrollListBody } from './InfiniteScrollListBody'
4
+ import { PagedGridBody } from './PagedGridBody'
5
+ import { PagedListBody } from './PagedListBody'
6
+
7
+ export const ListBody = ({ height }: { height: number }) => {
8
+ const { infiniteScroll, gridLayout, list } = useDataList()
9
+ const { grid } = list
10
+
11
+ if (gridLayout && grid) {
12
+ return infiniteScroll ? (
13
+ <InfiniteScrollGridBody options={grid} height={height} />
14
+ ) : (
15
+ <PagedGridBody options={grid} height={height} />
16
+ )
17
+ }
18
+ return infiniteScroll ? (
19
+ <InfiniteScrollListBody height={height} />
20
+ ) : (
21
+ <PagedListBody height={height} />
22
+ )
23
+ }
@@ -0,0 +1,104 @@
1
+ import { useColorMode } from '@chakra-ui/react'
2
+ import { AnyObject, BaseComponent, ItemPreviewProps, ListGridOptions } from '@chem-po/core'
3
+ import { useDataList, useFullSize, usePaginatedList } from '@chem-po/react'
4
+ import { memo, useEffect, useMemo, useRef } from 'react'
5
+ import { FixedSizeGrid, VariableSizeGrid } from 'react-window'
6
+ import { GridItemProps, ListCell } from '../ListItem/ListCell'
7
+ import { NoItemsRow } from '../NoItems'
8
+ import { getIsDynamicSize } from '../utils'
9
+ import { useGridDims } from './hooks'
10
+
11
+ const BasePagedGridBody = <
12
+ T extends AnyObject,
13
+ ItemPreviewComponent extends BaseComponent<ItemPreviewProps<T>>,
14
+ >({
15
+ height,
16
+ options,
17
+ }: {
18
+ height: number
19
+ options: ListGridOptions<T, ItemPreviewComponent>
20
+ }) => {
21
+ const { list, onSelectItem, mobileLayout, query } = useDataList<T>()
22
+ const {
23
+ data: { data: items },
24
+ pageIndex,
25
+ } = usePaginatedList<T>()
26
+ const variableRef = useRef<VariableSizeGrid>(null)
27
+ const fixedRef = useRef<FixedSizeGrid>(null)
28
+ const { width } = useFullSize()
29
+ const { numCols, numRows, rowHeight, colWidth } = useGridDims<T>(options, width)
30
+ const { colorMode } = useColorMode()
31
+ const itemData = useMemo<GridItemProps<T>>(
32
+ () => ({
33
+ list,
34
+ items,
35
+ numCols,
36
+ onSelect: item => onSelectItem(item._id),
37
+ mobileLayout,
38
+ colorMode,
39
+ grid: options,
40
+ }),
41
+ [items, list, onSelectItem, mobileLayout, numCols, options, colorMode],
42
+ )
43
+
44
+ useEffect(() => {
45
+ const varGrid = variableRef.current
46
+ if (varGrid) {
47
+ varGrid.resetAfterColumnIndex(0)
48
+ varGrid.resetAfterRowIndex(0)
49
+ }
50
+ }, [query])
51
+
52
+ // scroll to top when page or query changes
53
+ useEffect(() => {
54
+ const varGrid = variableRef.current
55
+ const fixGrid = fixedRef.current
56
+ if (varGrid) {
57
+ varGrid.scrollTo({ scrollLeft: 0, scrollTop: 0 })
58
+ }
59
+ if (fixGrid) {
60
+ fixGrid.scrollTo({ scrollLeft: 0, scrollTop: 0 })
61
+ }
62
+ }, [pageIndex, query])
63
+
64
+ const isDynamicHeight = useMemo(
65
+ () => getIsDynamicSize(options.rowHeight) || getIsDynamicSize(options.columnWidth),
66
+ [options],
67
+ )
68
+
69
+ if (!items.length) return <NoItemsRow />
70
+
71
+ return isDynamicHeight ? (
72
+ <VariableSizeGrid<GridItemProps<T>>
73
+ ref={variableRef}
74
+ height={height}
75
+ width={width}
76
+ rowCount={numRows}
77
+ columnCount={numCols}
78
+ rowHeight={rowHeight as (idx: number) => number}
79
+ columnWidth={colWidth as (idx: number) => number}
80
+ itemData={itemData}
81
+ style={{
82
+ overflowX: 'hidden',
83
+ }}>
84
+ {ListCell}
85
+ </VariableSizeGrid>
86
+ ) : (
87
+ <FixedSizeGrid<GridItemProps<T>>
88
+ height={height}
89
+ ref={fixedRef}
90
+ width={width}
91
+ rowCount={numRows}
92
+ columnCount={numCols}
93
+ rowHeight={rowHeight as number}
94
+ columnWidth={colWidth as number}
95
+ itemData={itemData}
96
+ style={{
97
+ overflowX: 'hidden',
98
+ }}>
99
+ {ListCell}
100
+ </FixedSizeGrid>
101
+ )
102
+ }
103
+
104
+ export const PagedGridBody = memo(BasePagedGridBody) as typeof BasePagedGridBody
@@ -0,0 +1,92 @@
1
+ import { useColorMode } from '@chakra-ui/react'
2
+ import { useDataList, useFullSize, usePaginatedList } from '@chem-po/react'
3
+ import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
4
+ import { FixedSizeList, VariableSizeList } from 'react-window'
5
+ import { ListItemProps, ListRow } from '../ListItem/ListRow'
6
+ import { NoItemsRow } from '../NoItems'
7
+ import { getIsDynamicSize } from '../utils'
8
+
9
+ const BasePagedListBody = ({ height }: { height: number }) => {
10
+ const { list, onSelectItem, mobileLayout, query } = useDataList()
11
+ const {
12
+ data: { data: items },
13
+ pageIndex,
14
+ } = usePaginatedList()
15
+ const { width } = useFullSize()
16
+ const { previewHeight, mobile } = list
17
+ const varRef = useRef<VariableSizeList>(null)
18
+ const fixedRef = useRef<FixedSizeList>(null)
19
+ const { colorMode } = useColorMode()
20
+ const itemData = useMemo<ListItemProps>(
21
+ () => ({
22
+ list,
23
+ colorMode,
24
+ items,
25
+ onSelect: item => onSelectItem(item._id),
26
+ mobileLayout,
27
+ }),
28
+ [items, list, onSelectItem, mobileLayout, colorMode],
29
+ )
30
+
31
+ const itemHeight = useMemo(
32
+ () => (mobileLayout ? (mobile?.previewHeight ?? previewHeight) : previewHeight),
33
+ [mobile, previewHeight, mobileLayout],
34
+ )
35
+
36
+ useEffect(() => {
37
+ const varList = varRef.current
38
+ if (varList) {
39
+ varList.resetAfterIndex(0)
40
+ }
41
+ }, [query])
42
+
43
+ // scroll to top when page or query changes
44
+ useEffect(() => {
45
+ const varList = varRef.current
46
+ const fixList = fixedRef.current
47
+ if (varList) {
48
+ varList.scrollTo(0)
49
+ }
50
+ if (fixList) {
51
+ fixList.scrollTo(0)
52
+ }
53
+ }, [query, pageIndex])
54
+
55
+ const getItemSize = useCallback(
56
+ (index: number) => (typeof itemHeight === 'function' ? itemHeight(items[index]) : itemHeight),
57
+ [items, itemHeight],
58
+ )
59
+
60
+ const isDynamicHeight = useMemo(() => getIsDynamicSize(itemHeight), [itemHeight])
61
+
62
+ if (!items.length) return <NoItemsRow />
63
+ return isDynamicHeight ? (
64
+ <FixedSizeList<ListItemProps>
65
+ ref={fixedRef}
66
+ height={height}
67
+ width={width}
68
+ itemSize={itemHeight as number}
69
+ itemCount={items.length}
70
+ itemData={itemData}
71
+ style={{
72
+ overflowX: 'hidden',
73
+ }}>
74
+ {ListRow}
75
+ </FixedSizeList>
76
+ ) : (
77
+ <VariableSizeList<ListItemProps>
78
+ ref={varRef}
79
+ height={height}
80
+ width={width}
81
+ itemSize={getItemSize}
82
+ itemCount={items.length}
83
+ itemData={itemData}
84
+ style={{
85
+ overflowX: 'hidden',
86
+ }}>
87
+ {ListRow}
88
+ </VariableSizeList>
89
+ )
90
+ }
91
+
92
+ export const PagedListBody = memo(BasePagedListBody) as typeof BasePagedListBody
@@ -0,0 +1,84 @@
1
+ import { AnyObject } from '@chem-po/core'
2
+ import { DataList, ListGridOptions, usePaginatedList } from '@chem-po/react'
3
+ import { useMemo } from 'react'
4
+
5
+ // determine number of columns and rows based on data and grid options
6
+ // if row height and column width are both variable, throw an error
7
+ // if row height is variable, calculate dims based on data
8
+ // if column width is variable, calculate dims based on data
9
+ interface CellDims {
10
+ numRows: number
11
+ numCols: number
12
+ }
13
+ const getCellDims = <T extends AnyObject>(
14
+ data: T[],
15
+ grid: Required<DataList<T>>['grid'],
16
+ gridWidth: number,
17
+ ): CellDims => {
18
+ const { rowHeight, columnWidth } = grid
19
+ if (typeof rowHeight !== 'number' && typeof columnWidth !== 'number') {
20
+ throw new Error('Cannot have both variable row height and variable column width')
21
+ }
22
+
23
+ if (typeof columnWidth === 'function') {
24
+ const numCols = Math.floor(gridWidth / columnWidth(data[0]))
25
+ const numRows = Math.ceil(data.length / numCols)
26
+ return { numRows, numCols }
27
+ }
28
+ const numCols = Math.max(1, Math.floor(gridWidth / columnWidth))
29
+ const numRows = Math.ceil(data.length / numCols)
30
+ return { numRows, numCols }
31
+ }
32
+
33
+ type CellSize = CellDims & {
34
+ rowHeight: number | ((idx: number) => number)
35
+ colWidth: number | ((idx: number) => number)
36
+ }
37
+
38
+ const getCellSize = <T extends AnyObject>(
39
+ data: T[],
40
+ grid: Required<DataList<T>>['grid'],
41
+ gridWidth: number,
42
+ ): CellSize => {
43
+ const { rowHeight: preferredHeight, columnWidth: preferredWidth } = grid
44
+ const { numRows, numCols } = getCellDims(data, grid, gridWidth)
45
+
46
+ const colWidths =
47
+ typeof preferredWidth === 'number'
48
+ ? gridWidth / numCols
49
+ : Array.from({ length: numCols }, (_i, i) => {
50
+ const col = data.filter((_j, j) => j % numCols === i)
51
+ return col.reduce((acc, item) => {
52
+ const itemWidth = preferredWidth(item)
53
+ return Math.max(acc, itemWidth)
54
+ }, 0)
55
+ })
56
+
57
+ const rowHeights =
58
+ typeof preferredHeight === 'number'
59
+ ? preferredHeight
60
+ : Array.from({ length: numRows }, (_, i) => {
61
+ const row = data.slice(i * numCols, (i + 1) * numCols)
62
+ return row.reduce((acc, item) => {
63
+ const itemHeight = preferredHeight(item)
64
+ return Math.max(acc, itemHeight)
65
+ }, 0)
66
+ })
67
+
68
+ const rowHeight = Array.isArray(rowHeights) ? (idx: number) => rowHeights[idx] : rowHeights
69
+ const colWidth = Array.isArray(colWidths) ? (idx: number) => colWidths[idx] : colWidths
70
+
71
+ return {
72
+ rowHeight,
73
+ colWidth,
74
+ numCols,
75
+ numRows,
76
+ }
77
+ }
78
+
79
+ export const useGridDims = <T extends AnyObject>(grid: ListGridOptions<T>, width: number) => {
80
+ const {
81
+ data: { data },
82
+ } = usePaginatedList<T>()
83
+ return useMemo(() => getCellSize(data, grid, width), [data, grid, width])
84
+ }
@@ -0,0 +1,32 @@
1
+ import { AnyObject } from '@chem-po/core'
2
+ import {
3
+ PaginatedListProvider,
4
+ useBackendBase,
5
+ useDataListData,
6
+ usePaginatedQuery,
7
+ } from '@chem-po/react'
8
+ import { ListContainer } from './ListContainer'
9
+ import { ListContent } from './ListContent'
10
+ import { ListViewProps } from './types'
11
+
12
+ export const DataList = <T extends AnyObject>({
13
+ list,
14
+ basePath,
15
+ flexProps,
16
+ infiniteScroll,
17
+ ...rest
18
+ }: ListViewProps<T>) => {
19
+ const {
20
+ adapter: { db },
21
+ } = useBackendBase()
22
+ const { baseQuery } = list
23
+ const state = useDataListData(list, infiniteScroll, basePath)
24
+ const data = usePaginatedQuery<T>(db, baseQuery, !!infiniteScroll)
25
+ return (
26
+ <PaginatedListProvider<T> state={state} data={data}>
27
+ <ListContainer {...flexProps}>
28
+ <ListContent {...rest} />
29
+ </ListContainer>
30
+ </PaginatedListProvider>
31
+ )
32
+ }
@@ -0,0 +1,20 @@
1
+ import { Flex, FlexProps } from '@chakra-ui/react'
2
+ import { useMounted } from '@chem-po/react'
3
+ import { FullSizeProvider } from '../box'
4
+
5
+ export const ListContainer = (props: FlexProps) => {
6
+ const mounted = useMounted()
7
+ return (
8
+ <FullSizeProvider>
9
+ <Flex
10
+ overflow="hidden"
11
+ flexFlow="column"
12
+ h="100%"
13
+ w="100%"
14
+ opacity={mounted ? 1 : 0}
15
+ transition="opacity 300ms"
16
+ {...props}
17
+ />
18
+ </FullSizeProvider>
19
+ )
20
+ }