fix(input): 重构消息队列动画 — 去掉容器外框、纯位移FLIP同步动画
This commit is contained in:
parent
d639278182
commit
84336b1802
@ -1143,20 +1143,16 @@ const RUNTIME_QUEUE_ANIM_EASING = 'cubic-bezier(0.25, 0.8, 0.25, 1)';
|
|||||||
const runtimeQueueList = ref<HTMLElement | null>(null);
|
const runtimeQueueList = ref<HTMLElement | null>(null);
|
||||||
const runtimeQueueItemRefs = new Map<string, HTMLElement>();
|
const runtimeQueueItemRefs = new Map<string, HTMLElement>();
|
||||||
const runtimeQueuePrevIds: string[] = [];
|
const runtimeQueuePrevIds: string[] = [];
|
||||||
const runtimeQueueRenderedIds: string[] = [];
|
|
||||||
const runtimeQueuePrevRects = new Map<string, DOMRect>();
|
const runtimeQueuePrevRects = new Map<string, DOMRect>();
|
||||||
const runtimeQueuePrevElements = new Map<string, HTMLElement>();
|
const runtimeQueuePrevElements = new Map<string, HTMLElement>();
|
||||||
const runtimeQueueGhosts = new Map<string, HTMLElement>();
|
const runtimeQueueGhosts = new Map<string, HTMLElement>();
|
||||||
const runtimeQueueActiveAnimations = new WeakMap<HTMLElement, Animation>();
|
|
||||||
const runtimeQueueFallbackTimers = new WeakMap<HTMLElement, number>();
|
const runtimeQueueFallbackTimers = new WeakMap<HTMLElement, number>();
|
||||||
const runtimeQueueTransitioning = ref(false);
|
const runtimeQueueTransitioning = ref(false);
|
||||||
let runtimeQueueAnimationReady = false;
|
let runtimeQueueAnimationReady = false;
|
||||||
let runtimeQueueTransitionTimer: number | null = null;
|
let runtimeQueueTransitionTimer: number | null = null;
|
||||||
|
|
||||||
const bindRuntimeQueueItemRef = (id: string) => (el: Element | null) => {
|
const bindRuntimeQueueItemRef = (id: string) => (el: Element | null) => {
|
||||||
if (!id) {
|
if (!id) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (el instanceof HTMLElement) {
|
if (el instanceof HTMLElement) {
|
||||||
runtimeQueueItemRefs.set(id, el);
|
runtimeQueueItemRefs.set(id, el);
|
||||||
} else {
|
} else {
|
||||||
@ -1164,31 +1160,21 @@ const bindRuntimeQueueItemRef = (id: string) => (el: Element | null) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopRuntimeQueueAnimation = (el: HTMLElement) => {
|
const resolveRuntimeQueueOffset = (el: HTMLElement): number => {
|
||||||
const anim = runtimeQueueActiveAnimations.get(el);
|
const rectHeight = Math.round(el.getBoundingClientRect().height);
|
||||||
if (anim) {
|
if (rectHeight > 0) return rectHeight;
|
||||||
try {
|
if (el.offsetHeight > 0) return el.offsetHeight;
|
||||||
anim.cancel();
|
return 34;
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
runtimeQueueActiveAnimations.delete(el);
|
|
||||||
}
|
|
||||||
const timer = runtimeQueueFallbackTimers.get(el);
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer);
|
|
||||||
runtimeQueueFallbackTimers.delete(el);
|
|
||||||
}
|
|
||||||
el.style.removeProperty('transition');
|
|
||||||
el.style.removeProperty('transform');
|
|
||||||
el.style.removeProperty('pointer-events');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const snapshotRuntimeQueueIds = (): string[] =>
|
||||||
|
runtimeQueuedMessagesForRender.value
|
||||||
|
.map((entry) => String(entry?.id || '').trim())
|
||||||
|
.filter((id) => !!id);
|
||||||
|
|
||||||
const keepRuntimeQueueTransitionCollapsed = () => {
|
const keepRuntimeQueueTransitionCollapsed = () => {
|
||||||
runtimeQueueTransitioning.value = true;
|
runtimeQueueTransitioning.value = true;
|
||||||
if (runtimeQueueTransitionTimer !== null) {
|
if (runtimeQueueTransitionTimer !== null) window.clearTimeout(runtimeQueueTransitionTimer);
|
||||||
window.clearTimeout(runtimeQueueTransitionTimer);
|
|
||||||
}
|
|
||||||
runtimeQueueTransitionTimer = window.setTimeout(() => {
|
runtimeQueueTransitionTimer = window.setTimeout(() => {
|
||||||
runtimeQueueTransitioning.value = false;
|
runtimeQueueTransitioning.value = false;
|
||||||
runtimeQueueTransitionTimer = null;
|
runtimeQueueTransitionTimer = null;
|
||||||
@ -1196,197 +1182,158 @@ const keepRuntimeQueueTransitionCollapsed = () => {
|
|||||||
}, RUNTIME_QUEUE_ANIM_DURATION + 40);
|
}, RUNTIME_QUEUE_ANIM_DURATION + 40);
|
||||||
};
|
};
|
||||||
|
|
||||||
const playRuntimeQueueTransform = (
|
const cancelAnim = (el: HTMLElement) => {
|
||||||
|
const timer = runtimeQueueFallbackTimers.get(el);
|
||||||
|
if (timer) { clearTimeout(timer); runtimeQueueFallbackTimers.delete(el); }
|
||||||
|
el.style.removeProperty('transition');
|
||||||
|
el.style.removeProperty('transform');
|
||||||
|
el.style.removeProperty('opacity');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a CSS transition on an element.
|
||||||
|
* All calls in the same synchronous batch share one RAF callback,
|
||||||
|
* so all animations start in the same frame.
|
||||||
|
*/
|
||||||
|
const scheduleTransition = (
|
||||||
el: HTMLElement,
|
el: HTMLElement,
|
||||||
fromY: number,
|
props: string[],
|
||||||
toY: number,
|
from: Record<string, string>,
|
||||||
|
to: Record<string, string>,
|
||||||
done?: () => void
|
done?: () => void
|
||||||
) => {
|
) => {
|
||||||
if (Math.abs(fromY - toY) < 0.5) {
|
|
||||||
stopRuntimeQueueAnimation(el);
|
|
||||||
el.style.removeProperty('transition');
|
|
||||||
el.style.removeProperty('transform');
|
|
||||||
done?.();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
keepRuntimeQueueTransitionCollapsed();
|
|
||||||
stopRuntimeQueueAnimation(el);
|
|
||||||
const from = `translateY(${fromY}px)`;
|
|
||||||
const to = `translateY(${toY}px)`;
|
|
||||||
const finish = () => {
|
const finish = () => {
|
||||||
el.style.removeProperty('transition');
|
el.style.removeProperty('transition');
|
||||||
el.style.removeProperty('transform');
|
props.forEach((p) => el.style.removeProperty(p));
|
||||||
done?.();
|
done?.();
|
||||||
};
|
};
|
||||||
|
// Apply from state immediately (no transition)
|
||||||
el.style.transition = 'none';
|
el.style.transition = 'none';
|
||||||
el.style.transform = from;
|
Object.entries(from).forEach(([k, v]) => { el.style.setProperty(k, v); });
|
||||||
|
// Defer to next frame: enable transition + apply to state
|
||||||
if (typeof el.animate === 'function') {
|
// All scheduleTransition calls in the same microtask share this RAF
|
||||||
// 强制应用起始位移,避免首帧先闪现到终点
|
|
||||||
void el.offsetHeight;
|
|
||||||
const animation = el.animate([{ transform: from }, { transform: to }], {
|
|
||||||
duration: RUNTIME_QUEUE_ANIM_DURATION,
|
|
||||||
easing: RUNTIME_QUEUE_ANIM_EASING,
|
|
||||||
fill: 'both'
|
|
||||||
});
|
|
||||||
runtimeQueueActiveAnimations.set(el, animation);
|
|
||||||
animation.addEventListener(
|
|
||||||
'finish',
|
|
||||||
() => {
|
|
||||||
if (runtimeQueueActiveAnimations.get(el) === animation) {
|
|
||||||
runtimeQueueActiveAnimations.delete(el);
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
},
|
|
||||||
{ once: true }
|
|
||||||
);
|
|
||||||
animation.addEventListener(
|
|
||||||
'cancel',
|
|
||||||
() => {
|
|
||||||
if (runtimeQueueActiveAnimations.get(el) === animation) {
|
|
||||||
runtimeQueueActiveAnimations.delete(el);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ once: true }
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
el.style.transition = `transform ${RUNTIME_QUEUE_ANIM_DURATION}ms ${RUNTIME_QUEUE_ANIM_EASING}`;
|
el.style.transition = props
|
||||||
el.style.transform = to;
|
.map((p) => `${p} ${RUNTIME_QUEUE_ANIM_DURATION}ms ${RUNTIME_QUEUE_ANIM_EASING}`)
|
||||||
const timer = window.setTimeout(() => {
|
.join(', ');
|
||||||
runtimeQueueFallbackTimers.delete(el);
|
Object.entries(to).forEach(([k, v]) => { el.style.setProperty(k, v); });
|
||||||
finish();
|
const timer = window.setTimeout(finish, RUNTIME_QUEUE_ANIM_DURATION + 40);
|
||||||
}, RUNTIME_QUEUE_ANIM_DURATION + 24);
|
|
||||||
runtimeQueueFallbackTimers.set(el, timer);
|
runtimeQueueFallbackTimers.set(el, timer);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveRuntimeQueueOffset = (el: HTMLElement): number => {
|
/* ═══════════════════ FLIP 动画 ═══════════════════ */
|
||||||
const rectHeight = Math.round(el.getBoundingClientRect().height);
|
|
||||||
if (rectHeight > 0) {
|
|
||||||
return rectHeight;
|
|
||||||
}
|
|
||||||
if (el.offsetHeight > 0) {
|
|
||||||
return el.offsetHeight;
|
|
||||||
}
|
|
||||||
return 34;
|
|
||||||
};
|
|
||||||
|
|
||||||
const snapshotRuntimeQueueIds = (): string[] => {
|
// 上一轮渲染的 ID(onBeforeUpdate 时数据已更新,必须用这个记录旧位置)
|
||||||
return runtimeQueuedMessagesForRender.value
|
let runtimeQueueLastRenderedIds: string[] = [];
|
||||||
.map((entry) => String(entry?.id || '').trim())
|
|
||||||
.filter((id) => !!id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const spawnRuntimeQueueLeaveGhost = (id: string) => {
|
|
||||||
const sourceEl = runtimeQueuePrevElements.get(id);
|
|
||||||
const sourceRect = runtimeQueuePrevRects.get(id);
|
|
||||||
const listEl = runtimeQueueList.value;
|
|
||||||
if (!sourceEl || !sourceRect || !listEl || !listEl.isConnected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingGhost = runtimeQueueGhosts.get(id);
|
|
||||||
if (existingGhost) {
|
|
||||||
stopRuntimeQueueAnimation(existingGhost);
|
|
||||||
existingGhost.remove();
|
|
||||||
runtimeQueueGhosts.delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ghost = sourceEl.cloneNode(true) as HTMLElement;
|
|
||||||
const listRect = listEl.getBoundingClientRect();
|
|
||||||
const top = sourceRect.top - listRect.top;
|
|
||||||
const width = sourceRect.width > 0 ? sourceRect.width : listRect.width;
|
|
||||||
|
|
||||||
ghost.style.position = 'absolute';
|
|
||||||
ghost.style.left = '0';
|
|
||||||
ghost.style.top = `${Math.round(top)}px`;
|
|
||||||
ghost.style.width = `${Math.round(width)}px`;
|
|
||||||
ghost.style.pointerEvents = 'none';
|
|
||||||
ghost.style.zIndex = '0';
|
|
||||||
ghost.style.margin = '0';
|
|
||||||
ghost.querySelectorAll('button').forEach((button) => {
|
|
||||||
if (button instanceof HTMLButtonElement) {
|
|
||||||
button.disabled = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
listEl.appendChild(ghost);
|
|
||||||
runtimeQueueGhosts.set(id, ghost);
|
|
||||||
|
|
||||||
const offset = resolveRuntimeQueueOffset(ghost);
|
|
||||||
playRuntimeQueueTransform(ghost, 0, offset, () => {
|
|
||||||
runtimeQueueGhosts.delete(id);
|
|
||||||
ghost.remove();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const animateRuntimeQueueEnter = (el: HTMLElement) => {
|
|
||||||
const offset = resolveRuntimeQueueOffset(el);
|
|
||||||
playRuntimeQueueTransform(el, offset, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
onBeforeUpdate(() => {
|
onBeforeUpdate(() => {
|
||||||
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length, ...runtimeQueueRenderedIds);
|
// 用上一轮渲染的 ID 记录旧位置(此时数据已更新,snapshotRuntimeQueueIds() 是新数据)
|
||||||
|
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length, ...runtimeQueueLastRenderedIds);
|
||||||
runtimeQueuePrevRects.clear();
|
runtimeQueuePrevRects.clear();
|
||||||
runtimeQueuePrevElements.clear();
|
runtimeQueuePrevElements.clear();
|
||||||
|
runtimeQueueLastRenderedIds.forEach((id) => {
|
||||||
runtimeQueueRenderedIds.forEach((id) => {
|
|
||||||
const el = runtimeQueueItemRefs.get(id);
|
const el = runtimeQueueItemRefs.get(id);
|
||||||
if (!el?.isConnected) {
|
if (!el?.isConnected) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
runtimeQueuePrevRects.set(id, el.getBoundingClientRect());
|
runtimeQueuePrevRects.set(id, el.getBoundingClientRect());
|
||||||
runtimeQueuePrevElements.set(id, el);
|
runtimeQueuePrevElements.set(id, el);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUpdated(() => {
|
onUpdated(() => {
|
||||||
|
const list = runtimeQueueList.value;
|
||||||
const currentIds = snapshotRuntimeQueueIds();
|
const currentIds = snapshotRuntimeQueueIds();
|
||||||
|
|
||||||
|
// First render: just bootstrap tracking state
|
||||||
if (!runtimeQueueAnimationReady) {
|
if (!runtimeQueueAnimationReady) {
|
||||||
runtimeQueueRenderedIds.splice(0, runtimeQueueRenderedIds.length, ...currentIds);
|
|
||||||
runtimeQueueAnimationReady = true;
|
runtimeQueueAnimationReady = true;
|
||||||
|
runtimeQueueLastRenderedIds = [...currentIds];
|
||||||
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
|
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
|
||||||
runtimeQueuePrevRects.clear();
|
runtimeQueuePrevRects.clear();
|
||||||
runtimeQueuePrevElements.clear();
|
runtimeQueuePrevElements.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keepRuntimeQueueTransitionCollapsed();
|
||||||
|
|
||||||
const previousIdSet = new Set(runtimeQueuePrevIds);
|
const previousIdSet = new Set(runtimeQueuePrevIds);
|
||||||
const currentIdSet = new Set(currentIds);
|
const currentIdSet = new Set(currentIds);
|
||||||
const enteringIdSet = new Set(currentIds.filter((id) => !previousIdSet.has(id)));
|
const enteringIds = currentIds.filter((id) => !previousIdSet.has(id));
|
||||||
const leavingIds = runtimeQueuePrevIds.filter((id) => !currentIdSet.has(id));
|
const leavingIds = runtimeQueuePrevIds.filter((id) => !currentIdSet.has(id));
|
||||||
|
|
||||||
|
if (!enteringIds.length && !leavingIds.length) return;
|
||||||
|
|
||||||
|
// ── Leave: create ghost clones at old positions ──
|
||||||
leavingIds.forEach((id) => {
|
leavingIds.forEach((id) => {
|
||||||
spawnRuntimeQueueLeaveGhost(id);
|
const src = runtimeQueuePrevElements.get(id);
|
||||||
|
const rect = runtimeQueuePrevRects.get(id);
|
||||||
|
if (!src || !rect || !list?.isConnected) return;
|
||||||
|
cancelAnim(src);
|
||||||
|
|
||||||
|
// Clean up any stale ghost with same id
|
||||||
|
const existing = runtimeQueueGhosts.get(id);
|
||||||
|
if (existing) { cancelAnim(existing); existing.remove(); runtimeQueueGhosts.delete(id); }
|
||||||
|
|
||||||
|
const ghost = src.cloneNode(true) as HTMLElement;
|
||||||
|
const lr = list.getBoundingClientRect();
|
||||||
|
ghost.style.position = 'absolute';
|
||||||
|
ghost.style.left = '0';
|
||||||
|
ghost.style.top = `${Math.round(rect.top - lr.top)}px`;
|
||||||
|
ghost.style.width = `${Math.round(rect.width > 0 ? rect.width : lr.width)}px`;
|
||||||
|
ghost.style.pointerEvents = 'none';
|
||||||
|
ghost.style.zIndex = '0';
|
||||||
|
ghost.style.margin = '0';
|
||||||
|
ghost.style.opacity = '1';
|
||||||
|
ghost.querySelectorAll('button').forEach((b) => {
|
||||||
|
if (b instanceof HTMLButtonElement) b.disabled = true;
|
||||||
|
});
|
||||||
|
list.appendChild(ghost);
|
||||||
|
runtimeQueueGhosts.set(id, ghost);
|
||||||
|
|
||||||
|
// Schedule ghost sink — shares RAF with item animations below
|
||||||
|
const offset = resolveRuntimeQueueOffset(ghost);
|
||||||
|
scheduleTransition(ghost, ['transform'],
|
||||||
|
{ transform: 'translateY(0)' },
|
||||||
|
{ transform: `translateY(${offset}px)` },
|
||||||
|
() => { runtimeQueueGhosts.delete(id); ghost.remove(); }
|
||||||
|
);
|
||||||
|
|
||||||
runtimeQueueItemRefs.delete(id);
|
runtimeQueueItemRefs.delete(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Remaining items: apply FLIP transforms ──
|
||||||
currentIds.forEach((id) => {
|
currentIds.forEach((id) => {
|
||||||
const el = runtimeQueueItemRefs.get(id);
|
const el = runtimeQueueItemRefs.get(id);
|
||||||
if (!el?.isConnected) {
|
if (!el?.isConnected) return;
|
||||||
|
cancelAnim(el);
|
||||||
|
|
||||||
|
if (enteringIds.includes(id)) {
|
||||||
|
// New item: rise from below
|
||||||
|
const offset = resolveRuntimeQueueOffset(el);
|
||||||
|
scheduleTransition(el, ['transform'],
|
||||||
|
{ transform: `translateY(${offset}px)` },
|
||||||
|
{ transform: 'translateY(0)' }
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const prevRect = runtimeQueuePrevRects.get(id);
|
|
||||||
if (!prevRect || enteringIdSet.has(id)) {
|
// Existing item: invert the position delta
|
||||||
animateRuntimeQueueEnter(el);
|
const prev = runtimeQueuePrevRects.get(id);
|
||||||
return;
|
if (!prev) return;
|
||||||
}
|
const next = el.getBoundingClientRect();
|
||||||
const nextRect = el.getBoundingClientRect();
|
const deltaY = prev.top - next.top;
|
||||||
const deltaY = prevRect.top - nextRect.top;
|
if (Math.abs(deltaY) < 0.5) return;
|
||||||
if (Math.abs(deltaY) < 0.5) {
|
scheduleTransition(el, ['transform'],
|
||||||
return;
|
{ transform: `translateY(${deltaY}px)` },
|
||||||
}
|
{ transform: 'translateY(0)' }
|
||||||
playRuntimeQueueTransform(el, deltaY, 0);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
runtimeQueueRenderedIds.splice(0, runtimeQueueRenderedIds.length, ...currentIds);
|
|
||||||
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
|
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
|
||||||
runtimeQueuePrevRects.clear();
|
runtimeQueuePrevRects.clear();
|
||||||
runtimeQueuePrevElements.clear();
|
runtimeQueuePrevElements.clear();
|
||||||
|
// 保存本轮 ID 供下个 onBeforeUpdate 使用
|
||||||
|
runtimeQueueLastRenderedIds = [...currentIds];
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasComposerContent = computed(() => {
|
const hasComposerContent = computed(() => {
|
||||||
@ -1839,20 +1786,20 @@ onBeforeUnmount(() => {
|
|||||||
editor.value?.destroy();
|
editor.value?.destroy();
|
||||||
runtimeQueueItemRefs.forEach((el) => {
|
runtimeQueueItemRefs.forEach((el) => {
|
||||||
if (el) {
|
if (el) {
|
||||||
stopRuntimeQueueAnimation(el);
|
cancelAnim(el);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
runtimeQueueGhosts.forEach((ghost) => {
|
runtimeQueueGhosts.forEach((ghost) => {
|
||||||
stopRuntimeQueueAnimation(ghost);
|
cancelAnim(ghost);
|
||||||
ghost.remove();
|
ghost.remove();
|
||||||
});
|
});
|
||||||
runtimeQueueGhosts.clear();
|
runtimeQueueGhosts.clear();
|
||||||
runtimeQueueItemRefs.clear();
|
runtimeQueueItemRefs.clear();
|
||||||
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
|
runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
|
||||||
runtimeQueueRenderedIds.splice(0, runtimeQueueRenderedIds.length);
|
|
||||||
runtimeQueuePrevRects.clear();
|
runtimeQueuePrevRects.clear();
|
||||||
runtimeQueuePrevElements.clear();
|
runtimeQueuePrevElements.clear();
|
||||||
runtimeQueueAnimationReady = false;
|
runtimeQueueAnimationReady = false;
|
||||||
|
runtimeQueueLastRenderedIds = [];
|
||||||
if (runtimeQueueTransitionTimer !== null) {
|
if (runtimeQueueTransitionTimer !== null) {
|
||||||
window.clearTimeout(runtimeQueueTransitionTimer);
|
window.clearTimeout(runtimeQueueTransitionTimer);
|
||||||
runtimeQueueTransitionTimer = null;
|
runtimeQueueTransitionTimer = null;
|
||||||
|
|||||||
@ -39,13 +39,9 @@
|
|||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
border-top: 1px solid var(--runtime-queue-border);
|
|
||||||
border-right: 1px solid var(--runtime-queue-border);
|
|
||||||
border-bottom: none;
|
|
||||||
border-left: 1px solid var(--runtime-queue-border);
|
|
||||||
border-top-left-radius: 12px;
|
|
||||||
border-top-right-radius: 12px;
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
@ -70,7 +66,10 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 6px 10px 6px 12px;
|
padding: 6px 10px 6px 12px;
|
||||||
border: none;
|
border-left: 1px solid var(--runtime-queue-border);
|
||||||
|
border-right: 1px solid var(--runtime-queue-border);
|
||||||
|
border-top: 1px solid var(--runtime-queue-border);
|
||||||
|
border-bottom: none;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
background: var(--runtime-queue-bg);
|
background: var(--runtime-queue-bg);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user