diff --git a/components/song/song.component.js b/components/song/song.component.js new file mode 100644 index 0000000..7304791 --- /dev/null +++ b/components/song/song.component.js @@ -0,0 +1,156 @@ +console.log("song.component.js running"); + +const initSongComponent = () => { + // CSS + const ComponentCSS = () => { + const styleTag = document.createElement("style"); + const ComponentCSS = ` + .dm-sp-dd-lst { + display:block; + } + .hide { + display: none; + } + `; + + styleTag.innerHTML = ComponentCSS; + + return styleTag; + }; + document.head.appendChild(ComponentCSS()); + + // Utils + const handleClickOutsideElement = (element, onClickOutside) => { + // source (2021-07-09): https://stackoverflow.com/questions/152975/how-do-i-detect-a-click-outside-an-element + const outsideClickListener = (event) => { + if (!element.contains(event.target) && isVisible(element)) { + // or use: event.target.closest(selector) === null + if (onClickOutside) onClickOutside(); + removeClickListener(); + } + }; + + const removeClickListener = () => { + document.removeEventListener("click", outsideClickListener); + }; + + document.addEventListener("click", outsideClickListener); + }; + + const isVisible = (elem) => + !!elem && + !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length); // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js + + /** + * Method for creating element of + * @param {string} type + * @param {...string} classes + * @returns + */ + const E = (type, ...classes) => { + const Element = document.createElement(type); + for (const c of classes) { + Element.classList.add(c); + } + return Element; + }; + + const ButtonE = ({ text }) => { + const Button = E("button", "dm-button"); + Button.innerHTML = text ?? ""; + return { DOMElement: Button }; + }; + + const DropdownMenuE = ({ + triggerText, + items = [{ text: "", onClick: undefined }], + openMenuOnHover = false, + }) => { + // Utils + let isOpen = false; + let timeout; + + const DropdownMenu = E("div"); + + // Content + const DropdownMenuContentE = E("div", "dm-sp-dd-lst", "hide"); + const DropdownMenuListE = E("div", "dm-sp-dd-list-items"); + DropdownMenuContentE.appendChild(DropdownMenuListE); + + items.forEach((item) => { + console.log(item); + const DropdownMenuListItemE = E("div", "dm-sp-dd-list-item"); + DropdownMenuListItemE.innerHTML = item.text ?? ""; + if (item.onClick) + DropdownMenuListItemE.addEventListener("click", item.onClick); + + DropdownMenuListE.appendChild(DropdownMenuListItemE); + }); + + // Handle state + const closeMenu = () => { + DropdownMenuContentE.classList.add("hide"); + isOpen = false; + }; + + const openMenu = () => { + DropdownMenuContentE.classList.remove("hide"); + if (timeout) clearTimeout(timeout); + timeout = setTimeout( + () => handleClickOutsideElement(DropdownMenuContentE, closeMenu), + 10 + ); + isOpen = true; + }; + + // Trigger + const DropdownMenuTrigger = ButtonE({ + text: triggerText, + }); + + const DropdownMenuTriggerE = DropdownMenuTrigger.DOMElement; + + DropdownMenuTriggerE.addEventListener("click", () => { + if (!isOpen) openMenu(); + }); + + DropdownMenu.appendChild(DropdownMenuTriggerE); + DropdownMenu.appendChild(DropdownMenuContentE); + + return { DOMElement: DropdownMenu }; + }; + + const SongControlsE = () => { + const SongControls = E("div"); + + const TransposeDropdown = DropdownMenuE({ + triggerText: "Transponering", + items: [ + { text: "Skriv ut" }, + { text: "Kopier ChordPro" }, + { text: "Last ned ChordPro" }, + ], + openMenuOnHover: true, + }); + SongControls.appendChild(TransposeDropdown.DOMElement); + + return { DOMElement: SongControls }; + }; + + const SongComponentE = () => { + const SongComponent = E("div"); + + const SongControls = SongControlsE(); + + SongComponent.appendChild(SongControls.DOMElement); + + return { DOMElement: SongComponent }; + }; + + return SongComponentE().DOMElement; +}; + +document.querySelectorAll("Song").forEach((e) => { + e.innerHTML = ""; + e.appendChild(initSongComponent()); +}); diff --git a/pages/song/extra.css b/pages/song/extra.css new file mode 100644 index 0000000..803afff --- /dev/null +++ b/pages/song/extra.css @@ -0,0 +1,96 @@ +.cp-meta-block { + position: absolute; + top: 0px; + right: 0px; +} + +.cp-meta-block .cp-meta { + text-align: right; +} + +.cp-song-title { + font-weight: bold; + font-size: 21px; + margin-bottom: 0px; +} + +.cp-song-artist { + font-size: 14px; + padding-bottom: 6px; +} + +.cp-info-block { + font-family: "Noto Sans", sans-serif; + margin-right: 100px; + -webkit-column-span: all; + column-span: all; + height: 0px; + overflow: hidden; + margin-bottom: 0px; +} + +.cp-chord { + font-weight: 500; + margin-bottom: -1em; +} + +.cp-heading { + font-weight: bold; +} + +.cp-section { + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} +.cp-two-columns { + -webkit-column-count: 2; /* Chrome, Safari, Opera */ + -moz-column-count: 2; /* Firefox */ + column-count: 2; +} +.cp-three-columns { + -webkit-column-count: 3; /* Chrome, Safari, Opera */ + -moz-column-count: 3; /* Firefox */ + column-count: 3; +} +.cp-extra-meta-bottom { + font-size: 10px; +} + +.cp-meta-wrapper { + position: relative; +} + +.cp-key-tempo-wrapper { + position: absolute; + top: 0.4rem; + right: 2.6rem; +} + +.chord-pro-preview { + display: none; +} +.chord-pro-preview.open { + display: block; + position: fixed; + z-index: 100; + background-color: rgba(0, 0, 0, 0.2); + top: 0; + left: 0; + height: 100%; + width: 100%; + + display: flex; + justify-content: center; + align-items: center; +} + +.chord-pro-preview.open > * { + max-width: 40rem; + background-color: #222; + height: 80%; +} + +.monofont { + font-family: "Courier New", Courier, monospace; +} diff --git a/pages/song/song.html b/pages/song/song.html index 0fa59be..f130ff6 100644 --- a/pages/song/song.html +++ b/pages/song/song.html @@ -8,6 +8,12 @@ +