Merge branch 'v2-ui' of http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev into v2-ui
This commit is contained in:
@@ -12,140 +12,198 @@ import Widget4InputCard3D from './Widget4InputCard3D'
|
|||||||
import WarehouseThroughputInputComponent from './WarehouseThroughputInputComponent'
|
import WarehouseThroughputInputComponent from './WarehouseThroughputInputComponent'
|
||||||
import { useWidgetStore } from '../../../../../store/useWidgetStore'
|
import { useWidgetStore } from '../../../../../store/useWidgetStore'
|
||||||
|
|
||||||
|
// const InputSelecterComponent = () => {
|
||||||
|
// const { selectedChartId } = useWidgetStore();
|
||||||
|
|
||||||
|
// if (selectedChartId && selectedChartId.type && selectedChartId.type === 'bar' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">2D Widget Input</div>
|
||||||
|
// <BarChartInput />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'line' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">2D Widget Input</div>
|
||||||
|
// <LineGrapInput />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'pie' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">2D Widget Input</div>
|
||||||
|
// <PieChartInput />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'doughnut' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">2D Widget Input</div>
|
||||||
|
// <PieChartInput />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'polarArea' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">2D Widget Input</div>
|
||||||
|
// <PieChartInput />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'progress 1' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">2D Widget Input</div>
|
||||||
|
// <Progress1Input />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'progress 2' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">2D Widget Input</div>
|
||||||
|
// <Progress2Input />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'warehouseThroughput floating' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">Floting Widget Input</div>
|
||||||
|
// <WarehouseThroughputInputComponent />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'fleetEfficiency floating' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">Floting Widget Input</div>
|
||||||
|
// <FleetEfficiencyInputComponent />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'floating total-card' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">Floting Widget Input</div>
|
||||||
|
// <FleetEfficiencyInputComponent />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 1' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">3D Widget Input</div>
|
||||||
|
// <Widget4InputCard3D />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 2' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">3D Widget Input</div>
|
||||||
|
// <Widget2InputCard3D />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 3' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">3D Widget Input</div>
|
||||||
|
// <Widget3InputCard3D />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 4' ) {
|
||||||
|
// return (
|
||||||
|
// <>
|
||||||
|
// <div className="sideBarHeader">3D Widget Input</div>
|
||||||
|
// <Widget4InputCard3D />
|
||||||
|
// </>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else {
|
||||||
|
// return (
|
||||||
|
// <div>No chart selected</div>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const chartTypeMap: Record<| 'bar'| 'line'| 'pie' | 'doughnut' | 'polarArea'| 'progress 1' | 'progress 2'
|
||||||
|
| 'ui-Widget 1'| 'ui-Widget 2'| 'ui-Widget 3'| 'ui-Widget 4',JSX.Element> = {
|
||||||
|
bar: <BarChartInput />,
|
||||||
|
line: <LineGrapInput />,
|
||||||
|
pie: <PieChartInput />,
|
||||||
|
doughnut: <PieChartInput />,
|
||||||
|
polarArea: <PieChartInput />,
|
||||||
|
'progress 1': <Progress1Input />,
|
||||||
|
'progress 2': <Progress2Input />,
|
||||||
|
'ui-Widget 1': <Widget4InputCard3D />,
|
||||||
|
'ui-Widget 2': <Widget2InputCard3D />,
|
||||||
|
'ui-Widget 3': <Widget3InputCard3D />,
|
||||||
|
'ui-Widget 4': <Widget4InputCard3D />,
|
||||||
|
};
|
||||||
|
|
||||||
|
const classNameMap: Record<
|
||||||
|
| 'warehouseThroughput floating'
|
||||||
|
| 'fleetEfficiency floating'
|
||||||
|
| 'floating total-card',
|
||||||
|
JSX.Element
|
||||||
|
> = {
|
||||||
|
'warehouseThroughput floating': <WarehouseThroughputInputComponent />,
|
||||||
|
'fleetEfficiency floating': <FleetEfficiencyInputComponent />,
|
||||||
|
'floating total-card': <FleetEfficiencyInputComponent />,
|
||||||
|
};
|
||||||
|
|
||||||
const InputSelecterComponent = () => {
|
const InputSelecterComponent = () => {
|
||||||
const { selectedChartId } = useWidgetStore();
|
const { selectedChartId } = useWidgetStore();
|
||||||
|
|
||||||
if (selectedChartId && selectedChartId.type && selectedChartId.type === 'bar' ) {
|
if (selectedChartId) {
|
||||||
|
const { type, className } = selectedChartId;
|
||||||
|
|
||||||
|
if (type && chartTypeMap[type as keyof typeof chartTypeMap]) {
|
||||||
|
const label = ['ui-Widget 1', 'ui-Widget 2', 'ui-Widget 3', 'ui-Widget 4'].includes(type)
|
||||||
|
? '3D Widget Input'
|
||||||
|
: '2D Widget Input';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="sideBarHeader">2D Widget Input</div>
|
<div className="sideBarHeader">{label}</div>
|
||||||
<BarChartInput />
|
{chartTypeMap[type as keyof typeof chartTypeMap]}
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'line' ) {
|
if (className && classNameMap[className as keyof typeof classNameMap]) {
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">2D Widget Input</div>
|
|
||||||
<LineGrapInput />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'pie' ) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">2D Widget Input</div>
|
|
||||||
<PieChartInput />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'doughnut' ) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">2D Widget Input</div>
|
|
||||||
<PieChartInput />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'polarArea' ) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">2D Widget Input</div>
|
|
||||||
<PieChartInput />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'progress 1' ) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">2D Widget Input</div>
|
|
||||||
<Progress1Input />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'progress 2' ) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">2D Widget Input</div>
|
|
||||||
<Progress2Input />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'warehouseThroughput floating' ) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="sideBarHeader">Floting Widget Input</div>
|
<div className="sideBarHeader">Floting Widget Input</div>
|
||||||
<WarehouseThroughputInputComponent />
|
{classNameMap[className as keyof typeof classNameMap]}
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'fleetEfficiency floating' ) {
|
return <div>No chart selected</div>;
|
||||||
return (
|
};
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">Floting Widget Input</div>
|
|
||||||
<FleetEfficiencyInputComponent />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.className && selectedChartId.className === 'floating total-card' ) {
|
export default InputSelecterComponent;
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">Floting Widget Input</div>
|
|
||||||
<FleetEfficiencyInputComponent />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 1' ) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">3D Widget Input</div>
|
|
||||||
<Widget4InputCard3D />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 2' ) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">3D Widget Input</div>
|
|
||||||
<Widget2InputCard3D />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 3' ) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">3D Widget Input</div>
|
|
||||||
<Widget3InputCard3D />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (selectedChartId && selectedChartId.type && selectedChartId.type === 'ui-Widget 4' ) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sideBarHeader">3D Widget Input</div>
|
|
||||||
<Widget4InputCard3D />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
<div>No chart selected</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default InputSelecterComponent
|
|
||||||
@@ -1,30 +1,75 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
|
import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
|
||||||
import { KebabIcon } from "../../icons/ExportCommonIcons";
|
import { KebabIcon } from "../../icons/ExportCommonIcons";
|
||||||
|
import { adjustHeight } from "./function/textAreaHeightAdjust";
|
||||||
|
|
||||||
interface MessageProps {
|
interface MessageProps {
|
||||||
val: Reply;
|
val: Reply | CommentSchema;
|
||||||
i: number;
|
i: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Messages: React.FC<MessageProps> = ({ val, i }) => {
|
const Messages: React.FC<MessageProps> = ({ val, i }) => {
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
const [openOptions, setOpenOptions] = useState(false);
|
const [openOptions, setOpenOptions] = useState(false);
|
||||||
|
|
||||||
|
// input
|
||||||
|
const [value, setValue] = useState<string>(
|
||||||
|
"reply" in val ? val.reply : val.comment
|
||||||
|
);
|
||||||
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const currentUser = "1";
|
const currentUser = "1";
|
||||||
|
|
||||||
const UserName = "username";
|
const UserName = "username";
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (textareaRef.current) adjustHeight(textareaRef.current);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
function handleCancelAction() {
|
||||||
|
setIsEditing(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSaveAction() {
|
||||||
|
setIsEditing(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeleteAction() {
|
||||||
|
setOpenOptions(false);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<div className="edit-container">
|
<div className="edit-container">
|
||||||
<div className="input-container">
|
<div className="input-container">
|
||||||
<textarea />
|
<textarea
|
||||||
|
placeholder="type here"
|
||||||
|
ref={textareaRef}
|
||||||
|
autoFocus
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => setValue(e.target.value)}
|
||||||
|
style={{ resize: "none" }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="actions-container">
|
<div className="actions-container">
|
||||||
|
<div className="options"></div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<button className="cancel-button">Cancel</button>
|
<button
|
||||||
<button className="save-button">Save</button>
|
className="cancel-button"
|
||||||
|
onClick={() => {
|
||||||
|
handleCancelAction();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="save-button"
|
||||||
|
onClick={() => {
|
||||||
|
handleSaveAction();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -46,7 +91,7 @@ const Messages: React.FC<MessageProps> = ({ val, i }) => {
|
|||||||
<button
|
<button
|
||||||
className="more-options-button"
|
className="more-options-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenOptions(true);
|
setOpenOptions(!openOptions);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<KebabIcon />
|
<KebabIcon />
|
||||||
@@ -56,17 +101,27 @@ const Messages: React.FC<MessageProps> = ({ val, i }) => {
|
|||||||
<button
|
<button
|
||||||
className="option"
|
className="option"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
setOpenOptions(false);
|
||||||
setIsEditing(true);
|
setIsEditing(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</button>
|
</button>
|
||||||
<button className="option">Delete</button>
|
<button
|
||||||
|
className="option"
|
||||||
|
onClick={() => {
|
||||||
|
handleDeleteAction();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="message">{val.reply}</div>
|
<div className="message">
|
||||||
|
{"reply" in val ? val.reply : val.comment}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,38 +1,115 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { CloseIcon, KebabIcon } from "../../icons/ExportCommonIcons";
|
import { CloseIcon, KebabIcon } from "../../icons/ExportCommonIcons";
|
||||||
import Messages from "./Messages";
|
import Messages from "./Messages";
|
||||||
import { ExpandIcon } from "../../icons/SimulationIcons";
|
import { ExpandIcon } from "../../icons/SimulationIcons";
|
||||||
|
import { adjustHeight } from "./function/textAreaHeightAdjust";
|
||||||
|
|
||||||
const ThreadChat: React.FC = () => {
|
const ThreadChat: React.FC = () => {
|
||||||
const [openThreadOptions, setOpenThreadOptions] = useState(false);
|
const [openThreadOptions, setOpenThreadOptions] = useState(false);
|
||||||
|
const [inputActive, setInputActive] = useState(false);
|
||||||
|
const [value, setValue] = useState<string>("");
|
||||||
|
|
||||||
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const [dragging, setDragging] = useState(false);
|
||||||
|
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
|
||||||
|
const [position, setPosition] = useState({ x: 100, y: 100 });
|
||||||
|
|
||||||
const messages = [
|
const messages = [
|
||||||
{
|
{
|
||||||
replyId: "user 1",
|
replyId: "user 1",
|
||||||
creatorId: "1",
|
creatorId: "1",
|
||||||
createdAt: "hello, thread check",
|
createdAt: "2 hrs ago",
|
||||||
lastUpdatedAt: "2 hrs ago",
|
lastUpdatedAt: "2 hrs ago",
|
||||||
reply: "true",
|
reply:
|
||||||
|
"reply testing reply content 1, reply testing reply content 1reply testing reply content 1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
replyId: "user 2",
|
replyId: "user 2",
|
||||||
creatorId: "2",
|
creatorId: "2",
|
||||||
createdAt: "hello, thread check",
|
createdAt: "2 hrs ago",
|
||||||
lastUpdatedAt: "2 hrs ago",
|
lastUpdatedAt: "2 hrs ago",
|
||||||
reply: "true",
|
reply: "reply 2",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (textareaRef.current) adjustHeight(textareaRef.current);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const clamp = (val: number, min: number, max: number) => {
|
||||||
|
return Math.min(Math.max(val, min), max);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
|
||||||
|
if (event.button !== 0) return;
|
||||||
|
|
||||||
|
const wrapper = wrapperRef.current;
|
||||||
|
if (!wrapper) return;
|
||||||
|
|
||||||
|
const rect = wrapper.getBoundingClientRect();
|
||||||
|
const offsetX = event.clientX - rect.left;
|
||||||
|
const offsetY = event.clientY - rect.top;
|
||||||
|
|
||||||
|
setDragging(true);
|
||||||
|
setDragOffset({ x: offsetX, y: offsetY });
|
||||||
|
|
||||||
|
wrapper.setPointerCapture(event.pointerId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
|
||||||
|
if (!dragging) return;
|
||||||
|
|
||||||
|
const container = document.getElementById("work-space-three-d-canvas");
|
||||||
|
const wrapper = wrapperRef.current;
|
||||||
|
if (!container || !wrapper) return;
|
||||||
|
|
||||||
|
const containerRect = container.getBoundingClientRect();
|
||||||
|
const wrapperRect = wrapper.getBoundingClientRect();
|
||||||
|
|
||||||
|
let newX = event.clientX - containerRect.left - dragOffset.x;
|
||||||
|
let newY = event.clientY - containerRect.top - dragOffset.y;
|
||||||
|
|
||||||
|
const maxX = containerRect.width - wrapper.offsetWidth;
|
||||||
|
const maxY = containerRect.height - wrapper.offsetHeight;
|
||||||
|
|
||||||
|
newX = clamp(newX, 0, maxX);
|
||||||
|
newY = clamp(newY, 0, maxY);
|
||||||
|
|
||||||
|
setPosition({ x: newX, y: newY });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {
|
||||||
|
if (!dragging) return;
|
||||||
|
setDragging(false);
|
||||||
|
const wrapper = wrapperRef.current;
|
||||||
|
if (wrapper) wrapper.releasePointerCapture(event.pointerId);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="thread-char-wrapper">
|
<div
|
||||||
<div className="thread-char-container">
|
ref={wrapperRef}
|
||||||
|
className="thread-chat-wrapper"
|
||||||
|
onPointerDown={handlePointerDown}
|
||||||
|
onPointerMove={handlePointerMove}
|
||||||
|
onPointerUp={handlePointerUp}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
left: position.x,
|
||||||
|
top: position.y,
|
||||||
|
cursor: dragging ? "grabbing" : "grab",
|
||||||
|
userSelect: "none",
|
||||||
|
zIndex: 9999,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="thread-chat-container">
|
||||||
<div className="header-wrapper">
|
<div className="header-wrapper">
|
||||||
<div className="header">Comment</div>
|
<div className="header">Comment</div>
|
||||||
<div className="header-options">
|
<div className="header-options">
|
||||||
<button
|
<button
|
||||||
className="options-button"
|
className="options-button"
|
||||||
onClick={() => {
|
onClick={() => setOpenThreadOptions(!openThreadOptions)}
|
||||||
setOpenThreadOptions(true);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<KebabIcon />
|
<KebabIcon />
|
||||||
</button>
|
</button>
|
||||||
@@ -40,7 +117,7 @@ const ThreadChat: React.FC = () => {
|
|||||||
<div className="options-list">
|
<div className="options-list">
|
||||||
<div className="options">Mark as Unread</div>
|
<div className="options">Mark as Unread</div>
|
||||||
<div className="options">Mark as Resolved</div>
|
<div className="options">Mark as Resolved</div>
|
||||||
<div className="options">Delete Thread</div>
|
<div className="options delete">Delete Thread</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<button className="close-button">
|
<button className="close-button">
|
||||||
@@ -48,15 +125,25 @@ const ThreadChat: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="messages-wrapper">
|
<div className="messages-wrapper">
|
||||||
{messages.map((val, i) => (
|
{messages.map((val, i) => (
|
||||||
<Messages val={val as Reply} i={i} key={val.replyId} />
|
<Messages val={val as any} i={i} key={val.replyId} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="send-message-wrapper">
|
<div className="send-message-wrapper">
|
||||||
<div className="input-container">
|
<div className={`input-container ${inputActive ? "active" : ""}`}>
|
||||||
<input type="text" />
|
<textarea
|
||||||
<div className="sent-button">
|
placeholder="type something"
|
||||||
|
ref={textareaRef}
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => setValue(e.target.value)}
|
||||||
|
onFocus={() => setInputActive(true)}
|
||||||
|
onBlur={() => setInputActive(false)}
|
||||||
|
style={{ resize: "none" }}
|
||||||
|
/>
|
||||||
|
<div className={`sent-button ${value === "" ? "disable-send-btn" : ""}`}>
|
||||||
<ExpandIcon />
|
<ExpandIcon />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export const adjustHeight = (textareaRef: HTMLTextAreaElement) => {
|
||||||
|
const el = textareaRef;
|
||||||
|
if (el) {
|
||||||
|
el.style.height = "auto";
|
||||||
|
el.style.height = "38px";
|
||||||
|
|
||||||
|
// Clamp to max height for 6 lines
|
||||||
|
const lineHeight = 18; // px, adjust if needed
|
||||||
|
const maxHeight = lineHeight * 6;
|
||||||
|
if (el.scrollHeight > maxHeight) {
|
||||||
|
el.style.overflowY = "auto";
|
||||||
|
el.style.height = `${maxHeight}px`;
|
||||||
|
} else {
|
||||||
|
el.style.overflowY = "hidden";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -5,7 +5,6 @@ import Controls from '../controls/controls';
|
|||||||
import { Environment } from '@react-three/drei'
|
import { Environment } from '@react-three/drei'
|
||||||
|
|
||||||
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
|
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
|
||||||
import { MovingClouds } from '../clouds/clouds';
|
|
||||||
|
|
||||||
function Setup() {
|
function Setup() {
|
||||||
return (
|
return (
|
||||||
@@ -18,7 +17,7 @@ function Setup() {
|
|||||||
|
|
||||||
<PostProcessing />
|
<PostProcessing />
|
||||||
|
|
||||||
<MovingClouds />
|
{/* <MovingClouds /> */}
|
||||||
|
|
||||||
<Environment files={background} environmentIntensity={1.5} />
|
<Environment files={background} environmentIntensity={1.5} />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ interface ProductionCapacityProps {
|
|||||||
type: string;
|
type: string;
|
||||||
position: [number, number, number];
|
position: [number, number, number];
|
||||||
rotation: [number, number, number];
|
rotation: [number, number, number];
|
||||||
|
scale?: [number, number, number];
|
||||||
Data?: any;
|
Data?: any;
|
||||||
onContextMenu?: (event: React.MouseEvent) => void;
|
onContextMenu?: (event: React.MouseEvent) => void;
|
||||||
// onPointerDown:any
|
// onPointerDown:any
|
||||||
@@ -41,7 +42,8 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
|
|||||||
type,
|
type,
|
||||||
Data,
|
Data,
|
||||||
position,
|
position,
|
||||||
rotation,
|
rotation = [0, 0, 0],
|
||||||
|
scale = [0.5, 0.5, 0.5],
|
||||||
onContextMenu,
|
onContextMenu,
|
||||||
}) => {
|
}) => {
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
@@ -198,10 +200,11 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
|
|||||||
useEffect(() => {}, [rotation]);
|
useEffect(() => {}, [rotation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Html
|
<>
|
||||||
|
{position && scale && rotation && <Html
|
||||||
// data
|
// data
|
||||||
position={position}
|
position={position}
|
||||||
scale={[0.5, 0.5, 0.5]}
|
scale={scale}
|
||||||
rotation={rotation}
|
rotation={rotation}
|
||||||
// class
|
// class
|
||||||
wrapperClass="pointer-none"
|
wrapperClass="pointer-none"
|
||||||
@@ -221,8 +224,7 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`productionCapacity-wrapper card ${
|
className={`productionCapacity-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""
|
||||||
selectedChartId?.id === id ? "activeChart" : ""
|
|
||||||
}`}
|
}`}
|
||||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
@@ -265,7 +267,8 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Html>
|
</Html>}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,6 @@ const Project: React.FC = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<VersionSaved />
|
<VersionSaved />
|
||||||
{/* <ThreadChat /> */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
// global input style
|
// global input style
|
||||||
|
|
||||||
input {
|
input,
|
||||||
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: #{$border-radius-large};
|
border-radius: #{$border-radius-large};
|
||||||
|
|||||||
@@ -93,66 +93,216 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.thread-char-wrapper {
|
.thread-chat-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
// remove later
|
// remove later
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
// ----
|
// ----
|
||||||
z-index: 10000;
|
z-index: #{$z-index-ui-highest};
|
||||||
.thread-char-container {
|
.thread-chat-container {
|
||||||
|
background: var(--background-color);
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
border-radius: #{$border-radius-extra-large};
|
||||||
|
width: 20rem;
|
||||||
.header-wrapper {
|
.header-wrapper {
|
||||||
.header {
|
padding: 12px;
|
||||||
}
|
@include flex-space-between;
|
||||||
.header-options {
|
.header-options {
|
||||||
.options-button {
|
@include flex-center;
|
||||||
}
|
position: relative;
|
||||||
.options-list {
|
.options-list {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
transform: translate(-24px, 100%);
|
||||||
|
background: var(--background-color);
|
||||||
|
padding: 8px 4px;
|
||||||
|
border-radius: #{$border-radius-medium};
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
z-index: 100;
|
||||||
.options {
|
.options {
|
||||||
|
text-wrap: nowrap;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: #{$border-radius-medium};
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: var(--text-button-color);
|
||||||
|
background: var(--background-color-accent);
|
||||||
|
}
|
||||||
|
&.delete {
|
||||||
|
&:hover {
|
||||||
|
color: var(--log-error-text-color);
|
||||||
|
background: var(--log-error-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.options-button,
|
||||||
|
.close-button {
|
||||||
|
@include flex-center;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
border-radius: #{$border-radius-medium};
|
||||||
|
&:hover {
|
||||||
|
background: var(--background-color-solid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.close-button {
|
.close-button {
|
||||||
|
svg {
|
||||||
|
scale: 1.4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.messages-wrapper {
|
.messages-wrapper {
|
||||||
|
padding: 12px;
|
||||||
|
padding-top: 0;
|
||||||
.edit-container {
|
.edit-container {
|
||||||
.input-container {
|
.input-container {
|
||||||
|
textarea{
|
||||||
|
background: var(--background-color);
|
||||||
|
&:focus{
|
||||||
|
outline-color: var(--border-color-accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.actions-container {
|
.actions-container {
|
||||||
|
@include flex-space-between;
|
||||||
|
width: 100%;
|
||||||
|
margin: 8px 0;
|
||||||
.actions {
|
.actions {
|
||||||
.cancel-button {
|
@include flex-center;
|
||||||
|
gap: 4px;
|
||||||
|
.cancel-button,
|
||||||
|
.save-button {
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: #{$border-radius-large};
|
||||||
|
background: var(--background-color-solid);
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
.save-button {
|
.save-button {
|
||||||
|
color: var(--text-button-color);
|
||||||
|
background: var(--background-color-accent);
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.message-container {
|
.message-container {
|
||||||
|
position: relative;
|
||||||
|
@include flex-space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 8px;
|
||||||
|
&:first-child{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
.profile {
|
.profile {
|
||||||
|
height: 28px;
|
||||||
|
width: 28px;
|
||||||
|
min-width: 28px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
line-height: 28px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: #{$border-radius-circle};
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
|
width: 100%;
|
||||||
.user-details {
|
.user-details {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 8px;
|
||||||
.user-name {
|
.user-name {
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
.time {
|
.time {
|
||||||
|
font-size: var(--font-size-tiny);
|
||||||
|
color: var(--input-text-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.more-options {
|
.more-options {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
.more-options-button {
|
.more-options-button {
|
||||||
|
@include flex-center;
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
border-radius: #{$border-radius-small};
|
||||||
|
&:hover{
|
||||||
|
background: var(--background-color-solid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.options-list {
|
.options-list {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
padding: 3px 6px;
|
||||||
|
background: var(--background-color);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
border-radius: #{$border-radius-medium};
|
||||||
|
z-index: 100;
|
||||||
.option {
|
.option {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: #{$border-radius-medium};
|
||||||
|
padding: 2px 6px;
|
||||||
|
text-align: start;
|
||||||
|
&:hover{
|
||||||
|
background: var(--background-color-accent);
|
||||||
|
color: var(--text-button-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.message{
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.send-message-wrapper {
|
.send-message-wrapper {
|
||||||
|
padding: 12px;
|
||||||
|
padding-top: 8px;
|
||||||
.input-container {
|
.input-container {
|
||||||
|
position: relative;
|
||||||
|
@include flex-space-between;
|
||||||
|
background: var(--background-color);
|
||||||
|
border-radius: #{$border-radius-extra-large};
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
textarea {
|
||||||
|
background: transparent;
|
||||||
|
outline: none;
|
||||||
|
width: calc(100% - 36px);
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 28px;
|
||||||
|
max-height: 108px;
|
||||||
|
}
|
||||||
.sent-button {
|
.sent-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 2px;
|
||||||
|
bottom: 2px;
|
||||||
|
@include flex-center;
|
||||||
|
padding: 2px;
|
||||||
|
svg {
|
||||||
|
rotate: 45deg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.disable-send-btn {
|
||||||
|
filter: saturate(0);
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
background: var(--background-color-solid);
|
||||||
|
padding-top: 4px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: end;
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
.sent-button {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user