@toptal/picasso-tabs 5.0.15-alpha-ff-7-tabs-2a3da7cea.16 → 5.0.15-alpha-bill-root-ref-context-42c3d2541.6

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 (62) hide show
  1. package/dist-package/src/Tab/Tab.d.ts +10 -7
  2. package/dist-package/src/Tab/Tab.d.ts.map +1 -1
  3. package/dist-package/src/Tab/Tab.js +26 -73
  4. package/dist-package/src/Tab/Tab.js.map +1 -1
  5. package/dist-package/src/Tab/index.d.ts +4 -1
  6. package/dist-package/src/Tab/index.d.ts.map +1 -1
  7. package/dist-package/src/Tab/index.js.map +1 -1
  8. package/dist-package/src/Tab/styles.d.ts +4 -0
  9. package/dist-package/src/Tab/styles.d.ts.map +1 -0
  10. package/dist-package/src/Tab/styles.js +95 -0
  11. package/dist-package/src/Tab/styles.js.map +1 -0
  12. package/dist-package/src/TabScrollButton/TabScrollButton.d.ts +12 -0
  13. package/dist-package/src/TabScrollButton/TabScrollButton.d.ts.map +1 -0
  14. package/dist-package/src/TabScrollButton/TabScrollButton.js +30 -0
  15. package/dist-package/src/TabScrollButton/TabScrollButton.js.map +1 -0
  16. package/dist-package/src/TabScrollButton/index.d.ts +5 -0
  17. package/dist-package/src/TabScrollButton/index.d.ts.map +1 -0
  18. package/dist-package/src/TabScrollButton/index.js +2 -0
  19. package/dist-package/src/TabScrollButton/index.js.map +1 -0
  20. package/dist-package/src/Tabs/Tabs.d.ts +12 -15
  21. package/dist-package/src/Tabs/Tabs.d.ts.map +1 -1
  22. package/dist-package/src/Tabs/Tabs.js +17 -71
  23. package/dist-package/src/Tabs/Tabs.js.map +1 -1
  24. package/dist-package/src/Tabs/index.d.ts +5 -1
  25. package/dist-package/src/Tabs/index.d.ts.map +1 -1
  26. package/dist-package/src/Tabs/index.js.map +1 -1
  27. package/dist-package/src/Tabs/styles.d.ts +4 -0
  28. package/dist-package/src/Tabs/styles.d.ts.map +1 -0
  29. package/dist-package/src/Tabs/styles.js +41 -0
  30. package/dist-package/src/Tabs/styles.js.map +1 -0
  31. package/dist-package/src/Tabs/use-tab-action.d.ts +5 -0
  32. package/dist-package/src/Tabs/use-tab-action.d.ts.map +1 -0
  33. package/dist-package/src/Tabs/use-tab-action.js +21 -0
  34. package/dist-package/src/Tabs/use-tab-action.js.map +1 -0
  35. package/dist-package/src/TabsCompound/index.d.ts +4 -6
  36. package/dist-package/src/TabsCompound/index.d.ts.map +1 -1
  37. package/dist-package/src/index.d.ts +1 -0
  38. package/dist-package/src/index.d.ts.map +1 -1
  39. package/dist-package/src/index.js +1 -0
  40. package/dist-package/src/index.js.map +1 -1
  41. package/package.json +13 -13
  42. package/src/Tab/Tab.tsx +79 -149
  43. package/src/Tab/__snapshots__/test.tsx.snap +45 -29
  44. package/src/Tab/index.ts +6 -1
  45. package/src/Tab/story/CustomValue.example.tsx +2 -2
  46. package/src/Tab/story/IconOrBadge.example.tsx +3 -3
  47. package/src/Tab/styles.ts +107 -0
  48. package/src/TabScrollButton/TabScrollButton.tsx +59 -0
  49. package/src/TabScrollButton/index.ts +6 -0
  50. package/src/Tabs/Tabs.tsx +60 -145
  51. package/src/Tabs/__snapshots__/test.tsx.snap +76 -45
  52. package/src/Tabs/index.ts +7 -1
  53. package/src/Tabs/story/Default.example.tsx +6 -7
  54. package/src/Tabs/styles.ts +45 -0
  55. package/src/Tabs/test.tsx +14 -16
  56. package/src/Tabs/use-tab-action.ts +27 -0
  57. package/src/index.ts +1 -0
  58. package/dist-package/src/Tabs/TabsContext.d.ts +0 -11
  59. package/dist-package/src/Tabs/TabsContext.d.ts.map +0 -1
  60. package/dist-package/src/Tabs/TabsContext.js +0 -16
  61. package/dist-package/src/Tabs/TabsContext.js.map +0 -1
  62. package/src/Tabs/TabsContext.tsx +0 -27
@@ -0,0 +1,107 @@
1
+ import type { Theme } from '@material-ui/core/styles'
2
+ import { createStyles } from '@material-ui/core/styles'
3
+ import { rem } from '@toptal/picasso-shared'
4
+ import { PicassoProvider } from '@toptal/picasso-provider'
5
+
6
+ PicassoProvider.override(({ breakpoints, palette }: Theme) => ({
7
+ MuiTab: {
8
+ root: {
9
+ minHeight: 0,
10
+ minWidth: 0,
11
+ lineHeight: 1,
12
+ textTransform: 'none',
13
+ padding: `${rem('9px')} 0 ${rem('7px')}`,
14
+ overflow: 'initial',
15
+
16
+ [breakpoints.up('md')]: {
17
+ padding: undefined,
18
+ },
19
+
20
+ color: palette.grey.dark,
21
+
22
+ [breakpoints.up('md')]: {
23
+ minWidth: 'auto',
24
+ fontSize: '1rem',
25
+ },
26
+ },
27
+ labelIcon: {
28
+ minHeight: 0,
29
+ paddingRight: '1.5rem',
30
+ paddingTop: rem('9px'),
31
+ '& $wrapper > *:first-child': {
32
+ position: 'absolute',
33
+ right: 0,
34
+ marginBottom: 0,
35
+ },
36
+ },
37
+ selected: {
38
+ color: palette.common.black,
39
+ },
40
+ textColorInherit: {
41
+ '&$disabled': {
42
+ color: palette.grey.main,
43
+ },
44
+ },
45
+ disabled: {},
46
+ },
47
+ }))
48
+
49
+ export default ({ sizes, palette, shadows, transitions }: Theme) =>
50
+ createStyles({
51
+ horizontal: {
52
+ paddingTop: 0,
53
+ '&:not(:last-child)': {
54
+ marginRight: '2em',
55
+ },
56
+ },
57
+ vertical: {
58
+ width: '100%',
59
+ borderRadius: `${sizes.borderRadius.small} 0 0 ${sizes.borderRadius.small}`,
60
+ margin: '0.25rem 0',
61
+ overflow: 'hidden',
62
+ padding: '0.5rem 1rem',
63
+ transition: `all ${transitions.duration.short}ms ${transitions.easing.easeInOut}`,
64
+ textAlign: 'left',
65
+ backgroundColor: palette.grey.lighter,
66
+ opacity: 1,
67
+ color: palette.grey.dark,
68
+
69
+ '&:first-child': {
70
+ marginTop: '1rem',
71
+ },
72
+
73
+ '&:last-child': {
74
+ marginBottom: '1rem',
75
+ },
76
+
77
+ '&:hover': {
78
+ color: palette.common.black,
79
+ },
80
+
81
+ '&:hover:not($selected)': {
82
+ backgroundColor: palette.grey.lighter2,
83
+ },
84
+
85
+ '& $wrapper': {
86
+ display: 'block',
87
+ },
88
+ },
89
+ selected: {
90
+ '&$vertical': {
91
+ color: palette.common.black,
92
+ boxShadow: shadows[1],
93
+ backgroundColor: palette.grey.lightest,
94
+
95
+ '&::before': {
96
+ content: '""',
97
+ background: palette.blue.main,
98
+ position: 'absolute',
99
+ left: 0,
100
+ top: 0,
101
+ bottom: 0,
102
+ width: '3px',
103
+ },
104
+ },
105
+ },
106
+ wrapper: {},
107
+ })
@@ -0,0 +1,59 @@
1
+ import React, { forwardRef } from 'react'
2
+ import { ButtonBase } from '@material-ui/core'
3
+ import type { BaseProps } from '@toptal/picasso-shared'
4
+ import { BackMinor16, ChevronMinor16 } from '@toptal/picasso-icons'
5
+ import { Container } from '@toptal/picasso-container'
6
+ import { twMerge } from '@toptal/picasso-tailwind-merge'
7
+
8
+ type DirectionType = 'left' | 'right'
9
+
10
+ export interface Props extends BaseProps {
11
+ /** The direction the button should indicate. */
12
+ direction: DirectionType
13
+ /** If `true`, the component is disabled. */
14
+ disabled?: boolean
15
+ }
16
+
17
+ export const TabScrollButton = forwardRef<HTMLDivElement, Props>(
18
+ function TabScrollButton(props, ref) {
19
+ const { className, style, direction, disabled, ...rest } = props
20
+
21
+ if (disabled) {
22
+ return null
23
+ }
24
+
25
+ return (
26
+ <Container
27
+ {...rest}
28
+ ref={ref}
29
+ className={twMerge('relative', className)}
30
+ style={style}
31
+ >
32
+ <Container
33
+ className={twMerge(
34
+ 'absolute w-10 h-full z-10',
35
+ direction === 'left'
36
+ ? 'bg-gradient-to-r from-white via-white to-transparent'
37
+ : 'bg-gradient-to-l from-white via-white to-transparent',
38
+ direction === 'left' ? 'left-0' : 'right-0'
39
+ )}
40
+ >
41
+ <ButtonBase
42
+ className={twMerge(
43
+ 'absolute w-4 h-full',
44
+ direction === 'left' ? 'left-0' : 'right-0'
45
+ )}
46
+ aria-label={`${direction} button`}
47
+ data-testid={`tab-scroll-button-${direction}`}
48
+ >
49
+ {direction === 'left' ? <BackMinor16 /> : <ChevronMinor16 />}
50
+ </ButtonBase>
51
+ </Container>
52
+ </Container>
53
+ )
54
+ }
55
+ )
56
+
57
+ TabScrollButton.displayName = 'TabScrollButton'
58
+
59
+ export default TabScrollButton
@@ -0,0 +1,6 @@
1
+ import type { OmitInternalProps } from '@toptal/picasso-shared'
2
+
3
+ import type { Props } from './TabScrollButton'
4
+
5
+ export { default as TabScrollButton } from './TabScrollButton'
6
+ export type TabScrollButtonProps = OmitInternalProps<Props>
package/src/Tabs/Tabs.tsx CHANGED
@@ -1,164 +1,79 @@
1
- import type { ReactNode, ChangeEvent, Ref, ReactElement } from 'react'
2
- import React, { forwardRef, useMemo, useCallback } from 'react'
1
+ import type { ForwardedRef, ReactNode } from 'react'
2
+ import React, { forwardRef } from 'react'
3
+ import type { Theme } from '@material-ui/core/styles'
4
+ import { makeStyles } from '@material-ui/core/styles'
5
+ import { Tabs as MUITabs } from '@material-ui/core'
3
6
  import type { BaseProps } from '@toptal/picasso-shared'
4
- import { twJoin, twMerge } from '@toptal/picasso-tailwind-merge'
5
7
 
6
- import { TabsContext } from './TabsContext'
7
- import type { TabProps } from '../Tab'
8
- import { Tab } from '../Tab'
8
+ import { TabScrollButton } from '../TabScrollButton'
9
+ import styles from './styles'
10
+ import useTabAction from './use-tab-action'
9
11
 
10
- export interface TabsProps<T = number> extends BaseProps {
12
+ export type TabsValueType = string | number | false
13
+
14
+ export interface Props<V extends TabsValueType> extends BaseProps {
11
15
  /** Tabs content containing Tab components */
12
16
  children: ReactNode
13
17
 
14
18
  /** Callback fired when the value changes. */
15
- onChange?: (event: ChangeEvent<{}>, value: T) => void
19
+ onChange?: (event: React.ChangeEvent<{}> | null, value: V) => void
16
20
 
17
- /**
18
- * The value of the currently selected Tab.
19
- * If you don't want any selected Tab, you can set this property to null.
20
- */
21
- value: T
21
+ /** The value of the currently selected Tab. If you don't want any selected Tab, you can set this property to false. */
22
+ value: V
22
23
 
23
24
  /** The tabs orientation (layout flow direction). */
24
25
  orientation?: 'horizontal' | 'vertical'
25
26
 
26
27
  /** Determines additional display behavior of the tabs */
27
28
  variant?: 'scrollable' | 'fullWidth'
28
-
29
- /** The default value. Use when the component is not controlled. */
30
- defaultValue?: T
31
-
32
- /** The direction of the text. */
33
- direction?: 'ltr' | 'rtl'
34
29
  }
35
30
 
36
- const indicatorClasses = [
37
- 'after:absolute',
38
- 'after:content-[""]',
39
- 'after:bottom-0',
40
- 'after:left-0',
41
- 'after:right-0',
42
- 'after:h-[1px]',
43
- 'after:bg-gray-500',
44
- 'after:z-0',
45
- ]
46
-
47
- const classesByOrientation = {
48
- vertical: {
49
- root: 'w-[200px] m-0 flex-col',
50
- scroller: 'pl-2',
51
- },
52
- horizontal: {
53
- root: '',
54
- scroller: indicatorClasses,
55
- },
56
- }
57
-
58
- const classesByVariant = {
59
- scrollable: {
60
- root: 'overflow-x-auto',
61
- scroller: '',
62
- },
63
- fullWidth: {
64
- root: '',
65
- scroller: 'w-full overflow-hidden',
66
- },
67
- }
68
-
69
- // eslint-disable-next-line func-style
70
- function TabsInner<T = number>(props: TabsProps<T>, ref: Ref<HTMLDivElement>) {
71
- const {
72
- children,
73
- orientation = 'horizontal',
74
- onChange,
75
- value: valueProp,
76
- defaultValue,
77
- variant = 'scrollable',
78
- direction = 'ltr',
79
- className,
80
- ...rest
81
- } = props
82
-
83
- const [value, setValue] = React.useState<T>(defaultValue as T)
84
- const isControlled = valueProp !== undefined
85
- const currentValue = isControlled ? valueProp : value
86
-
87
- const handleChange = useCallback(
88
- (event: ChangeEvent<{}>, newValue: T) => {
89
- if (!isControlled) {
90
- setValue(newValue)
91
- }
92
- onChange?.(event, newValue)
93
- },
94
- [isControlled, onChange]
95
- )
96
-
97
- const contextValue = useMemo(
98
- () => ({
99
- value: currentValue,
100
- onChange: handleChange,
101
- orientation,
102
- variant,
103
- direction,
104
- }),
105
- [currentValue, handleChange, orientation, variant, direction]
106
- )
107
-
108
- const isVertical = orientation === 'vertical'
109
-
110
- const childrenWithIndex = React.Children.map(children, (child, idx) => {
111
- if (
112
- React.isValidElement(child) &&
113
- child.type === Tab &&
114
- child.props.value === undefined
115
- ) {
116
- return React.cloneElement(child as React.ReactElement<TabProps<number>>, {
117
- value: idx,
118
- })
119
- }
120
-
121
- return child
122
- })
123
-
124
- return (
125
- <TabsContext.Provider value={contextValue}>
126
- <div
127
- {...rest}
128
- ref={ref}
129
- data-component-type='tabs'
130
- className={twMerge(
131
- 'relative min-h-0 flex overflow-hidden',
132
- classesByOrientation[orientation].root,
133
- classesByVariant[variant].root,
134
- className
135
- )}
136
- aria-orientation={orientation}
137
- >
138
- <div
139
- className={twJoin(
140
- classesByVariant[variant].scroller,
141
- classesByOrientation[orientation].scroller,
142
- 'flex-auto inline-block relative whitespace-nowrap'
143
- )}
31
+ const useStyles = makeStyles<Theme>(styles, {
32
+ name: 'Tabs',
33
+ })
34
+
35
+ export const TabsOrientationContext = React.createContext<
36
+ 'horizontal' | 'vertical'
37
+ >('horizontal')
38
+
39
+ export const Tabs = forwardRef(
40
+ <V extends TabsValueType = TabsValueType>(
41
+ props: Props<V>,
42
+ ref: ForwardedRef<HTMLButtonElement>
43
+ ) => {
44
+ const {
45
+ children,
46
+ orientation = 'horizontal',
47
+ onChange,
48
+ value,
49
+ variant = 'scrollable',
50
+ ...rest
51
+ } = props
52
+ const classes = useStyles(props)
53
+ const action = useTabAction()
54
+
55
+ return (
56
+ <TabsOrientationContext.Provider value={orientation}>
57
+ <MUITabs
58
+ {...rest}
59
+ classes={{ root: classes[orientation] }}
60
+ ref={ref}
61
+ onChange={onChange}
62
+ value={value}
63
+ action={action}
64
+ scrollButtons='auto'
65
+ ScrollButtonComponent={TabScrollButton}
66
+ orientation={orientation}
67
+ variant={variant}
68
+ data-component-type='tabs'
144
69
  >
145
- <div
146
- className={twJoin('flex', isVertical && 'flex-col')}
147
- role='tablist'
148
- tabIndex={-1}
149
- >
150
- {childrenWithIndex}
151
- </div>
152
- </div>
153
- </div>
154
- </TabsContext.Provider>
155
- )
156
- }
157
-
158
- TabsInner.displayName = 'Tabs'
159
-
160
- export const Tabs = forwardRef(TabsInner) as <T = number>(
161
- props: TabsProps<T> & { ref?: Ref<HTMLDivElement> }
162
- ) => ReactElement | null
70
+ {children}
71
+ </MUITabs>
72
+ </TabsOrientationContext.Provider>
73
+ )
74
+ }
75
+ ) as <V extends TabsValueType = TabsValueType>(
76
+ props: Props<V> & { ref?: ForwardedRef<HTMLDivElement> }
77
+ ) => ReturnType<typeof MUITabs>
163
78
 
164
79
  export default Tabs