Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d204c9aaa | ||
|
|
070e186b57 |
No files matched your search
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.18.0",
|
||||
"pnpm": ">=11.13.1"
|
||||
"pnpm": ">=11.11.0"
|
||||
},
|
||||
"packageManager": "pnpm@11.13.1"
|
||||
"packageManager": "pnpm@11.11.0"
|
||||
}
|
||||
+2
-2
@@ -69,7 +69,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.18.0",
|
||||
"pnpm": ">=11.13.1"
|
||||
"pnpm": ">=11.11.0"
|
||||
},
|
||||
"packageManager": "pnpm@11.13.1"
|
||||
"packageManager": "pnpm@11.11.0"
|
||||
}
|
||||
@@ -271,7 +271,6 @@ export const onboardRouter = createTRPCRouter({
|
||||
setupIntegrations: onboardingProcedure.requiresStep("integrations").mutation(async ({ ctx }) => {
|
||||
const db = ctx.db;
|
||||
|
||||
const allIntegrations = await db.query.integrations.findMany();
|
||||
const allApps = await db.query.apps.findMany();
|
||||
|
||||
const board = await db.query.boards.findFirst({
|
||||
@@ -316,7 +315,6 @@ export const onboardRouter = createTRPCRouter({
|
||||
layoutId: layout.id,
|
||||
columnCount: layout.columnCount,
|
||||
},
|
||||
allIntegrations,
|
||||
allApps,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -698,13 +698,11 @@ const seedBoardWidgetsAsync = async (db: Database) => {
|
||||
const layout = board.layouts[0];
|
||||
if (!section || !layout) return;
|
||||
|
||||
const allIntegrations = await db.query.integrations.findMany();
|
||||
const allApps = await db.query.apps.findMany();
|
||||
|
||||
const count = await placeAllWidgetsAsync(
|
||||
db,
|
||||
{ boardId: board.id, sectionId: section.id, layoutId: layout.id, columnCount: layout.columnCount },
|
||||
allIntegrations,
|
||||
allApps,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
import superjson from "superjson";
|
||||
import { stringify } from "superjson";
|
||||
|
||||
import { createId } from "@homarr/common";
|
||||
import {
|
||||
defaultBookmarkApps,
|
||||
defaultWidgetConfigs,
|
||||
emptySuperJSON,
|
||||
getWidgetKindsForIntegration,
|
||||
} from "@homarr/definitions";
|
||||
import { defaultBookmarkApps, defaultWidgetConfigs, emptySuperJSON } from "@homarr/definitions";
|
||||
import type { WidgetKind } from "@homarr/definitions";
|
||||
|
||||
import type { Database } from "..";
|
||||
import { integrationItems, itemLayouts, items } from "../schema";
|
||||
import type { Integration } from "../schema";
|
||||
|
||||
const widgetConfigMap = new Map(defaultWidgetConfigs.map((config) => [config.kind, config]));
|
||||
|
||||
interface BoardTarget {
|
||||
boardId: string;
|
||||
@@ -77,12 +69,7 @@ interface App {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const placeAllWidgetsAsync = async (
|
||||
db: Database,
|
||||
target: BoardTarget,
|
||||
allIntegrations: Integration[],
|
||||
allApps: App[],
|
||||
) => {
|
||||
export const placeAllWidgetsAsync = async (db: Database, target: BoardTarget, allApps: App[]) => {
|
||||
const ctx: PlacementContext = {
|
||||
...target,
|
||||
xOffset: 0,
|
||||
@@ -92,38 +79,11 @@ export const placeAllWidgetsAsync = async (
|
||||
|
||||
const placedWidgets = new Set<WidgetKind>();
|
||||
|
||||
for (const integration of allIntegrations) {
|
||||
for (const widgetKind of getWidgetKindsForIntegration(integration.kind)) {
|
||||
if (placedWidgets.has(widgetKind)) continue;
|
||||
const config = widgetConfigMap.get(widgetKind);
|
||||
if (config?.skip) continue;
|
||||
placedWidgets.add(widgetKind);
|
||||
|
||||
const matchingIds = allIntegrations
|
||||
.filter((row) => getWidgetKindsForIntegration(row.kind).includes(widgetKind))
|
||||
.map((row) => row.id);
|
||||
|
||||
const options = config?.options ? superjson.stringify(config.options) : undefined;
|
||||
await placeWidgetAsync(
|
||||
db,
|
||||
ctx,
|
||||
widgetKind,
|
||||
matchingIds,
|
||||
options,
|
||||
config && { width: config.width, height: config.height },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const app of allApps) {
|
||||
await placeWidgetAsync(
|
||||
db,
|
||||
ctx,
|
||||
"app",
|
||||
[],
|
||||
superjson.stringify({ appId: app.id, openInNewTab: true, showTitle: true }),
|
||||
{ width: 1, height: 1 },
|
||||
);
|
||||
await placeWidgetAsync(db, ctx, "app", [], stringify({ appId: app.id, openInNewTab: true, showTitle: true }), {
|
||||
width: 1,
|
||||
height: 1,
|
||||
});
|
||||
}
|
||||
|
||||
const bookmarkAppNames = new Set(defaultBookmarkApps.map((bookmark) => bookmark.name));
|
||||
@@ -138,7 +98,7 @@ export const placeAllWidgetsAsync = async (
|
||||
options = { ...options, items: bookmarkAppIds };
|
||||
}
|
||||
|
||||
await placeWidgetAsync(db, ctx, config.kind, [], options ? superjson.stringify(options) : undefined, {
|
||||
await placeWidgetAsync(db, ctx, config.kind, [], options ? stringify(options) : undefined, {
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
});
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Box, Container, Flex, HoverCard, Text, useMantineTheme } from "@mantine/core";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Box, Container, Flex, Popover, Text, useMantineTheme } from "@mantine/core";
|
||||
import { useElementSize } from "@mantine/hooks";
|
||||
|
||||
import { useRequiredBoard } from "@homarr/boards/context";
|
||||
import type { CalendarEvent } from "@homarr/integrations/types";
|
||||
@@ -11,9 +13,14 @@ interface CalendarDayProps {
|
||||
disabled: boolean;
|
||||
rootWidth: number;
|
||||
rootHeight: number;
|
||||
onOpen: (close: () => void) => void;
|
||||
}
|
||||
|
||||
export const CalendarDay = ({ date, events, disabled, rootHeight, rootWidth }: CalendarDayProps) => {
|
||||
export const CalendarDay = ({ date, events, disabled, rootHeight, rootWidth, onOpen }: CalendarDayProps) => {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [pinned, setPinned] = useState(false);
|
||||
const { primaryColor } = useMantineTheme();
|
||||
const { ref, height } = useElementSize();
|
||||
const board = useRequiredBoard();
|
||||
const mantineTheme = useMantineTheme();
|
||||
const actualItemRadius = mantineTheme.radius[board.itemRadius];
|
||||
@@ -22,19 +29,39 @@ export const CalendarDay = ({ date, events, disabled, rootHeight, rootWidth }: C
|
||||
const shouldScaleDown = minAxisSize < 350;
|
||||
const isSmall = rootHeight < 256;
|
||||
|
||||
const isTooSmallForIndicators = height < 30;
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
if (disabled) return;
|
||||
onOpen(() => {
|
||||
setOpened(false);
|
||||
setPinned(false);
|
||||
});
|
||||
setOpened(true);
|
||||
}, [disabled, onOpen]);
|
||||
|
||||
const handleMouseLeave = useCallback(() => {
|
||||
if (!pinned) setOpened(false);
|
||||
}, [pinned]);
|
||||
|
||||
return (
|
||||
<HoverCard
|
||||
<Popover
|
||||
position="bottom"
|
||||
withArrow
|
||||
withinPortal
|
||||
radius="lg"
|
||||
shadow="sm"
|
||||
transitionProps={{ transition: "pop" }}
|
||||
openDelay={350}
|
||||
closeDelay={400}
|
||||
transitionProps={{
|
||||
transition: "pop",
|
||||
}}
|
||||
onChange={(value) => {
|
||||
setOpened(value);
|
||||
if (!value) setPinned(false);
|
||||
}}
|
||||
opened={opened}
|
||||
disabled={disabled}
|
||||
>
|
||||
<HoverCard.Target>
|
||||
<Popover.Target>
|
||||
<Container
|
||||
h="100%"
|
||||
w="100%"
|
||||
@@ -42,23 +69,38 @@ export const CalendarDay = ({ date, events, disabled, rootHeight, rootWidth }: C
|
||||
pt={isSmall ? 0 : 10}
|
||||
pb={isSmall ? 0 : 10}
|
||||
m={0}
|
||||
pos="relative"
|
||||
ref={ref}
|
||||
bd={`2px solid ${opened && !disabled ? primaryColor : "transparent"}`}
|
||||
style={{
|
||||
alignContent: "center",
|
||||
borderRadius: actualItemRadius,
|
||||
cursor: disabled ? "default" : "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!disabled) setPinned((prev) => !prev);
|
||||
}}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<Text ta={"center"} size={shouldScaleDown ? "xs" : "md"} lh={1}>
|
||||
{date.getDate()}
|
||||
</Text>
|
||||
<NotificationIndicator events={events} isSmall={isSmall} />
|
||||
{!isTooSmallForIndicators && <NotificationIndicator events={events} isSmall={isSmall} />}
|
||||
</Container>
|
||||
</HoverCard.Target>
|
||||
<HoverCard.Dropdown maw="calc(100vw - 24px)" w={512} pe={4} pb={0} style={{ overflow: "hidden" }}>
|
||||
</Popover.Target>
|
||||
{/* Popover has some offset on the left side, padding is removed because of scrollarea paddings */}
|
||||
<Popover.Dropdown
|
||||
maw="calc(100vw - 24px)"
|
||||
w={512}
|
||||
pe={4}
|
||||
pb={0}
|
||||
style={{ overflow: "hidden" }}
|
||||
onMouseEnter={() => setOpened(true)}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<CalendarEventList events={events} />
|
||||
</HoverCard.Dropdown>
|
||||
</HoverCard>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -69,6 +111,7 @@ interface NotificationIndicatorProps {
|
||||
|
||||
const NotificationIndicator = ({ events, isSmall }: NotificationIndicatorProps) => {
|
||||
const notificationEvents = [...new Set(events.map((event) => event.indicatorColor))].filter(String);
|
||||
/* position bottom is lower when small to not be on top of number*/
|
||||
return (
|
||||
<Flex
|
||||
w="75%"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo, useState } from "react";
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useMantineTheme } from "@mantine/core";
|
||||
import { Calendar } from "@mantine/dates";
|
||||
@@ -65,6 +65,11 @@ const CalendarBase = ({ isEditMode, events, month, setMonth, options }: Calendar
|
||||
const isSmall = width < 256;
|
||||
|
||||
const normalizedEvents = useMemo(() => splitEvents(events), [events]);
|
||||
const activeCloseRef = useRef<(() => void) | null>(null);
|
||||
const onDayOpen = useCallback((close: () => void) => {
|
||||
activeCloseRef.current?.();
|
||||
activeCloseRef.current = close;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
@@ -141,6 +146,7 @@ const CalendarBase = ({ isEditMode, events, month, setMonth, options }: Calendar
|
||||
disabled={isEditMode || eventsForDate.length === 0}
|
||||
rootWidth={width}
|
||||
rootHeight={height}
|
||||
onOpen={onDayOpen}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
||||
Reference in new issue
Block a user