Update Button and implementations

This commit is contained in:
Justin Edmund 2022-12-06 13:16:11 -08:00
parent 3376aa7b75
commit 1e3480e3c0
7 changed files with 284 additions and 208 deletions

View file

@ -1,35 +1,44 @@
.Button {
align-items: center;
background: transparent;
background: var(--button-bg);
border: none;
border-radius: 6px;
color: $grey-55;
border-radius: $input-corner;
color: var(--button-text);
display: inline-flex;
font-size: $font-button;
font-weight: $normal;
gap: 6px;
padding: 8px 12px;
&:hover {
background: $grey-100;
&:hover,
&.Blended:hover {
background: var(--button-bg-hover);
cursor: pointer;
color: $grey-15;
color: var(--button-text-hover);
.icon svg {
fill: $grey-15;
.Accessory svg {
fill: var(--button-text-hover);
}
.icon.stroke svg {
.Accessory svg.stroke {
fill: none;
stroke: $grey-15;
stroke: var(--button-text-hover);
}
}
&.Blended {
background: transparent;
}
&.medium {
height: $unit * 5.5;
padding: ($unit * 1.5) $unit-2x;
}
&.destructive:hover {
background: $error;
color: $grey-100;
.icon svg {
.Accessory svg {
fill: $grey-100;
}
}
@ -37,7 +46,7 @@
&.save:hover {
color: #ff4d4d;
.icon svg {
.Accessory svg {
fill: #ff4d4d;
stroke: #ff4d4d;
}
@ -46,7 +55,7 @@
&.save.Active {
color: #ff4d4d;
.icon svg {
.Accessory svg {
fill: #ff4d4d;
stroke: #ff4d4d;
}
@ -73,13 +82,30 @@
}
}
.icon {
margin-top: 2px;
.Accessory {
$dimension: $unit-2x;
display: flex;
svg {
fill: $grey-55;
height: 12px;
width: 12px;
fill: var(--button-text);
height: $dimension;
width: $dimension;
&.stroke {
fill: none;
stroke: var(--button-text);
}
&.Add {
height: 18px;
width: 18px;
}
&.Check {
height: 22px;
width: 22px;
}
}
&.check svg {
@ -88,21 +114,12 @@
width: auto;
}
&.stroke svg {
fill: none;
stroke: $grey-55;
}
&.settings svg {
svg &.settings svg {
height: 13px;
width: 13px;
}
}
&.Active {
background: $grey-100;
}
&.btn-blue {
background: $blue;
color: #8b8b8b;

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { PropsWithChildren, useEffect, useState } from 'react'
import classNames from 'classnames'
import Link from 'next/link'
@ -15,147 +15,169 @@ import SettingsIcon from '~public/icons/Settings.svg'
import './index.scss'
import { ButtonType } from '~utils/enums'
import { access } from 'fs'
interface Props {
active?: boolean
disabled?: boolean
classes?: string[]
icon?: string
type?: ButtonType
children?: React.ReactNode
onClick?: (event: React.MouseEvent<HTMLElement>) => void
interface Props
extends React.DetailedHTMLProps<
React.ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
> {
accessoryIcon?: React.ReactNode
blended?: boolean
size?: 'small' | 'medium' | 'large'
text?: string
}
const Button = (props: Props) => {
// States
const [active, setActive] = useState(false)
const [disabled, setDisabled] = useState(false)
const [pressed, setPressed] = useState(false)
const [buttonType, setButtonType] = useState(ButtonType.Base)
const defaultProps = {
blended: false,
size: 'medium',
}
const classes = classNames(
{
Button: true,
Active: active,
'btn-pressed': pressed,
'btn-disabled': disabled,
save: props.icon === 'save',
destructive: props.type == ButtonType.Destructive,
},
props.classes
)
const Button = React.forwardRef<HTMLButtonElement, Props>(
({ accessoryIcon, blended, size, text, ...props }, forwardedRef) => {
const classes = classNames(
{
Button: true,
Blended: blended,
// Active: active,
// 'btn-pressed': pressed,
// 'btn-disabled': disabled,
// save: props.icon === 'save',
// destructive: props.type == ButtonType.Destructive,
},
size,
props.className
)
useEffect(() => {
if (props.active) setActive(props.active)
if (props.disabled) setDisabled(props.disabled)
if (props.type) setButtonType(props.type)
}, [props.active, props.disabled, props.type])
const addIcon = (
<span className="icon">
<AddIcon />
</span>
)
const menuIcon = (
<span className="icon">
<MenuIcon />
</span>
)
const linkIcon = (
<span className="icon stroke">
<LinkIcon />
</span>
)
const checkIcon = (
<span className="icon check">
<CheckIcon />
</span>
)
const crossIcon = (
<span className="icon">
<CrossIcon />
</span>
)
const editIcon = (
<span className="icon">
<EditIcon />
</span>
)
const saveIcon = (
<span className="icon stroke">
<SaveIcon />
</span>
)
const settingsIcon = (
<span className="icon settings">
<SettingsIcon />
</span>
)
function getIcon() {
let icon: React.ReactNode
switch (props.icon) {
case 'new':
icon = addIcon
break
case 'menu':
icon = menuIcon
break
case 'link':
icon = linkIcon
break
case 'check':
icon = checkIcon
break
case 'cross':
icon = crossIcon
break
case 'edit':
icon = editIcon
break
case 'save':
icon = saveIcon
break
case 'settings':
icon = settingsIcon
break
const hasAccessory = () => {
if (accessoryIcon)
return <span className="Accessory">{accessoryIcon}</span>
}
return icon
}
const hasText = () => {
if (text) return <span className="Text">{text}</span>
}
function handleMouseDown() {
setPressed(true)
}
return (
<button {...props} className={classes} ref={forwardedRef}>
{hasAccessory()}
{hasText()}
</button>
)
function handleMouseUp() {
setPressed(false)
}
return (
<button
className={classes}
disabled={disabled}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onClick={props.onClick}
>
{getIcon()}
// useEffect(() => {
// if (props.type) setButtonType(props.type)
// }, [props.type])
{props.type != ButtonType.IconOnly ? (
<span className="text">{props.children}</span>
) : (
''
)}
</button>
)
}
// const addIcon = (
// <span className="icon">
// <AddIcon />
// </span>
// )
// const menuIcon = (
// <span className="icon">
// <MenuIcon />
// </span>
// )
// const linkIcon = (
// <span className="icon stroke">
// <LinkIcon />
// </span>
// )
// const checkIcon = (
// <span className="icon check">
// <CheckIcon />
// </span>
// )
// const crossIcon = (
// <span className="icon">
// <CrossIcon />
// </span>
// )
// const editIcon = (
// <span className="icon">
// <EditIcon />
// </span>
// )
// const saveIcon = (
// <span className="icon stroke">
// <SaveIcon />
// </span>
// )
// const settingsIcon = (
// <span className="icon settings">
// <SettingsIcon />
// </span>
// )
// function getIcon() {
// let icon: React.ReactNode
// switch (props.icon) {
// case 'new':
// icon = addIcon
// break
// case 'menu':
// icon = menuIcon
// break
// case 'link':
// icon = linkIcon
// break
// case 'check':
// icon = checkIcon
// break
// case 'cross':
// icon = crossIcon
// break
// case 'edit':
// icon = editIcon
// break
// case 'save':
// icon = saveIcon
// break
// case 'settings':
// icon = settingsIcon
// break
// }
// return icon
// }
// function handleMouseDown() {
// setPressed(true)
// }
// function handleMouseUp() {
// setPressed(false)
// }
// return (
// <button
// className={classes}
// disabled={disabled}
// onMouseDown={handleMouseDown}
// onMouseUp={handleMouseUp}
// ref={forwardedRef}
// {...props}
// >
// {getIcon()}
// {props.type != ButtonType.IconOnly ? (
// <span className="text">{children}</span>
// ) : (
// ''
// )}
// </button>
// )
}
)
Button.defaultProps = defaultProps
export default Button

View file

@ -1,6 +1,6 @@
.Header {
display: flex;
height: 34px;
margin-bottom: $unit-2x;
width: 100%;
&.bottom {
@ -19,10 +19,10 @@
&:hover {
padding-right: 50px;
padding-bottom: 16px;
.Button {
background: $grey-100;
background: var(--button-bg-hover);
color: var(--button-text-hover);
}
.Menu {

View file

@ -1,24 +1,25 @@
.Menu {
background: $grey-100;
background: var(--menu-bg);
border-radius: 6px;
display: none;
min-width: 220px;
position: absolute;
top: $unit * 5; // This shouldn't be hardcoded. How to calculate it?
// Also, add space that doesn't make the menu disappear if you move your mouse slowly
z-index: 10;
}
.MenuItem {
color: $grey-50;
color: var(--text-secondary);
font-weight: $normal;
&:hover:not(.disabled) {
background: $grey-95;
color: $grey-15;
background: var(--menu-bg-item-hover);
color: var(--text-primary);
cursor: pointer;
a {
color: $grey-15;
color: var(--text-primary);
}
}
@ -112,8 +113,8 @@
&:hover {
i.tag {
background: $grey-60;
color: $grey-100;
background: var(--tag-bg);
color: var(--tag-text);
}
}
@ -131,7 +132,7 @@
}
.MenuGroup {
border-bottom: 1px solid #f5f5f5;
border-bottom: 1px solid var(--menu-separator);
&:first-child .MenuItem:first-child:hover {
border-top-left-radius: 6px;

View file

@ -88,6 +88,10 @@
h1 {
color: var(--text-primary);
&.empty {
color: var(--text-tertiary);
}
}
}
}

View file

@ -8,7 +8,6 @@ import Linkify from 'react-linkify'
import classNames from 'classnames'
import * as AlertDialog from '@radix-ui/react-alert-dialog'
import CrossIcon from '~public/icons/Cross.svg'
import Button from '~components/Button'
import CharLimitedFieldset from '~components/CharLimitedFieldset'
@ -18,6 +17,10 @@ import TextFieldset from '~components/TextFieldset'
import { accountState } from '~utils/accountState'
import { appState } from '~utils/appState'
import CheckIcon from '~public/icons/Check.svg'
import CrossIcon from '~public/icons/Cross.svg'
import EditIcon from '~public/icons/Edit.svg'
import './index.scss'
import Link from 'next/link'
import { formatTimeAgo } from '~utils/timeAgo'
@ -169,11 +172,11 @@ const PartyDetails = (props: Props) => {
if (party.editable) {
return (
<AlertDialog.Root>
<AlertDialog.Trigger className="Button destructive">
<span className="icon">
<AlertDialog.Trigger className="Button Blended medium destructive">
<span className="Accessory">
<CrossIcon />
</span>
<span className="text">{t('buttons.delete')}</span>
<span className="Text">{t('buttons.delete')}</span>
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Overlay className="Overlay" />
@ -236,13 +239,12 @@ const PartyDetails = (props: Props) => {
{router.pathname !== '/new' ? deleteButton() : ''}
</div>
<div className="right">
<Button active={true} onClick={toggleDetails}>
{t('buttons.cancel')}
</Button>
<Button active={true} icon="check" onClick={updateDetails}>
{t('buttons.save_info')}
</Button>
<Button text={t('buttons.cancel')} onClick={toggleDetails} />
<Button
accessoryIcon={<CheckIcon className="Check" />}
text={t('buttons.save_info')}
onClick={updateDetails}
/>
</div>
</div>
</section>
@ -252,7 +254,9 @@ const PartyDetails = (props: Props) => {
<section className={readOnlyClasses}>
<div className="info">
<div className="left">
{party.name ? <h1>{party.name}</h1> : ''}
<h1 className={!party.name ? 'empty' : ''}>
{party.name ? party.name : 'Untitled'}
</h1>
<div className="attribution">
{party.user ? linkedUserBlock(party.user) : userBlock()}
{party.raid ? linkedRaidBlock(party.raid) : ''}
@ -270,9 +274,11 @@ const PartyDetails = (props: Props) => {
</div>
<div className="right">
{party.editable ? (
<Button active={true} icon="edit" onClick={toggleDetails}>
{t('buttons.show_info')}
</Button>
<Button
accessoryIcon={<EditIcon />}
text={t('buttons.show_info')}
onClick={toggleDetails}
/>
) : (
<div />
)}
@ -291,9 +297,11 @@ const PartyDetails = (props: Props) => {
const emptyDetails = (
<div className={emptyClasses}>
{party.editable ? (
<Button active={true} icon="edit" onClick={toggleDetails}>
{t('buttons.show_info')}
</Button>
<Button
accessoryIcon={<EditIcon />}
text={t('buttons.show_info')}
onClick={toggleDetails}
/>
) : (
<div />
)}

View file

@ -14,6 +14,11 @@ import Header from '~components/Header'
import Button from '~components/Button'
import HeaderMenu from '~components/HeaderMenu'
import AddIcon from '~public/icons/Add.svg'
import LinkIcon from '~public/icons/Link.svg'
import MenuIcon from '~public/icons/Menu.svg'
import SaveIcon from '~public/icons/Save.svg'
const TopHeader = () => {
const { t } = useTranslation('common')
@ -94,10 +99,26 @@ const TopHeader = () => {
else console.error('Failed to unsave team: No party ID')
}
const copyButton = () => {
if (router.route === '/p/[party]')
return (
<Button
accessoryIcon={<LinkIcon className="stroke" />}
blended={true}
text={t('buttons.copy')}
onClick={copyToClipboard}
/>
)
}
const leftNav = () => {
return (
<div className="dropdown">
<Button icon="menu">{t('buttons.menu')}</Button>
<Button
accessoryIcon={<MenuIcon />}
blended={true}
text={t('buttons.menu')}
/>
{account.user ? (
<HeaderMenu
authenticated={account.authorized}
@ -114,15 +135,19 @@ const TopHeader = () => {
const saveButton = () => {
if (party.favorited)
return (
<Button icon="save" active={true} onClick={toggleFavorite}>
Saved
</Button>
<Button
accessoryIcon={<SaveIcon />}
text="Saved"
onClick={toggleFavorite}
/>
)
else
return (
<Button icon="save" onClick={toggleFavorite}>
Save
</Button>
<Button
accessoryIcon={<SaveIcon />}
text="Save"
onClick={toggleFavorite}
/>
)
}
@ -134,16 +159,15 @@ const TopHeader = () => {
(!party.user || party.user.id !== account.user.id)
? saveButton()
: ''}
{router.route === '/p/[party]' ? (
<Button icon="link" onClick={copyToClipboard}>
{t('buttons.copy')}
</Button>
) : (
''
)}
<Button icon="new" onClick={newParty}>
{t('buttons.new')}
</Button>
{copyButton()}
<Button
accessoryIcon={<AddIcon className="Add" />}
blended={true}
text={t('buttons.new')}
onClick={newParty}
/>
</div>
)
}