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": "^18",
"react-color": "^2.19.3", "react-color": "^2.19.3",
"react-data-grid": "7.0.0-beta.47", "react-data-grid": "7.0.0-beta.47",
"react-day-picker": "^9.6.2", "react-day-picker": "8.10.1",
"react-dom": "^18", "react-dom": "^18",
"react-hotkeys-hook": "^4.6.1", "react-hotkeys-hook": "^4.6.1",
"react-markdown": "^10.1.0", "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" "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": { "node_modules/@dnd-kit/accessibility": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
@ -10056,21 +10050,16 @@
} }
}, },
"node_modules/date-fns": { "node_modules/date-fns": {
"version": "4.1.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/kossnocorp" "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": { "node_modules/dayjs": {
"version": "1.11.13", "version": "1.11.13",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
@ -18051,24 +18040,17 @@
} }
}, },
"node_modules/react-day-picker": { "node_modules/react-day-picker": {
"version": "9.6.2", "version": "8.10.1",
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.6.2.tgz", "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz",
"integrity": "sha512-HVrTpDUYGKbi9scU9v2N3BPGMnL5jwekGFXcyyrvpamZdgYGfzVcovsBD4/yNGxhpCIX5X/5IoLDEAayNv1EqA==", "integrity": "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==",
"license": "MIT", "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": { "funding": {
"type": "individual", "type": "individual",
"url": "https://github.com/sponsors/gpbl" "url": "https://github.com/sponsors/gpbl"
}, },
"peerDependencies": { "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": { "node_modules/react-docgen": {

View File

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

View File

@ -69,6 +69,7 @@ export const AccessDropdown: React.FC<{
footerContent={<FooterContent />} footerContent={<FooterContent />}
footerClassName="p-0!" footerClassName="p-0!"
onSelect={onSelectMenuItem} onSelect={onSelectMenuItem}
sideOffset={9}
selectType="single" selectType="single"
align="end" align="end"
side="bottom"> 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 <AccessDropdown
showRemove={false} showRemove={false}
groupShare={false} groupShare={false}
className="absolute right-[10px]" className="absolute top-[50%] right-[10px] -translate-y-1/2"
shareLevel={defaultPermissionLevel} shareLevel={defaultPermissionLevel}
onChangeShareLevel={onChangeAccessDropdown} onChangeShareLevel={onChangeAccessDropdown}
/> />

View File

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

View File

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

View File

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

View File

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