@dialpad/dialtone
Version:
Dialpad's Dialtone design system monorepo
292 lines (291 loc) • 8.25 kB
JavaScript
import { warn, openBlock, createElementBlock, mergeProps, toHandlers, normalizeClass, renderSlot, createCommentVNode } from "vue";
import { hasSlotContent } from "../../common/utils.js";
import { ICON_POSITION_MODIFIERS, BUTTON_IMPORTANCE_MODIFIERS, BUTTON_TYPES, BUTTON_SIZE_MODIFIERS, BUTTON_KIND_MODIFIERS, BUTTON_ICON_SIZES, INVALID_COMBINATION } from "./button_constants.js";
import _export_sfc from "../../_virtual/_plugin-vue_export-helper.js";
import { LINK_KIND_MODIFIERS, getLinkKindModifier } from "../link/link_constants.js";
const _sfc_main = {
compatConfig: { MODE: 3 },
name: "DtButton",
props: {
/**
* Whether the button is a circle or not.
* @values true, false
*/
circle: {
type: Boolean,
default: false
},
/**
* The position of the icon slot within the button.
* @values left, right, top, bottom
*/
iconPosition: {
type: String,
default: "left",
validator: (position) => Object.keys(ICON_POSITION_MODIFIERS).includes(position)
},
/**
* The fill and outline of the button associated with its visual importance.
* @values clear, outlined, primary
*/
importance: {
type: String,
default: "primary",
validator: (i) => Object.keys(BUTTON_IMPORTANCE_MODIFIERS).includes(i)
},
/**
* Whether the button should be styled as a link or not.
* @values true, false
* @see DtLink
*/
link: {
type: Boolean,
default: false
},
/**
* The color of the link and button if the button is styled as a link.
* @values default, warning, danger, success, muted
* @see DtLink
*/
linkKind: {
type: String,
default: "default",
validator: (lk) => Object.keys(LINK_KIND_MODIFIERS).includes(lk)
},
/**
* Determines whether the link should have inverted styling if the button is styled as a link.
* @values true, false
* @see DtLink
*/
linkInverted: {
type: Boolean,
default: false
},
/**
* HTML button disabled attribute
* <a class="d-link" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#disabled" target="_blank"> (Reference) </a>
* @values true, false
*/
disabled: {
type: Boolean,
default: false
},
/**
* HTML button type attribute
* <a
* class="d-link"
* href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type"
* target="_blank"
* >
* (Reference)
* </a>
* @values button, submit, reset
*/
type: {
type: String,
default: "button",
validator: (t) => BUTTON_TYPES.includes(t)
},
/**
* Button width, accepts
* <a class="d-link" href="https://developer.mozilla.org/en-US/docs/Web/CSS/width" target="_blank">
* CSS width attribute
* </a>
* values
*/
width: {
type: String,
default: null
},
/**
* The size of the button.
* @values xs, sm, md, lg, xl
*/
size: {
type: String,
default: "md",
validator: (s) => Object.keys(BUTTON_SIZE_MODIFIERS).includes(s)
},
/**
* Used to customize the label container
*/
labelClass: {
type: [String, Array, Object],
default: ""
},
/**
* Whether the button should display a loading animation or not.
* @values true, false
*/
loading: {
type: Boolean,
default: false
},
/**
* The color of the button.
* @values default, muted, danger, inverted
*/
kind: {
type: String,
default: "default",
validator: (k) => Object.keys(BUTTON_KIND_MODIFIERS).includes(k)
},
/**
* Determines whether a screenreader reads live updates of
* the button content to the user while the button
* is in focus. default is to not.
* @values true, false
*/
assertiveOnFocus: {
type: Boolean,
default: false
},
/**
* Determines whether the button should have active styling
* default is false.
* @values true, false
*/
active: {
type: Boolean,
default: false
}
},
emits: [
/**
* Native button focus in event
*
* @event focusin
* @type {FocusEvent}
*/
"focusin",
/**
* Native button focus out event
*
* @event focusout
* @type {FocusEvent}
*/
"focusout"
],
data() {
return {
ICON_POSITION_MODIFIERS,
// whether the button is currently in focus
isInFocus: false,
hasSlotContent
};
},
computed: {
buttonListeners() {
return {
focusin: (e) => {
this.isInFocus = this.assertiveOnFocus;
this.$emit("focusin", e);
},
focusout: (e) => {
this.isInFocus = false;
this.$emit("focusout", e);
}
};
},
computedAriaLive() {
return this.assertiveOnFocus && this.isInFocus ? "assertive" : this.$attrs.ariaLive;
},
iconSize() {
return BUTTON_ICON_SIZES[this.size];
}
},
watch: {
$props: {
deep: true,
immediate: true,
handler() {
if (process.env.NODE_ENV === "production") return;
if (this.circle && this.link) {
warn("You cannot enable circle and link at the same time", this);
}
this.isInvalidPropCombination(this.circle, this.kind, this.importance);
}
}
},
methods: {
buttonClasses() {
if (this.link) {
return [
"d-link",
getLinkKindModifier(this.linkKind, this.linkInverted),
BUTTON_SIZE_MODIFIERS[this.size]
];
}
return [
"d-btn",
BUTTON_IMPORTANCE_MODIFIERS[this.importance],
BUTTON_KIND_MODIFIERS[this.kind],
BUTTON_SIZE_MODIFIERS[this.size],
{
"d-btn--circle": this.circle,
"d-btn--loading": this.loading,
"d-btn--icon-only": this.isIconOnly(),
"d-btn--vertical": this.isVerticalIconLayout(),
"d-btn--active": this.active
}
];
},
isInvalidPropCombination(circle, kind, importance) {
for (const row of INVALID_COMBINATION) {
if (circle === row.circle && kind === row.kind && importance === row.importance) {
console.warn(row.message);
return false;
}
}
return true;
},
shouldRenderIcon() {
return hasSlotContent(this.$slots.icon) && !this.link;
},
isIconOnly() {
return this.shouldRenderIcon() && !hasSlotContent(this.$slots.default);
},
isVerticalIconLayout() {
return !this.isIconOnly() && ["top", "bottom"].includes(this.iconPosition);
}
}
};
const _hoisted_1 = ["type", "disabled", "aria-live", "aria-label"];
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("button", mergeProps({
class: [
"base-button__button",
$options.buttonClasses()
],
"data-qa": "dt-button",
type: $props.type,
disabled: $props.disabled,
style: { width: $props.width },
"aria-live": $options.computedAriaLive,
"aria-label": $props.loading ? "loading" : _ctx.$attrs["aria-label"]
}, toHandlers($options.buttonListeners, true)), [
$options.shouldRenderIcon() ? (openBlock(), createElementBlock("span", {
key: 0,
"data-qa": "dt-button-icon",
class: normalizeClass([
"base-button__icon",
"d-btn__icon",
$data.ICON_POSITION_MODIFIERS[$props.iconPosition]
])
}, [
renderSlot(_ctx.$slots, "icon", { iconSize: $options.iconSize })
], 2)) : createCommentVNode("", true),
$data.hasSlotContent(_ctx.$slots.default) ? (openBlock(), createElementBlock("span", {
key: 1,
"data-qa": "dt-button-label",
class: normalizeClass(["d-btn__label", "base-button__label", $props.labelClass])
}, [
renderSlot(_ctx.$slots, "default")
], 2)) : createCommentVNode("", true)
], 16, _hoisted_1);
}
const DtButton = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
export {
DtButton as default
};
//# sourceMappingURL=button.vue.js.map