diff --git a/admin.html b/admin.html index eb98145..a2f750d 100644 --- a/admin.html +++ b/admin.html @@ -31,7 +31,7 @@

Users

@@ -49,7 +49,7 @@

Groups

diff --git a/config.html b/config.html index adf6505..d0a00fe 100644 --- a/config.html +++ b/config.html @@ -40,22 +40,22 @@ Disks
- Add New Disk - Add New CDROM + +
Network Interfaces
- Add New Network Interface +
PCIe Devices
- Add New PCIe Device +
diff --git a/css/style.css b/css/style.css index d3ebd3b..2f144a1 100644 --- a/css/style.css +++ b/css/style.css @@ -92,13 +92,14 @@ input, select, textarea { color: var(--main-text-color); } -img.clickable { +img.clickable, svg.clickable { cursor: pointer; } -img { +img, svg { height: 1em; width: 1em; + color: var(--main-text-color) } hr, * { @@ -165,8 +166,19 @@ hr, * { background-size: 1em auto; } +/* add hide large class similar to w3-hide-medium and w3-hide-small */ @media (width >=993px) { .w3-hide-large { display: none !important; } +} + +/* fix edge case in w3-hide-medium where width between 992 and 993 */ +@media (width <=993px) and (width >=601px){ + .w3-hide-medium{display:none!important} +} + +/* fix edge case in w3-hide-small when width between 600 and 601 */ +@media (width <=601px) { + .w3-hide-small{display:none!important} } \ No newline at end of file diff --git a/images/actions/delete-active.svg b/images/actions/delete-active.svg index 2198eaa..e4ca44d 100644 --- a/images/actions/delete-active.svg +++ b/images/actions/delete-active.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/delete-inactive.svg b/images/actions/delete-inactive.svg index e327779..4f6626f 100644 --- a/images/actions/delete-inactive.svg +++ b/images/actions/delete-inactive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/device/add.svg b/images/actions/device/add.svg index 614aec3..8bb7bca 100644 --- a/images/actions/device/add.svg +++ b/images/actions/device/add.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/device/config.svg b/images/actions/device/config.svg index f71ce3f..69952f6 100644 --- a/images/actions/device/config.svg +++ b/images/actions/device/config.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/disk/add-cd.svg b/images/actions/disk/add-cd.svg index 1bc1ccb..e05015f 100644 --- a/images/actions/disk/add-cd.svg +++ b/images/actions/disk/add-cd.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/disk/add-disk.svg b/images/actions/disk/add-disk.svg index 5da9b0e..5fcc611 100644 --- a/images/actions/disk/add-disk.svg +++ b/images/actions/disk/add-disk.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/disk/attach.svg b/images/actions/disk/attach.svg index 4d5d24e..bc1e885 100644 --- a/images/actions/disk/attach.svg +++ b/images/actions/disk/attach.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/disk/detach.svg b/images/actions/disk/detach.svg index 431ed82..596b8de 100644 --- a/images/actions/disk/detach.svg +++ b/images/actions/disk/detach.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/disk/detach_attach-inactive.svg b/images/actions/disk/detach_attach-inactive.svg index 5fda422..84d94c3 100644 --- a/images/actions/disk/detach_attach-inactive.svg +++ b/images/actions/disk/detach_attach-inactive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/disk/move-active.svg b/images/actions/disk/move-active.svg index bb78ec7..fb0afca 100644 --- a/images/actions/disk/move-active.svg +++ b/images/actions/disk/move-active.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/disk/move-inactive.svg b/images/actions/disk/move-inactive.svg index 141a262..f77ac9d 100644 --- a/images/actions/disk/move-inactive.svg +++ b/images/actions/disk/move-inactive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/disk/resize-active.svg b/images/actions/disk/resize-active.svg index 614aec3..1b90f8f 100644 --- a/images/actions/disk/resize-active.svg +++ b/images/actions/disk/resize-active.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/disk/resize-inactive.svg b/images/actions/disk/resize-inactive.svg index 02375b1..bf0aee6 100644 --- a/images/actions/disk/resize-inactive.svg +++ b/images/actions/disk/resize-inactive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/drag.svg b/images/actions/drag.svg index 0aab748..55a9f6a 100644 --- a/images/actions/drag.svg +++ b/images/actions/drag.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/group/add.svg b/images/actions/group/add.svg index 614aec3..ef44b1d 100644 --- a/images/actions/group/add.svg +++ b/images/actions/group/add.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/instance/add.svg b/images/actions/instance/add.svg index 614aec3..40c4178 100644 --- a/images/actions/instance/add.svg +++ b/images/actions/instance/add.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/instance/config-active.svg b/images/actions/instance/config-active.svg index f71ce3f..016a690 100644 --- a/images/actions/instance/config-active.svg +++ b/images/actions/instance/config-active.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/instance/config-inactive.svg b/images/actions/instance/config-inactive.svg index b34ff17..5b313f5 100644 --- a/images/actions/instance/config-inactive.svg +++ b/images/actions/instance/config-inactive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/instance/console-active.svg b/images/actions/instance/console-active.svg index 6093231..17c8085 100644 --- a/images/actions/instance/console-active.svg +++ b/images/actions/instance/console-active.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/instance/console-inactive.svg b/images/actions/instance/console-inactive.svg index af82ee7..f3cab59 100644 --- a/images/actions/instance/console-inactive.svg +++ b/images/actions/instance/console-inactive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/instance/start.svg b/images/actions/instance/start.svg index 61fafe0..081442b 100644 --- a/images/actions/instance/start.svg +++ b/images/actions/instance/start.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/instance/stop.svg b/images/actions/instance/stop.svg index e7870d2..a2942f6 100644 --- a/images/actions/instance/stop.svg +++ b/images/actions/instance/stop.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/network/add.svg b/images/actions/network/add.svg index 614aec3..ec25750 100644 --- a/images/actions/network/add.svg +++ b/images/actions/network/add.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/network/config.svg b/images/actions/network/config.svg index f71ce3f..470bf23 100644 --- a/images/actions/network/config.svg +++ b/images/actions/network/config.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/actions/user/add.svg b/images/actions/user/add.svg index 614aec3..1540cf7 100644 --- a/images/actions/user/add.svg +++ b/images/actions/user/add.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/favicon.svg b/images/favicon.svg index 58e5433..524bd61 100644 --- a/images/favicon.svg +++ b/images/favicon.svg @@ -1 +1 @@ -H0 \ No newline at end of file +H0 \ No newline at end of file diff --git a/images/resources/cpu.svg b/images/resources/cpu.svg index f4cfff3..0a2c072 100644 --- a/images/resources/cpu.svg +++ b/images/resources/cpu.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/resources/device.svg b/images/resources/device.svg index 54e8c5a..8f6f69d 100644 --- a/images/resources/device.svg +++ b/images/resources/device.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/resources/disk.svg b/images/resources/disk.svg index b12ac77..713157a 100644 --- a/images/resources/disk.svg +++ b/images/resources/disk.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/resources/drive.svg b/images/resources/drive.svg index 38a2fe6..a1efbd3 100644 --- a/images/resources/drive.svg +++ b/images/resources/drive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/resources/network.svg b/images/resources/network.svg index 8f69888..6d7148b 100644 --- a/images/resources/network.svg +++ b/images/resources/network.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/resources/ram.svg b/images/resources/ram.svg index 355f568..e3c775f 100644 --- a/images/resources/ram.svg +++ b/images/resources/ram.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/resources/swap.svg b/images/resources/swap.svg index 65b6f7c..739210a 100644 --- a/images/resources/swap.svg +++ b/images/resources/swap.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/static/search.svg b/images/static/search.svg index 6533aed..d31ceb1 100644 --- a/images/static/search.svg +++ b/images/static/search.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/status/active.svg b/images/status/active.svg index 83bf551..fc376c4 100644 --- a/images/status/active.svg +++ b/images/status/active.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/status/inactive.svg b/images/status/inactive.svg index 893ca7c..c99b98d 100644 --- a/images/status/inactive.svg +++ b/images/status/inactive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/status/loading.svg b/images/status/loading.svg index 3e42630..ff64f94 100644 --- a/images/status/loading.svg +++ b/images/status/loading.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/index.html b/index.html index e1f7ba8..29fd515 100644 --- a/index.html +++ b/index.html @@ -51,12 +51,12 @@
diff --git a/scripts/config.js b/scripts/config.js index 4c1ba89..ab099fd 100644 --- a/scripts/config.js +++ b/scripts/config.js @@ -1,4 +1,4 @@ -import { requestPVE, requestAPI, goToPage, getURIData, resourcesConfig, setTitleAndHeader, bootConfig, setAppearance } from "./utils.js"; +import { requestPVE, requestAPI, goToPage, getURIData, resourcesConfig, setTitleAndHeader, bootConfig, setAppearance, setSVGSrc, setSVGAlt } from "./utils.js"; import { alert, dialog } from "./dialog.js"; window.addEventListener("DOMContentLoaded", init); // do the dumb thing where the disk config refreshes every second @@ -88,9 +88,9 @@ async function populateResources () { function addResourceLine (fieldset, iconHref, type, labelText, id, attributes, unitText = null) { const field = document.querySelector(`#${fieldset}`); - const icon = document.createElement("img"); - icon.src = iconHref; - icon.alt = labelText; + const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + setSVGSrc(icon, iconHref); + setSVGAlt(icon, labelText); field.append(icon); const label = document.createElement("label"); @@ -168,9 +168,9 @@ function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) { const diskID = `${busPrefix}${device}`; // Set the disk icon, either drive.svg or disk.svg - const icon = document.createElement("img"); - icon.src = diskMetaData[type][busPrefix].icon; - icon.alt = diskName; + const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + setSVGSrc(icon, diskMetaData[type][busPrefix].icon); + setSVGAlt(icon, diskName); icon.dataset.disk = diskID; field.append(icon); @@ -190,23 +190,24 @@ function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) { const actionDiv = document.createElement("div"); diskMetaData.actionBarOrder.forEach((element) => { - const action = document.createElement("img"); + const action = document.createElementNS("http://www.w3.org/2000/svg", "svg"); if (element === "detach_attach" && diskMetaData[type][busPrefix].actions.includes("attach")) { // attach - action.src = "images/actions/disk/attach.svg"; + setSVGSrc(action, diskMetaData.actions.attach.src); + setSVGAlt(action, diskMetaData.actions.attach.title); action.title = "Attach Disk"; action.addEventListener("click", handleDiskAttach); action.classList.add("clickable"); } else if (element === "detach_attach" && diskMetaData[type][busPrefix].actions.includes("detach")) { // detach - action.src = "images/actions/disk/detach.svg"; - action.title = "Detach Disk"; + setSVGSrc(action, diskMetaData.actions.detach.src); + setSVGAlt(action, diskMetaData.actions.detach.title); action.addEventListener("click", handleDiskDetach); action.classList.add("clickable"); } else if (element === "delete") { const active = diskMetaData[type][busPrefix].actions.includes(element) ? "active" : "inactive"; // resize - action.src = `images/actions/delete-${active}.svg`; - action.title = "Delete Disk"; + setSVGSrc(action, `images/actions/delete-${active}.svg`); + setSVGAlt(action, "Delete Disk"); if (active === "active") { action.addEventListener("click", handleDiskDelete); action.classList.add("clickable"); @@ -214,9 +215,9 @@ function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) { } else { const active = diskMetaData[type][busPrefix].actions.includes(element) ? "active" : "inactive"; // resize - action.src = `images/actions/disk/${element}-${active}.svg`; + setSVGSrc(action, `images/actions/disk/${element}-${active}.svg`); if (active === "active") { - action.title = `${element.charAt(0).toUpperCase()}${element.slice(1)} Disk`; + setSVGAlt(action, `${element.charAt(0).toUpperCase()}${element.slice(1)} Disk`); if (element === "move") { action.addEventListener("click", handleDiskMove); } @@ -227,7 +228,6 @@ function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) { } } action.dataset.disk = diskID; - action.alt = action.title; actionDiv.append(action); }); field.append(actionDiv); @@ -239,7 +239,7 @@ async function handleDiskDetach () { const body = `

Are you sure you want to detach disk ${disk}

`; dialog(header, body, async (result, form) => { if (result === "confirm") { - document.querySelector(`img[data-disk="${disk}"]`).src = "images/status/loading.svg"; + setSVGSrc(document.querySelector(`svg[data-disk="${disk}"]`), "images/status/loading.svg"); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/detach`, "POST"); if (result.status !== 200) { alert(result.error); @@ -263,7 +263,7 @@ async function handleDiskAttach () { dialog(header, body, async (result, form) => { if (result === "confirm") { const device = form.get("device"); - document.querySelector(`img[data-disk="${this.dataset.disk}"]`).src = "images/status/loading.svg"; + setSVGSrc(document.querySelector(`svg[data-disk="${this.dataset.disk}"]`), "images/status/loading.svg"); const body = { source: this.dataset.disk.replace("unused", "") }; @@ -292,7 +292,7 @@ async function handleDiskResize () { dialog(header, body, async (result, form) => { if (result === "confirm") { const disk = this.dataset.disk; - document.querySelector(`img[data-disk="${disk}"]`).src = "images/status/loading.svg"; + setSVGSrc(document.querySelector(`svg[data-disk="${disk}"]`), "images/status/loading.svg"); const body = { size: form.get("size-increment") }; @@ -332,7 +332,7 @@ async function handleDiskMove () { dialog(header, body, async (result, form) => { if (result === "confirm") { const disk = this.dataset.disk; - document.querySelector(`img[data-disk="${disk}"]`).src = "images/status/loading.svg"; + setSVGSrc(document.querySelector(`svg[data-disk="${disk}"]`), "images/status/loading.svg"); const body = { storage: form.get("storage-select"), delete: form.get("delete-check") === "on" ? "1" : "0" @@ -355,7 +355,7 @@ async function handleDiskDelete () { const body = `

Are you sure you want to delete disk${disk}

`; dialog(header, body, async (result, form) => { if (result === "confirm") { - document.querySelector(`img[data-disk="${disk}"]`).src = "images/status/loading.svg"; + setSVGSrc(document.querySelector(`svg[data-disk="${disk}"]`), "images/status/loading.svg"); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/delete`, "DELETE"); if (result.status !== 200) { alert(result.error); @@ -465,9 +465,9 @@ async function populateNetworks () { function addNetworkLine (fieldset, prefix, netID, netDetails) { const field = document.querySelector(`#${fieldset}`); - const icon = document.createElement("img"); - icon.src = "images/resources/network.svg"; - icon.alt = `${prefix}${netID}`; + const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + setSVGSrc(icon, "images/resources/network.svg"); + setSVGAlt(icon, `${prefix}${netID}`); icon.dataset.network = netID; icon.dataset.values = netDetails; field.appendChild(icon); @@ -488,19 +488,19 @@ function addNetworkLine (fieldset, prefix, netID, netDetails) { const actionDiv = document.createElement("div"); - const configBtn = document.createElement("img"); + const configBtn = document.createElementNS("http://www.w3.org/2000/svg", "svg"); configBtn.classList.add("clickable"); - configBtn.src = "images/actions/network/config.svg"; - configBtn.title = "Config Interface"; + setSVGSrc(configBtn, "images/actions/network/config.svg"); + setSVGAlt(configBtn, "Config Interface"); configBtn.addEventListener("click", handleNetworkConfig); configBtn.dataset.network = netID; configBtn.dataset.values = netDetails; actionDiv.appendChild(configBtn); - const deleteBtn = document.createElement("img"); + const deleteBtn = document.createElementNS("http://www.w3.org/2000/svg", "svg"); deleteBtn.classList.add("clickable"); - deleteBtn.src = "images/actions/delete-active.svg"; - deleteBtn.title = "Delete Interface"; + setSVGSrc(deleteBtn, "images/actions/delete-active.svg"); + setSVGAlt(deleteBtn, "Delete Interface"); deleteBtn.addEventListener("click", handleNetworkDelete); deleteBtn.dataset.network = netID; deleteBtn.dataset.values = netDetails; @@ -521,7 +521,7 @@ async function handleNetworkConfig () { const d = dialog(header, body, async (result, form) => { if (result === "confirm") { - document.querySelector(`img[data-network="${netID}"]`).src = "images/status/loading.svg"; + setSVGSrc(document.querySelector(`svg[data-network="${netID}"]`), "images/status/loading.svg"); const body = { rate: form.get("rate") }; @@ -546,7 +546,7 @@ async function handleNetworkDelete () { dialog(header, body, async (result, form) => { if (result === "confirm") { - document.querySelector(`img[data-network="${netID}"]`).src = "images/status/loading.svg"; + setSVGSrc(document.querySelector(`svg[data-network="${netID}"]`), "images/status/loading.svg"); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/net/net${netID}/delete`, "DELETE"); if (result.status !== 200) { alert(result.error); @@ -615,9 +615,9 @@ async function populateDevices () { function addDeviceLine (fieldset, prefix, deviceID, deviceDetails, deviceName) { const field = document.querySelector(`#${fieldset}`); - const icon = document.createElement("img"); - icon.src = "images/resources/device.svg"; - icon.alt = `${prefix}${deviceID}`; + const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + setSVGSrc(icon, "images/resources/device.svg"); + setSVGAlt(icon, `${prefix}${deviceID}`); icon.dataset.device = deviceID; icon.dataset.values = deviceDetails; icon.dataset.name = deviceName; @@ -634,20 +634,20 @@ function addDeviceLine (fieldset, prefix, deviceID, deviceDetails, deviceName) { const actionDiv = document.createElement("div"); - const configBtn = document.createElement("img"); + const configBtn = document.createElementNS("http://www.w3.org/2000/svg", "svg"); configBtn.classList.add("clickable"); - configBtn.src = "images/actions/device/config.svg"; - configBtn.title = "Config Device"; + setSVGSrc(configBtn, "images/actions/device/config.svg"); + setSVGAlt(configBtn, "Config Device"); configBtn.addEventListener("click", handleDeviceConfig); configBtn.dataset.device = deviceID; configBtn.dataset.values = deviceDetails; configBtn.dataset.name = deviceName; actionDiv.appendChild(configBtn); - const deleteBtn = document.createElement("img"); + const deleteBtn = document.createElementNS("http://www.w3.org/2000/svg", "svg"); deleteBtn.classList.add("clickable"); - deleteBtn.src = "images/actions/delete-active.svg"; - deleteBtn.title = "Delete Device"; + setSVGSrc(deleteBtn, "images/actions/delete-active.svg"); + setSVGAlt(deleteBtn, "Delete Device"); deleteBtn.addEventListener("click", handleDeviceDelete); deleteBtn.dataset.device = deviceID; deleteBtn.dataset.values = deviceDetails; @@ -670,7 +670,7 @@ async function handleDeviceConfig () { const d = dialog(header, body, async (result, form) => { if (result === "confirm") { - document.querySelector(`img[data-device="${deviceID}"]`).src = "images/status/loading.svg"; + setSVGSrc(document.querySelector(`svg[data-device="${deviceID}"]`), "images/status/loading.svg"); const body = { device: form.get("device"), pcie: form.get("pcie") ? 1 : 0 @@ -699,7 +699,7 @@ async function handleDeviceDelete () { dialog(header, body, async (result, form) => { if (result === "confirm") { - document.querySelector(`img[data-device="${deviceID}"]`).src = "images/status/loading.svg"; + setSVGSrc(document.querySelector(`svg[data-device="${deviceID}"]`), "images/status/loading.svg"); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/pci/hostpci${deviceID}/delete`, "DELETE"); if (result.status !== 200) { alert(result.error); @@ -783,14 +783,12 @@ function addBootLine (container, data, before = null) { item.data = data; item.innerHTML = `
- drag icon - ${bootMetaData[data.prefix].alt} + drag icon + ${bootMetaData[data.prefix].alt}

${data.id}

${data.detail}

`; - item.draggable = true; - item.classList.add("drop-target"); item.id = `boot-${data.id}`; if (before) { document.querySelector(`#${container}`).insertBefore(item, before); diff --git a/scripts/draggable.js b/scripts/draggable.js index c7cdf18..ed6e07d 100644 --- a/scripts/draggable.js +++ b/scripts/draggable.js @@ -11,6 +11,9 @@ class DraggableContainer extends HTMLElement { border-radius: 5px; margin: -1px; } + draggable-item::part(wrapper) { + cursor: grab; + }
@@ -78,7 +81,7 @@ class DraggableItem extends HTMLElement { // for whatever reason, only grid layout seems to respect the parent's content bounds this.shadowRoot.innerHTML = `