-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Button] Create ButtonUnstyled and useButton #27600
Merged
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
8e9e330
Add ButtonUnstyled
michaldudak 4711e17
useButton
michaldudak c5f12ea
Move props to a separate file
michaldudak aad757a
Move logic to useButton
michaldudak 8a11765
Make useButton props consistent with ButtonUnstyled
michaldudak 858eca8
Add API docs and demos
michaldudak 11359b7
Fix the build
michaldudak acbd594
Format demos
michaldudak 7290dee
Add better support for the active state
michaldudak 4f10ac0
Add useButton tests
michaldudak ee1ae90
Merge remote-tracking branch 'upstream/next' into feat/unstyled-button
michaldudak f21564a
Write API docs
michaldudak 6b33819
Add more demos
michaldudak c2ee127
Merge remote-tracking branch 'upstream/next' into feat/unstyled-button
michaldudak 06d4b3c
Use useButton in ButtonBase
michaldudak 68b06ab
Merge remote-tracking branch 'upstream/next' into feat/unstyled-button
michaldudak 08d8f41
Fix tests
michaldudak b85648c
Use useTouchRipple in ButtonBase
michaldudak d3be252
Extract chainEventHandlers to utils
michaldudak fcee0fb
Merge branch 'next' into feat/unstyled-button
michaldudak 126e515
Fix the build
michaldudak b0f669d
Make demos prettier
michaldudak 065052a
Refactor and correct useButton and demos
michaldudak 4d1fcc9
Do not call all event handlers automatically
michaldudak a46d7c1
Merge branch 'next' into feat/unstyled-button
michaldudak 52ec0a6
Add dark mode for adv customisation demo
michaldudak 75c5c0b
Fix regression tests
michaldudak 4586840
Apply CR suggestions
michaldudak ce874d9
Merge remote-tracking branch 'upstream/next' into feat/unstyled-button
michaldudak df52895
Do not dispatch a click event
michaldudak 41858c1
Remove mergeEventHandlers utility
michaldudak 66019df
Tweak transition speed in demos
michaldudak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import * as React from 'react'; | ||
import ApiPage from 'docs/src/modules/components/ApiPage'; | ||
import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; | ||
import jsonPageContent from './button-unstyled.json'; | ||
|
||
export default function Page(props) { | ||
const { descriptions, pageContent } = props; | ||
return <ApiPage descriptions={descriptions} pageContent={pageContent} />; | ||
} | ||
|
||
Page.getInitialProps = () => { | ||
const req = require.context( | ||
'docs/translations/api-docs/button-unstyled', | ||
false, | ||
/button-unstyled.*.json$/, | ||
); | ||
const descriptions = mapApiPageTranslations(req); | ||
|
||
return { | ||
descriptions, | ||
pageContent: jsonPageContent, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"props": { | ||
"action": { | ||
"type": { | ||
"name": "union", | ||
"description": "func<br>| { current?: { focusVisible: func } }" | ||
} | ||
}, | ||
"component": { "type": { "name": "elementType" }, "default": "'button'" }, | ||
"components": { | ||
"type": { "name": "shape", "description": "{ Root?: elementType }" }, | ||
"default": "{}" | ||
}, | ||
"disabled": { "type": { "name": "bool" } } | ||
}, | ||
"name": "ButtonUnstyled", | ||
"styles": { "classes": [], "globalClasses": {}, "name": null }, | ||
"spread": true, | ||
"forwardsRefTo": "HTMLButtonElement", | ||
"filename": "/packages/material-ui-unstyled/src/ButtonUnstyled/ButtonUnstyled.tsx", | ||
"inheritance": null, | ||
"demos": "<ul><li><a href=\"/components/buttons/\">Buttons</a></li></ul>", | ||
"styledComponent": true, | ||
"cssComponent": false | ||
} |
108 changes: 108 additions & 0 deletions
108
docs/src/pages/components/buttons/UnstyledButtonCustom.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import ButtonUnstyled, { | ||
buttonUnstyledClasses, | ||
} from '@material-ui/unstyled/ButtonUnstyled'; | ||
import { ThemeProvider, createTheme } from '@material-ui/core/styles'; | ||
import { styled, alpha } from '@material-ui/system'; | ||
|
||
const ButtonRoot = React.forwardRef(function ButtonRoot(props, ref) { | ||
const { children, ...other } = props; | ||
|
||
return ( | ||
<svg width="150" height="50" {...other} ref={ref}> | ||
<polygon points="0,50 0,0 150,0 150,50" className="bg" /> | ||
<polygon points="0,50 0,0 150,0 150,50" className="borderEffect" /> | ||
<foreignObject x="0" y="0" width="150" height="50"> | ||
<div className="content">{children}</div> | ||
</foreignObject> | ||
</svg> | ||
); | ||
}); | ||
|
||
ButtonRoot.propTypes = { | ||
children: PropTypes.node, | ||
}; | ||
|
||
const CustomButtonRoot = styled(ButtonRoot)( | ||
({ theme }) => ` | ||
overflow: visible; | ||
cursor: pointer; | ||
|
||
& polygon { | ||
fill: transparent; | ||
transition: all 800ms ease; | ||
pointer-events: none; | ||
} | ||
|
||
& .bg { | ||
stroke: ${theme.palette.primary.main}; | ||
stroke-width: 0.5; | ||
filter: drop-shadow(0 4px 20px rgba(0, 0, 0, 0.1)); | ||
} | ||
|
||
& .borderEffect { | ||
stroke: ${theme.palette.primary.main}; | ||
stroke-width: 2; | ||
stroke-dasharray: 150 600; | ||
stroke-dashoffset: 150; | ||
} | ||
|
||
&:hover, | ||
&.${buttonUnstyledClasses.focusVisible} { | ||
.borderEffect { | ||
stroke-dashoffset: -600; | ||
} | ||
|
||
.bg { | ||
fill: ${alpha(theme.palette.primary.main, theme.palette.action.hoverOpacity)}; | ||
} | ||
} | ||
|
||
&:focus, | ||
&.${buttonUnstyledClasses.focusVisible} { | ||
outline: none; | ||
} | ||
|
||
&.${buttonUnstyledClasses.active} { | ||
& .bg { | ||
fill: ${alpha( | ||
theme.palette.primary.main, | ||
theme.palette.action.activatedOpacity, | ||
)}; | ||
transition: fill 300ms ease-out; | ||
} | ||
} | ||
|
||
& foreignObject { | ||
pointer-events: none; | ||
|
||
& .content { | ||
font-family: Helvetica, Inter, Arial, sans-serif; | ||
font-size: 14px; | ||
font-weight: 200; | ||
height: 100%; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
color: ${theme.palette.primary.main}; | ||
text-transform: uppercase; | ||
} | ||
|
||
& svg { | ||
margin: 0 5px; | ||
} | ||
}`, | ||
); | ||
|
||
const SvgButton = React.forwardRef(function SvgButton(props, ref) { | ||
return <ButtonUnstyled {...props} component={CustomButtonRoot} ref={ref} />; | ||
}); | ||
|
||
export default function UnstyledButtonCustom() { | ||
return ( | ||
<ThemeProvider theme={createTheme()}> | ||
<SvgButton>Button</SvgButton> | ||
</ThemeProvider> | ||
); | ||
} |
110 changes: 110 additions & 0 deletions
110
docs/src/pages/components/buttons/UnstyledButtonCustom.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import * as React from 'react'; | ||
import ButtonUnstyled, { | ||
ButtonUnstyledProps, | ||
buttonUnstyledClasses, | ||
} from '@material-ui/unstyled/ButtonUnstyled'; | ||
import { Theme, ThemeProvider, createTheme } from '@material-ui/core/styles'; | ||
import { styled, alpha } from '@material-ui/system'; | ||
|
||
const ButtonRoot = React.forwardRef(function ButtonRoot( | ||
props: React.PropsWithChildren<{}>, | ||
ref: React.ForwardedRef<any>, | ||
) { | ||
const { children, ...other } = props; | ||
|
||
return ( | ||
<svg width="150" height="50" {...other} ref={ref}> | ||
<polygon points="0,50 0,0 150,0 150,50" className="bg" /> | ||
<polygon points="0,50 0,0 150,0 150,50" className="borderEffect" /> | ||
<foreignObject x="0" y="0" width="150" height="50"> | ||
<div className="content">{children}</div> | ||
</foreignObject> | ||
</svg> | ||
); | ||
}); | ||
|
||
const CustomButtonRoot = styled(ButtonRoot)( | ||
({ theme }: { theme: Theme }) => ` | ||
overflow: visible; | ||
cursor: pointer; | ||
|
||
& polygon { | ||
fill: transparent; | ||
transition: all 800ms ease; | ||
pointer-events: none; | ||
} | ||
|
||
& .bg { | ||
stroke: ${theme.palette.primary.main}; | ||
stroke-width: 0.5; | ||
filter: drop-shadow(0 4px 20px rgba(0, 0, 0, 0.1)); | ||
} | ||
|
||
& .borderEffect { | ||
stroke: ${theme.palette.primary.main}; | ||
stroke-width: 2; | ||
stroke-dasharray: 150 600; | ||
stroke-dashoffset: 150; | ||
} | ||
|
||
&:hover, | ||
&.${buttonUnstyledClasses.focusVisible} { | ||
.borderEffect { | ||
stroke-dashoffset: -600; | ||
} | ||
|
||
.bg { | ||
fill: ${alpha(theme.palette.primary.main, theme.palette.action.hoverOpacity)}; | ||
} | ||
} | ||
|
||
&:focus, | ||
&.${buttonUnstyledClasses.focusVisible} { | ||
outline: none; | ||
} | ||
|
||
&.${buttonUnstyledClasses.active} { | ||
& .bg { | ||
fill: ${alpha( | ||
theme.palette.primary.main, | ||
theme.palette.action.activatedOpacity, | ||
)}; | ||
transition: fill 300ms ease-out; | ||
} | ||
} | ||
|
||
& foreignObject { | ||
pointer-events: none; | ||
|
||
& .content { | ||
font-family: Helvetica, Inter, Arial, sans-serif; | ||
font-size: 14px; | ||
font-weight: 200; | ||
height: 100%; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
color: ${theme.palette.primary.main}; | ||
text-transform: uppercase; | ||
} | ||
|
||
& svg { | ||
margin: 0 5px; | ||
} | ||
}`, | ||
); | ||
|
||
const SvgButton = React.forwardRef(function SvgButton( | ||
props: ButtonUnstyledProps, | ||
ref: React.ForwardedRef<any>, | ||
) { | ||
return <ButtonUnstyled {...props} component={CustomButtonRoot} ref={ref} />; | ||
}); | ||
|
||
export default function UnstyledButtonCustom() { | ||
return ( | ||
<ThemeProvider theme={createTheme()}> | ||
<SvgButton>Button</SvgButton> | ||
</ThemeProvider> | ||
); | ||
} |
52 changes: 52 additions & 0 deletions
52
docs/src/pages/components/buttons/UnstyledButtonsSimple.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import * as React from 'react'; | ||
import Stack from '@material-ui/core/Stack'; | ||
import ButtonUnstyled, { | ||
buttonUnstyledClasses, | ||
} from '@material-ui/unstyled/ButtonUnstyled'; | ||
import { styled } from '@material-ui/system'; | ||
|
||
const CustomButtonRoot = styled('button')(` | ||
background-color: #007fff; | ||
padding: 15px 20px; | ||
border-radius: 10px; | ||
color: #fff; | ||
font-weight: 600; | ||
font-family: Helvetica, Arial, sans-serif; | ||
font-size: 14px; | ||
transition: all 200ms ease; | ||
cursor: pointer; | ||
box-shadow: 0 4px 20px 0 rgba(61, 71, 82, 0.1), 0 0 0 0 rgba(0, 127, 255, 0); | ||
border: none; | ||
|
||
&:hover { | ||
background-color: #0059b2; | ||
} | ||
|
||
&.${buttonUnstyledClasses.active} { | ||
background-color: #004386; | ||
} | ||
|
||
&.${buttonUnstyledClasses.focusVisible} { | ||
box-shadow: 0 4px 20px 0 rgba(61, 71, 82, 0.1), 0 0 0 5px rgba(0, 127, 255, 0.5); | ||
outline: none; | ||
} | ||
|
||
&.${buttonUnstyledClasses.disabled} { | ||
opacity: 0.5; | ||
cursor: not-allowed; | ||
box-shadow: 0 0 0 0 rgba(0, 127, 255, 0); | ||
} | ||
`); | ||
|
||
function CustomButton(props) { | ||
return <ButtonUnstyled {...props} component={CustomButtonRoot} />; | ||
} | ||
|
||
export default function UnstyledButton() { | ||
return ( | ||
<Stack spacing={2} direction="row"> | ||
<CustomButton>Button</CustomButton> | ||
<CustomButton disabled>Disabled</CustomButton> | ||
</Stack> | ||
); | ||
} |
53 changes: 53 additions & 0 deletions
53
docs/src/pages/components/buttons/UnstyledButtonsSimple.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,53 @@ | ||||||
import * as React from 'react'; | ||||||
import Stack from '@material-ui/core/Stack'; | ||||||
import ButtonUnstyled, { | ||||||
buttonUnstyledClasses, | ||||||
ButtonUnstyledProps, | ||||||
} from '@material-ui/unstyled/ButtonUnstyled'; | ||||||
import { styled } from '@material-ui/system'; | ||||||
|
||||||
const CustomButtonRoot = styled('button')(` | ||||||
background-color: #007fff; | ||||||
padding: 15px 20px; | ||||||
border-radius: 10px; | ||||||
color: #fff; | ||||||
font-weight: 600; | ||||||
font-family: Helvetica, Arial, sans-serif; | ||||||
font-size: 14px; | ||||||
transition: all 200ms ease; | ||||||
cursor: pointer; | ||||||
box-shadow: 0 4px 20px 0 rgba(61, 71, 82, 0.1), 0 0 0 0 rgba(0, 127, 255, 0); | ||||||
border: none; | ||||||
|
||||||
&:hover { | ||||||
background-color: #0059b2; | ||||||
} | ||||||
|
||||||
&.${buttonUnstyledClasses.active} { | ||||||
background-color: #004386; | ||||||
} | ||||||
|
||||||
&.${buttonUnstyledClasses.focusVisible} { | ||||||
box-shadow: 0 4px 20px 0 rgba(61, 71, 82, 0.1), 0 0 0 5px rgba(0, 127, 255, 0.5); | ||||||
outline: none; | ||||||
} | ||||||
|
||||||
&.${buttonUnstyledClasses.disabled} { | ||||||
opacity: 0.5; | ||||||
cursor: not-allowed; | ||||||
box-shadow: 0 0 0 0 rgba(0, 127, 255, 0); | ||||||
} | ||||||
`); | ||||||
|
||||||
function CustomButton(props: ButtonUnstyledProps) { | ||||||
return <ButtonUnstyled {...props} component={CustomButtonRoot} />; | ||||||
} | ||||||
|
||||||
export default function UnstyledButton() { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
return ( | ||||||
<Stack spacing={2} direction="row"> | ||||||
<CustomButton>Button</CustomButton> | ||||||
<CustomButton disabled>Disabled</CustomButton> | ||||||
</Stack> | ||||||
); | ||||||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way we could remove the import from the soon to be named material package? I'm not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. When imported from
@material-ui/system
, the theme got fromcreateTheme()
doesn't have the defaults, so it would be necessary to create all the used tokens. Alternatively, I'm thinking I could rely on just thetheme.palette.mode
and use hardcoded colors in CSS.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I honestly don't know what would be best. The current tradeoff might already be great.
Another option would be to use the color palette of Joy, if the defaut theme comes fully loaded with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, use the palette from the docs.
However, not relying on theme values would make the demo look the same everywhere. So if someone just takes the demo code and pastes it in their project, they won't be confused that it looks different.
Here's a new implementation - let me know if you like it better (it has not changed visually): michaldudak@40045aa
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @siriwatknp and @danilo-leal for direction on this.
I had this in mind with the palette color of Joy, say if it comes with blue, red, yellow, purple, orange, etc. by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move the discussion to #28073