New Help & Feedback menu and tab-selected styles (#1374)
* Added a new dropdown help and feedback menu to the side menu * Added a shortcut to the popover menu * Removed dev cli connected button for now * Contact us form uses original Feedback component to prevent broken links * Improved the messaging when selecting different options in the email form * buttons style tweak * SideMenuItem supports the trailingIconClassName * Adding a consistent focus-visible states * Removing tooltips for now * Squashed commit of the following: commit7d11123c0aAuthor: Eric Goldman <eric@sequin.io> Date: Mon Sep 30 17:54:06 2024 -0700 Add sequin guide (#1368) Co-authored-by: James Ritchie <james@trigger.dev> commit8da495ac00Author: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Mon Sep 30 13:42:22 2024 +0100 Improve checkpoint reliability and cleanup of temp files (#1367) * improve cleanup reliability * improve logging * bye-bye execa * fix for trailing newlines * prettier errors * trim args and log output by default * fix archive cleanup * prevent potential memleak * more cleanup debug logs * ignore abort during cleanup * rename checkpoint dir env var and move to helper * add global never throw override * add tmp cleaner * also clean up checkpoint dir by default * split by any whitespace, not just tabs * only create tmp cleaner if paths to clean commit69ec68ee31Author: Eric Allam <eallam@icloud.com> Date: Sun Sep 29 19:18:39 2024 -0700 Release 3.0.9 commita6ea8444c9Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun Sep 29 19:17:26 2024 -0700 chore: Update version for release (#1366) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> commit4c1ee3d6eaAuthor: Eric Allam <eallam@icloud.com> Date: Sun Sep 29 19:09:38 2024 -0700 fix: run metadata not working when using npx/pnpm dlx * More support for custom-focus * More custom focus styles added * Support for focus-visible style for the Segmented control * Fixed table triple dot menu z-index issue * Improved help menu wording * When you submit the help form, close the modal * focus-visible style for radio buttons * button prop is now optional in the SideMenu component * focus styling for a text link * Deleted unused sequin files
This commit is contained in:
@@ -1,31 +1,23 @@
|
||||
import { conform, useForm } from "@conform-to/react";
|
||||
import { parse } from "@conform-to/zod";
|
||||
import { BookOpenIcon } from "@heroicons/react/20/solid";
|
||||
import {
|
||||
CalendarDaysIcon,
|
||||
ChevronRightIcon,
|
||||
EnvelopeIcon,
|
||||
LifebuoyIcon,
|
||||
LightBulbIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import { EnvelopeIcon, LightBulbIcon } from "@heroicons/react/24/solid";
|
||||
import { Form, useActionData, useLocation, useNavigation } from "@remix-run/react";
|
||||
import { DiscordIcon } from "@trigger.dev/companyicons";
|
||||
import { ActivityIcon } from "lucide-react";
|
||||
import { type ReactNode, useState } from "react";
|
||||
import { type ReactNode, useState, useEffect } from "react";
|
||||
import { type FeedbackType, feedbackTypeLabel, schema } from "~/routes/resources.feedback";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { docsTroubleshootingPath } from "~/utils/pathBuilder";
|
||||
import { Button, LinkButton } from "./primitives/Buttons";
|
||||
import { Button } from "./primitives/Buttons";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "./primitives/Dialog";
|
||||
import { Fieldset } from "./primitives/Fieldset";
|
||||
import { FormButtons } from "./primitives/FormButtons";
|
||||
import { FormError } from "./primitives/FormError";
|
||||
import { Header1 } from "./primitives/Headers";
|
||||
import { Icon } from "./primitives/Icon";
|
||||
import { InfoPanel } from "./primitives/InfoPanel";
|
||||
import { InputGroup } from "./primitives/InputGroup";
|
||||
import { Label } from "./primitives/Label";
|
||||
import { Paragraph } from "./primitives/Paragraph";
|
||||
import { Select, SelectItem } from "./primitives/Select";
|
||||
import { Sheet, SheetBody, SheetContent, SheetTrigger } from "./primitives/Sheet";
|
||||
import { TextArea } from "./primitives/TextArea";
|
||||
import { InformationCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { TextLink } from "./primitives/TextLink";
|
||||
|
||||
type FeedbackProps = {
|
||||
button: ReactNode;
|
||||
@@ -37,161 +29,111 @@ export function Feedback({ button, defaultValue = "bug" }: FeedbackProps) {
|
||||
const location = useLocation();
|
||||
const lastSubmission = useActionData();
|
||||
const navigation = useNavigation();
|
||||
const [type, setType] = useState<FeedbackType>(defaultValue);
|
||||
|
||||
const [form, { path, feedbackType, message }] = useForm({
|
||||
id: "accept-invite",
|
||||
// TODO: type this
|
||||
lastSubmission: lastSubmission as any,
|
||||
onValidate({ formData }) {
|
||||
return parse(formData, { schema });
|
||||
},
|
||||
shouldRevalidate: "onInput",
|
||||
});
|
||||
|
||||
if (
|
||||
open &&
|
||||
navigation.formAction === "/resources/feedback" &&
|
||||
form.error === undefined &&
|
||||
form.errors.length === 0
|
||||
) {
|
||||
setOpen(false);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (
|
||||
navigation.formAction === "/resources/feedback" &&
|
||||
navigation.state === "loading" &&
|
||||
form.error === undefined &&
|
||||
form.errors.length === 0
|
||||
) {
|
||||
setOpen(false);
|
||||
}
|
||||
}, [navigation, form]);
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetTrigger asChild={true}>{button}</SheetTrigger>
|
||||
<SheetContent className="@container">
|
||||
<SheetBody className="flex h-full flex-col justify-between">
|
||||
<LinkBanner
|
||||
title="Join our Discord community"
|
||||
icon={<DiscordIcon className="size-9" />}
|
||||
to="https://trigger.dev/discord"
|
||||
className="hover:border-text-link"
|
||||
>
|
||||
<Paragraph>The quickest way to get answers from the Trigger.dev community.</Paragraph>
|
||||
</LinkBanner>
|
||||
<LinkBanner
|
||||
title="Book a 15 min chat with the founders"
|
||||
icon={<CalendarDaysIcon className="size-9 text-green-500" />}
|
||||
to="https://cal.com/team/triggerdotdev/founders-call"
|
||||
className="hover:border-green-500"
|
||||
>
|
||||
<Paragraph>Have a question or want to chat? Book a time to talk with us.</Paragraph>
|
||||
</LinkBanner>
|
||||
<LinkBanner
|
||||
title="Suggest a feature"
|
||||
icon={<LightBulbIcon className="size-9 text-sun-500" />}
|
||||
to="https://feedback.trigger.dev/"
|
||||
className="hover:border-sun-400"
|
||||
>
|
||||
<Paragraph>Have an idea for a new feature or improvement? Let us know!</Paragraph>
|
||||
</LinkBanner>
|
||||
<LinkBanner
|
||||
title="Troubleshooting"
|
||||
icon={<LifebuoyIcon className="size-9 text-rose-500" />}
|
||||
>
|
||||
<Paragraph>
|
||||
If you're having trouble, check out our troubleshooting guide or the Trigger.dev
|
||||
Status page.
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>{button}</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>Contact us</DialogHeader>
|
||||
<div className="mt-2 flex flex-col gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<Icon icon={EnvelopeIcon} className="size-10 min-w-[2.5rem] text-blue-500" />
|
||||
<Paragraph variant="base/bright">
|
||||
How can we help? We read every message and will respond as quickly as we can.
|
||||
</Paragraph>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<LinkButton
|
||||
to={docsTroubleshootingPath("")}
|
||||
variant="tertiary/medium"
|
||||
LeadingIcon={BookOpenIcon}
|
||||
>
|
||||
Troubleshooting Docs
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
to={"https://status.trigger.dev/"}
|
||||
variant="tertiary/medium"
|
||||
LeadingIcon={ActivityIcon}
|
||||
>
|
||||
Trigger.dev Status
|
||||
</LinkButton>
|
||||
</div>
|
||||
</LinkBanner>
|
||||
<LinkBanner
|
||||
title="Send us an email"
|
||||
icon={<EnvelopeIcon className="size-9 text-blue-500" />}
|
||||
>
|
||||
<Paragraph>We read every message and respond quickly.</Paragraph>
|
||||
<Form method="post" action="/resources/feedback" {...form.props} className="w-full">
|
||||
<Fieldset className="max-w-full gap-y-3">
|
||||
<input value={location.pathname} {...conform.input(path, { type: "hidden" })} />
|
||||
<InputGroup className="max-w-full">
|
||||
<Select
|
||||
{...conform.select(feedbackType)}
|
||||
variant="tertiary/medium"
|
||||
defaultValue={defaultValue}
|
||||
placeholder="Select type"
|
||||
text={(value) => feedbackTypeLabel[value]}
|
||||
dropdownIcon
|
||||
</div>
|
||||
<hr className="border-charcoal-800" />
|
||||
<Form method="post" action="/resources/feedback" {...form.props} className="w-full">
|
||||
<Fieldset className="max-w-full gap-y-3">
|
||||
<input value={location.pathname} {...conform.input(path, { type: "hidden" })} />
|
||||
<InputGroup className="max-w-full">
|
||||
{type === "feature" && (
|
||||
<InfoPanel
|
||||
icon={InformationCircleIcon}
|
||||
iconClassName="text-blue-500"
|
||||
panelClassName="w-full mb-2"
|
||||
>
|
||||
{Object.entries(feedbackTypeLabel).map(([name, title]) => (
|
||||
<SelectItem key={name} value={name}>
|
||||
{title}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<FormError id={feedbackType.errorId}>{feedbackType.error}</FormError>
|
||||
</InputGroup>
|
||||
<InputGroup className="max-w-full">
|
||||
<Label>Message</Label>
|
||||
<TextArea {...conform.textarea(message)} />
|
||||
<FormError id={message.errorId}>{message.error}</FormError>
|
||||
</InputGroup>
|
||||
<FormError>{form.error}</FormError>
|
||||
<div className="flex w-full justify-end">
|
||||
<FormButtons
|
||||
className="m-0 w-max"
|
||||
confirmButton={
|
||||
<Button type="submit" variant="tertiary/medium">
|
||||
Send message
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</LinkBanner>
|
||||
</SheetBody>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
|
||||
function LinkBanner({
|
||||
className,
|
||||
icon,
|
||||
title,
|
||||
children,
|
||||
to,
|
||||
}: {
|
||||
className?: string;
|
||||
icon?: ReactNode;
|
||||
title?: string;
|
||||
children?: ReactNode;
|
||||
to?: string;
|
||||
}) {
|
||||
return (
|
||||
<a
|
||||
href={to}
|
||||
target="_blank"
|
||||
className={cn(
|
||||
"group/banner mb-4 flex w-full items-center justify-between rounded-md border border-grid-bright bg-charcoal-750 p-4 transition",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="flex w-full items-start gap-4">
|
||||
<span>{icon}</span>
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<Header1 className="text-2xl font-semibold text-text-bright">{title}</Header1>
|
||||
{children}
|
||||
<Paragraph variant="small">
|
||||
All our feature requests are public and voted on by the community. The best
|
||||
way to submit your feature request is to{" "}
|
||||
<TextLink to="https://feedback.trigger.dev">
|
||||
post it to our feedback forum
|
||||
</TextLink>
|
||||
.
|
||||
</Paragraph>
|
||||
</InfoPanel>
|
||||
)}
|
||||
{type === "help" && (
|
||||
<InfoPanel
|
||||
icon={InformationCircleIcon}
|
||||
iconClassName="text-blue-500"
|
||||
panelClassName="w-full mb-2"
|
||||
>
|
||||
<Paragraph variant="small">
|
||||
The quickest way to get answers from the Trigger.dev team and community is to{" "}
|
||||
<TextLink to="https://trigger.dev/discord">ask in our Discord</TextLink>.
|
||||
</Paragraph>
|
||||
</InfoPanel>
|
||||
)}
|
||||
<Select
|
||||
{...conform.select(feedbackType)}
|
||||
variant="tertiary/medium"
|
||||
value={type}
|
||||
defaultValue={type}
|
||||
setValue={(v) => setType(v as FeedbackType)}
|
||||
placeholder="Select type"
|
||||
text={(value) => feedbackTypeLabel[value as FeedbackType]}
|
||||
dropdownIcon
|
||||
>
|
||||
{Object.entries(feedbackTypeLabel).map(([name, title]) => (
|
||||
<SelectItem key={name} value={name}>
|
||||
{title}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<FormError id={feedbackType.errorId}>{feedbackType.error}</FormError>
|
||||
</InputGroup>
|
||||
<InputGroup className="max-w-full">
|
||||
<Label>Message</Label>
|
||||
<TextArea {...conform.textarea(message)} />
|
||||
<FormError id={message.errorId}>{message.error}</FormError>
|
||||
</InputGroup>
|
||||
<FormError>{form.error}</FormError>
|
||||
<div className="flex w-full justify-end">
|
||||
<FormButtons
|
||||
className="m-0 w-max"
|
||||
confirmButton={
|
||||
<Button type="submit" variant="tertiary/medium">
|
||||
Send message
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
{to && (
|
||||
<ChevronRightIcon className="size-5 text-charcoal-500 transition group-hover:translate-x-1 group-hover/banner:text-text-bright" />
|
||||
)}
|
||||
</a>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export function FreePlanUsage({ to, percentage }: { to: string; percentage: numb
|
||||
<ArrowUpCircleIcon className="h-5 w-5 text-text-dimmed" />
|
||||
<Paragraph className="text-2sm text-text-bright">Free Plan</Paragraph>
|
||||
</div>
|
||||
<Link to={to} className="text-2sm text-text-link">
|
||||
<Link to={to} className="text-2sm text-text-link focus-custom">
|
||||
Upgrade
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -241,7 +241,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
||||
onMouseEnter={() => setMouseOver(true)}
|
||||
onMouseLeave={() => setMouseOver(false)}
|
||||
className={cn(
|
||||
"absolute right-3 z-50 transition-colors duration-100 hover:cursor-pointer",
|
||||
"absolute right-3 z-50 transition-colors duration-100 focus-custom hover:cursor-pointer",
|
||||
showChrome ? "top-10" : "top-2.5",
|
||||
copied ? "text-emerald-500" : "text-charcoal-500 hover:text-charcoal-300"
|
||||
)}
|
||||
|
||||
@@ -38,7 +38,7 @@ export function AccountSideMenu({ user }: { user: User }) {
|
||||
<SideMenuItem
|
||||
name="Your profile"
|
||||
icon="account"
|
||||
iconColor="text-indigo-500"
|
||||
activeIconColor="text-indigo-500"
|
||||
to={accountPath()}
|
||||
data-action="account"
|
||||
/>
|
||||
@@ -49,7 +49,7 @@ export function AccountSideMenu({ user }: { user: User }) {
|
||||
<SideMenuItem
|
||||
name="Personal Access Tokens"
|
||||
icon={ShieldCheckIcon}
|
||||
iconColor="text-emerald-500"
|
||||
activeIconColor="text-emerald-500"
|
||||
to={personalAccessTokensPath()}
|
||||
data-action="tokens"
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import {
|
||||
AcademicCapIcon,
|
||||
ArrowRightOnRectangleIcon,
|
||||
ArrowUpRightIcon,
|
||||
BeakerIcon,
|
||||
BellAlertIcon,
|
||||
CalendarDaysIcon,
|
||||
ChartBarIcon,
|
||||
ChatBubbleLeftEllipsisIcon,
|
||||
ClockIcon,
|
||||
CreditCardIcon,
|
||||
CursorArrowRaysIcon,
|
||||
EnvelopeIcon,
|
||||
IdentificationIcon,
|
||||
KeyIcon,
|
||||
LightBulbIcon,
|
||||
RectangleStackIcon,
|
||||
ServerStackIcon,
|
||||
ShieldCheckIcon,
|
||||
@@ -17,13 +22,14 @@ import {
|
||||
import { UserGroupIcon, UserPlusIcon } from "@heroicons/react/24/solid";
|
||||
import { useNavigation } from "@remix-run/react";
|
||||
import { DiscordIcon, SlackIcon } from "@trigger.dev/companyicons";
|
||||
import { Fragment, useEffect, useRef, useState } from "react";
|
||||
import { Fragment, ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { TaskIcon } from "~/assets/icons/TaskIcon";
|
||||
import { useFeatures } from "~/hooks/useFeatures";
|
||||
import { type MatchedOrganization } from "~/hooks/useOrganizations";
|
||||
import { type MatchedProject } from "~/hooks/useProject";
|
||||
import { type User } from "~/models/user.server";
|
||||
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
|
||||
import { FeedbackType } from "~/routes/resources.feedback";
|
||||
import { cn } from "~/utils/cn";
|
||||
import {
|
||||
accountPath,
|
||||
@@ -77,11 +83,12 @@ import {
|
||||
PopoverCustomTrigger,
|
||||
PopoverMenuItem,
|
||||
PopoverSectionHeader,
|
||||
PopoverSideMenuTrigger,
|
||||
} from "../primitives/Popover";
|
||||
import { StepNumber } from "../primitives/StepNumber";
|
||||
import { TextLink } from "../primitives/TextLink";
|
||||
import { SideMenuHeader } from "./SideMenuHeader";
|
||||
import { SideMenuItem } from "./SideMenuItem";
|
||||
import { MenuCount, SideMenuItem } from "./SideMenuItem";
|
||||
|
||||
type SideMenuUser = Pick<User, "email" | "admin"> & { isImpersonating: boolean };
|
||||
type SideMenuProject = Pick<MatchedProject, "id" | "name" | "slug" | "version">;
|
||||
@@ -91,6 +98,8 @@ type SideMenuProps = {
|
||||
project: SideMenuProject;
|
||||
organization: MatchedOrganization;
|
||||
organizations: MatchedOrganization[];
|
||||
button?: ReactNode;
|
||||
defaultValue?: FeedbackType;
|
||||
};
|
||||
|
||||
export function SideMenu({ user, project, organization, organizations }: SideMenuProps) {
|
||||
@@ -171,7 +180,7 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
name="Team"
|
||||
icon={UserGroupIcon}
|
||||
to={organizationTeamPath(organization)}
|
||||
iconColor="text-amber-500"
|
||||
activeIconColor="text-amber-500"
|
||||
data-action="team"
|
||||
/>
|
||||
{organization.projects.some((proj) => proj.version === "V3") && isManagedCloud && (
|
||||
@@ -180,14 +189,14 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
name="Usage"
|
||||
icon={ChartBarIcon}
|
||||
to={v3UsagePath(organization)}
|
||||
iconColor="text-green-600"
|
||||
activeIconColor="text-green-600"
|
||||
data-action="usage"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Billing"
|
||||
icon={CreditCardIcon}
|
||||
to={v3BillingPath(organization)}
|
||||
iconColor="text-blue-600"
|
||||
activeIconColor="text-blue-600"
|
||||
data-action="billing"
|
||||
badge={
|
||||
currentPlan?.v3Subscription?.isPaying
|
||||
@@ -202,14 +211,14 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
name="Usage (v2)"
|
||||
icon={ChartBarIcon}
|
||||
to={organizationBillingPath(organization)}
|
||||
iconColor="text-green-600"
|
||||
activeIconColor="text-green-600"
|
||||
data-action="usage & billing"
|
||||
/>
|
||||
)}
|
||||
<SideMenuItem
|
||||
name="Organization settings"
|
||||
icon="settings"
|
||||
iconColor="text-teal-500"
|
||||
activeIconColor="text-teal-500"
|
||||
to={organizationSettingsPath(organization)}
|
||||
data-action="organization-settings"
|
||||
/>
|
||||
@@ -239,96 +248,7 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 border-t border-grid-bright p-1">
|
||||
{currentPlan?.v3Subscription?.plan?.limits.support === "slack" && (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="small-menu-item"
|
||||
LeadingIcon={SlackIcon}
|
||||
data-action="join our slack"
|
||||
fullWidth
|
||||
textAlignLeft
|
||||
>
|
||||
Join our Slack
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>Join our Slack</DialogHeader>
|
||||
<div className="mt-2 flex flex-col gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<Icon icon={SlackIcon} className="h-10 w-10 min-w-[2.5rem]" />
|
||||
<Paragraph variant="base/bright">
|
||||
As a subscriber, you have access to a dedicated Slack channel for 1-to-1
|
||||
support with the Trigger.dev team.
|
||||
</Paragraph>
|
||||
</div>
|
||||
<hr className="border-charcoal-800" />
|
||||
<div>
|
||||
<StepNumber stepNumber="1" title="Email us" />
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
Send us an email to this address from your Trigger.dev account email
|
||||
address:
|
||||
<ClipboardField
|
||||
variant="primary/medium"
|
||||
value="priority-support@trigger.dev"
|
||||
className="my-2"
|
||||
/>
|
||||
</Paragraph>
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="2" title="Look out for an invite from Slack" />
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
As soon as we can, we'll setup a Slack Connect channel and say hello!
|
||||
</Paragraph>
|
||||
</StepContentContainer>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
<SideMenuItem
|
||||
name="Join our Discord"
|
||||
icon={DiscordIcon}
|
||||
to="https://trigger.dev/discord"
|
||||
data-action="join our discord"
|
||||
target="_blank"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Documentation"
|
||||
icon="docs"
|
||||
to="https://trigger.dev/docs"
|
||||
data-action="documentation"
|
||||
target="_blank"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Changelog"
|
||||
icon="star"
|
||||
to="https://trigger.dev/changelog"
|
||||
data-action="changelog"
|
||||
target="_blank"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Status"
|
||||
icon={SignalIcon}
|
||||
to="https://status.trigger.dev/"
|
||||
data-action="status"
|
||||
target="_blank"
|
||||
/>
|
||||
<Feedback
|
||||
button={
|
||||
<Button
|
||||
variant="small-menu-item"
|
||||
LeadingIcon="log"
|
||||
leadingIconClassName="text-primary"
|
||||
data-action="help & feedback"
|
||||
fullWidth
|
||||
textAlignLeft
|
||||
>
|
||||
<span className="text-primary">Help & Feedback</span>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<HelpAndFeedback />
|
||||
{isV3Project && isFreeV3User && (
|
||||
<FreePlanUsage
|
||||
to={v3BillingPath(organization)}
|
||||
@@ -341,6 +261,167 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
);
|
||||
}
|
||||
|
||||
function HelpAndFeedback() {
|
||||
const [isHelpMenuOpen, setHelpMenuOpen] = useState(false);
|
||||
const currentPlan = useCurrentPlan();
|
||||
|
||||
return (
|
||||
<Popover onOpenChange={(open) => setHelpMenuOpen(open)}>
|
||||
<PopoverSideMenuTrigger isOpen={isHelpMenuOpen} shortcut={{ key: "h" }}>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<ChatBubbleLeftEllipsisIcon className="size-4 text-success" />
|
||||
Help & Feedback
|
||||
</div>
|
||||
</PopoverSideMenuTrigger>
|
||||
<PopoverContent
|
||||
className="min-w-[14rem] divide-y divide-grid-bright overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
|
||||
align="start"
|
||||
>
|
||||
<Fragment>
|
||||
<div className="flex flex-col gap-1 p-1">
|
||||
<SideMenuItem
|
||||
name="Documentation"
|
||||
icon="docs"
|
||||
trailingIcon={ArrowUpRightIcon}
|
||||
trailingIconClassName="text-text-dimmed"
|
||||
inactiveIconColor="text-green-500"
|
||||
activeIconColor="text-green-500"
|
||||
to="https://trigger.dev/docs"
|
||||
data-action="documentation"
|
||||
target="_blank"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 p-1">
|
||||
<SideMenuItem
|
||||
name="Status"
|
||||
icon={SignalIcon}
|
||||
trailingIcon={ArrowUpRightIcon}
|
||||
trailingIconClassName="text-text-dimmed"
|
||||
inactiveIconColor="text-green-500"
|
||||
activeIconColor="text-green-500"
|
||||
to="https://status.trigger.dev/"
|
||||
data-action="status"
|
||||
target="_blank"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Suggest a feature"
|
||||
icon={LightBulbIcon}
|
||||
trailingIcon={ArrowUpRightIcon}
|
||||
trailingIconClassName="text-text-dimmed"
|
||||
inactiveIconColor="text-sun-500"
|
||||
activeIconColor="text-sun-500"
|
||||
to="https://feedback.trigger.dev/"
|
||||
data-action="suggest-a-feature"
|
||||
target="_blank"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Changelog"
|
||||
icon="star"
|
||||
trailingIcon={ArrowUpRightIcon}
|
||||
trailingIconClassName="text-text-dimmed"
|
||||
inactiveIconColor="text-sun-500"
|
||||
activeIconColor="text-sun-500"
|
||||
to="https://trigger.dev/changelog"
|
||||
data-action="changelog"
|
||||
target="_blank"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 p-1">
|
||||
<Paragraph className="pb-1 pl-1.5 pt-1.5 text-xs">Need help?</Paragraph>
|
||||
{currentPlan?.v3Subscription?.plan?.limits.support === "slack" && (
|
||||
<div>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="small-menu-item"
|
||||
LeadingIcon={SlackIcon}
|
||||
data-action="join-our-slack"
|
||||
fullWidth
|
||||
textAlignLeft
|
||||
>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<span className="text-text-bright">Join our Slack…</span>
|
||||
<MenuCount count="PRO" />
|
||||
</div>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>Join our Slack</DialogHeader>
|
||||
<div className="mt-2 flex flex-col gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<Icon icon={SlackIcon} className="h-10 w-10 min-w-[2.5rem]" />
|
||||
<Paragraph variant="base/bright">
|
||||
As a subscriber, you have access to a dedicated Slack channel for 1-to-1
|
||||
support with the Trigger.dev team.
|
||||
</Paragraph>
|
||||
</div>
|
||||
<hr className="border-charcoal-800" />
|
||||
<div>
|
||||
<StepNumber stepNumber="1" title="Email us" />
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
Send us an email to this address from your Trigger.dev account email
|
||||
address:
|
||||
<ClipboardField
|
||||
variant="secondary/medium"
|
||||
value="priority-support@trigger.dev"
|
||||
className="my-2"
|
||||
/>
|
||||
</Paragraph>
|
||||
</StepContentContainer>
|
||||
<StepNumber stepNumber="2" title="Look out for an invite from Slack" />
|
||||
<StepContentContainer>
|
||||
<Paragraph>
|
||||
As soon as we can, we'll setup a Slack Connect channel and say hello!
|
||||
</Paragraph>
|
||||
</StepContentContainer>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)}
|
||||
<SideMenuItem
|
||||
name="Ask in our Discord"
|
||||
icon={DiscordIcon}
|
||||
trailingIcon={ArrowUpRightIcon}
|
||||
trailingIconClassName="text-text-dimmed"
|
||||
to="https://trigger.dev/discord"
|
||||
data-action="join our discord"
|
||||
target="_blank"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Book a 15 min call"
|
||||
icon={CalendarDaysIcon}
|
||||
trailingIcon={ArrowUpRightIcon}
|
||||
trailingIconClassName="text-text-dimmed"
|
||||
inactiveIconColor="text-rose-500"
|
||||
activeIconColor="text-rose-500"
|
||||
to="https://cal.com/team/triggerdotdev/founders-call"
|
||||
data-action="book-a-call"
|
||||
target="_blank"
|
||||
/>
|
||||
<Feedback
|
||||
button={
|
||||
<Button
|
||||
variant="small-menu-item"
|
||||
LeadingIcon={EnvelopeIcon}
|
||||
leadingIconClassName="text-blue-500"
|
||||
data-action="contact-us"
|
||||
fullWidth
|
||||
textAlignLeft
|
||||
>
|
||||
Contact us…
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
function ProjectSelector({
|
||||
project,
|
||||
organizations,
|
||||
@@ -495,47 +576,47 @@ function V2ProjectSideMenu({
|
||||
<SideMenuItem
|
||||
name="Jobs"
|
||||
icon="job"
|
||||
iconColor="text-indigo-500"
|
||||
activeIconColor="text-indigo-500"
|
||||
to={projectPath(organization, project)}
|
||||
data-action="jobs"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Runs"
|
||||
icon="runs"
|
||||
iconColor="text-teal-500"
|
||||
activeIconColor="text-teal-500"
|
||||
to={projectRunsPath(organization, project)}
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Triggers"
|
||||
icon="trigger"
|
||||
iconColor="text-amber-500"
|
||||
activeIconColor="text-amber-500"
|
||||
to={projectTriggersPath(organization, project)}
|
||||
data-action="triggers"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Events"
|
||||
icon={CursorArrowRaysIcon}
|
||||
iconColor="text-sky-500"
|
||||
activeIconColor="text-sky-500"
|
||||
to={projectEventsPath(organization, project)}
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="HTTP endpoints"
|
||||
icon="http-endpoint"
|
||||
iconColor="text-pink-500"
|
||||
activeIconColor="text-pink-500"
|
||||
to={projectHttpEndpointsPath(organization, project)}
|
||||
data-action="httpendpoints"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Environments & API Keys"
|
||||
icon="environment"
|
||||
iconColor="text-rose-500"
|
||||
activeIconColor="text-rose-500"
|
||||
to={projectEnvironmentsPath(organization, project)}
|
||||
data-action="environments & api keys"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Project settings"
|
||||
icon="settings"
|
||||
iconColor="text-teal-500"
|
||||
activeIconColor="text-teal-500"
|
||||
to={projectSettingsPath(organization, project)}
|
||||
data-action="project-settings"
|
||||
/>
|
||||
@@ -558,41 +639,41 @@ function V3ProjectSideMenu({
|
||||
<SideMenuItem
|
||||
name="Tasks"
|
||||
icon={TaskIcon}
|
||||
iconColor="text-blue-500"
|
||||
activeIconColor="text-blue-500"
|
||||
to={v3ProjectPath(organization, project)}
|
||||
data-action="tasks"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Runs"
|
||||
icon="runs"
|
||||
iconColor="text-teal-500"
|
||||
activeIconColor="text-teal-500"
|
||||
to={v3RunsPath(organization, project)}
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Test"
|
||||
icon={BeakerIcon}
|
||||
iconColor="text-lime-500"
|
||||
activeIconColor="text-lime-500"
|
||||
to={v3TestPath(organization, project)}
|
||||
data-action="test"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Schedules"
|
||||
icon={ClockIcon}
|
||||
iconColor="text-sun-500"
|
||||
activeIconColor="text-sun-500"
|
||||
to={v3SchedulesPath(organization, project)}
|
||||
data-action="schedules"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="API keys"
|
||||
icon={KeyIcon}
|
||||
iconColor="text-amber-500"
|
||||
activeIconColor="text-amber-500"
|
||||
to={v3ApiKeysPath(organization, project)}
|
||||
data-action="api keys"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Environment variables"
|
||||
icon={IdentificationIcon}
|
||||
iconColor="text-pink-500"
|
||||
activeIconColor="text-pink-500"
|
||||
to={v3EnvironmentVariablesPath(organization, project)}
|
||||
data-action="environment variables"
|
||||
/>
|
||||
@@ -600,7 +681,7 @@ function V3ProjectSideMenu({
|
||||
<SideMenuItem
|
||||
name="Deployments"
|
||||
icon={ServerStackIcon}
|
||||
iconColor="text-blue-500"
|
||||
activeIconColor="text-blue-500"
|
||||
to={v3DeploymentsPath(organization, project)}
|
||||
data-action="deployments"
|
||||
/>
|
||||
@@ -608,7 +689,7 @@ function V3ProjectSideMenu({
|
||||
<SideMenuItem
|
||||
name="Alerts"
|
||||
icon={BellAlertIcon}
|
||||
iconColor="text-red-500"
|
||||
activeIconColor="text-red-500"
|
||||
to={v3ProjectAlertsPath(organization, project)}
|
||||
data-action="alerts"
|
||||
/>
|
||||
@@ -616,14 +697,14 @@ function V3ProjectSideMenu({
|
||||
<SideMenuItem
|
||||
name="Concurrency limits"
|
||||
icon={RectangleStackIcon}
|
||||
iconColor="text-indigo-500"
|
||||
activeIconColor="text-indigo-500"
|
||||
to={v3ConcurrencyPath(organization, project)}
|
||||
data-action="concurrency"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Project settings"
|
||||
icon="settings"
|
||||
iconColor="text-teal-500"
|
||||
activeIconColor="text-teal-500"
|
||||
to={v3ProjectSettingsPath(organization, project)}
|
||||
data-action="project-settings"
|
||||
/>
|
||||
|
||||
@@ -3,25 +3,26 @@ import { usePathName } from "~/hooks/usePathName";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { LinkButton } from "../primitives/Buttons";
|
||||
import { type IconNames } from "../primitives/NamedIcon";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip";
|
||||
import { Icon } from "../primitives/Icon";
|
||||
import { IconExclamationCircle } from "@tabler/icons-react";
|
||||
|
||||
export function SideMenuItem({
|
||||
icon,
|
||||
iconColor,
|
||||
activeIconColor,
|
||||
inactiveIconColor,
|
||||
trailingIcon,
|
||||
trailingIconClassName,
|
||||
name,
|
||||
to,
|
||||
hasWarning,
|
||||
badge,
|
||||
target,
|
||||
subItem = false,
|
||||
}: {
|
||||
icon?: IconNames | React.ComponentType<any>;
|
||||
iconColor?: string;
|
||||
activeIconColor?: string;
|
||||
inactiveIconColor?: string;
|
||||
trailingIcon?: IconNames | React.ComponentType<any>;
|
||||
trailingIconClassName?: string;
|
||||
name: string;
|
||||
to: string;
|
||||
hasWarning?: string | boolean;
|
||||
badge?: string;
|
||||
target?: AnchorHTMLAttributes<HTMLAnchorElement>["target"];
|
||||
subItem?: boolean;
|
||||
@@ -35,11 +36,13 @@ export function SideMenuItem({
|
||||
fullWidth
|
||||
textAlignLeft
|
||||
LeadingIcon={icon}
|
||||
leadingIconClassName={isActive ? iconColor : "text-text-dimmed"}
|
||||
leadingIconClassName={isActive ? activeIconColor : inactiveIconColor ?? "text-text-dimmed"}
|
||||
TrailingIcon={trailingIcon}
|
||||
trailingIconClassName={trailingIconClassName}
|
||||
to={to}
|
||||
target={target}
|
||||
className={cn(
|
||||
"text-text-bright group-hover:bg-charcoal-750",
|
||||
"text-text-bright group-hover:bg-charcoal-750 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0",
|
||||
subItem ? "text-text-dimmed" : "",
|
||||
isActive ? "bg-tertiary text-text-bright" : "group-hover:text-text-bright"
|
||||
)}
|
||||
@@ -48,27 +51,13 @@ export function SideMenuItem({
|
||||
{name}
|
||||
<div className="flex items-center gap-1">
|
||||
{badge !== undefined && <MenuCount count={badge} />}
|
||||
{typeof hasWarning === "string" ? (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Icon icon={IconExclamationCircle} className="h-5 w-5 text-error" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="flex items-center gap-1 border border-error bg-error/20 backdrop-blur-xl">
|
||||
{hasWarning}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
hasWarning && <Icon icon={IconExclamationCircle} className="h-5 w-5 text-error" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</LinkButton>
|
||||
);
|
||||
}
|
||||
|
||||
function MenuCount({ count }: { count: number | string }) {
|
||||
export function MenuCount({ count }: { count: number | string }) {
|
||||
return (
|
||||
<div className="rounded-full bg-charcoal-900 px-2 py-1 text-xxs uppercase tracking-wider text-text-dimmed">
|
||||
{count}
|
||||
|
||||
@@ -45,7 +45,10 @@ export function AppliedFilter({
|
||||
</div>
|
||||
{removable && (
|
||||
<button
|
||||
className={cn("group flex size-6 items-center justify-center", variantClassName.clear)}
|
||||
className={cn(
|
||||
"group flex size-6 items-center justify-center focus-custom",
|
||||
variantClassName.clear
|
||||
)}
|
||||
onClick={onRemove}
|
||||
>
|
||||
<XMarkIcon className="size-3.5" />
|
||||
|
||||
@@ -48,11 +48,11 @@ const theme = {
|
||||
"border-black/40 text-charcoal-900 group-hover:border-black/60 group-hover:text-charcoal-900",
|
||||
},
|
||||
secondary: {
|
||||
textColor: "text-secondary group-hover:text-secondary transition group-disabled:text-secondary",
|
||||
textColor: "text-text-bright transition group-disabled:text-text-dimmed/80",
|
||||
button:
|
||||
"bg-transparent border border-secondary group-hover:border-secondary group-hover:bg-secondary/10 group-disabled:opacity-30 group-disabled:border-secondary group-disabled:bg-transparent group-disabled:pointer-events-none",
|
||||
"bg-secondary group-hover:bg-charcoal-600 group-hover:border-charcoal-650 border border-charcoal-600 group-disabled:bg-secondary group-disabled:opacity-60 group-disabled:pointer-events-none",
|
||||
shortcut:
|
||||
"border-secondary/30 text-secondary group-hover:text-text-bright/80 group-hover:border-dimmed/60",
|
||||
"border-text-dimmed/40 text-text-dimmed group-hover:text-text-bright group-hover:border-text-dimmed",
|
||||
},
|
||||
tertiary: {
|
||||
textColor: "text-text-bright transition group-disabled:text-text-dimmed/80",
|
||||
@@ -114,8 +114,7 @@ const variant = {
|
||||
"danger/extra-large": createVariant("extra-large", "danger"),
|
||||
"menu-item": {
|
||||
textColor: "text-text-bright px-1",
|
||||
button:
|
||||
"h-9 px-[0.475rem] text-sm rounded-sm bg-transparent group-hover:bg-charcoal-800 transition",
|
||||
button: "h-9 px-[0.475rem] text-sm rounded-sm bg-transparent group-hover:bg-charcoal-750",
|
||||
icon: "h-5",
|
||||
iconSpacing: "gap-x-0.5",
|
||||
shortcutVariant: undefined,
|
||||
@@ -124,7 +123,7 @@ const variant = {
|
||||
"small-menu-item": {
|
||||
textColor: "text-text-bright",
|
||||
button:
|
||||
"h-[1.8rem] px-[0.4rem] text-2sm rounded-sm text-text-dimmed bg-transparent group-hover:bg-charcoal-850 transition",
|
||||
"h-[1.8rem] px-[0.4rem] text-2sm rounded-sm text-text-dimmed bg-transparent group-hover:bg-charcoal-750",
|
||||
icon: "h-4",
|
||||
iconSpacing: "gap-x-1.5",
|
||||
shortcutVariant: undefined,
|
||||
@@ -133,7 +132,7 @@ const variant = {
|
||||
"small-menu-sub-item": {
|
||||
textColor: "text-text-dimmed",
|
||||
button:
|
||||
"h-[1.8rem] px-[0.5rem] ml-5 text-2sm rounded-sm text-text-dimmed bg-transparent group-hover:bg-charcoal-850 transition",
|
||||
"h-[1.8rem] px-[0.5rem] ml-5 text-2sm rounded-sm text-text-dimmed bg-transparent group-hover:bg-charcoal-750 focus-custom",
|
||||
icon: undefined,
|
||||
iconSpacing: undefined,
|
||||
shortcutVariant: undefined,
|
||||
@@ -142,7 +141,7 @@ const variant = {
|
||||
};
|
||||
|
||||
const allVariants = {
|
||||
$all: "font-normal text-center font-sans justify-center items-center shrink-0 transition duration-150 rounded-[3px] select-none group-focus:outline-none group-disabled:opacity-75 group-disabled:pointer-events-none",
|
||||
$all: "font-normal text-center font-sans justify-center items-center shrink-0 transition duration-150 rounded-[3px] select-none group-focus:outline-none group-disabled:opacity-75 group-disabled:pointer-events-none focus-custom",
|
||||
variant: variant,
|
||||
};
|
||||
|
||||
@@ -268,7 +267,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonPropsType>(
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn("group outline-none", props.fullWidth ? "w-full" : "")}
|
||||
className={cn("group outline-none focus-custom", props.fullWidth ? "w-full" : "")}
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
@@ -328,7 +327,7 @@ export const LinkButton = ({
|
||||
<ExtLink
|
||||
href={to.toString()}
|
||||
ref={innerRef}
|
||||
className={cn("group outline-none", props.fullWidth ? "w-full" : "")}
|
||||
className={cn("group focus-custom", props.fullWidth ? "w-full" : "")}
|
||||
onClick={onClick}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseEnter={onMouseEnter}
|
||||
@@ -343,7 +342,7 @@ export const LinkButton = ({
|
||||
<Link
|
||||
to={to}
|
||||
ref={innerRef}
|
||||
className={cn("group outline-none", props.fullWidth ? "w-full" : "")}
|
||||
className={cn("group focus-custom", props.fullWidth ? "w-full" : "")}
|
||||
onClick={onClick}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseEnter={onMouseEnter}
|
||||
|
||||
@@ -50,7 +50,7 @@ const DialogContent = React.forwardRef<
|
||||
>
|
||||
<hr className="absolute left-0 top-11 w-full" />
|
||||
{children}
|
||||
<DialogPrimitive.Close className="ring-offset-background data-[state=open]:bg-accent data-[state=open]:text-muted-foreground focus-visible:ring-ring absolute right-3 top-3 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none">
|
||||
<DialogPrimitive.Close className="data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-3 top-2 rounded-sm py-1 pr-1 opacity-70 transition-opacity focus-custom hover:opacity-100 disabled:pointer-events-none">
|
||||
<div className="flex gap-x-2">
|
||||
<ShortcutKey
|
||||
shortcut={{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { LinkButton } from "./Buttons";
|
||||
import { Header3 } from "./Headers";
|
||||
import { Paragraph } from "./Paragraph";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { LinkButton } from "./Buttons";
|
||||
import { Header2 } from "./Headers";
|
||||
import { Paragraph } from "./Paragraph";
|
||||
|
||||
const variants = {
|
||||
info: {
|
||||
@@ -57,7 +57,7 @@ export function InfoPanel({
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
{title && <Header3 className="text-text-bright">{title}</Header3>}
|
||||
{title && <Header2 className="text-text-bright">{title}</Header2>}
|
||||
{typeof children === "string" ? (
|
||||
<Paragraph variant={"small"} className="text-text-dimmed">
|
||||
{children}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cn } from "~/utils/cn";
|
||||
import { Icon, RenderIcon } from "./Icon";
|
||||
|
||||
const containerBase =
|
||||
"has-[:focus-visible]:outline-none has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-0 has-[:focus]:border-ring has-[:focus]:outline-none has-[:focus]:ring-2 has-[:focus]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ring-offset-background transition cursor-text";
|
||||
"has-[:focus-visible]:outline-none has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-text-link has-[:focus-visible]:ring-offset-0 has-[:focus]:border-ring has-[:focus]:outline-none has-[:focus]:ring-2 has-[:focus]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ring-offset-background transition cursor-text";
|
||||
|
||||
const inputBase =
|
||||
"h-full w-full text-text-bright bg-transparent file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 disabled:cursor-not-allowed outline-none ring-0 border-none";
|
||||
|
||||
@@ -49,7 +49,7 @@ export function PageTitle({ title, backButton }: PageTitleProps) {
|
||||
<div className="group -ml-1.5 flex items-center gap-0">
|
||||
<Link
|
||||
to={backButton.to}
|
||||
className="rounded px-1.5 py-1 text-xs text-text-dimmed transition group-hover:bg-charcoal-700 group-hover:text-text-bright"
|
||||
className="rounded px-1.5 py-1 text-xs text-text-dimmed transition focus-custom group-hover:bg-charcoal-700 group-hover:text-text-bright"
|
||||
>
|
||||
{backButton.text}
|
||||
</Link>
|
||||
|
||||
@@ -6,6 +6,8 @@ import * as React from "react";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { type ButtonContentPropsType, LinkButton } from "./Buttons";
|
||||
import { Paragraph, type ParagraphVariant } from "./Paragraph";
|
||||
import { ShortcutKey } from "./ShortcutKey";
|
||||
import { ShortcutDefinition, useShortcutKeys } from "~/hooks/useShortcutKeys";
|
||||
|
||||
const Popover = PopoverPrimitive.Root;
|
||||
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||
@@ -91,7 +93,7 @@ function PopoverCustomTrigger({
|
||||
<PopoverTrigger
|
||||
{...props}
|
||||
className={cn(
|
||||
"group flex items-center justify-end gap-1 rounded text-text-dimmed transition hover:bg-charcoal-850 hover:text-text-bright",
|
||||
"group flex items-center justify-end gap-1 rounded text-text-dimmed transition focus-custom hover:bg-charcoal-850 hover:text-text-bright",
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -100,6 +102,45 @@ function PopoverCustomTrigger({
|
||||
);
|
||||
}
|
||||
|
||||
function PopoverSideMenuTrigger({
|
||||
isOpen,
|
||||
children,
|
||||
className,
|
||||
shortcut,
|
||||
...props
|
||||
}: { isOpen?: boolean; shortcut?: ShortcutDefinition } & React.ComponentPropsWithoutRef<
|
||||
typeof PopoverTrigger
|
||||
>) {
|
||||
const ref = React.useRef<HTMLButtonElement>(null);
|
||||
useShortcutKeys({
|
||||
shortcut: shortcut,
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (ref.current) {
|
||||
ref.current.click();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<PopoverTrigger
|
||||
{...props}
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-[1.8rem] shrink-0 select-none items-center gap-x-1.5 rounded-sm bg-transparent px-[0.4rem] text-center font-sans text-2sm font-normal text-text-bright transition duration-150 focus-custom hover:bg-charcoal-750",
|
||||
shortcut ? "justify-between" : "",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{shortcut && (
|
||||
<ShortcutKey className={cn("size-4 flex-none")} shortcut={shortcut} variant={"small"} />
|
||||
)}
|
||||
</PopoverTrigger>
|
||||
);
|
||||
}
|
||||
|
||||
function PopoverArrowTrigger({
|
||||
isOpen,
|
||||
children,
|
||||
@@ -116,7 +157,7 @@ function PopoverArrowTrigger({
|
||||
<PopoverTrigger
|
||||
{...props}
|
||||
className={cn(
|
||||
"group flex h-6 items-center gap-1 rounded px-2 text-text-dimmed transition hover:bg-charcoal-700 hover:text-text-bright",
|
||||
"group flex h-6 items-center gap-1 rounded px-2 text-text-dimmed transition focus-custom hover:bg-charcoal-700 hover:text-text-bright",
|
||||
fullWidth && "w-full justify-between",
|
||||
className
|
||||
)}
|
||||
@@ -149,7 +190,7 @@ function PopoverVerticalEllipseTrigger({
|
||||
<PopoverTrigger
|
||||
{...props}
|
||||
className={cn(
|
||||
"group flex items-center justify-end gap-1 rounded px-1.5 py-1.5 text-text-dimmed transition hover:bg-charcoal-750 hover:text-text-bright",
|
||||
"group flex items-center justify-end gap-1 rounded px-1.5 py-1.5 text-text-dimmed transition focus-custom hover:bg-charcoal-750 hover:text-text-bright",
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -165,6 +206,7 @@ export {
|
||||
PopoverCustomTrigger,
|
||||
PopoverMenuItem,
|
||||
PopoverSectionHeader,
|
||||
PopoverSideMenuTrigger,
|
||||
PopoverTrigger,
|
||||
PopoverVerticalEllipseTrigger,
|
||||
};
|
||||
|
||||
@@ -70,7 +70,7 @@ export function RadioButtonCircle({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"ring-offset-background focus-visible:ring-ring aspect-square h-4 w-4 shrink-0 overflow-hidden rounded-full border border-charcoal-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"ring-offset-background aspect-square h-4 w-4 shrink-0 overflow-hidden rounded-full border border-charcoal-600 focus-custom disabled:cursor-not-allowed disabled:opacity-50",
|
||||
boxClassName
|
||||
)}
|
||||
>
|
||||
@@ -81,7 +81,9 @@ export function RadioButtonCircle({
|
||||
outerCircleClassName
|
||||
)}
|
||||
>
|
||||
<Circle className={cn("h-1.5 w-1.5 fill-white text-white", innerCircleClassName)} />
|
||||
<Circle
|
||||
className={cn("size-1.5 fill-text-bright text-text-bright", innerCircleClassName)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -121,7 +123,7 @@ export const RadioGroupItem = React.forwardRef<
|
||||
<RadioGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"group flex cursor-pointer items-start gap-x-2 transition",
|
||||
"group flex cursor-pointer items-start gap-x-2 transition focus-custom",
|
||||
variation.button,
|
||||
className
|
||||
)}
|
||||
@@ -129,7 +131,7 @@ export const RadioGroupItem = React.forwardRef<
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"ring-offset-background focus-visible:ring-ring aspect-square h-4 w-4 shrink-0 overflow-hidden rounded-full border border-charcoal-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"ring-offset-background focus-visible:ring-ring aspect-square h-4 w-4 shrink-0 overflow-hidden rounded-full border border-charcoal-600 focus-custom disabled:cursor-not-allowed disabled:opacity-50",
|
||||
variation.inputPosition
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -26,7 +26,7 @@ const ResizableHandle = ({
|
||||
}) => (
|
||||
<PanelResizer
|
||||
className={cn(
|
||||
"focus-visible:ring-ring group relative flex w-0.75 items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 hover:w-0.75 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-secondary/50 focus-visible:ring-offset-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
|
||||
"group relative flex w-0.75 items-center justify-center focus-custom after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 hover:w-0.75 [&[data-panel-group-direction=vertical]>div]:rotate-90",
|
||||
className
|
||||
)}
|
||||
size="3px"
|
||||
|
||||
@@ -65,10 +65,7 @@ export default function SegmentedControl({
|
||||
value={option.value}
|
||||
className={({ active, checked }) =>
|
||||
cn(
|
||||
"relative flex h-full grow cursor-pointer text-center font-normal focus:outline-none",
|
||||
active
|
||||
? "ring-offset-2 focus-visible:ring focus-visible:ring-secondary focus-visible:ring-opacity-60"
|
||||
: "",
|
||||
"relative flex h-full grow cursor-pointer text-center font-normal focus-custom",
|
||||
checked
|
||||
? variants[variant].active
|
||||
: "text-text-dimmed transition hover:text-text-bright"
|
||||
|
||||
@@ -15,18 +15,18 @@ const sizes = {
|
||||
button: "h-6 rounded text-xs px-2 ",
|
||||
},
|
||||
medium: {
|
||||
button: "h-8 rounded text-xs px-3 text-sm",
|
||||
button: "h-8 rounded px-3 text-sm",
|
||||
},
|
||||
};
|
||||
|
||||
const style = {
|
||||
tertiary: {
|
||||
button:
|
||||
"bg-tertiary focus-within:ring-charcoal-500 border border-tertiary hover:text-text-bright hover:border-charcoal-600",
|
||||
"bg-tertiary focus-custom border border-tertiary hover:text-text-bright hover:border-charcoal-600",
|
||||
},
|
||||
minimal: {
|
||||
button:
|
||||
"bg-transparent focus-within:ring-charcoal-500 hover:bg-tertiary disabled:bg-transparent disabled:pointer-events-none",
|
||||
"bg-transparent focus-custom hover:bg-tertiary disabled:bg-transparent disabled:pointer-events-none",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -322,7 +322,7 @@ export function SelectTrigger({
|
||||
render={
|
||||
<Ariakit.Select
|
||||
className={cn(
|
||||
"group flex items-center gap-1 outline-offset-0 focus-within:outline-none focus-within:ring-1 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"group flex items-center gap-1 focus-custom disabled:cursor-not-allowed disabled:opacity-50",
|
||||
variantClasses.button,
|
||||
className
|
||||
)}
|
||||
@@ -426,7 +426,7 @@ export function SelectList(props: SelectListProps) {
|
||||
<Component
|
||||
{...props}
|
||||
className={cn(
|
||||
"overflow-y-auto overscroll-contain outline-none scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600",
|
||||
"overflow-y-auto overscroll-contain scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600 focus-custom",
|
||||
props.className
|
||||
)}
|
||||
/>
|
||||
@@ -440,11 +440,11 @@ export interface SelectItemProps extends Ariakit.SelectItemProps {
|
||||
}
|
||||
|
||||
const selectItemClasses =
|
||||
"group cursor-pointer px-1 pt-1 text-xs text-text-dimmed outline-none last:pb-1";
|
||||
"group cursor-pointer px-1 pt-1 text-sm text-text-dimmed focus-custom last:pb-1";
|
||||
|
||||
export function SelectItem({
|
||||
icon,
|
||||
checkIcon = <Ariakit.SelectItemCheck className="size-8 flex-none text-white" />,
|
||||
checkIcon = <Ariakit.SelectItemCheck className="size-8 flex-none text-text-bright" />,
|
||||
shortcut,
|
||||
...props
|
||||
}: SelectItemProps) {
|
||||
@@ -477,7 +477,7 @@ export function SelectItem({
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<div className="flex h-7 w-full items-center gap-1 rounded-sm px-2 group-data-[active-item=true]:bg-tertiary">
|
||||
<div className="flex h-8 w-full items-center gap-1 rounded-sm px-2 group-data-[active-item=true]:bg-tertiary">
|
||||
{icon}
|
||||
<div className="grow truncate">{props.children || props.value}</div>
|
||||
{checkIcon}
|
||||
|
||||
@@ -6,14 +6,14 @@ import { cn } from "~/utils/cn";
|
||||
|
||||
const variations = {
|
||||
large: {
|
||||
container: "flex items-center gap-x-2 rounded-md hover:bg-tertiary p-2 transition",
|
||||
container: "flex items-center gap-x-2 rounded-md hover:bg-tertiary p-2 transition focus-custom",
|
||||
root: "h-6 w-11",
|
||||
thumb: "h-5 w-5 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
|
||||
text: "text-sm text-charcoal-400 group-hover:text-charcoal-200 transition",
|
||||
},
|
||||
small: {
|
||||
container:
|
||||
"flex items-center gap-x-1.5 rounded hover:bg-tertiary pr-1 py-[0.1rem] pl-1.5 transition",
|
||||
"flex items-center h-[1.5rem] gap-x-1.5 rounded hover:bg-tertiary pr-1 py-[0.1rem] pl-1.5 transition focus-custom",
|
||||
root: "h-3 w-6",
|
||||
thumb: "h-2.5 w-2.5 data-[state=checked]:translate-x-2.5 data-[state=unchecked]:translate-x-0",
|
||||
text: "text-xs text-charcoal-400 group-hover:text-charcoal-200 hover:cursor-pointer transition",
|
||||
@@ -38,14 +38,14 @@ export const Switch = React.forwardRef<React.ElementRef<typeof SwitchPrimitives.
|
||||
) : null}
|
||||
<div
|
||||
className={cn(
|
||||
"group-focus-visible:ring-ring group-focus-visible:ring-offset-background peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors group-focus-visible:ring-2 group-focus-visible:ring-offset-2 group-disabled:cursor-not-allowed group-disabled:opacity-50 group-data-[state=checked]:bg-secondary group-data-[state=unchecked]:bg-charcoal-700 focus-visible:outline-none",
|
||||
"inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors group-disabled:cursor-not-allowed group-disabled:opacity-50 group-data-[state=checked]:bg-text-link group-data-[state=unchecked]:bg-charcoal-700",
|
||||
root
|
||||
)}
|
||||
>
|
||||
<SwitchPrimitives.Thumb
|
||||
className={cn(
|
||||
thumb,
|
||||
"pointer-events-none block rounded-full bg-charcoal-200 shadow-lg ring-0 transition group-data-[state=checked]:bg-charcoal-700"
|
||||
"pointer-events-none block rounded-full bg-charcoal-200 transition group-data-[state=checked]:bg-text-bright"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -151,7 +151,7 @@ const rowHoverStyles = {
|
||||
};
|
||||
|
||||
const stickyStyles =
|
||||
"sticky right-0 z-10 w-[2.8rem] min-w-[2.8rem] bg-background-dimmed before:absolute before:pointer-events-none before:-left-8 before:top-0 before:h-full before:min-w-[2rem]";
|
||||
"sticky right-0 w-[2.8rem] min-w-[2.8rem] bg-background-dimmed before:absolute before:pointer-events-none before:-left-8 before:top-0 before:h-full before:min-w-[2rem]";
|
||||
|
||||
export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
|
||||
(
|
||||
@@ -202,11 +202,11 @@ export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
|
||||
colSpan={colSpan}
|
||||
>
|
||||
{to ? (
|
||||
<Link to={to} className={cn(flexClasses, actionClassName)}>
|
||||
<Link to={to} className={cn("focus-custom", flexClasses, actionClassName)}>
|
||||
{children}
|
||||
</Link>
|
||||
) : onClick ? (
|
||||
<button onClick={onClick} className={cn(flexClasses, actionClassName)}>
|
||||
<button onClick={onClick} className={cn("focus-custom", flexClasses, actionClassName)}>
|
||||
{children}
|
||||
</button>
|
||||
) : (
|
||||
|
||||
@@ -46,7 +46,7 @@ export function TabLink({
|
||||
layoutId: string;
|
||||
}) {
|
||||
return (
|
||||
<NavLink to={to} className="group flex flex-col items-center pt-1" end>
|
||||
<NavLink to={to} className="group flex flex-col items-center pt-1 focus-custom" end>
|
||||
{({ isActive, isPending }) => {
|
||||
return (
|
||||
<>
|
||||
@@ -96,7 +96,7 @@ export function TabButton({
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn("group flex flex-col items-center pt-1", props.className)}
|
||||
className={cn("group flex flex-col items-center pt-1 focus-custom", props.className)}
|
||||
type="button"
|
||||
ref={ref}
|
||||
{...props}
|
||||
|
||||
@@ -8,7 +8,7 @@ export function TextArea({ className, rows, ...props }: TextAreaProps) {
|
||||
{...props}
|
||||
rows={rows ?? 6}
|
||||
className={cn(
|
||||
"ring-offset-background placeholder:text-muted-foreground focus:border-ring focus:ring-ring focus-visible:ring-ring w-full rounded-md border border-tertiary bg-tertiary px-3 text-sm text-text-bright transition file:border-0 file:bg-transparent file:text-base file:font-medium hover:border-charcoal-600 focus:outline-none focus:ring-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"placeholder:text-muted-foreground w-full rounded-md border border-tertiary bg-tertiary px-3 text-sm text-text-bright transition focus-custom file:border-0 file:bg-transparent file:text-base file:font-medium hover:border-charcoal-600 focus:border-transparent focus:ring-0 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -41,7 +41,7 @@ const TooltipContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden animate-in data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1",
|
||||
"z-50 overflow-hidden animate-in data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 focus-visible:outline-none",
|
||||
variantClasses[variant],
|
||||
className
|
||||
)}
|
||||
@@ -72,7 +72,9 @@ function SimpleTooltip({
|
||||
return (
|
||||
<TooltipProvider disableHoverableContent={disableHoverableContent}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger className={cn("h-fit", buttonClassName)}>{button}</TooltipTrigger>
|
||||
<TooltipTrigger tabIndex={-1} className={cn("h-fit", buttonClassName)}>
|
||||
{button}
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side={side}
|
||||
hidden={hidden}
|
||||
|
||||
@@ -368,7 +368,7 @@ function AppliedStatusFilter() {
|
||||
{(search, setSearch) => (
|
||||
<StatusDropdown
|
||||
trigger={
|
||||
<Ariakit.Select render={<div className="group cursor-pointer" />}>
|
||||
<Ariakit.Select render={<div className="group cursor-pointer focus-custom" />}>
|
||||
<AppliedFilter
|
||||
label="Status"
|
||||
value={appliedSummary(statuses.map((v) => runStatusTitle(v as TaskRunStatus)))}
|
||||
@@ -456,7 +456,7 @@ function AppliedEnvironmentFilter({
|
||||
{(search, setSearch) => (
|
||||
<EnvironmentsDropdown
|
||||
trigger={
|
||||
<Ariakit.Select render={<div className="group cursor-pointer" />}>
|
||||
<Ariakit.Select render={<div className="group cursor-pointer focus-custom" />}>
|
||||
<AppliedFilter
|
||||
label="Environment"
|
||||
value={appliedSummary(
|
||||
@@ -547,7 +547,7 @@ function AppliedTaskFilter({ possibleTasks }: Pick<RunFiltersProps, "possibleTas
|
||||
{(search, setSearch) => (
|
||||
<TasksDropdown
|
||||
trigger={
|
||||
<Ariakit.Select render={<div className="group cursor-pointer" />}>
|
||||
<Ariakit.Select render={<div className="group cursor-pointer focus-custom" />}>
|
||||
<AppliedFilter
|
||||
label="Task"
|
||||
value={appliedSummary(
|
||||
@@ -645,7 +645,7 @@ function AppliedBulkActionsFilter({ bulkActions }: Pick<RunFiltersProps, "bulkAc
|
||||
{(search, setSearch) => (
|
||||
<BulkActionsDropdown
|
||||
trigger={
|
||||
<Ariakit.Select render={<div className="group cursor-pointer" />}>
|
||||
<Ariakit.Select render={<div className="group cursor-pointer focus-custom" />}>
|
||||
<AppliedFilter
|
||||
label="Bulk action"
|
||||
value={bulkId}
|
||||
@@ -764,7 +764,7 @@ function AppliedTagsFilter() {
|
||||
{(search, setSearch) => (
|
||||
<TagsDropdown
|
||||
trigger={
|
||||
<Ariakit.Select render={<div className="group cursor-pointer" />}>
|
||||
<Ariakit.Select render={<div className="group cursor-pointer focus-custom" />}>
|
||||
<AppliedFilter
|
||||
label="Tags"
|
||||
value={appliedSummary(values("tags"))}
|
||||
@@ -902,7 +902,7 @@ function AppliedPeriodFilter() {
|
||||
{(search, setSearch) => (
|
||||
<CreatedDropdown
|
||||
trigger={
|
||||
<Ariakit.Select render={<div className="group cursor-pointer" />}>
|
||||
<Ariakit.Select render={<div className="group cursor-pointer focus-custom" />}>
|
||||
<AppliedFilter
|
||||
label="Created"
|
||||
value={
|
||||
|
||||
@@ -75,7 +75,7 @@ export function RunInspector({
|
||||
if (!run) {
|
||||
return (
|
||||
<div className="grid h-full max-h-full grid-rows-[2.5rem_1fr] overflow-hidden bg-background-bright">
|
||||
<div className="mx-3 flex items-center justify-between gap-2 overflow-x-hidden">
|
||||
<div className="flex items-center justify-between gap-2 overflow-x-hidden px-3">
|
||||
<div className="flex items-center gap-1 overflow-x-hidden">
|
||||
<RunIcon name={"task"} spanName="" className="h-4 min-h-4 w-4 min-w-4" />
|
||||
<Header2 className={cn("overflow-x-hidden text-blue-500")}>
|
||||
@@ -101,7 +101,7 @@ export function RunInspector({
|
||||
|
||||
return (
|
||||
<div className="grid h-full max-h-full grid-rows-[2.5rem_2rem_1fr_3.25rem] overflow-hidden bg-background-bright">
|
||||
<div className="mx-3 flex items-center justify-between gap-2 overflow-x-hidden">
|
||||
<div className="flex items-center justify-between gap-2 overflow-x-hidden px-3">
|
||||
<div className="flex items-center gap-1 overflow-x-hidden">
|
||||
<RunIcon
|
||||
name={"task"}
|
||||
|
||||
@@ -47,7 +47,7 @@ export function SpanInspector({
|
||||
|
||||
return (
|
||||
<div className="grid h-full max-h-full grid-rows-[2.5rem_2rem_1fr] overflow-hidden bg-background-bright">
|
||||
<div className="mx-3 flex items-center justify-between gap-2 overflow-x-hidden">
|
||||
<div className="flex items-center justify-between gap-2 overflow-x-hidden px-3">
|
||||
<div className="flex items-center gap-1 overflow-x-hidden">
|
||||
<RunIcon
|
||||
name={span.style?.icon}
|
||||
|
||||
+8
-10
@@ -501,16 +501,14 @@ function TasksTreeView({
|
||||
|
||||
return (
|
||||
<div className="grid h-full grid-rows-[2.5rem_1fr_3.25rem] overflow-hidden">
|
||||
<div className="mx-3 flex items-center justify-between gap-2 border-b border-grid-dimmed">
|
||||
<div className="flex items-center justify-between gap-2 border-b border-grid-dimmed px-2">
|
||||
<SearchField onChange={setFilterText} />
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
variant="small"
|
||||
label="Errors only"
|
||||
checked={errorsOnly}
|
||||
onCheckedChange={(e) => setErrorsOnly(e.valueOf())}
|
||||
/>
|
||||
</div>
|
||||
<Switch
|
||||
variant="small"
|
||||
label="Errors only"
|
||||
checked={errorsOnly}
|
||||
onCheckedChange={(e) => setErrorsOnly(e.valueOf())}
|
||||
/>
|
||||
</div>
|
||||
<ResizablePanelGroup autosaveId={resizableSettings.tree.autosaveId}>
|
||||
{/* Tree list */}
|
||||
@@ -1008,7 +1006,7 @@ function ShowParentLink({
|
||||
{mouseOver ? (
|
||||
<ShowParentIconSelected className="h-4 w-4 text-indigo-500" />
|
||||
) : (
|
||||
<ShowParentIcon className="text-charcoal-650 h-4 w-4" />
|
||||
<ShowParentIcon className="h-4 w-4 text-charcoal-650" />
|
||||
)}
|
||||
<Paragraph
|
||||
variant="small"
|
||||
|
||||
+8
-10
@@ -528,16 +528,14 @@ function TasksTreeView({
|
||||
|
||||
return (
|
||||
<div className="grid h-full grid-rows-[2.5rem_1fr_3.25rem] overflow-hidden">
|
||||
<div className="mx-3 flex items-center justify-between gap-2 border-b border-grid-dimmed">
|
||||
<div className="flex items-center justify-between gap-2 border-b border-grid-dimmed px-3">
|
||||
<SearchField onChange={setFilterText} />
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
variant="small"
|
||||
label="Errors only"
|
||||
checked={errorsOnly}
|
||||
onCheckedChange={(e) => setErrorsOnly(e.valueOf())}
|
||||
/>
|
||||
</div>
|
||||
<Switch
|
||||
variant="small"
|
||||
label="Errors only"
|
||||
checked={errorsOnly}
|
||||
onCheckedChange={(e) => setErrorsOnly(e.valueOf())}
|
||||
/>
|
||||
</div>
|
||||
<ResizablePanelGroup autosaveId={resizableSettings.tree.autosaveId}>
|
||||
{/* Tree list */}
|
||||
@@ -1015,7 +1013,7 @@ function ShowParentLink({ runFriendlyId }: { runFriendlyId: string }) {
|
||||
{mouseOver ? (
|
||||
<ShowParentIconSelected className="h-4 w-4 text-indigo-500" />
|
||||
) : (
|
||||
<ShowParentIcon className="text-charcoal-650 h-4 w-4" />
|
||||
<ShowParentIcon className="h-4 w-4 text-charcoal-650" />
|
||||
)}
|
||||
<Paragraph
|
||||
variant="small"
|
||||
|
||||
@@ -14,7 +14,7 @@ export const feedbackTypeLabel = {
|
||||
feature: "Feature request",
|
||||
help: "Help me out",
|
||||
enterprise: "Enterprise enquiry",
|
||||
"developer preview": "Developer preview feedback",
|
||||
feedback: "General feedback",
|
||||
};
|
||||
|
||||
export type FeedbackType = keyof typeof feedbackTypeLabel;
|
||||
|
||||
+2
-2
@@ -172,7 +172,7 @@ function SpanBody({
|
||||
|
||||
return (
|
||||
<div className="grid h-full max-h-full grid-rows-[2.5rem_2rem_1fr] overflow-hidden bg-background-bright">
|
||||
<div className="mx-3 flex items-center justify-between gap-2 overflow-x-hidden">
|
||||
<div className="flex items-center justify-between gap-2 overflow-x-hidden px-3">
|
||||
<div className="flex items-center gap-1 overflow-x-hidden">
|
||||
<RunIcon
|
||||
name={span.style?.icon}
|
||||
@@ -413,7 +413,7 @@ function RunBody({
|
||||
|
||||
return (
|
||||
<div className="grid h-full max-h-full grid-rows-[2.5rem_2rem_1fr_3.25rem] overflow-hidden bg-background-bright">
|
||||
<div className="mx-3 flex items-center justify-between gap-2 overflow-x-hidden">
|
||||
<div className="flex items-center justify-between gap-2 overflow-x-hidden px-3">
|
||||
<div className="flex items-center gap-1 overflow-x-hidden">
|
||||
<RunIcon
|
||||
name={"task"}
|
||||
|
||||
@@ -62,6 +62,7 @@ const charcoal = {
|
||||
400: "#878C99",
|
||||
500: "#5F6570",
|
||||
600: "#3B3E45",
|
||||
650: "#2C3034",
|
||||
700: "#272A2E",
|
||||
750: "#212327",
|
||||
800: "#1A1B1F",
|
||||
@@ -132,7 +133,7 @@ const lavender = {
|
||||
|
||||
/** Text colors */
|
||||
const primary = apple[500];
|
||||
const secondary = lavender[400];
|
||||
const secondary = charcoal[650];
|
||||
const tertiary = charcoal[700];
|
||||
const textLink = lavender[400];
|
||||
const textDimmed = charcoal[400];
|
||||
@@ -210,6 +211,12 @@ module.exports = {
|
||||
staging: stagingEnv,
|
||||
prod: prodEnv,
|
||||
},
|
||||
focusStyles: {
|
||||
outline: "2px solid",
|
||||
outlineOffset: "0px",
|
||||
outlineColor: textLink,
|
||||
borderRadius: "2px",
|
||||
},
|
||||
borderRadius: {
|
||||
lg: radius,
|
||||
md: `calc(${radius} - 2px)`,
|
||||
@@ -280,5 +287,13 @@ module.exports = {
|
||||
require("tailwind-scrollbar"),
|
||||
require("tailwind-scrollbar-hide"),
|
||||
require("tailwindcss-textshadow"),
|
||||
function ({ addUtilities, theme }) {
|
||||
const focusStyles = theme("focusStyles", {});
|
||||
addUtilities({
|
||||
".focus-custom": {
|
||||
"&:focus-visible": focusStyles,
|
||||
},
|
||||
});
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user