@tellescope/react-components 1.182.0 → 1.184.0

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 (66) hide show
  1. package/lib/cjs/Forms/hooks.d.ts.map +1 -1
  2. package/lib/cjs/Forms/hooks.js +2 -1
  3. package/lib/cjs/Forms/hooks.js.map +1 -1
  4. package/lib/cjs/forms.d.ts +2 -1
  5. package/lib/cjs/forms.d.ts.map +1 -1
  6. package/lib/cjs/forms.js +2 -2
  7. package/lib/cjs/forms.js.map +1 -1
  8. package/lib/cjs/inputs_shared.d.ts +43 -0
  9. package/lib/cjs/inputs_shared.d.ts.map +1 -1
  10. package/lib/cjs/inputs_shared.js +441 -3
  11. package/lib/cjs/inputs_shared.js.map +1 -1
  12. package/lib/cjs/layout.d.ts.map +1 -1
  13. package/lib/cjs/layout.js +4 -2
  14. package/lib/cjs/layout.js.map +1 -1
  15. package/lib/cjs/state.d.ts +6 -1
  16. package/lib/cjs/state.d.ts.map +1 -1
  17. package/lib/cjs/state.js +38 -57
  18. package/lib/cjs/state.js.map +1 -1
  19. package/lib/cjs/table.d.ts +8 -3
  20. package/lib/cjs/table.d.ts.map +1 -1
  21. package/lib/cjs/table.js +16 -13
  22. package/lib/cjs/table.js.map +1 -1
  23. package/lib/esm/CMS/components.d.ts +1 -0
  24. package/lib/esm/CMS/components.d.ts.map +1 -1
  25. package/lib/esm/Forms/form_responses.d.ts +1 -0
  26. package/lib/esm/Forms/form_responses.d.ts.map +1 -1
  27. package/lib/esm/Forms/forms.d.ts +3 -3
  28. package/lib/esm/Forms/hooks.d.ts.map +1 -1
  29. package/lib/esm/Forms/hooks.js +2 -1
  30. package/lib/esm/Forms/hooks.js.map +1 -1
  31. package/lib/esm/Forms/inputs.d.ts +1 -1
  32. package/lib/esm/Forms/inputs.native.d.ts +1 -0
  33. package/lib/esm/Forms/inputs.native.d.ts.map +1 -1
  34. package/lib/esm/controls.d.ts +2 -2
  35. package/lib/esm/forms.d.ts +2 -1
  36. package/lib/esm/forms.d.ts.map +1 -1
  37. package/lib/esm/forms.js +2 -2
  38. package/lib/esm/forms.js.map +1 -1
  39. package/lib/esm/inputs.d.ts +1 -1
  40. package/lib/esm/inputs.native.d.ts +1 -0
  41. package/lib/esm/inputs.native.d.ts.map +1 -1
  42. package/lib/esm/inputs_shared.d.ts +43 -0
  43. package/lib/esm/inputs_shared.d.ts.map +1 -1
  44. package/lib/esm/inputs_shared.js +436 -5
  45. package/lib/esm/inputs_shared.js.map +1 -1
  46. package/lib/esm/layout.d.ts.map +1 -1
  47. package/lib/esm/layout.js +4 -2
  48. package/lib/esm/layout.js.map +1 -1
  49. package/lib/esm/state.d.ts +292 -287
  50. package/lib/esm/state.d.ts.map +1 -1
  51. package/lib/esm/state.js +38 -57
  52. package/lib/esm/state.js.map +1 -1
  53. package/lib/esm/table.d.ts +8 -3
  54. package/lib/esm/table.d.ts.map +1 -1
  55. package/lib/esm/table.js +16 -13
  56. package/lib/esm/table.js.map +1 -1
  57. package/lib/esm/theme.native.d.ts +1 -0
  58. package/lib/esm/theme.native.d.ts.map +1 -1
  59. package/lib/tsconfig.tsbuildinfo +1 -1
  60. package/package.json +9 -9
  61. package/src/Forms/hooks.tsx +2 -1
  62. package/src/forms.tsx +3 -2
  63. package/src/inputs_shared.tsx +451 -6
  64. package/src/layout.tsx +10 -1
  65. package/src/state.tsx +38 -46
  66. package/src/table.tsx +31 -5
package/src/state.tsx CHANGED
@@ -165,7 +165,7 @@ export const WithFetchContext = ( { children } : { children: React.ReactNode })
165
165
  }
166
166
 
167
167
  // doesn't throw
168
- export const toLoadedData = async <T,>(p: () => Promise<T>): Promise<{
168
+ export const toLoadedData = async <T,>(p: () => Promise<T>, o?: { valueOnError?: T }): Promise<{
169
169
  status: LoadingStatus.Loaded, value: T,
170
170
  } | {
171
171
  status: LoadingStatus.Error, value: APIError
@@ -173,6 +173,7 @@ export const toLoadedData = async <T,>(p: () => Promise<T>): Promise<{
173
173
  try {
174
174
  return { status: LoadingStatus.Loaded, value: await p() }
175
175
  } catch(err: any) {
176
+ if (o?.valueOnError) { return { status: LoadingStatus.Loaded, value: o.valueOnError } }
176
177
  return { status: LoadingStatus.Error, value: err }
177
178
  }
178
179
  }
@@ -522,6 +523,7 @@ export interface LoadMoreOptions <T> {
522
523
  key?: string,
523
524
  limit?: number,
524
525
  filter?: ReadFilter<T> | undefined
526
+ mdbFilter?: Record<string, any>,
525
527
  }
526
528
 
527
529
  export interface LoadMoreFunctions<T> {
@@ -1030,25 +1032,25 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
1030
1032
  const sort = loadOptions?.sort ?? options?.sort
1031
1033
  const sortBy = loadOptions?.sortBy ?? options?.sortBy
1032
1034
 
1035
+ const _mdbFilter = loadOptions?.mdbFilter || options?.mdbFilter
1036
+ const mdbFilter = (_mdbFilter && _mdbFilter?.$and?.length) ? _mdbFilter : undefined
1037
+
1033
1038
  if (!loadQuery) return
1034
1039
  if (options?.dontFetch && !force) return
1035
- const fetchKey = (loadFilter || sort || sortBy) ? JSON.stringify({ ...loadFilter, sort, sortBy }) + modelName : modelName
1040
+ const fetchKey = (mdbFilter || loadFilter || sort || sortBy) ? JSON.stringify({ ...mdbFilter, ...loadFilter, sort, sortBy }) + modelName : modelName
1036
1041
 
1037
1042
  if (didFetch(fetchKey, force, options?.refetchInMS)) return
1038
1043
  setFetched(fetchKey, true)
1039
1044
 
1040
1045
  const limit = options?.limit || DEFAULT_FETCH_LIMIT
1041
- toLoadedData(() => loadQuery({ filter: loadFilter, limit, sort, sortBy })).then(
1046
+ toLoadedData(() => loadQuery({ mdbFilter, filter: loadFilter, limit, sort, sortBy }), { valueOnError: mdbFilter ? [] : undefined }).then(
1042
1047
  es => {
1043
1048
  if (es.status === LoadingStatus.Loaded) {
1044
- if (es.value.length < limit && !loadFilter) {
1049
+ if (es.value.length < limit && !loadFilter && !mdbFilter) {
1045
1050
  setFetched('id' + modelName + DONE_LOADING_TOKEN, true)
1046
1051
  }
1047
1052
  if (es.value.length) { // don't store oldest record from a filter, may skip some pages
1048
- setLastId(
1049
- modelName + (loadFilter ? JSON.stringify(loadFilter): ''),
1050
- es.value[es.value.length - 1]?.id?.toString()
1051
- )
1053
+ setLastId(fetchKey, es.value[es.value.length - 1]?.id?.toString())
1052
1054
  const createdAt: any = (es.value[es.value.length - 1] as any).createdAt;
1053
1055
  if (typeof createdAt === 'string' || createdAt instanceof Date) {
1054
1056
  setLastDate(modelName, new Date(createdAt))
@@ -1074,49 +1076,39 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
1074
1076
  const reload: ListUpdateMethods <T, ADD>['reload'] = useCallback(options => load(true, { ...options, reloading: true }), [load])
1075
1077
 
1076
1078
  useEffect(() => {
1077
- load(false)
1078
- }, [load])
1079
-
1080
- useEffect(() => {
1081
- if (didFetch(modelName + 'socket')) return
1082
- setFetched(modelName + 'socket', true, false)
1083
-
1084
- session.handle_events({
1085
- // create, update, and delete must go in this order
1086
- // e.g. to ensure delete events are processed last, so deleted records don't appear as created
1087
- [`created-${modelName}`]: addLocalElements,
1088
- [`updated-${modelName}`]: es => {
1089
- const idToUpdates = {} as Indexable<Partial<T>>
1090
- for (const { id, ...e } of es) {
1091
- idToUpdates[id] = e
1092
- }
1093
- updateLocalElements(idToUpdates)
1094
- },
1095
- [`deleted-${modelName}`]: removeLocalElements,
1096
- })
1097
-
1098
- return () => {
1099
- setFetched(modelName + 'socket', false, false)
1100
- session.removeListenersForEvent(`created-${modelName}`)
1101
- session.removeListenersForEvent(`updated-${modelName}`)
1102
- session.removeListenersForEvent(`deleted-${modelName}`)
1079
+ if (options?.unbounceMS) {
1080
+ const i = setTimeout(() => load(false), options.unbounceMS)
1081
+ return () => { clearTimeout(i) }
1103
1082
  }
1104
- }, [session, addLocalElement, updateLocalElements, removeLocalElements, modelName, didFetch])
1083
+ load(false)
1084
+ }, [load, options?.unbounceMS])
1105
1085
 
1106
1086
  const doneLoading = useCallback((key="id") => (
1107
1087
  didFetch(key + modelName + DONE_LOADING_TOKEN)
1108
1088
  ), [didFetch, modelName])
1109
1089
 
1110
1090
  const loadMore = useCallback(async (loadOptions?: LoadMoreOptions<T>) => {
1111
- const filter = loadOptions?.filter ?? options?.loadFilter
1091
+ const sort = options?.sort
1092
+ const sortBy = options?.sortBy
1112
1093
 
1113
- const lastId = getLastId(
1114
- modelName + (filter ? JSON.stringify(filter) : ""),
1094
+ const _filter = loadOptions?.filter ?? options?.loadFilter
1095
+ const filter = (_filter && object_is_empty(_filter)) ? undefined : _filter
1096
+
1097
+ const _mdbFilter = loadOptions?.mdbFilter || options?.mdbFilter
1098
+ const mdbFilter = (_mdbFilter && _mdbFilter?.$and?.length) ? _mdbFilter : undefined
1099
+
1100
+ const mdbFilterIsActive = (mdbFilter && mdbFilter?.$and?.length)
1101
+ const filterKey = (
1102
+ (mdbFilter || filter || sort || sortBy)
1103
+ ? JSON.stringify({ ...mdbFilter, ...filter, sort, sortBy }) + modelName
1104
+ : modelName
1115
1105
  )
1106
+
1107
+ const lastId = getLastId(filterKey)
1116
1108
  if (!lastId) return
1117
1109
  if (!loadQuery) return
1118
- if (didFetch(modelName + 'lastId' + lastId)) return
1119
- setFetched(modelName + 'lastId' + lastId, true)
1110
+ if (didFetch(filterKey + 'lastId' + lastId)) return
1111
+ setFetched(filterKey + 'lastId' + lastId, true)
1120
1112
 
1121
1113
  // todo: support for updatedAt as well, and more?
1122
1114
  const key = loadOptions?.key ?? 'id'
@@ -1128,18 +1120,16 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
1128
1120
  lastId,
1129
1121
  limit,
1130
1122
  filter,
1123
+ mdbFilter: mdbFilterIsActive ? mdbFilter : undefined,
1131
1124
  })).then(
1132
1125
  es => {
1133
1126
  if (es.status === LoadingStatus.Loaded) {
1134
- if (es.value.length < limit) {
1127
+ if (es.value.length < limit && !mdbFilter && (!filter || object_is_empty(filter))) {
1135
1128
  setFetched(key + modelName + DONE_LOADING_TOKEN, true)
1136
1129
  }
1137
1130
  const newLastId = es.value[es.value.length - 1]?.id?.toString()
1138
1131
  if (newLastId) {
1139
- setLastId(
1140
- modelName + (filter ? JSON.stringify(filter) : ""),
1141
- newLastId
1142
- )
1132
+ setLastId(filterKey, newLastId)
1143
1133
  }
1144
1134
 
1145
1135
  dispatch(slice.actions.addSome({ value: es.value, options: { replaceIfMatch: true, addTo: 'end' } }))
@@ -1148,7 +1138,7 @@ export const useListStateHook = <T extends { id: string | number }, ADD extends
1148
1138
  }
1149
1139
  }
1150
1140
  )
1151
- }, [getLastId, modelName, loadQuery, didFetch, setFetched])
1141
+ }, [getLastId, modelName, loadQuery, didFetch, setFetched, options?.mdbFilter, options?.loadFilter, options?.sort, options?.sortBy, options?.limit, dispatch])
1152
1142
 
1153
1143
  const loadRecentlyCreated = React.useCallback(async () => {
1154
1144
  if (!loadQuery) return []
@@ -1206,10 +1196,12 @@ export type HookOptions<T> = {
1206
1196
  sortBy?: SortBy,
1207
1197
  limit?: number,
1208
1198
  loadFilter?: ReadFilter<T>,
1199
+ mdbFilter?: Record<string, any>,
1209
1200
  refetchInMS?: number,
1210
1201
  dontFetch?: boolean,
1211
1202
  addTo?: AddOptions['addTo'],
1212
1203
  onBulkRead?: (matches: T[]) => void,
1204
+ unbounceMS?: number,
1213
1205
  }
1214
1206
 
1215
1207
  export const useChatRoomDisplayInfo = (roomId: string, options={} as HookOptions<ChatRoomDisplayInfo>) => {
package/src/table.tsx CHANGED
@@ -39,6 +39,7 @@ import { PRIMARY_HEX } from "@tellescope/constants"
39
39
  import CloudDownloadIcon from '@mui/icons-material/CloudDownload';
40
40
  import { Autocomplete } from "@mui/material"
41
41
  import { ListQueryQualifier, SortingField } from "@tellescope/types-models"
42
+ import { LoadingButton } from "./forms"
42
43
  // import DragHandleIcon from '@mui/icons-material/DragHandle';
43
44
 
44
45
  const LIGHT_GRAY = "#fafafa"
@@ -150,6 +151,7 @@ export interface TableHeaderProps<T extends Item> extends Styled, HorizontalPadd
150
151
  filterSuggestions: Record<string, string[]>,
151
152
  minColumnWidth?: number,
152
153
  columnResizeZIndex?: number,
154
+ headerHeight?: number,
153
155
  }
154
156
  export const TableHeader = <T extends Item>({
155
157
  fields,
@@ -171,6 +173,7 @@ export const TableHeader = <T extends Item>({
171
173
  filterSuggestions,
172
174
  minColumnWidth=75,
173
175
  columnResizeZIndex=1000,
176
+ headerHeight=ROW_HEIGHT,
174
177
  } : TableHeaderProps<T>) => {
175
178
  const [openFilter, setOpenFilter] = useState(-1)
176
179
  const [startX, setStartX] = useState(0)
@@ -241,7 +244,7 @@ export const TableHeader = <T extends Item>({
241
244
 
242
245
  <Flex alignItems="center" style={{
243
246
  paddingLeft: horizontalPadding, paddingRight: horizontalPadding,
244
- minHeight: ROW_HEIGHT,
247
+ minHeight: headerHeight,
245
248
  backgroundColor: DARK_GRAY,
246
249
  ...style
247
250
  }}>
@@ -405,6 +408,7 @@ export interface TableRowProps<T extends Item> extends Styled, HorizontalPadded,
405
408
  textStyle?: CSSProperties,
406
409
  widthOffsets: Record<string, number>,
407
410
  minColumnWidth?: number,
411
+ rowHeight?: number,
408
412
  }
409
413
  export const TableRow = <T extends Item>({
410
414
  item, indices, fields, onClick, onPress, hover,
@@ -422,13 +426,14 @@ export const TableRow = <T extends Item>({
422
426
  allowUnselectItemsAfterSelectAll,
423
427
  setAllSelected,
424
428
  minColumnWidth=75,
429
+ rowHeight=ROW_HEIGHT,
425
430
  } : TableRowProps<T>) => (
426
431
  <WithHover hoveredColor={hoveredColor ?? GRAY} notHoveredColor={notHoveredColor} disabled={!hover} flex>
427
432
  <Flex flex={1} alignItems="center"
428
433
  onClick={() => (onClick ?? onPress)?.(item)}
429
434
  style={{
430
435
  paddingLeft: horizontalPadding, paddingRight: horizontalPadding,
431
- minHeight: ROW_HEIGHT,
436
+ minHeight: rowHeight,
432
437
  ...style,
433
438
  backgroundColor: undefined, // leave in parent component
434
439
  }}
@@ -717,6 +722,7 @@ export interface TableProps<T extends Item> extends WithTitle, WithHeader<T>, Wi
717
722
  titleStyle?: React.CSSProperties,
718
723
  // description?: string,
719
724
  titleActionsComponent?: React.ReactNode,
725
+ titleComponentHeight?: number,
720
726
  noPaper?: boolean,
721
727
  emptyText?: string,
722
728
  emptyComponent?: React.ReactElement,
@@ -744,10 +750,13 @@ export interface TableProps<T extends Item> extends WithTitle, WithHeader<T>, Wi
744
750
  refreshFilterSuggestionsKey?: number,
745
751
  minColumnWidth?: number,
746
752
  columnResizeZIndex?: number,
753
+ rowHeight?: number,
754
+ headerHeight?: number,
747
755
  }
748
756
  export const Table = <T extends Item>({
749
757
  items,
750
758
  emptyText,
759
+ titleComponentHeight,
751
760
  emptyComponent,
752
761
  noPaper,
753
762
  pageOptions={ paginated: true },
@@ -762,7 +771,7 @@ export const Table = <T extends Item>({
762
771
  doneLoading,
763
772
  loadMoreOptions,
764
773
  // onClearFilter,
765
- filterCounts,
774
+ filterCounts: _filterCounts,
766
775
 
767
776
  title,
768
777
  titleStyle,
@@ -772,11 +781,13 @@ export const Table = <T extends Item>({
772
781
  renderTitleComponent,
773
782
  fields,
774
783
  HeaderComponent=TableHeader,
784
+ headerHeight,
775
785
  hover,
776
786
  hoveredColor,
777
787
  RowComponent=TableRow,
778
788
  footerStyle='numbered',
779
789
  FooterComponent=footerStyle === 'numbered' ? TableFooterNumbered : TableFooter,
790
+ rowHeight,
780
791
 
781
792
  selectable,
782
793
  selected,
@@ -965,6 +976,9 @@ export const Table = <T extends Item>({
965
976
  })
966
977
  }, [sorted, localFilters, fields])
967
978
 
979
+ // make sure filterCounts incorporates column filters whose state is in Table, not parent component
980
+ const filterCounts = _filterCounts ? { ..._filterCounts, filtered: filtered.length } : undefined
981
+
968
982
  const headerFilterIsActive = (
969
983
  !!(fields.find(f => f.filterIsActive) || localFilters.find(f => f?.query))
970
984
  )
@@ -1039,6 +1053,7 @@ export const Table = <T extends Item>({
1039
1053
  virtualization={virtualization}
1040
1054
  header={fields && HeaderComponent && fields.length > 0 && (items.length > 0 || headerFilterIsActive) && (
1041
1055
  <HeaderComponent selectable={selectable} allSelected={allSelected} allowUnselectItemsAfterSelectAll={allowUnselectItemsAfterSelectAll}
1056
+ headerHeight={headerHeight}
1042
1057
  setAllSelected={v => {
1043
1058
  setAllSelected?.(v)
1044
1059
  if (v) {
@@ -1080,14 +1095,25 @@ export const Table = <T extends Item>({
1080
1095
  // renderProps={{ horizontalPadding }}
1081
1096
  emptyText={emptyComponent ?? (
1082
1097
  (emptyText || headerFilterIsActive)
1083
- ? <Typography style={{ padding: horizontalPadding }}>
1098
+ ? (
1099
+ <>
1100
+ <Typography style={{ padding: horizontalPadding }}>
1084
1101
  {emptyText || 'No results found the current filter'}
1085
1102
  </Typography>
1103
+
1104
+ <div style={{ paddingLeft: horizontalPadding, paddingBottom: horizontalPadding }}>
1105
+ <LoadingButton submitText="Load Older Data" submittingText="Loading..."
1106
+ disabled={doneLoading?.()} onClick={loadMore}
1107
+ variant="outlined" style={{ width: 200, textAlign: 'center', marginTop: 10 }}
1108
+ />
1109
+ </div>
1110
+ </>
1111
+ )
1086
1112
  : undefined
1087
1113
  )
1088
1114
  }
1089
1115
  Item={({ item, index }) => ( // index within this list, e.g. a single page
1090
- <RowComponent widthOffsets={widthOffsets}
1116
+ <RowComponent widthOffsets={widthOffsets} rowHeight={rowHeight}
1091
1117
  selectable={selectable} selected={selected} setSelected={setSelected} allSelected={allSelected} setAllSelected={setAllSelected} allowUnselectItemsAfterSelectAll={allowUnselectItemsAfterSelectAll}
1092
1118
  key={item.id} item={item}
1093
1119
  indices={{