11import React , { useCallback } from 'react'
22import styled , { css } from 'styled-components'
33import { useVisibleFocus } from 'react-hooks-shareable'
4-
54import { Icon , IconType } from '../Icon'
65import { Typography } from '../Typography'
76import { componentSize , opacity , spacing , shape } from '../designparams'
87
98// Button min-width should be "Cancel" button width
109const BUTTON_MIN_WIDTH = '83px'
1110
11+ //Common CSS for NativeButton and NativeIconTextButton
12+ const COMMON_STYLE = css `
13+ white-space : nowrap;
14+ height : ${ componentSize . small } ;
15+ outline : none;
16+ & ::-moz-focus-inner {
17+ border : 0 ;
18+ }
19+ border-radius : ${ shape . radius . small } ;
20+ cursor : pointer;
21+ user-select : none;
22+ transition : all 200ms ;
23+ max-width : 100% ;
24+ `
25+
1226/**
1327 * Button
1428 *
@@ -24,20 +38,9 @@ export const NativeButton = styled.button<{
2438 readonly icon ?: IconType
2539 readonly visibleFocus : boolean
2640} > `
27- white-space: nowrap;
28- max-width: 100%;
41+ ${ COMMON_STYLE }
2942 min-width: ${ BUTTON_MIN_WIDTH } ;
30- height: ${ componentSize . small } ;
31- outline: none;
32- &::-moz-focus-inner {
33- border: 0;
34- }
3543 border: 2px solid transparent;
36- border-radius: ${ shape . radius . small } ;
37- cursor: pointer;
38- user-select: none;
39- transition: all 200ms;
40-
4144 padding: ${ ( { icon } ) =>
4245 icon === undefined
4346 ? `0 ${ spacing . large } `
@@ -497,3 +500,150 @@ export const IconButton = React.forwardRef<BaseElement, IconButtonProps>(
497500 )
498501 }
499502)
503+
504+ /**
505+ * IconTextButton
506+ *
507+ * Always has a primary icon and a secondary text
508+ *
509+ */
510+
511+ export const NativeIconTextButton = styled . button < {
512+ readonly visibleFocus : boolean
513+ } > `
514+ ${ COMMON_STYLE }
515+ border: none;
516+ padding: 0 ${ spacing . large } 0 0;
517+ ${ ( { visibleFocus, theme } ) => {
518+ return css `
519+ color : ${ theme . color . text04 ( ) } ;
520+ fill : ${ theme . color . text04 ( ) } ;
521+ background-color : transparent;
522+
523+ & : hover {
524+ color : ${ theme . color . text03 ( ) } ;
525+ background-color : ${ theme . color . element11 ( opacity [ 16 ] ) } ;
526+ ${ IconContainer } {
527+ background-color : ${ theme . color . textPrimary ( ) } ;
528+ }
529+ }
530+ & : focus {
531+ ${ visibleFocus
532+ ? css `
533+ color : ${ theme . color . text04 ( ) } ;
534+ background-color: ${ theme . color . element11 ( opacity [ 16 ] ) } ;
535+ `
536+ : undefined } ;
537+ }
538+ & : active {
539+ box-shadow : 0 0 0 4px ${ theme . color . elementPrimary ( opacity [ 24 ] ) } ;
540+ background-color : ${ theme . color . element11 ( opacity [ 24 ] ) } ;
541+ }
542+ & : disabled {
543+ opacity : ${ opacity [ 48 ] } ;
544+ cursor : default;
545+ box-shadow : none;
546+ & : hover {
547+ ${ IconContainer } {
548+ background-color : ${ theme . color . elementPrimary ( ) } ;
549+ }
550+ }
551+ }
552+ `
553+ } }
554+ `
555+ const IconContainer = styled ( Icon ) `
556+ ${ ( { theme } ) => {
557+ return css `
558+ height : ${ componentSize . small } ;
559+ width : ${ componentSize . small } ;
560+ color : ${ theme . color . text00 ( ) } ;
561+ background-color : ${ theme . color . elementPrimary ( ) } ;
562+ border-radius : ${ shape . radius . small } ;
563+ margin-right : ${ spacing . medium } ;
564+ padding : ${ spacing . small } ;
565+ transition : all 200ms ;
566+ `
567+ } }
568+ `
569+ export interface IconTextButtonProps
570+ extends Omit < BaseButtonProps , 'variant' | 'accent' > {
571+ /**
572+ * String used to label the button.
573+ */
574+ readonly label : string
575+ /**
576+ * The icon element.
577+ */
578+ readonly icon : IconType
579+ }
580+
581+ // eslint-disable-next-line react/display-name
582+ export const IconTextButton = React . forwardRef <
583+ BaseElement ,
584+ IconTextButtonProps
585+ > (
586+ (
587+ {
588+ disabled = false ,
589+ type = 'button' ,
590+ icon,
591+ onPointerDown,
592+ onPointerUp,
593+ onFocus,
594+ label,
595+ ...props
596+ } ,
597+ ref
598+ ) => {
599+ const {
600+ isPointerOn,
601+ isPointerOff,
602+ determineVisibleFocus,
603+ visibleFocus,
604+ } = useVisibleFocus ( )
605+
606+ const handleFocus = useCallback < React . FocusEventHandler < BaseElement > > (
607+ e => {
608+ onFocus ?.( e )
609+ determineVisibleFocus ( )
610+ } ,
611+ [ determineVisibleFocus , onFocus ]
612+ )
613+ const handlePointerDown = useCallback <
614+ React . PointerEventHandler < BaseElement >
615+ > (
616+ e => {
617+ onPointerDown ?.( e )
618+ isPointerOn ( )
619+ } ,
620+ [ isPointerOn , onPointerDown ]
621+ )
622+ const handlePointerUp = useCallback < React . PointerEventHandler < BaseElement > > (
623+ e => {
624+ onPointerUp ?.( e )
625+ isPointerOff ( )
626+ } ,
627+ [ isPointerOff , onPointerUp ]
628+ )
629+ return (
630+ < NativeIconTextButton
631+ ref = { ref }
632+ disabled = { disabled }
633+ type = { type }
634+ onPointerDown = { handlePointerDown }
635+ onPointerUp = { handlePointerUp }
636+ onFocus = { handleFocus }
637+ { ...props }
638+ visibleFocus = { visibleFocus }
639+ >
640+ < Container >
641+ < IconContainer icon = { icon } />
642+ < LabelContainer variant = "primary" accent = { false } >
643+ < Typography variant = "button-text" > { label } </ Typography >
644+ </ LabelContainer >
645+ </ Container >
646+ </ NativeIconTextButton >
647+ )
648+ }
649+ )
0 commit comments