share menu update

This commit is contained in:
Nate Kelley 2025-03-17 16:20:15 -06:00
parent 768d19a2fd
commit bc3558315f
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
9 changed files with 85 additions and 81 deletions

38
web/package-lock.json generated
View File

@ -65,7 +65,7 @@
"react": "^18",
"react-color": "^2.19.3",
"react-data-grid": "7.0.0-beta.47",
"react-day-picker": "^9.6.2",
"react-day-picker": "8.10.1",
"react-dom": "^18",
"react-hotkeys-hook": "^4.6.1",
"react-markdown": "^10.1.0",
@ -2150,12 +2150,6 @@
"storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0"
}
},
"node_modules/@date-fns/tz": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.2.0.tgz",
"integrity": "sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==",
"license": "MIT"
},
"node_modules/@dnd-kit/accessibility": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
@ -10056,21 +10050,16 @@
}
},
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/date-fns-jalali": {
"version": "4.1.0-0",
"resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz",
"integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==",
"license": "MIT"
},
"node_modules/dayjs": {
"version": "1.11.13",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
@ -18051,24 +18040,17 @@
}
},
"node_modules/react-day-picker": {
"version": "9.6.2",
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.6.2.tgz",
"integrity": "sha512-HVrTpDUYGKbi9scU9v2N3BPGMnL5jwekGFXcyyrvpamZdgYGfzVcovsBD4/yNGxhpCIX5X/5IoLDEAayNv1EqA==",
"version": "8.10.1",
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz",
"integrity": "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==",
"license": "MIT",
"dependencies": {
"@date-fns/tz": "^1.2.0",
"date-fns": "^4.1.0",
"date-fns-jalali": "^4.1.0-0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/gpbl"
},
"peerDependencies": {
"react": ">=16.8.0"
"date-fns": "^2.28.0 || ^3.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-docgen": {

View File

@ -73,7 +73,7 @@
"react": "^18",
"react-color": "^2.19.3",
"react-data-grid": "7.0.0-beta.47",
"react-day-picker": "^9.6.2",
"react-day-picker": "8.10.1",
"react-dom": "^18",
"react-hotkeys-hook": "^4.6.1",
"react-markdown": "^10.1.0",

View File

@ -69,6 +69,7 @@ export const AccessDropdown: React.FC<{
footerContent={<FooterContent />}
footerClassName="p-0!"
onSelect={onSelectMenuItem}
sideOffset={9}
selectType="single"
align="end"
side="bottom">

View File

@ -85,3 +85,16 @@ export const ViewerPermission: Story = {
}
}
};
export const PublishedMetric: Story = {
args: {
assetId: 'metric-123',
assetType: ShareAssetType.METRIC,
shareAssetConfig: {
...mockShareConfig,
publicly_accessible: true,
public_expiry_date: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), // 7 days from now
public_enabled_by: 'test@example.com'
}
}
};

View File

@ -175,7 +175,7 @@ const ShareMenuContentShare: React.FC<{
<AccessDropdown
showRemove={false}
groupShare={false}
className="absolute right-[10px]"
className="absolute top-[50%] right-[10px] -translate-y-1/2"
shareLevel={defaultPermissionLevel}
onChangeShareLevel={onChangeAccessDropdown}
/>

View File

@ -17,6 +17,7 @@ import { DatePicker } from '@/components/ui/date';
import { useUpdateCollection } from '@/api/buster_rest/collections';
import { useUpdateMetric } from '@/api/buster_rest/metrics';
import { useUpdateDashboard } from '@/api/buster_rest/dashboards';
import { SelectSingleEventHandler } from 'react-day-picker';
export const ShareMenuContentPublish: React.FC<{
onCopyLink: () => void;
@ -127,8 +128,8 @@ export const ShareMenuContentPublish: React.FC<{
<>
<IsPublishedInfo isPublished={publicly_accessible} />
<div className="w-full!">
<Input size="small" value={url} />
<div className="flex w-full space-x-0.5">
<Input size="small" readOnly value={url} />
<Button variant="default" className="flex" prefix={<Link />} onClick={onCopyLink} />
</div>
@ -160,15 +161,18 @@ export const ShareMenuContentPublish: React.FC<{
<>
<Separator />
<div className="flex justify-end space-x-2 px-3 py-2">
<div className="flex justify-end space-x-2 py-2.5">
<Button
block
loading={isPublishing}
onClick={async (v) => {
onTogglePublish(false);
}}>
Unpublish
</Button>
<Button onClick={onCopyLink}>Copy link</Button>
<Button block onClick={onCopyLink}>
Copy link
</Button>
</div>
</>
)}
@ -201,16 +205,21 @@ const LinkExpiration: React.FC<{
}, []);
const maxDate = useMemo(() => {
return createDayjsDate(new Date()).add(3, 'year');
return createDayjsDate(new Date()).add(2, 'year');
}, []);
const onSelect: SelectSingleEventHandler = useMemoizedFn((date) => {
onChangeLinkExpiry(date || null);
});
return (
<div className="flex items-center justify-between space-x-2">
<Text>Link expiration</Text>
<Text truncate>Link expiration</Text>
<DatePicker
date={linkExpiry || new Date()}
onSelect={onChangeLinkExpiry}
selected={linkExpiry || new Date()}
onSelect={onSelect}
mode="single"
dateFormat={dateFormat}
placeholder="Never"
disabled={(date) => {
@ -276,7 +285,7 @@ const SetAPassword: React.FC<{
{isPasswordProtected && (
<div className="flex w-full items-center space-x-2">
<div className="flex w-full">
<div className="w-full">
<div className="relative flex w-full space-x-0.5">
<Input
value={password}
onChange={onChangePassword}
@ -285,7 +294,9 @@ const SetAPassword: React.FC<{
/>
<Button
className="h-full!"
variant="ghost"
size="small"
className="absolute top-1/2 right-[7px] -translate-y-1/2"
prefix={!visibilityToggle ? <Eye /> : <EyeSlash />}
onClick={onClickVisibilityToggle}></Button>
</div>

View File

@ -1,27 +1,18 @@
'use client';
import * as React from 'react';
import { ChevronLeft } from '@/components/ui/icons';
import { DayPicker, DayPickerProps } from 'react-day-picker';
import { DayPicker } from 'react-day-picker';
import { ChevronRight, ChevronLeft } from '../icons';
import { cn } from '@/lib/utils';
import { buttonVariants } from '@/components/ui/buttons';
export type CalendarProps = DayPickerProps;
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
function Calendar({
className,
classNames,
showOutsideDays = true,
required = false,
...props
}: CalendarProps) {
function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn('p-3', className)}
required={true}
mode="single"
selected={undefined}
classNames={{
months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
month: 'space-y-4',
@ -29,8 +20,7 @@ function Calendar({
caption_label: 'text-sm font-medium',
nav: 'space-x-1 flex items-center',
nav_button: cn(
buttonVariants({ variant: 'default' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100'
'h-7 w-7 bg-transparent p-0 opacity-50 cursor-pointer hover:opacity-100 rounded flex items-center justify-center hover:bg-item-hover'
),
nav_button_previous: 'absolute left-1',
nav_button_next: 'absolute right-1',
@ -45,26 +35,30 @@ function Calendar({
: '[&:has([aria-selected])]:rounded-md'
),
day: cn(
buttonVariants({ variant: 'ghost' }),
'h-8 w-8 p-0 font-normal aria-selected:opacity-100'
'h-8 w-8 p-0 cursor-pointer font-normal aria-selected:opacity-100 hover:bg-item-hover rounded'
),
day_range_start: 'day-range-start',
day_range_end: 'day-range-end',
day_selected:
'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
'bg-primary text-background hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
day_today: 'bg-accent text-accent-foreground',
day_outside:
'day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground',
day_disabled: 'text-muted-foreground opacity-50',
day_disabled: 'text-muted-foreground opacity-50 cursor-not-allowed!',
day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
day_hidden: 'invisible',
...classNames
}}
components={{
Chevron: ({ className, ...props }) => (
<div className={cn('flex items-center justify-center', className)} {...props}>
IconLeft: ({ className, ...props }) => (
<div className={cn(className)} {...props}>
<ChevronLeft />
</div>
),
IconRight: ({ className, ...props }) => (
<div className={cn(className)} {...props}>
<ChevronRight />
</div>
)
}}
{...props}

View File

@ -5,43 +5,40 @@ import { Calendar as CalendarIcon } from '@/components/ui/icons';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/buttons';
import { Calendar } from '@/components/ui/calendar';
import { Calendar, CalendarProps } from '@/components/ui/calendar';
import {
PopoverRoot as Popover,
PopoverContent,
PopoverTrigger
} from '@/components/ui/tooltip/PopoverBase';
import { formatDate } from '@/lib';
import { type DayPickerProps } from 'react-day-picker';
import { DayPickerProps } from 'react-day-picker';
export interface DatePickerProps extends Omit<DayPickerProps, 'mode' | 'selected' | 'onSelect'> {
date: Date;
onSelect: (date: Date) => void;
export type DatePickerProps = CalendarProps & {
dateFormat?: string;
placeholder?: string;
}
};
export function DatePicker({
date,
onSelect,
dateFormat = 'lll',
placeholder = 'Pick a date'
placeholder = 'Pick a date',
selected,
...props
}: DatePickerProps) {
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={'default'}
variant={'ghost'}
prefix={<CalendarIcon />}
className={cn(
'w-[280px] justify-start text-left font-normal',
!date && 'text-muted-foreground'
'justify-start text-left font-normal',
!selected && 'text-muted-foreground'
)}>
<div className="mr-2 h-4 w-4">
<CalendarIcon />
</div>
{date ? (
{selected ? (
formatDate({
date,
date: selected as Date,
format: dateFormat
})
) : (
@ -50,7 +47,7 @@ export function DatePicker({
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar mode="single" selected={date} onSelect={onSelect} autoFocus required />
<Calendar {...props} />
</PopoverContent>
</Popover>
);

View File

@ -64,6 +64,7 @@ export interface DropdownProps<T = string> extends DropdownMenuProps {
showIndex?: boolean;
contentClassName?: string;
footerClassName?: string;
sideOffset?: number;
}
export interface DropdownContentProps<T = string>
@ -92,6 +93,7 @@ export const DropdownBase = <T,>({
footerContent,
dir,
modal,
sideOffset,
footerClassName = '',
showIndex = false
}: DropdownProps<T>) => {
@ -105,7 +107,11 @@ export const DropdownBase = <T,>({
<DropdownMenuTrigger asChild>
<span className="dropdown-trigger">{children}</span>
</DropdownMenuTrigger>
<DropdownMenuContent className={cn('max-w-72 min-w-44', className)} align={align} side={side}>
<DropdownMenuContent
className={cn('max-w-72 min-w-44', className)}
align={align}
side={side}
sideOffset={sideOffset}>
<DropdownContent
items={items}
selectType={selectType}