import type { VNode } from 'vue'; import type { _ButtonArgsBase, ButtonLayout, ButtonSize } from './button-layout'; import { Tooltip } from 'bootstrap'; import { Prop, toNative } from 'vue-facing-decorator'; import TsxComponent, { Component } from '../../app/vuetsx'; import { isNullOrEmpty } from '../../common/utils/is-null-or-empty'; interface ButtonArgs extends _ButtonArgsBase { href?: string; hrefTarget?: '_blank' | '_self' | '_parent' | '_top'; /** Render the href variant as a (SPA navigation, no full reload) instead of a plain . Default false. */ isRouterLink?: boolean; clicked?: (e: any) => void; } @Component class ButtonComponent extends TsxComponent implements ButtonArgs { @Prop() cssClass!: string; @Prop() layout!: ButtonLayout; @Prop() text!: string | VNode; @Prop() tooltip!: string; @Prop() size!: ButtonSize; @Prop() round!: boolean; @Prop() outlined!: boolean; @Prop() dismissModal!: boolean; @Prop() icon!: string; @Prop() iconButton!: boolean; @Prop() iconOnRight!: boolean; @Prop() clicked: (e: any) => void; @Prop() disabled!: boolean; @Prop() fullWidth!: boolean; @Prop() pulsate!: boolean; @Prop() ariaLabel!: string; @Prop() href!: string; @Prop() hrefTarget!: '_blank' | '_self' | '_parent' | '_top'; @Prop() isRouterLink!: boolean; // When update, update Button & DropdownButton :/ getCssClass(): string { return ( this.getParsedLayout() + (this.size != null ? ` ${this.size}` : '') + (this.outlined ? ' btn-simple' : '') + (this.round ? ' btn-round' : '') + (this.iconButton ? ' btn-icon' : '') + (this.cssClass != null ? ` ${this.cssClass}` : '') + (this.fullWidth ? ' full-width' : '') + (this.pulsate ? ' btn-pulsating' : '') + (this.iconOnRight ? ' btn-icon-on-right' : '') ); } getParsedLayout() { if (!this.outlined) { return this.layout || 'btn btn-primary'; } else { return (this.layout || 'btn btn-primary').replace('btn btn-', 'btn btn-outline-'); } } handleButtonClicked(e) { // Optional since the href/router-link variants are pure navigation. this.clicked?.(e); if (!isNullOrEmpty(this.tooltip)) { document.querySelector('body > .tooltip')?.remove(); } // Blur after a POINTER click only (clears the lingering focus ring). For // keyboard activation (Enter/Space → click with detail === 0) we must NOT // blur, or focus drops to and keyboard navigation breaks — e.g. // repeatedly pressing a +/- counter in the occupancy picker (WCAG 2.4.3). if (e?.detail !== 0) { (this.$el as HTMLElement)?.blur?.(); } } mounted() { if (!isNullOrEmpty(this.tooltip)) { // eslint-disable-next-line no-new new Tooltip(this.$el); } } render(h) { if (this.href == null) { return ( ); } else if (this.isRouterLink == true) { return ( // disabled must be null (not false) when enabled: router-link passes it through as a fallthrough // attr, which stringifies false to disabled="false" — and CSS [disabled] matches by presence. {this.icon && this.iconOnRight != true && ( {this.text &&   } )} {this.text} {this.icon && this.iconOnRight == true && ( {this.text &&   } )} ); } else { return ( {this.icon && this.iconOnRight != true && ( {this.text &&   } )} {this.text} {this.icon && this.iconOnRight == true && ( {this.text &&   } )} ); } } } const Button = toNative(ButtonComponent); export default Button;