Update Button and implementations
This commit is contained in:
parent
3376aa7b75
commit
1e3480e3c0
7 changed files with 284 additions and 208 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -88,6 +88,10 @@
|
|||
|
||||
h1 {
|
||||
color: var(--text-primary);
|
||||
|
||||
&.empty {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue