@superdispatch/ui 0.21.6 → 0.21.13

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 (233) hide show
  1. package/.babelrc.js +5 -0
  2. package/.turbo/turbo-version.log +28 -0
  3. package/package.json +65 -39
  4. package/pkg/README.md +30 -0
  5. package/{dist-node → pkg/dist-node}/index.js +2 -1
  6. package/pkg/dist-node/index.js.map +1 -0
  7. package/{dist-src → pkg/dist-src}/number-field/NumberField.js +2 -1
  8. package/{dist-types → pkg/dist-types}/index.d.ts +364 -364
  9. package/{dist-web → pkg/dist-web}/index.js +2 -1
  10. package/pkg/dist-web/index.js.map +1 -0
  11. package/pkg/package.json +39 -0
  12. package/playroom.ts +31 -0
  13. package/src/__tests__/index.spec.ts +134 -0
  14. package/src/adaptive-toolbar/AdaptiveToolbar.stories.tsx +50 -0
  15. package/src/adaptive-toolbar/AdaptiveToolbar.tsx +199 -0
  16. package/src/adaptive-toolbar/__tests__/AdaptiveToolbar.spec.tsx +11 -0
  17. package/src/adaptive-vertical-toolbar/AdaptiveVerticalToolbar.stories.tsx +49 -0
  18. package/src/adaptive-vertical-toolbar/AdaptiveVerticalToolbar.tsx +171 -0
  19. package/src/app-bar/AppBar.stories.tsx +62 -0
  20. package/src/app-bar/AppBarOverrides.ts +9 -0
  21. package/src/app-bar/__tests__/AppBar.spec.tsx +86 -0
  22. package/src/autocomplete/Autocomplete.stories.tsx +76 -0
  23. package/src/autocomplete/AutocompleteOverrides.tsx +58 -0
  24. package/src/autocomplete/__tests__/Autocomplete.spec.tsx +323 -0
  25. package/src/avatar/Avatar.stories.tsx +17 -0
  26. package/src/avatar/AvatarOverrides.ts +24 -0
  27. package/src/avatar/__tests__/Avatar.spec.tsx +71 -0
  28. package/src/avatar-button/AvatarButton.stories.tsx +153 -0
  29. package/src/avatar-button/AvatarButton.tsx +223 -0
  30. package/src/avatar-button/__tests__/AvatarButton.spec.tsx +126 -0
  31. package/src/button/Button.tsx +51 -0
  32. package/src/button/ButtonOverrides.ts +375 -0
  33. package/src/button/__tests__/Button.spec.tsx +599 -0
  34. package/src/card/Card.stories.tsx +39 -0
  35. package/src/card/CardOverrides.ts +9 -0
  36. package/src/card/__tests__/Card.spec.tsx +68 -0
  37. package/src/card-button/CardButton.stories.tsx +36 -0
  38. package/src/card-button/CardButton.tsx +202 -0
  39. package/src/card-button/__tests__/CardButton.spec.tsx +95 -0
  40. package/src/checkbox/CheckboxField.stories.tsx +61 -0
  41. package/src/checkbox/CheckboxField.tsx +56 -0
  42. package/src/checkbox/CheckboxGroudField.tsx +53 -0
  43. package/src/checkbox/CheckboxOverrides.tsx +55 -0
  44. package/src/checkbox/__tests__/Checkbox.spec.tsx +105 -0
  45. package/src/chip/Chip.stories.tsx +26 -0
  46. package/src/chip/ChipOverrides.tsx +111 -0
  47. package/src/chip/__tests__/Chip.spec.tsx +308 -0
  48. package/src/columns/Column.tsx +124 -0
  49. package/src/columns/Columns.stories.tsx +426 -0
  50. package/src/columns/Columns.tsx +76 -0
  51. package/src/description-list/DescriptionList.spec.tsx +137 -0
  52. package/src/description-list/DescriptionList.stories.tsx +148 -0
  53. package/src/description-list/DescriptionList.tsx +170 -0
  54. package/src/dialog/Dialog.playroom.tsx +24 -0
  55. package/src/dialog/Dialog.stories.tsx +60 -0
  56. package/src/dialog/DialogOverrides.ts +39 -0
  57. package/src/dialog/__tests__/Dialog.spec.tsx +219 -0
  58. package/src/drawer/Drawer.playroom.tsx +22 -0
  59. package/src/drawer/Drawer.stories.tsx +194 -0
  60. package/src/drawer/DrawerActions.tsx +69 -0
  61. package/src/drawer/DrawerContent.tsx +30 -0
  62. package/src/drawer/DrawerList.tsx +60 -0
  63. package/src/drawer/DrawerOverrides.ts +19 -0
  64. package/src/drawer/DrawerTitle.tsx +143 -0
  65. package/src/drawer/__tests__/Drawer.spec.tsx +227 -0
  66. package/src/dropdown-button/DropdownButton.stories.tsx +15 -0
  67. package/src/dropdown-button/DropdownButton.tsx +131 -0
  68. package/src/grid/GridStack.tsx +45 -0
  69. package/src/grid/InlineGrid.tsx +56 -0
  70. package/src/icon-button/IconButton.stories.tsx +53 -0
  71. package/src/icon-button/IconButtonOverrides.ts +32 -0
  72. package/src/icon-button/__tests__/IconButton.spec.tsx +124 -0
  73. package/src/index.ts +47 -0
  74. package/src/info-card/InfoCard.stories.tsx +29 -0
  75. package/src/info-card/InfoCard.tsx +95 -0
  76. package/src/info-card/__tests__/InfoCard.spec.tsx +22 -0
  77. package/src/info-tooltip/InfoTooltip.stories.tsx +77 -0
  78. package/src/info-tooltip/InfoTooltip.tsx +72 -0
  79. package/src/info-tooltip/__tests__/InfoTooltip.spec.tsx +166 -0
  80. package/src/inline/Inline.stories.tsx +135 -0
  81. package/src/inline/Inline.tsx +144 -0
  82. package/src/link/Link.stories.tsx +41 -0
  83. package/src/link/LinkOverrides.ts +43 -0
  84. package/src/link/__tests__/Link.spec.tsx +98 -0
  85. package/src/list/ListOverrides.ts +11 -0
  86. package/src/list/__tests__/List.spec.tsx +182 -0
  87. package/src/menu/Menu.stories.tsx +35 -0
  88. package/src/menu/MenuOverrides.ts +17 -0
  89. package/src/menu/__tests__/Menu.spec.tsx +99 -0
  90. package/src/number-field/NumberField.stories.tsx +123 -0
  91. package/src/number-field/NumberField.tsx +98 -0
  92. package/src/overflow-text/OverflowText.stories.tsx +32 -0
  93. package/src/overflow-text/OverflowText.tsx +114 -0
  94. package/src/overflow-text/__tests__/OverflowText.spec.tsx +24 -0
  95. package/src/pagination/Pagination.stories.tsx +36 -0
  96. package/src/pagination/PaginationOverrides.ts +47 -0
  97. package/src/pagination/__tests__/Pagination.spec.tsx +228 -0
  98. package/src/paper/PaperOverrides.ts +10 -0
  99. package/src/paper/__tests__/Paper.spec.tsx +155 -0
  100. package/src/props/AlignProps.ts +22 -0
  101. package/src/props/CollapseProp.ts +13 -0
  102. package/src/props/ResponsiveProp.ts +65 -0
  103. package/src/props/SpaceProp.ts +40 -0
  104. package/src/radio/RadioField.stories.tsx +42 -0
  105. package/src/radio/RadioField.tsx +55 -0
  106. package/src/radio/RadioGroupField.tsx +64 -0
  107. package/src/radio/RadioOverrides.tsx +51 -0
  108. package/src/radio/__tests__/Radio.spec.tsx +90 -0
  109. package/src/responsive/CollapseBreakpoint.ts +21 -0
  110. package/src/responsive/MinBreakpoint.ts +21 -0
  111. package/src/responsive/ResponsiveContext.tsx +57 -0
  112. package/src/snackbar/Snackbar.stories.tsx +196 -0
  113. package/src/snackbar/Snackbar.tsx +83 -0
  114. package/src/snackbar/SnackbarContent.tsx +147 -0
  115. package/src/snackbar/SnackbarOverrides.ts +23 -0
  116. package/src/snackbar/SnackbarStack.tsx +160 -0
  117. package/src/snackbar/__tests__/Snackbar.spec.tsx +196 -0
  118. package/src/stack/Stack.stories.tsx +113 -0
  119. package/src/stack/Stack.tsx +86 -0
  120. package/src/svg-icon/SvgIcon.stories.tsx +29 -0
  121. package/src/svg-icon/SvgIconOverrides.ts +32 -0
  122. package/src/svg-icon/__tests__/SvgIcon.spec.tsx +67 -0
  123. package/src/switch/Switch.stories.tsx +60 -0
  124. package/src/switch/SwitchOverrides.ts +88 -0
  125. package/src/switch/__tests__/Switch.spec.tsx +204 -0
  126. package/src/tabs/Tabs.stories.tsx +40 -0
  127. package/src/tabs/TabsOverrides.ts +45 -0
  128. package/src/tabs/__tests__/Tabs.spec.tsx +226 -0
  129. package/src/tag/Tag.stories.tsx +53 -0
  130. package/src/tag/Tag.tsx +130 -0
  131. package/src/tag/__tests__/Tag.spec.tsx +82 -0
  132. package/src/text-field/TextField.stories.tsx +50 -0
  133. package/src/text-field/TextFieldOverrides.tsx +149 -0
  134. package/src/text-field/__tests__/TextField.spec.tsx +662 -0
  135. package/src/theme/Color.ts +117 -0
  136. package/src/theme/SuperDispatchTheme.ts +3 -0
  137. package/src/theme/ThemeProvider.tsx +179 -0
  138. package/src/theme/__tests__/CssBaseline.spec.tsx +49 -0
  139. package/src/theme/__tests__/ThemeProvider.spec.ts +40 -0
  140. package/src/theme/__tests__/__snapshots__/ThemeProvider.spec.ts.snap +204 -0
  141. package/src/tiles/Tiles.stories.tsx +69 -0
  142. package/src/tiles/Tiles.tsx +163 -0
  143. package/src/tiles/__tests__/Tiles.spec.tsx +221 -0
  144. package/src/toolbar/ToolbarOverrides.ts +14 -0
  145. package/src/toolbar/__tests__/Toolbar.spec.tsx +50 -0
  146. package/src/tooltip/Tooltip.stories.tsx +32 -0
  147. package/src/tooltip/TooltipOverrides.ts +34 -0
  148. package/src/tooltip/__tests__/Tooltip.spec.tsx +182 -0
  149. package/src/typography/Typography.stories.tsx +132 -0
  150. package/src/typography/TypographyOverrides.ts +125 -0
  151. package/src/typography/__tests__/Typography.spec.tsx +253 -0
  152. package/src/utils/ExitTransitionPlaceholder.tsx +22 -0
  153. package/src/utils/ResizeObserver.tsx +28 -0
  154. package/src/utils/VisibilityObserver.tsx +75 -0
  155. package/src/utils/__tests__/ExitTransitionPlaceholder.spec.tsx +34 -0
  156. package/src/utils/isEmptyReactNode.ts +11 -0
  157. package/src/utils/mergeRefs.ts +21 -0
  158. package/src/utils/renderChildren.ts +10 -0
  159. package/src/utils/useUID.ts +9 -0
  160. package/tsconfig.json +19 -0
  161. package/LICENSE +0 -21
  162. package/dist-node/index.js.map +0 -1
  163. package/dist-web/index.js.map +0 -1
  164. /package/{dist-src → pkg/dist-src}/adaptive-toolbar/AdaptiveToolbar.js +0 -0
  165. /package/{dist-src → pkg/dist-src}/adaptive-vertical-toolbar/AdaptiveVerticalToolbar.js +0 -0
  166. /package/{dist-src → pkg/dist-src}/app-bar/AppBarOverrides.js +0 -0
  167. /package/{dist-src → pkg/dist-src}/autocomplete/AutocompleteOverrides.js +0 -0
  168. /package/{dist-src → pkg/dist-src}/avatar/AvatarOverrides.js +0 -0
  169. /package/{dist-src → pkg/dist-src}/avatar-button/AvatarButton.js +0 -0
  170. /package/{dist-src → pkg/dist-src}/button/Button.js +0 -0
  171. /package/{dist-src → pkg/dist-src}/button/ButtonOverrides.js +0 -0
  172. /package/{dist-src → pkg/dist-src}/card/CardOverrides.js +0 -0
  173. /package/{dist-src → pkg/dist-src}/card-button/CardButton.js +0 -0
  174. /package/{dist-src → pkg/dist-src}/checkbox/CheckboxField.js +0 -0
  175. /package/{dist-src → pkg/dist-src}/checkbox/CheckboxGroudField.js +0 -0
  176. /package/{dist-src → pkg/dist-src}/checkbox/CheckboxOverrides.js +0 -0
  177. /package/{dist-src → pkg/dist-src}/chip/ChipOverrides.js +0 -0
  178. /package/{dist-src → pkg/dist-src}/columns/Column.js +0 -0
  179. /package/{dist-src → pkg/dist-src}/columns/Columns.js +0 -0
  180. /package/{dist-src → pkg/dist-src}/description-list/DescriptionList.js +0 -0
  181. /package/{dist-src → pkg/dist-src}/dialog/DialogOverrides.js +0 -0
  182. /package/{dist-src → pkg/dist-src}/drawer/DrawerActions.js +0 -0
  183. /package/{dist-src → pkg/dist-src}/drawer/DrawerContent.js +0 -0
  184. /package/{dist-src → pkg/dist-src}/drawer/DrawerList.js +0 -0
  185. /package/{dist-src → pkg/dist-src}/drawer/DrawerOverrides.js +0 -0
  186. /package/{dist-src → pkg/dist-src}/drawer/DrawerTitle.js +0 -0
  187. /package/{dist-src → pkg/dist-src}/dropdown-button/DropdownButton.js +0 -0
  188. /package/{dist-src → pkg/dist-src}/grid/GridStack.js +0 -0
  189. /package/{dist-src → pkg/dist-src}/grid/InlineGrid.js +0 -0
  190. /package/{dist-src → pkg/dist-src}/icon-button/IconButtonOverrides.js +0 -0
  191. /package/{dist-src → pkg/dist-src}/index.js +0 -0
  192. /package/{dist-src → pkg/dist-src}/info-card/InfoCard.js +0 -0
  193. /package/{dist-src → pkg/dist-src}/inline/Inline.js +0 -0
  194. /package/{dist-src → pkg/dist-src}/link/LinkOverrides.js +0 -0
  195. /package/{dist-src → pkg/dist-src}/list/ListOverrides.js +0 -0
  196. /package/{dist-src → pkg/dist-src}/menu/MenuOverrides.js +0 -0
  197. /package/{dist-src → pkg/dist-src}/overflow-text/OverflowText.js +0 -0
  198. /package/{dist-src → pkg/dist-src}/pagination/PaginationOverrides.js +0 -0
  199. /package/{dist-src → pkg/dist-src}/paper/PaperOverrides.js +0 -0
  200. /package/{dist-src → pkg/dist-src}/props/AlignProps.js +0 -0
  201. /package/{dist-src → pkg/dist-src}/props/CollapseProp.js +0 -0
  202. /package/{dist-src → pkg/dist-src}/props/ResponsiveProp.js +0 -0
  203. /package/{dist-src → pkg/dist-src}/props/SpaceProp.js +0 -0
  204. /package/{dist-src → pkg/dist-src}/radio/RadioField.js +0 -0
  205. /package/{dist-src → pkg/dist-src}/radio/RadioGroupField.js +0 -0
  206. /package/{dist-src → pkg/dist-src}/radio/RadioOverrides.js +0 -0
  207. /package/{dist-src → pkg/dist-src}/responsive/CollapseBreakpoint.js +0 -0
  208. /package/{dist-src → pkg/dist-src}/responsive/MinBreakpoint.js +0 -0
  209. /package/{dist-src → pkg/dist-src}/responsive/ResponsiveContext.js +0 -0
  210. /package/{dist-src → pkg/dist-src}/snackbar/Snackbar.js +0 -0
  211. /package/{dist-src → pkg/dist-src}/snackbar/SnackbarContent.js +0 -0
  212. /package/{dist-src → pkg/dist-src}/snackbar/SnackbarOverrides.js +0 -0
  213. /package/{dist-src → pkg/dist-src}/snackbar/SnackbarStack.js +0 -0
  214. /package/{dist-src → pkg/dist-src}/stack/Stack.js +0 -0
  215. /package/{dist-src → pkg/dist-src}/svg-icon/SvgIconOverrides.js +0 -0
  216. /package/{dist-src → pkg/dist-src}/switch/SwitchOverrides.js +0 -0
  217. /package/{dist-src → pkg/dist-src}/tabs/TabsOverrides.js +0 -0
  218. /package/{dist-src → pkg/dist-src}/tag/Tag.js +0 -0
  219. /package/{dist-src → pkg/dist-src}/text-field/TextFieldOverrides.js +0 -0
  220. /package/{dist-src → pkg/dist-src}/theme/Color.js +0 -0
  221. /package/{dist-src → pkg/dist-src}/theme/SuperDispatchTheme.js +0 -0
  222. /package/{dist-src → pkg/dist-src}/theme/ThemeProvider.js +0 -0
  223. /package/{dist-src → pkg/dist-src}/tiles/Tiles.js +0 -0
  224. /package/{dist-src → pkg/dist-src}/toolbar/ToolbarOverrides.js +0 -0
  225. /package/{dist-src → pkg/dist-src}/tooltip/TooltipOverrides.js +0 -0
  226. /package/{dist-src → pkg/dist-src}/typography/TypographyOverrides.js +0 -0
  227. /package/{dist-src → pkg/dist-src}/utils/ExitTransitionPlaceholder.js +0 -0
  228. /package/{dist-src → pkg/dist-src}/utils/ResizeObserver.js +0 -0
  229. /package/{dist-src → pkg/dist-src}/utils/VisibilityObserver.js +0 -0
  230. /package/{dist-src → pkg/dist-src}/utils/isEmptyReactNode.js +0 -0
  231. /package/{dist-src → pkg/dist-src}/utils/mergeRefs.js +0 -0
  232. /package/{dist-src → pkg/dist-src}/utils/renderChildren.js +0 -0
  233. /package/{dist-src → pkg/dist-src}/utils/useUID.js +0 -0
@@ -0,0 +1,57 @@
1
+ import { useMediaQuery } from '@material-ui/core';
2
+ import type { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
3
+ import {
4
+ createContext,
5
+ ReactElement,
6
+ ReactNode,
7
+ useContext,
8
+ useMemo,
9
+ } from 'react';
10
+ import { SuperDispatchTheme } from '../theme/SuperDispatchTheme';
11
+
12
+ export interface ResponsiveContext {
13
+ breakpoint?: Breakpoint;
14
+ }
15
+
16
+ const Context = createContext<ResponsiveContext>({});
17
+
18
+ function useBreakpoint(breakpoint: Breakpoint): boolean {
19
+ return useMediaQuery<SuperDispatchTheme>((theme) =>
20
+ theme.breakpoints.only(breakpoint),
21
+ );
22
+ }
23
+
24
+ export interface ResponsiveContextProviderProps {
25
+ children?: ReactNode;
26
+ defaultBreakpoint?: Breakpoint;
27
+ }
28
+
29
+ export function ResponsiveContextProvider({
30
+ children,
31
+ defaultBreakpoint,
32
+ }: ResponsiveContextProviderProps): ReactElement {
33
+ const isXS = useBreakpoint('xs');
34
+ const isSM = useBreakpoint('sm');
35
+ const isMD = useBreakpoint('md');
36
+ const isLG = useBreakpoint('lg');
37
+ const isXL = useBreakpoint('xl');
38
+ const breakpoint: undefined | Breakpoint = isXS
39
+ ? 'xs'
40
+ : isSM
41
+ ? 'sm'
42
+ : isMD
43
+ ? 'md'
44
+ : isLG
45
+ ? 'lg'
46
+ : isXL
47
+ ? 'xl'
48
+ : defaultBreakpoint;
49
+
50
+ const ctx = useMemo<ResponsiveContext>(() => ({ breakpoint }), [breakpoint]);
51
+
52
+ return <Context.Provider value={ctx}>{children}</Context.Provider>;
53
+ }
54
+
55
+ export function useResponsiveContext(): ResponsiveContext {
56
+ return useContext(Context);
57
+ }
@@ -0,0 +1,196 @@
1
+ import { FormControlLabel, FormGroup, Switch } from '@material-ui/core';
2
+ import { UseState } from '@superdispatch/ui-docs';
3
+ import {
4
+ Button,
5
+ Inline,
6
+ Snackbar,
7
+ SnackbarContent,
8
+ SnackbarStackConsumer,
9
+ } from '..';
10
+
11
+ export default {
12
+ title: 'Feedback/Snackbar',
13
+ component: Snackbar,
14
+ subcomponents: { SnackbarContent },
15
+ };
16
+
17
+ export const basic = () => (
18
+ <UseState initialState={false}>
19
+ {(open, setOpen) => (
20
+ <>
21
+ <Button
22
+ onClick={() => {
23
+ setOpen(!open);
24
+ }}
25
+ >
26
+ Toggle snackbar visibility
27
+ </Button>
28
+
29
+ <Snackbar open={open}>This is a basic snackbar</Snackbar>
30
+ </>
31
+ )}
32
+ </UseState>
33
+ );
34
+
35
+ export const closable = () => (
36
+ <UseState initialState={false}>
37
+ {(open, setOpen) => (
38
+ <>
39
+ <Button
40
+ onClick={() => {
41
+ setOpen(!open);
42
+ }}
43
+ >
44
+ Toggle snackbar visibility
45
+ </Button>
46
+
47
+ <Snackbar
48
+ open={open}
49
+ onClose={() => {
50
+ setOpen(false);
51
+ }}
52
+ >
53
+ This snackbar has a close button
54
+ </Snackbar>
55
+ </>
56
+ )}
57
+ </UseState>
58
+ );
59
+
60
+ export const variants = () => (
61
+ <UseState initialState={{}}>
62
+ {(props, setProps) => (
63
+ <>
64
+ <FormGroup row={true}>
65
+ <FormControlLabel
66
+ label="Default"
67
+ control={<Switch />}
68
+ checked={!!props.open && !props.variant}
69
+ onChange={(_, checked) => {
70
+ setProps({
71
+ ...props,
72
+ open: checked,
73
+ variant: undefined,
74
+ });
75
+ }}
76
+ />
77
+
78
+ <FormControlLabel
79
+ label="Success"
80
+ control={<Switch />}
81
+ checked={props.variant === 'success'}
82
+ onChange={(_, checked) => {
83
+ setProps({
84
+ ...props,
85
+ open: checked,
86
+ variant: checked ? 'success' : undefined,
87
+ });
88
+ }}
89
+ />
90
+
91
+ <FormControlLabel
92
+ label="Error"
93
+ control={<Switch />}
94
+ checked={props.variant === 'error'}
95
+ onChange={(_, checked) => {
96
+ setProps({
97
+ ...props,
98
+ open: checked,
99
+ variant: checked ? 'error' : undefined,
100
+ });
101
+ }}
102
+ />
103
+ </FormGroup>
104
+
105
+ <Snackbar {...props}>This is snackbar content</Snackbar>
106
+ </>
107
+ )}
108
+ </UseState>
109
+ );
110
+
111
+ export const stack = () => (
112
+ <SnackbarStackConsumer>
113
+ {({ addSnackbar, clearStack }) => {
114
+ const getTime = () => new Date().toISOString().slice(11, 23);
115
+
116
+ return (
117
+ <Inline>
118
+ <Button
119
+ onClick={() => {
120
+ addSnackbar(`This is default snackbar (${getTime()})`);
121
+ }}
122
+ >
123
+ Add default snackbar
124
+ </Button>
125
+ <Button
126
+ onClick={() => {
127
+ addSnackbar(`This is success snackbar (${getTime()})`, {
128
+ variant: 'success',
129
+ });
130
+ }}
131
+ >
132
+ Add success snackbar
133
+ </Button>
134
+ <Button
135
+ onClick={() => {
136
+ addSnackbar(`This is error snackbar (${getTime()})`, {
137
+ variant: 'error',
138
+ });
139
+ }}
140
+ >
141
+ Add error snackbar
142
+ </Button>
143
+
144
+ <Button
145
+ onClick={() => {
146
+ clearStack();
147
+ }}
148
+ >
149
+ Clear stack
150
+ </Button>
151
+ </Inline>
152
+ );
153
+ }}
154
+ </SnackbarStackConsumer>
155
+ );
156
+
157
+ export const undoable = () => (
158
+ <SnackbarStackConsumer>
159
+ {({ addSnackbar, clearStack }) => (
160
+ <Button
161
+ onClick={() => {
162
+ clearStack();
163
+
164
+ const trxID = Math.random().toString(32).slice(2, 8).toUpperCase();
165
+ const closeSnackbar = addSnackbar(
166
+ <span>
167
+ Transaction <strong>#{trxID}</strong> confirmed
168
+ </span>,
169
+ {
170
+ variant: 'success',
171
+ action: (
172
+ <Button
173
+ size="small"
174
+ color="white"
175
+ variant="contained"
176
+ onClick={() => {
177
+ closeSnackbar();
178
+ addSnackbar(
179
+ <span>
180
+ Transaction <strong>#{trxID}</strong> rejected
181
+ </span>,
182
+ );
183
+ }}
184
+ >
185
+ Reject
186
+ </Button>
187
+ ),
188
+ },
189
+ );
190
+ }}
191
+ >
192
+ Confirm transaction
193
+ </Button>
194
+ )}
195
+ </SnackbarStackConsumer>
196
+ );
@@ -0,0 +1,83 @@
1
+ import {
2
+ Portal,
3
+ Slide,
4
+ Snackbar as MaterialSnackbar,
5
+ SnackbarCloseReason as MaterialSnackbarCloseReason,
6
+ SnackbarProps as MaterialSnackbarProps,
7
+ } from '@material-ui/core';
8
+ import { TransitionProps } from '@material-ui/core/transitions';
9
+ import {
10
+ forwardRef,
11
+ ForwardRefExoticComponent,
12
+ ReactElement,
13
+ ReactNode,
14
+ } from 'react';
15
+ import { SnackbarContent, SnackbarVariant } from './SnackbarContent';
16
+
17
+ function SlideTransition(props: TransitionProps): ReactElement {
18
+ return <Slide {...props} direction="up" />;
19
+ }
20
+
21
+ export type SnackbarCloseReason = 'timeout' | 'explicit';
22
+
23
+ export interface SnackbarProps
24
+ extends Omit<MaterialSnackbarProps, 'onClose' | 'message' | 'children'> {
25
+ children?: ReactNode;
26
+ variant?: SnackbarVariant;
27
+ hasCloseButton?: boolean;
28
+ onClose?: (reason: SnackbarCloseReason) => void;
29
+ }
30
+
31
+ export const Snackbar: ForwardRefExoticComponent<SnackbarProps> = forwardRef(
32
+ (
33
+ {
34
+ open,
35
+ action,
36
+ variant,
37
+ onClose,
38
+ children,
39
+ ContentProps,
40
+ hasCloseButton = onClose != null,
41
+ TransitionComponent = SlideTransition,
42
+ ...props
43
+ },
44
+ ref,
45
+ ) => {
46
+ function handleClose(
47
+ reason: SnackbarCloseReason | MaterialSnackbarCloseReason,
48
+ ): void {
49
+ if (reason !== 'clickaway') {
50
+ onClose?.(reason === 'timeout' ? 'timeout' : 'explicit');
51
+ }
52
+ }
53
+
54
+ return (
55
+ <Portal>
56
+ <MaterialSnackbar
57
+ {...props}
58
+ ref={ref}
59
+ open={open}
60
+ TransitionComponent={TransitionComponent}
61
+ onClose={(_, reason) => {
62
+ handleClose(reason);
63
+ }}
64
+ >
65
+ <SnackbarContent
66
+ {...ContentProps}
67
+ action={action}
68
+ variant={variant}
69
+ onClose={
70
+ !hasCloseButton
71
+ ? undefined
72
+ : () => {
73
+ handleClose('explicit');
74
+ }
75
+ }
76
+ >
77
+ {children}
78
+ </SnackbarContent>
79
+ </MaterialSnackbar>
80
+ </Portal>
81
+ );
82
+ },
83
+ );
@@ -0,0 +1,147 @@
1
+ import {
2
+ Grid,
3
+ IconButton,
4
+ SnackbarContent as MuiSnackbarContent,
5
+ SnackbarContentClassKey as MuiSnackbarContentClassKey,
6
+ SnackbarContentProps as MuiSnackbarContentProps,
7
+ Theme,
8
+ } from '@material-ui/core';
9
+ import { CheckCircle, Close, Warning } from '@material-ui/icons';
10
+ import { ClassNameMap, makeStyles } from '@material-ui/styles';
11
+ import clsx from 'clsx';
12
+ import {
13
+ forwardRef,
14
+ ForwardRefExoticComponent,
15
+ ReactNode,
16
+ RefAttributes,
17
+ } from 'react';
18
+ import { Color } from '../theme/Color';
19
+
20
+ type SnackbarContentClassKey =
21
+ | MuiSnackbarContentClassKey
22
+ | 'icon'
23
+ | 'closeButton'
24
+ | 'variantError'
25
+ | 'variantSuccess';
26
+
27
+ const useStyles = makeStyles<
28
+ Theme,
29
+ { classes?: Partial<ClassNameMap<SnackbarContentClassKey>> },
30
+ SnackbarContentClassKey
31
+ >(
32
+ (theme) => ({
33
+ root: {
34
+ color: Color.White,
35
+ backgroundColor: Color.Dark500,
36
+ '&$variantError': {
37
+ color: Color.White,
38
+ backgroundColor: Color.Red500,
39
+ },
40
+ },
41
+
42
+ action: {
43
+ paddingLeft: theme.spacing(1),
44
+ },
45
+
46
+ message: {
47
+ alignItems: 'center',
48
+ [theme.breakpoints.down('xs')]: {
49
+ fontSize: theme.spacing(2),
50
+ },
51
+ },
52
+
53
+ icon: {
54
+ marginRight: theme.spacing(1),
55
+ fontSize: theme.spacing(3),
56
+ },
57
+
58
+ closeButton: {
59
+ color: Color.White40,
60
+ '&:hover, &:focus': {
61
+ backgroundColor: Color.White08,
62
+ color: Color.White40,
63
+ },
64
+ },
65
+
66
+ variantError: {},
67
+ variantSuccess: {},
68
+ }),
69
+ { name: 'SD-SnackbarContent' },
70
+ );
71
+ export type SnackbarVariant = 'default' | 'error' | 'success';
72
+
73
+ export interface SnackbarContentProps
74
+ extends RefAttributes<unknown>,
75
+ Omit<MuiSnackbarContentProps, 'classes' | 'message' | 'variant'> {
76
+ children?: ReactNode;
77
+ onClose?: () => void;
78
+ variant?: SnackbarVariant;
79
+ classes?: Partial<ClassNameMap<SnackbarContentClassKey>>;
80
+ }
81
+
82
+ export const SnackbarContent: ForwardRefExoticComponent<SnackbarContentProps> =
83
+ forwardRef(
84
+ (
85
+ {
86
+ action,
87
+ children,
88
+ onClose,
89
+ className,
90
+ classes,
91
+ variant = 'default',
92
+ ...props
93
+ },
94
+ ref,
95
+ ) => {
96
+ const { icon, closeButton, variantError, variantSuccess, ...styles } =
97
+ useStyles({ classes });
98
+ const Icon =
99
+ variant === 'error'
100
+ ? Warning
101
+ : variant === 'success'
102
+ ? CheckCircle
103
+ : undefined;
104
+
105
+ return (
106
+ <MuiSnackbarContent
107
+ {...props}
108
+ ref={ref}
109
+ classes={styles}
110
+ className={clsx(className, {
111
+ [variantError]: variant === 'error',
112
+ [variantSuccess]: variant === 'success',
113
+ })}
114
+ message={
115
+ <>
116
+ {Icon && <Icon className={icon} />}
117
+ {children}
118
+ </>
119
+ }
120
+ action={
121
+ !action && !onClose ? null : (
122
+ <Grid
123
+ container={true}
124
+ spacing={1}
125
+ alignItems="center"
126
+ wrap="nowrap"
127
+ >
128
+ {!!action && <Grid item={true}>{action}</Grid>}
129
+
130
+ {onClose && (
131
+ <Grid item={true}>
132
+ <IconButton
133
+ aria-label="close"
134
+ onClick={onClose}
135
+ className={closeButton}
136
+ >
137
+ <Close fontSize="small" />
138
+ </IconButton>
139
+ </Grid>
140
+ )}
141
+ </Grid>
142
+ )
143
+ }
144
+ />
145
+ );
146
+ },
147
+ );
@@ -0,0 +1,23 @@
1
+ import { SuperDispatchTheme } from '../theme/SuperDispatchTheme';
2
+
3
+ export function overrideSnackbar(theme: SuperDispatchTheme): void {
4
+ theme.overrides.MuiSnackbar = {
5
+ anchorOriginBottomCenter: { left: 0, right: 0, bottom: 0 },
6
+ };
7
+
8
+ theme.overrides.MuiSnackbarContent = {
9
+ root: {
10
+ width: '100%',
11
+ borderRadius: 0,
12
+ minHeight: theme.spacing(7.5),
13
+
14
+ [theme.breakpoints.up('sm')]: {
15
+ width: theme.spacing(54),
16
+ maxWidth: theme.spacing(54),
17
+ borderRadius: theme.spacing(0.5),
18
+ },
19
+ },
20
+
21
+ message: { flex: 1, display: 'flex' },
22
+ };
23
+ }
@@ -0,0 +1,160 @@
1
+ import { useDeepEqualMemo } from '@superdispatch/hooks';
2
+ import {
3
+ ConsumerProps,
4
+ createContext,
5
+ Key,
6
+ ReactElement,
7
+ ReactNode,
8
+ useCallback,
9
+ useContext,
10
+ useMemo,
11
+ useRef,
12
+ useState,
13
+ } from 'react';
14
+ import { Snackbar, SnackbarProps } from './Snackbar';
15
+
16
+ export interface SnackbarStackOptions
17
+ extends Omit<SnackbarProps, 'open' | 'children'> {
18
+ key?: Key;
19
+ }
20
+
21
+ export interface SnackbarStack {
22
+ clearStack: () => void;
23
+ addSnackbar: (
24
+ message: ReactNode,
25
+ options?: SnackbarStackOptions,
26
+ ) => () => void;
27
+ }
28
+
29
+ function warnContext(): void {
30
+ // eslint-disable-next-line no-console
31
+ console.log('`useSnackbarStack` is used outside of `SnackbarStackProvider`.');
32
+ }
33
+
34
+ const Context = createContext<SnackbarStack>({
35
+ clearStack: warnContext,
36
+ addSnackbar: () => {
37
+ warnContext();
38
+
39
+ return warnContext;
40
+ },
41
+ });
42
+
43
+ export function useSnackbarStack(): SnackbarStack {
44
+ return useContext(Context);
45
+ }
46
+
47
+ export function SnackbarStackConsumer({
48
+ children,
49
+ }: ConsumerProps<SnackbarStack>): ReactElement {
50
+ return <Context.Consumer>{children}</Context.Consumer>;
51
+ }
52
+
53
+ const TRANSIENT_KEY = '@@transient@@';
54
+
55
+ export interface SnackbarStackProviderProps {
56
+ children: ReactNode;
57
+ }
58
+
59
+ export function SnackbarStackProvider({
60
+ children,
61
+ }: SnackbarStackProviderProps): ReactElement {
62
+ const [stack, setStack] = useState(new Map<Key, SnackbarProps>());
63
+ const transientKeyRef = useRef(0);
64
+ const getNextTransientKey = useCallback(
65
+ () => TRANSIENT_KEY + String((transientKeyRef.current += 1)),
66
+ [],
67
+ );
68
+
69
+ const clearStack = useCallback<SnackbarStack['clearStack']>(() => {
70
+ setStack((x) => (x.size === 0 ? x : new Map()));
71
+ }, []);
72
+
73
+ const addSnackbar = useCallback<SnackbarStack['addSnackbar']>(
74
+ (
75
+ message,
76
+ {
77
+ onClose,
78
+ variant,
79
+ key = variant !== 'error'
80
+ ? // We don't want non error snackbars without key to pop back.
81
+ getNextTransientKey()
82
+ : typeof message === 'string'
83
+ ? message
84
+ : Math.random(),
85
+ id = String(key),
86
+ autoHideDuration = 5000,
87
+ ...props
88
+ }: SnackbarStackOptions = {},
89
+ ) => {
90
+ function removeSnackbar(): void {
91
+ setStack((prev) => {
92
+ if (prev.has(key)) {
93
+ const next = new Map(prev);
94
+
95
+ next.delete(key);
96
+
97
+ return next;
98
+ }
99
+
100
+ return prev;
101
+ });
102
+ }
103
+
104
+ setStack((prev) => {
105
+ const next = new Map(prev);
106
+
107
+ for (const prevKey of prev.keys()) {
108
+ // Ensure that we insert value to the end of the map.
109
+ if (Object.is(key, prevKey)) {
110
+ next.delete(key);
111
+ }
112
+
113
+ // Ensure that transient snackbar will not pop back.
114
+ if (typeof prevKey == 'string' && prevKey.startsWith(TRANSIENT_KEY)) {
115
+ next.delete(prevKey);
116
+ }
117
+ }
118
+
119
+ return next.set(key, {
120
+ ...props,
121
+ id,
122
+ key,
123
+ variant,
124
+ autoHideDuration,
125
+ children: message,
126
+
127
+ onClose: (reason) => {
128
+ removeSnackbar();
129
+ onClose?.(reason);
130
+ },
131
+ });
132
+ });
133
+
134
+ return removeSnackbar;
135
+ },
136
+ [getNextTransientKey],
137
+ );
138
+
139
+ const snackbarProps = useDeepEqualMemo<SnackbarProps>(
140
+ (prev) => {
141
+ const next = Array.from(stack.values()).pop();
142
+
143
+ return next ? { ...next, open: true } : { ...prev, open: false };
144
+ },
145
+ [stack],
146
+ );
147
+
148
+ const api = useMemo<SnackbarStack>(
149
+ () => ({ clearStack, addSnackbar }),
150
+ [clearStack, addSnackbar],
151
+ );
152
+
153
+ return (
154
+ <Context.Provider value={api}>
155
+ {children}
156
+
157
+ <Snackbar {...snackbarProps} />
158
+ </Context.Provider>
159
+ );
160
+ }