@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,290 @@
1
+ import { useToast } from '@chakra-ui/react'
2
+ import { FetchFeedFunction } from '@chem-po/core'
3
+ import { useAuth } from '@chem-po/react'
4
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
5
+ import { UpdatePanelsArgs } from './types'
6
+
7
+ const DEFAULT_LIMIT = 10
8
+ export const useMediaFeed = (
9
+ fetch: FetchFeedFunction,
10
+ onUpdatePanels: (items: UpdatePanelsArgs) => void,
11
+ limit: number = DEFAULT_LIMIT,
12
+ authRequired = false,
13
+ ) => {
14
+ // first element is prev ids, second element is curr ids, third element is next ids
15
+ const [ids, setIds] = useState<[string[] | null, string[] | null, string[] | null]>([
16
+ null,
17
+ null,
18
+ null,
19
+ ])
20
+ const [idIdx, setIdIdx] = useState<number>(0)
21
+ const user = useAuth(s => s.user)
22
+
23
+ const [loading, setLoading] = useState(false)
24
+ const [refreshing, setRefreshing] = useState(false)
25
+ const [error, setError] = useState<string | null>(null)
26
+ const [itemLoading, setItemLoading] = useState(false)
27
+ const [direction, setDirection] = useState<'next' | 'prev' | null>(null)
28
+
29
+ const toast = useToast()
30
+
31
+ const fetchingId = useRef<string | null>(null)
32
+
33
+ const idsRef = useRef<{ prev: string | null; curr: string | null; next: string | null }>({
34
+ prev: null,
35
+ curr: null,
36
+ next: null,
37
+ })
38
+
39
+ const updateCurr = useCallback(
40
+ (id: string | null) => {
41
+ if (id) {
42
+ if (idsRef.current.prev === id) {
43
+ idsRef.current.prev = null
44
+ }
45
+ if (idsRef.current.next === id) {
46
+ idsRef.current.next = null
47
+ }
48
+ }
49
+ idsRef.current.curr = id
50
+ onUpdatePanels({ ...idsRef.current })
51
+ },
52
+ [onUpdatePanels],
53
+ )
54
+
55
+ const updateNext = useCallback(
56
+ (id: string | null) => {
57
+ if (id) {
58
+ if (idsRef.current.prev === id) {
59
+ idsRef.current.prev = null
60
+ }
61
+ if (idsRef.current.curr === id) {
62
+ idsRef.current.curr = null
63
+ }
64
+ }
65
+ idsRef.current.next = id
66
+ onUpdatePanels({ ...idsRef.current })
67
+ },
68
+ [onUpdatePanels],
69
+ )
70
+
71
+ const updatePrev = useCallback(
72
+ (id: string | null) => {
73
+ if (id) {
74
+ if (idsRef.current.curr === id) {
75
+ idsRef.current.curr = null
76
+ }
77
+ if (idsRef.current.next === id) {
78
+ idsRef.current.next = null
79
+ }
80
+ }
81
+ idsRef.current.prev = id
82
+ onUpdatePanels({ ...idsRef.current })
83
+ },
84
+ [onUpdatePanels],
85
+ )
86
+
87
+ const fetchIds = useCallback(
88
+ async (
89
+ startBefore: string | null,
90
+ startAfter: string | null,
91
+ isPrefetch: boolean,
92
+ ): Promise<string[]> => {
93
+ if (!user && authRequired) return []
94
+ if (!isPrefetch) setLoading(true)
95
+
96
+ try {
97
+ const data = await fetch({ limit, startAfter, startBefore })
98
+ if (!isPrefetch) {
99
+ const firstId = data.ids[0]
100
+ const secondId = data.ids[1] || null
101
+ updateCurr(firstId)
102
+ updateNext(secondId)
103
+ updatePrev(startAfter)
104
+ if (startAfter) {
105
+ setIds(s => [s[1], data.ids, null])
106
+ } else if (startBefore) {
107
+ setIds(s => [null, data.ids, s[1]])
108
+ } else {
109
+ setIds([null, data.ids, null])
110
+ }
111
+ const newLastId = data.ids[data.ids.length - 1]
112
+ if (newLastId) {
113
+ fetchIds(null, newLastId, true)
114
+ }
115
+ } else if (startAfter) {
116
+ setIds(s => [s[0], s[1], data.ids])
117
+ } else if (startBefore) {
118
+ setIds(s => [data.ids, s[1], s[2]])
119
+ }
120
+ setLoading(false)
121
+ return data.ids || []
122
+ } catch (err: any) {
123
+ setError(err.message)
124
+ toast({
125
+ title: 'Error fetching feed',
126
+ description: err.message,
127
+ status: 'error',
128
+ duration: 9000,
129
+ isClosable: true,
130
+ })
131
+ }
132
+ setLoading(false)
133
+ return []
134
+ },
135
+ [fetch, toast, user, authRequired, updateCurr, updateNext, updatePrev, limit],
136
+ )
137
+
138
+ // handles prefetching of next items
139
+ const getNextId = useCallback(
140
+ async (currIdx: number, isPrefetch: boolean): Promise<{ id: string; idx: number } | null> => {
141
+ const currIds = ids[1] ?? []
142
+ const nextIds = ids[2] ?? []
143
+ const nextIdx = currIdx + 1
144
+ if (nextIdx < currIds.length) {
145
+ return { id: currIds[nextIdx], idx: nextIdx }
146
+ }
147
+ if (nextIds[0]) {
148
+ const newLastId = nextIds[nextIds.length - 1]
149
+ if (!isPrefetch) {
150
+ setIds(s => [s[1], s[2], null])
151
+ fetchIds(null, newLastId, true)
152
+ }
153
+ return { id: nextIds[0], idx: 0 }
154
+ }
155
+ const fetchedNextIds = await fetchIds(null, currIds[currIds.length - 1], isPrefetch)
156
+ const fetchedNextId = fetchedNextIds[0]
157
+ return fetchedNextId ? { id: fetchedNextId, idx: 0 } : null
158
+ },
159
+ [ids, fetchIds],
160
+ )
161
+
162
+ const getPrevId = useCallback(
163
+ async (currIdx: number, isPrefetch: boolean): Promise<{ id: string; idx: number } | null> => {
164
+ const currIds = ids[1] ?? []
165
+ const prevIds = ids[0] ?? []
166
+ const prevIdx = currIdx - 1
167
+ if (prevIdx >= 0) {
168
+ return { id: currIds[prevIdx], idx: prevIdx }
169
+ }
170
+ if (prevIds[prevIds.length - 1]) {
171
+ const newFirstId = prevIds[0]
172
+ if (!isPrefetch) {
173
+ setIds([null, ids[0], ids[1]])
174
+ fetchIds(newFirstId, null, true)
175
+ }
176
+ return { id: prevIds[prevIds.length - 1], idx: prevIds.length - 1 }
177
+ }
178
+ const fetchedPrevIds = await fetchIds(currIds[0], null, isPrefetch)
179
+ const fetchedPrevId = fetchedPrevIds[fetchedPrevIds.length - 1]
180
+ return fetchedPrevId ? { id: fetchedPrevId, idx: fetchedPrevIds.length - 1 } : null
181
+ },
182
+ [ids, fetchIds],
183
+ )
184
+
185
+ const goNext = useCallback(async () => {
186
+ if (itemLoading) return
187
+ // case 1: next item ID is fetched
188
+ const newPrev = idsRef.current.curr
189
+
190
+ setItemLoading(true)
191
+ const nextIdData = await getNextId(idIdx, false)
192
+ setDirection('next')
193
+ if (nextIdData) {
194
+ const { id: nextId, idx: nextIdx } = nextIdData
195
+ fetchingId.current = nextId
196
+ setIdIdx(nextIdx)
197
+ updateCurr(nextId)
198
+ updateNext(null)
199
+ updatePrev(newPrev)
200
+ getNextId(nextIdx, true).then(newNextId => updateNext(newNextId?.id ?? null))
201
+ } else {
202
+ toast({
203
+ title: 'No more items',
204
+ status: 'info',
205
+ duration: 3000,
206
+ isClosable: true,
207
+ })
208
+ }
209
+ setItemLoading(false)
210
+ }, [idIdx, getNextId, updateCurr, updateNext, updatePrev, toast, itemLoading])
211
+
212
+ const goPrev = useCallback(async () => {
213
+ if (itemLoading) return
214
+ // case 1: next item ID is fetched
215
+ const newNext = idsRef.current.curr
216
+
217
+ setDirection('prev')
218
+ setItemLoading(true)
219
+ const prevIdData = await getPrevId(idIdx, false)
220
+ if (prevIdData) {
221
+ const { id: prevId, idx: prevIdx } = prevIdData
222
+ fetchingId.current = prevId
223
+ setIdIdx(prevIdx)
224
+ updateCurr(prevId)
225
+ updatePrev(null)
226
+ updateNext(newNext)
227
+ getPrevId(prevIdx, true).then(newPrevId => updatePrev(newPrevId?.id ?? null))
228
+ } else {
229
+ toast({
230
+ title: 'No more items',
231
+ status: 'info',
232
+ duration: 3000,
233
+ isClosable: true,
234
+ })
235
+ }
236
+ setItemLoading(false)
237
+ }, [idIdx, getPrevId, updateCurr, updateNext, updatePrev, toast, itemLoading])
238
+
239
+ const refresh = useCallback(async () => {
240
+ setLoading(true)
241
+ setRefreshing(true)
242
+ setIds([null, null, null])
243
+ updateCurr(null)
244
+ updateNext(null)
245
+ updatePrev(null)
246
+ setDirection(null)
247
+ setIdIdx(0)
248
+ await fetchIds(null, null, false)
249
+ setRefreshing(false)
250
+ }, [fetchIds, updateCurr, updateNext, updatePrev])
251
+
252
+ const [initFetchIds] = useState(() => fetchIds)
253
+ useEffect(() => {
254
+ initFetchIds(null, null, false)
255
+ }, [initFetchIds])
256
+
257
+ const canGoPrev = useMemo(() => (idIdx > 0 && !!ids[1]?.length) || !!ids[0]?.length, [ids, idIdx])
258
+ const canGoNext = useMemo(
259
+ () => (ids[1] && idIdx < ids[1].length) ?? !!ids[2]?.length,
260
+ [ids, idIdx],
261
+ )
262
+ return useMemo(
263
+ () => ({
264
+ ids,
265
+ goNext,
266
+ goPrev,
267
+ error,
268
+ refresh,
269
+ refreshing,
270
+ loading,
271
+ canGoNext,
272
+ direction,
273
+ canGoPrev,
274
+ itemLoading,
275
+ }),
276
+ [
277
+ ids,
278
+ goNext,
279
+ goPrev,
280
+ loading,
281
+ error,
282
+ itemLoading,
283
+ canGoNext,
284
+ canGoPrev,
285
+ refresh,
286
+ refreshing,
287
+ direction,
288
+ ],
289
+ )
290
+ }
@@ -0,0 +1,2 @@
1
+ export { useMediaFeed } from './context'
2
+ export * from './MediaFeed'
@@ -0,0 +1,50 @@
1
+ import { AnyObject, FetchFeedFunction, FileValue, WithId } from '@chem-po/core'
2
+ import { JSX } from 'react'
3
+
4
+ export type GetBackgroundUrl<T extends AnyObject = AnyObject> = (
5
+ item: T,
6
+ ) => string | null | undefined
7
+ export type GetBackgroundValue<T extends AnyObject = AnyObject> = (
8
+ item: T,
9
+ ) => FileValue | null | undefined
10
+ export interface MediaFeedProps<T extends AnyObject = AnyObject> {
11
+ collection: string
12
+ fetch: FetchFeedFunction
13
+ authRequired?: boolean
14
+ limit?: number
15
+ // onSwipeUp?: () => void
16
+ swipeDisabled?: boolean
17
+ defaultBackground?: string
18
+ getBackgroundUrl?: GetBackgroundUrl<T>
19
+ getBackgroundValue?: GetBackgroundValue<T>
20
+ RenderItem: (item: WithId<T>) => JSX.Element
21
+ }
22
+
23
+ export type UseMediaFeedProps = Pick<MediaFeedProps, 'collection' | 'fetch'>
24
+
25
+ export interface UseMediaFeed<T extends AnyObject = AnyObject> {
26
+ ids: string[][]
27
+ goNext: () => void
28
+ goPrev: () => void
29
+ error: string | null
30
+ loading: boolean
31
+ itemLoading: boolean
32
+ currItem: WithId<T> | null
33
+ }
34
+ export type SwipeDirection = 'next' | 'prev'
35
+ export interface UpdatePanelsArgs {
36
+ prev: string | null
37
+ curr: string | null
38
+ next: string | null
39
+ }
40
+
41
+ export type PanelStatus = 'current' | 'next' | 'prev'
42
+
43
+ export interface MediaBackgroundRef<T extends AnyObject> {
44
+ onNewData: (data: WithId<T> | null) => void
45
+ }
46
+ export interface PanelData {
47
+ id: string
48
+ status: PanelStatus
49
+ prevStatus?: PanelStatus
50
+ }
@@ -0,0 +1,26 @@
1
+ import { PropsWithChildren, useMemo } from 'react'
2
+ import { useWatch } from 'react-hook-form'
3
+ import { ExpandOnMount } from '../box'
4
+
5
+ export const Condition = ({
6
+ path,
7
+ condition,
8
+ children,
9
+ }: PropsWithChildren<{
10
+ path: string
11
+ condition: (values: any) => boolean
12
+ }>) => {
13
+ // const { register } = useFormContext()
14
+ // const {
15
+ // input: { value },
16
+ // } = useField(path ?? '', { subscription: { value: true } })
17
+ const value = useWatch({ exact: true, name: path })
18
+ const isVisible = useMemo(() => condition(value), [value, condition])
19
+
20
+ if (!isVisible) return null
21
+ return (
22
+ <ExpandOnMount py="3px" animateOpacity in>
23
+ {children}
24
+ </ExpandOnMount>
25
+ )
26
+ }
@@ -0,0 +1,39 @@
1
+ import { InputRef } from '@chem-po/core'
2
+ import { ForwardedRef, forwardRef } from 'react'
3
+
4
+ import { Field, useField } from '@chem-po/react'
5
+ import { ChangeHandler, Controller } from 'react-hook-form'
6
+ import { Input } from './input/input'
7
+
8
+ const FieldComponentBase = (
9
+ { field, name }: { field: Field; name: string },
10
+ ref: ForwardedRef<InputRef>,
11
+ ) => {
12
+ const { control, getOnChange, meta, onFocus, onBlur: fieldBlur } = useField(name, field)
13
+ return (
14
+ <Controller<Field['defaultValue']>
15
+ control={control}
16
+ name={name}
17
+ render={({ field: { name: _, ref: _r, onBlur, ...inputProps } }) => {
18
+ return (
19
+ <Input
20
+ ref={ref}
21
+ field={field}
22
+ input={{
23
+ ...inputProps,
24
+ onFocus,
25
+ onBlur: () => {
26
+ fieldBlur()
27
+ onBlur()
28
+ },
29
+ onChange: getOnChange(inputProps.onChange as ChangeHandler),
30
+ }}
31
+ meta={meta}
32
+ />
33
+ )
34
+ }}
35
+ />
36
+ )
37
+ }
38
+
39
+ export const FieldComponent = forwardRef(FieldComponentBase)