2019-11-09 22:25:26 +00:00
|
|
|
<!-- Toggle buttons should have an immutable label, e.g. a mute/unmute button should just always say
|
|
|
|
"mute." For sighted users, though, I think it's nice if the title changes when the action changes.
|
2019-11-18 02:23:32 +00:00
|
|
|
See http://w3c.github.io/aria-practices/#button
|
|
|
|
We also have toggleButton === false which can make it just a normal button that changes its aria-label.
|
|
|
|
-->
|
2019-02-23 20:32:00 +00:00
|
|
|
<button type="button"
|
2019-11-18 02:23:32 +00:00
|
|
|
{title}
|
|
|
|
aria-label={ariaLabel}
|
|
|
|
aria-pressed={ariaPressed}
|
2019-11-18 01:51:44 +00:00
|
|
|
aria-hidden={ariaHidden ? 'true' : undefined}
|
2019-09-22 01:16:55 +00:00
|
|
|
tabindex="{ariaHidden ? '-1' : '0'}"
|
2019-02-23 20:32:00 +00:00
|
|
|
class={computedClass}
|
|
|
|
{disabled}
|
|
|
|
ref:node
|
|
|
|
>
|
2019-03-03 03:02:06 +00:00
|
|
|
<SvgIcon className="icon-button-svg {svgClassName || ''}" ref:svg {href} />
|
2019-02-23 20:32:00 +00:00
|
|
|
</button>
|
2018-01-28 20:51:48 +00:00
|
|
|
<style>
|
2018-03-16 15:42:10 +00:00
|
|
|
.icon-button {
|
2018-01-28 20:51:48 +00:00
|
|
|
padding: 6px 10px;
|
|
|
|
background: none;
|
|
|
|
border: none;
|
2018-04-05 04:45:19 +00:00
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
2018-01-28 20:51:48 +00:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button-svg) {
|
2018-01-28 20:51:48 +00:00
|
|
|
width: 24px;
|
|
|
|
height: 24px;
|
|
|
|
fill: var(--action-button-fill-color);
|
2018-03-23 03:09:20 +00:00
|
|
|
pointer-events: none; /* hack for Edge */
|
2018-01-28 20:51:48 +00:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.big-icon .icon-button-svg) {
|
2018-01-28 20:51:48 +00:00
|
|
|
width: 32px;
|
|
|
|
height: 32px;
|
|
|
|
}
|
|
|
|
|
2018-04-05 04:45:19 +00:00
|
|
|
/*
|
|
|
|
* regular styles
|
|
|
|
*/
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button:hover .icon-button-svg) {
|
2018-01-28 20:51:48 +00:00
|
|
|
fill: var(--action-button-fill-color-hover);
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.not-pressable:active .icon-button-svg,
|
|
|
|
.icon-button.same-pressed:active .icon-button-svg) {
|
2018-01-28 20:51:48 +00:00
|
|
|
fill: var(--action-button-fill-color-active);
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.pressed.not-same-pressed .icon-button-svg) {
|
2018-03-21 16:38:20 +00:00
|
|
|
fill: var(--action-button-fill-color-pressed);
|
2018-01-28 20:51:48 +00:00
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.pressed.not-same-pressed:hover .icon-button-svg) {
|
2018-01-28 20:51:48 +00:00
|
|
|
fill: var(--action-button-fill-color-pressed-hover);
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.pressed.not-same-pressed:active .icon-button-svg) {
|
2018-01-28 20:51:48 +00:00
|
|
|
fill: var(--action-button-fill-color-pressed-active);
|
|
|
|
}
|
2018-04-05 04:45:19 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* muted
|
|
|
|
*/
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.muted-style .icon-button-svg) {
|
2018-04-05 04:45:19 +00:00
|
|
|
fill: var(--action-button-deemphasized-fill-color);
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.muted-style:hover .icon-button-svg) {
|
2018-04-05 04:45:19 +00:00
|
|
|
fill: var(--action-button-deemphasized-fill-color-hover);
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.muted-style.not-pressable:active .icon-button-svg,
|
|
|
|
.icon-button.muted-style.same-pressed:active .icon-button-svg) {
|
2018-04-05 04:45:19 +00:00
|
|
|
fill: var(--action-button-deemphasized-fill-color-active);
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.muted-style.pressed.not-same-pressed .icon-button-svg) {
|
2018-04-05 04:45:19 +00:00
|
|
|
fill: var(--action-button-deemphasized-fill-color-pressed);
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.muted-style.pressed.not-same-pressed:hover .icon-button-svg) {
|
2018-04-05 04:45:19 +00:00
|
|
|
fill: var(--action-button-deemphasized-fill-color-pressed-hover);
|
|
|
|
}
|
|
|
|
|
2019-03-03 03:02:06 +00:00
|
|
|
:global(.icon-button.muted-style.pressed.not-same-pressed:active .icon-button-svg) {
|
2018-04-05 04:45:19 +00:00
|
|
|
fill: var(--action-button-deemphasized-fill-color-pressed-active);
|
|
|
|
}
|
|
|
|
|
2018-02-24 22:49:28 +00:00
|
|
|
</style>
|
|
|
|
<script>
|
2018-03-15 01:52:33 +00:00
|
|
|
import { classname } from '../_utils/classname'
|
2018-03-23 03:23:00 +00:00
|
|
|
import { store } from '../_store/store'
|
2019-03-03 03:02:06 +00:00
|
|
|
import SvgIcon from './SvgIcon.html'
|
2018-03-15 01:52:33 +00:00
|
|
|
|
2018-02-24 22:49:28 +00:00
|
|
|
export default {
|
2019-02-23 20:32:00 +00:00
|
|
|
oncreate () {
|
2019-08-03 20:49:37 +00:00
|
|
|
const { clickListener, elementId } = this.get()
|
2019-02-23 20:32:00 +00:00
|
|
|
if (clickListener) {
|
|
|
|
this.onClick = this.onClick.bind(this)
|
|
|
|
this.refs.node.addEventListener('click', this.onClick)
|
|
|
|
}
|
|
|
|
if (elementId) {
|
|
|
|
this.refs.node.setAttribute('id', elementId)
|
|
|
|
}
|
2019-11-09 22:25:26 +00:00
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
|
|
const { pressable, pressedLabel, label } = this.get()
|
|
|
|
if (pressable && ((!pressedLabel || !label) || pressedLabel === label)) {
|
|
|
|
throw new Error('pressable buttons should have a label and a pressedLabel different from each other')
|
|
|
|
}
|
|
|
|
}
|
2019-02-23 20:32:00 +00:00
|
|
|
},
|
|
|
|
ondestroy () {
|
2019-08-03 20:49:37 +00:00
|
|
|
const { clickListener } = this.get()
|
2019-02-23 20:32:00 +00:00
|
|
|
if (clickListener) {
|
|
|
|
this.refs.node.removeEventListener('click', this.onClick)
|
|
|
|
}
|
|
|
|
},
|
2018-04-30 05:13:41 +00:00
|
|
|
data: () => ({
|
|
|
|
big: false,
|
|
|
|
muted: false,
|
|
|
|
disabled: false,
|
2019-08-20 02:08:59 +00:00
|
|
|
svgClassName: undefined,
|
|
|
|
elementId: undefined,
|
2018-04-30 05:13:41 +00:00
|
|
|
pressable: false,
|
|
|
|
pressed: false,
|
2019-11-09 22:25:26 +00:00
|
|
|
pressedLabel: undefined,
|
2019-08-20 02:08:59 +00:00
|
|
|
className: undefined,
|
2019-02-10 03:05:59 +00:00
|
|
|
sameColorWhenPressed: false,
|
2019-02-23 20:32:00 +00:00
|
|
|
ariaHidden: false,
|
2019-11-18 02:23:32 +00:00
|
|
|
clickListener: true,
|
|
|
|
toggleButton: true // whether or not to actually present it as a toggle button to screen readers (when pressable)
|
2018-04-30 05:13:41 +00:00
|
|
|
}),
|
2018-03-23 03:23:00 +00:00
|
|
|
store: () => store,
|
2018-02-24 22:49:28 +00:00
|
|
|
computed: {
|
2019-11-18 02:23:32 +00:00
|
|
|
computedClass: ({ pressable, pressed, big, muted, sameColorWhenPressed, className }) => (classname(
|
|
|
|
'icon-button',
|
|
|
|
!pressable && 'not-pressable',
|
|
|
|
pressed && 'pressed',
|
|
|
|
big && 'big-icon',
|
|
|
|
muted && 'muted-style',
|
|
|
|
sameColorWhenPressed ? 'same-pressed' : 'not-same-pressed',
|
|
|
|
className
|
|
|
|
)),
|
|
|
|
title: ({ pressable, pressed, pressedLabel, label }) => pressable ? (pressed ? pressedLabel : label) : label,
|
|
|
|
ariaLabel: ({ toggleButton, pressable, pressed, label, pressedLabel }) => {
|
|
|
|
if (!pressable || toggleButton) {
|
|
|
|
return label // per Aria Practices, toggleButtons never change their label
|
|
|
|
} else {
|
|
|
|
return pressed ? pressedLabel : label
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ariaPressed: ({ toggleButton, pressable, pressed }) => (toggleButton && pressable) ? !!pressed : undefined
|
2018-04-21 15:32:40 +00:00
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
animate (animation) {
|
2019-03-03 03:02:06 +00:00
|
|
|
this.refs.svg.animate(animation)
|
2019-02-23 20:32:00 +00:00
|
|
|
},
|
|
|
|
onClick (e) {
|
|
|
|
this.fire('click', e)
|
2018-04-21 15:32:40 +00:00
|
|
|
}
|
2019-03-03 03:02:06 +00:00
|
|
|
},
|
|
|
|
components: {
|
|
|
|
SvgIcon
|
2018-02-24 22:49:28 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-05 06:31:46 +00:00
|
|
|
</script>
|