Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1896efa3a0 | ||
|
|
62c8603863 | ||
|
|
7d8559b0b0 | ||
|
|
7a48bbe553 | ||
|
|
817e95c246 | ||
|
|
4ac6225a35 | ||
|
|
38d9b93dee | ||
|
|
376b38d4ed | ||
|
|
0e181f6d9f |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@calcom/web",
|
||||
"version": "3.2.7",
|
||||
"version": "3.2.7.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"analyze": "ANALYZE=true next build",
|
||||
|
||||
@@ -873,7 +873,7 @@ const Main = ({
|
||||
<MobileTeamsTab eventTypeGroups={data.eventTypeGroups} />
|
||||
) : (
|
||||
data.eventTypeGroups.map((group: EventTypeGroup, index: number) => (
|
||||
<div className="flex flex-col" key={group.profile.slug}>
|
||||
<div className="mt-4 flex flex-col" key={group.profile.slug}>
|
||||
<EventTypeListHeading
|
||||
profile={group.profile}
|
||||
membershipCount={group.metadata.membershipCount}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "https://profile-next-cold-starts.vercel.app/builder.tgz"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -387,7 +387,7 @@ export const AUTH_OPTIONS: AuthOptions = {
|
||||
if (trigger === "update") {
|
||||
return {
|
||||
...token,
|
||||
locale: session?.locale ?? token.locale,
|
||||
locale: session?.locale ?? token.locale ?? "en",
|
||||
name: session?.name ?? token.name,
|
||||
username: session?.username ?? token.username,
|
||||
email: session?.email ?? token.email,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { shallow } from "zustand/shallow";
|
||||
|
||||
import type { Dayjs } from "@calcom/dayjs";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { default as DatePickerComponent } from "@calcom/features/calendars/DatePicker";
|
||||
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
|
||||
@@ -23,8 +24,13 @@ export const DatePicker = () => {
|
||||
return (
|
||||
<DatePickerComponent
|
||||
isLoading={schedule.isLoading}
|
||||
onChange={(date) => setSelectedDate(date ? date.format("YYYY-MM-DD") : date)}
|
||||
onMonthChange={(date) => setMonth(date.format("YYYY-MM"))}
|
||||
onChange={(date: Dayjs | null) => {
|
||||
setSelectedDate(date === null ? date : date.format("YYYY-MM-DD"));
|
||||
}}
|
||||
onMonthChange={(date: Dayjs) => {
|
||||
setMonth(date.format("YYYY-MM"));
|
||||
setSelectedDate(date.format("YYYY-MM-DD"));
|
||||
}}
|
||||
includedDates={nonEmptyScheduleDays}
|
||||
locale={i18n.language}
|
||||
browsingDate={month ? dayjs(month) : undefined}
|
||||
|
||||
@@ -154,14 +154,19 @@ export const useBookerStore = create<BookerStore>((set, get) => ({
|
||||
},
|
||||
selectedDate: getQueryParam("date") || null,
|
||||
setSelectedDate: (selectedDate: string | null) => {
|
||||
// unset selected date
|
||||
if (!selectedDate) {
|
||||
removeQueryParam("date");
|
||||
return;
|
||||
}
|
||||
|
||||
const currentSelection = dayjs(get().selectedDate);
|
||||
const newSelection = dayjs(selectedDate);
|
||||
set({ selectedDate });
|
||||
updateQueryParam("date", selectedDate ?? "");
|
||||
|
||||
// Setting month make sure small calendar in fullscreen layouts also updates.
|
||||
// If selectedDate is null, prevents setting month to Invalid-Date
|
||||
if (selectedDate && newSelection.month() !== currentSelection.month()) {
|
||||
if (newSelection.month() !== currentSelection.month()) {
|
||||
set({ month: newSelection.format("YYYY-MM") });
|
||||
updateQueryParam("month", newSelection.format("YYYY-MM"));
|
||||
}
|
||||
@@ -194,6 +199,7 @@ export const useBookerStore = create<BookerStore>((set, get) => ({
|
||||
setMonth: (month: string | null) => {
|
||||
set({ month, selectedTimeslot: null });
|
||||
updateQueryParam("month", month ?? "");
|
||||
get().setSelectedDate(null);
|
||||
},
|
||||
isTeamEvent: false,
|
||||
seatedEventData: {
|
||||
|
||||
@@ -30,7 +30,7 @@ export type DatePickerProps = {
|
||||
/** Defaults to [], which dates are not bookable. Array of valid dates like: ["2022-04-23", "2022-04-24"] */
|
||||
excludedDates?: string[];
|
||||
/** defaults to all, which dates are bookable (inverse of excludedDates) */
|
||||
includedDates?: string[] | null;
|
||||
includedDates?: string[];
|
||||
/** allows adding classes to the container */
|
||||
className?: string;
|
||||
/** Shows a small loading spinner next to the month name */
|
||||
@@ -100,6 +100,40 @@ const NoAvailabilityOverlay = ({
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes care of selecting a valid date in the month if the selected date is not available in the month
|
||||
*/
|
||||
const useHandleInitialDateSelection = ({
|
||||
daysToRenderForTheMonth,
|
||||
selected,
|
||||
onChange,
|
||||
}: {
|
||||
daysToRenderForTheMonth: { day: Dayjs | null; disabled: boolean }[];
|
||||
selected: Dayjs | Dayjs[] | null | undefined;
|
||||
onChange: (date: Dayjs | null) => void;
|
||||
}) => {
|
||||
// Let's not do something for now in case of multiple selected dates as behaviour is unclear and it's not needed at the moment
|
||||
if (selected instanceof Array) {
|
||||
return;
|
||||
}
|
||||
const firstAvailableDateOfTheMonth = daysToRenderForTheMonth.find((day) => !day.disabled)?.day;
|
||||
|
||||
const isSelectedDateAvailable = selected
|
||||
? daysToRenderForTheMonth.some(({ day, disabled }) => {
|
||||
if (day && yyyymmdd(day) === yyyymmdd(selected) && !disabled) return true;
|
||||
})
|
||||
: false;
|
||||
|
||||
if (!isSelectedDateAvailable && firstAvailableDateOfTheMonth) {
|
||||
// If selected date not available in the month, select the first available date of the month
|
||||
onChange(firstAvailableDateOfTheMonth);
|
||||
}
|
||||
|
||||
if (!firstAvailableDateOfTheMonth) {
|
||||
onChange(null);
|
||||
}
|
||||
};
|
||||
|
||||
const Days = ({
|
||||
minDate = dayjs.utc(),
|
||||
excludedDates = [],
|
||||
@@ -121,7 +155,7 @@ const Days = ({
|
||||
// Create placeholder elements for empty days in first week
|
||||
const weekdayOfFirst = browsingDate.date(1).day();
|
||||
const currentDate = minDate.utcOffset(browsingDate.utcOffset());
|
||||
const availableDates = (includedDates: string[] | undefined | null) => {
|
||||
const availableDates = (includedDates: string[] | undefined) => {
|
||||
const dates = [];
|
||||
const lastDateOfMonth = browsingDate.date(daysInMonth(browsingDate));
|
||||
for (
|
||||
@@ -148,21 +182,6 @@ const Days = ({
|
||||
days.push(date);
|
||||
}
|
||||
|
||||
const daysToRenderForTheMonth = days.map((day) => {
|
||||
if (!day) return { day: null, disabled: true };
|
||||
return {
|
||||
day: day,
|
||||
disabled:
|
||||
(includedDates && !includedDates.includes(yyyymmdd(day))) || excludedDates.includes(yyyymmdd(day)),
|
||||
};
|
||||
});
|
||||
|
||||
useHandleInitialDateSelection({
|
||||
daysToRenderForTheMonth,
|
||||
selected,
|
||||
onChange: props.onChange,
|
||||
});
|
||||
|
||||
const [selectedDatesAndTimes] = useBookerStore((state) => [state.selectedDatesAndTimes], shallow);
|
||||
|
||||
const isActive = (day: dayjs.Dayjs) => {
|
||||
@@ -190,6 +209,21 @@ const Days = ({
|
||||
return false;
|
||||
};
|
||||
|
||||
const daysToRenderForTheMonth = days.map((day) => {
|
||||
if (!day) return { day: null, disabled: true };
|
||||
return {
|
||||
day: day,
|
||||
disabled:
|
||||
(includedDates && !includedDates.includes(yyyymmdd(day))) || excludedDates.includes(yyyymmdd(day)),
|
||||
};
|
||||
});
|
||||
|
||||
useHandleInitialDateSelection({
|
||||
daysToRenderForTheMonth,
|
||||
selected,
|
||||
onChange: props.onChange,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{daysToRenderForTheMonth.map(({ day, disabled }, idx) => (
|
||||
@@ -305,41 +339,4 @@ const DatePicker = ({
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes care of selecting a valid date in the month if the selected date is not available in the month
|
||||
*/
|
||||
const useHandleInitialDateSelection = ({
|
||||
daysToRenderForTheMonth,
|
||||
selected,
|
||||
onChange,
|
||||
}: {
|
||||
daysToRenderForTheMonth: { day: Dayjs | null; disabled: boolean }[];
|
||||
selected: Dayjs | Dayjs[] | null | undefined;
|
||||
onChange: (date: Dayjs | null) => void;
|
||||
}) => {
|
||||
// Let's not do something for now in case of multiple selected dates as behaviour is unclear and it's not needed at the moment
|
||||
if (selected instanceof Array) {
|
||||
return;
|
||||
}
|
||||
const firstAvailableDateOfTheMonth = daysToRenderForTheMonth.find((day) => !day.disabled)?.day;
|
||||
|
||||
const isSelectedDateAvailable = selected
|
||||
? daysToRenderForTheMonth.some(({ day, disabled }) => {
|
||||
if (day && yyyymmdd(day) === yyyymmdd(selected) && !disabled) return true;
|
||||
})
|
||||
: false;
|
||||
|
||||
if (firstAvailableDateOfTheMonth) {
|
||||
// If selected date not available in the month, select the first available date of the month
|
||||
if (!isSelectedDateAvailable) {
|
||||
onChange(firstAvailableDateOfTheMonth);
|
||||
}
|
||||
} else {
|
||||
// No date is available and if we were asked to select something inform that it couldn't be selected. This would actually help in not showing the timeslots section(with No Time Available) when no date in the month is available
|
||||
if (selected) {
|
||||
onChange(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default DatePicker;
|
||||
|
||||
@@ -32,6 +32,7 @@ import { GoogleWorkspaceInviteButton } from "./GoogleWorkspaceInviteButton";
|
||||
|
||||
type MemberInvitationModalProps = {
|
||||
isOpen: boolean;
|
||||
justEmailInvites?: boolean;
|
||||
onExit: () => void;
|
||||
orgMembers?: RouterOutputs["viewer"]["organizations"]["getMembers"];
|
||||
onSubmit: (values: NewMemberForm, resetFields: () => void) => void;
|
||||
@@ -206,7 +207,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
|
||||
render={({ field: { onChange }, fieldState: { error } }) => (
|
||||
<>
|
||||
<TextField
|
||||
label={t("email_or_username")}
|
||||
label={props.justEmailInvites ? t("email") : t("email_or_username")}
|
||||
id="inviteUser"
|
||||
name="inviteUser"
|
||||
placeholder="email@example.com"
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { ControlProps } from "react-select";
|
||||
import { components } from "react-select";
|
||||
import { shallow } from "zustand/shallow";
|
||||
|
||||
import type { Dayjs } from "@calcom/dayjs";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { AvailableTimes } from "@calcom/features/bookings";
|
||||
import { useBookerStore, useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
|
||||
@@ -223,21 +224,27 @@ const EmailEmbed = ({ eventType, username }: { eventType?: EventType; username:
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="mb-[9px] font-medium">
|
||||
<div className="text-default text-sm">{t("select_date")}</div>
|
||||
<DatePicker
|
||||
isLoading={schedule.isLoading}
|
||||
onChange={(date) => setSelectedDate(date ? date.format("YYYY-MM-DD") : date)}
|
||||
onMonthChange={(date) => {
|
||||
setMonth(date.format("YYYY-MM"));
|
||||
setSelectedDate(date.format("YYYY-MM-DD"));
|
||||
}}
|
||||
includedDates={nonEmptyScheduleDays}
|
||||
locale={i18n.language}
|
||||
browsingDate={month ? dayjs(month) : undefined}
|
||||
selected={dayjs(selectedDate)}
|
||||
weekStart={weekdayToWeekIndex(event?.data?.users?.[0]?.weekStart)}
|
||||
eventSlug={eventType?.slug}
|
||||
/>
|
||||
<Collapsible open>
|
||||
<CollapsibleContent>
|
||||
<div className="text-default text-sm">{t("select_date")}</div>
|
||||
<DatePicker
|
||||
isLoading={schedule.isLoading}
|
||||
onChange={(date: Dayjs | null) => {
|
||||
setSelectedDate(date === null ? date : date.format("YYYY-MM-DD"));
|
||||
}}
|
||||
onMonthChange={(date: Dayjs) => {
|
||||
setMonth(date.format("YYYY-MM"));
|
||||
setSelectedDate(date.format("YYYY-MM-DD"));
|
||||
}}
|
||||
includedDates={nonEmptyScheduleDays}
|
||||
locale={i18n.language}
|
||||
browsingDate={month ? dayjs(month) : undefined}
|
||||
selected={dayjs(selectedDate)}
|
||||
weekStart={weekdayToWeekIndex(event?.data?.users?.[0]?.weekStart)}
|
||||
eventSlug={eventType?.slug}
|
||||
/>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
{selectedDate ? (
|
||||
<div className="mt-[9px] font-medium ">
|
||||
|
||||
@@ -51,11 +51,7 @@ const DateOverrideForm = ({
|
||||
|
||||
const [selectedDates, setSelectedDates] = useState<Dayjs[]>(value ? [dayjs.utc(value[0].start)] : []);
|
||||
|
||||
const onDateChange = (newDate: Dayjs | null) => {
|
||||
// If no date is selected, do nothing
|
||||
if (!newDate) {
|
||||
return;
|
||||
}
|
||||
const onDateChange = (newDate: Dayjs) => {
|
||||
// If clicking on a selected date unselect it
|
||||
if (selectedDates.some((date) => yyyymmdd(date) === yyyymmdd(newDate))) {
|
||||
setSelectedDates(selectedDates.filter((date) => yyyymmdd(date) !== yyyymmdd(newDate)));
|
||||
@@ -154,7 +150,9 @@ const DateOverrideForm = ({
|
||||
excludedDates={excludedDates}
|
||||
weekStart={0}
|
||||
selected={selectedDates}
|
||||
onChange={(day) => onDateChange(day)}
|
||||
onChange={(day) => {
|
||||
if (day) onDateChange(day);
|
||||
}}
|
||||
onMonthChange={(newMonth) => {
|
||||
setBrowsingDate(newMonth);
|
||||
}}
|
||||
|
||||
@@ -369,7 +369,7 @@ function UserDropdown({ small }: UserDropdownProps) {
|
||||
<span
|
||||
className={classNames(
|
||||
small ? "h-4 w-4" : "h-5 w-5 ltr:mr-2 rtl:ml-2",
|
||||
"relative flex-shrink-0 rounded-full bg-gray-300"
|
||||
"relative flex-shrink-0 rounded-full "
|
||||
)}>
|
||||
<Avatar
|
||||
size={small ? "xs" : "xsm"}
|
||||
|
||||
@@ -60,6 +60,7 @@ export function InviteMemberModal(props: Props) {
|
||||
});
|
||||
}}
|
||||
teamId={orgId}
|
||||
justEmailInvites={!!orgId}
|
||||
isLoading={inviteMemberMutation.isLoading}
|
||||
onSubmit={(values) => {
|
||||
inviteMemberMutation.mutate({
|
||||
|
||||
@@ -24,11 +24,12 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
|
||||
// A user can only have one org so we pass in their currentOrgId here
|
||||
const currentOrgId = ctx.user?.organization?.id || input.orgId;
|
||||
|
||||
if (!currentOrgId || ctx.user.role !== UserPermissionRole.ADMIN)
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
const isUserOrganizationAdmin = currentOrgId && (await isOrganisationAdmin(ctx.user?.id, currentOrgId));
|
||||
const isUserRoleAdmin = ctx.user.role === UserPermissionRole.ADMIN;
|
||||
|
||||
if (!(await isOrganisationAdmin(ctx.user?.id, currentOrgId)) || ctx.user.role !== UserPermissionRole.ADMIN)
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
const isUserAuthorizedToUpdate = !!(isUserOrganizationAdmin || isUserRoleAdmin);
|
||||
|
||||
if (!currentOrgId || !isUserAuthorizedToUpdate) throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
|
||||
if (input.slug) {
|
||||
const userConflict = await prisma.team.findMany({
|
||||
|
||||
@@ -56,7 +56,7 @@ export const inviteMemberHandler = async ({ ctx, input }: InviteMemberOptions) =
|
||||
});
|
||||
const invitee = await getUserToInviteOrThrowIfExists({
|
||||
usernameOrEmail,
|
||||
orgId: input.teamId,
|
||||
teamId: input.teamId,
|
||||
isOrg: input.isOrg,
|
||||
});
|
||||
|
||||
|
||||
@@ -66,17 +66,21 @@ export async function getEmailsToInvite(usernameOrEmail: string | string[]) {
|
||||
|
||||
export async function getUserToInviteOrThrowIfExists({
|
||||
usernameOrEmail,
|
||||
orgId,
|
||||
teamId,
|
||||
isOrg,
|
||||
}: {
|
||||
usernameOrEmail: string;
|
||||
orgId: number;
|
||||
teamId: number;
|
||||
isOrg?: boolean;
|
||||
}) {
|
||||
// Check if user exists in ORG or exists all together
|
||||
|
||||
const orgWhere = isOrg && {
|
||||
organizationId: teamId,
|
||||
};
|
||||
const invitee = await prisma.user.findFirst({
|
||||
where: {
|
||||
OR: [{ username: usernameOrEmail, organizationId: orgId }, { email: usernameOrEmail }],
|
||||
OR: [{ username: usernameOrEmail, ...orgWhere }, { email: usernameOrEmail }],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ export function Avatar(props: AvatarProps) {
|
||||
let avatar = (
|
||||
<AvatarPrimitive.Root
|
||||
className={classNames(
|
||||
"bg-emphasis item-center relative aspect-square justify-center overflow-hidden rounded-full",
|
||||
"bg-emphasis item-center relative inline-flex aspect-square justify-center overflow-hidden rounded-full",
|
||||
props.className,
|
||||
sizesPropsBySize[size]
|
||||
)}>
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
import { Canvas, Story, Meta } from "@storybook/addon-docs";
|
||||
import { Button } from "@calcom/ui";
|
||||
|
||||
import {
|
||||
Examples,
|
||||
Example,
|
||||
Title,
|
||||
CustomArgsTable,
|
||||
VariantRow,
|
||||
VariantsTable,
|
||||
} from "@calcom/storybook/components";
|
||||
|
||||
import { Plus, Trash, Copy } from "@calcom/ui/components/icon";
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
} from "./Dropdown";
|
||||
|
||||
<Meta title="UI/Form/Dropdown" component={Dropdown} />
|
||||
|
||||
<Title title="Dropdown" suffix="Brief" subtitle="Version 1.0 — Last Update: 29 Aug 2023" />
|
||||
|
||||
## Definition
|
||||
|
||||
`Dropdown` is an element that displays a menu to the user—such as a set of actions or functions.
|
||||
|
||||
## Structure
|
||||
|
||||
The `Dropdown` component can be used to display a menu to the user.
|
||||
|
||||
<CustomArgsTable of={Dropdown} />
|
||||
|
||||
### Dropdown components that have arguments:
|
||||
|
||||
#### DropdownMenuTrigger
|
||||
|
||||
<CustomArgsTable of={DropdownMenuTrigger} />
|
||||
|
||||
#### DropdownMenuContent
|
||||
|
||||
<CustomArgsTable of={DropdownMenuContent} />
|
||||
|
||||
#### DropdownMenuItem
|
||||
|
||||
<CustomArgsTable of={DropdownMenuItem} />
|
||||
|
||||
#### DropdownMenuSeparator
|
||||
|
||||
<CustomArgsTable of={DropdownMenuSeparator} />
|
||||
|
||||
## Examples
|
||||
|
||||
<Examples title="Dropdown">
|
||||
<Example title="Simple">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
<Example title="No modal">
|
||||
<Dropdown modal={false}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
</Examples>
|
||||
<Examples title="Dropdown Menu Trigger">
|
||||
<Example title="Simple Button">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
<Example title="Button Icon">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button StartIcon={Plus} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
<Example title="Disabled">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger disabled={true} asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
<Example title="Not as child">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
</Examples>
|
||||
<Examples title="Dropdown Menu Content">
|
||||
<Example title="Custom width">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent style={{ minWidth: "200px" }}>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
<Example title="Align start">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent style={{ minWidth: "80px" }} align={"start"}>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
<Example title="Align center">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent style={{ minWidth: "80px" }} align={"center"}>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
<Example title="With Menu Separator">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
<Example title="With side offset">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent sideOffset={50}>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
<Example title="With Some icons">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem StartIcon={Trash}>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem StartIcon={Plus}>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem StartIcon={Copy}>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
</Examples>
|
||||
<Examples title="Dropdown Menu Label">
|
||||
<Example title="Simple">
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align={"start"}>
|
||||
<DropdownMenuLabel>Number</DropdownMenuLabel>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</Example>
|
||||
</Examples>
|
||||
|
||||
## Dropdown Story
|
||||
|
||||
<Canvas>
|
||||
<Story
|
||||
name="Dropdown"
|
||||
args={{
|
||||
asChild: false,
|
||||
align: "start",
|
||||
disabled: false,
|
||||
sideOffset: 0
|
||||
}}
|
||||
argTypes={{
|
||||
sideOffset: {
|
||||
control: {
|
||||
type: "number",
|
||||
},
|
||||
},
|
||||
asChild: {
|
||||
control: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
align: {
|
||||
control: {
|
||||
type: "inline-radio",
|
||||
options: ["center", "start", "end"],
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
control: {
|
||||
type: "boolean"
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{({ dir, asChild, sideOffset, align, disabled }) => (
|
||||
<VariantsTable titles={["Default"]} columnMinWidth={150}>
|
||||
<VariantRow>
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger disabled={disabled} asChild>
|
||||
<Button>more</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align={align} sideOffset={sideOffset}>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem StartIcon={Trash}>First</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem StartIcon={Plus}>Second</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem StartIcon={Copy}>Third</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</VariantRow>
|
||||
</VariantsTable>
|
||||
)}
|
||||
</Story>
|
||||
</Canvas>
|
||||
Reference in New Issue
Block a user