@superdispatch/ui-lab 0.50.4 → 0.50.5

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 (124) hide show
  1. package/.babelrc.js +5 -0
  2. package/package.json +38 -13
  3. package/pkg/README.md +10 -0
  4. package/{dist-node → pkg/dist-node}/index.js +41 -21
  5. package/pkg/dist-node/index.js.map +1 -0
  6. package/{dist-src → pkg/dist-src}/email-autocomplate/EmailAutocomplete.js +3 -3
  7. package/{dist-src → pkg/dist-src}/file-drop-zone/FileDropZone.js +2 -2
  8. package/{dist-src → pkg/dist-src}/index.js +1 -0
  9. package/{dist-src → pkg/dist-src}/navbar/Navbar.js +13 -4
  10. package/{dist-src → pkg/dist-src}/navbar/NavbarAccordion.js +18 -9
  11. package/{dist-src → pkg/dist-src}/navbar/NavbarList.js +1 -1
  12. package/{dist-src → pkg/dist-src}/sidebar/SidebarBackButton.js +1 -1
  13. package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItem.js +1 -1
  14. package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItemAvatar.js +1 -1
  15. package/{dist-types → pkg/dist-types}/index.d.ts +8 -2
  16. package/{dist-web → pkg/dist-web}/index.js +41 -23
  17. package/pkg/dist-web/index.js.map +1 -0
  18. package/pkg/package.json +41 -0
  19. package/playroom.ts +24 -0
  20. package/src/alert/Alert.stories.tsx +162 -0
  21. package/src/alert/Alert.tsx +135 -0
  22. package/src/banner/Banner.stories.tsx +64 -0
  23. package/src/banner/Banner.tsx +120 -0
  24. package/src/box/Box.stories.tsx +20 -0
  25. package/src/box/Box.tsx +257 -0
  26. package/src/button/Button.stories.tsx +739 -0
  27. package/src/button/Button.tsx +498 -0
  28. package/src/button-area/ButtonArea.stories.tsx +65 -0
  29. package/src/button-area/ButtonArea.tsx +90 -0
  30. package/src/chat/Chat.stories.tsx +130 -0
  31. package/src/chat/Chat.tsx +57 -0
  32. package/src/chat/ChatMessage.tsx +45 -0
  33. package/src/chat/README.MD +7 -0
  34. package/src/chat/__tests__/Chat.spec.tsx +29 -0
  35. package/src/chat/__tests__/ChatMessage.spec.tsx +39 -0
  36. package/src/container/Container.tsx +48 -0
  37. package/src/description-item/DescriptionItem.stories.tsx +163 -0
  38. package/src/description-item/DescriptionItem.tsx +104 -0
  39. package/src/description-line-item/DescriptionLineItem.stories.tsx +23 -0
  40. package/src/description-line-item/DescriptionLineItem.tsx +29 -0
  41. package/src/email-autocomplate/CloseIcon.tsx +20 -0
  42. package/src/email-autocomplate/EmailAutocomplete.stories.tsx +47 -0
  43. package/src/email-autocomplate/EmailAutocomplete.tsx +138 -0
  44. package/src/file-drop-zone/FileDropZone.stories.tsx +44 -0
  45. package/src/file-drop-zone/FileDropZone.tsx +170 -0
  46. package/src/file-list-item/FileListItem.stories.tsx +37 -0
  47. package/src/file-list-item/FileListItem.tsx +148 -0
  48. package/src/file-list-item/__tests__/FileListItem.spec.tsx +339 -0
  49. package/src/flag-list/FlagList.stories.tsx +72 -0
  50. package/src/flag-list/FlagList.tsx +54 -0
  51. package/src/flag-list/FlagListItem.tsx +126 -0
  52. package/src/index.spec.ts +53 -0
  53. package/src/index.ts +36 -0
  54. package/src/linked-text/LinkeText.stories.tsx +42 -0
  55. package/src/linked-text/LinkedText.tsx +47 -0
  56. package/src/multiline-text/MultilineText.stories.tsx +30 -0
  57. package/src/multiline-text/MultilineText.ts +16 -0
  58. package/src/navbar/Navbar.stories.tsx +137 -0
  59. package/src/navbar/Navbar.tsx +132 -0
  60. package/src/navbar/NavbarAccordion.tsx +195 -0
  61. package/src/navbar/NavbarAvatar.tsx +51 -0
  62. package/src/navbar/NavbarBottomBar.tsx +135 -0
  63. package/src/navbar/NavbarContext.tsx +22 -0
  64. package/src/navbar/NavbarItem.tsx +125 -0
  65. package/src/navbar/NavbarList.tsx +247 -0
  66. package/src/navbar/NavbarMenu.tsx +102 -0
  67. package/src/passwordStepper/PasswordStrength.stories.tsx +95 -0
  68. package/src/passwordStepper/PasswordStrength.tsx +107 -0
  69. package/src/passwordStepper/PasswordUtils.tsx +42 -0
  70. package/src/passwordStepper/PasswordValidationComponents.tsx +95 -0
  71. package/src/sidebar/Sidebar.stories.tsx +376 -0
  72. package/src/sidebar/Sidebar.tsx +75 -0
  73. package/src/sidebar/SidebarBackButton.tsx +33 -0
  74. package/src/sidebar/SidebarContainer.tsx +114 -0
  75. package/src/sidebar/SidebarContent.tsx +119 -0
  76. package/src/sidebar/SidebarDivider.tsx +15 -0
  77. package/src/sidebar/SidebarMenuItem.tsx +196 -0
  78. package/src/sidebar/SidebarMenuItemAction.tsx +27 -0
  79. package/src/sidebar/SidebarMenuItemAvatar.tsx +59 -0
  80. package/src/sidebar/SidebarMenuItemContext.tsx +33 -0
  81. package/src/sidebar/SidebarSubheader.tsx +38 -0
  82. package/src/styled.d.ts +12 -0
  83. package/src/text-box/TextBox.stories.tsx +114 -0
  84. package/src/text-box/TextBox.tsx +238 -0
  85. package/src/utils/RuleNormalizer.ts +24 -0
  86. package/src/utils/mergeStyles.ts +28 -0
  87. package/tsconfig.json +19 -0
  88. package/LICENSE +0 -21
  89. package/dist-node/index.js.map +0 -1
  90. package/dist-web/index.js.map +0 -1
  91. /package/{dist-src → pkg/dist-src}/alert/Alert.js +0 -0
  92. /package/{dist-src → pkg/dist-src}/banner/Banner.js +0 -0
  93. /package/{dist-src → pkg/dist-src}/box/Box.js +0 -0
  94. /package/{dist-src → pkg/dist-src}/button/Button.js +0 -0
  95. /package/{dist-src → pkg/dist-src}/button-area/ButtonArea.js +0 -0
  96. /package/{dist-src → pkg/dist-src}/chat/Chat.js +0 -0
  97. /package/{dist-src → pkg/dist-src}/chat/ChatMessage.js +0 -0
  98. /package/{dist-src → pkg/dist-src}/container/Container.js +0 -0
  99. /package/{dist-src → pkg/dist-src}/description-item/DescriptionItem.js +0 -0
  100. /package/{dist-src → pkg/dist-src}/description-line-item/DescriptionLineItem.js +0 -0
  101. /package/{dist-src → pkg/dist-src}/email-autocomplate/CloseIcon.js +0 -0
  102. /package/{dist-src → pkg/dist-src}/file-list-item/FileListItem.js +0 -0
  103. /package/{dist-src → pkg/dist-src}/flag-list/FlagList.js +0 -0
  104. /package/{dist-src → pkg/dist-src}/flag-list/FlagListItem.js +0 -0
  105. /package/{dist-src → pkg/dist-src}/linked-text/LinkedText.js +0 -0
  106. /package/{dist-src → pkg/dist-src}/multiline-text/MultilineText.js +0 -0
  107. /package/{dist-src → pkg/dist-src}/navbar/NavbarAvatar.js +0 -0
  108. /package/{dist-src → pkg/dist-src}/navbar/NavbarBottomBar.js +0 -0
  109. /package/{dist-src → pkg/dist-src}/navbar/NavbarContext.js +0 -0
  110. /package/{dist-src → pkg/dist-src}/navbar/NavbarItem.js +0 -0
  111. /package/{dist-src → pkg/dist-src}/navbar/NavbarMenu.js +0 -0
  112. /package/{dist-src → pkg/dist-src}/passwordStepper/PasswordStrength.js +0 -0
  113. /package/{dist-src → pkg/dist-src}/passwordStepper/PasswordUtils.js +0 -0
  114. /package/{dist-src → pkg/dist-src}/passwordStepper/PasswordValidationComponents.js +0 -0
  115. /package/{dist-src → pkg/dist-src}/sidebar/Sidebar.js +0 -0
  116. /package/{dist-src → pkg/dist-src}/sidebar/SidebarContainer.js +0 -0
  117. /package/{dist-src → pkg/dist-src}/sidebar/SidebarContent.js +0 -0
  118. /package/{dist-src → pkg/dist-src}/sidebar/SidebarDivider.js +0 -0
  119. /package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItemAction.js +0 -0
  120. /package/{dist-src → pkg/dist-src}/sidebar/SidebarMenuItemContext.js +0 -0
  121. /package/{dist-src → pkg/dist-src}/sidebar/SidebarSubheader.js +0 -0
  122. /package/{dist-src → pkg/dist-src}/text-box/TextBox.js +0 -0
  123. /package/{dist-src → pkg/dist-src}/utils/RuleNormalizer.js +0 -0
  124. /package/{dist-src → pkg/dist-src}/utils/mergeStyles.js +0 -0
@@ -0,0 +1,498 @@
1
+ import { CircularProgress } from '@material-ui/core';
2
+ import { Color, ColorDynamic } from '@superdispatch/ui';
3
+ import {
4
+ AriaAttributes,
5
+ FocusEventHandler,
6
+ forwardRef,
7
+ HTMLAttributes,
8
+ MouseEventHandler,
9
+ ReactNode,
10
+ } from 'react';
11
+ import styled, { css } from 'styled-components';
12
+
13
+ export type ButtonSizeProp = 'small' | 'medium' | 'large';
14
+ export type ButtonVariantProp =
15
+ | 'critical'
16
+ | 'default'
17
+ | 'inverted'
18
+ | 'neutral'
19
+ | 'primary'
20
+ | 'text'
21
+ | 'success';
22
+
23
+ interface ButtonStyleProps {
24
+ disabled: boolean;
25
+ fullWidth: boolean;
26
+ size: ButtonSizeProp;
27
+ variant: ButtonVariantProp;
28
+ }
29
+
30
+ interface ButtonVariables {
31
+ fontSize: number;
32
+ fontSizeMobile: number;
33
+ lineHeight: number;
34
+ lineHeightMobile: number;
35
+
36
+ paddingX: number;
37
+ paddingXMobile: number;
38
+ paddingY: number;
39
+ paddingYMobile: number;
40
+
41
+ textColor: ColorDynamic | Color;
42
+ textColorHovered: ColorDynamic | Color;
43
+ textColorDisabled: ColorDynamic | Color;
44
+
45
+ iconColor: ColorDynamic | Color;
46
+
47
+ outlineColor: ColorDynamic;
48
+
49
+ borderColor: ColorDynamic;
50
+ borderColorHovered: ColorDynamic;
51
+ borderColorDisabled: ColorDynamic;
52
+
53
+ backgroundColor: ColorDynamic;
54
+ backgroundColorActive: ColorDynamic;
55
+ backgroundColorHovered: ColorDynamic;
56
+ backgroundColorDisabled: ColorDynamic;
57
+ }
58
+
59
+ function createButtonVariables(
60
+ size: ButtonSizeProp,
61
+ {
62
+ fontSize = size === 'large' ? 16 : 14,
63
+ lineHeight = size === 'large' ? 24 : 20,
64
+ fontSizeMobile = size === 'large' ? 18 : 16,
65
+ lineHeightMobile = size === 'large' ? 28 : 24,
66
+
67
+ paddingX = size === 'large' ? 32 : 16,
68
+ paddingY = size === 'large' ? 8 : size === 'small' ? 2 : 6,
69
+ paddingXMobile = size === 'large' ? 64 : 24,
70
+ paddingYMobile = size === 'large' ? 14 : size === 'small' ? 4 : 10,
71
+
72
+ textColor = ColorDynamic.Transparent,
73
+ textColorHovered = textColor,
74
+ textColorDisabled = textColor,
75
+
76
+ iconColor = textColor,
77
+
78
+ outlineColor = ColorDynamic.Transparent,
79
+
80
+ borderColor = ColorDynamic.Transparent,
81
+ borderColorHovered = borderColor,
82
+ borderColorDisabled = borderColor,
83
+
84
+ backgroundColor = ColorDynamic.Transparent,
85
+ backgroundColorHovered = backgroundColor,
86
+ backgroundColorActive = backgroundColorHovered,
87
+ backgroundColorDisabled = backgroundColor,
88
+ }: Partial<ButtonVariables>,
89
+ ): ButtonVariables {
90
+ return {
91
+ paddingX,
92
+ paddingY,
93
+ fontSize,
94
+ lineHeight,
95
+
96
+ paddingXMobile,
97
+ paddingYMobile,
98
+ fontSizeMobile,
99
+ lineHeightMobile,
100
+
101
+ textColor,
102
+ borderColor,
103
+ outlineColor,
104
+ backgroundColor,
105
+
106
+ textColorHovered,
107
+ borderColorHovered,
108
+ backgroundColorHovered,
109
+
110
+ backgroundColorActive,
111
+
112
+ textColorDisabled,
113
+ borderColorDisabled,
114
+ backgroundColorDisabled,
115
+
116
+ iconColor,
117
+ };
118
+ }
119
+
120
+ function getDefaultVariables(size: ButtonSizeProp): ButtonVariables {
121
+ return createButtonVariables(size, {
122
+ textColor: Color.White,
123
+ outlineColor: ColorDynamic.Blue30,
124
+ backgroundColor: ColorDynamic.Blue300,
125
+
126
+ textColorHovered: Color.White,
127
+ backgroundColorHovered: ColorDynamic.Blue500,
128
+
129
+ backgroundColorActive: ColorDynamic.Blue400,
130
+ textColorDisabled: Color.White50,
131
+ backgroundColorDisabled: ColorDynamic.Blue30,
132
+ });
133
+ }
134
+
135
+ function getPrimaryVariables(size: ButtonSizeProp): ButtonVariables {
136
+ return getDefaultVariables(size);
137
+ }
138
+
139
+ function getNeutralVariables(size: ButtonSizeProp): ButtonVariables {
140
+ return createButtonVariables(size, {
141
+ textColor: ColorDynamic.Dark500,
142
+ borderColor: ColorDynamic.Silver500,
143
+ outlineColor: ColorDynamic.Blue30,
144
+ backgroundColor: ColorDynamic.White,
145
+
146
+ textColorHovered: ColorDynamic.Blue500,
147
+ borderColorHovered: ColorDynamic.Blue500,
148
+ backgroundColorHovered: ColorDynamic.Blue50,
149
+
150
+ backgroundColorActive: ColorDynamic.Blue50,
151
+
152
+ textColorDisabled: ColorDynamic.Silver500,
153
+ iconColor: ColorDynamic.Dark100,
154
+ });
155
+ }
156
+
157
+ function getCriticalVariables(size: ButtonSizeProp): ButtonVariables {
158
+ return createButtonVariables(size, {
159
+ textColor: ColorDynamic.Red500,
160
+ borderColor: ColorDynamic.Red500,
161
+ outlineColor: ColorDynamic.Red30,
162
+ backgroundColor: ColorDynamic.Red50,
163
+
164
+ backgroundColorHovered: ColorDynamic.Red300Aplha20,
165
+
166
+ backgroundColorActive: ColorDynamic.Red10,
167
+
168
+ textColorDisabled: ColorDynamic.Red30,
169
+ borderColorDisabled: ColorDynamic.Red30,
170
+ backgroundColorDisabled: ColorDynamic.Red50,
171
+ });
172
+ }
173
+
174
+ function getTextVariables(size: ButtonSizeProp): ButtonVariables {
175
+ return createButtonVariables(size, {
176
+ textColor: ColorDynamic.Blue500,
177
+
178
+ outlineColor: ColorDynamic.Blue30,
179
+
180
+ textColorHovered: ColorDynamic.Blue500,
181
+ backgroundColorHovered: ColorDynamic.Blue50,
182
+
183
+ backgroundColorActive: ColorDynamic.Blue50,
184
+
185
+ textColorDisabled: ColorDynamic.Blue30,
186
+ });
187
+ }
188
+
189
+ function getInvertedVariables(size: ButtonSizeProp): ButtonVariables {
190
+ return createButtonVariables(size, {
191
+ textColor: ColorDynamic.White,
192
+ outlineColor: ColorDynamic.White40,
193
+ backgroundColor: ColorDynamic.White20,
194
+
195
+ textColorHovered: ColorDynamic.White,
196
+ backgroundColorHovered: ColorDynamic.White40,
197
+
198
+ backgroundColorActive: ColorDynamic.White20,
199
+
200
+ textColorDisabled: ColorDynamic.White50,
201
+ backgroundColorDisabled: ColorDynamic.White08,
202
+ });
203
+ }
204
+
205
+ function getSuccessVariables(size: ButtonSizeProp): ButtonVariables {
206
+ return createButtonVariables(size, {
207
+ textColor: Color.White,
208
+ outlineColor: ColorDynamic.Green30,
209
+ backgroundColor: ColorDynamic.Green300,
210
+
211
+ backgroundColorHovered: ColorDynamic.Green500,
212
+
213
+ backgroundColorActive: ColorDynamic.Green500,
214
+
215
+ textColorDisabled: Color.White50,
216
+ backgroundColorDisabled: ColorDynamic.Green30,
217
+ });
218
+ }
219
+
220
+ const ButtonRoot = styled.button<ButtonStyleProps>(
221
+ ({ size, theme, variant, fullWidth }) => {
222
+ const variables =
223
+ variant === 'primary'
224
+ ? getPrimaryVariables(size)
225
+ : variant === 'neutral'
226
+ ? getNeutralVariables(size)
227
+ : variant === 'critical'
228
+ ? getCriticalVariables(size)
229
+ : variant === 'text'
230
+ ? getTextVariables(size)
231
+ : variant === 'inverted'
232
+ ? getInvertedVariables(size)
233
+ : variant === 'success'
234
+ ? getSuccessVariables(size)
235
+ : getDefaultVariables(size);
236
+
237
+ return css`
238
+ /* Reset button styles */
239
+ border: 0;
240
+ margin: 0;
241
+ outline: 0;
242
+ position: relative;
243
+ vertical-align: middle;
244
+
245
+ /* Fixes for the anchor element */
246
+ cursor: pointer;
247
+ text-decoration: none;
248
+
249
+ &[aria-disabled='true'] {
250
+ cursor: default;
251
+ /* Disable link interactions */
252
+ pointer-events: none;
253
+ }
254
+
255
+ /* Firefox fixes */
256
+ -moz-appearance: none;
257
+
258
+ &::-moz-focus-inner {
259
+ /* Remove Firefox dotted outline */
260
+ border-style: none;
261
+ }
262
+
263
+ /* Webkit fixes */
264
+ -webkit-appearance: none;
265
+ -webkit-user-select: none;
266
+ -webkit-tap-highlight-color: transparent;
267
+ @media print {
268
+ -webkit-print-color-adjust: exact;
269
+ }
270
+
271
+ /* Button styles */
272
+
273
+ --button-visibility: visible;
274
+ --button-text-color: ${variables.textColor};
275
+ --button-border-color: ${variables.borderColor};
276
+ --button-outline-color: ${ColorDynamic.Transparent};
277
+ --button-background-color: ${variables.backgroundColor};
278
+ --button-icon-color: ${variables.iconColor};
279
+
280
+ --button-padding-x: ${variables.paddingXMobile}px;
281
+ --button-padding-y: ${variables.paddingYMobile}px;
282
+ --button-font-size: ${variables.fontSizeMobile}px;
283
+ --button-line-height: ${variables.lineHeightMobile}px;
284
+
285
+ ${theme.breakpoints.up('sm')} {
286
+ --button-padding-x: ${variables.paddingX}px;
287
+ --button-padding-y: ${variables.paddingY}px;
288
+ --button-font-size: ${variables.fontSize}px;
289
+ --button-line-height: ${variables.lineHeight}px;
290
+ }
291
+
292
+ &[aria-disabled='true'] {
293
+ --button-text-color: ${variables.textColorDisabled};
294
+ --button-border-color: ${variables.borderColorDisabled};
295
+ --button-background-color: ${variables.backgroundColorDisabled};
296
+ --button-icon-color: ${variables.textColorDisabled};
297
+
298
+ &[aria-busy='true'] {
299
+ --button-visibility: hidden;
300
+ }
301
+ }
302
+
303
+ &[aria-disabled='false'] {
304
+ &[aria-expanded='true'] {
305
+ --button-text-color: ${variables.textColorHovered};
306
+ --button-border-color: ${variables.borderColorHovered};
307
+ --button-background-color: ${variables.backgroundColorHovered};
308
+ --button-icon-color: ${variables.textColorHovered};
309
+ }
310
+
311
+ &,
312
+ &[aria-expanded='true'] {
313
+ &:focus {
314
+ --button-outline-color: ${variables.outlineColor};
315
+ }
316
+
317
+ @media (hover: hover) and (pointer: fine) {
318
+ &:hover {
319
+ --button-text-color: ${variables.textColorHovered};
320
+ --button-border-color: ${variables.borderColorHovered};
321
+ --button-background-color: ${variables.backgroundColorHovered};
322
+
323
+ span {
324
+ svg {
325
+ fill: ${variables.textColorHovered};
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ &:active {
332
+ --button-background-color: ${variables.backgroundColorActive};
333
+ }
334
+ }
335
+ }
336
+
337
+ display: inline-flex;
338
+ align-items: center;
339
+ justify-content: center;
340
+
341
+ min-width: 48px;
342
+ width: ${fullWidth ? '100%' : 'auto'};
343
+
344
+ border-radius: 4px;
345
+ font-family: ${theme.typography.fontFamily};
346
+ font-weight: ${theme.typography.fontWeightBold};
347
+
348
+ color: var(--button-text-color);
349
+ background-color: var(--button-background-color);
350
+ font-size: var(--button-font-size);
351
+ line-height: var(--button-line-height);
352
+ padding: var(--button-padding-y) var(--button-padding-x);
353
+
354
+ box-shadow: inset 0 0 0 1px var(--button-border-color),
355
+ 0 0 0 2px var(--button-outline-color);
356
+
357
+ transition: ${theme.transitions.create([
358
+ 'color',
359
+ 'box-shadow',
360
+ 'background-color',
361
+ ])};
362
+ `;
363
+ },
364
+ );
365
+
366
+ const ButtonLabel = styled.span`
367
+ display: inherit;
368
+ align-items: inherit;
369
+ justify-content: inherit;
370
+ visibility: var(--button-visibility);
371
+ --mui-svg-icon-size: var(--button-line-height);
372
+ `;
373
+
374
+ const ButtonStartIcon = styled.span`
375
+ margin-right: 4px;
376
+ color: var(--button-icon-color);
377
+ `;
378
+
379
+ const ButtonEndIcon = styled.span`
380
+ margin-left: 4px;
381
+ color: var(--button-icon-color);
382
+ `;
383
+
384
+ const ButtonPendingIndicator = styled.span`
385
+ left: 50%;
386
+ display: flex;
387
+ position: absolute;
388
+ visibility: visible;
389
+ transform: translate(-50%);
390
+ `;
391
+
392
+ interface BaseButtonProps<T extends HTMLElement>
393
+ extends Pick<
394
+ AriaAttributes,
395
+ 'aria-label' | 'aria-controls' | 'aria-haspopup' | 'aria-labelledby'
396
+ > {
397
+ active?: boolean;
398
+ pending?: boolean;
399
+ disabled?: boolean;
400
+ autoFocus?: boolean;
401
+
402
+ fullWidth?: boolean;
403
+ size?: ButtonSizeProp;
404
+ variant?: ButtonVariantProp;
405
+
406
+ children?: ReactNode;
407
+ startIcon?: ReactNode;
408
+ endIcon?: ReactNode;
409
+
410
+ id?: string;
411
+ tabIndex?: number;
412
+
413
+ onClick?: MouseEventHandler<T>;
414
+ onFocus?: FocusEventHandler<T>;
415
+ onBlur?: FocusEventHandler<T>;
416
+
417
+ form?: string;
418
+ }
419
+
420
+ function useButtonProps<T extends HTMLElement>({
421
+ children,
422
+
423
+ endIcon,
424
+ startIcon,
425
+
426
+ tabIndex: tabIndexProp = 0,
427
+
428
+ active = false,
429
+ pending = false,
430
+ disabled: disabledProp = false,
431
+
432
+ size = 'medium',
433
+ fullWidth = false,
434
+ variant = 'default',
435
+ ...props
436
+ }: BaseButtonProps<T>): ButtonStyleProps & HTMLAttributes<T> {
437
+ const disabled = pending || disabledProp;
438
+ const tabIndex = disabled ? -1 : tabIndexProp;
439
+
440
+ return {
441
+ ...props,
442
+ size,
443
+ variant,
444
+ fullWidth,
445
+ tabIndex,
446
+ disabled,
447
+ 'aria-busy': pending,
448
+ 'aria-expanded': active,
449
+ 'aria-disabled': disabled,
450
+ children: (
451
+ <ButtonLabel>
452
+ {!!startIcon && <ButtonStartIcon>{startIcon}</ButtonStartIcon>}
453
+ {children}
454
+ {!!endIcon && <ButtonEndIcon>{endIcon}</ButtonEndIcon>}
455
+ {pending && (
456
+ <ButtonPendingIndicator>
457
+ <CircularProgress size="1em" color="inherit" />
458
+ </ButtonPendingIndicator>
459
+ )}
460
+ </ButtonLabel>
461
+ ),
462
+ };
463
+ }
464
+
465
+ export interface ButtonProps extends BaseButtonProps<HTMLButtonElement> {
466
+ type?: 'button' | 'submit';
467
+ }
468
+
469
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
470
+ ({ type = 'button', ...props }, ref) => {
471
+ const buttonProps = useButtonProps(props);
472
+
473
+ return <ButtonRoot {...buttonProps} ref={ref} type={type} />;
474
+ },
475
+ );
476
+
477
+ export interface AnchorButtonProps extends BaseButtonProps<HTMLAnchorElement> {
478
+ href: string;
479
+ target?: '_self' | '_blank';
480
+ }
481
+
482
+ export const AnchorButton = forwardRef<HTMLAnchorElement, AnchorButtonProps>(
483
+ ({ href, target, ...props }, ref) => {
484
+ const buttonProps = useButtonProps(props);
485
+ const rel = target === '_blank' ? 'noopener noreferrer' : undefined;
486
+
487
+ return (
488
+ <ButtonRoot
489
+ {...buttonProps}
490
+ as="a"
491
+ ref={ref}
492
+ rel={rel}
493
+ href={href}
494
+ target={target}
495
+ />
496
+ );
497
+ },
498
+ );
@@ -0,0 +1,65 @@
1
+ import { ThumbDown, ThumbUp } from '@material-ui/icons';
2
+ import { Meta } from '@storybook/react';
3
+ import { Column, Columns, Stack } from '@superdispatch/ui';
4
+ import { ButtonArea } from './ButtonArea';
5
+
6
+ export default {
7
+ title: 'Lab/ButtonArea',
8
+ component: ButtonArea,
9
+ } as Meta;
10
+
11
+ export const basic = () => (
12
+ <Columns space="small">
13
+ <Column>
14
+ <Stack>
15
+ <ButtonArea fullWidth={true} icon={<ThumbUp />} variant="success">
16
+ Positive
17
+ </ButtonArea>
18
+
19
+ <ButtonArea
20
+ active={true}
21
+ fullWidth={true}
22
+ icon={<ThumbUp />}
23
+ variant="success"
24
+ >
25
+ Positive
26
+ </ButtonArea>
27
+
28
+ <ButtonArea
29
+ disabled={true}
30
+ fullWidth={true}
31
+ icon={<ThumbUp />}
32
+ variant="success"
33
+ >
34
+ Positive
35
+ </ButtonArea>
36
+ </Stack>
37
+ </Column>
38
+
39
+ <Column>
40
+ <Stack>
41
+ <ButtonArea fullWidth={true} icon={<ThumbDown />} variant="danger">
42
+ Negative
43
+ </ButtonArea>
44
+
45
+ <ButtonArea
46
+ active={true}
47
+ fullWidth={true}
48
+ icon={<ThumbDown />}
49
+ variant="danger"
50
+ >
51
+ Negative
52
+ </ButtonArea>
53
+
54
+ <ButtonArea
55
+ disabled={true}
56
+ fullWidth={true}
57
+ icon={<ThumbDown />}
58
+ variant="danger"
59
+ >
60
+ Negative
61
+ </ButtonArea>
62
+ </Stack>
63
+ </Column>
64
+ </Columns>
65
+ );
@@ -0,0 +1,90 @@
1
+ import { ButtonBase, ButtonBaseProps, Typography } from '@material-ui/core';
2
+ import { ColorDynamic, Stack } from '@superdispatch/ui';
3
+ import { forwardRef, ReactNode } from 'react';
4
+ import styled from 'styled-components';
5
+
6
+ const ButtonRoot = styled(ButtonBase)`
7
+ padding: 12px 32px;
8
+
9
+ border-width: 1px;
10
+ border-radius: 4px;
11
+ border-style: solid;
12
+ border-color: ${ColorDynamic.Silver500};
13
+
14
+ color: ${ColorDynamic.Dark100};
15
+ background-color: ${ColorDynamic.White};
16
+
17
+ & svg {
18
+ color: inherit;
19
+ font-size: 24px;
20
+ }
21
+
22
+ &[data-full-width='true'] {
23
+ width: 100%;
24
+ }
25
+
26
+ &[data-disabled='true'] {
27
+ color: ${ColorDynamic.Silver500};
28
+ border-color: ${ColorDynamic.Silver400};
29
+ }
30
+
31
+ &[data-variant='success'] {
32
+ &:hover {
33
+ color: ${ColorDynamic.Green300};
34
+ box-shadow: 0 0 0 2px ${ColorDynamic.Green100};
35
+ border-color: ${ColorDynamic.Green300};
36
+ }
37
+
38
+ &[data-active='true'] {
39
+ color: ${ColorDynamic.Green300};
40
+ border-color: ${ColorDynamic.Green300};
41
+ background-color: ${ColorDynamic.Green50};
42
+ }
43
+ }
44
+
45
+ &[data-variant='danger'] {
46
+ &:hover {
47
+ color: ${ColorDynamic.Red300};
48
+ box-shadow: 0 0 0 2px ${ColorDynamic.Red100};
49
+ border-color: ${ColorDynamic.Red300};
50
+ }
51
+
52
+ &[data-active='true'] {
53
+ color: ${ColorDynamic.Red300};
54
+ border-color: ${ColorDynamic.Red300};
55
+ background-color: ${ColorDynamic.Red50};
56
+ }
57
+ }
58
+ `;
59
+
60
+ type ButtonAreaVariant = 'danger' | 'success';
61
+
62
+ export interface ButtonAreaProps extends ButtonBaseProps {
63
+ icon: ReactNode;
64
+ active?: boolean;
65
+ fullWidth?: boolean;
66
+ variant?: ButtonAreaVariant;
67
+ children?: ReactNode;
68
+ }
69
+
70
+ export const ButtonArea = forwardRef<
71
+ HTMLButtonElement,
72
+ Omit<ButtonAreaProps, 'ref'>
73
+ >(({ icon, children, variant, active, disabled, fullWidth, ...props }, ref) => (
74
+ <ButtonRoot
75
+ ref={ref}
76
+ disabled={disabled}
77
+ data-active={active}
78
+ data-disabled={disabled}
79
+ data-variant={variant}
80
+ data-full-width={fullWidth}
81
+ {...props}
82
+ >
83
+ <Stack align="center" space="xxsmall">
84
+ {icon}
85
+ <Typography variant="h4" color={disabled ? 'inherit' : 'textPrimary'}>
86
+ {children}
87
+ </Typography>
88
+ </Stack>
89
+ </ButtonRoot>
90
+ ));