@abcagency/hc-ui-components 1.0.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 (102) hide show
  1. package/dist/globals.css +3 -0
  2. package/dist/index.js +4644 -0
  3. package/dist/output.css +784 -0
  4. package/dist/services/globals.css +3 -0
  5. package/dist/services/listingService.js +606 -0
  6. package/package.json +38 -0
  7. package/postcss.config.js +15 -0
  8. package/rollup.config.js +67 -0
  9. package/src/apis/hcApi.js +68 -0
  10. package/src/clientToken.js +9 -0
  11. package/src/components/layout/footer.js +34 -0
  12. package/src/components/layout/header.js +23 -0
  13. package/src/components/layout/layout.js +36 -0
  14. package/src/components/modules/accordions/MapAccordionItem.js +69 -0
  15. package/src/components/modules/accordions/default.js +173 -0
  16. package/src/components/modules/accordions/filterItem.js +53 -0
  17. package/src/components/modules/accordions/filters.js +44 -0
  18. package/src/components/modules/animations/slidein.js +41 -0
  19. package/src/components/modules/buttons/button-group-apply.js +75 -0
  20. package/src/components/modules/buttons/commute-pill.js +21 -0
  21. package/src/components/modules/buttons/default.js +196 -0
  22. package/src/components/modules/buttons/items-pill.js +31 -0
  23. package/src/components/modules/buttons/pill-wrapper.js +26 -0
  24. package/src/components/modules/buttons/show-all-button.js +20 -0
  25. package/src/components/modules/cards/default.js +168 -0
  26. package/src/components/modules/cards/filter.js +55 -0
  27. package/src/components/modules/dialogs/apply-dialog.js +47 -0
  28. package/src/components/modules/filter/commute.js +149 -0
  29. package/src/components/modules/filter/index.js +86 -0
  30. package/src/components/modules/filter/item.js +77 -0
  31. package/src/components/modules/filter/location.js +69 -0
  32. package/src/components/modules/filter/points-of-interest.js +43 -0
  33. package/src/components/modules/filter/radio-item.js +51 -0
  34. package/src/components/modules/filter/search.js +89 -0
  35. package/src/components/modules/filter/search.js.rej +9 -0
  36. package/src/components/modules/filter/sort.js +83 -0
  37. package/src/components/modules/form.js +362 -0
  38. package/src/components/modules/grid.js +75 -0
  39. package/src/components/modules/icon.js +33 -0
  40. package/src/components/modules/jobListing/listing-details.js +87 -0
  41. package/src/components/modules/jumbotron.js +81 -0
  42. package/src/components/modules/maps/info-window-card.js +17 -0
  43. package/src/components/modules/maps/info-window-content.js +60 -0
  44. package/src/components/modules/maps/list/field-mapper.js +113 -0
  45. package/src/components/modules/maps/list/header-item.js +90 -0
  46. package/src/components/modules/maps/list/header.js +46 -0
  47. package/src/components/modules/maps/list/index.js +104 -0
  48. package/src/components/modules/maps/list/item-expand-card/index.js +21 -0
  49. package/src/components/modules/maps/list/item-expand-card/recruiter-contact-nav.js +48 -0
  50. package/src/components/modules/maps/list/item-expand-card/recruiter-details.js +67 -0
  51. package/src/components/modules/maps/list/item-expand-card/recruiter-headshot.js +22 -0
  52. package/src/components/modules/maps/list/list-item/index.js +133 -0
  53. package/src/components/modules/maps/map-list.js +73 -0
  54. package/src/components/modules/maps/map-marker.js +84 -0
  55. package/src/components/modules/maps/map.js +218 -0
  56. package/src/components/modules/maps/place-marker.js +41 -0
  57. package/src/components/modules/maps/tabs.js +79 -0
  58. package/src/components/modules/navigation/nav-link.js +65 -0
  59. package/src/components/modules/navigation/navbar.js +109 -0
  60. package/src/components/modules/navigation/skip-link.js +21 -0
  61. package/src/components/modules/navigation/social.js +29 -0
  62. package/src/components/modules/sections/default.js +59 -0
  63. package/src/components/modules/sections/sectionContext.js +4 -0
  64. package/src/components/modules/video-player.js +126 -0
  65. package/src/constants/placeTypes.js +8 -0
  66. package/src/contexts/mapContext.js +116 -0
  67. package/src/contexts/mapListContext.js +212 -0
  68. package/src/contexts/placesContext.js +98 -0
  69. package/src/hooks/useClickOutside.js +16 -0
  70. package/src/hooks/useEventListener.js +25 -0
  71. package/src/hooks/useEventTracker.js +19 -0
  72. package/src/hooks/useList.js +102 -0
  73. package/src/hooks/useRefScrollProgress.js +24 -0
  74. package/src/hooks/useScript.js +63 -0
  75. package/src/hooks/useScrollDirection.js +39 -0
  76. package/src/hooks/useSectionTracker.js +95 -0
  77. package/src/hooks/useUserAgent.js +43 -0
  78. package/src/hooks/useWindowSize.js +28 -0
  79. package/src/index.css +25 -0
  80. package/src/index.js +116 -0
  81. package/src/services/configService.js +16 -0
  82. package/src/services/googlePlacesNearbyService.js +33 -0
  83. package/src/services/listingAggregatorService.js +42 -0
  84. package/src/services/listingEntityService.js +14 -0
  85. package/src/services/listingService.js +28 -0
  86. package/src/services/recruiterService.js +17 -0
  87. package/src/styles/fonts.js +0 -0
  88. package/src/styles/globals.css +25 -0
  89. package/src/tailwind/preset.default.js +15 -0
  90. package/src/tailwind/tailwind.config.js +126 -0
  91. package/src/util/arrayUtil.js +3 -0
  92. package/src/util/fieldMapper.js +19 -0
  93. package/src/util/filterUtil.js +195 -0
  94. package/src/util/loading.js +17 -0
  95. package/src/util/localStorageUtil.js +27 -0
  96. package/src/util/mapIconUtil.js +179 -0
  97. package/src/util/mapUtil.js +91 -0
  98. package/src/util/page-head.js +62 -0
  99. package/src/util/provider.js +12 -0
  100. package/src/util/sortUtil.js +33 -0
  101. package/src/util/stringUtils.js +6 -0
  102. package/src/util/urlFilterUtil.js +91 -0
@@ -0,0 +1,67 @@
1
+ const babel = require('@rollup/plugin-babel').default;
2
+ const resolve = require('@rollup/plugin-node-resolve');
3
+ const external = require('rollup-plugin-peer-deps-external');
4
+ const alias = require('@rollup/plugin-alias');
5
+ const path = require('path');
6
+ const commonjs = require('@rollup/plugin-commonjs');
7
+ const replace = require('@rollup/plugin-replace');
8
+ const dotenv = require('dotenv');
9
+ const postcss = require('rollup-plugin-postcss');
10
+ dotenv.config();
11
+
12
+ const plugins = [
13
+ external(),
14
+ resolve(),
15
+ postcss({
16
+ extensions: ['.css'],
17
+ extract: 'globals.css',
18
+ minimize: true,
19
+ plugins: [
20
+ require('tailwindcss'),
21
+ require('autoprefixer')
22
+ ],
23
+ }),
24
+ replace({
25
+ preventAssignment: true,
26
+ 'process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY': JSON.stringify(process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY),
27
+ 'process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_URL': JSON.stringify(process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_URL),
28
+ 'process.env.NEXT_PUBLIC_HC_API_BASE_URL': JSON.stringify(process.env.NEXT_PUBLIC_HC_API_BASE_URL),
29
+ }),
30
+ alias({
31
+ entries: [{ find: '~', replacement: path.resolve(__dirname, 'src') }],
32
+ }),
33
+ babel({
34
+ babelHelpers: 'bundled',
35
+ exclude: 'node_modules/**',
36
+ presets: ['@babel/preset-env', '@babel/preset-react']
37
+ }),
38
+ commonjs(),
39
+ ];
40
+
41
+ const externalDependencies = [
42
+ 'react', 'react-dom', 'react-router-dom', 'form-data', 'axios', 'next/router',
43
+ 'next/image', 'react/jsx-runtime', 'tailwind-merge', '@rollup/plugin-alias'
44
+ ];
45
+
46
+ module.exports = [
47
+ {
48
+ input: 'src/index.js',
49
+ output: {
50
+ file: 'dist/index.js',
51
+ format: 'es',
52
+ exports: 'named'
53
+ },
54
+ plugins,
55
+ external: externalDependencies
56
+ },
57
+ {
58
+ input: 'src/services/listingService.js',
59
+ output: {
60
+ file: 'dist/services/listingService.js',
61
+ format: 'es',
62
+ exports: 'named'
63
+ },
64
+ plugins,
65
+ external: externalDependencies
66
+ },
67
+ ];
@@ -0,0 +1,68 @@
1
+
2
+ import { getClientAuthKey } from '~/clientToken.js'
3
+ const baseURL = `${process.env.NEXT_PUBLIC_HC_API_BASE_URL}`
4
+
5
+ const login = async () => {
6
+ const clientAuthKey = getClientAuthKey();
7
+ try {
8
+
9
+ const response = await fetch(`${baseURL}/auth/login`, {
10
+ method: 'POST',
11
+ headers: {
12
+ 'Content-Type': 'application/json'
13
+ },
14
+ body: JSON.stringify({
15
+ clientAuthKey: clientAuthKey
16
+ })
17
+ });
18
+ const data = await response.json();
19
+ if (data.token && data.expiration) {
20
+ sessionStorage.setItem('authToken', data.token);
21
+ sessionStorage.setItem('tokenExpiration', data.expiration);
22
+ return { token: data.token, expiration: data.expiration };
23
+ }
24
+ } catch (error) {
25
+ console.error('Login failed:', error);
26
+ throw error;
27
+ }
28
+ };
29
+
30
+ const fetchWithAuth = async (url, options = {}) => {
31
+ let token = sessionStorage.getItem('authToken');
32
+ const expirationDateTime = sessionStorage.getItem('tokenExpiration');
33
+ const currentTime = new Date();
34
+
35
+ if (!token || !expirationDateTime || new Date(expirationDateTime) <= currentTime) {
36
+ const authResponse = await login();
37
+ token = authResponse.token;
38
+ }
39
+
40
+ const headers = new Headers(options.headers || {});
41
+ headers.append('Authorization', `Bearer ${token}`);
42
+
43
+ const finalOptions = {
44
+ ...options,
45
+ headers
46
+ };
47
+ const response = await fetch(`${baseURL}${url}`, finalOptions);
48
+
49
+ if (!response.ok) throw new Error('Network response was not ok.');
50
+ return response;
51
+ };
52
+
53
+ export default {
54
+ get: async (url) => {
55
+ const response = await fetchWithAuth(url);
56
+ return response.json();
57
+ },
58
+ post: async (url, data) => {
59
+ const response = await fetchWithAuth(url, {
60
+ method: 'POST',
61
+ headers: {
62
+ 'Content-Type': 'application/json'
63
+ },
64
+ body: JSON.stringify(data)
65
+ });
66
+ return response.json();
67
+ }
68
+ };
@@ -0,0 +1,9 @@
1
+ let clientAuthKey = null;
2
+
3
+ export const setClientAuthKey = (key) => {
4
+ clientAuthKey = key;
5
+ };
6
+
7
+ export const getClientAuthKey = () => {
8
+ return clientAuthKey;
9
+ };
@@ -0,0 +1,34 @@
1
+ import NavSocial from '~/components/modules/navigation/social';
2
+ import Button from '~/components/modules/buttons/default';
3
+ import React from 'react'
4
+ import site from '~/data/site.json';
5
+
6
+ const Footer = () => {
7
+ return (
8
+ <footer className="px-4 py-3 bg-slate-100 text-gray-700 text-center text-xs">
9
+ <div className="container flex justify-between items-center">
10
+ <p className="m-0">
11
+ © {new Date().getFullYear()}. <a href={`${site.copyright.url}`} target="_blank">{site.copyright.name}</a>
12
+ </p>
13
+
14
+ <div className="flex items-center gap-3">
15
+ <NavSocial />
16
+
17
+ <Button.Scroll
18
+ href="top"
19
+ variant="icon"
20
+ size="sq"
21
+ >
22
+ <span className="sr-only">To the top!</span>
23
+ <Button.Icon
24
+ icon="mdi:arrow-collapse-up"
25
+ size="w-4 h-4"
26
+ />
27
+ </Button.Scroll>
28
+ </div>
29
+ </div>
30
+ </footer>
31
+ );
32
+ };
33
+
34
+ export default Footer;
@@ -0,0 +1,23 @@
1
+ import React from 'react'
2
+ import { useState } from 'react';
3
+ import Headroom from 'react-headroom';
4
+
5
+ import Navbar from '~/components/modules/navigation/navbar';
6
+
7
+ const Header = () => {
8
+ const [isPinned, setIsPinned] = useState(false);
9
+
10
+ return (
11
+ <Headroom
12
+ style={{ zIndex: 50 }}
13
+ onPin={() => setIsPinned(true)}
14
+ onUnfix={() => setIsPinned(false)}
15
+ >
16
+ <header>
17
+ <Navbar isPinned={isPinned} />
18
+ </header>
19
+ </Headroom>
20
+ );
21
+ };
22
+
23
+ export default Header;
@@ -0,0 +1,36 @@
1
+ import React, { useState } from 'react';
2
+
3
+ import { SectionContext } from '~/components/modules/sections/sectionContext';
4
+
5
+ import { Provider } from '~/util/provider';
6
+ import SkipLink from '~/components/modules/navigation/skip-link';
7
+
8
+ const RootLayout = ({
9
+ children
10
+ }) => {
11
+ const [currentSection, setCurrentSection] = useState(null);
12
+
13
+ return (
14
+ <div
15
+ id="top"
16
+ className="relative"
17
+ >
18
+ <Provider>
19
+ <SectionContext.Provider
20
+ value={{
21
+ currentSection,
22
+ setCurrentSection
23
+ }}
24
+ >
25
+ <SkipLink />
26
+
27
+ <main id="start-of-content">
28
+ {children}
29
+ </main>
30
+ </SectionContext.Provider>
31
+ </Provider>
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export default RootLayout;
@@ -0,0 +1,69 @@
1
+ import Accordion from "~/components/modules/accordions/default";
2
+ import ListItem from "~/components/modules/maps/list/list-item";
3
+ import ItemExpandCard from "~/components/modules/maps/list/item-expand-card";
4
+ import { useMap } from "~/contexts/mapContext";
5
+ import { setStorageObject } from "~/util/localStorageUtil";
6
+ import { useEffect } from "react";
7
+ import { useMapList } from "~/contexts/mapListContext";
8
+ import React from 'react'
9
+ const MapAccordionItem = ({
10
+ item,
11
+ itemRefs,
12
+ itemExpandedContent,
13
+ fieldsShown,
14
+ showMap,
15
+ hasListItemSelected,
16
+ specialFeatures,
17
+ isActive
18
+ }) => {
19
+ const {
20
+ mapItems,
21
+ recruiters,
22
+ setMobileTab,
23
+ favorites,
24
+ handleSettingFavorites
25
+ } = useMapList();
26
+ const { selectItem } = useMap();
27
+ const setSelectedItemAndZoomMap = item => {
28
+ if (isActive) {
29
+ localStorage.removeItem("selectedListItem");
30
+ mapItems.find(x => x.items.hasOwnProperty(item.id)) || null;
31
+ selectItem(null, null, 9, { lat: 39.8283, lng: -98.5795 });
32
+ } else {
33
+ setStorageObject("selectedListItem", item);
34
+ let location = mapItems.find(x => x.items.hasOwnProperty(item.id)) || null;
35
+ selectItem(item, location, 12, {
36
+ lat: location?.latitude,
37
+ lng: location?.longitude
38
+ });
39
+ }
40
+ };
41
+ return (
42
+ <Accordion.Item key={item.id} id={item.id}>
43
+ <Accordion.Trigger.Blank>
44
+ <ListItem
45
+ ref={el => (itemRefs.current[item.id] = el)}
46
+ id={item.id}
47
+ key={item.id}
48
+ item={item}
49
+ fieldsShown={fieldsShown}
50
+ onClick={() => setSelectedItemAndZoomMap(item)}
51
+ showMap={showMap}
52
+ isActive={isActive}
53
+ setMobileTab={setMobileTab}
54
+ specialFeatures={specialFeatures}
55
+ className={hasListItemSelected ? isActive ? "!border-secondary border border-b-0" : "opacity-85" : ""}
56
+ favorites={favorites}
57
+ setFavorites={handleSettingFavorites}
58
+ />
59
+ </Accordion.Trigger.Blank>
60
+ { isActive &&
61
+ <Accordion.Content bodyClassName="px-2 py-2 pt-0 bg-uiAccent/5 border-secondary border border-t-0">
62
+ <ItemExpandCard content={itemExpandedContent(item, recruiters)} />
63
+ </Accordion.Content>
64
+ }
65
+ </Accordion.Item>
66
+ );
67
+ };
68
+
69
+ export default MapAccordionItem;
@@ -0,0 +1,173 @@
1
+ import { forwardRef } from 'react';
2
+ import * as RadixAccordion from '@radix-ui/react-accordion';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import Icon from '~/components/modules/icon';
5
+ import React from 'react';
6
+ import trackEvent from '~/hooks/useEventTracker';
7
+
8
+ const Accordion = ({
9
+ className,
10
+ type = 'single',
11
+ defaultValue,
12
+ collapsible = true,
13
+ children
14
+ }) => {
15
+ return (
16
+ <RadixAccordion.Root
17
+ type={type}
18
+ value={defaultValue ?? null}
19
+ collapsible={collapsible}
20
+ className={className ?? ''}
21
+ onValueChange={value => trackEvent('Engagement', 'Open Accordion', value)}
22
+ >
23
+ {children}
24
+ </RadixAccordion.Root>
25
+ );
26
+ };
27
+
28
+ export const AccordionItem = forwardRef((
29
+ {
30
+ id,
31
+ children,
32
+ className,
33
+ ...props
34
+ },
35
+ forwardedRef
36
+ ) => {
37
+ return (
38
+ <RadixAccordion.Item
39
+ ref={forwardedRef}
40
+ value={id}
41
+ className={className ?? ''}
42
+ {...props}
43
+ >
44
+ {children}
45
+ </RadixAccordion.Item>
46
+ );
47
+ });
48
+
49
+ export const AccordionTrigger = forwardRef((
50
+ {
51
+ children,
52
+ className,
53
+ ...props
54
+ },
55
+ forwardedRef
56
+ ) => (
57
+ <RadixAccordion.Header asChild>
58
+ <RadixAccordion.Trigger
59
+ ref={forwardedRef}
60
+ className={twMerge(
61
+ 'group flex justify-between w-full p-4 font-bold text-left text-uiText focus:outline-none focus-visible:ring focus-visible:ring-uiAccent focus-visible:ring-opacity-75 hover:text-primary focus:text-primary transition data-[state=open]:text-primary',
62
+ className ?? ''
63
+ )}
64
+ {...props}
65
+ >
66
+ {children}
67
+ <Icon
68
+ icon="uil:angle-down"
69
+ size="w-5 h-5"
70
+ className="transition-transform group-data-[state=open]:!rotate-180"
71
+ aria-hidden="true"
72
+ />
73
+ </RadixAccordion.Trigger>
74
+ </RadixAccordion.Header>
75
+ ));
76
+
77
+ export const AccordionTriggerHasHeader = forwardRef((
78
+ {
79
+ header,
80
+ headerClassName,
81
+ iconClassName,
82
+ children,
83
+ className,
84
+ ...props
85
+ },
86
+ forwardedRef
87
+ ) => (
88
+ <RadixAccordion.Header
89
+ className={twMerge(
90
+ 'group flex items-start justify-between w-full p-4 pr-3 font-bold text-left text-uiText focus:outline-none focus-visible:ring focus-visible:ring-uiAccent focus-visible:ring-opacity-75 hover:text-primary focus:text-primary transition data-[state=open]:text-primary',
91
+ headerClassName ?? ''
92
+ )}
93
+ >
94
+ <RadixAccordion.Trigger
95
+ ref={forwardedRef}
96
+ className={twMerge('flex items-center justify-between w-full', className ?? '')}
97
+ {...props}
98
+ >
99
+ {children}
100
+ <Icon
101
+ icon="uil:angle-down"
102
+ size="w-5 h-5"
103
+ className={twMerge(
104
+ 'transition-transform group-data-[state=open]:!rotate-180',
105
+ iconClassName ?? ''
106
+ )}
107
+ aria-hidden="true"
108
+ />
109
+ {header}
110
+ </RadixAccordion.Trigger>
111
+ </RadixAccordion.Header>
112
+ ));
113
+
114
+ export const AccordionTriggerBlank = forwardRef((
115
+ {
116
+ children,
117
+ className,
118
+ ...props
119
+ },
120
+ forwardedRef
121
+ ) => (
122
+ <RadixAccordion.Header asChild>
123
+ <RadixAccordion.Trigger
124
+ asChild
125
+ ref={forwardedRef}
126
+ {...props}
127
+ >
128
+ {children}
129
+ </RadixAccordion.Trigger>
130
+ </RadixAccordion.Header>
131
+ ));
132
+
133
+ export const AccordionContent = forwardRef((
134
+ {
135
+ children,
136
+ className,
137
+ bodyClassName,
138
+ ...props
139
+ },
140
+ forwardedRef
141
+ ) => (
142
+ <RadixAccordion.Content
143
+ ref={forwardedRef}
144
+ className={twMerge(
145
+ 'data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp overflow-hidden',
146
+ className ?? ''
147
+ )}
148
+ {...props}
149
+ >
150
+ <div
151
+ className={twMerge(
152
+ 'p-4 pt-0.5',
153
+ bodyClassName ?? ''
154
+ )}
155
+ >
156
+ {children}
157
+ </div>
158
+ </RadixAccordion.Content>
159
+ ));
160
+
161
+ Accordion.Item = AccordionItem;
162
+ Accordion.Trigger = AccordionTrigger;
163
+ Accordion.Trigger.Blank = AccordionTriggerBlank;
164
+ Accordion.Trigger.HasHeader = AccordionTriggerHasHeader;
165
+ Accordion.Content = AccordionContent;
166
+
167
+ AccordionItem.displayName = 'AccordionItem';
168
+ AccordionTrigger.displayName = 'AccordionTrigger';
169
+ AccordionTriggerBlank.displayName = 'AccordionTriggerBlank';
170
+ AccordionTriggerHasHeader.displayName = 'AccordionTriggerHasHeader';
171
+ AccordionContent.displayName = 'AccordionContent';
172
+
173
+ export default Accordion;
@@ -0,0 +1,53 @@
1
+ import { memo } from "react";
2
+ import React from 'react';
3
+ import Accordion from "~/components/modules/accordions/default";
4
+ import FilterItem from "~/components/modules/filter/item";
5
+ import ItemsPill from "~/components/modules/buttons/items-pill";
6
+
7
+ const AccordionFilterItem = memo(({ filter, setDefaultValue, setSelectedFilters, selectedFilters }) => {
8
+ const fieldKey = filter.id;
9
+ const activeItemsCount = selectedFilters != null && selectedFilters[fieldKey]
10
+ ? Object.keys(selectedFilters[fieldKey]).length
11
+ : 0;
12
+
13
+ const handleClearFilters = () => {
14
+ setSelectedFilters(prevFilters => {
15
+ const updatedFilters = { ...prevFilters };
16
+ delete updatedFilters[fieldKey];
17
+ return updatedFilters;
18
+ });
19
+ };
20
+ return (
21
+ <Accordion.Item key={filter.id} id={filter.id}>
22
+ <Accordion.Trigger.HasHeader
23
+ onClick={() => setDefaultValue(filter.id)}
24
+ className="stretched-link text-left"
25
+ iconClassName="order-last"
26
+ headerClassName="relative py-2 rounded border border-uiAccent/20 bg-white text-sm transition data-[state=open]:border-b-transparent data-[state=open]:rounded-b-none"
27
+ header={
28
+ activeItemsCount > 0 && (
29
+ <ItemsPill
30
+ activeItemsCount={activeItemsCount}
31
+ onClick={handleClearFilters}
32
+ />
33
+ )
34
+ }
35
+ >
36
+ {filter.title}
37
+ </Accordion.Trigger.HasHeader>
38
+ <Accordion.Content bodyClassName="px-2 py-1 bg-white rounded-b border border-uiAccent/20 border-t-0 max-h-[20vh] md:max-h-[25vh] overflow-auto">
39
+ {filter.items.sort().map(item => (
40
+ <FilterItem
41
+ key={item.name}
42
+ item={item}
43
+ field={filter.id}
44
+ selectedFilters={selectedFilters}
45
+ setSelectedFilters={setSelectedFilters}
46
+ />
47
+ ))}
48
+ </Accordion.Content>
49
+ </Accordion.Item>
50
+ );
51
+ });
52
+ AccordionFilterItem.displayName = 'AccordionItem';
53
+ export default AccordionFilterItem;
@@ -0,0 +1,44 @@
1
+ import Accordion from "~/components/modules/accordions/default";
2
+ import AccordionFilterItem from "./filterItem";
3
+ import FilterCard from "~/components/modules/cards/filter";
4
+ import Loading from "~/util/loading";
5
+ import { useMapList } from "~/contexts/mapListContext";
6
+ import React from 'react'
7
+ const AccordionFilters = ({
8
+ className,
9
+ defaultValue,
10
+ setDefaultValue,
11
+ setLocation,
12
+ setSelectedListItem
13
+ }) => {
14
+ const { filterOptions, selectedFilters, setSelectedFilters, favorites, filterByFavorites, setFilterByFavorites } = useMapList();
15
+
16
+ return (
17
+ <FilterCard className={className ?? ""}>
18
+ <FilterCard.Title icon="fa-solid:sliders-h">
19
+ <span>
20
+ Filter <span className="md:hidden lg:inline">your search</span>
21
+ </span>
22
+ </FilterCard.Title>
23
+
24
+ <Accordion defaultValue={defaultValue} className="space-y-4" >
25
+ {!filterOptions?.filters && (
26
+ <Accordion.Item>
27
+ <Loading />
28
+ </Accordion.Item>
29
+ )}
30
+ {filterOptions?.filters && filterOptions?.filters.map(filter => (
31
+ <AccordionFilterItem
32
+ key={filter.id}
33
+ filter={filter}
34
+ setDefaultValue={setDefaultValue}
35
+ selectedFilters={selectedFilters}
36
+ setSelectedFilters={prevFilters => { setSelectedFilters(prevFilters); setLocation(null); setSelectedListItem(null); }}
37
+ />
38
+ ))}
39
+ </Accordion>
40
+ </FilterCard>
41
+ );
42
+ };
43
+
44
+ export default AccordionFilters;
@@ -0,0 +1,41 @@
1
+ import { useRef } from 'react';
2
+ import { motion, useScroll, useSpring, useTransform } from 'framer-motion';
3
+ import React from 'react'
4
+ const SlideIn = ({
5
+ as = "div",
6
+ children,
7
+ delay = 0.05,
8
+ opacity = 1,
9
+ scale = 0.95,
10
+ y = 50,
11
+ ...rest
12
+ }) => {
13
+ const ref = useRef();
14
+
15
+ const { scrollYProgress } = useScroll({
16
+ target: ref,
17
+ offset: ["start end", `start 0.85`]
18
+ });
19
+
20
+ const scaleContained = useTransform(scrollYProgress, [0, 1], [scale, 1]);
21
+
22
+ const scaleSpring = useSpring(scaleContained, {
23
+ damping: 100,
24
+ stiffness: 1000
25
+ });
26
+
27
+ return (
28
+ <motion.div
29
+ ref={ref}
30
+ className="h-full"
31
+ style={{
32
+ scale: scaleSpring
33
+ }}
34
+ {...rest}
35
+ >
36
+ {children}
37
+ </motion.div>
38
+ );
39
+ };
40
+
41
+ export default SlideIn;
@@ -0,0 +1,75 @@
1
+ import { twMerge } from 'tailwind-merge';
2
+ import React from 'react'
3
+ import Button from '~/components/modules/buttons/default';
4
+ import ApplyDialog from '../dialogs/apply-dialog';
5
+
6
+ const ButtonGroupApply = ({
7
+ applyUrl,
8
+ useDetailsPostMessage,
9
+ applyText = 'Apply Now',
10
+ detailsUrl,
11
+ detailsText = 'View Details',
12
+ className,
13
+ buttonSize = 'default',
14
+ applyButtonVariant = 'primary',
15
+ detailsButtonVariant = 'outline',
16
+ includeDialog = false,
17
+ internalApplyLink,
18
+ itemId,
19
+ companyName
20
+ }) => {
21
+
22
+ const applyButton = () => {
23
+ return <Button.Anchor
24
+ variant={applyButtonVariant}
25
+ size={buttonSize}
26
+ >
27
+ {applyText}
28
+ </Button.Anchor>;;
29
+ };
30
+
31
+ return (
32
+ <nav
33
+ className={twMerge`
34
+ flex flex-row justify-between gap-2 w-full
35
+ ${className ?? ''}
36
+ `}
37
+ >
38
+ {detailsUrl && useDetailsPostMessage !== true &&
39
+ <Button.Anchor
40
+ href={detailsUrl}
41
+ variant={detailsButtonVariant}
42
+ size={buttonSize}
43
+ >
44
+ {detailsText}
45
+ </Button.Anchor>
46
+ }
47
+ {useDetailsPostMessage === true &&
48
+ <Button.Btn
49
+ onClick={() => { window.parent.postMessage({ itemId: itemId, type:'LISTING_ID' }, '*');}}
50
+ variant={detailsButtonVariant}
51
+ size={buttonSize}
52
+ >
53
+ {detailsText}
54
+ </Button.Btn>}
55
+ {applyUrl && !includeDialog &&
56
+ <>
57
+ <Button.Anchor
58
+ href={applyUrl}
59
+ variant={applyButtonVariant}
60
+ size={buttonSize}
61
+ >
62
+ {applyText}
63
+ </Button.Anchor>
64
+ </>
65
+ }
66
+ {applyUrl && includeDialog &&
67
+ <ApplyDialog applyUrl={applyUrl} internalApplyLink={internalApplyLink} companyName={companyName}>
68
+ {applyButton()}
69
+ </ApplyDialog>
70
+ }
71
+ </nav>
72
+ );
73
+ };
74
+
75
+ export default ButtonGroupApply;