@abcagency/hc-ui-components 1.3.29 → 1.3.30

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.
@@ -27,14 +27,15 @@ const FieldMapper = ({
27
27
 
28
28
  return (
29
29
  <>
30
- {orderedFields.map(field => {
30
+ {orderedFields.map((field, index) => {
31
31
  let value = item.fields[field];
32
32
  return (
33
33
  <Grid.Item
34
34
  key={field}
35
35
  className={`
36
- hc-hidden md:hc-block
37
- ${field.toLowerCase() === "position" ? "hc-col-span-2 hc-text-balance hc-pr-2 hc-font-semibold" : "hc-col-span-1"}
36
+ hc-hidden md:hc-block hc-px-2
37
+ ${index === 0 ? "hc-pl-7" : ""}
38
+ ${field.toLowerCase() === "position" ? "hc-col-span-4 hc-text-balance hc-font-semibold" : (field.toLowerCase() === "state" || field.toLowerCase() == "favorite") ? "hc-col-span-1" : "hc-col-span-2"}
38
39
  `}
39
40
  >
40
41
  <span className="hc-sr-only">{capitalize(field)}</span>
@@ -1,91 +1,92 @@
1
- import React, { useState, useEffect } from 'react';
2
-
3
- import Button from '~/components/modules/buttons/default';
4
-
5
- const SORT_STATE = {
6
- notSorted: "not-sorted",
7
- sortedAsc: "sorted-asc",
8
- sortedDesc: "sorted-desc"
9
- };
10
-
11
- const HeaderItem = ({
12
- className,
13
- children,
14
- field,
15
- setSortSetting,
16
- sortSetting,
17
- isSortable,
18
- ...rest
19
- }) => {
20
- const [isSorted, setIsSorted] = useState(SORT_STATE.notSorted);
21
-
22
- const handleChange = field => {
23
- if (isSortable === false)
24
- return;
25
- let setting = {
26
- field: field,
27
- type: isSorted === SORT_STATE.sortedAsc ? "desc" : "asc"
28
- };
29
- setSortSetting(setting);
30
- isSorted === SORT_STATE.notSorted
31
- ? setIsSorted(SORT_STATE.sortedAsc)
32
- : isSorted === SORT_STATE.sortedAsc
33
- ? setIsSorted(SORT_STATE.sortedDesc)
34
- : setIsSorted(SORT_STATE.sortedAsc);
35
- };
36
-
37
- useEffect(() => {
38
- if (sortSetting?.field != null && sortSetting.field != field) {
39
- setIsSorted(SORT_STATE.notSorted);
40
- } else if (
41
- sortSetting?.field != null &&
42
- isSorted == SORT_STATE.notSorted &&
43
- sortSetting.field == field
44
- ) {
45
- setIsSorted(
46
- sortSetting.type == "asc"
47
- ? SORT_STATE.sortedAsc
48
- : SORT_STATE.sortedDesc
49
- );
50
- }
51
- }, [sortSetting, isSorted, field]);
52
-
53
- return (
54
- <Button.Btn
55
- onClick={() => handleChange(field)}
56
- variant="none"
57
- className={`
58
- hc-p-2 hc-rounded-none hc-text-left hc-normal-case hover:hc-bg-uiAccent/5 focus:hc-bg-uiAccent/5
59
- ${field.toLowerCase() === "position" ? "hc-col-span-2" : "hc-col-span-1"}
60
- ${className ?? ""}
61
- `}
62
- {...rest}
63
- >
64
- <Button.Body>
65
- <span className="hc-font-semibold">{children}</span>
66
- {isSortable && (
67
- <div className="hc-flex hc-flex-col hc-pr-2">
68
- <Button.Icon
69
- icon="bi:caret-up-fill"
70
- size="hc-size-2.5"
71
- className={`
72
- hc-transition-opacity
73
- ${isSorted === "sorted-asc" ? "hc-opacity-100 hc-text-primary" : "hc-opacity-30"}
74
- `}
75
- />
76
- <Button.Icon
77
- icon="bi:caret-down-fill"
78
- size="hc-size-2.5"
79
- className={`
80
- transition-opacity
81
- ${isSorted === "sorted-desc" ? "hc-opacity-100" : "hc-opacity-30"}
82
- `}
83
- />
84
- </div>
85
- )}
86
- </Button.Body>
87
- </Button.Btn>
88
- );
89
- };
90
-
91
- export default HeaderItem;
1
+ import React, { useState, useEffect } from 'react';
2
+
3
+ import Button from '~/components/modules/buttons/default';
4
+
5
+ const SORT_STATE = {
6
+ notSorted: "not-sorted",
7
+ sortedAsc: "sorted-asc",
8
+ sortedDesc: "sorted-desc"
9
+ };
10
+
11
+ const HeaderItem = ({
12
+ className,
13
+ children,
14
+ field,
15
+ setSortSetting,
16
+ sortSetting,
17
+ isSortable,
18
+ ...rest
19
+ }) => {
20
+ const [isSorted, setIsSorted] = useState(SORT_STATE.notSorted);
21
+
22
+ const handleChange = field => {
23
+ if (isSortable === false)
24
+ return;
25
+ let setting = {
26
+ field: field,
27
+ type: isSorted === SORT_STATE.sortedAsc ? "desc" : "asc"
28
+ };
29
+ setSortSetting(setting);
30
+ isSorted === SORT_STATE.notSorted
31
+ ? setIsSorted(SORT_STATE.sortedAsc)
32
+ : isSorted === SORT_STATE.sortedAsc
33
+ ? setIsSorted(SORT_STATE.sortedDesc)
34
+ : setIsSorted(SORT_STATE.sortedAsc);
35
+ };
36
+
37
+ useEffect(() => {
38
+ if (sortSetting?.field != null && sortSetting.field != field) {
39
+ setIsSorted(SORT_STATE.notSorted);
40
+ } else if (
41
+ sortSetting?.field != null &&
42
+ isSorted == SORT_STATE.notSorted &&
43
+ sortSetting.field == field
44
+ ) {
45
+ setIsSorted(
46
+ sortSetting.type == "asc"
47
+ ? SORT_STATE.sortedAsc
48
+ : SORT_STATE.sortedDesc
49
+ );
50
+ }
51
+ }, [sortSetting, isSorted, field]);
52
+
53
+ return (
54
+ <Button.Btn
55
+ onClick={() => handleChange(field)}
56
+ variant="none"
57
+ size="none"
58
+ className={`
59
+ hc-p-2 hc-rounded-none hc-text-left hc-normal-case hover:hc-bg-uiAccent/5 focus:hc-bg-uiAccent/5
60
+ ${field.toLowerCase() === "position" ? "hc-pl-7 hc-col-span-4" : (field.toLowerCase() === "state" || field.toLowerCase() == "favorite") ? "hc-col-span-1" : "hc-col-span-2"}
61
+ ${className ?? ""}
62
+ `}
63
+ {...rest}
64
+ >
65
+ <Button.Body>
66
+ <span className="hc-font-semibold">{children}</span>
67
+ {isSortable && (
68
+ <div className="hc-flex hc-flex-col hc-pr-2">
69
+ <Button.Icon
70
+ icon="bi:caret-up-fill"
71
+ size="hc-size-2.5"
72
+ className={`
73
+ hc-transition-opacity
74
+ ${isSorted === "sorted-asc" ? "hc-opacity-100 hc-text-primary" : "hc-opacity-30"}
75
+ `}
76
+ />
77
+ <Button.Icon
78
+ icon="bi:caret-down-fill"
79
+ size="hc-size-2.5"
80
+ className={`
81
+ transition-opacity
82
+ ${isSorted === "sorted-desc" ? "hc-opacity-100" : "hc-opacity-30"}
83
+ `}
84
+ />
85
+ </div>
86
+ )}
87
+ </Button.Body>
88
+ </Button.Btn>
89
+ );
90
+ };
91
+
92
+ export default HeaderItem;
@@ -1,49 +1,51 @@
1
- import React from 'react';
2
- import { twMerge } from 'tailwind-merge';
3
-
4
- import Grid from '~/components/modules/grid';
5
- import HeaderItem from '~/components/modules/list/header-item';
6
-
7
- const ListHeader = ({
8
- className,
9
- fieldsShown,
10
- fieldNames,
11
- fieldIsSortable = true,
12
- setSortSetting,
13
- sortSetting,
14
- includeFavorite = false
15
- }) => {
16
- return (
17
- <Grid
18
- columns="hc-grid-flow-col hc-auto-cols-fr"
19
- gap="hc-gap-0"
20
- isAnimated={false}
21
- className={twMerge`
22
- hc-hidden md:hc-grid hc-bg-uiAccent/10 hc-border-b hc-border-uiAccent/10 hc-sticky hc-top-0 hc-z-10
23
- ${className ?? ""}
24
- `}
25
- >
26
- {fieldsShown.map(field => (
27
- <HeaderItem
28
- key={field}
29
- isSortable={fieldIsSortable}
30
- sortSetting={sortSetting}
31
- field={field}
32
- setSortSetting={setSortSetting}
33
- >
34
- {fieldNames[field]}
35
- </HeaderItem>
36
- ))}
37
- { includeFavorite == true &&
38
- <HeaderItem key={"favorite"}
39
- isSortable={false}
40
- field={"favorite"}
41
- >
42
- Favorite
43
- </HeaderItem>
44
- }
45
- </Grid>
46
- );
47
- };
48
-
49
- export default ListHeader;
1
+ import React from 'react';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ import Grid from '~/components/modules/grid';
5
+ import HeaderItem from '~/components/modules/list/header-item';
6
+
7
+ const ListHeader = ({
8
+ className,
9
+ fieldsShown,
10
+ fieldNames,
11
+ fieldIsSortable = true,
12
+ setSortSetting,
13
+ sortSetting,
14
+ includeFavorite = false,
15
+ scrollbarWidth
16
+ }) => {
17
+ return (
18
+ <Grid
19
+ columns="hc-grid-flow-col hc-auto-cols-fr"
20
+ gap="hc-gap-0"
21
+ isAnimated={false}
22
+ style={{ paddingRight: `${scrollbarWidth && scrollbarWidth > 0 ? scrollbarWidth + 6 : 1 }px` }}
23
+ className={twMerge`
24
+ hc-hidden md:hc-grid hc-bg-uiAccent/10 hc-border-b hc-border-uiAccent/10 hc-sticky hc-top-0 hc-z-10
25
+ ${className ?? ""}
26
+ `}
27
+ >
28
+ {fieldsShown.map(field => (
29
+ <HeaderItem
30
+ key={field}
31
+ isSortable={fieldIsSortable}
32
+ sortSetting={sortSetting}
33
+ field={field}
34
+ setSortSetting={setSortSetting}
35
+ >
36
+ {fieldNames[field]}
37
+ </HeaderItem>
38
+ ))}
39
+ { includeFavorite == true &&
40
+ <HeaderItem key={"favorite"}
41
+ isSortable={false}
42
+ field={"favorite"}
43
+ >
44
+ Favorite
45
+ </HeaderItem>
46
+ }
47
+ </Grid>
48
+ );
49
+ };
50
+
51
+ export default ListHeader;
@@ -1,4 +1,4 @@
1
- import React, { RefObject, ReactNode } from 'react';
1
+ import React, { useEffect, useRef, useState, RefObject, ReactNode } from 'react';
2
2
  import Header from '~/components/modules/list/header';
3
3
  import Sort from '~/components/modules/filter/sort';
4
4
  import Loading from "~/util/loading";
@@ -22,63 +22,97 @@ interface ItemsListProps {
22
22
  }
23
23
 
24
24
  const ItemsList: React.FC<ItemsListProps> = ({
25
- fieldNames,
26
- showMap,
27
- fieldsShown,
28
- filteredListings,
29
- loading,
30
- sortSetting,
31
- setSortSetting,
32
- itemLimit,
33
- loader,
34
- scrollContainerRef,
35
- includeFavorite = false,
36
- children
37
- }) => (
38
- <div className="hc-relative hc-bg-white md:hc-px-4 hc-flex hc-flex-col">
39
- <div className="hc-flex hc-flex-wrap hc-items-center hc-justify-between hc-gap-4 md:hc-mb-2 hc-p-3 md:hc-p-0 hc-bg-uiAccent/10 md:hc-bg-transparent hc-border-b md:hc-border-none hc-border-uiAccent/20">
40
- <h2 className="hc-text-gray-500 hc-font-semibold hc-text-xs md:hc-text-sm">
41
- {loading && <span>Loading...</span>}
42
- {!loading && <span>{filteredListings.length} results</span>}
43
- </h2>
44
- <div className="hc-block md:hc-hidden">
45
- <Sort
46
- className={''}
47
- fields={fieldsShown}
48
- setSortSetting={setSortSetting}
49
- fieldNames={fieldNames}
50
- />
51
- </div>
52
- </div>
53
- <div>
54
- <Header
55
- className={''}
56
- setSortSetting={setSortSetting}
57
- sortSetting={sortSetting}
58
- fieldsShown={fieldsShown}
59
- fieldNames={fieldNames}
60
- includeFavorite={includeFavorite}
61
- />
62
- </div>
63
- <div
64
- ref={scrollContainerRef}
65
- className={`
66
- hc-flex-grow hc-overflow-y-auto
67
- ${showMap ? "md:hc-max-h-45vh hc-max-h-[100vh]" : "md:hc-max-h-95vh hc-max-h-[95vh]"}
68
- `}
69
- >
70
- {loading ? (
71
- <div className="hc-flex hc-justify-center hc-items-center hc-pt-20">
72
- <Loading />
73
- </div>
74
- ) : (
75
- children
76
- )}
77
- <div ref={loader} style={{ height: "100px", textAlign: "center" }}>
78
- {filteredListings.length >= itemLimit && <Loading />}
79
- </div>
80
- </div>
81
- </div>
82
- );
25
+ fieldNames,
26
+ showMap,
27
+ fieldsShown,
28
+ filteredListings,
29
+ loading,
30
+ sortSetting,
31
+ setSortSetting,
32
+ itemLimit,
33
+ loader,
34
+ scrollContainerRef,
35
+ includeFavorite = false,
36
+ children
37
+ }) => {
38
+ const [scrollbarWidth, setScrollbarWidth] = useState<number>(0);
39
+
40
+ const checkScrollbar = () => {
41
+ if (scrollContainerRef.current) {
42
+ const { scrollHeight, clientHeight, offsetWidth, clientWidth } = scrollContainerRef.current;
43
+ const hasVerticalScrollbar = scrollHeight > clientHeight;
44
+ const newScrollbarWidth = hasVerticalScrollbar ? offsetWidth - clientWidth : 0;
45
+ setScrollbarWidth(newScrollbarWidth);
46
+ console.log('Scrollbar width:', newScrollbarWidth);
47
+ }
48
+ };
49
+
50
+ useEffect(() => {
51
+ checkScrollbar();
52
+
53
+ const resizeObserver = new ResizeObserver(() => checkScrollbar());
54
+ const mutationObserver = new MutationObserver(() => checkScrollbar());
55
+
56
+ if (scrollContainerRef.current) {
57
+ resizeObserver.observe(scrollContainerRef.current);
58
+ mutationObserver.observe(scrollContainerRef.current, { childList: true, subtree: true, attributes: true });
59
+ }
60
+
61
+ return () => {
62
+ if (scrollContainerRef.current) {
63
+ resizeObserver.unobserve(scrollContainerRef.current);
64
+ mutationObserver.disconnect();
65
+ }
66
+ };
67
+ }, [scrollContainerRef]);
68
+
69
+ return (
70
+ <div className="hc-relative hc-bg-white md:hc-px-4 hc-flex hc-flex-col">
71
+ <div className="hc-flex hc-flex-wrap hc-items-center hc-justify-between hc-gap-4 md:hc-mb-2 hc-p-3 md:hc-p-0 hc-bg-uiAccent/10 md:hc-bg-transparent hc-border-b md:hc-border-none hc-border-uiAccent/20">
72
+ <h2 className="hc-text-gray-500 hc-font-semibold hc-text-xs md:hc-text-sm">
73
+ {loading && <span>Loading...</span>}
74
+ {!loading && <span>{filteredListings.length} results</span>}
75
+ </h2>
76
+ <div className="hc-block md:hc-hidden">
77
+ <Sort
78
+ className={''}
79
+ fields={fieldsShown}
80
+ setSortSetting={setSortSetting}
81
+ fieldNames={fieldNames}
82
+ />
83
+ </div>
84
+ </div>
85
+ <div>
86
+ <Header
87
+ className={''}
88
+ setSortSetting={setSortSetting}
89
+ sortSetting={sortSetting}
90
+ fieldsShown={fieldsShown}
91
+ fieldNames={fieldNames}
92
+ includeFavorite={includeFavorite}
93
+ scrollbarWidth={scrollbarWidth}
94
+ />
95
+ </div>
96
+ <div
97
+ ref={scrollContainerRef}
98
+ className={`
99
+ hc-flex-grow hc-overflow-y-auto
100
+ ${showMap ? "md:hc-max-h-45vh hc-max-h-[100vh]" : "md:hc-max-h-95vh hc-max-h-[95vh]"}
101
+ `}
102
+ >
103
+ {loading ? (
104
+ <div className="hc-flex hc-justify-center hc-items-center hc-pt-20">
105
+ <Loading />
106
+ </div>
107
+ ) : (
108
+ children
109
+ )}
110
+ <div ref={loader} style={{ height: "100px", textAlign: "center" }}>
111
+ {filteredListings.length >= itemLimit && <Loading />}
112
+ </div>
113
+ </div>
114
+ </div>
115
+ );
116
+ };
83
117
 
84
118
  export default ItemsList;