@bloom-housing/ui-components 2.0.0-pre-tailwind

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 (223) hide show
  1. package/.jest/setup-tests.js +24 -0
  2. package/CHANGELOG.md +20 -0
  3. package/README.md +195 -0
  4. package/index.ts +148 -0
  5. package/jest.config.js +41 -0
  6. package/package.json +98 -0
  7. package/public/images/alameda-logo-white.svg +1 -0
  8. package/public/images/arrow-down.png +0 -0
  9. package/public/images/arrow-down.svg +1 -0
  10. package/public/images/check.png +0 -0
  11. package/public/images/check.svg +11 -0
  12. package/public/images/eho-logo-white.svg +1 -0
  13. package/public/images/eho-logo.svg +1 -0
  14. package/public/images/logo_glyph.svg +11 -0
  15. package/src/actions/Button.scss +157 -0
  16. package/src/actions/Button.tsx +80 -0
  17. package/src/actions/ExpandableContent.tsx +29 -0
  18. package/src/actions/ExpandableText.scss +18 -0
  19. package/src/actions/ExpandableText.tsx +52 -0
  20. package/src/actions/LinkButton.tsx +30 -0
  21. package/src/actions/LocalizedLink.tsx +11 -0
  22. package/src/authentication/AuthContext.ts +327 -0
  23. package/src/authentication/RequireLogin.tsx +62 -0
  24. package/src/authentication/index.ts +5 -0
  25. package/src/authentication/timeout.tsx +127 -0
  26. package/src/authentication/token.ts +17 -0
  27. package/src/authentication/useRequireLoggedInUser.ts +19 -0
  28. package/src/blocks/ActionBlock.scss +108 -0
  29. package/src/blocks/ActionBlock.tsx +51 -0
  30. package/src/blocks/AppStatusItem.scss +140 -0
  31. package/src/blocks/AppStatusItem.tsx +75 -0
  32. package/src/blocks/DashBlock.tsx +42 -0
  33. package/src/blocks/DashBlocks.scss +56 -0
  34. package/src/blocks/DashBlocks.tsx +7 -0
  35. package/src/blocks/FormCard.scss +201 -0
  36. package/src/blocks/FormCard.tsx +29 -0
  37. package/src/blocks/HousingCounselor.tsx +51 -0
  38. package/src/blocks/ImageCard.scss +91 -0
  39. package/src/blocks/ImageCard.tsx +77 -0
  40. package/src/blocks/InfoCard.scss +42 -0
  41. package/src/blocks/InfoCard.tsx +44 -0
  42. package/src/blocks/StatusBar.scss +30 -0
  43. package/src/blocks/StatusBar.tsx +31 -0
  44. package/src/blocks/ViewItem.scss +59 -0
  45. package/src/blocks/ViewItem.tsx +32 -0
  46. package/src/config/ConfigContext.tsx +36 -0
  47. package/src/config/NavigationContext.tsx +54 -0
  48. package/src/config/index.ts +2 -0
  49. package/src/footers/ExygyFooter.tsx +12 -0
  50. package/src/footers/SiteFooter.scss +28 -0
  51. package/src/footers/SiteFooter.tsx +10 -0
  52. package/src/forms/CloudinaryUpload.ts +50 -0
  53. package/src/forms/DOBField.tsx +132 -0
  54. package/src/forms/DateField.tsx +120 -0
  55. package/src/forms/Dropzone.scss +17 -0
  56. package/src/forms/Dropzone.tsx +67 -0
  57. package/src/forms/Field.tsx +115 -0
  58. package/src/forms/FieldGroup.tsx +82 -0
  59. package/src/forms/Form.tsx +22 -0
  60. package/src/forms/HouseholdMemberForm.tsx +41 -0
  61. package/src/forms/HouseholdSizeField.tsx +74 -0
  62. package/src/forms/PhoneField.tsx +69 -0
  63. package/src/forms/PhoneMask.tsx +24 -0
  64. package/src/forms/Select.tsx +80 -0
  65. package/src/forms/Textarea.scss +40 -0
  66. package/src/forms/Textarea.tsx +64 -0
  67. package/src/forms/TimeField.tsx +176 -0
  68. package/src/global/AppearanceTypes.ts +46 -0
  69. package/src/global/ApplicationStatusType.ts +6 -0
  70. package/src/global/accordion.scss +4 -0
  71. package/src/global/blocks.scss +137 -0
  72. package/src/global/custom_counter.scss +50 -0
  73. package/src/global/forms.scss +362 -0
  74. package/src/global/headers.scss +89 -0
  75. package/src/global/homepage.scss +8 -0
  76. package/src/global/index.scss +72 -0
  77. package/src/global/lists.scss +21 -0
  78. package/src/global/markdown.scss +33 -0
  79. package/src/global/mixins.scss +175 -0
  80. package/src/global/navbar.scss +280 -0
  81. package/src/global/print.scss +59 -0
  82. package/src/global/tables.scss +197 -0
  83. package/src/global/text.scss +141 -0
  84. package/src/global/vendor/AgPagination.tsx +133 -0
  85. package/src/global/vendor/_setup_bulma.scss +31 -0
  86. package/src/global/vendor/ag_grid.scss +140 -0
  87. package/src/headers/Hero.scss +56 -0
  88. package/src/headers/Hero.tsx +76 -0
  89. package/src/headers/PageHeader.scss +31 -0
  90. package/src/headers/PageHeader.tsx +39 -0
  91. package/src/headers/SiteHeader.tsx +136 -0
  92. package/src/helpers/address.tsx +46 -0
  93. package/src/helpers/blankApplication.ts +108 -0
  94. package/src/helpers/capitalize.tsx +7 -0
  95. package/src/helpers/dateToString.ts +11 -0
  96. package/src/helpers/debounce.ts +12 -0
  97. package/src/helpers/formOptions.tsx +229 -0
  98. package/src/helpers/formatYesNoLabel.ts +9 -0
  99. package/src/helpers/getTranslationWithArguments.ts +14 -0
  100. package/src/helpers/links.ts +7 -0
  101. package/src/helpers/localeRoute.tsx +13 -0
  102. package/src/helpers/mergeDeep.ts +12 -0
  103. package/src/helpers/nextjs.ts +7 -0
  104. package/src/helpers/numberOrdinal.ts +17 -0
  105. package/src/helpers/occupancyFormatting.tsx +46 -0
  106. package/src/helpers/pdfs.ts +19 -0
  107. package/src/helpers/photos.ts +19 -0
  108. package/src/helpers/preferences.tsx +426 -0
  109. package/src/helpers/resolveObject.ts +5 -0
  110. package/src/helpers/state.tsx +7 -0
  111. package/src/helpers/tableSummaries.tsx +80 -0
  112. package/src/helpers/translator.tsx +37 -0
  113. package/src/helpers/useKeyPress.ts +17 -0
  114. package/src/helpers/useMutate.ts +40 -0
  115. package/src/helpers/useOutsideClick.ts +25 -0
  116. package/src/helpers/validators.ts +3 -0
  117. package/src/icons/HeaderBadge.scss +29 -0
  118. package/src/icons/HeaderBadge.tsx +38 -0
  119. package/src/icons/Icon.scss +76 -0
  120. package/src/icons/Icon.tsx +145 -0
  121. package/src/icons/Icons.tsx +556 -0
  122. package/src/lists/PreferencesList.scss +72 -0
  123. package/src/lists/PreferencesList.tsx +60 -0
  124. package/src/locales/es.json +745 -0
  125. package/src/locales/general.json +1307 -0
  126. package/src/locales/general_OLD.json +868 -0
  127. package/src/locales/vi.json +745 -0
  128. package/src/locales/zh.json +745 -0
  129. package/src/navigation/Breadcrumbs.scss +25 -0
  130. package/src/navigation/Breadcrumbs.tsx +27 -0
  131. package/src/navigation/FooterNav.scss +47 -0
  132. package/src/navigation/FooterNav.tsx +19 -0
  133. package/src/navigation/LanguageNav.scss +32 -0
  134. package/src/navigation/LanguageNav.tsx +53 -0
  135. package/src/navigation/ProgressNav.scss +102 -0
  136. package/src/navigation/ProgressNav.tsx +50 -0
  137. package/src/navigation/TabNav.scss +38 -0
  138. package/src/navigation/TabNav.tsx +69 -0
  139. package/src/navigation/Tabs.scss +65 -0
  140. package/src/navigation/Tabs.tsx +93 -0
  141. package/src/navigation/UserNav.tsx +37 -0
  142. package/src/notifications/AlertBox.scss +78 -0
  143. package/src/notifications/AlertBox.tsx +79 -0
  144. package/src/notifications/AlertNotice.scss +58 -0
  145. package/src/notifications/AlertNotice.tsx +37 -0
  146. package/src/notifications/ApplicationStatus.scss +10 -0
  147. package/src/notifications/ApplicationStatus.tsx +64 -0
  148. package/src/notifications/ErrorMessage.tsx +15 -0
  149. package/src/notifications/SiteAlert.tsx +54 -0
  150. package/src/notifications/StatusAside.scss +11 -0
  151. package/src/notifications/StatusAside.tsx +25 -0
  152. package/src/notifications/StatusMessage.scss +25 -0
  153. package/src/notifications/StatusMessage.tsx +59 -0
  154. package/src/notifications/alertTypes.ts +7 -0
  155. package/src/notifications/index.ts +4 -0
  156. package/src/overlays/Drawer.scss +105 -0
  157. package/src/overlays/Drawer.tsx +51 -0
  158. package/src/overlays/LoadingOverlay.scss +25 -0
  159. package/src/overlays/LoadingOverlay.tsx +29 -0
  160. package/src/overlays/Modal.scss +55 -0
  161. package/src/overlays/Modal.tsx +61 -0
  162. package/src/overlays/Overlay.scss +50 -0
  163. package/src/overlays/Overlay.tsx +100 -0
  164. package/src/page_components/listing/AdditionalFees.tsx +56 -0
  165. package/src/page_components/listing/ListingCard.scss +47 -0
  166. package/src/page_components/listing/ListingCard.tsx +34 -0
  167. package/src/page_components/listing/ListingDetailHeader.tsx +25 -0
  168. package/src/page_components/listing/ListingDetails.tsx +29 -0
  169. package/src/page_components/listing/ListingMap.scss +36 -0
  170. package/src/page_components/listing/ListingMap.tsx +138 -0
  171. package/src/page_components/listing/ListingsGroup.scss +65 -0
  172. package/src/page_components/listing/ListingsGroup.tsx +49 -0
  173. package/src/page_components/listing/UnitTables.tsx +111 -0
  174. package/src/page_components/listing/listing_sidebar/ApplicationSection.tsx +49 -0
  175. package/src/page_components/listing/listing_sidebar/Apply.tsx +225 -0
  176. package/src/page_components/listing/listing_sidebar/LeasingAgent.tsx +77 -0
  177. package/src/page_components/listing/listing_sidebar/ListingUpdated.tsx +20 -0
  178. package/src/page_components/listing/listing_sidebar/ReferralApplication.tsx +28 -0
  179. package/src/page_components/listing/listing_sidebar/SidebarAddress.tsx +56 -0
  180. package/src/page_components/listing/listing_sidebar/Waitlist.tsx +94 -0
  181. package/src/page_components/listing/listing_sidebar/WhatToExpect.tsx +22 -0
  182. package/src/page_components/listing/listing_sidebar/events/DownloadLotteryResults.tsx +34 -0
  183. package/src/page_components/listing/listing_sidebar/events/EventDateSection.tsx +24 -0
  184. package/src/page_components/listing/listing_sidebar/events/LotteryResultsEvent.tsx +26 -0
  185. package/src/page_components/listing/listing_sidebar/events/OpenHouseEvent.tsx +27 -0
  186. package/src/page_components/listing/listing_sidebar/events/PublicLotteryEvent.tsx +22 -0
  187. package/src/prototypes/AppCard.scss +64 -0
  188. package/src/prototypes/Back.scss +19 -0
  189. package/src/prototypes/ButtonGroup.scss +6 -0
  190. package/src/prototypes/ButtonPager.scss +22 -0
  191. package/src/prototypes/FieldSection.scss +35 -0
  192. package/src/prototypes/FieldSection.tsx +31 -0
  193. package/src/prototypes/GridItem.tsx +15 -0
  194. package/src/prototypes/SideNav.scss +32 -0
  195. package/src/prototypes/SideNav.tsx +14 -0
  196. package/src/prototypes/SummaryCard.scss +34 -0
  197. package/src/sections/ContentSection.scss +15 -0
  198. package/src/sections/ContentSection.tsx +25 -0
  199. package/src/sections/FooterSection.scss +6 -0
  200. package/src/sections/FooterSection.tsx +16 -0
  201. package/src/sections/GridSection.scss +72 -0
  202. package/src/sections/GridSection.tsx +82 -0
  203. package/src/sections/InfoCardGrid.scss +45 -0
  204. package/src/sections/InfoCardGrid.tsx +20 -0
  205. package/src/sections/ListSection.scss +7 -0
  206. package/src/sections/ListSection.tsx +23 -0
  207. package/src/sections/MarkdownSection.scss +13 -0
  208. package/src/sections/MarkdownSection.tsx +21 -0
  209. package/src/sections/ResponsiveContentList.tsx +67 -0
  210. package/src/sections/ResponsiveWrappers.tsx +23 -0
  211. package/src/tables/GroupedTable.tsx +86 -0
  212. package/src/tables/MinimalTable.tsx +32 -0
  213. package/src/tables/ResponsiveTable.tsx +24 -0
  214. package/src/tables/StandardTable.tsx +229 -0
  215. package/src/text/Description.scss +52 -0
  216. package/src/text/Description.tsx +24 -0
  217. package/src/text/Message.scss +16 -0
  218. package/src/text/Message.tsx +16 -0
  219. package/src/text/Tag.scss +94 -0
  220. package/src/text/Tag.tsx +22 -0
  221. package/tailwind.config.js +128 -0
  222. package/tailwind.tosass.js +29 -0
  223. package/tsconfig.json +31 -0
@@ -0,0 +1,10 @@
1
+ .application-status {
2
+ @apply p-4;
3
+ @screen lg {
4
+ @apply whitespace-no-wrap;
5
+ }
6
+ }
7
+
8
+ .application-status__sub-content {
9
+ padding-left: 22px;
10
+ }
@@ -0,0 +1,64 @@
1
+ import * as React from "react"
2
+ import { Icon, IconFillColors } from "../icons/Icon"
3
+ import { ApplicationStatusType } from "../global/ApplicationStatusType"
4
+ import "./ApplicationStatus.scss"
5
+
6
+ export interface ApplicationStatusProps {
7
+ content: string
8
+ subContent?: string
9
+ status?: ApplicationStatusType
10
+ vivid?: boolean
11
+ withIcon?: boolean
12
+ }
13
+
14
+ const ApplicationStatus = (props: ApplicationStatusProps) => {
15
+ let bgColor = ""
16
+ // determine styling
17
+ const vivid = props.vivid || false
18
+ let textColor = vivid ? "text-white" : "text-gray-800"
19
+ const textSize = vivid ? "text-xs" : "text-sm"
20
+
21
+ const status = props.status || ApplicationStatusType.Open
22
+ const content = props.content
23
+ const withIcon = props.withIcon ?? true
24
+
25
+ let icon
26
+
27
+ if (withIcon) {
28
+ icon = (
29
+ <span>
30
+ <Icon size="medium" symbol="clock" fill={vivid ? IconFillColors.white : undefined} /> &nbsp;
31
+ </span>
32
+ )
33
+ }
34
+
35
+ switch (status) {
36
+ case ApplicationStatusType.Open:
37
+ bgColor = vivid ? "bg-primary" : "bg-primary-light"
38
+ break
39
+ case ApplicationStatusType.Closed:
40
+ bgColor = vivid ? "bg-alert" : "bg-alert-light"
41
+ break
42
+ case ApplicationStatusType.PostLottery:
43
+ bgColor = "bg-gray-850"
44
+ textColor = "text-white"
45
+ break
46
+ default:
47
+ bgColor = "bg-primary"
48
+ }
49
+
50
+ return (
51
+ <div className={`application-status ${textSize} ${textColor} ${bgColor}`}>
52
+ {icon}
53
+ {content}
54
+ {props.subContent && (
55
+ <>
56
+ <br />
57
+ <span className={"application-status__sub-content"}>{props.subContent}</span>
58
+ </>
59
+ )}
60
+ </div>
61
+ )
62
+ }
63
+
64
+ export { ApplicationStatus as default, ApplicationStatus }
@@ -0,0 +1,15 @@
1
+ import React from "react"
2
+
3
+ const ErrorMessage = (props: { id?: string; error?: boolean; children?: React.ReactNode }) => {
4
+ if (props.error) {
5
+ return (
6
+ <span id={props.id} className="error-message" aria-live="assertive">
7
+ {props.children}
8
+ </span>
9
+ )
10
+ } else {
11
+ return <></>
12
+ }
13
+ }
14
+
15
+ export { ErrorMessage as default, ErrorMessage }
@@ -0,0 +1,54 @@
1
+ import React, { useEffect, useState } from "react"
2
+ import { AlertBox } from "./AlertBox"
3
+ import { AlertTypes } from "./alertTypes"
4
+
5
+ type SiteAlertProps = {
6
+ timeout?: number
7
+ dismissable?: boolean
8
+ type?: AlertTypes
9
+ className?: string
10
+ }
11
+
12
+ export const setSiteAlertMessage = (message: string, type: AlertTypes) => {
13
+ sessionStorage.setItem(`alert_message_${type}`, message)
14
+ }
15
+
16
+ /**
17
+ * Show an alert based on a url query param.
18
+ */
19
+ export const SiteAlert = ({
20
+ timeout,
21
+ dismissable = true,
22
+ type = "alert",
23
+ className,
24
+ }: SiteAlertProps) => {
25
+ const [open, setOpen] = useState(false)
26
+ const [message, setMessage] = useState("")
27
+
28
+ useEffect(() => {
29
+ let timeoutRef: number
30
+ const storedMessage = sessionStorage.getItem(`alert_message_${type}`)
31
+
32
+ if (storedMessage) {
33
+ setMessage(storedMessage)
34
+ setOpen(true)
35
+ sessionStorage.removeItem(`alert_message_${type}`)
36
+
37
+ // Automatically dismiss the message after the timeout, if applicable
38
+ if (timeout) {
39
+ timeoutRef = (setTimeout(() => setOpen(false), timeout) as unknown) as number
40
+ }
41
+ }
42
+ return () => clearTimeout(timeoutRef)
43
+ }, [timeout, type])
44
+
45
+ return open ? (
46
+ <AlertBox
47
+ onClose={dismissable ? () => setOpen(false) : undefined}
48
+ className={className}
49
+ type={type}
50
+ >
51
+ {message}
52
+ </AlertBox>
53
+ ) : null
54
+ }
@@ -0,0 +1,11 @@
1
+ .status-aside__buttons {
2
+ @apply mb-6;
3
+ @apply pb-2;
4
+ @apply border-b;
5
+ }
6
+
7
+ .status-aside__title {
8
+ @apply font-alt-sans;
9
+ @apply text-lg;
10
+ @apply pt-4;
11
+ }
@@ -0,0 +1,25 @@
1
+ import React from "react"
2
+ import { GridSection } from "../sections/GridSection"
3
+
4
+ import "./StatusAside.scss"
5
+
6
+ // Ensure each action has a unique key
7
+ export interface StatusAsideProps {
8
+ actions: React.ReactNode[]
9
+ columns?: number
10
+ children?: React.ReactNode
11
+ }
12
+
13
+ export const StatusAside = (props: StatusAsideProps) => (
14
+ <div className="status-aside">
15
+ <div className="status-aside__buttons">
16
+ <GridSection columns={props.columns || 2} tightSpacing={true}>
17
+ {props.actions}
18
+ </GridSection>
19
+ </div>
20
+
21
+ {React.Children.count(props.children) > 0 && (
22
+ <div className="status-aside__messages">{props.children}</div>
23
+ )}
24
+ </div>
25
+ )
@@ -0,0 +1,25 @@
1
+ .status-message {
2
+ @apply flex;
3
+ @apply flex-col;
4
+ @apply py-4;
5
+ @apply text-sm;
6
+ @apply text-gray-800;
7
+ @apply border-b;
8
+
9
+ &:last-of-type {
10
+ @apply border-b-0;
11
+ }
12
+ }
13
+
14
+ .status-message__status {
15
+ @apply flex;
16
+ @apply justify-between;
17
+ }
18
+
19
+ .status-message__status + .status-message__note {
20
+ @apply mt-3;
21
+ }
22
+
23
+ .status-message__note {
24
+ @apply text-sm;
25
+ }
@@ -0,0 +1,59 @@
1
+ import React from "react"
2
+ import { AppearanceSizeType, AppearanceStyleType } from "../global/AppearanceTypes"
3
+ import { Tag } from "../text/Tag"
4
+ import { t } from "../helpers/translator"
5
+
6
+ import "./StatusMessage.scss"
7
+
8
+ export interface StatusMessagesProps {
9
+ lastTimestamp?: string
10
+ children?: React.ReactNode
11
+ }
12
+
13
+ export const StatusMessages = (props: StatusMessagesProps) => {
14
+ if (React.Children.count(props.children) == 0) {
15
+ return (
16
+ <ul className="status-messages">
17
+ {props.lastTimestamp && (
18
+ <li className="status-message">
19
+ <div className="status-message__note text-center">
20
+ {t("t.lastUpdated")}: {props.lastTimestamp}
21
+ </div>
22
+ </li>
23
+ )}
24
+ </ul>
25
+ )
26
+ } else {
27
+ return (
28
+ <>
29
+ <h3 className="status-messages__title">{t("t.statusHistory")}</h3>
30
+ <ul className="status-messages">{props.children}</ul>
31
+ </>
32
+ )
33
+ }
34
+ }
35
+
36
+ export interface StatusMessageProps {
37
+ status: string
38
+ timestamp: string
39
+ body?: string
40
+ style?: AppearanceStyleType
41
+ }
42
+
43
+ const StatusMessage = (props: StatusMessageProps) => {
44
+ return (
45
+ <li className="status-message">
46
+ <div className="status-message__status">
47
+ <Tag pillStyle={true} styleType={props.style} size={AppearanceSizeType.small}>
48
+ {props.status}
49
+ </Tag>
50
+
51
+ <span className="status-message__time">{props.timestamp}</span>
52
+ </div>
53
+
54
+ {props.body && <div className="status-message__note">{props.body}</div>}
55
+ </li>
56
+ )
57
+ }
58
+
59
+ export { StatusMessage as default, StatusMessage }
@@ -0,0 +1,7 @@
1
+ export const colorClasses = {
2
+ alert: "alert",
3
+ notice: "primary",
4
+ success: "success",
5
+ }
6
+
7
+ export type AlertTypes = keyof typeof colorClasses
@@ -0,0 +1,4 @@
1
+ export { AlertBox } from "./AlertBox"
2
+ export { AlertNotice } from "./AlertNotice"
3
+ export { SiteAlert, setSiteAlertMessage } from "./SiteAlert"
4
+ export type { AlertTypes } from "./alertTypes"
@@ -0,0 +1,105 @@
1
+ .drawer {
2
+ @apply flex;
3
+ @apply flex-col;
4
+ @apply bg-gray-200;
5
+ @apply max-w-5xl;
6
+ @apply border-l;
7
+ @apply w-full;
8
+ @apply absolute;
9
+ @apply top-0;
10
+ @apply right-0;
11
+ @apply bottom-0;
12
+ @apply left-auto;
13
+ @apply overflow-y-auto;
14
+ }
15
+
16
+ .fixed-overlay.has-drawer {
17
+ &.overlay-effect-enter,
18
+ &.overlay-effect-exit-active {
19
+ .fixed-overlay__inner {
20
+ opacity: 0.5;
21
+ transform: translate(100px, 0px);
22
+ }
23
+ }
24
+ &.overlay-effect-exit-active {
25
+ .fixed-overlay__inner {
26
+ opacity: 0;
27
+ }
28
+ }
29
+ &.overlay-effect-enter-active {
30
+ .fixed-overlay__inner {
31
+ opacity: 1;
32
+ transform: translate(0px, 0px);
33
+ }
34
+ }
35
+
36
+ .fixed-overlay__inner {
37
+ @apply w-full;
38
+ @apply h-full;
39
+ @apply max-w-5xl;
40
+ @apply mr-0;
41
+ }
42
+
43
+ &.is-direction-left {
44
+ .fixed-overlay__inner {
45
+ @apply ml-0;
46
+ @apply mr-auto;
47
+ }
48
+ &.overlay-effect-enter,
49
+ &.overlay-effect-exit-active {
50
+ .fixed-overlay__inner {
51
+ transform: translate(-100px, 0px);
52
+ }
53
+ }
54
+ &.overlay-effect-enter-active {
55
+ .fixed-overlay__inner {
56
+ transform: translate(0px, 0px);
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ .drawer__header {
63
+ @apply flex;
64
+ @apply relative;
65
+ @apply bg-white;
66
+ @apply p-5;
67
+ @apply border-b;
68
+ flex: 0 0 4.625rem;
69
+ }
70
+
71
+ .drawer__close {
72
+ @apply absolute;
73
+ top: 1.625rem;
74
+ left: 1.5rem;
75
+ }
76
+
77
+ .drawer__title {
78
+ @apply font-alt-sans;
79
+ @apply text-xl;
80
+ @apply ml-10;
81
+ }
82
+
83
+ .is-direction-left {
84
+ .drawer__title {
85
+ @apply ml-0;
86
+ }
87
+ .drawer__close {
88
+ left: auto;
89
+ right: 1.5rem;
90
+ }
91
+ }
92
+
93
+ .drawer__body {
94
+ @apply overflow-y-auto;
95
+ @apply relative;
96
+ // flex: 1 1 auto;
97
+ }
98
+
99
+ .drawer__content {
100
+ @apply m-auto;
101
+ @apply px-4;
102
+ @apply pt-8;
103
+ @apply pb-4;
104
+ @apply overflow-y-visible;
105
+ }
@@ -0,0 +1,51 @@
1
+ import * as React from "react"
2
+ import "./Drawer.scss"
3
+ import { Icon } from "../icons/Icon"
4
+ import { Overlay, OverlayProps } from "./Overlay"
5
+
6
+ export enum DrawerSide {
7
+ left = "left",
8
+ right = "right",
9
+ }
10
+
11
+ // Ensure each action has a unique key
12
+ export interface DrawerProps extends OverlayProps {
13
+ title?: string
14
+ subtitle?: string
15
+ className?: string
16
+ direction?: DrawerSide
17
+ actions?: React.ReactNode[]
18
+ }
19
+
20
+ const Drawer = (props: DrawerProps) => {
21
+ const drawerClasses = ["drawer"]
22
+ if (props.className) drawerClasses.push(props.className)
23
+
24
+ return (
25
+ <Overlay
26
+ ariaLabel={props.ariaLabel || props.title}
27
+ ariaDescription={props.ariaDescription}
28
+ open={props.open}
29
+ onClose={props.onClose}
30
+ backdrop={props.backdrop}
31
+ className={"has-drawer" + (props.direction == DrawerSide.left ? " is-direction-left" : "")}
32
+ >
33
+ <div className={drawerClasses.join(" ")}>
34
+ <header className="drawer__header">
35
+ {props.title && <h1 className="drawer__title">{props.title}</h1>}
36
+ <button onClick={props.onClose} className="drawer__close" aria-label="Close" tabIndex={0}>
37
+ <Icon size="medium" symbol="close" />
38
+ </button>
39
+ </header>
40
+
41
+ <div className="drawer__body">
42
+ <div className="drawer__content">{props.children}</div>
43
+ </div>
44
+
45
+ {props.actions && <div className="p-4 flex gap-4">{props.actions}</div>}
46
+ </div>
47
+ </Overlay>
48
+ )
49
+ }
50
+
51
+ export { Drawer as default, Drawer }