@duetds/components
Version:
This package includes Duet Core Components and related tools.
265 lines (264 loc) • 10.1 kB
JavaScript
import { Build, Host, h } from "@stencil/core";
export class DuetLayout {
constructor() {
/**
* Own Properties.
*/
this.hasSidebar = false;
this.hasTop = false;
this.ua = "";
this.layoutHeight = 0;
this.sidebarHeight = 0;
this.isTicking = false;
this.lastPosition = -1;
/**
* Makes the sidebar stick to the top of the window when scrolling down.
*/
this.sticky = false;
/**
* Controls the margin of the component. Can be one of: "auto", "none".
*/
this.margin = "auto";
/**
* Adjust the distance from top of the viewport (in pixels) when the sidebar becomes sticky.
*/
this.stickyTop = 540;
/**
* Adjust sticky sidebar’s distance to duet navigation component using this property. Can be one of: "with-links", "without-links".
*/
this.stickyDistance = "with-links";
/**
* Center align all direct childs of this component.
*/
this.center = false;
/**
* Align container vertically in the middle when the space allows it.
*/
this.middle = false;
/**
* Local methods
*/
this.getFrame = () => {
if (!this.isTicking) {
requestAnimationFrame(this.animationLoop);
this.isTicking = true;
}
};
this.animationLoop = () => {
// Avoid calculations if not needed
const pos = window.pageYOffset;
if (this.lastPosition === pos) {
this.isTicking = false;
}
else {
this.lastPosition = pos;
}
if (this.sidebarHeight === 0) {
this.sidebarHeight = this.sidebar.offsetHeight;
}
const scrollHeight = this.layoutHeight + this.stickyTop - this.sidebarHeight - 20;
// If we’ve scrolled past the trigger point, but haven’t reached the bottom of container
if (pos > this.stickyTop && pos < scrollHeight) {
this.sidebar.classList.add("sticky");
this.sidebar.classList.remove("sticky-stopped");
// If we’ve scrolled past the trigger point AND have reached the bottom of container
}
else if (pos > this.stickyTop && pos > scrollHeight) {
this.sidebar.classList.add("sticky-stopped");
// When scrolling back to top, remove all sticky classes
}
else {
this.sidebar.classList.remove("sticky");
this.sidebar.classList.remove("sticky-stopped");
}
this.isTicking = false;
};
}
/**
* Component lifecycle events.
*/
componentWillLoad() {
this.hasSidebar = !!this.element.querySelector("[slot='sidebar']");
this.hasTop = !!this.element.querySelector("[slot='top']");
if (!this.element.querySelector("[slot='main']")) {
console.warn("[DUET WARNING]: Nothing passed to <duet-layout> named slots. Please use one of 'main', 'sidebar' or 'top' to display contents.");
}
}
componentDidLoad() {
if (Build.isBrowser) {
this.sidebar = this.element.shadowRoot.querySelector(".duet-sidebar-container");
}
this.layoutHeight = this.element.offsetHeight;
this.ua = navigator.userAgent || navigator.vendor;
if (this.sticky) {
window.addEventListener("scroll", this.getFrame, false);
}
}
componentDidUnload() {
if (this.sticky) {
window.removeEventListener("scroll", this.getFrame);
}
}
/**
* render() function.
* Always the last one in the class.
*/
render() {
return (h(Host, { class: { "duet-middle": this.middle, "duet-ie": document["documentMode"] || /Edge/.test(this.ua) } },
this.hasTop ? (h("div", { class: { "duet-layout-top": true, "has-sidebar": this.hasSidebar, "duet-center": this.center } },
h("div", { class: "duet-layout-top-wrapper" },
h("div", { class: "duet-layout-top-margin" },
h("slot", { name: "top" }))))) : (""),
h("div", { class: {
"duet-layout": true,
"has-sidebar": this.hasSidebar,
"duet-m-0": this.margin === "none",
"duet-no-nav": this.navDistance === "none",
"duet-with-links": this.navDistance === "with-links",
"duet-without-links": this.navDistance === "without-links",
"duet-center": this.center,
"sticky-without-links": this.stickyDistance === "without-links",
} },
h("main", { class: "duet-main" },
h("slot", { name: "main" })),
this.hasSidebar ? (h("aside", { class: "duet-sidebar" },
h("div", { class: "duet-sidebar-container" },
h("slot", { name: "sidebar" })))) : (""))));
}
static get is() { return "duet-layout"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() { return {
"$": ["duet-layout.scss"]
}; }
static get styleUrls() { return {
"$": ["duet-layout.css"]
}; }
static get properties() { return {
"sticky": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Makes the sidebar stick to the top of the window when scrolling down."
},
"attribute": "sticky",
"reflect": false,
"defaultValue": "false"
},
"margin": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Controls the margin of the component. Can be one of: \"auto\", \"none\"."
},
"attribute": "margin",
"reflect": false,
"defaultValue": "\"auto\""
},
"stickyTop": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Adjust the distance from top of the viewport (in pixels) when the sidebar becomes sticky."
},
"attribute": "sticky-top",
"reflect": false,
"defaultValue": "540"
},
"stickyDistance": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Adjust sticky sidebar\u2019s distance to duet navigation component using this property. Can be one of: \"with-links\", \"without-links\"."
},
"attribute": "sticky-distance",
"reflect": false,
"defaultValue": "\"with-links\""
},
"navDistance": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Adjust layout\u2019s distance to duet navigation component using this property. Useful if the page you\u2019re working on doesn\u2019t use the hero component. Basically adds top margin that matches the sizing of Duet Navigation. Can be one of: \"none\", \"with-links\", \"without-links\"."
},
"attribute": "nav-distance",
"reflect": false
},
"center": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Center align all direct childs of this component."
},
"attribute": "center",
"reflect": false,
"defaultValue": "false"
},
"middle": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Align container vertically in the middle when the space allows it."
},
"attribute": "middle",
"reflect": false,
"defaultValue": "false"
}
}; }
static get elementRef() { return "element"; }
}