mirror of https://github.com/buster-so/buster.git
share menu update
This commit is contained in:
parent
768d19a2fd
commit
bc3558315f
|
@ -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": {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -69,6 +69,7 @@ export const AccessDropdown: React.FC<{
|
|||
footerContent={<FooterContent />}
|
||||
footerClassName="p-0!"
|
||||
onSelect={onSelectMenuItem}
|
||||
sideOffset={9}
|
||||
selectType="single"
|
||||
align="end"
|
||||
side="bottom">
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Reference in New Issue