1
1
import React , { useCallback } from 'react'
2
2
import styled , { css } from 'styled-components'
3
3
import { useVisibleFocus } from 'react-hooks-shareable'
4
-
5
4
import { Icon , IconType } from '../Icon'
6
5
import { Typography } from '../Typography'
7
6
import { componentSize , opacity , spacing , shape } from '../designparams'
8
7
9
8
// Button min-width should be "Cancel" button width
10
9
const BUTTON_MIN_WIDTH = '83px'
11
10
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
+
12
26
/**
13
27
* Button
14
28
*
@@ -24,20 +38,9 @@ export const NativeButton = styled.button<{
24
38
readonly icon ?: IconType
25
39
readonly visibleFocus : boolean
26
40
} > `
27
- white-space: nowrap;
28
- max-width: 100%;
41
+ ${ COMMON_STYLE }
29
42
min-width: ${ BUTTON_MIN_WIDTH } ;
30
- height: ${ componentSize . small } ;
31
- outline: none;
32
- &::-moz-focus-inner {
33
- border: 0;
34
- }
35
43
border: 2px solid transparent;
36
- border-radius: ${ shape . radius . small } ;
37
- cursor: pointer;
38
- user-select: none;
39
- transition: all 200ms;
40
-
41
44
padding: ${ ( { icon } ) =>
42
45
icon === undefined
43
46
? `0 ${ spacing . large } `
@@ -497,3 +500,150 @@ export const IconButton = React.forwardRef<BaseElement, IconButtonProps>(
497
500
)
498
501
}
499
502
)
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