Merge remote-tracking branch 'origin/main-demo-ui' into main-dev
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useState, type RefObject } from "react";
|
import { useState, useRef, useEffect, type RefObject } from "react";
|
||||||
import { Block } from "../../../../types/exportedTypes";
|
import { Block } from "../../../../types/exportedTypes";
|
||||||
import { getAlphaFromRgba, rgbaToHex } from "../../functions/helpers/colorHandlers";
|
import { getAlphaFromRgba, rgbaToHex } from "../../functions/helpers/colorHandlers";
|
||||||
import { getCurrentBlockStyleValue } from "../../functions/helpers/getCurrentBlockStyleValue";
|
import { getCurrentBlockStyleValue } from "../../functions/helpers/getCurrentBlockStyleValue";
|
||||||
@@ -9,7 +9,7 @@ import InputRange from "../../../ui/inputs/InputRange";
|
|||||||
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
|
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
|
||||||
import { DeleteIcon } from "../../../icons/ContextMenuIcons";
|
import { DeleteIcon } from "../../../icons/ContextMenuIcons";
|
||||||
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||||
import { AddIcon, DeviceIcon, ParametersIcon } from "../../../icons/ExportCommonIcons";
|
import { AddIcon, DeviceIcon, ParametersIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons";
|
||||||
import DataDetailedDropdown from "../../../ui/inputs/DataDetailedDropdown";
|
import DataDetailedDropdown from "../../../ui/inputs/DataDetailedDropdown";
|
||||||
import RenameInput from "../../../ui/inputs/RenameInput";
|
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||||
import DataSourceSelector from "../../../ui/inputs/DataSourceSelector";
|
import DataSourceSelector from "../../../ui/inputs/DataSourceSelector";
|
||||||
@@ -38,9 +38,119 @@ const BlockEditor: React.FC<BlockEditorProps> = ({
|
|||||||
handleRemoveBlock,
|
handleRemoveBlock,
|
||||||
}) => {
|
}) => {
|
||||||
const [color, setColor] = useState("#000000");
|
const [color, setColor] = useState("#000000");
|
||||||
|
// Dragging for block editor
|
||||||
|
const panelRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [position, setPosition] = useState<{ x: number; y: number }>(() => {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
// approximate initial left using a default panel width of 300
|
||||||
|
return { x: Math.max(0, window.innerWidth - 300 - 40), y: 80 };
|
||||||
|
}
|
||||||
|
return { x: 120, y: 80 };
|
||||||
|
});
|
||||||
|
const initialPositionRef = useRef<{ x: number; y: number } | null>(null);
|
||||||
|
const draggingRef = useRef(false);
|
||||||
|
const startXRef = useRef(0);
|
||||||
|
const startYRef = useRef(0);
|
||||||
|
const startLeftRef = useRef(0);
|
||||||
|
const startTopRef = useRef(0);
|
||||||
|
|
||||||
|
// compute exact initial position once we have panel dimensions
|
||||||
|
useEffect(() => {
|
||||||
|
const panelEl = panelRef.current || (blockEditorRef && (blockEditorRef as any).current);
|
||||||
|
const width = panelEl?.offsetWidth || 300;
|
||||||
|
const nx = Math.max(0, window.innerWidth - width - 40);
|
||||||
|
const ny = 80;
|
||||||
|
initialPositionRef.current = { x: nx, y: ny };
|
||||||
|
setPosition({ x: nx, y: ny });
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
// cleanup in case
|
||||||
|
draggingRef.current = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const startDrag = (ev: React.PointerEvent) => {
|
||||||
|
|
||||||
|
if (ev.detail > 1) return;
|
||||||
|
|
||||||
|
const panel = panelRef.current || (blockEditorRef && (blockEditorRef as any).current);
|
||||||
|
if (!panel) return;
|
||||||
|
|
||||||
|
panel.setPointerCapture?.(ev.pointerId);
|
||||||
|
|
||||||
|
draggingRef.current = true;
|
||||||
|
startXRef.current = ev.clientX;
|
||||||
|
startYRef.current = ev.clientY;
|
||||||
|
startLeftRef.current = position.x;
|
||||||
|
startTopRef.current = position.y;
|
||||||
|
|
||||||
|
const onPointerMove = (e: PointerEvent) => {
|
||||||
|
if (!draggingRef.current) return;
|
||||||
|
const dx = e.clientX - startXRef.current;
|
||||||
|
const dy = e.clientY - startYRef.current;
|
||||||
|
|
||||||
|
const panelEl = panelRef.current || (blockEditorRef && (blockEditorRef as any).current);
|
||||||
|
const width = panelEl?.offsetWidth || 300;
|
||||||
|
const height = panelEl?.offsetHeight || 300;
|
||||||
|
|
||||||
|
const maxX = window.innerWidth - width - 8;
|
||||||
|
const maxY = window.innerHeight - height - 8;
|
||||||
|
|
||||||
|
let nx = startLeftRef.current + dx;
|
||||||
|
let ny = startTopRef.current + dy;
|
||||||
|
|
||||||
|
nx = Math.max(0, Math.min(nx, maxX));
|
||||||
|
ny = Math.max(0, Math.min(ny, maxY));
|
||||||
|
|
||||||
|
setPosition({ x: nx, y: ny });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPointerUp = (e: PointerEvent) => {
|
||||||
|
draggingRef.current = false;
|
||||||
|
window.removeEventListener("pointermove", onPointerMove);
|
||||||
|
window.removeEventListener("pointerup", onPointerUp);
|
||||||
|
panel.releasePointerCapture?.(e.pointerId);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("pointermove", onPointerMove);
|
||||||
|
window.addEventListener("pointerup", onPointerUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetPosition = () => {
|
||||||
|
|
||||||
|
console.log("adsasdasdadasd");
|
||||||
|
if (initialPositionRef.current) {
|
||||||
|
setPosition(initialPositionRef.current);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const panelEl = panelRef.current || (blockEditorRef && (blockEditorRef as any).current);
|
||||||
|
const width = panelEl?.offsetWidth || 300;
|
||||||
|
const nx = Math.max(0, window.innerWidth - width - 40);
|
||||||
|
setPosition({ x: nx, y: 80 });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={blockEditorRef} className="panel block-editor-panel">
|
<div
|
||||||
|
ref={(el) => {
|
||||||
|
panelRef.current = el;
|
||||||
|
if (blockEditorRef && typeof blockEditorRef === "object" && "current" in blockEditorRef) {
|
||||||
|
(blockEditorRef as any).current = el;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="panel block-editor-panel"
|
||||||
|
style={{ position: "fixed", left: position.x, top: position.y, zIndex: 999 }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="resize-icon"
|
||||||
|
onDoubleClick={resetPosition}
|
||||||
|
onPointerDown={startDrag}
|
||||||
|
>
|
||||||
|
<ResizeHeightIcon />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h4>Block Style</h4>
|
<h4>Block Style</h4>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
|||||||
setShowSwapUI,
|
setShowSwapUI,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
|
||||||
{element?.type === "graph" && (
|
{element?.type === "graph" && (
|
||||||
<div className="design-section">
|
<div className="design-section">
|
||||||
<DataSourceSelector
|
<DataSourceSelector
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useCallback, useMemo, useState, type RefObject } from "react";
|
import { useCallback, useMemo, useState, useRef, useEffect, type RefObject } from "react";
|
||||||
import { ExtendedCSSProperties, UIElement } from "../../../../types/exportedTypes";
|
import { ExtendedCSSProperties, UIElement } from "../../../../types/exportedTypes";
|
||||||
import { DeleteIcon } from "../../../icons/ContextMenuIcons";
|
import { DeleteIcon } from "../../../icons/ContextMenuIcons";
|
||||||
import DataSourceSelector from "../../../ui/inputs/DataSourceSelector";
|
import DataSourceSelector from "../../../ui/inputs/DataSourceSelector";
|
||||||
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||||
import InputRange from "../../../ui/inputs/InputRange";
|
import InputRange from "../../../ui/inputs/InputRange";
|
||||||
import RenameInput from "../../../ui/inputs/RenameInput";
|
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||||
import { AddIcon, DeviceIcon, ParametersIcon } from "../../../icons/ExportCommonIcons";
|
import { AddIcon, DeviceIcon, ParametersIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons";
|
||||||
import DataDetailedDropdown from "../../../ui/inputs/DataDetailedDropdown";
|
import DataDetailedDropdown from "../../../ui/inputs/DataDetailedDropdown";
|
||||||
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
||||||
import ElementDesign from "./ElementDesign";
|
import ElementDesign from "./ElementDesign";
|
||||||
@@ -64,6 +64,117 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
|||||||
const [selectType, setSelectType] = useState("design");
|
const [selectType, setSelectType] = useState("design");
|
||||||
const [selectDataMapping, setSelectDataMapping] = useState(element?.type === "graph" && element.dataBinding?.dataType === "multiple-machine" ? "multipleMachine" : "singleMachine");
|
const [selectDataMapping, setSelectDataMapping] = useState(element?.type === "graph" && element.dataBinding?.dataType === "multiple-machine" ? "multipleMachine" : "singleMachine");
|
||||||
|
|
||||||
|
// Dragging state for the panel
|
||||||
|
const panelRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [position, setPosition] = useState<{ x: number; y: number }>(() => {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
// approximate initial left using a default panel width of 300
|
||||||
|
return { x: Math.max(0, window.innerWidth - 300 - 40), y: 80 };
|
||||||
|
}
|
||||||
|
return { x: 100, y: 80 };
|
||||||
|
});
|
||||||
|
const initialPositionRef = useRef<{ x: number; y: number } | null>(null);
|
||||||
|
|
||||||
|
// On mount, compute exact initial position based on panel width and store it
|
||||||
|
useEffect(() => {
|
||||||
|
const panelEl = panelRef.current || (elementEditorRef && (elementEditorRef as any).current);
|
||||||
|
const width = panelEl?.offsetWidth || 300;
|
||||||
|
const nx = Math.max(0, window.innerWidth - width - 40);
|
||||||
|
const ny = 80;
|
||||||
|
initialPositionRef.current = { x: nx, y: ny };
|
||||||
|
setPosition({ x: nx, y: ny });
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
const draggingRef = useRef(false);
|
||||||
|
const startXRef = useRef(0);
|
||||||
|
const startYRef = useRef(0);
|
||||||
|
const startLeftRef = useRef(0);
|
||||||
|
const startTopRef = useRef(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onPointerMove = (ev: PointerEvent) => {
|
||||||
|
if (!draggingRef.current) return;
|
||||||
|
const dx = ev.clientX - startXRef.current;
|
||||||
|
const dy = ev.clientY - startYRef.current;
|
||||||
|
const panel = panelRef.current;
|
||||||
|
const width = panel?.offsetWidth || 300;
|
||||||
|
const height = panel?.offsetHeight || 300;
|
||||||
|
const maxX = window.innerWidth - width - 8;
|
||||||
|
const maxY = window.innerHeight - height - 8;
|
||||||
|
let nx = startLeftRef.current + dx;
|
||||||
|
let ny = startTopRef.current + dy;
|
||||||
|
if (nx < 0) nx = 0;
|
||||||
|
if (ny < 0) ny = 0;
|
||||||
|
if (nx > maxX) nx = maxX;
|
||||||
|
if (ny > maxY) ny = maxY;
|
||||||
|
setPosition({ x: nx, y: ny });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPointerUp = () => {
|
||||||
|
draggingRef.current = false;
|
||||||
|
window.removeEventListener("pointermove", onPointerMove);
|
||||||
|
window.removeEventListener("pointerup", onPointerUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("pointermove", onPointerMove);
|
||||||
|
window.removeEventListener("pointerup", onPointerUp);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const startDrag = (ev: React.PointerEvent) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
const panel = panelRef.current || (elementEditorRef && (elementEditorRef as any).current);
|
||||||
|
if (!panel) return;
|
||||||
|
panel.setPointerCapture?.(ev.pointerId);
|
||||||
|
draggingRef.current = true;
|
||||||
|
startXRef.current = ev.clientX;
|
||||||
|
startYRef.current = ev.clientY;
|
||||||
|
startLeftRef.current = position.x;
|
||||||
|
startTopRef.current = position.y;
|
||||||
|
|
||||||
|
const onPointerMove = (e: PointerEvent) => {
|
||||||
|
if (!draggingRef.current) return;
|
||||||
|
const dx = e.clientX - startXRef.current;
|
||||||
|
const dy = e.clientY - startYRef.current;
|
||||||
|
const panelEl = panelRef.current || (elementEditorRef && (elementEditorRef as any).current);
|
||||||
|
const width = panelEl?.offsetWidth || 300;
|
||||||
|
const height = panelEl?.offsetHeight || 300;
|
||||||
|
const maxX = window.innerWidth - width - 8;
|
||||||
|
const maxY = window.innerHeight - height - 8;
|
||||||
|
let nx = startLeftRef.current + dx;
|
||||||
|
let ny = startTopRef.current + dy;
|
||||||
|
if (nx < 0) nx = 0;
|
||||||
|
if (ny < 0) ny = 0;
|
||||||
|
if (nx > maxX) nx = maxX;
|
||||||
|
if (ny > maxY) ny = maxY;
|
||||||
|
setPosition({ x: nx, y: ny });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPointerUp = (e: PointerEvent) => {
|
||||||
|
draggingRef.current = false;
|
||||||
|
window.removeEventListener("pointermove", onPointerMove);
|
||||||
|
window.removeEventListener("pointerup", onPointerUp);
|
||||||
|
try {
|
||||||
|
(panel as Element).releasePointerCapture?.((e as any).pointerId);
|
||||||
|
} catch (err) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("pointermove", onPointerMove);
|
||||||
|
window.addEventListener("pointerup", onPointerUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetPosition = () => {
|
||||||
|
if (initialPositionRef.current) {
|
||||||
|
setPosition(initialPositionRef.current);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const panelEl = panelRef.current || (elementEditorRef && (elementEditorRef as any).current);
|
||||||
|
const width = panelEl?.offsetWidth || 300;
|
||||||
|
const nx = Math.max(0, window.innerWidth - width - 40);
|
||||||
|
setPosition({ x: nx, y: 80 });
|
||||||
|
};
|
||||||
|
|
||||||
const getAssetDropdownItems = useCallback(() => {
|
const getAssetDropdownItems = useCallback(() => {
|
||||||
if (!product?.eventDatas) return [];
|
if (!product?.eventDatas) return [];
|
||||||
|
|
||||||
@@ -348,7 +459,19 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={elementEditorRef} className="panel element-editor-panel">
|
<div
|
||||||
|
ref={(el) => {
|
||||||
|
panelRef.current = el;
|
||||||
|
if (elementEditorRef && typeof elementEditorRef === "object" && "current" in elementEditorRef) {
|
||||||
|
(elementEditorRef as any).current = el;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="panel element-editor-panel"
|
||||||
|
style={{ position: "fixed", left: position.x, top: position.y, zIndex: 1000 }}
|
||||||
|
>
|
||||||
|
<div className="resize-icon" onPointerDown={startDrag} onDoubleClick={resetPosition}>
|
||||||
|
<ResizeHeightIcon />
|
||||||
|
</div>
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h4>Element Style</h4>
|
<h4>Element Style</h4>
|
||||||
<div
|
<div
|
||||||
@@ -410,6 +533,7 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
|||||||
updateElementData(selectedBlock, selectedElement, { label: value });
|
updateElementData(selectedBlock, selectedElement, { label: value });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<InputWithDropDown label="Title" value={`title`} placeholder={"Label 1"} min={0.1} step={0.1} max={2} onChange={() => { }} />
|
||||||
<div className="data">
|
<div className="data">
|
||||||
<DataDetailedDropdown
|
<DataDetailedDropdown
|
||||||
title="Data Source"
|
title="Data Source"
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const DataDetailedDropdown: React.FC<DataDetailedDropdownProps> = ({
|
|||||||
<div className="input-container">
|
<div className="input-container">
|
||||||
<div className="input-wrapper">
|
<div className="input-wrapper">
|
||||||
<div className="input" onClick={() => setOpen((v) => !v)}>
|
<div className="input" onClick={() => setOpen((v) => !v)}>
|
||||||
{value?.label || placeholder}
|
<div className="key">{value?.label || placeholder}</div>
|
||||||
<div className="icon">▾</div>
|
<div className="icon">▾</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -92,13 +92,7 @@ const RegularDropDown: React.FC<DropdownProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="regularDropdown-container" ref={dropdownRef}>
|
||||||
className="regularDropdown-container"
|
|
||||||
ref={dropdownRef}
|
|
||||||
onPointerLeave={() => {
|
|
||||||
setIsOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
|
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
|
||||||
<div className="key">{selectedOption || header}</div>
|
<div className="key">{selectedOption || header}</div>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const RegularDropDownID: React.FC<DropdownProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="regularDropdown-container" ref={dropdownRef} onPointerLeave={() => setIsOpen(false)}>
|
<div className="regularDropdown-container" ref={dropdownRef}>
|
||||||
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
|
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
|
||||||
<div className="key">{selectedOption || header}</div>
|
<div className="key">{selectedOption || header}</div>
|
||||||
<div className="icon">▾</div>
|
<div className="icon">▾</div>
|
||||||
|
|||||||
@@ -225,6 +225,9 @@ input[type="number"] {
|
|||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
.key{
|
||||||
|
text-overflow: none;
|
||||||
|
}
|
||||||
.dropdown-header {
|
.dropdown-header {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -260,7 +263,7 @@ input[type="number"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.option {
|
.option {
|
||||||
padding: 2px 4px;
|
padding: 6px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
flex-direction: row !important;
|
flex-direction: row !important;
|
||||||
border-radius: #{$border-radius-medium};
|
border-radius: #{$border-radius-medium};
|
||||||
|
|||||||
@@ -282,6 +282,15 @@
|
|||||||
min-height: 60vh;
|
min-height: 60vh;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
|
||||||
|
.resize-icon {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 100;
|
||||||
|
svg {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -556,6 +565,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-details {
|
.data-details {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -683,6 +693,9 @@
|
|||||||
|
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 80px;
|
||||||
|
right: 40px;
|
||||||
.appearance {
|
.appearance {
|
||||||
.design-datas-wrapper {
|
.design-datas-wrapper {
|
||||||
|
|
||||||
@@ -930,7 +943,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.input-container {
|
.input-container {
|
||||||
width: 100%;
|
min-width: 100px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
@@ -940,6 +953,12 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.key {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
|||||||
Reference in New Issue
Block a user