fast fix for typechecks

This commit is contained in:
Nate Kelley 2025-08-21 13:55:04 -06:00
parent 0a247421c6
commit 51344c527f
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
18 changed files with 243 additions and 199 deletions

View File

@ -1,7 +1,7 @@
import type { ShareAssetType, ShareRole, WorkspaceShareRole } from '@buster/server-shared/share';
import last from 'lodash/last';
import React, { useMemo } from 'react';
import type { DropdownItem } from '@/components/ui/dropdown';
import type { IDropdownItem } from '@/components/ui/dropdown';
import { Dropdown } from '@/components/ui/dropdown';
import { ChevronDown } from '@/components/ui/icons/NucleoIconFilled';
import { Paragraph, Text } from '@/components/ui/typography';
@ -39,7 +39,7 @@ export const AccessDropdown: React.FC<AccessDropdownProps> = React.memo(
const items = useMemo(() => {
const isWorkspace = props.type === 'workspace';
const baseItems: DropdownItem<DropdownValue>[] = [
const baseItems: IDropdownItem<DropdownValue>[] = [
...(isWorkspace ? workspaceItems : itemsRecord[assetType] || []),
];
@ -133,7 +133,7 @@ export const AccessDropdown: React.FC<AccessDropdownProps> = React.memo(
AccessDropdown.displayName = 'AccessDropdown';
const metricItems: DropdownItem<ShareRole>[] = [
const metricItems: IDropdownItem<ShareRole>[] = [
{
value: 'full_access',
label: 'Full access',
@ -151,7 +151,7 @@ const metricItems: DropdownItem<ShareRole>[] = [
},
];
const dashboardItems: DropdownItem<ShareRole>[] = [
const dashboardItems: IDropdownItem<ShareRole>[] = [
{
value: 'full_access',
label: 'Full access',
@ -169,7 +169,7 @@ const dashboardItems: DropdownItem<ShareRole>[] = [
},
];
const collectionItems: DropdownItem<ShareRole>[] = [
const collectionItems: IDropdownItem<ShareRole>[] = [
{
value: 'full_access',
label: 'Full access',
@ -187,7 +187,7 @@ const collectionItems: DropdownItem<ShareRole>[] = [
},
];
const reportItems: DropdownItem<ShareRole>[] = [
const reportItems: IDropdownItem<ShareRole>[] = [
{
value: 'full_access',
label: 'Full access',
@ -205,7 +205,7 @@ const reportItems: DropdownItem<ShareRole>[] = [
},
];
const workspaceItems: DropdownItem<WorkspaceShareRole>[] = [
const workspaceItems: IDropdownItem<WorkspaceShareRole>[] = [
{
value: 'full_access',
label: 'Full access',
@ -228,7 +228,7 @@ const workspaceItems: DropdownItem<WorkspaceShareRole>[] = [
},
];
const itemsRecord: Record<ShareAssetType, DropdownItem<ShareRole>[]> = {
const itemsRecord: Record<ShareAssetType, IDropdownItem<ShareRole>[]> = {
dashboard: dashboardItems,
metric: metricItems,
collection: collectionItems,
@ -236,15 +236,15 @@ const itemsRecord: Record<ShareAssetType, DropdownItem<ShareRole>[]> = {
report: reportItems,
};
const OWNER_ITEM: DropdownItem<DropdownValue> = {
const OWNER_ITEM: IDropdownItem<DropdownValue> = {
value: 'owner',
label: 'Owner',
secondaryLabel: 'Owner of the asset.',
};
const WORKSPACE_NOT_SHARED_ITEM: DropdownItem<DropdownValue> = last(
const WORKSPACE_NOT_SHARED_ITEM: IDropdownItem<DropdownValue> = last(
workspaceItems
) as DropdownItem<DropdownValue>;
) as IDropdownItem<DropdownValue>;
const FooterContent = React.memo(() => {
return (

View File

@ -8,40 +8,61 @@ import { Link } from '@/components/ui/icons';
import { Input } from '@/components/ui/inputs';
import { Text } from '@/components/ui/typography';
import { useBusterNotifications } from '@/context/BusterNotifications';
import { useMemoizedFn } from '@/hooks';
import { cn } from '@/lib/classMerge';
import { BusterRoutes, createBusterRoute } from '@/routes';
import { useBuildLocation } from '../../../context/Routes/useRouteBuilder';
import type { ShareMenuContentBodyProps } from './ShareMenuContentBody';
export const ShareMenuContentEmbed: React.FC<ShareMenuContentBodyProps> = React.memo(
({ className, assetType, assetId }) => {
const buildLocation = useBuildLocation();
const { openSuccessMessage } = useBusterNotifications();
const embedURL = useMemo(() => {
let url = '';
if (assetType === 'metric') {
url = createBusterRoute({
route: BusterRoutes.EMBED_METRIC_ID,
metricId: assetId,
});
return buildLocation({
to: '/embed/metric/$metricId',
params: {
metricId: assetId,
},
}).href;
}
if (assetType === 'dashboard') {
url = createBusterRoute({
route: BusterRoutes.EMBED_DASHBOARD_ID,
dashboardId: assetId,
});
return buildLocation({
to: '/embed/dashboard/$dashboardId',
params: {
dashboardId: assetId,
},
}).href;
}
return url;
}, [assetType, assetId]);
if (assetType === 'report') {
return buildLocation({
to: '/embed/report/$reportId',
params: {
reportId: assetId,
},
}).href;
}
const onCopyLink = useMemoizedFn(() => {
if (assetType === 'chat') {
return '';
}
if (assetType === 'collection') {
return '';
}
const _exhaustiveCheck: never = assetType;
return '';
}, [assetType, assetId, buildLocation]);
const onCopyLink = () => {
const url = window.location.origin + embedURL;
navigator.clipboard.writeText(url);
openSuccessMessage('Link copied to clipboard');
});
};
return (
<div className={cn('flex flex-col', className)}>
@ -73,7 +94,7 @@ export const ShareMenuContentEmbedFooter = ({
const { mutateAsync: onShareCollection } = useUpdateCollectionShare();
const { openSuccessMessage } = useBusterNotifications();
const onPublish = useMemoizedFn(async () => {
const onPublish = async () => {
const payload: Parameters<typeof onShareMetric>[0] = {
id: assetId,
params: {
@ -88,7 +109,7 @@ export const ShareMenuContentEmbedFooter = ({
await onShareCollection(payload);
}
openSuccessMessage('Succuessfully published');
});
};
return (
<div className="bg-item-hover flex justify-start overflow-hidden rounded-b px-3 py-2.5">

View File

@ -2,7 +2,6 @@ import type { ShareAssetType } from '@buster/server-shared/share';
import React, { useMemo } from 'react';
import type { SegmentedItem } from '@/components/ui/segmented';
import { AppSegmented } from '@/components/ui/segmented';
import { useMemoizedFn } from '@/hooks';
import { CopyLinkButton } from './CopyLinkButton';
export enum ShareMenuTopBarOptions {
@ -41,9 +40,9 @@ export const ShareMenuTopBar: React.FC<{
.map((o) => ({ ...o, show: undefined }));
}, [assetType, canEditPermissions]);
const onChange = useMemoizedFn((v: SegmentedItem<ShareMenuTopBarOptions>) => {
const onChange = (v: SegmentedItem<ShareMenuTopBarOptions>) => {
onChangeSelectedOption(v.value);
});
};
return (
<div className="flex items-center justify-between">

View File

@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/nextjs';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { WorkspaceAvatar } from './WorkspaceAvatar';
const meta: Meta<typeof WorkspaceAvatar> = {

View File

@ -1,6 +1,6 @@
import type { VerificationStatus } from '@buster/server-shared/share';
import { useMemo } from 'react';
import type { DropdownItem, DropdownProps } from '@/components/ui/dropdown';
import type { DropdownProps, IDropdownItem } from '@/components/ui/dropdown';
import { getTooltipText } from './helpers';
import { StatusBadgeIndicator } from './StatusBadgeIndicator';
@ -27,7 +27,7 @@ export const useStatusDropdownContent = ({
'showIndex' | 'items' | 'emptyStateText' | 'menuHeader' | 'selectType'
> => {
const items = useMemo(() => {
return statuses.map<DropdownItem<VerificationStatus>>((status) => {
return statuses.map<IDropdownItem<VerificationStatus>>((status) => {
const requiresAdmin = requiresAdminItems.includes(status);
return {
label: getTooltipText(status),

View File

@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import { useGetMetric } from '@/api/buster_rest/metrics';
import { DropdownContent, type IDropdownItem } from '@/components/ui/dropdown';
import { DropdownContent, type IDropdownItem, type IDropdownItems } from '@/components/ui/dropdown';
import { History, Star, WandSparkle } from '@/components/ui/icons';
import { Star as StarFilled } from '@/components/ui/icons/NucleoIconFilled';
import { FollowUpWithAssetContent } from '../assets/FollowUpWithAsset';
@ -51,7 +51,7 @@ export const useFavoriteMetricSelectMenu = ({ metricId }: { metricId: string })
name: name || '',
});
const item: DropdownItem = useMemo(
const item: IDropdownItem = useMemo(
() => ({
label: isFavorited ? 'Remove from favorites' : 'Add to favorites',
value: 'add-to-favorites',
@ -65,7 +65,7 @@ export const useFavoriteMetricSelectMenu = ({ metricId }: { metricId: string })
return item;
};
export const useMetricDrilldownItem = ({ metricId }: { metricId: string }): DropdownItem => {
export const useMetricDrilldownItem = ({ metricId }: { metricId: string }): IDropdownItem => {
return useMemo(
() => ({
value: 'drilldown',

View File

@ -1,45 +1,53 @@
import type { UserFavorite } from '@buster/server-shared/user';
import type { OptionsTo } from '@/types/routes';
import type { RegisteredRouter } from '@tanstack/react-router';
import { defineLink } from '@/lib/routes';
export const getFavoriteRoute = (favorite: Pick<UserFavorite, 'asset_type' | 'id'>): OptionsTo => {
export const getFavoriteRoute = <
TRouter extends RegisteredRouter = RegisteredRouter,
TOptions = unknown,
TFrom extends string = string,
>(
favorite: Pick<UserFavorite, 'asset_type' | 'id'>
) => {
if (favorite.asset_type === 'chat') {
return {
return defineLink({
to: '/app/chats/$chatId',
params: { chatId: favorite.id },
} as const satisfies OptionsTo;
params: { chatId: '123' },
});
}
if (favorite.asset_type === 'metric') {
return {
return defineLink({
to: '/app/metrics/$metricId',
params: { metricId: favorite.id },
} as const satisfies OptionsTo;
});
}
if (favorite.asset_type === 'dashboard') {
return {
return defineLink({
to: '/app/dashboards/$dashboardId',
params: { dashboardId: favorite.id },
} as const satisfies OptionsTo;
});
}
if (favorite.asset_type === 'collection') {
return {
return defineLink({
to: '/app/collections/$collectionId',
params: { collectionId: favorite.id },
} as const satisfies OptionsTo;
});
}
if (favorite.asset_type === 'report') {
return {
return defineLink({
to: '/app/reports/$reportId',
params: { reportId: favorite.id },
} as const satisfies OptionsTo;
});
}
const _exhaustiveCheck: never = favorite.asset_type;
return {
return defineLink({
to: '/app/chats/$chatId',
params: { chatId: favorite.id },
} as const satisfies OptionsTo;
});
};

View File

@ -1,4 +1,4 @@
import { type LinkProps, useMatchRoute } from '@tanstack/react-router';
import { useMatchRoute } from '@tanstack/react-router';
import { useMemo } from 'react';
import {
useDeleteUserFavorite,
@ -6,9 +6,11 @@ import {
useUpdateUserFavorites,
} from '@/api/buster_rest/users';
import { assetTypeToIcon } from '@/components/features/icons/assetIcons';
import type { ISidebarGroup, ISidebarItem } from '@/components/ui/sidebar';
import type { ISidebarGroup } from '@/components/ui/sidebar';
import { useMount } from '@/hooks/useMount';
import { assetParamsToRoute } from '@/lib/assets/assetParamsToRoute';
import { createSidebarItem } from '../../../ui/sidebar/create-sidebar-item';
import { getFavoriteRoute } from './getFavoriteRoute';
export const useFavoriteSidebarPanel = (): ISidebarGroup | null => {
@ -29,18 +31,17 @@ export const useFavoriteSidebarPanel = (): ISidebarGroup | null => {
id: 'favorites',
isSortable: true,
onItemsReorder: updateUserFavorites,
items: favorites.map<ISidebarItem>((favorite) => {
items: favorites.map((favorite) => {
const Icon = assetTypeToIcon(favorite.asset_type);
const route = getFavoriteRoute(favorite);
// const isActive = !!matcher({ ...route, fuzzy: true } as Parameters<typeof matcher>[0]);
return {
const link = getFavoriteRoute(favorite);
return createSidebarItem({
label: favorite.name,
icon: <Icon />,
route,
link,
id: favorite.id,
preload: 'intent',
onRemove: () => deleteUserFavorite([favorite.id]),
};
});
}),
} satisfies ISidebarGroup;
}, [favorites, matcher, deleteUserFavorite]);

View File

@ -1,35 +1,31 @@
import Link from 'next/link';
import { Link } from '@tanstack/react-router';
import React, { useCallback, useMemo, useRef } from 'react';
import { Button } from '@/components/ui/buttons';
import { History, Xmark } from '@/components/ui/icons';
import { Check3 } from '@/components/ui/icons/NucleoIconFilled';
import { AppPageLayout } from '@/components/ui/layouts';
import Check3 from '@/components/ui/icons/NucleoIconFilled/check-3';
import { AppPageLayout } from '@/components/ui/layouts/AppPageLayout';
import { CircleSpinnerLoader } from '@/components/ui/loaders';
import { AppTooltip } from '@/components/ui/tooltip';
import { Text } from '@/components/ui/typography';
import { useGetFileLink } from '@/context/Assets/useGetFileLink';
import { useMemoizedFn, useMount } from '@/hooks';
import { useChatLayoutContextSelector } from '@/layouts/ChatLayout';
import { useCloseVersionHistory } from '@/layouts/ChatLayout/FileContainer/FileContainerHeader/FileContainerHeaderVersionHistory';
import { timeFromNow, timeout } from '@/lib';
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
import { useMount } from '@/hooks/useMount';
import { cn } from '@/lib/classMerge';
import { useListVersionHistories } from './useListVersionHistories';
import { timeFromNow } from '@/lib/date';
import { timeout } from '@/lib/timeout';
export const VersionHistoryPanel = React.memo(
({ assetId, type }: { assetId: string; type: 'metric' | 'dashboard' }) => {
const chatId = useChatLayoutContextSelector((x) => x.chatId);
const {
listItems,
onPrefetchAsset,
restoringVersion,
currentVersionNumber,
selectedQueryVersion,
onClickRestoreVersion,
} = useListVersionHistories({
assetId,
type,
});
const { getFileLink } = useGetFileLink();
// const {
// listItems,
// onPrefetchAsset,
// restoringVersion,
// currentVersionNumber,
// selectedQueryVersion,
// onClickRestoreVersion,
// } = useListVersionHistories({
// assetId,
// type,
// });
const bodyRef = useRef<HTMLDivElement>(null);
@ -51,7 +47,8 @@ export const VersionHistoryPanel = React.memo(
headerBorderVariant="ghost"
>
<div ref={bodyRef} className="mx-2 mb-1.5 flex flex-col">
{listItems?.map((item) => (
TODO
{/* {listItems?.map((item) => (
<ListItem
key={item.version_number}
{...item}
@ -70,109 +67,107 @@ export const VersionHistoryPanel = React.memo(
}) || ''
}
/>
))}
))} */}
</div>
</AppPageLayout>
);
}
);
const ListItem = React.memo(
({
version_number,
updated_at,
selected,
showRestoreButton,
link,
restoringVersion,
onClickRestoreVersion,
onPrefetchAsset,
}: {
version_number: number;
updated_at: string;
selected: boolean;
showRestoreButton: boolean;
restoringVersion: number | null;
onClickRestoreVersion: (versionNumber: number) => void;
onPrefetchAsset: (versionNumber: number, link: string) => Promise<void>;
link: string;
}) => {
const routePrefetchTimeoutRef = useRef<NodeJS.Timeout>();
// const ListItem = React.memo(
// ({
// version_number,
// updated_at,
// selected,
// showRestoreButton,
// link,
// restoringVersion,
// onClickRestoreVersion,
// onPrefetchAsset,
// }: {
// version_number: number;
// updated_at: string;
// selected: boolean;
// showRestoreButton: boolean;
// restoringVersion: number | null;
// onClickRestoreVersion: (versionNumber: number) => void;
// onPrefetchAsset: (versionNumber: number, link: string) => Promise<void>;
// link: string;
// }) => {
// const routePrefetchTimeoutRef = useRef<NodeJS.Timeout>(null);
const onHoverLink = useMemoizedFn(() => {
// Prefetch route after 50ms
routePrefetchTimeoutRef.current = setTimeout(() => {
onPrefetchAsset(version_number, link);
}, 125);
});
// const onHoverLink = useMemoizedFn(() => {
// // Prefetch route after 50ms
// routePrefetchTimeoutRef.current = setTimeout(() => {
// onPrefetchAsset(version_number, link);
// }, 125);
// });
const onHoverEnd = useCallback(() => {
if (routePrefetchTimeoutRef.current) {
clearTimeout(routePrefetchTimeoutRef.current);
}
}, []);
// const onHoverEnd = useCallback(() => {
// if (routePrefetchTimeoutRef.current) {
// clearTimeout(routePrefetchTimeoutRef.current);
// }
// }, []);
const isRestoringVersion = restoringVersion === version_number;
// const isRestoringVersion = restoringVersion === version_number;
return (
<Link prefetch={false} href={link} onMouseEnter={onHoverLink} onMouseLeave={onHoverEnd}>
<div
className={cn(
'group hover:bg-item-hover flex cursor-pointer items-center justify-between space-x-2 rounded px-2.5 py-1.5',
selected && 'bg-item-select hover:bg-item-select selected-version'
)}
>
<div className="flex flex-col justify-center space-y-0.5">
<Text>{`Version ${version_number}`}</Text>
<Text size={'xs'} variant={'secondary'}>
{timeFromNow(updated_at, false)}
</Text>
</div>
// return (
// <Link prefetch={false} href={link} onMouseEnter={onHoverLink} onMouseLeave={onHoverEnd}>
// <div
// className={cn(
// 'group hover:bg-item-hover flex cursor-pointer items-center justify-between space-x-2 rounded px-2.5 py-1.5',
// selected && 'bg-item-select hover:bg-item-select selected-version'
// )}
// >
// <div className="flex flex-col justify-center space-y-0.5">
// <Text>{`Version ${version_number}`}</Text>
// <Text size={'xs'} variant={'secondary'}>
// {timeFromNow(updated_at, false)}
// </Text>
// </div>
<div className="text-icon-color animate-in fade-in-0 flex items-center space-x-2 duration-200">
{showRestoreButton && (
<AppTooltip title={restoringVersion ? 'Restoring...' : 'Restore version'}>
<button
type="button"
onClick={(e) => {
if (restoringVersion) return;
// <div className="text-icon-color animate-in fade-in-0 flex items-center space-x-2 duration-200">
// {showRestoreButton && (
// <AppTooltip title={restoringVersion ? 'Restoring...' : 'Restore version'}>
// <button
// type="button"
// onClick={(e) => {
// if (restoringVersion) return;
e.stopPropagation();
e.preventDefault();
onClickRestoreVersion(version_number);
}}
className={cn(
'hover:bg-gray-light/20 hover:text-foreground -mr-1 cursor-pointer rounded p-1 opacity-0 group-hover:block group-hover:opacity-100',
isRestoringVersion && 'cursor-not-allowed opacity-100!'
)}
>
{isRestoringVersion ? <CircleSpinnerLoader size={12} /> : <History />}
</button>
</AppTooltip>
)}
// e.stopPropagation();
// e.preventDefault();
// onClickRestoreVersion(version_number);
// }}
// className={cn(
// 'hover:bg-gray-light/20 hover:text-foreground -mr-1 cursor-pointer rounded p-1 opacity-0 group-hover:block group-hover:opacity-100',
// isRestoringVersion && 'cursor-not-allowed opacity-100!'
// )}
// >
// {isRestoringVersion ? <CircleSpinnerLoader size={12} /> : <History />}
// </button>
// </AppTooltip>
// )}
{selected && (
<div className="group-hover:opacity-100">
<Check3 />
</div>
)}
</div>
</div>
</Link>
);
}
);
ListItem.displayName = 'ListItem';
// {selected && (
// <div className="group-hover:opacity-100">
// <Check3 />
// </div>
// )}
// </div>
// </div>
// </Link>
// );
// }
// );
// ListItem.displayName = 'ListItem';
const PanelHeader = React.memo(() => {
const { href } = useCloseVersionHistory();
return (
<div className="flex w-full items-center justify-between">
<Text>Version history</Text>
<Link href={href} prefetch className="-mr-1.5">
{/* <Link href={href} className="-mr-1.5">
<Button variant="ghost" prefix={<Xmark />} />
</Link>
</Link> */}
</div>
);
});

View File

@ -1,2 +1 @@
export * from './useListVersionHistories';
export * from './VersionHistoryPanel';

View File

@ -1,5 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Breadcrumb } from './Breadcrumb';
import { createBreadcrumbItem } from './create-breadcrumb';
const meta: Meta<typeof Breadcrumb> = {
title: 'UI/Breadcrumb',
@ -16,8 +17,8 @@ type Story = StoryObj<typeof Breadcrumb>;
export const Default: Story = {
args: {
items: [
{ label: 'Home', route: { to: '/app/home' } },
{ label: 'Datasets', route: { to: '/app/datasets' } },
{ label: 'Home', link: { to: '/app/home' } },
{ label: 'Datasets', link: { to: '/app/datasets' } },
{ label: 'Current Dataset' },
],
},
@ -26,13 +27,13 @@ export const Default: Story = {
export const WithDropdown: Story = {
args: {
items: [
{ label: 'Home', route: { to: '/app/home' } },
{ label: 'Home', link: { to: '/app/home' } },
{
label: null,
dropdown: [
{ label: 'Dataset A', route: { to: '/app/datasets' } },
{ label: 'Dataset B', route: { to: '/app/datasets' } },
{ label: 'Dataset C', route: { to: '/app/datasets' } },
{ label: 'Dataset A', link: { to: '/app/datasets' } },
{ label: 'Dataset B', link: { to: '/app/datasets' } },
{ label: 'Dataset C', link: { to: '/app/datasets' } },
],
},
{ label: 'Current Dataset' },
@ -43,9 +44,12 @@ export const WithDropdown: Story = {
export const CustomActiveIndex: Story = {
args: {
items: [
{ label: 'Home', route: { to: '/app/home' } },
{ label: 'Datasets', route: { to: '/app/datasets' } },
{ label: 'Settings', route: { to: '/app/chats/$chatId', params: { chatId: '123' } } },
{ label: 'Home', link: { to: '/app/home' } },
{ label: 'Datasets', link: { to: '/app/datasets' } },
createBreadcrumbItem({
label: 'Settings',
link: { to: '/app/chats/$chatId', params: { chatId: '123' } },
}),
{ label: 'Profile' },
],
activeIndex: 2,

View File

@ -1,7 +1,7 @@
import { Link, type LinkProps } from '@tanstack/react-router';
import * as React from 'react';
import { cn } from '@/lib/classMerge';
import type { OptionsTo } from '@/types/routes';
import type { ILinkProps } from '@/types/routes';
import { ChevronLeft } from '../icons';
import { Button } from './Button';
@ -11,7 +11,7 @@ interface BackButtonProps {
size?: 'medium' | 'large';
className?: string;
style?: React.CSSProperties;
linkUrl?: OptionsTo;
linkUrl?: ILinkProps;
activeProps?: LinkProps['activeProps'];
activeOptions?: LinkProps['activeOptions'];
}

View File

@ -6,7 +6,8 @@ import React from 'react';
import { fn } from 'storybook/test';
import { Button } from '../buttons/Button';
import { PaintRoller, Star, Storage } from '../icons';
import { Dropdown, type DropdownItems } from './Dropdown';
import { Dropdown } from './Dropdown';
import type { IDropdownItems } from './dropdown-items.types';
const meta: Meta<typeof Dropdown> = {
title: 'UI/Dropdowns/Dropdown',
@ -249,7 +250,7 @@ export const WithSelectionMultiple: Story = {
render: () => {
const [selectedIds, setSelectedIds] = React.useState<Set<string>>(new Set(['3']));
const items: DropdownItems = [
const items: IDropdownItems = [
{
value: '1',
label: 'Option 1',
@ -464,7 +465,7 @@ export const WithLinksAndMultipleSelection: Story = {
render: () => {
const [selectedIds, setSelectedIds] = React.useState<Set<string>>(new Set(['2']));
const items: DropdownItems = [
const items: IDropdownItems = [
{
value: '1',
label: 'Documentation Home',

View File

@ -3,7 +3,7 @@ import get from 'lodash/get';
import React, { useMemo } from 'react';
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
import { cn } from '@/lib/classMerge';
import type { OptionsTo } from '@/types/routes';
import type { ILinkProps } from '@/types/routes';
import { CheckboxColumn } from './CheckboxColumn';
import { HEIGHT_OF_ROW } from './config';
import type { BusterListColumn, BusterListProps, BusterListRowItem } from './interfaces';
@ -160,7 +160,7 @@ const BusterListCellComponent = <T,>({
const LinkWrapper: React.FC<
{
link?: OptionsTo;
link?: ILinkProps;
children: React.ReactNode;
} & LinkProps
> = ({ link, children, preload, preloadDelay, activeOptions }) => {

View File

@ -3,7 +3,7 @@ import React from 'react';
import { Button } from '@/components/ui/buttons';
import { Plus } from '@/components/ui/icons';
import { Paragraph, Title } from '@/components/ui/typography';
import type { OptionsTo } from '@/types/routes';
import type { ILinkProps } from '@/types/routes';
export const ListEmptyStateWithButton: React.FC<{
isAdmin?: boolean;
@ -14,7 +14,7 @@ export const ListEmptyStateWithButton: React.FC<{
buttonPrefix?: React.ReactNode;
buttonSuffix?: React.ReactNode;
loading?: boolean;
link?: OptionsTo | string;
link?: ILinkProps | string;
linkButtonTarget?: '_blank' | '_self';
}> = React.memo(
({
@ -66,7 +66,7 @@ export const ListEmptyStateWithButton: React.FC<{
const ButtonWrapper: React.FC<{
children: React.ReactNode;
link?: OptionsTo | string;
link?: ILinkProps | string;
target?: '_blank' | '_self';
}> = ({ children, link, target }) => {
if (!link) return <>{children}</>;

View File

@ -1,14 +1,13 @@
import type { RegisteredRouter } from '@tanstack/react-router';
import { defineLink } from '../../../lib/routes';
import type { ISidebarGroup, ISidebarItem, ISidebarList } from './interfaces';
export function createSidebarItem<
TRouter extends RegisteredRouter,
TOptions,
TFrom extends string = string,
>(
item: ISidebarItem<TRouter, TOptions, TFrom> & { show?: boolean }
): ISidebarItem<TRouter, TOptions, TFrom> & { show?: boolean } {
return item;
>(item: ISidebarItem<TRouter, TOptions, TFrom> & { show?: boolean }) {
return item as ISidebarItem & { show?: boolean };
}
/**
@ -40,3 +39,21 @@ export function createSidebarGroup<
>(items: ISidebarGroup<TRouter, TOptions, TFrom>): ISidebarGroup<TRouter, TOptions, TFrom> {
return items;
}
const test = createSidebarItem({
label: 'Test',
id: '123',
link: {
to: '/app/chats/$chatId',
params: { chatId: '123' },
},
});
const test123 = createSidebarItem({
label: 'Test',
id: '123',
link: defineLink({
to: '/app/chats/$chatId',
params: { chatId: '123' },
}),
});

View File

@ -1,6 +1,6 @@
import type { RegisteredRouter, ValidateLinkOptions } from '@tanstack/react-router';
import type { RegisteredRouter } from '@tanstack/react-router';
import type React from 'react';
import type { ILinkOptions } from '@/types/routes';
import type { ILinkProps } from '@/types/routes';
// Base properties shared by all sidebar items
type ISidebarItemBase = {
@ -22,7 +22,7 @@ export type ISidebarItem<
> = ISidebarItemBase &
(
| {
link: ValidateLinkOptions<TRouter, TOptions, TFrom> & ILinkOptions;
link: ILinkProps<TRouter, TOptions, TFrom>;
}
| {
link?: never;

View File

@ -1,12 +1,11 @@
import type { RegisteredRouter, ValidateLinkOptions } from '@tanstack/react-router';
import type { ILinkProps } from '@/types/routes';
export function defineLink<
TRouter extends RegisteredRouter,
TOptions,
TFrom extends string = string,
>(
link: ValidateLinkOptions<TRouter, TOptions, TFrom>
): ValidateLinkOptions<TRouter, TOptions, TFrom> {
>(link: ILinkProps<TRouter, TOptions, TFrom>): ILinkProps<TRouter, TOptions, TFrom> {
return link;
}