{children}
diff --git a/apps/dashboard/src/components/profiles/table/index.tsx b/apps/dashboard/src/components/profiles/table/index.tsx
index e16a4f5e..6672c2a6 100644
--- a/apps/dashboard/src/components/profiles/table/index.tsx
+++ b/apps/dashboard/src/components/profiles/table/index.tsx
@@ -3,6 +3,7 @@ import { DataTable } from '@/components/data-table';
import { FullPageEmptyState } from '@/components/full-page-empty-state';
import { Pagination } from '@/components/pagination';
import { Button } from '@/components/ui/button';
+import { TableSkeleton } from '@/components/ui/table';
import type { UseQueryResult } from '@tanstack/react-query';
import { GanttChartIcon } from 'lucide-react';
@@ -26,15 +27,7 @@ export const ProfilesTable = ({ type, query, ...props }: Props) => {
const { data, isFetching, isLoading } = query;
if (isLoading) {
- return (
-
- );
+ return
;
}
if (data?.length === 0) {
diff --git a/apps/dashboard/src/components/report/chart/ChartEmpty.tsx b/apps/dashboard/src/components/report/chart/ChartEmpty.tsx
index d2f862c3..e8ec49e2 100644
--- a/apps/dashboard/src/components/report/chart/ChartEmpty.tsx
+++ b/apps/dashboard/src/components/report/chart/ChartEmpty.tsx
@@ -1,8 +1,8 @@
import { FullPageEmptyState } from '@/components/full-page-empty-state';
-import { cn } from '@/utils/cn';
import { useChartContext } from './ChartProvider';
import { MetricCardEmpty } from './MetricCard';
+import { ResponsiveContainer } from './ResponsiveContainer';
export function ChartEmpty() {
const { editMode, chartType } = useChartContext();
@@ -20,12 +20,10 @@ export function ChartEmpty() {
}
return (
-
- No data
-
+
+
+ No data
+
+
);
}
diff --git a/apps/dashboard/src/components/report/chart/ChartLoading.tsx b/apps/dashboard/src/components/report/chart/ChartLoading.tsx
index 360f5431..181672f2 100644
--- a/apps/dashboard/src/components/report/chart/ChartLoading.tsx
+++ b/apps/dashboard/src/components/report/chart/ChartLoading.tsx
@@ -1,15 +1,20 @@
import { cn } from '@/utils/cn';
+import { ResponsiveContainer } from './ResponsiveContainer';
+
interface ChartLoadingProps {
className?: string;
+ aspectRatio?: number;
}
-export function ChartLoading({ className }: ChartLoadingProps) {
+export function ChartLoading({ className, aspectRatio }: ChartLoadingProps) {
return (
-
+
+
+
);
}
diff --git a/apps/dashboard/src/components/report/chart/ChartProvider.tsx b/apps/dashboard/src/components/report/chart/ChartProvider.tsx
index 9362a24c..fcfa22a0 100644
--- a/apps/dashboard/src/components/report/chart/ChartProvider.tsx
+++ b/apps/dashboard/src/components/report/chart/ChartProvider.tsx
@@ -6,6 +6,9 @@ import type { LucideIcon } from 'lucide-react';
import type { IChartProps, IChartSerie } from '@openpanel/validation';
export interface IChartContextType extends IChartProps {
+ hideXAxis?: boolean;
+ hideYAxis?: boolean;
+ aspectRatio?: number;
editMode?: boolean;
hideID?: boolean;
onClick?: (item: IChartSerie) => void;
diff --git a/apps/dashboard/src/components/report/chart/LazyChart.tsx b/apps/dashboard/src/components/report/chart/LazyChart.tsx
index a3c72832..41c02d76 100644
--- a/apps/dashboard/src/components/report/chart/LazyChart.tsx
+++ b/apps/dashboard/src/components/report/chart/LazyChart.tsx
@@ -1,13 +1,17 @@
'use client';
import React, { useEffect, useRef } from 'react';
+import { cn } from '@/utils/cn';
import { useInViewport } from 'react-in-viewport';
import type { IChartRoot } from '.';
import { ChartRoot } from '.';
import { ChartLoading } from './ChartLoading';
-export function LazyChart(props: IChartRoot) {
+export function LazyChart({
+ className,
+ ...props
+}: IChartRoot & { className?: string }) {
const ref = useRef
(null);
const once = useRef(false);
const { inViewport } = useInViewport(ref, undefined, {
@@ -21,11 +25,11 @@ export function LazyChart(props: IChartRoot) {
}, [inViewport]);
return (
-
+
{once.current || inViewport ? (
) : (
-
+
)}
);
diff --git a/apps/dashboard/src/components/report/chart/ReportChartTooltip.tsx b/apps/dashboard/src/components/report/chart/ReportChartTooltip.tsx
index 012159a1..cb6d7245 100644
--- a/apps/dashboard/src/components/report/chart/ReportChartTooltip.tsx
+++ b/apps/dashboard/src/components/report/chart/ReportChartTooltip.tsx
@@ -1,9 +1,11 @@
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import { useFormatDateInterval } from '@/hooks/useFormatDateInterval';
-import { useMappings } from '@/hooks/useMappings';
import { useNumber } from '@/hooks/useNumerFormatter';
import type { IRechartPayloadItem } from '@/hooks/useRechartDataModel';
import type { IToolTipProps } from '@/types';
+import * as Portal from '@radix-ui/react-portal';
+import { bind } from 'bind-event-listener';
+import throttle from 'lodash.throttle';
import { PreviousDiffIndicator } from '../PreviousDiffIndicator';
import { useChartContext } from './ChartProvider';
@@ -24,11 +26,35 @@ export function ReportChartTooltip({
const { unit, interval } = useChartContext();
const formatDate = useFormatDateInterval(interval);
const number = useNumber();
- if (!active || !payload) {
- return null;
- }
+ const [position, setPosition] = useState<{ x: number; y: number } | null>(
+ null
+ );
- if (!payload.length) {
+ const inactive = !active || !payload?.length;
+ useEffect(() => {
+ const setPositionThrottled = throttle(setPosition, 50);
+ const unsubMouseMove = bind(window, {
+ type: 'mousemove',
+ listener(event) {
+ if (!inactive) {
+ setPositionThrottled({ x: event.clientX, y: event.clientY + 20 });
+ }
+ },
+ });
+ const unsubDragEnter = bind(window, {
+ type: 'pointerdown',
+ listener() {
+ setPosition(null);
+ },
+ });
+
+ return () => {
+ unsubMouseMove();
+ unsubDragEnter();
+ };
+ }, [inactive]);
+
+ if (inactive) {
return null;
}
@@ -41,55 +67,81 @@ export function ReportChartTooltip({
const visible = sorted.slice(0, limit);
const hidden = sorted.slice(limit);
+ const correctXPosition = (x: number | undefined) => {
+ if (!x) {
+ return undefined;
+ }
+
+ const tooltipWidth = 300;
+ const screenWidth = window.innerWidth;
+ const newX = x;
+
+ if (newX + tooltipWidth > screenWidth) {
+ return screenWidth - tooltipWidth;
+ }
+ return newX;
+ };
+
return (
-
- {visible.map((item, index) => {
- // If we have a
| component, payload can be nested
- const payload = item.payload.payload ?? item.payload;
- const data = (
- item.dataKey.includes(':')
- ? // @ts-expect-error
- payload[`${item.dataKey.split(':')[0]}:payload`]
- : payload
- ) as IRechartPayloadItem;
+
+
+ {visible.map((item, index) => {
+ // If we have a
| component, payload can be nested
+ const payload = item.payload.payload ?? item.payload;
+ const data = (
+ item.dataKey.includes(':')
+ ? // @ts-expect-error
+ payload[`${item.dataKey.split(':')[0]}:payload`]
+ : payload
+ ) as IRechartPayloadItem;
- return (
-
- {index === 0 && data.date && (
-
-
{formatDate(new Date(data.date))}
-
- )}
-
-
-
-
-
-
+ return (
+
+ {index === 0 && data.date && (
+
+
{formatDate(new Date(data.date))}
-
-
- {number.formatWithUnit(data.count, unit)}
- {!!data.previous && (
-
- ({number.formatWithUnit(data.previous.value, unit)})
-
- )}
+ )}
+
+
+
+
+
+
+
+
+ {number.formatWithUnit(data.count, unit)}
+ {!!data.previous && (
+
+ ({number.formatWithUnit(data.previous.value, unit)})
+
+ )}
+
-
+
+
-
-
- );
- })}
- {hidden.length > 0 && (
-
and {hidden.length} more...
- )}
-
+
+ );
+ })}
+ {hidden.length > 0 && (
+
+ and {hidden.length} more...
+
+ )}
+
+
);
}
diff --git a/apps/dashboard/src/components/report/chart/ReportHistogramChart.tsx b/apps/dashboard/src/components/report/chart/ReportHistogramChart.tsx
index 60c2db17..a9559429 100644
--- a/apps/dashboard/src/components/report/chart/ReportHistogramChart.tsx
+++ b/apps/dashboard/src/components/report/chart/ReportHistogramChart.tsx
@@ -6,10 +6,9 @@ import { useVisibleSeries } from '@/hooks/useVisibleSeries';
import type { IChartData } from '@/trpc/client';
import { cn } from '@/utils/cn';
import { getChartColor, theme } from '@/utils/theme';
+import { useTheme } from 'next-themes';
import { Bar, BarChart, CartesianGrid, Tooltip, XAxis, YAxis } from 'recharts';
-import type { IInterval } from '@openpanel/validation';
-
import { getYAxisWidth } from './chart-utils';
import { useChartContext } from './ChartProvider';
import { ReportChartTooltip } from './ReportChartTooltip';
@@ -21,7 +20,11 @@ interface ReportHistogramChartProps {
}
function BarHover({ x, y, width, height, top, left, right, bottom }: any) {
- const bg = theme?.colors?.slate?.['200'] as string;
+ const themeMode = useTheme();
+ const bg =
+ themeMode?.theme === 'dark'
+ ? theme.colors['def-100']
+ : theme.colors['def-300'];
return (
-
-
+
+
{({ width, height }) => (
-
+
{
return (
+
+
+
+
+
+
{previous && (
)}
);
diff --git a/apps/dashboard/src/components/report/chart/ReportLineChart.tsx b/apps/dashboard/src/components/report/chart/ReportLineChart.tsx
index ece9e258..3e542f63 100644
--- a/apps/dashboard/src/components/report/chart/ReportLineChart.tsx
+++ b/apps/dashboard/src/components/report/chart/ReportLineChart.tsx
@@ -45,7 +45,11 @@ export function ReportLineChart({ data }: ReportLineChartProps) {
endDate,
range,
lineType,
+ aspectRatio,
+ hideXAxis,
+ hideYAxis,
} = useChartContext();
+ const dataLength = data.series[0]?.data?.length || 0;
const references = api.reference.getChartReferences.useQuery(
{
projectId,
@@ -120,11 +124,10 @@ export function ReportLineChart({ data }: ReportLineChartProps) {
}, [series]);
const isAreaStyle = series.length === 1;
-
return (
<>
-
-
+
+
{({ width, height }) => (
))}
} />
React.ReactNode;
+ aspectRatio?: number;
+ children:
+ | ((props: { width: number; height: number }) => React.ReactNode)
+ | React.ReactNode;
}
-export function ResponsiveContainer({ children }: ResponsiveContainerProps) {
+export function ResponsiveContainer({
+ children,
+ aspectRatio = 0.5625,
+}: ResponsiveContainerProps) {
const maxHeight = 300;
- const minHeight = 200;
+
return (
-
- {({ width }) =>
- children({
- width,
- height: Math.min(maxHeight, width * 0.5625),
- })
- }
-
+ {typeof children === 'function' ? (
+
+ {({ width }) =>
+ children({
+ width,
+ height: Math.min(
+ maxHeight,
+ width * aspectRatio || DEFAULT_ASPECT_RATIO
+ ),
+ })
+ }
+
+ ) : (
+ children
+ )}
);
}
diff --git a/apps/dashboard/src/components/report/chart/index.tsx b/apps/dashboard/src/components/report/chart/index.tsx
index 2daac349..cdbd0419 100644
--- a/apps/dashboard/src/components/report/chart/index.tsx
+++ b/apps/dashboard/src/components/report/chart/index.tsx
@@ -1,6 +1,7 @@
'use client';
import { Suspense, useEffect, useState } from 'react';
+import * as Portal from '@radix-ui/react-portal';
import type { IChartProps } from '@openpanel/validation';
@@ -24,14 +25,18 @@ export function ChartRoot(props: IChartContextType) {
return props.chartType === 'metric' ? (
) : (
-
+
);
}
return (
:
+ props.chartType === 'metric' ? (
+
+ ) : (
+
+ )
}
>
@@ -49,9 +54,13 @@ interface ChartRootShortcutProps {
interval?: IChartProps['interval'];
events: IChartProps['events'];
breakdowns?: IChartProps['breakdowns'];
+ lineType?: IChartProps['lineType'];
+ hideXAxis?: boolean;
+ aspectRatio?: number;
}
export const ChartRootShortcut = ({
+ hideXAxis,
projectId,
range = '7d',
previous = false,
@@ -59,19 +68,25 @@ export const ChartRootShortcut = ({
interval = 'day',
events,
breakdowns,
+ aspectRatio,
+ lineType = 'monotone',
}: ChartRootShortcutProps) => {
return (
-
+
+
+
);
};
diff --git a/apps/dashboard/src/components/ui/table.tsx b/apps/dashboard/src/components/ui/table.tsx
index fdddb30b..a04429a1 100644
--- a/apps/dashboard/src/components/ui/table.tsx
+++ b/apps/dashboard/src/components/ui/table.tsx
@@ -110,6 +110,39 @@ const TableCaption = React.forwardRef<
));
TableCaption.displayName = 'TableCaption';
+export function TableSkeleton({
+ rows = 10,
+ cols = 2,
+}: {
+ rows?: number;
+ cols?: number;
+}) {
+ return (
+
+
+
+ {Array.from({ length: cols }).map((_, j) => (
+
+
+
+ ))}
+
+
+
+ {Array.from({ length: rows }).map((_, i) => (
+
+ {Array.from({ length: cols }).map((_, j) => (
+
+
+
+ ))}
+
+ ))}
+
+
+ );
+}
+
export {
Table,
TableHeader,
diff --git a/apps/dashboard/src/components/widget.tsx b/apps/dashboard/src/components/widget.tsx
index b806ebcb..52141e7c 100644
--- a/apps/dashboard/src/components/widget.tsx
+++ b/apps/dashboard/src/components/widget.tsx
@@ -37,7 +37,7 @@ export function WidgetTitle({
)}
>
{Icon && (
-
+
)}
diff --git a/packages/constants/index.ts b/packages/constants/index.ts
index 604f7800..fba70c97 100644
--- a/packages/constants/index.ts
+++ b/packages/constants/index.ts
@@ -1,5 +1,6 @@
import { isSameDay, isSameMonth } from 'date-fns';
+export const DEFAULT_ASPECT_RATIO = 0.5625;
export const NOT_SET_VALUE = '(not set)';
export const timeWindows = {
diff --git a/packages/db/src/services/event.service.ts b/packages/db/src/services/event.service.ts
index 71888789..a3ce3cc0 100644
--- a/packages/db/src/services/event.service.ts
+++ b/packages/db/src/services/event.service.ts
@@ -28,6 +28,15 @@ export type IImportedEvent = Omit<
properties: Record
;
};
+export type IServicePage = {
+ path: string;
+ count: number;
+ project_id: string;
+ first_seen: string;
+ title: string;
+ origin: string;
+};
+
export interface IClickhouseEvent {
id: string;
name: string;
@@ -493,3 +502,30 @@ export async function getLastScreenViewFromProfileId({
return eventInDb || null;
}
+
+export async function getTopPages({
+ projectId,
+ cursor,
+ take,
+ search,
+}: {
+ projectId: string;
+ cursor?: number;
+ take: number;
+ search?: string;
+}) {
+ const res = await chQuery(`
+ SELECT path, count(*) as count, project_id, first_value(created_at) as first_seen, max(properties['__title']) as title, origin
+ FROM events_v2
+ WHERE name = 'screen_view'
+ AND project_id = ${escape(projectId)}
+ AND created_at > now() - INTERVAL 30 DAY
+ ${search ? `AND path LIKE '%${search}%'` : ''}
+ GROUP BY path, project_id, origin
+ ORDER BY count desc
+ LIMIT ${take}
+ OFFSET ${Math.max(0, (cursor ?? 0) * take)}
+ `);
+
+ return res;
+}
diff --git a/packages/trpc/src/routers/event.ts b/packages/trpc/src/routers/event.ts
index 084ba703..28a30040 100644
--- a/packages/trpc/src/routers/event.ts
+++ b/packages/trpc/src/routers/event.ts
@@ -8,6 +8,7 @@ import {
db,
getEventList,
getEvents,
+ getTopPages,
TABLE_NAMES,
} from '@openpanel/db';
import { zChartEventFilter } from '@openpanel/validation';
@@ -69,7 +70,6 @@ export const eventRouter = createTRPCRouter({
z.object({
projectId: z.string(),
cursor: z.number().optional(),
- limit: z.number().default(8),
profileId: z.string().optional(),
take: z.number().default(50),
events: z.array(z.string()).optional(),
@@ -165,4 +165,17 @@ export const eventRouter = createTRPCRouter({
count: counts[0]?.count ?? 0,
};
}),
+
+ pages: protectedProcedure
+ .input(
+ z.object({
+ projectId: z.string(),
+ cursor: z.number().optional(),
+ take: z.number().default(20),
+ search: z.string().optional(),
+ })
+ )
+ .query(async ({ input }) => {
+ return getTopPages(input);
+ }),
});
diff --git a/packages/trpc/src/routers/organization.ts b/packages/trpc/src/routers/organization.ts
index 42802e1e..6bbbe36b 100644
--- a/packages/trpc/src/routers/organization.ts
+++ b/packages/trpc/src/routers/organization.ts
@@ -5,7 +5,7 @@ import { z } from 'zod';
import { db } from '@openpanel/db';
import { zInviteUser } from '@openpanel/validation';
-import { getOrganizationAccess, getOrganizationAccessCached } from '../access';
+import { getOrganizationAccess } from '../access';
import { TRPCAccessError } from '../errors';
import { createTRPCRouter, protectedProcedure } from '../trpc';
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d585d543..72b2650f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -233,6 +233,9 @@ importers:
'@radix-ui/react-popover':
specifier: ^1.0.7
version: 1.0.7(@types/react-dom@18.2.19)(@types/react@18.2.56)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-portal':
+ specifier: ^1.1.1
+ version: 1.1.1(@types/react-dom@18.2.19)(@types/react@18.2.56)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-progress':
specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.56)(react-dom@18.2.0)(react@18.2.0)
@@ -5574,6 +5577,19 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-compose-refs@1.1.0(@types/react@18.2.56)(react@18.2.0):
+ resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.56
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-context@1.0.0(react@18.2.0):
resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==}
peerDependencies:
@@ -5982,6 +5998,27 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-portal@1.1.1(@types/react-dom@18.2.19)(@types/react@18.2.56)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.19)(@types/react@18.2.56)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.56)(react@18.2.0)
+ '@types/react': 18.2.56
+ '@types/react-dom': 18.2.19
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-presence@1.0.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==}
peerDependencies:
@@ -6050,6 +6087,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-primitive@2.0.0(@types/react-dom@18.2.19)(@types/react@18.2.56)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@radix-ui/react-slot': 1.1.0(@types/react@18.2.56)(react@18.2.0)
+ '@types/react': 18.2.56
+ '@types/react-dom': 18.2.19
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-progress@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.56)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==}
peerDependencies:
@@ -6185,6 +6242,20 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-slot@1.1.0(@types/react@18.2.56)(react@18.2.0):
+ resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.56)(react@18.2.0)
+ '@types/react': 18.2.56
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.56)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==}
peerDependencies:
@@ -6450,6 +6521,19 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.2.56)(react@18.2.0):
+ resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.56
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-use-previous@1.0.1(@types/react@18.2.56)(react@18.2.0):
resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==}
peerDependencies: