@postnord/web-components
Version:
PostNord Web Components
276 lines (275 loc) • 10.6 kB
JavaScript
/*!
* Built with Stencil
* By PostNord.
*/
import { h, Host } from "@stencil/core";
import { uuidv4 } from "../../../index";
/**
* The open/closing of the dropdown is handled by the component itself.
*
* @nativeClick Use the `click` event to listen when the button is clicked.
*/
export class PnButtonDropdown {
id = `pn-button-${uuidv4()}`;
focusableElements;
container;
dropdownButtonId = `${this.id}-dropdown`;
dropdownContentId = `${this.id}-content`;
hostElement;
right = false;
top = false;
/** The required label on the button. */
label;
/** The optional SVG content of the icon you want. */
icon;
/** Select between `light` and `warning`. */
appearance = '';
/** Select between `outlined` and `borderless`. */
variant = '';
/** Smaller button. */
small = false;
/** Tooltip (required for Icon Only). */
tooltip;
/** Open/close the dropdown without user interaction. */
open = false;
openHandler() {
this.toggleDropdown();
if (this.open) {
document.addEventListener('click', this.globalHandler);
document.addEventListener('keyup', this.globalHandler);
}
else {
document.removeEventListener('click', this.globalHandler);
document.removeEventListener('keyup', this.globalHandler);
}
}
componentDidLoad() {
if (this.open)
this.openHandler();
}
getRect(element) {
return element.getBoundingClientRect();
}
globalHandler = (e) => {
// global events that we want to track to close the select, like "click outside" or tab out
if (!this.hostElement.contains(e.target) || e?.key === 'Escape') {
this.open = false;
}
};
toggleDropdown() {
this.right = this.hostElement.offsetLeft > window.innerWidth / 2;
requestAnimationFrame(() => {
if (this.open) {
this.container.style.transition = 'none';
this.container.style.transform = 'none';
}
const containerRect = this.getRect(this.container);
const hostElementRect = this.getRect(this.hostElement);
// Calculate if dropdown should open upwards
// if there is enough space above button
const enoughSpaceAbove = hostElementRect.top > containerRect.height + window.innerWidth * 0.025;
this.top =
enoughSpaceAbove &&
hostElementRect.top + hostElementRect.height + containerRect.height >
window.innerHeight - window.innerWidth * 0.025;
let xAxis = 0;
if (containerRect.right > window.innerWidth || containerRect.x < 0) {
// 0.025 because the max width of the element is 95vw and we want the same margin on both sides
xAxis = containerRect.x + containerRect.width + window.innerWidth * 0.025 - window.innerWidth;
}
this.container.style.transform = '';
requestAnimationFrame(() => {
this.container.style.transition = '';
this.container.style.transform = this.open ? `scale(1) translateX(-${xAxis}px)` : '';
requestAnimationFrame(() => {
this.hostElement.querySelector('.pn-button-dropdown').dataset.open = `${this.open}`;
});
});
});
}
toggle = () => {
this.open = !this.open;
};
render() {
return (h(Host, { key: '0b6deb752a9f01bea9571627205b5cd3efbc1c66' }, h("div", { key: 'c7833d539d682a980bb7cc529f17d9942ffd7eb2', class: "pn-button-dropdown" }, h("pn-button", { key: '42d252b99f98b0c3f51795c894e899c6489839c2', label: this.label, buttonId: this.dropdownButtonId, class: "pn-button-dropdown-label", appearance: this.appearance, variant: this.variant, small: this.small, icon: this.icon, iconOnly: !!this.tooltip && !!this.icon && !this.label, ariacontrols: this.dropdownContentId, ariahaspopup: "true", ariaexpanded: this.open.toString(), tooltip: this.tooltip, tooltipUp: !this.top, onPnClick: this.toggle }), h("div", { key: 'dd494c992ef2b51f31ed5c48d87fb3044ed40009', ref: el => (this.container = el), id: this.dropdownContentId, class: "pn-button-dropdown-container", role: "region", "aria-labelledby": this.dropdownButtonId, "data-right": this.right, "data-top": this.top }, h("div", { key: 'e217ae843fa641335ce0714272f68f9a080afe7d', class: "pn-button-dropdown-content" }, h("slot", { key: 'f52ad763a52e9ee17d9a95a1e3a959d61fae2fe1' }))))));
}
static get is() { return "pn-button-dropdown"; }
static get originalStyleUrls() {
return {
"$": ["pn-button-dropdown.scss"]
};
}
static get styleUrls() {
return {
"$": ["pn-button-dropdown.css"]
};
}
static get properties() {
return {
"label": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": true,
"optional": false,
"docs": {
"tags": [],
"text": "The required label on the button."
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "label"
},
"icon": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The optional SVG content of the icon you want."
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "icon"
},
"appearance": {
"type": "string",
"mutable": false,
"complexType": {
"original": "PnButtonAppearance",
"resolved": "\"\" | \"light\" | \"warning\"",
"references": {
"PnButtonAppearance": {
"location": "import",
"path": "@/index",
"id": "src/index.ts::PnButtonAppearance",
"referenceLocation": "PnButtonAppearance"
}
}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Select between `light` and `warning`."
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "appearance",
"defaultValue": "''"
},
"variant": {
"type": "string",
"mutable": false,
"complexType": {
"original": "PnButtonVariant",
"resolved": "\"\" | \"borderless\" | \"outlined\"",
"references": {
"PnButtonVariant": {
"location": "import",
"path": "@/index",
"id": "src/index.ts::PnButtonVariant",
"referenceLocation": "PnButtonVariant"
}
}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Select between `outlined` and `borderless`."
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "variant",
"defaultValue": "''"
},
"small": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Smaller button."
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "small",
"defaultValue": "false"
},
"tooltip": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Tooltip (required for Icon Only)."
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "tooltip"
},
"open": {
"type": "boolean",
"mutable": true,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Open/close the dropdown without user interaction."
},
"getter": false,
"setter": false,
"reflect": true,
"attribute": "open",
"defaultValue": "false"
}
};
}
static get states() {
return {
"right": {},
"top": {}
};
}
static get elementRef() { return "hostElement"; }
static get watchers() {
return [{
"propName": "open",
"methodName": "openHandler"
}];
}
}