Compare commits

...

6 Commits

Author SHA1 Message Date
4d1254753b Fixed React issue.
All checks were successful
homepanel/HomePanel/pipeline/head This commit looks good
2026-02-13 01:48:22 +00:00
f96c6e792f Improve animation timing. 2026-02-13 01:48:12 +00:00
c614354c09 Refactor dashboard. 2026-02-13 01:48:04 +00:00
6ec351a8e5 Fixed overflow. 2026-02-13 01:47:55 +00:00
69998ac347 Use correct animated collapse. 2026-02-13 01:46:18 +00:00
ec754fbaa4 Changed title transform origin. 2026-02-13 01:35:39 +00:00
5 changed files with 206 additions and 180 deletions

View File

@ -23,7 +23,7 @@ import EditWidget from "./admin/EditWidget";
import SlideView from "./dashboard/controls/SlideView";
import DashboardFlex from "./dashboard/controls/DashboardFlex";
import { useSearchParams } from "react-router-dom";
import AnimatedCollapse from "./admin/controls/AnimatedCollapse";
import AnimatedCollapse from "./dashboard/controls/AnimatedCollapse";
import EntityDashboard from "./dashboard/controls/EntityDashboard";
import { WidgetGroup } from "./dashboard/controls/WidgetGroup";
@ -37,15 +37,12 @@ function Dashboard({
onChange,
onSave,
}) {
const {
data,
subscribeToPanelUpdate,
isConnected,
connectionStatus,
} = useWebSocket();
const { data, subscribeToPanelUpdate, isConnected, connectionStatus } =
useWebSocket();
const deviceContext = useDeviceSafe();
const panelSelectorVisible = deviceContext?.panelSelectorVisible ?? false;
const setPanelSelectorVisible = deviceContext?.setPanelSelectorVisible ?? (() => {});
const setPanelSelectorVisible =
deviceContext?.setPanelSelectorVisible ?? (() => {});
const isInDeviceContext = !!deviceContext;
const hpAppContext = useHPAppSafe();
@ -154,12 +151,10 @@ function Dashboard({
const currentEntities = panel.entities || [];
const exists = currentEntities.some(
(e) => (typeof e === "string" ? e : e.entityId) === entity.entityId
(e) => (typeof e === "string" ? e : e.entityId) === entity.entityId,
);
if (!exists) {
onChange({
...panel,
entities: [...currentEntities, entity],
@ -178,7 +173,9 @@ function Dashboard({
// Generate a unique ID for the widget if it doesn't have one
const widgetWithId = {
...widget,
id: widget.id || `widget-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
id:
widget.id ||
`widget-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
};
onChange({
...panel,
@ -192,7 +189,7 @@ function Dashboard({
const currentEntities = panel.entities || [];
const updatedEntities = currentEntities.filter(
(e) => (typeof e === "string" ? e : e.entityId) !== entity.entityId
(e) => (typeof e === "string" ? e : e.entityId) !== entity.entityId,
);
onChange({
@ -235,7 +232,7 @@ function Dashboard({
setLocalEntities(reorderedEntities);
setUnsavedChanges(true);
},
[panel, onChange]
[panel, onChange],
);
const handleDeleteWidget = useCallback(
@ -255,7 +252,7 @@ function Dashboard({
});
setUnsavedChanges(true);
},
[panel, onChange]
[panel, onChange],
);
const handleWidgetEdit = useCallback((widget) => {
@ -263,7 +260,8 @@ function Dashboard({
setIsEditWidgetModalOpen(true);
}, []);
const handleWidgetUpdate = useCallback((updatedWidget) => {
const handleWidgetUpdate = useCallback(
(updatedWidget) => {
if (!updatedWidget || !panel || !onChange) return;
const currentWidgets = panel.widgets || [];
@ -284,7 +282,9 @@ function Dashboard({
widgets: updatedWidgets,
});
setUnsavedChanges(true);
}, [panel, onChange]);
},
[panel, onChange],
);
const handleReorderWidgets = useCallback(
(reorderedWidgets) => {
@ -296,7 +296,7 @@ function Dashboard({
});
setUnsavedChanges(true);
},
[panel, onChange]
[panel, onChange],
);
const handleCancel = () => {
@ -381,16 +381,24 @@ function Dashboard({
className={`dashboard-content${
!isContentVisible ? " dashboard-content-hidden" : ""
} ${
isEntityDashboardOpen ? " dashboard-content-entity-dashboard-open" : ""
isEntityDashboardOpen
? " dashboard-content-entity-dashboard-open"
: ""
}`}
>
<div style={{ position: "relative" }}>
<DashboardFlex style={{ marginBottom: "35px" }}>
<DashboardTitle onPanelSelect={isInDeviceContext ? () => {
<DashboardTitle
onPanelSelect={
isInDeviceContext
? () => {
setPanelSelectorVisible(!panelSelectorVisible);
} : undefined}>
{title
}</DashboardTitle>
}
: undefined
}
>
{title}
</DashboardTitle>
<WidgetGroup
widgets={panel?.widgets || []}
panelName={title}
@ -415,7 +423,10 @@ function Dashboard({
)}
</div>
<ConnectionStatus showIfConnected={false} />
<AnimatedCollapse visible={connectionStatus == "connected" && !panelSelectorVisible} style={{ overflow: "visible" }}>
<AnimatedCollapse
visible={connectionStatus == "connected" && !panelSelectorVisible}
style={{ overflow: "visible" }}
>
<SlideView ref={slideViewRef} className="views-container">
{/* Main View: All Entities */}
<SlideView.Item>
@ -467,8 +478,16 @@ function Dashboard({
</div>
</DashboardContainer>
</div>
<AddEntity isOpen={isAddEntityModalOpen} onClose={() => setIsAddEntityModalOpen(false)} onAdd={handleAddEntity} />
<AddWidget isOpen={isAddWidgetModalOpen} onClose={() => setIsAddWidgetModalOpen(false)} onAdd={handleAddWidget} />
<AddEntity
isOpen={isAddEntityModalOpen}
onClose={() => setIsAddEntityModalOpen(false)}
onAdd={handleAddEntity}
/>
<AddWidget
isOpen={isAddWidgetModalOpen}
onClose={() => setIsAddWidgetModalOpen(false)}
onAdd={handleAddWidget}
/>
<EditWidget
isOpen={isEditWidgetModalOpen}
onClose={() => {
@ -478,7 +497,11 @@ function Dashboard({
widget={editingWidget}
onUpdate={handleWidgetUpdate}
/>
<EntityDashboard isOpen={isEntityDashboardOpen} onClose={handleCloseEntityDashboard} entity={entityDashboardEntity} />
<EntityDashboard
isOpen={isEntityDashboardOpen}
onClose={handleCloseEntityDashboard}
entity={entityDashboardEntity}
/>
</>
);
}

View File

@ -117,7 +117,7 @@ function DashboardCarousel({
console.log("transitioning", isTransitioning);
setIsTransitioning(false);
timeoutRef.current = null;
}, transitionDuration);
}, transitionDuration + 500);
}, [currentPanelIndex, panels, activeSlot, slot1, slot2, transitionDuration]);
useEffect(() => {
@ -199,7 +199,7 @@ function DashboardCarousel({
width,
height,
position: "relative",
"--dashboard-carousel-duration": `${transitionDuration}ms`,
"--dashboard-carousel-duration": `${transitionDuration - 500}ms`,
}}
>
<div className="dashboard-carousel-item dashboard-carousel-visible">
@ -225,7 +225,7 @@ function DashboardCarousel({
width,
height,
position: "relative",
"--dashboard-carousel-duration": `${transitionDuration}ms`,
"--dashboard-carousel-duration": `${transitionDuration - 500}ms`,
}}
>
{slot1 && (

View File

@ -1,5 +1,7 @@
.animated-collapse {
transition: opacity 150ms ease-in-out, transform 150ms ease-in-out,
transition:
opacity 150ms ease-in-out,
transform 150ms ease-in-out,
max-height 150ms ease-in-out;
transform: translateY(10px);
opacity: 0;
@ -10,6 +12,7 @@
.animated-collapse.visible {
opacity: 1;
transform: translateY(0);
overflow: visible;
}
.animated-collapse.hidden {

View File

@ -7,7 +7,7 @@
.dashboard-title-clickable {
cursor: pointer;
transition: transform 0.2s ease;
transform-origin: left center;
transform-origin: center center;
}
.dashboard-title-clickable:active {

View File

@ -108,21 +108,21 @@ export const Entity = ({
// onChange handler called when WebSocket updates the entity
const onChange = useCallback(
(updatedEntity) => {
setEntityState((prev) => {
const newState = {
setEntityState((prev) => ({
...prev,
...updatedEntity,
};
// Call onUpdate callback when entity is updated via WebSocket
if (onUpdate) {
onUpdate(newState);
}
return newState;
});
}));
},
[onUpdate]
[]
);
// Call onUpdate callback after entityState changes (outside of render)
useEffect(() => {
if (onUpdate) {
onUpdate(entityState);
}
}, [entityState, onUpdate]);
// Subscribe to entity updates
// Including isConnected as a dependency ensures we resubscribe on reconnection
useEffect(() => {