Щелкните событие на кнопки, не работающие в раскрывающемся Shadow DomJavascript

Форум по Javascript
Ответить
Anonymous
 Щелкните событие на кнопки, не работающие в раскрывающемся Shadow Dom

Сообщение Anonymous »

Я делаю компонент и столкнулся с проблемой, которую я не могу думать, и нахожу какие -либо рабочие решения. Когда что -то находится в раскрывающемся спине, их события тоже не указаны, но когда они выходят за пределы выпадения в заголовок, они работают. Я использую библиотеку компонентов системы проектирования кальцита для действий кальцита, но я попытался использовать DIV/кнопки, чтобы убедиться, что это не проблема. Я тоже пытался поиграть с указательными событиями, но это тоже не сработало. />https://codepen.io/keeron1/pen/ggpjbe

Код: Выделить всё

class Floater extends HTMLElement {
constructor() {
super();
this.attachShadow({
mode: "open"
});

// Wait for Calcite's  to be defined before injecting HTML
customElements.whenDefined("calcite-action").then(() => {

// Inject HTML template
const tpl = document.getElementById("floater-template");
const clone = tpl.content.cloneNode(true);
this.shadowRoot.appendChild(clone);

this._init();
});
}

// Public API
get position() {
return this.getAttribute("position") || "top-left";
}
set position(v) {
this.setAttribute("position", v);
}
get isDraggable() {
if (!this.hasAttribute("draggable")) return true
return this.getAttribute("draggable") === "true" ? true : false
}
set isDraggable(v) {
this.setAttribute("draggable", v);
}
get isResizable() {
return this.getAttribute("resizable") || "none"
}
set isResizable(v) {
this.setAttribute("resizable", v);
}
get isHandleEnabled() {
if (!this.hasAttribute("handle-enabled")) return false
return this.getAttribute("handle-enabled") === "true" ? true : false
}
set isHandleEnabled(v) {
this.setAttribute("handle-enabled", v);
}

_init() {
// Elements
this.floaterCont = this.shadowRoot.querySelector(".floater-container")
this.floater = this.shadowRoot.querySelector(".floater");
// Heading
this.heading = this.shadowRoot.querySelector(".floater-heading")
this.hTitle = this.shadowRoot.querySelector(".floater-heading-title");
this.originalhTitle = this.hTitle?.outerHTML || null;
this.headingEnd = this.shadowRoot.querySelector(".floater-heading-end")
this.headingEndSlot = this.shadowRoot.querySelector('slot[name="heading-end"]');
this.closeButton = this.shadowRoot.querySelector(".floater-close");
this.originalCloseButton = this.closeButton?.outerHTML || null
// Dropdown
this.headingDropdown = this.shadowRoot.querySelector(".floater-heading-dropdown")
this.originalHeadingDropdown = this.headingDropdown?.outerHTML || null
this.headingDropdownAction = this.shadowRoot.querySelector(".floater-heading-dropdown-action")
this.headingDropdownItems = this.shadowRoot.querySelector(".floater-heading-dropdown-items")
this.headingEndDropdownSlot = this.shadowRoot.querySelector('slot[name="dropdown-heading-end"]')
// Content
this.floaterContent = this.shadowRoot.querySelector(".floater-content");
this.contentSlot = this.shadowRoot.querySelector('slot:not([name])');

this.test = this.shadowRoot.querySelector(".floater-test")

// Attributes
this.isDragging = false;
this.dragOffsetX = 0; // Distance between cursor and left of the component
this.dragOffsetY = 0; // Distance between cursor and top of the component
this.lastWindowWH = {
width: null,
height: null
}; // Window width and height

requestAnimationFrame(() =>  {
this._updateTitle();
this._updateClose();
this._setScale();
this._updateDraggable();
this._updateResizable();
window.addEventListener("resize", this._onResize);
this.closeButton?.addEventListener("click", this._close);

this.test?.addEventListener("click", this._testClick)

this._setStartingPosition();

this.lastWindowWH.width = window.innerWidth;
this.lastWindowWH.height = window.innerHeight;
});
}

// Trigger on component created (not used since we need to wait for calcite action)
connectedCallback() {}

// Trigger on component delete
disconnectedCallback() {
window.removeEventListener("resize", this._onResize)
if (this.isDraggable) this.heading.removeEventListener("pointerdown", this._onDown)
if (this.closeButton) this.closeButton.removeEventListener("click", this._close)

if (this.test) this.test.removeEventListener("click", this._testClick)
}

static get observedAttributes() {
return ["title", "close-disabled", "scale", "draggable", "resizable", "handle-enabled"];
}

// Trigger when attribute changes
attributeChangedCallback(name, oldValue, newValue) {
if (name === "title") this._updateTitle();
if (name === "close-disabled") this._updateClose();
if (name === "scale") this._setScale();
if (name === "draggable") this._updateDraggable();
if (name === "resizable") this._updateResizable();
if (name === "handle-enabled") this._updateHandle();
}

_testClick = () => {
if (!this.isHandleEnabled) this.isHandleEnabled = true
else this.isHandleEnabled = false
}

_updateHandle = () => {
if (this.isHandleEnabled) this._sendHeadingItemsToDropdown();
else this._sendDropdownItemsToHeading();
}

_sendHeadingItemsToDropdown = () => {
if (!this.hasAttribute("close-disabled"))
this.headingDropdownItems.insertBefore(this.closeButton, this.headingEndDropdownSlot)

const endSlot = this.headingEndSlot.assignedElements()
if (endSlot.length === 0) return;
endSlot.forEach(element => {
element.setAttribute("slot", "dropdown-heading-end");
});
}

_sendDropdownItemsToHeading = () => {
if (!this.hasAttribute("close-disabled")) this.headingEnd.append(this.closeButton)

const endSlot = this.headingEndDropdownSlot.assignedElements()
if (endSlot.length === 0) return;
endSlot.forEach(element => {
element.setAttribute("slot", "heading-end");
});
}

_close = () => {
console.log("closed btn clicked")
this.dispatchEvent(new CustomEvent("floaterClose", {
bubbles: true,
composed: true
}));

if (!this.hasAttribute("manual-close"))
this.remove();
}

_onResize = () => {
// Could be improved by saving the floater's left value before it gets pushed, so
//  that when the window could grow it could stick to that value

const winWidth = window.innerWidth
const winHeight = window.innerHeight

// Calculate window delta
const deltaX = winWidth - this.lastWindowWH.width;
const deltaY = winHeight - this.lastWindowWH.height;

// Get floater's current properties
const floaterRect = this.floater.getBoundingClientRect();
const currentTop = floaterRect.top
const currentLeft = floaterRect.left
const fw = this.floater.offsetWidth;
const fh = this.floater.offsetHeight;

// Remove inital position class
this.floaterCont.classList.remove("top-left", "top-right", "bottom-left", "bottom-right", "center");

let newTop = currentTop + deltaY;
let newLeft = currentLeft + deltaX;

// Horizontal nudge only on shrink AND if closer to the right edge
if (deltaX < 0) {
const distRight = winWidth - fw - currentLeft;
if (distRight  {
if (this.isDraggable) {
this.heading.classList.add("draggable")
this.heading.addEventListener("pointerdown", this._onDown)
} else {
this.heading.classList.remove("draggable")
this.heading.removeEventListener("pointerdown", this._onDown)
}
}

_updateResizable = () => {
this.floaterContent.classList.remove("resize-horizontal", "resize-vertical", "resize-both");
switch (this.isResizable) {
case "horizontal":
this.floaterContent.classList.add("resize-horizontal")
break
case "vertical":
this.floaterContent.classList.add("resize-vertical")
break
case "both":
this.floaterContent.classList.add("resize-both")
break
}
}

_updateTitle = () => {
const titleAtr = this.getAttribute("title") || "";
this.hTitle = this._handleElementLife(titleAtr, this.hTitle, this.originalhTitle, this.heading)
}

_updateClose = () => {
const disabled = this.hasAttribute("close-disabled");
if (disabled) {
this.closeButton.removeEventListener("click", this._close);
this.closeButton.remove();
this.closeButton = null;
return;
}

// Add the element
if (!this.closeButton && this.originalCloseButton) {
const temp = document.createElement("div");
temp.innerHTML = this.originalCloseButton;
this.closeButton = temp.firstElementChild;
this.closeButton.addEventListener("click", this._close);
this.closeButton.scale = this.getAttribute("scale") || "s"
if (this.isHandleEnabled) this.headingDropdownItems.insertBefore(this.closeButton, this.headingEndDropdownSlot)
else this.headingEnd.append(this.closeButton);
}
}

_setStartingPosition = () => {
switch (this.position) {
case "center":
this.floaterCont.classList.add("center")
break;
case "top-left":
this.floaterCont.classList.add("top-left")
break;
case "top-right":
this.floaterCont.classList.add("top-right")
break;
case "bottom-left":
this.floaterCont.classList.add("bottom-left")
break;
case "bottom-right":
this.floaterCont.classList.add("bottom-right")
break;
}
}

_setScale = () => {
let scaleAtr = this.getAttribute("scale") || "s";
if (this.closeButton) this.closeButton.scale = scaleAtr
if (this.headingDropdownAction) this.headingDropdownAction.scale = scaleAtr
}

// Handle floater movement
_onDown = (e) => {
if (e.target.closest(".floater-heading-end")) return;
if (this.headingEndSlot.assignedElements().some(el =>  el === e.target)) return

e.preventDefault();
this.isDragging = true;

// capture the pointer so we don't lose events
this.setPointerCapture(e.pointerId);

// Compute position based on visual location
const rect = this.floater.getBoundingClientRect();
this.dragOffsetX = e.clientX - rect.left;
this.dragOffsetY = e.clientY - rect.top;

this.addEventListener("pointermove", this._onMove);
this.addEventListener("pointerup", this._onUp);
};

_onMove = (e) => {
if (!this.isDragging) return;

// Remove previous
this.floaterCont.classList.remove("top-left", "top-right", "bottom-left", "bottom-right", "center");

// New positions
let newTop = e.clientY - this.dragOffsetY;
let newLeft = e.clientX - this.dragOffsetX;

const vw = window.innerWidth;
const vh = window.innerHeight;
const fw = this.floater.offsetWidth;
const fh = this.floater.offsetHeight;

// Clamp to viewport
newTop = Math.max(0, Math.min(vh - fh, newTop));
newLeft = Math.max(0, Math.min(vw - fw, newLeft));

// Remove container offset
const contRect = this.floaterCont.getBoundingClientRect();
newTop -= contRect.top;
newLeft -= contRect.left;

this.floater.style.top = `${newTop}px`;
this.floater.style.left = `${newLeft}px`;
}

_onUp = (e) =>  {
this.isDragging = false;
this.releasePointerCapture(e.pointerId);
this.removeEventListener("mousemove", this._onMove);
this.removeEventListener("mouseup", this._onUp);
}
}

if (!customElements.get("custom-floater"))
customElements.define("custom-floater", Floater);< /code>





:host {
--floater-offset-x: 0px;
--floater-offset-y: 0px;
--floater-min-size-x: auto;
--floater-max-size-x: none;
--floater-min-size-y: auto;
--floater-max-size-y: none;
--floater-content-padding: var(--calcite-spacing-md);
--floater-opacity: 1;
--floater-content-opacity: 1;
}

.floater-container {
position: fixed;
z-index: var(--calcite-z-index-modal);
top: var(--floater-offset-y, 0);
left: var(--floater-offset-x, 0);
pointer-events: none;
}

/* Starting positions */
.floater-container.center {
inset: 0;

>.floater {
top: calc(50% + var(--floater-offset-y, 0px));
left: calc(50% + var(--floater-offset-x, 0px));
transform: translate(-50%, -50%);
}
}

.floater-container.top-right {
top: var(--floater-offset-y, 0);
right: var(--floater-offset-x, 0);
left: auto;

>.floater {
top: var(--calcite-spacing-md);
right: var(--calcite-spacing-md);
}
}

.floater-container.top-left>.floater {
top: var(--calcite-spacing-md);
left: var(--calcite-spacing-md);
}

.floater-container.bottom-left {
bottom: var(--floater-offset-y, 0);
left: var(--floater-offset-x, 0);
top: auto;

>.floater {
bottom: var(--calcite-spacing-md);
left: var(--calcite-spacing-md);
}
}

.floater-container.bottom-right {
bottom: var(--floater-offset-y, 0);
right: var(--floater-offset-x, 0);
top: auto;
left: auto;

>.floater {
bottom: var(--calcite-spacing-md);
right: var(--calcite-spacing-md);
}
}

/* End of starting positions */
.floater {
display: flex;
flex-direction: column;
max-height: 100vh;
position: absolute;
pointer-events: all;
box-sizing: border-box;
border-radius: 0.25rem;
background-color: var(--floater-background-color);
font-family: var(--calcite-sans-family);
box-shadow: var(--calcite-shadow-sm);
opacity: var(--floater-opacity);
}

.floater-heading {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
flex: 0 0 auto;
border-bottom: 1px solid var(--calcite-color-border-3);
user-select: none;
}

.floater-heading.draggable {
cursor: move;
}

.floater-heading-title {
padding: var(--calcite-spacing-xs) var(--calcite-spacing-md-plus);
font-weight: var(--calcite-font-weight-medium);
font-size: var(--calcite-font-size-0);
color: var(--calcite-color-text-1);
}

.floater-heading-end {
margin-left: auto;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}

/* Dropdown menu */
.floater-heading-dropdown-action {
height: 100%;
}

.floater-heading-dropdown-items {
pointer-events: all;
visibility: hidden;
position: absolute;
top: auto;
right: 0;
background: var(--calcite-color-background);
padding: 0.5rem;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
z-index: 100;
border-radius: 4px;
cursor: default;
overflow-y: auto;
height: 100%;
max-height: 300px;
/* Firefox */
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-color);
}

slot[name="dropdown-heading-end"] {
display: flex;
flex-direction:  column-reverse;
}

/* Chrome, Edge, Safari */
@supports selector(::-webkit-scrollbar) {
.floater-heading-dropdown-items {
/* Override rules */
scrollbar-width: auto;
scrollbar-color: auto;
}

.floater-heading-dropdown-items::-webkit-scrollbar {
width: var(--scrollbar-width);
}

.floater-heading-dropdown-items::-webkit-scrollbar-thumb {
background: var(--scrollbar-background-color);
border-radius: var(--scrollbar-border-radius);
}
}

.floater-heading-dropdown:hover .floater-heading-dropdown-items {
visibility: visible;
}

/* End of Dropdown menu */
/* .floater-close{ */
/* height: 100%; */
/* width: auto; */
/* background-color: transparent; */
/* border: 0; */
/* cursor: pointer; */
/* } */
.floater-content {
box-sizing: border-box;
padding: var(--floater-content-padding);
overflow: auto;
flex: 1 1 auto;
max-width: var(--floater-max-size-x);
min-width: var(--floater-min-size-x);
max-height: var(--floater-max-size-y);
min-height: var(--floater-min-size-y);
opacity: var(--floater-content-opacity);
}

.floater-content.resize-horizontal {
resize: horizontal;
}

.floater-content.resize-vertical {
resize: vertical;
}

.floater-content.resize-both {
resize: both;
}

/* Hide the resize handle  */
.floater-content.resize-horizontal::-webkit-resizer,
.floater-content.resize-vertical::-webkit-resizer,
.floater-content.resize-both::-webkit-resizer {
display: none;
}

.floater-content.resize-horizontal::-moz-resizer,
.floater-content.resize-vertical::-moz-resizer,
.floater-content.resize-both::-moz-resizer {
display: none;
}









test




















This is some placeholder content




Подробнее здесь: https://stackoverflow.com/questions/797 ... shadow-dom
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»