Skip to content

Commit

Permalink
fix: add linkbutton component
Browse files Browse the repository at this point in the history
  • Loading branch information
Birkbjo committed Mar 5, 2024
1 parent d6f076c commit c19814f
Show file tree
Hide file tree
Showing 5 changed files with 389 additions and 9 deletions.
288 changes: 288 additions & 0 deletions src/components/LinkButton/LinkButton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
.linkButton {
display: inline-flex;
position: relative;
align-items: center;
justify-content: center;
border-radius: 4px;
font-weight: 400;
letter-spacing: 0.5px;
text-decoration: none;
cursor: pointer;
user-select: none;
color: var(--colors-grey900);

/*medium*/
height: 36px;
padding: 0 var(--spacers-dp12);
font-size: 14px;
line-height: 16px;

/*basic*/
border: 1px solid var(--colors-grey500);
background-color: #f9fafb;
}

.linkButton:disabled {
cursor: not-allowed;
}

.linkButton:focus {
outline: 3px solid blue;
outline-offset: -3px;
text-decoration: underline;
}

/* Prevent focus styles when mouse clicking */
.linkButton:focus:not(:focus-visible) {
outline: none;
text-decoration: none;
}

/* Prevent focus styles on active and disabled buttons */
.linkButton:active:focus,
.linkButton:disabled:focus {
outline: none;
text-decoration: none;
}

.linkButton:hover {
border-color: var(--colors-grey500);
background-color: var(--colors-grey200);
}

.linkButton:active,
.linkButton:active:focus {
border-color: var(--colors-grey500);
background-color: var(--colors-grey200);
box-shadow: 0 0 0 1px rgb(0, 0, 0, 0.1) inset;
}

.linkButton:focus {
background-color: #f9fafb;
}

.linkButton:disabled {
border-color: var(--colors-grey400);
background-color: #f9fafb;
box-shadow: none;
color: var(--theme-disabled);
fill: var(--theme-disabled);
}

.small {
height: 28px;
padding: 0 6px;
font-size: 14px;
line-height: 16px;
}

.large {
height: 43px;
padding: 0 var(--spacers-dp24);
font-size: 16px;
letter-spacing: 0.57px;
line-height: 19px;
}

.primary {
border-color: var(--theme-primary800);
background: linear-gradient(180deg, #1565c0 0%, #0650a3 100%);
background-color: #2b61b3;
color: var(--colors-white);
fill: var(--colors-white);
font-weight: 500;
}

.primary:hover {
border-color: var(--theme-primary800);
background: linear-gradient(180deg, #054fa3 0%, #034793 100%);
background-color: #21539f;
}

.primary:active,
.primary:active:focus {
background: linear-gradient(180deg, #054fa3 0%, #034793 100%);
background-color: #1c4a90;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.18) inset;
}

.primary:focus {
background: var(--colors-blue800);
border-color: var(--colors-blue900);
outline-offset: -5px;
}

.primary:disabled {
border-color: #93a6bd;
background: #b3c6de;
box-shadow: none;
color: var(--colors-white);
fill: var(--colors-white);
}

.secondary {
border-color: rgba(74, 87, 104, 0.25);
background-color: transparent;
}

.secondary:hover {
border-color: rgba(74, 87, 104, 0.5);
background-color: rgba(160, 173, 186, 0.05);
}

.secondary:active,
.secondary:active:focus {
background-color: rgba(160, 173, 186, 0.2);
box-shadow: none;
}

.secondary:focus {
background-color: transparent;
}

.secondary:disabled {
border-color: rgba(74, 87, 104, 0.25);
background-color: transparent;
box-shadow: none;
color: var(--theme-disabled);
fill: var(--theme-disabled);
}

.destructive {
border-color: #a10b0b;
background: linear-gradient(180deg, #d32f2f 0%, #b71c1c 100%);
background-color: #b9242b;
color: var(--colors-white);
fill: var(--colors-white);
font-weight: 500;
}

.destructive:hover {
border-color: #a10b0b;
background: linear-gradient(180deg, #b81c1c 0%, #b80c0b 100%);
background-color: #ac0f1a;
}

.destructive:active,
.destructive:active:focus {
background: linear-gradient(180deg, #b81c1c 0%, #b80c0b 100%);
background-color: #ac101b;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.18) inset;
}

.destructive:focus {
background: linear-gradient(180deg, #d32f2f 0%, #b71c1c 100%);
background-color: #b72229;
}

.destructive:disabled {
border-color: #c59898;
background: #d6a8a8;
box-shadow: none;
color: var(--colors-white);
fill: var(--colors-white);
}

.destructive.secondary {
border-color: rgba(74, 87, 104, 0.25);
background: transparent;
color: var(--colors-red700);
fill: var(--colors-red700);
font-weight: 400;
}

.destructive.secondary:hover {
border-color: var(--colors-red600);
background: var(--colors-red050);
color: var(--colors-red800);
fill: var(--colors-red800);
}

.destructive.secondary:active,
.destructive.secondary:active:focus {
background: var(--colors-red100);
border-color: var(--colors-red700);
box-shadow: none;
}

.destructive.secondary:disabled {
background: transparent;
border-color: rgba(74, 87, 104, 0.25);
color: rgba(183, 28, 28, 0.6);
fill: rgba(183, 28, 28, 0.6);
}

.icon-only {
padding: 0 0 0 5px;
}

.button-icon {
margin-right: 6px;
color: inherit;
fill: inherit;
font-size: 26px;
vertical-align: middle;
pointer-events: none;
}

.icon-only .button-icon {
margin-right: 5px;
}

.small.icon-only {
padding: 0 4px 0 5px;
}

.small .button-icon {
margin-right: 2px;
}

.small.icon-only .button-icon {
margin-right: 1px;
}

.toggled {
background: var(--colors-grey700);
border: 1px solid var(--colors-grey900);
color: var(--colors-grey050);
fill: var(--colors-grey050);
}

.toggled:focus {
background: var(--colors-grey800);
}

.toggled:hover {
background: var(--colors-grey800);
border-color: var(--colors-grey900);
}

.toggled:active,
.toggled:active:focus {
background: var(--colors-grey900);
border-color: var(--colors-grey900);
}

.toggled:disabled {
background: var(--colors-grey500);
border-color: var(--colors-grey600);
color: var(--colors-grey050);
fill: var(--colors-grey050);
}

.loader {
width: 16px;
height: 16px;
margin-right: 8px;
}

.loader + .button-icon {
display: none;
}

.icon-only .loader {
margin: 0 8px 0 4px;
}
.small.icon-only .loader {
margin: 0 4px;
}
87 changes: 87 additions & 0 deletions src/components/LinkButton/LinkButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { ButtonProps } from '@dhis2/ui'
import cx from 'classnames'
import React, { AnchorHTMLAttributes } from 'react'
import { useLinkClickHandler, useHref } from 'react-router-dom'
import css from './LinkButton.module.css'

type UseLinkClickHandlerParameters = Parameters<typeof useLinkClickHandler>

type LinkClickHandlerOptions = UseLinkClickHandlerParameters[1]

type RelevantButtonProps = Pick<
ButtonProps,
| 'disabled'
| 'className'
| 'primary'
| 'secondary'
| 'small'
| 'toggled'
| 'large'
| 'destructive'
>

type LinkButtonProps = AnchorHTMLAttributes<HTMLAnchorElement> &
LinkClickHandlerOptions &
RelevantButtonProps & {
to: Parameters<typeof useLinkClickHandler>[0]
}

/* Wrapping button with anchor-tags are not valid, style anchor as a UI-button */
export const LinkButton = ({
onClick,
disabled,
className,
primary,
secondary,
small,
toggled,
large,
destructive,
target,
replace,
state,
preventScrollReset,
relative,
to,
href,
...anchorProps
}: LinkButtonProps) => {
const resolvedHref = useHref(to, { relative })
const handleClickInternal = useLinkClickHandler(to, {
replace,
state,
preventScrollReset,
relative,
target,
})

const handleClick = (
event: React.MouseEvent<HTMLAnchorElement, MouseEvent>
) => {
if (onClick) {
onClick(event)
}
if (!event.defaultPrevented) {
handleClickInternal(event)
}
}

const resolvedClassname = cx(css.linkButton, className, {
[css.disabled]: disabled,
[css.primary]: primary,
[css.secondary]: secondary,
[css.destructive]: destructive,
[css.toggled]: toggled,
[css.large]: large,
[css.small]: small,
})
return (
<a
{...anchorProps}
className={resolvedClassname}
href={href || resolvedHref}
onClick={href ? onClick : handleClick}
target={target}
/>
)
}
1 change: 1 addition & 0 deletions src/components/LinkButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './LinkButton'
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
gap: var(--spacers-dp8);
}

.listActions button {
.listActions button,
.listActions a {
padding: 0 2px !important;
}

Expand Down
Loading

0 comments on commit c19814f

Please sign in to comment.