Major v3 web app update (#924)

* Added the new colours to the Tailwind config

* Fixed Tsailwind compile errors

* WIP Geist working in the app

* WIP Storybook

* Storybook is working again… ouch

* Use Geist mono for CodeMirror

* Some find/replace class changes

* Resizable colours improved

* WIP on primary button

* Minor style improvements

* Improve the side menu header styling

* Improved v2 breadcrumbs

* Account side menu tweaks

* The page now works with the NavBar and UpgradePrompt

* Removed unused import

* Refactored the buttons to be dry-er

* Removed the breadcrumbs

* WIP on nav bar

* WIP updating the button styles

* Fix for the old test page

* Fix for type errors

* Removed console.log

* Colour change in the resizable handle

* New loading bar divider

* Removed the design plugin

* Creating our own storybook pages

* Added some “storybook” pages

* Added DetailCell storybook

* WIP new animation for the loading page

* Added the new loading divider

* Fix for the time frame filter having a green border

* Style improvements to the new test page

* Small style improvements

* Improvements to the list pagination components

* Nav bar back button improved

* Improvements to the run page

* Lots of v2 style improvements

* Help looks better

* Improvements to the regenerate API key modal

* More general improvements

* Added section titles to our storybook

* Added form stories

* Fix for missing key prop

* Loading bar is working

* Fixed button submitting state

* Fix for the test button icon

* Code bg improved

* Improved oauth buttons

* Integration page header fix

* Various improvements, mainly changing button variants

* Segmented control style updates

* Added an outline of 3px for the segment control

* Fixes for the billing pages

* More pricing improvements

* Added dialog to storybook

* Typography now in storybook

* Free usage bar added to storybook

* Added inline clode to storybook

* Separated menu types into a sub section in storybook

* NamedIcon now in storybook

* Added page header to storybook

* Added pricing callout to storybook

* Added radio groups to storybook

* Added segmented controllers to storybook

* Added shortcuts to storybook

* Added switches to storybook

* Added toast to storybook

* Added tooltips to storybook

* Added tree view to storybook

* Added usage charts to storybook

* Tarted up the environment variables page

* Fix for the FreePlanUsage panel bg colours

* Added new badge variants

* Added the <TaskPath /> to the tasks table

* Span header improvements

* Started work on the timeline

* Started work on the timeline

* Fix for the run page header

* Timeline tick marks are working

* Timeline working

* Got the durations showing on hover

* Button improvements

* Experimenting with how the duration should be shown

* Lots of refinements to the timeline

* Much nicer API for the Timeline

* The switch is now purple to match the design, with inverted thumb when on

* Removed old stuff

* Export the props

* Added the span with duration back in

* Simplified more and fixed the span durations

* Put in some decent dummy data

* Rendering defaults

* Wip with the mouse cursor

* Improvements to the story

* Added Slider primitive

* Integrated the icons into the slider

* Lots of layout improvements

* Cleaned up imports

* “Root” indicator

* Animated background on partials

* Pull the styles through to the spans and log dots

* Fixed some bugs in the TreeView when nodes get created or destroyed

* Fix for incorrect parentTask reference

* Added run streaming endpoint for live refreshing

* Added the tabs to Storybook

* Fix for status font size mismatch

* Fix for the misaligned run filter status

* Set a minimum duration on the timeline

* The TreeView now re-calculates if the number of nodes has changed

* Partials now render to the end

* Improved the durations gutter

* The duration is calculated using the current time if there are partial events

* Added the environment label to storybook

* Improved the environment label style

* Removed bottom margin from badge

* Button style improvements

* Callouts style fits the new design better

* Got the scrollbars to appear on the tree/timeline

* Added scroll support

* Synced scrolling working

* Close button and exit icon for the detail panel

* secure field shows start of env

* Clipboard improvements

* Latest lockfile

* Fix for the durations wrapping

---------

Co-authored-by: James Ritchie <james@jamesritchie.co.uk>
This commit is contained in:
Matt Aitken
2024-03-04 17:33:53 +00:00
committed by GitHub
parent 64401ee8ec
commit 2a1273b683
240 changed files with 5187 additions and 4513 deletions
-1
View File
@@ -27,7 +27,6 @@ const config: StorybookConfig = {
"@storybook/addon-interactions",
"storybook-addon-variants",
"storybook-addon-designs",
"@storybook/addon-docs",
{
name: "@storybook/addon-styling",
options: {
+1 -1
View File
@@ -19,7 +19,7 @@ const preview: Preview = {
values: [
{
name: "App background",
value: "#0B1018",
value: "#15171A",
},
],
},
+14
View File
@@ -0,0 +1,14 @@
export function ExitIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="3.5" y1="8" x2="11.5" y2="8" stroke="currentColor" stroke-linecap="round" />
<line x1="15.5" y1="1.5" x2="15.5" y2="14.5" stroke="currentColor" stroke-linecap="round" />
<path
d="M8.5 4.5L12 8L8.5 11.5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

+3 -3
View File
@@ -17,13 +17,13 @@ export function ActiveBadge({ active, className, badgeSize = "normal" }: ActiveB
switch (active) {
case true:
return (
<span className={cn(variant[badgeSize], "bg-slate-800 text-green-500", className)}>
<span className={cn(variant[badgeSize], "bg-charcoal-800 text-green-500", className)}>
Active
</span>
);
case false:
return (
<span className={cn(variant[badgeSize], "bg-slate-800 text-dimmed", className)}>
<span className={cn(variant[badgeSize], "bg-charcoal-800 text-text-dimmed", className)}>
Disabled
</span>
);
@@ -52,6 +52,6 @@ export function NewBadge({
badgeSize?: keyof typeof variant;
}) {
return (
<span className={cn(variant[badgeSize], "bg-green-600 text-background", className)}>New!</span>
<span className={cn(variant[badgeSize], "text-background bg-green-600", className)}>New!</span>
);
}
+3 -3
View File
@@ -13,10 +13,10 @@ type ComingSoonProps = {
export function ComingSoon({ title, description, icon }: ComingSoonProps) {
return (
<MainCenteredContainer>
<div className="flex flex-col justify-center rounded border border-slate-800 bg-slate-850">
<div className="flex items-center gap-2 border-b border-slate-750 px-4 py-4">
<div className="flex flex-col justify-center rounded border border-charcoal-800 bg-charcoal-850">
<div className="flex items-center gap-2 border-b border-charcoal-750 px-4 py-4">
{typeof icon === "string" ? (
<NamedIconInBox name={icon} className={"h-10 w-10 bg-midnight-800"} />
<NamedIconInBox name={icon} className={"bg-midnight-800 h-10 w-10"} />
) : (
icon
)}
@@ -15,7 +15,7 @@ export function DefinitionTip({
<TooltipProvider>
<Tooltip disableHoverableContent>
<TooltipTrigger>
<span className="underline decoration-slate-600 decoration-dashed underline-offset-4 transition hover:decoration-slate-500">
<span className="underline decoration-charcoal-600 decoration-dashed underline-offset-4 transition hover:decoration-charcoal-500">
{children}
</span>
</TooltipTrigger>
+1 -1
View File
@@ -40,7 +40,7 @@ type DisplayOptionsProps = {
export function ErrorDisplay({ title, message, button }: DisplayOptionsProps) {
return (
<div className="p-4">
<Header1 className="mb-4 border-b border-slate-800 pb-4">{title}</Header1>
<Header1 className="mb-4 border-b border-charcoal-800 pb-4">{title}</Header1>
{message && <Header3>{message}</Header3>}
<LinkButton to={button ? button.to : "/"} variant="primary/medium" className="mt-8">
{button ? button.title : "Home"}
+3 -3
View File
@@ -114,11 +114,11 @@ function DiscordBanner() {
<a
href="https://trigger.dev/discord"
target="_blank"
className="group mb-4 flex w-full items-center justify-between rounded-md border border-slate-600 bg-gradient-to-br from-blue-400/30 to-indigo-400/50 p-4 transition hover:border-indigo-400"
className="group mb-4 flex w-full items-center justify-between rounded-md border border-charcoal-600 bg-gradient-to-br from-blue-400/30 to-indigo-400/50 p-4 transition hover:border-indigo-400"
>
<div className="flex flex-col gap-y-2">
<DiscordIcon className="h-8 w-8" />
<h2 className="font-title text-2xl text-bright transition group-hover:text-white">
<h2 className="font-title text-2xl text-text-bright transition group-hover:text-white">
Join the Trigger.dev
<br />
Discord community
@@ -127,7 +127,7 @@ function DiscordBanner() {
Get help or answer questions from the Trigger.dev community.
</Paragraph>
</div>
<ChevronRightIcon className="h-5 w-5 text-slate-400 transition group-hover:translate-x-1 group-hover:text-indigo-400" />
<ChevronRightIcon className="h-5 w-5 text-charcoal-400 transition group-hover:translate-x-1 group-hover:text-indigo-400" />
</a>
);
}
+10 -6
View File
@@ -25,11 +25,13 @@ function NextButton({ cursor }: { cursor?: string }) {
return (
<LinkButton
to={path ?? "#"}
variant={"tertiary/small"}
TrailingIcon="chevron-right"
variant={"minimal/small"}
TrailingIcon="arrow-right"
trailingIconClassName="text-text-dimmed"
className={cn(
"flex items-center",
!path && "cursor-default opacity-50 group-hover:bg-transparent group-hover:text-slate-800"
!path &&
"cursor-not-allowed opacity-50 group-hover:bg-transparent group-hover:text-text-dimmed"
)}
onClick={(e) => !path && e.preventDefault()}
>
@@ -44,11 +46,13 @@ function PreviousButton({ cursor }: { cursor?: string }) {
return (
<LinkButton
to={path ?? "#"}
variant={"tertiary/small"}
LeadingIcon="chevron-left"
variant={"minimal/small"}
LeadingIcon="arrow-left"
leadingIconClassName="text-text-dimmed"
className={cn(
"flex items-center",
!path && "cursor-default opacity-50 group-hover:bg-transparent group-hover:text-slate-800"
!path &&
"cursor-not-allowed opacity-50 group-hover:bg-transparent group-hover:text-text-dimmed"
)}
onClick={(e) => !path && e.preventDefault()}
>
@@ -43,7 +43,7 @@ const quotes: QuoteType[] = [
];
const layout = "group grid place-items-center text-center overflow-hidden";
const gridCell = "hover:bg-midnight-850 rounded-lg transition bg-midnight-850/40";
const gridCell = "hover:bg-background-bright rounded-lg transition bg-background-bright/40";
const opacity = "opacity-10 group-hover:opacity-100 transition group-hover:scale-105";
const logos = "h-[60%] w-[60%] transition grayscale group-hover:grayscale-0";
const features = "h-[60%] w-[60%] text-gray-500 grayscale transition group-hover:grayscale-0";
@@ -61,7 +61,7 @@ export function LoginPageLayout({ children }: { children: React.ReactNode }) {
return (
<main className="grid h-full grid-cols-12">
<div className="border-midnight-750 z-10 col-span-12 border-r bg-midnight-850 md:col-span-6">
<div className="border-midnight-750 z-10 col-span-12 border-r bg-background-bright md:col-span-6">
<div className="flex h-full flex-col items-center justify-between p-6">
<div className="flex w-full items-center justify-between">
<a href="https://trigger.dev">
@@ -106,12 +106,12 @@ export function LoginPageLayout({ children }: { children: React.ReactNode }) {
<LoginTooltip side="top" content={<QuoteTooltipContent />}>
<div className={cn(layout, gridCell, wider)}>
<div className="px-4">
<Header3 className="relative text-2xl font-normal leading-8 text-gray-600 transition before:relative before:right-1 before:top-0 before:text-4xl before:text-slate-600 before:opacity-20 before:content-['❝'] group-hover:text-slate-500 group-hover:before:opacity-30 lg-height:text-xl md-height:text-lg">
<Header3 className="relative text-2xl font-normal leading-8 text-gray-600 transition before:relative before:right-1 before:top-0 before:text-4xl before:text-charcoal-600 before:opacity-20 before:content-['❝'] group-hover:text-charcoal-500 group-hover:before:opacity-30 lg-height:text-xl md-height:text-lg">
{randomQuote?.quote}
</Header3>
<Paragraph
variant="small"
className="mt-4 text-gray-700 transition group-hover:text-slate-600"
className="mt-4 text-gray-700 transition group-hover:text-charcoal-600"
>
{randomQuote?.person}
</Paragraph>
@@ -7,9 +7,9 @@ export function NoMobileOverlay() {
<>
<div className="fixed inset-0 z-[9999] flex items-center justify-center sm:hidden">
<div className="relative h-full w-full bg-black opacity-90"></div>
<div className="absolute mx-8 flex flex-col items-center gap-6 rounded-lg bg-slate-850 px-8 py-10 text-center">
<div className="absolute mx-8 flex flex-col items-center gap-6 rounded-lg bg-charcoal-850 px-8 py-10 text-center">
<XMarkIcon className="absolute top-14 h-8 w-8 text-rose-600" />
<DevicePhoneMobileIcon className="h-16 w-16 text-slate-500" />
<DevicePhoneMobileIcon className="h-16 w-16 text-charcoal-500" />
<Paragraph>Trigger.dev is currently only available on desktop.</Paragraph>
<LinkButton to="https://trigger.dev" variant="primary/medium">
Back Home
@@ -14,7 +14,7 @@ export function ProductHuntBanner() {
<LinkButton
to="https://www.producthunt.com/posts/trigger-dev"
target="_blank"
className="!text-white underline underline-offset-2 transition hover:decoration-slate-100 hover:decoration-2"
className="!text-white underline underline-offset-2 transition hover:decoration-charcoal-100 hover:decoration-2"
variant="tertiary/small"
>
Vote for us today only!
@@ -25,6 +25,6 @@ export function UserAvatar({
/>
</div>
) : (
<UserCircleIcon className={cn("aspect-square text-slate-400", className)} />
<UserCircleIcon className={cn("aspect-square text-charcoal-400", className)} />
);
}
+3 -3
View File
@@ -14,13 +14,13 @@ export function VersionLabel({ environment, userName, version }: VersionLabelPro
return (
<div
className={cn(
"inline-flex items-center justify-stretch justify-items-stretch rounded-sm border border-midnight-700 text-xxs"
"border-midnight-700 inline-flex items-center justify-stretch justify-items-stretch rounded-sm border text-xxs"
)}
>
<div className="px-1 text-xs tabular-nums text-dimmed">v{version}</div>
<div className="px-1 text-xs tabular-nums text-text-dimmed">v{version}</div>
<div
className={cn(
"inline-flex items-center justify-center rounded-r-sm border-l border-midnight-700 px-1 text-xxs font-medium uppercase tracking-wider",
"border-midnight-700 inline-flex items-center justify-center rounded-r-sm border-l px-1 text-xxs font-medium uppercase tracking-wider",
environmentTextClassName(environment)
)}
>
@@ -18,14 +18,14 @@ export function FreePlanUsage({ to, percentage }: { to: string; percentage: numb
return (
<div
className={cn(
"rounded border border-slate-900 bg-[#101722] p-2.5",
"rounded border border-charcoal-700 bg-charcoal-750 p-2.5",
hasHitLimit && "border-rose-800/60"
)}
>
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-1">
<ArrowUpCircleIcon className="h-5 w-5 text-dimmed" />
<Paragraph className="text-2sm text-bright">Free Plan</Paragraph>
<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-indigo-500">
Learn more
@@ -84,18 +84,18 @@ function ConcurrentRunsSlider({
max={options.length - 1}
step={1}
>
<Slider.Track className="relative h-[8px] grow rounded-full bg-slate-850">
<Slider.Range className="absolute h-full rounded-full bg-indigo-500" />
<Slider.Track className="relative h-[8px] grow rounded-full bg-grid-bright">
<Slider.Range className="absolute h-full rounded-full bg-primary" />
</Slider.Track>
<Slider.Thumb
className="block h-5 w-5 rounded-full border-4 border-indigo-500 bg-slate-850 shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] transition hover:border-indigo-400 hover:bg-slate-800 focus:shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] focus:outline-none"
className="block h-5 w-5 rounded-full border-4 border-primary bg-charcoal-850 shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] transition hover:border-primary hover:bg-charcoal-800 focus:shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] focus:outline-none"
aria-label="Concurrent runs slider"
/>
</Slider.Root>
<div className="ml-1.5 flex w-[99.85%] items-center justify-between">
{options.map((tier, i) => {
return (
<Paragraph variant="extra-small" className="text-slate-600" key={i}>
<Paragraph variant="extra-small" key={i}>
{tier.upto}
</Paragraph>
);
@@ -103,13 +103,13 @@ function ConcurrentRunsSlider({
</div>
</div>
<div className="flex h-full items-start">
<span className="ml-6 text-dimmed">=</span>
<Header2 className="min-w-[8ch] text-right text-dimmed">
<span className="ml-6 text-text-dimmed">=</span>
<Header2 className="min-w-[8ch] text-right text-text-dimmed">
{formatCurrency(cost, true)}
</Header2>
</div>
</div>
<hr className="mt-6 border-border" />
<hr className="mt-6 border-grid-bright" />
</div>
);
}
@@ -157,11 +157,11 @@ function RunsSlider({
max={runIncrements}
step={1}
>
<Slider.Track className="relative h-[8px] grow rounded-full bg-slate-850">
<Slider.Range className="absolute h-full rounded-full bg-indigo-500" />
<Slider.Track className="relative h-[8px] grow rounded-full bg-grid-bright">
<Slider.Range className="absolute h-full rounded-full bg-primary" />
</Slider.Track>
<Slider.Thumb
className="block h-5 w-5 rounded-full border-4 border-indigo-500 bg-slate-850 shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] transition hover:border-indigo-400 hover:bg-slate-800 focus:shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] focus:outline-none"
className="block h-5 w-5 rounded-full border-4 border-primary bg-charcoal-850 shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] transition hover:border-primary hover:bg-charcoal-800 focus:shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] focus:outline-none"
aria-label="Concurrent runs slider"
/>
</Slider.Root>
@@ -185,13 +185,13 @@ function RunsSlider({
</div>
</div>
<div className="flex h-full items-start">
<span className="ml-6 text-dimmed">=</span>
<Header2 className="min-w-[8ch] text-right text-dimmed">
<span className="ml-6 text-text-dimmed">=</span>
<Header2 className="min-w-[8ch] text-right text-text-dimmed">
{formatCurrency(cost, true)}
</Header2>
</div>
</div>
<hr className="mt-6 border-border" />
<hr className="mt-6 border-grid-bright" />
</div>
);
}
@@ -250,9 +250,7 @@ function SliderMarker({
: "justify-middle right-0"
)}
>
<Paragraph variant="extra-small" className="text-slate-600">
{text}
</Paragraph>
<Paragraph variant="extra-small">{text}</Paragraph>
</div>
</div>
);
@@ -144,7 +144,7 @@ export function TierFree({
<div className="py-6">
{buttonPath ? (
<LinkButton
variant="secondary/large"
variant="tertiary/large"
fullWidth
className="text-md font-medium"
to={buttonPath}
@@ -153,7 +153,7 @@ export function TierFree({
</LinkButton>
) : (
<Button
variant="secondary/large"
variant="tertiary/large"
fullWidth
className="text-md font-medium"
disabled={isLoading || isCurrentPlan}
@@ -275,7 +275,7 @@ export function TierPro({
<Form action={`/resources/${organizationSlug}/subscribe`} method="post" {...form.props}>
<Header title={plan.title} isHighlighted cost={selectedTier?.tierCost} />
<div className="mb-2 mt-6 font-sans text-sm font-normal text-bright">
<div className="mb-2 mt-6 font-sans text-sm font-normal text-text-bright">
<DefinitionTip
title={pricingDefinitions.concurrentRuns.title}
content={pricingDefinitions.concurrentRuns.content}
@@ -460,7 +460,7 @@ function TierContainer({
<div
className={cn(
"flex w-full min-w-[16rem] flex-col rounded-md border p-6",
isHighlighted ? "border-indigo-500" : "border-border"
isHighlighted ? "border-primary" : "border-grid-bright"
)}
>
{children}
@@ -479,13 +479,15 @@ function Header({
}) {
return (
<div className="flex flex-col gap-2">
<h2 className={cn("text-xl font-medium", isHighlighted ? "text-indigo-500" : "text-dimmed")}>
<h2
className={cn("text-xl font-medium", isHighlighted ? "text-primary" : "text-text-dimmed")}
>
{title}
</h2>
{flatCost === 0 || flatCost ? (
<h3 className="text-4xl font-medium">
<h3 className="text-4xl font-medium text-text-bright">
${flatCost}
<span className="text-2sm font-normal tracking-wide text-dimmed">/month</span>
<span className="text-2sm font-normal tracking-wide text-text-dimmed">/month</span>
</h3>
) : (
<h2 className="text-4xl font-medium">Custom</h2>
@@ -498,7 +500,9 @@ function TierLimit({ children }: { children: React.ReactNode }) {
return (
<div>
<hr className="my-[1.9rem]" />
<div className="mb-[0.6rem] mt-6 font-sans text-sm font-normal text-bright">{children}</div>
<div className="mb-[0.6rem] mt-6 font-sans text-sm font-normal text-text-bright">
{children}
</div>
</div>
);
}
@@ -507,11 +511,16 @@ function FeatureItem({ checked, children }: { checked?: boolean; children: React
return (
<li className="flex items-center gap-2">
{checked ? (
<CheckIcon className="h-4 w-4 text-green-500" />
<CheckIcon className="h-4 w-4 text-primary" />
) : (
<XMarkIcon className="h-4 w-4 text-slate-500" />
<XMarkIcon className="h-4 w-4 text-charcoal-500" />
)}
<div className={cn("font-sans text-sm font-normal", checked ? "text-bright" : "text-dimmed")}>
<div
className={cn(
"font-sans text-sm font-normal",
checked ? "text-text-bright" : "text-text-dimmed"
)}
>
{children}
</div>
</li>
@@ -13,7 +13,7 @@ export function RunsVolumeDiscountTable({
brackets: RunPriceBracket[];
}) {
const runsVolumeDiscountRow =
"flex justify-between whitespace-nowrap border-b gap-16 border-border last:pb-0 last:border-none py-2";
"flex justify-between whitespace-nowrap border-b gap-16 border-grid-bright last:pb-0 last:border-none py-2";
const bracketData = bracketInfo(brackets);
@@ -1,43 +1,61 @@
import { ExclamationCircleIcon } from "@heroicons/react/20/solid";
import { ArrowUpCircleIcon } from "@heroicons/react/24/outline";
import tileBgPath from "~/assets/images/error-banner-tile@2x.png";
import { MatchedOrganization } from "~/hooks/useOrganizations";
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
import { formatNumberCompact } from "~/utils/numberFormatter";
import { plansPath } from "~/utils/pathBuilder";
import { LinkButton } from "../primitives/Buttons";
import { Icon } from "../primitives/Icon";
import { Paragraph } from "../primitives/Paragraph";
import { Callout } from "../primitives/Callout";
type UpgradePromptProps = {
organization: MatchedOrganization;
runsEnabled: boolean;
runCountCap: number;
planPath: string;
};
export function UpgradePrompt({ organization }: UpgradePromptProps) {
const currentPlan = useCurrentPlan();
if (!currentPlan || !currentPlan.usage.exceededRunCount || !currentPlan.usage.runCountCap) {
return null;
}
export function UpgradePrompt({ runsEnabled, runCountCap, planPath }: UpgradePromptProps) {
return (
<Callout variant="error" className="flex h-full items-center rounded-none px-1 py-0">
<div className="flex items-center justify-between gap-3">
<Paragraph variant="extra-small" className="text-white">
{organization.runsEnabled
? `You have exceeded the monthly ${formatNumberCompact(
currentPlan.usage.runCountCap
)} runs
<div
className="flex h-10 items-center justify-between border border-error bg-repeat py-0 pl-3 pr-2"
style={{ backgroundImage: `url(${tileBgPath})`, backgroundSize: "8px 8px" }}
>
<div className="flex items-center gap-2">
<Icon icon={ExclamationCircleIcon} className="h-5 w-5 text-error" />
<Paragraph variant="small" className="text-error">
{runsEnabled
? `You have exceeded the monthly ${formatNumberCompact(runCountCap)} runs
limit`
: `No runs are executing because you have exceeded the free limit`}
</Paragraph>
<LinkButton
variant={"primary/small"}
LeadingIcon={ArrowUpCircleIcon}
leadingIconClassName="px-0"
to={plansPath(organization)}
>
Upgrade
</LinkButton>
</div>
</Callout>
<LinkButton
variant={"primary/small"}
LeadingIcon={ArrowUpCircleIcon}
leadingIconClassName="px-0"
to={planPath}
>
Upgrade
</LinkButton>
</div>
);
}
export function useShowUpgradePrompt(organization?: MatchedOrganization) {
const currentPlan = useCurrentPlan();
const shouldShow =
organization !== undefined &&
currentPlan !== undefined &&
currentPlan.usage.exceededRunCount &&
currentPlan.usage.runCountCap !== undefined;
if (!shouldShow) {
return { shouldShow };
}
return {
shouldShow,
runCountCap: currentPlan.usage.runCountCap!,
runsEnabled: organization.runsEnabled,
};
}
@@ -38,7 +38,7 @@ export function UsageBar({
return (
<div className="h-fit w-full py-16">
<div className="relative h-3 w-full rounded-sm bg-slate-800">
<div className="relative h-3 w-full rounded-sm bg-charcoal-800">
{billingLimit && (
<motion.div
initial={{ width: 0 }}
@@ -145,16 +145,16 @@ function Legend({ text, value, position, percentage, tooltipContent }: LegendPro
return (
<div
className={cn(
"absolute left-full z-10 flex border-slate-400",
"absolute left-full z-10 flex border-charcoal-400",
positions[position],
flipLegendPosition === true ? "-translate-x-full border-r" : "border-l"
)}
>
<SimpleTooltip
button={
<Paragraph className="mr-px h-fit whitespace-nowrap bg-background px-1.5 text-xs text-dimmed">
<Paragraph className="mr-px h-fit whitespace-nowrap bg-background-dimmed px-1.5 text-xs text-text-dimmed">
{text}
<span className="ml-1 text-bright">{value}</span>
<span className="ml-1 text-text-bright">{value}</span>
</Paragraph>
}
variant="dark"
+12 -12
View File
@@ -46,7 +46,7 @@ const extraLinesWhenClipping = 0.35;
const defaultTheme: PrismTheme = {
plain: {
color: "#9CDCFE",
backgroundColor: "#0e1521",
backgroundColor: "#1A1B1F",
},
styles: [
{
@@ -196,7 +196,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
return (
<div
className={cn("relative overflow-hidden rounded-md border border-slate-800", className)}
className={cn("relative overflow-hidden rounded-md border border-charcoal-800", className)}
style={{
backgroundColor: theme.plain.backgroundColor,
}}
@@ -215,7 +215,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
className={cn(
"absolute right-3 z-50 transition-colors duration-100 hover:cursor-pointer",
showChrome ? "top-10" : "top-3",
copied ? "text-emerald-500" : "text-slate-500 hover:text-slate-300"
copied ? "text-emerald-500" : "text-charcoal-500 hover:text-charcoal-300"
)}
>
{copied ? (
@@ -242,7 +242,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
}) => (
<div
dir="ltr"
className="overflow-auto px-2 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
className="overflow-auto px-2 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
style={{
maxHeight,
}}
@@ -291,7 +291,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
{showLineNumbers && (
<div
className={
"mr-2 flex-none select-none text-right text-slate-500 transition-opacity duration-500"
"mr-2 flex-none select-none text-right text-charcoal-500 transition-opacity duration-500"
}
style={{
width: `calc(8 * ${maxLineWidth / 16}rem)`,
@@ -328,7 +328,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
) : (
<div
dir="ltr"
className="overflow-auto px-2 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
className="overflow-auto px-2 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
style={{
maxHeight,
}}
@@ -347,17 +347,17 @@ CodeBlock.displayName = "CodeBlock";
function Chrome({ title }: { title?: string }) {
return (
<div className="grid h-7 grid-cols-[100px_auto_100px] border-b border-slate-800 bg-slate-900">
<div className="grid h-7 grid-cols-[100px_auto_100px] border-b border-charcoal-800 bg-charcoal-900">
<div className="ml-2 flex items-center gap-2">
<div className="h-3 w-3 rounded-full bg-slate-700" />
<div className="h-3 w-3 rounded-full bg-slate-700" />
<div className="h-3 w-3 rounded-full bg-slate-700" />
<div className="h-3 w-3 rounded-full bg-charcoal-700" />
<div className="h-3 w-3 rounded-full bg-charcoal-700" />
<div className="h-3 w-3 rounded-full bg-charcoal-700" />
</div>
<div className="flex items-center justify-center">
<div
className={cn(
"rounded-sm px-3 py-0.5 text-xs text-slate-500",
title && "bg-midnight-900"
"rounded-sm px-3 py-0.5 text-xs text-charcoal-500",
title && "bg-background-dimmed"
)}
>
{title}
@@ -1,7 +1,7 @@
import { cn } from "~/utils/cn";
const inlineCode =
"px-1 py-0.5 rounded border border-slate-800 bg-midnight-850 text-sky-200 font-mono";
"px-1 py-0.5 rounded border border-charcoal-700 bg-charcoal-800 text-charcoal-200 font-mono";
const variants = {
"extra-extra-small": "text-xxs",
+16 -15
View File
@@ -1,6 +1,6 @@
import { json as jsonLang } from "@codemirror/lang-json";
import type { ViewUpdate } from "@codemirror/view";
import { CheckIcon, ClipboardIcon } from "@heroicons/react/20/solid";
import { CheckIcon, ClipboardIcon, TrashIcon } from "@heroicons/react/20/solid";
import type { ReactCodeMirrorProps, UseCodeMirror } from "@uiw/react-codemirror";
import { useCodeMirror } from "@uiw/react-codemirror";
import { useCallback, useEffect, useRef, useState } from "react";
@@ -108,20 +108,13 @@ export function JSONEditor(opts: JSONEditorProps) {
}, [view]);
return (
<div className={cn(opts.className, "relative")}>
<div
className="h-full w-full"
ref={editor}
onBlur={() => {
if (!onBlur) return;
onBlur(editor.current?.textContent ?? "");
}}
/>
<div className="absolute right-3 top-3 flex items-center gap-2">
<div className={cn(opts.className, "grid grid-rows-[2.5rem_1fr]")}>
<div className="mx-3 flex items-center justify-end gap-2 border-b border-grid-dimmed">
{showClearButton && (
<Button
type="button"
variant="secondary/small"
variant="minimal/small"
TrailingIcon={TrashIcon}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
@@ -134,9 +127,9 @@ export function JSONEditor(opts: JSONEditorProps) {
{showCopyButton && (
<Button
type="button"
variant="secondary/small"
LeadingIcon={copied ? CheckIcon : ClipboardIcon}
leadingIconClassName={copied ? "text-green-500 group-hover:text-green-500" : undefined}
variant="minimal/small"
TrailingIcon={copied ? CheckIcon : ClipboardIcon}
trailingIconClassName={copied ? "text-green-500 group-hover:text-green-500" : undefined}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
@@ -147,6 +140,14 @@ export function JSONEditor(opts: JSONEditorProps) {
</Button>
)}
</div>
<div
className="w-full overflow-auto"
ref={editor}
onBlur={() => {
if (!onBlur) return;
onBlur(editor.current?.textContent ?? "");
}}
/>
</div>
);
}
@@ -1,8 +1,7 @@
import { EditorView } from "@codemirror/view";
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
import type { Extension } from "@codemirror/state";
import { HighlightStyle } from "@codemirror/language";
import { tagHighlighter, tags } from "@lezer/highlight";
import { syntaxHighlighting } from "@codemirror/language";
import { EditorView } from "@codemirror/view";
import { tags } from "@lezer/highlight";
export function darkTheme(): Extension {
const chalky = "#e5c07b",
@@ -21,11 +20,11 @@ export function darkTheme(): Extension {
tooltipBackground = "#353a42",
selection = "rgb(71 85 105)",
cursor = "#528bff",
scrollbarTrack = "#0E1521",
scrollbarTrack = "rgba(0,0,0,0)",
scrollbarTrackActive = "#131B2B",
scrollbarThumb = "#293649",
scrollbarThumbActive = "#3C4B62",
scrollbarBg = "#0E1521";
scrollbarBg = "rgba(0,0,0,0)";
const jsonHeroEditorTheme = EditorView.theme(
{
@@ -36,7 +35,7 @@ export function darkTheme(): Extension {
".cm-content": {
caretColor: cursor,
fontFamily: "monospace",
fontFamily: "Geist Mono Variable",
fontSize: "14px",
},
@@ -63,7 +63,7 @@ export function endpointIndexStatusTitle(status: EndpointIndexStatus): string {
export function endpointIndexStatusClassNameColor(status: EndpointIndexStatus): string {
switch (status) {
case "PENDING":
return "text-dimmed";
return "text-text-dimmed";
case "STARTED":
return "text-blue-500";
case "SUCCESS":
@@ -2,21 +2,29 @@ import { RuntimeEnvironment } from "~/models/runtimeEnvironment.server";
import { cn } from "~/utils/cn";
type Environment = Pick<RuntimeEnvironment, "type">;
const variants = {
small: "h-4 text-xxs px-[0.1875rem] rounded-[2px]",
large: "h-6 text-xs px-1.5 rounded",
};
export function EnvironmentLabel({
environment,
size = "small",
userName,
className,
}: {
environment: Environment;
size?: keyof typeof variants;
userName?: string;
className?: string;
}) {
return (
<span
className={cn(
"inline-flex h-4 items-center justify-center rounded-[2px] px-1 text-xxs font-medium uppercase tracking-wider text-midnight-900 whitespace-nowrap",
environmentColorClassName(environment),
"text-midnight-900 inline-flex items-center justify-center whitespace-nowrap border font-medium uppercase tracking-wider",
environmentBorderClassName(environment),
environmentTextClassName(environment),
variants[size],
className
)}
>
@@ -54,13 +62,13 @@ export function environmentColorClassName(environment: Environment) {
export function environmentBorderClassName(environment: Environment) {
switch (environment.type) {
case "PRODUCTION":
return "border-green-500";
return "border-green-500/50";
case "STAGING":
return "border-amber-500";
return "border-amber-500/50";
case "DEVELOPMENT":
return "border-pink-500";
return "border-pink-500/50";
case "PREVIEW":
return "border-yellow-500";
return "border-yellow-500/50";
}
}
@@ -1,4 +1,4 @@
import { ArrowPathIcon } from "@heroicons/react/20/solid";
import { ArrowPathIcon, ArrowRightIcon } from "@heroicons/react/20/solid";
import { ExclamationTriangleIcon } from "@heroicons/react/24/solid";
import { useFetcher } from "@remix-run/react";
import { useState } from "react";
@@ -9,6 +9,10 @@ import { Header1 } from "../primitives/Headers";
import { Input } from "../primitives/Input";
import { Paragraph } from "../primitives/Paragraph";
import { Spinner } from "../primitives/Spinner";
import { Callout } from "../primitives/Callout";
import { Fieldset } from "../primitives/Fieldset";
import { InputGroup } from "../primitives/InputGroup";
import { FormButtons } from "../primitives/FormButtons";
type ModalProps = {
id: string;
@@ -27,8 +31,8 @@ export function RegenerateApiKeyModal({ id, title }: ModalProps) {
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button
variant="tertiary/small"
leadingIconClassName="text-dimmed"
variant="minimal/small"
leadingIconClassName="text-text-dimmed"
LeadingIcon={ArrowPathIcon}
>
Regenerate
@@ -58,47 +62,47 @@ const RegenerateApiKeyModalContent = ({ id, randomWord, title, closeModal }: Mod
}
return (
<div className="flex w-full flex-col items-center gap-y-5 pb-4">
<div className="mt-3 flex gap-x-3 rounded-md border border-ui-border py-4 pl-3 pr-5">
<ExclamationTriangleIcon className="relative top-1 h-6 w-6 min-w-[2rem] text-amber-500" />
<Paragraph>
Regenerating the keys for this environment will temporarily break any live Jobs in the
<span className="text-bright"> {title} Environment</span> until the new API keys are set
in the relevant environment variables.
</Paragraph>
</div>
<div className="flex flex-col items-center gap-y-5 py-4">
<Callout variant="warning">
{`Regenerating the keys for this environment will temporarily break any live Jobs in the
${title} Environmentuntil the new API keys are set in the relevant environment variables.`}
</Callout>
<fetcher.Form
method="post"
action={`/resources/environments/${id}/regenerate-api-key`}
className="mt-2 flex w-full flex-col items-center gap-2"
className="mt-2 w-full"
>
<div className="mb-4 flex items-center gap-x-2">
<Paragraph variant="small/bright">Enter this text below to confirm:</Paragraph>
<Paragraph
variant="small"
className="select-all rounded-md border border-ui-border bg-slate-900 px-2 py-1 font-mono text-bright"
>
{randomWord}
</Paragraph>
</div>
<div className="flex items-center">
<Input
type="text"
placeholder="Confirmation text"
fullWidth
value={confirmationText}
onChange={(e) => setConfirmationText(e.target.value)}
className="rounded-r-none"
variant="large"
<Fieldset className="w-full">
<InputGroup>
<Paragraph variant="small/bright">Enter this text below to confirm:</Paragraph>
<Paragraph
variant="small"
className="select-all rounded-md border border-grid-bright bg-charcoal-900 px-2 py-1 font-mono"
>
{randomWord}
</Paragraph>
<Input
type="text"
placeholder="Confirmation text"
fullWidth
value={confirmationText}
onChange={(e) => setConfirmationText(e.target.value)}
/>
</InputGroup>
<FormButtons
confirmButton={
<Button
type="submit"
variant={"primary/small"}
LeadingIcon={isSubmitting ? Spinner : undefined}
disabled={confirmationText !== randomWord}
>
Regenerate
</Button>
}
cancelButton={<Button variant={"tertiary/small"}>Cancel</Button>}
/>
<Button
variant="primary/large"
disabled={confirmationText !== randomWord}
className="rounded-l-none"
>
{isSubmitting ? <Spinner color="white" /> : <>Regenerate</>}
</Button>
</div>
</Fieldset>
</fetcher.Form>
</div>
);
@@ -96,7 +96,7 @@ export function EventsFilters() {
<TimeFrameFilter from={from} to={to} onRangeChanged={handleTimeFrameChange} />
<Button variant="tertiary/small" onClick={() => clearFilters()} LeadingIcon={"close"}>
<Button variant="minimal/small" onClick={() => clearFilters()} LeadingIcon={"close"}>
Clear
</Button>
</div>
@@ -101,9 +101,9 @@ export function EventsTable({
</TableCell>
<TableCell to={path}>
{event.isTest ? (
<CheckIcon className="h-4 w-4 text-slate-400" />
<CheckIcon className="h-4 w-4 text-charcoal-400" />
) : (
<StopIcon className="h-4 w-4 text-slate-850" />
<StopIcon className="h-4 w-4 text-charcoal-850" />
)}
</TableCell>
<TableCell to={path}>{event.runs}</TableCell>
@@ -115,9 +115,9 @@ export function EventsTable({
{isLoading && (
<TableBlankRow
colSpan={8}
className="absolute left-0 top-0 flex h-full w-full items-center justify-center gap-2 bg-slate-900/90"
className="absolute left-0 top-0 flex h-full w-full items-center justify-center gap-2 bg-charcoal-900/90"
>
<Spinner /> <span className="text-dimmed">Loading</span>
<Spinner /> <span className="text-text-dimmed">Loading</span>
</TableBlankRow>
)}
</TableBody>
@@ -36,18 +36,18 @@ export function FrameworkComingSoon({
Choose a different framework
</LinkButton>
</div>
<Paragraph spacing className="border-b border-slate-800 pb-4">
<Paragraph spacing className="border-b border-charcoal-800 pb-4">
We're working hard to bring support for {frameworkName} in Trigger.dev. Follow along with
the GitHub issue or contribute and help us bring it to Trigger.dev faster.
</Paragraph>
<a
href={githubIssueUrl}
target="_blank"
className="group mt-4 block max-w-sm rounded-md bg-bright px-10 py-8 transition hover:bg-slate-100"
className="bg-bright group mt-4 block max-w-sm rounded-md px-10 py-8 transition hover:bg-charcoal-100"
>
<Paragraph spacing>triggerdotdev/trigger.dev</Paragraph>
<h2 className="text-2xl font-semibold text-black">
<span className="mr-1 font-normal text-dimmed">#{githubIssueNumber}</span>Framework:
<span className="mr-1 font-normal text-text-dimmed">#{githubIssueNumber}</span>Framework:
support for {frameworkName}
</h2>
<div className="mt-4 flex items-center gap-1.5">
@@ -55,7 +55,7 @@ export function FrameworkComingSoon({
<Paragraph variant="small">View on GitHub</Paragraph>
<NamedIcon
name="arrow-right"
className="h-4 w-4 text-dimmed transition group-hover:translate-x-1"
className="h-4 w-4 text-text-dimmed transition group-hover:translate-x-1"
/>
</div>
</a>
@@ -89,7 +89,7 @@ function FrameworkLink({ children, to, supported = false }: FrameworkLinkProps)
<Link
to={to}
className={cn(
"flex h-28 w-full items-center justify-center rounded-md border border-slate-750 px-8 py-4 transition hover:bg-slate-850",
"flex h-28 w-full items-center justify-center rounded-md border border-charcoal-750 px-8 py-4 transition hover:bg-charcoal-850",
!supported && "border opacity-30 grayscale transition hover:opacity-100 hover:grayscale-0"
)}
>
@@ -21,6 +21,7 @@ import selectExample from "./select-example.png";
import { StepContentContainer } from "../StepContentContainer";
import { TriggerDevCommand } from "../SetupCommands";
import { IntegrationIcon } from "~/assets/icons/IntegrationIcon";
import { BookOpenIcon } from "@heroicons/react/20/solid";
export function HowToRunYourJob() {
const organization = useOrganization();
@@ -41,7 +42,7 @@ export function HowToRunYourJob() {
to={jobTestPath(organization, project, job)}
variant={"primary/small"}
LeadingIcon={"beaker"}
leadingIconClassName="text-bright"
leadingIconClassName="text-text-bright"
>
Test
</LinkButton>
@@ -55,9 +56,9 @@ export function HowToRunYourJob() {
<LinkButton
to="https://trigger.dev/docs/documentation/guides/running-jobs"
variant={"primary/small"}
LeadingIcon={"docs"}
leadingIconClassName="text-bright"
variant={"tertiary/small"}
LeadingIcon={BookOpenIcon}
leadingIconClassName="text-text-bright"
>
How to run a Job
</LinkButton>
@@ -50,16 +50,16 @@ export function ConnectToIntegrationSheet({
</div>
<LinkButton
to={apiReferencePath(integration.identifier)}
variant="secondary/small"
variant="tertiary/small"
TrailingIcon="arrow-up-right"
trailingIconClassName="h-4 w-4 text-slate-400"
trailingIconClassName="h-4 w-4 text-charcoal-400"
target="_blank"
>
View examples
</LinkButton>
<LinkButton
to={docsIntegrationPath(integration.identifier)}
variant="secondary/small"
variant="tertiary/small"
LeadingIcon="docs"
target="_blank"
>
@@ -179,7 +179,7 @@ export function ConnectToOAuthForm({
</fieldset> */}
<div className="mb-2 mt-4 flex items-center justify-between">
<Header3>Select {integration.name} scopes</Header3>
<Paragraph variant="small" className="text-slate-500">
<Paragraph variant="small" className="text-charcoal-500">
{simplur`${selectedScopes.size} scope[|s] selected`}
</Paragraph>
</div>
@@ -232,7 +232,7 @@ export function ConnectToOAuthForm({
)}
</Fieldset>
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-slate-800 bg-midnight-900 p-4">
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-charcoal-800 bg-background-dimmed p-4">
<FormError>{scopes.error}</FormError>
<Button
type="submit"
@@ -241,7 +241,7 @@ export function ConnectToOAuthForm({
variant="primary/medium"
LeadingIcon={integration.icon ?? integration.identifier}
>
Connect to {integration.name}
{`Connect to ${integration.name}`}
</Button>
</div>
</fetcher.Form>
@@ -41,14 +41,14 @@ export function CustomHelp({ api }: { api: Api }) {
modified to suit your use-case.
</Paragraph>
{api.examples.length > 1 && (
<div className=" flex w-full flex-row gap-4 overflow-x-scroll scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700">
<div className=" flex w-full flex-row gap-4 overflow-x-scroll scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
{api.examples?.map((example, index) => (
<button
onClick={() => changeCodeExample(index)}
key={example.codeUrl}
className={cn(
"w-64 min-w-[16rem] p-2 transition-colors duration-300 sm:w-full sm:rounded",
"border-px focus:border-px cursor-pointer border border-slate-900 bg-slate-900 text-slate-300 transition duration-300 hover:bg-slate-800 focus:border focus:border-indigo-600"
"border-px focus:border-px cursor-pointer border border-charcoal-900 bg-charcoal-900 text-charcoal-300 transition duration-300 hover:bg-charcoal-800 focus:border focus:border-indigo-600"
)}
>
{example.title}
@@ -37,7 +37,7 @@ export function IntegrationWithMissingFieldSheet({
</div>
<LinkButton
to={docsIntegrationPath(integration.identifier)}
variant="secondary/small"
variant="tertiary/small"
LeadingIcon="docs"
target="_blank"
>
@@ -8,6 +8,7 @@ import { Paragraph } from "../primitives/Paragraph";
import { Client } from "~/presenters/IntegrationsPresenter.server";
import { UpdateOAuthForm } from "./UpdateOAuthForm";
import { LinkButton } from "../primitives/Buttons";
import { BookOpenIcon } from "@heroicons/react/20/solid";
export function SelectOAuthMethod({
integration,
@@ -114,8 +115,8 @@ export function SelectOAuthMethod({
We support external authentication providers through Auth Resolvers. Read the docs to
learn more:{" "}
<LinkButton
variant="secondary/small"
LeadingIcon={"docs"}
variant={"tertiary/small"}
LeadingIcon={BookOpenIcon}
TrailingIcon={"external-link"}
to="https://trigger.dev/docs/documentation/guides/using-integrations-byo-auth"
target="_blank"
@@ -168,7 +168,7 @@ export function UpdateOAuthForm({
</fieldset> */}
<div className="mb-2 mt-4 flex items-center justify-between">
<Header3>Select {integration.name} scopes</Header3>
<Paragraph variant="small" className="text-slate-500">
<Paragraph variant="small" className="text-charcoal-500">
{simplur`${selectedScopes.size} scope[|s] selected`}
</Paragraph>
</div>
@@ -221,7 +221,7 @@ export function UpdateOAuthForm({
)}
</Fieldset>
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-slate-800 bg-midnight-900 p-4">
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-charcoal-800 bg-background-dimmed p-4">
<FormError>{scopes.error}</FormError>
<Button
type="submit"
@@ -230,7 +230,7 @@ export function UpdateOAuthForm({
variant="primary/medium"
LeadingIcon={integration.identifier}
>
Connect to {integration.name}
{`Connect to ${integration.name}`}
</Button>
</div>
</fetcher.Form>
@@ -74,7 +74,7 @@ export function DeleteJobDialogContent({
<Header2
className={cn(
canDelete ? "border-rose-500 bg-rose-500/10" : "border-amber-500 bg-amber-500/10",
"rounded border px-3.5 py-2 text-center text-bright"
"rounded border px-3.5 py-2 text-center text-text-bright"
)}
>
{canDelete
@@ -84,8 +84,9 @@ export function DeleteJobDialogContent({
<Paragraph variant="small" className="px-6 text-center">
{canDelete ? (
<>
This will permanently delete the Job <span className="strong text-bright">{title}</span>
. This includes the deletion of all Run history. This cannot be undone.
This will permanently delete the Job{" "}
<span className="strong text-text-bright">{title}</span>. This includes the deletion of
all Run history. This cannot be undone.
</>
) : (
<>
@@ -111,7 +112,7 @@ export function DeleteJobDialogContent({
<>
<NamedIcon
name="trash-can"
className="mr-1.5 h-4 w-4 text-bright transition group-hover:text-bright"
className="mr-1.5 h-4 w-4 text-text-bright transition group-hover:text-text-bright"
/>
Delete this Job
</>
@@ -123,7 +124,7 @@ export function DeleteJobDialogContent({
<>
<NamedIcon
name="trash-can"
className="mr-1.5 h-4 w-4 text-bright transition group-hover:text-bright"
className="mr-1.5 h-4 w-4 text-text-bright transition group-hover:text-text-bright"
/>
Delete this Job
</>
@@ -1,17 +1,17 @@
export function JobSkeleton() {
return (
<div className={"flex w-full gap-x-4 bg-midnight-850 p-4 pr-5 "}>
<div className="aspect-square h-6 w-6 rounded border border-slate-800 bg-slate-900 p-1.5 md:h-10 md:w-10 lg:h-12 lg:w-12" />
<div className={"flex w-full gap-x-4 bg-background-bright p-4 pr-5 "}>
<div className="aspect-square h-6 w-6 rounded border border-charcoal-800 bg-charcoal-900 p-1.5 md:h-10 md:w-10 lg:h-12 lg:w-12" />
<div className="flex w-full">
<div className="flex w-full flex-col justify-between gap-y-1">
<div className="flex items-baseline justify-between gap-x-3 pr-3 md:justify-start md:pr-0">
<div className="h-5 w-1/4 rounded border border-slate-800 bg-slate-900" />
<div className="h-5 w-1/4 rounded border border-charcoal-800 bg-charcoal-900" />
</div>
<div className="flex flex-wrap items-center gap-x-4 gap-y-1">
<div className="h-2.5 w-1/2 rounded border border-slate-800 bg-slate-900" />
<div className="h-2.5 w-1/2 rounded border border-charcoal-800 bg-charcoal-900" />
</div>
<div className="flex flex-wrap gap-x-4 gap-y-1">
<div className="h-1 w-1/5 rounded border border-slate-800 bg-slate-900" />
<div className="h-1 w-1/5 rounded border border-charcoal-800 bg-charcoal-900" />
</div>
</div>
</div>
+22 -15
View File
@@ -1,20 +1,28 @@
import { useOptionalOrganization } from "~/hooks/useOrganizations";
import { cn } from "~/utils/cn";
import { useShowUpgradePrompt } from "../billing/UpgradePrompt";
/** This container is used to surround the entire app, it correctly places the nav bar */
export function AppContainer({ children }: { children: React.ReactNode }) {
return <div className={cn("grid h-full w-full grid-rows-1 overflow-hidden")}>{children}</div>;
}
export function MainBody({ children }: { children: React.ReactNode }) {
return <div className={cn("grid grid-rows-1 overflow-hidden")}>{children}</div>;
}
/** This container should be placed around the content on a page */
export function PageContainer({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) {
export function PageContainer({ children }: { children: React.ReactNode }) {
const organization = useOptionalOrganization();
const showUpgradePrompt = useShowUpgradePrompt(organization);
return (
<div className={cn("grid h-full grid-rows-[auto_1fr] overflow-hidden", className)}>
<div
className={cn(
"grid overflow-hidden",
showUpgradePrompt.shouldShow ? "grid-rows-[5rem_1fr]" : "grid-rows-[2.5rem_1fr]"
)}
>
{children}
</div>
);
@@ -23,16 +31,19 @@ export function PageContainer({
export function PageBody({
children,
scrollable = true,
className,
}: {
children: React.ReactNode;
scrollable?: boolean;
className?: string;
}) {
return (
<div
className={cn(
scrollable
? "overflow-y-auto p-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
: "overflow-hidden"
? "overflow-y-auto p-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
: "overflow-hidden",
className
)}
>
{children}
@@ -40,10 +51,6 @@ export function PageBody({
);
}
export function PageBodyPadding({ children }: { children: React.ReactNode }) {
return <div className="p-4">{children}</div>;
}
export function MainCenteredContainer({
children,
className,
@@ -52,7 +59,7 @@ export function MainCenteredContainer({
className?: string;
}) {
return (
<div className="h-full w-full overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700">
<div className="h-full w-full overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<div className={cn("mx-auto mt-[25vh] max-w-xs overflow-y-auto", className)}>{children}</div>
</div>
);
@@ -16,15 +16,13 @@ export function AccountSideMenu({ user }: { user: User }) {
return (
<div
className={cn(
"flex h-full flex-col gap-y-8 overflow-hidden border-r border-ui-border transition"
"flex h-full flex-col gap-y-8 overflow-hidden border-r border-grid-bright bg-background-bright transition"
)}
>
<div className="flex h-full flex-col">
<div
className={cn("flex items-center justify-between border-b bg-background p-px transition")}
>
<div className={cn("flex items-center justify-between border-b p-px transition")}>
<LinkButton
variant="tertiary/medium"
variant="minimal/medium"
LeadingIcon={ArrowLeftIcon}
to={rootPath()}
fullWidth
@@ -33,7 +31,7 @@ export function AccountSideMenu({ user }: { user: User }) {
Account
</LinkButton>
</div>
<div className="h-full overflow-hidden overflow-y-auto pt-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700">
<div className="h-full overflow-hidden overflow-y-auto pt-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<div className="mb-6 flex flex-col gap-1 px-1">
<SideMenuHeader title={user.name ?? user.displayName ?? user.email} />
@@ -58,7 +56,7 @@ export function AccountSideMenu({ user }: { user: User }) {
</div>
)}
</div>
<div className="flex flex-col gap-1 border-t border-border p-1">
<div className="flex flex-col gap-1 border-t border-grid-bright p-1">
<SideMenuItem
name="Join our Discord"
icon={DiscordIcon}
@@ -1,35 +0,0 @@
import { UIMatch, useMatches } from "@remix-run/react";
import { Fragment, ReactNode } from "react";
import { BreadcrumbIcon } from "../primitives/BreadcrumbIcon";
import { Handle } from "~/utils/handle";
import { LinkButton } from "../primitives/Buttons";
export type BreadcrumbItem = (match: UIMatch, allMatches: UIMatch[]) => ReactNode;
export function Breadcrumb() {
const matches = useMatches() as UIMatch<unknown, Handle>[];
return (
<div className="flex items-center px-1">
{matches
.filter((b) => b.handle && b.handle.breadcrumb)
.map((match, index) => {
const breadcrumb = match.handle.breadcrumb as BreadcrumbItem;
return (
<Fragment key={match.id}>
{index !== 0 && <BreadcrumbIcon />} {breadcrumb(match, matches)}
</Fragment>
);
})}
</div>
);
}
export function BreadcrumbLink({ title, to }: { title: string; to: string }) {
return (
<LinkButton to={to} variant="tertiary/small">
{title}
</LinkButton>
);
}
@@ -1,10 +0,0 @@
import { useNavigation } from "@remix-run/react";
import { Spinner } from "../primitives/Spinner";
import { cn } from "~/utils/cn";
export function PageNavigationIndicator({ className }: { className?: string }) {
const navigation = useNavigation();
if (navigation.state === "loading") {
return <Spinner color="blue" className={cn("h-4 w-4", className)} />;
}
}
@@ -110,21 +110,21 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
return (
<div
className={cn(
"flex h-full flex-col gap-y-8 overflow-hidden border-r border-ui-border transition"
"flex h-full flex-col gap-y-8 overflow-hidden border-r border-grid-bright bg-background-bright transition"
)}
>
<div className="flex h-full flex-col">
<div
className={cn(
"flex items-center justify-between border-b bg-background px-1 py-1 transition",
showHeaderDivider ? " border-border" : "border-transparent"
"flex items-center justify-between px-1 py-1 transition",
showHeaderDivider ? " border-grid-bright" : "border-transparent"
)}
>
<ProjectSelector organizations={organizations} project={project} />
<UserMenu user={user} />
</div>
<div
className="h-full overflow-hidden overflow-y-auto pt-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
className="h-full overflow-hidden overflow-y-auto pt-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
ref={borderRef}
>
<div className="mb-6 flex flex-col gap-1 px-1">
@@ -182,7 +182,7 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
/>
</div>
</div>
<div className="flex flex-col gap-1 border-t border-border p-1">
<div className="flex flex-col gap-1 border-t border-grid-bright p-1">
{currentPlan?.subscription?.isPaying === true && (
<Dialog>
<DialogTrigger asChild>
@@ -206,13 +206,13 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
support with the Trigger.dev team.
</Paragraph>
</div>
<hr className="border-slate-800" />
<hr className="border-charcoal-800" />
<div>
<StepNumber stepNumber="1" title="Create a new Slack channel" />
<StepContentContainer>
<Paragraph>
In your Slack app, create a new channel from the main menu by going to File{" "}
<ArrowRightIcon className="inline h-4 w-4 text-dimmed" /> New Channel
<ArrowRightIcon className="inline h-4 w-4 text-text-dimmed" /> New Channel
</Paragraph>
</StepContentContainer>
<StepNumber stepNumber="2" title="Setup your channel" />
@@ -312,7 +312,7 @@ function ProjectSelector({
<span className="truncate">{project.name ?? "Select a project"}</span>
</PopoverArrowTrigger>
<PopoverContent
className="min-w-[16rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
className="min-w-[16rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
align="start"
style={{ maxHeight: `calc(var(--radix-popover-content-available-height) - 10vh)` }}
>
@@ -328,7 +328,7 @@ function ProjectSelector({
key={p.id}
to={projectPath(organization, p)}
title={
<div className="flex w-full items-center justify-between text-bright">
<div className="flex w-full items-center justify-between text-text-bright">
<span className="grow truncate text-left">{p.name}</span>
<MenuCount count={p.jobCount} />
</div>
@@ -348,7 +348,7 @@ function ProjectSelector({
</div>
</Fragment>
))}
<div className="border-t border-slate-800 p-1">
<div className="border-t border-charcoal-800 p-1">
<PopoverMenuItem to={newOrganizationPath()} title="New Organization" icon="plus" />
</div>
</PopoverContent>
@@ -367,16 +367,16 @@ function UserMenu({ user }: { user: SideMenuUser }) {
return (
<Popover onOpenChange={(open) => setProfileMenuOpen(open)}>
<PopoverCustomTrigger isOpen={isProfileMenuOpen} className="p-1">
<PopoverCustomTrigger isOpen={isProfileMenuOpen} className="p-1 hover:bg-transparent">
<UserProfilePhoto
className={cn(
"h-5 w-5 text-slate-600",
"h-5 w-5 rounded-full border border-transparent text-charcoal-600 transition hover:border-charcoal-600",
user.isImpersonating && "rounded-full border border-yellow-500"
)}
/>
</PopoverCustomTrigger>
<PopoverContent
className="min-w-[12rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
className="min-w-[12rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
align="start"
>
<Fragment>
@@ -16,17 +16,17 @@ export function SideMenuHeader({ title, children }: { title: string; children?:
<div className="group flex items-center justify-between pl-1.5">
<Paragraph
variant="extra-extra-small/caps"
className="cursor-default truncate text-slate-500"
className="cursor-default truncate text-charcoal-500"
>
{title}
</Paragraph>
{children !== undefined ? (
<Popover onOpenChange={(open) => setHeaderMenuOpen(open)} open={isHeaderMenuOpen}>
<PopoverCustomTrigger className="p-1">
<EllipsisHorizontalIcon className="h-4 w-4 text-slate-500 transition group-hover:text-bright" />
<EllipsisHorizontalIcon className="h-4 w-4 text-charcoal-500 transition group-hover:text-text-bright" />
</PopoverCustomTrigger>
<PopoverContent
className="min-w-max overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
className="min-w-max overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
align="start"
>
<div className="flex flex-col gap-1 p-1">{children}</div>
@@ -35,13 +35,13 @@ export function SideMenuItem({
fullWidth
textAlignLeft
LeadingIcon={icon}
leadingIconClassName={isActive ? iconColor : "text-dimmed"}
leadingIconClassName={isActive ? iconColor : "text-text-dimmed"}
to={to}
target={target}
className={cn(
"text-bright group-hover:bg-slate-850",
subItem ? "text-dimmed" : "",
isActive ? "bg-slate-850 text-bright" : "group-hover:text-bright"
"text-text-bright group-hover:bg-charcoal-750",
subItem ? "text-text-dimmed" : "",
isActive ? "bg-tertiary text-text-bright" : "group-hover:text-text-bright"
)}
>
<div className="flex w-full items-center justify-between">
@@ -69,5 +69,7 @@ export function SideMenuItem({
}
export function MenuCount({ count }: { count: number | string }) {
return <div className="rounded-full bg-slate-900 px-2 py-1 text-xxs text-dimmed">{count}</div>;
return (
<div className="rounded-full bg-charcoal-900 px-2 py-1 text-xxs text-text-dimmed">{count}</div>
);
}
@@ -27,7 +27,7 @@ const AlertOverlay = React.forwardRef<
>(({ className, children, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-opacity animate-in fade-in",
"fixed inset-0 z-50 bg-background-dimmed/80 backdrop-blur-sm transition-opacity animate-in fade-in",
className
)}
{...props}
@@ -45,7 +45,7 @@ const AlertContent = React.forwardRef<
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
"fixed z-50 grid w-full max-w-lg scale-100 gap-4 border bg-background p-6 opacity-100 shadow-lg animate-in fade-in-90 slide-in-from-bottom-10 sm:rounded-lg sm:zoom-in-90 sm:slide-in-from-bottom-0 md:w-full",
"fixed z-50 grid w-full max-w-lg scale-100 gap-4 border bg-background-dimmed p-6 opacity-100 shadow-lg animate-in fade-in-90 slide-in-from-bottom-10 sm:rounded-lg sm:zoom-in-90 sm:slide-in-from-bottom-0 md:w-full",
className
)}
{...props}
@@ -85,7 +85,7 @@ const AlertDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
));
@@ -3,9 +3,14 @@ import { cn } from "~/utils/cn";
const variants = {
default:
"grid place-items-center rounded-full px-2 h-5 tracking-wider text-xxs bg-slate-700 text-bright uppercase whitespace-nowrap",
"grid place-items-center rounded-full px-2 h-5 tracking-wider text-xxs bg-charcoal-750 text-text-bright uppercase whitespace-nowrap",
small:
"grid place-items-center rounded-full px-[0.4rem] h-4 tracking-wider text-xxs bg-background-dimmed text-text-dimmed uppercase whitespace-nowrap",
outline:
"grid place-items-center rounded-sm px-1 h-5 tracking-wider text-xxs border border-dimmed text-dimmed uppercase whitespace-nowrap",
"grid place-items-center rounded-sm px-1.5 h-5 tracking-wider text-xxs border border-dimmed text-text-dimmed uppercase whitespace-nowrap",
v3: "grid place-items-center rounded-full px-[0.4rem] h-5 tracking-wider text-xxs bg-charcoal-750 text-primary whitespace-nowrap",
"outline-rounded":
"grid place-items-center rounded-full px-1.5 h-5 tracking-wider text-xxs border border-blue-500 text-blue-500 uppercase whitespace-nowrap",
};
type BadgeProps = React.HTMLAttributes<HTMLDivElement> & {
@@ -15,7 +20,7 @@ type BadgeProps = React.HTMLAttributes<HTMLDivElement> & {
export function Badge({ className, variant = "default", children, ...props }: BadgeProps) {
return (
<div className={cn(variants[variant], className)} {...props}>
<span className="-mb-0.5">{children}</span>
<span>{children}</span>
</div>
);
}
@@ -3,7 +3,7 @@ import { cn } from "~/utils/cn";
export function BreadcrumbIcon({ className }: { className?: string }) {
return (
<svg
className={cn("text-slate-650", className)}
className={cn("text-charcoal-600", className)}
width="9"
height="20"
viewBox="0 0 9 26"
+113 -134
View File
@@ -1,160 +1,139 @@
import { Link, LinkProps, NavLink, NavLinkProps } from "@remix-run/react";
import React, { ReactComponentElement, forwardRef, useImperativeHandle, useRef } from "react";
import React, { forwardRef, useImperativeHandle, useRef } from "react";
import { ShortcutDefinition, useShortcutKeys } from "~/hooks/useShortcutKeys";
import { cn } from "~/utils/cn";
import { IconNamesOrString, NamedIcon } from "./NamedIcon";
import { ShortcutKey } from "./ShortcutKey";
const sizes = {
small: {
button: "h-6 px-2.5 text-xs",
icon: "h-3.5 -mx-1",
iconSpacing: "gap-x-2.5",
shortcutVariant: "small" as const,
shortcut: "ml-2 -mr-1.5 justify-self-center",
},
medium: {
button: "h-8 px-2.5 text-sm",
icon: "h-4 -mx-1",
iconSpacing: "gap-x-2.5",
shortcutVariant: "medium" as const,
shortcut: "ml-2 -mr-1.5 rounded justify-self-center",
},
large: {
button: "h-10 px-2 text-sm font-medium",
icon: "h-5",
iconSpacing: "gap-x-0.5",
shortcutVariant: "medium" as const,
shortcut: "ml-1.5 -mr-0.5",
},
"extra-large": {
button: "h-12 px-2 text-base font-medium",
icon: "h-5",
iconSpacing: "gap-x-0.5",
shortcutVariant: "medium" as const,
shortcut: "ml-1.5 -mr-0.5",
},
};
type Size = keyof typeof sizes;
const theme = {
primary: {
textColor:
"text-charcoal-900 group-hover:text-charcoal-900 transition group-disabled:text-charcoal-900",
button:
"bg-primary group-hover:bg-apple-200 group-disabled:opacity-50 group-disabled:bg-primary group-disabled:pointer-events-none",
shortcut:
"border-black/40 text-charcoal-900 group-hover:border-black/60 group-hover:text-charcoal-900",
},
secondary: {
textColor: "text-primary group-hover:text-apple-200 transition group-disabled:text-primary",
button:
"bg-transparent border border-primary group-hover:border-apple-200 group-hover:bg-apple-950 group-disabled:opacity-30 group-disabled:border-primary group-disabled:bg-transparent group-disabled:pointer-events-none",
shortcut:
"border-primary/30 text-apple-200 group-hover:text-text-bright/80 group-hover:border-dimmed/60",
},
tertiary: {
textColor: "text-text-bright transition group-disabled:text-text-dimmed/80",
button:
"bg-tertiary group-hover:bg-charcoal-600 group-disabled:bg-tertiary group-disabled:opacity-60 group-disabled:pointer-events-none",
shortcut:
"border-text-dimmed/40 text-text-dimmed group-hover:text-text-bright group-hover:border-text-dimmed",
},
minimal: {
textColor:
"text-text-dimmed group-hover:text-text-bright transition group-disabled:text-text-dimmed/80",
button:
"bg-transparent group-hover:bg-tertiary disabled:opacity-50 group-disabled:bg-transparent group-disabled:pointer-events-none",
shortcut:
"border-dimmed/40 text-text-dimmed group-hover:text-text-bright/80 group-hover:border-dimmed/60",
},
danger: {
textColor:
"text-text-bright group-hover:text-white transition group-disabled:text-text-bright/80",
button:
"bg-error group-hover:bg-rose-500 disabled:opacity-50 group-disabled:bg-error group-disabled:pointer-events-none",
shortcut: "border-text-bright text-text-bright group-hover:border-bright/60",
},
};
type Theme = keyof typeof theme;
function createVariant(sizeName: Size, themeName: Theme) {
return {
textColor: theme[themeName].textColor,
button: cn(sizes[sizeName].button, theme[themeName].button),
icon: sizes[sizeName].icon,
iconSpacing: sizes[sizeName].iconSpacing,
shortcutVariant: sizes[sizeName].shortcutVariant,
shortcut: cn(sizes[sizeName].shortcut, theme[themeName].shortcut),
};
}
const variant = {
"primary/small": {
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/80 px-1",
button:
"h-6 px-[5px] text-xs bg-indigo-600 group-hover:bg-indigo-500/90 group-disabled:opacity-50 group-disabled:pointer-events-none",
icon: "h-3.5",
iconSpacing: "gap-x-0.5",
shortcutVariant: "small" as const,
shortcut:
"ml-1 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60 justify-self-center",
},
"secondary/small": {
textColor: "text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80 px-1",
button:
"h-6 px-[5px] text-xs bg-slate-800 group-hover:bg-slate-700/70 disabled:opacity-50 group-disabled:pointer-events-none",
icon: "h-3.5",
iconSpacing: "gap-x-0.5",
shortcutVariant: "small" as const,
shortcut:
"ml-1 -mr-0.5 border-dimmed/40 text-dimmed group-hover:text-bright/80 group-hover:border-dimmed/60",
},
"tertiary/small": {
textColor: "text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80 px-1",
button:
"h-6 px-[5px] text-xs bg-transparent group-hover:bg-slate-850 disabled:opacity-50 group-disabled:pointer-events-none",
icon: "h-3.5",
iconSpacing: "gap-x-0.5",
shortcutVariant: "small" as const,
shortcut:
"ml-1 -mr-0.5 border-dimmed/40 text-dimmed group-hover:text-bright/80 group-hover:border-dimmed/60",
},
"danger/small": {
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/80 px-1",
button:
"h-6 px-[5px] text-xs bg-rose-600 group-hover:bg-rose-500 disabled:opacity-50 group-disabled:pointer-events-none",
icon: "h-3.5",
iconSpacing: "gap-x-0.5",
shortcutVariant: "small" as const,
shortcut: "ml-1 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
},
"primary/medium": {
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/80 px-1",
button: "h-8 px-2 text-sm bg-indigo-600 group-hover:bg-indigo-500/90 disabled:opacity-50",
icon: "h-4",
iconSpacing: "gap-x-0.5",
shortcutVariant: "medium" as const,
shortcut: "ml-1.5 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
},
"secondary/medium": {
textColor: "text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80 px-1",
button: "h-8 px-2 text-sm bg-slate-800 group-hover:bg-slate-700/70 disabled:opacity-50",
icon: "h-4",
iconSpacing: "gap-x-0.5",
shortcutVariant: "medium" as const,
shortcut:
"ml-1.5 -mr-0.5 border-dimmed/40 text-dimmed group-hover:border-dimmed group-hover:text-bright",
},
"tertiary/medium": {
textColor: "text-dimmed group-hover:text-bright transition group-disabled:text-dimmed/80 px-1",
button: "h-8 px-2 text-sm bg-transparent group-hover:bg-slate-850 disabled:opacity-50",
icon: "h-4",
iconSpacing: "gap-x-0.5",
shortcutVariant: "medium" as const,
shortcut:
"ml-1.5 -mr-0.5 border-bright/40 text-dimmed group-hover:border-bright/60 group-hover:text-bright",
},
"danger/medium": {
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/80 px-1",
button: "h-8 px-2 text-sm bg-rose-600 group-hover:bg-rose-500 disabled:opacity-50",
icon: "h-4",
iconSpacing: "gap-x-0.5",
shortcutVariant: "medium" as const,
shortcut: "ml-1.5 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
},
"primary/large": {
textColor: "text-bright group-hover:text-white transition group-disabled:text-dimmed/80 px-1",
button:
"h-10 px-2 text-sm font-medium bg-indigo-600 group-hover:bg-indigo-500/90 group-disabled:opacity-50 group-disabled:group-hover:bg-indigo-600",
icon: "h-5",
iconSpacing: "gap-x-0.5",
shortcutVariant: undefined,
shortcut: undefined,
},
"secondary/large": {
textColor: "text-bright group-disabled:text-dimmed px-1",
button:
"h-10 px-2 text-sm transition font-medium bg-slate-800 group-hover:bg-slate-700/70 group-disabled:opacity-40 group-disabled:group-hover:bg-slate-800",
icon: "h-5",
iconSpacing: "gap-x-0.5",
shortcutVariant: undefined,
shortcut: undefined,
},
"danger/large": {
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/50 px-1",
button:
"h-10 px-2 text-md bg-rose-600 group-hover:bg-rose-500 group-disabled:opacity-50 group-disabled:group-hover:bg-rose-600",
icon: "h-5",
iconSpacing: "gap-x-0.5",
shortcutVariant: "medium" as const,
shortcut: "ml-1.5 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
},
"primary/extra-large": {
textColor: "text-bright group-hover:text-white transition group-disabled:text-dimmed/80",
button:
"h-12 px-2 text-md font-medium bg-indigo-600 group-hover:bg-indigo-500/90 disabled:opacity-50",
icon: "h-5",
iconSpacing: undefined,
shortcutVariant: undefined,
shortcut: undefined,
},
"secondary/extra-large": {
textColor: "text-dimmed",
button:
"h-12 px-2 text-md text-dimmed group-hover:text-bright transition font-medium bg-slate-800 group-hover:bg-slate-700/70 disabled:opacity-50",
icon: "h-5",
iconSpacing: undefined,
shortcutVariant: undefined,
shortcut: undefined,
},
"danger/extra-large": {
textColor: "text-bright group-hover:text-white transition group-disabled:text-bright/50",
button:
"h-12 px-2 text-md bg-rose-600 group-hover:bg-rose-500 group-disabled:opacity-50 group-disabled:group-hover:bg-rose-600",
icon: "h-5",
iconSpacing: undefined,
shortcutVariant: "medium" as const,
shortcut: "ml-1.5 -mr-0.5 border-bright/40 text-bright group-hover:border-bright/60",
},
"primary/small": createVariant("small", "primary"),
"primary/medium": createVariant("medium", "primary"),
"primary/large": createVariant("large", "primary"),
"primary/extra-large": createVariant("extra-large", "primary"),
"secondary/small": createVariant("small", "secondary"),
"secondary/medium": createVariant("medium", "secondary"),
"secondary/large": createVariant("large", "secondary"),
"secondary/extra-large": createVariant("extra-large", "secondary"),
"tertiary/small": createVariant("small", "tertiary"),
"tertiary/medium": createVariant("medium", "tertiary"),
"tertiary/large": createVariant("large", "tertiary"),
"tertiary/extra-large": createVariant("extra-large", "tertiary"),
"minimal/small": createVariant("small", "minimal"),
"minimal/medium": createVariant("medium", "minimal"),
"minimal/large": createVariant("large", "minimal"),
"minimal/extra-large": createVariant("extra-large", "minimal"),
"danger/small": createVariant("small", "danger"),
"danger/medium": createVariant("medium", "danger"),
"danger/large": createVariant("large", "danger"),
"danger/extra-large": createVariant("extra-large", "danger"),
"menu-item": {
textColor: "text-bright px-1",
textColor: "text-text-bright px-1",
button:
"h-9 px-[0.475rem] text-sm rounded-sm bg-transparent group-hover:bg-slate-800 transition",
"h-9 px-[0.475rem] text-sm rounded-sm bg-transparent group-hover:bg-charcoal-800 transition",
icon: "h-5",
iconSpacing: "gap-x-0.5",
shortcutVariant: undefined,
shortcut: undefined,
},
"small-menu-item": {
textColor: "text-bright",
textColor: "text-text-bright",
button:
"h-[1.8rem] px-[0.4rem] text-2sm rounded-sm text-dimmed bg-transparent group-hover:bg-slate-850 transition",
"h-[1.8rem] px-[0.4rem] text-2sm rounded-sm text-text-dimmed bg-transparent group-hover:bg-charcoal-850 transition",
icon: "h-4",
iconSpacing: "gap-x-1.5",
shortcutVariant: undefined,
shortcut: undefined,
},
"small-menu-sub-item": {
textColor: "text-dimmed",
textColor: "text-text-dimmed",
button:
"h-[1.8rem] px-[0.5rem] ml-5 text-2sm rounded-sm text-dimmed bg-transparent group-hover:bg-slate-850 transition",
"h-[1.8rem] px-[0.5rem] ml-5 text-2sm rounded-sm text-text-dimmed bg-transparent group-hover:bg-charcoal-850 transition",
icon: undefined,
iconSpacing: undefined,
shortcutVariant: undefined,
@@ -18,52 +18,52 @@ import { Spinner } from "./Spinner";
export const variantClasses = {
info: {
className: "border-slate-700 bg-slate-800",
icon: <InformationCircleIcon className="h-5 w-5 shrink-0 text-dimmed" />,
textColor: "text-bright",
linkClassName: "transition hover:bg-slate-750",
className: "border-charcoal-700 bg-charcoal-800",
icon: <InformationCircleIcon className="h-5 w-5 shrink-0 text-text-dimmed" />,
textColor: "text-text-bright",
linkClassName: "transition hover:bg-charcoal-750",
},
warning: {
className: "border-yellow-400/20 bg-yellow-400/30",
icon: <ExclamationTriangleIcon className="h-5 w-5 shrink-0 text-yellow-400" />,
className: "border-warning/20 bg-warning/10",
icon: <ExclamationTriangleIcon className="h-5 w-5 shrink-0 text-warning" />,
textColor: "text-yellow-200",
linkClassName: "transition hover:bg-yellow-400/40",
linkClassName: "transition hover:bg-warning/20",
},
error: {
className: "border-rose-500/20 bg-rose-500/30",
icon: <ExclamationCircleIcon className="h-5 w-5 shrink-0 text-rose-400" />,
className: "border-error/20 bg-error/10",
icon: <ExclamationCircleIcon className="h-5 w-5 shrink-0 text-error" />,
textColor: "text-rose-200",
linkClassName: "transition hover:bg-rose-500/40",
linkClassName: "transition hover:bg-error/20",
},
idea: {
className: "border-green-400/20 bg-green-400/30",
icon: <LightBulbIcon className="h-5 w-5 shrink-0 text-green-400" />,
className: "border-success/20 bg-success/10",
icon: <LightBulbIcon className="h-5 w-5 shrink-0 text-success" />,
textColor: "text-green-200",
linkClassName: "transition hover:bg-green-400/40",
linkClassName: "transition hover:bg-success/20",
},
success: {
className: "border-green-400/20 bg-green-400/30",
icon: <CheckCircleIcon className="h-5 w-5 shrink-0 text-green-400" />,
className: "border-success/20 bg-success/10",
icon: <CheckCircleIcon className="h-5 w-5 shrink-0 text-success" />,
textColor: "text-green-200",
linkClassName: "transition hover:bg-green-400/40",
linkClassName: "transition hover:bg-success/20",
},
docs: {
className: "border-blue-400/20 bg-blue-400/30",
className: "border-blue-400/20 bg-blue-400/10",
icon: <BookOpenIcon className="mt-0.5 h-5 w-5 shrink-0 text-blue-400" />,
textColor: "text-blue-200",
linkClassName: "transition hover:bg-blue-400/40",
linkClassName: "transition hover:bg-blue-400/20",
},
pending: {
className: "border-blue-400/20 bg-blue-800/30",
icon: <Spinner className="h-5 w-5 shrink-0 " />,
textColor: "text-blue-300",
linkClassName: "transition hover:bg-blue-400/40",
linkClassName: "transition hover:bg-blue-400/20",
},
pricing: {
className: "border-indigo-400/20 bg-indigo-800/30",
icon: <ChartBarIcon className="h-5 w-5 shrink-0 text-indigo-500" />,
textColor: "text-indigo-200",
linkClassName: "transition hover:bg-indigo-400/40",
className: "border-charcoal-700 bg-charcoal-800",
icon: <ChartBarIcon className="h-5 w-5 shrink-0 text-text-dimmed" />,
textColor: "text-text-bright",
linkClassName: "transition hover:bg-charcoal-750",
},
} as const;
@@ -93,13 +93,13 @@ export function Callout({
href={to}
target="_blank"
className={cn(
`flex w-full items-center justify-between gap-2.5 rounded-md border py-2 pl-2 pr-3 shadow-md backdrop-blur-sm`,
`flex w-full items-start justify-between gap-2.5 rounded-md border py-2 pl-2 pr-3 shadow-md backdrop-blur-sm`,
variantDefinition.className,
variantDefinition.linkClassName,
className
)}
>
<div className={"flex w-full items-center gap-x-2"}>
<div className={"flex w-full items-start gap-x-2"}>
{icon ? icon : variantDefinition.icon}
{typeof children === "string" ? (
@@ -135,7 +135,9 @@ export function Callout({
children
)}
</div>
<ChevronRightIcon className={cn("h-5 w-5", variantDefinition.textColor)} />
<div className="flex h-full items-center">
<ChevronRightIcon className={cn("h-5 w-5", variantDefinition.textColor)} />
</div>
</Link>
);
}
@@ -144,13 +146,13 @@ export function Callout({
return (
<div
className={cn(
"flex w-full items-center gap-2 rounded-md border pl-2 pr-2 shadow-md backdrop-blur-sm",
"flex w-full items-start gap-2 rounded-md border pl-2 pr-2 shadow-md backdrop-blur-sm",
cta ? "py-2" : "py-2.5",
variantDefinition.className,
className
)}
>
<div className={cn(`flex w-full items-center gap-2.5`)}>
<div className={cn(`flex w-full items-start gap-2.5`)}>
{icon ? icon : variantDefinition.icon}
{typeof children === "string" ? (
@@ -7,44 +7,44 @@ import { Paragraph } from "./Paragraph";
const variants = {
"simple/small": {
button: "w-fit pr-4",
label: "text-sm text-bright mt-0.5 select-none",
description: "text-dimmed",
label: "text-sm text-text-bright mt-0.5 select-none",
description: "text-text-dimmed",
inputPosition: "mt-1",
isChecked: "",
isDisabled: "opacity-70",
},
simple: {
button: "w-fit pr-4",
label: "text-bright select-none",
description: "text-dimmed",
label: "text-text-bright select-none",
description: "text-text-dimmed",
inputPosition: "mt-1",
isChecked: "",
isDisabled: "opacity-70",
},
"button/small": {
button:
"flex items-center w-fit h-8 pl-2 pr-3 rounded border border-slate-800 hover:bg-slate-850 hover:border-slate-750 transition",
label: "text-sm text-bright select-none",
description: "text-dimmed",
"flex items-center w-fit h-8 pl-2 pr-3 rounded border border-charcoal-800 hover:bg-charcoal-850 hover:border-charcoal-750 transition",
label: "text-sm text-text-bright select-none",
description: "text-text-dimmed",
inputPosition: "mt-0",
isChecked: "bg-slate-850 border-slate-750 hover:!bg-slate-850",
isChecked: "bg-charcoal-850 border-charcoal-750 hover:!bg-charcoal-850",
isDisabled: "opacity-70 hover:bg-transparent",
},
button: {
button:
"w-fit py-2 pl-3 pr-4 rounded border border-slate-800 hover:bg-slate-850 hover:border-slate-750 transition",
label: "text-bright select-none",
description: "text-dimmed",
"w-fit py-2 pl-3 pr-4 rounded border border-charcoal-800 hover:bg-charcoal-850 hover:border-charcoal-750 transition",
label: "text-text-bright select-none",
description: "text-text-dimmed",
inputPosition: "mt-1",
isChecked: "bg-slate-850 border-slate-750 hover:!bg-slate-850",
isChecked: "bg-charcoal-850 border-charcoal-750 hover:!bg-charcoal-850",
isDisabled: "opacity-70 hover:bg-transparent",
},
description: {
button: "w-full py-2 pl-3 pr-4 checked:hover:bg-slate-850 transition",
label: "text-bright font-semibold",
description: "text-dimmed",
button: "w-full py-2 pl-3 pr-4 checked:hover:bg-charcoal-850 transition",
label: "text-text-bright font-semibold",
description: "text-text-dimmed",
inputPosition: "mt-1",
isChecked: "bg-slate-850",
isChecked: "bg-charcoal-850",
isDisabled: "opacity-70",
},
};
@@ -137,7 +137,7 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
className={cn(
inputPositionClasses,
props.readOnly || disabled ? "cursor-default" : "cursor-pointer",
"rounded-sm border border-slate-700 bg-transparent transition checked:!bg-indigo-500 read-only:border-slate-650 read-only:!bg-slate-700 group-hover:bg-slate-900 group-hover:checked:bg-indigo-500 group-focus:ring-1 focus:ring-indigo-500 focus:ring-offset-0 focus:ring-offset-transparent focus-visible:outline-none focus-visible:ring-indigo-500 disabled:border-slate-650 disabled:!bg-slate-700"
"read-only:border-charcoal-650 disabled:border-charcoal-650 rounded-sm border border-charcoal-700 bg-transparent transition checked:!bg-indigo-500 read-only:!bg-charcoal-700 group-hover:bg-charcoal-900 group-hover:checked:bg-indigo-500 group-focus:ring-1 focus:ring-indigo-500 focus:ring-offset-0 focus:ring-offset-transparent focus-visible:outline-none focus-visible:ring-indigo-500 disabled:!bg-charcoal-700"
)}
id={id}
ref={ref}
@@ -26,7 +26,7 @@ const ClientTabsTrigger = React.forwardRef<
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap border-r border-slate-700 px-2 text-sm ring-offset-background transition-all first:pl-0 last:border-none data-[state=active]:text-indigo-500 data-[state=inactive]:text-dimmed data-[state=inactive]:hover:text-bright focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
"ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center whitespace-nowrap border-r border-charcoal-700 px-2 text-sm transition-all first:pl-0 last:border-none data-[state=active]:text-indigo-500 data-[state=inactive]:text-text-dimmed data-[state=inactive]:hover:text-text-bright focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
className
)}
{...props}
@@ -41,7 +41,7 @@ const ClientTabsContent = React.forwardRef<
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-1 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
"ring-offset-background focus-visible:ring-ring mt-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
className
)}
{...props}
@@ -62,7 +62,7 @@ export type TabsProps = {
export function ClientTabsWithUnderline({ className, tabs, currentValue, layoutId }: TabsProps) {
return (
<TabsPrimitive.List
className={cn(`flex flex-row gap-x-6 border-b border-slate-700`, className)}
className={cn(`flex flex-row gap-x-6 border-b border-charcoal-700`, className)}
>
{tabs.map((tab, index) => {
const isActive = currentValue === tab.value;
@@ -75,7 +75,7 @@ export function ClientTabsWithUnderline({ className, tabs, currentValue, layoutI
<span
className={cn(
"text-sm transition duration-200",
isActive ? "text-indigo-500" : "text-slate-200"
isActive ? "text-indigo-500" : "text-charcoal-200"
)}
>
{tab.label}
@@ -83,7 +83,7 @@ export function ClientTabsWithUnderline({ className, tabs, currentValue, layoutI
{isActive ? (
<motion.div layoutId={layoutId} className="mt-1 h-0.5 w-full bg-indigo-500" />
) : (
<div className="mt-1 h-0.5 w-full bg-slate-500 opacity-0 transition duration-200 group-hover:opacity-100" />
<div className="mt-1 h-0.5 w-full bg-charcoal-500 opacity-0 transition duration-200 group-hover:opacity-100" />
)}
</TabsPrimitive.Trigger>
);
@@ -4,12 +4,12 @@ import { cn } from "~/utils/cn";
import { Button } from "./Buttons";
import { IconNames, NamedIcon } from "./NamedIcon";
const variations = {
const variants = {
"primary/small": {
container:
"flex items-center text-dimmed font-mono rounded border border-slate-800 bg-slate-850 text-xs transition hover:border-slate-750 hover:bg-slate-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
"flex items-center text-text-dimmed font-mono rounded border bg-charcoal-850 text-xs transition hover:bg-charcoal-700 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
input:
"bg-slate-850 border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent",
"bg-charcoal-750 border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent",
buttonVariant: "primary/small" as const,
button: "rounded-l-none min-w-[3.1rem]",
iconSize: "h-3 w-3",
@@ -17,29 +17,30 @@ const variations = {
},
"secondary/small": {
container:
"flex items-center text-dimmed font-mono rounded border border-slate-750 bg-slate-850 text-xs transition hover:border-slate-750 hover:bg-slate-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
"flex items-center text-text-dimmed font-mono rounded border bg-transparent text-xs transition hover:bg-charcoal-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
input:
"bg-slate-850 border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent",
buttonVariant: "secondary/small" as const,
button: "rounded-l-none border-l border-slate-750 min-w-[3.1rem]",
"bg-charcoal-750 border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent",
buttonVariant: "tertiary/small" as const,
button: "rounded-l-none border-l border-charcoal-750 min-w-[3.1rem]",
iconSize: "h-3 w-3",
iconPadding: "pl-1",
},
"tertiary/small": {
container:
"flex items-center text-dimmed font-mono rounded border border-slate-850 bg-transparent text-xs transition hover:border-slate-800 hover:bg-slate-950 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
"group flex items-center text-text-dimmed font-mono rounded bg-transparent text-xs transition hover:border-charcoal-700 hover:bg-tertiary focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
input:
"bg-transparent border-0 text-xs px-2 w-auto rounded-l h-6 leading-6 focus:ring-transparent",
buttonVariant: "tertiary/small" as const,
button: "rounded-l-none border-l border-slate-850 min-w-[3.1rem]",
buttonVariant: "minimal/small" as const,
button:
"rounded-l-none border-l border-transparent group-hover:border-charcoal-700 min-w-[3.1rem]",
iconSize: "h-3 w-3",
iconPadding: "pl-1",
},
"primary/medium": {
container:
"flex items-center text-dimmed font-mono rounded border border-slate-800 bg-slate-850 text-sm transition hover:border-slate-750 hover:bg-slate-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
"flex items-center text-text-dimmed font-mono rounded border border-charcoal-800 bg-charcoal-850 text-sm transition hover:border-charcoal-750 hover:bg-charcoal-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
input:
"bg-slate-850 border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent",
"bg-charcoal-850 border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent",
buttonVariant: "primary/medium" as const,
button: "rounded-l-none min-w-[4rem]",
iconSize: "h-4 w-4",
@@ -47,21 +48,21 @@ const variations = {
},
"secondary/medium": {
container:
"flex items-center text-dimmed font-mono rounded border border-slate-750 bg-slate-850 text-sm transition hover:border-slate-750 hover:bg-slate-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
"flex items-center text-text-dimmed font-mono rounded border border-charcoal-750 bg-charcoal-850 text-sm transition hover:border-charcoal-750 hover:bg-charcoal-800 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 focus:border-transparent focus:outline-none focus:ring-0 focus:ring-transparent",
input:
"bg-slate-850 border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent",
"bg-charcoal-850 border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent",
buttonVariant: "secondary/medium" as const,
button: "rounded-l-none border-l border-slate-750 min-w-[4rem]",
button: "rounded-l-none border-l border-charcoal-750 min-w-[4rem]",
iconSize: "h-4 w-4",
iconPadding: "pl-2",
},
"tertiary/medium": {
container:
"flex items-center text-dimmed font-mono rounded border border-slate-850 bg-transparent text-sm transition hover:border-slate-800 hover:bg-slate-950 focus:border-4 focus:border-solid focus:border-l-transparent",
"flex items-center text-text-dimmed font-mono rounded border border-charcoal-850 bg-transparent text-sm transition hover:border-charcoal-800 hover:bg-charcoal-950 focus:border-4 focus:border-solid focus:border-l-transparent",
input:
"bg-transparent border-0 text-sm px-3 w-auto rounded-l h-8 leading-6 focus:ring-transparent",
buttonVariant: "tertiary/medium" as const,
button: "rounded-l-none border-l border-slate-850 min-w-[4rem]",
button: "rounded-l-none border-l border-charcoal-850 min-w-[4rem]",
iconSize: "h-4 w-4",
iconPadding: "pl-2",
},
@@ -70,7 +71,7 @@ const variations = {
type ClipboardFieldProps = {
value: string;
secure?: boolean | string;
variant: keyof typeof variations;
variant: keyof typeof variants;
className?: string;
icon?: IconNames | React.ReactNode;
fullWidth?: boolean;
@@ -104,9 +105,9 @@ export function ClipboardField({
setIsSecure(secure !== undefined && secure);
}, [secure]);
const { container, input, buttonVariant, button } = variations[variant];
const iconClassName = variations[variant].iconSize;
const iconPosition = variations[variant].iconPadding;
const { container, input, buttonVariant, button } = variants[variant];
const iconClassName = variants[variant].iconSize;
const iconPosition = variants[variant].iconPadding;
const inputIcon = useRef<HTMLInputElement>(null);
return (
@@ -90,7 +90,7 @@ export function DateField({
return (
<div className={`flex flex-col items-start ${className || ""}`}>
<span {...labelProps} className="mb-1 ml-0.5 text-xs text-slate-300">
<span {...labelProps} className="mb-1 ml-0.5 text-xs text-charcoal-300">
{label}
</span>
<div className="flex flex-row items-center gap-1">
@@ -98,7 +98,7 @@ export function DateField({
{...fieldProps}
ref={ref}
className={cn(
"flex rounded-sm border border-slate-800 bg-midnight-900 p-0.5 px-1.5 transition-colors focus-within:border-slate-500 hover:border-slate-700 focus-within:hover:border-slate-500",
"flex rounded-sm border border-charcoal-800 bg-charcoal-750 p-0.5 px-1.5 transition-colors focus-within:border-charcoal-500 hover:border-charcoal-700 focus-within:hover:border-charcoal-500",
fieldClassName
)}
>
@@ -118,7 +118,7 @@ export function DateField({
</div>
{showNowButton && (
<Button
variant="secondary/small"
variant="tertiary/small"
onClick={() => {
const now = new Date();
setValue(utcDateToCalendarDate(new Date()));
@@ -130,7 +130,7 @@ export function DateField({
)}
{showClearButton && (
<Button
variant="secondary/small"
variant="tertiary/small"
LeadingIcon={"close"}
onClick={() => {
setValue(undefined);
@@ -187,13 +187,13 @@ function DateSegment({ segment, state }: DateSegmentProps) {
minWidth: minWidthForSegment(segment),
}}
className={`group box-content rounded-sm px-0.5 text-right text-sm tabular-nums outline-none focus:bg-indigo-500 focus:text-white ${
!segment.isEditable ? "text-slate-500" : "text-bright"
!segment.isEditable ? "text-charcoal-500" : "text-text-bright"
}`}
>
{/* Always reserve space for the placeholder, to prevent layout shift when editing. */}
<span
aria-hidden="true"
className="block text-center italic text-slate-500 group-focus:text-white"
className="block text-center italic text-charcoal-500 group-focus:text-white"
style={{
visibility: segment.isPlaceholder ? undefined : "hidden",
height: segment.isPlaceholder ? "" : 0,
@@ -232,10 +232,10 @@ function DateSegmentGuide({ segment }: { segment: DateSegment }) {
minWidth: minWidthForSegment(segment),
}}
className={`group box-content rounded-sm px-0.5 text-right text-sm tabular-nums outline-none ${
!segment.isEditable ? "text-slate-500" : "text-bright"
!segment.isEditable ? "text-charcoal-500" : "text-text-bright"
}`}
>
<span className="block text-center italic text-slate-500">
<span className="block text-center italic text-charcoal-500">
{segment.type !== "literal" ? segment.placeholder : segment.text}
</span>
</div>
@@ -51,19 +51,19 @@ export function DetailCell({
return (
<div
className={cn(
"group flex h-11 w-full items-center gap-3 rounded-md p-1 pr-3 transition hover:bg-slate-900",
"group flex h-11 w-full items-center gap-3 rounded-md p-1 pr-3 transition hover:bg-charcoal-900",
className
)}
>
<IconInBox
icon={leadingIcon}
className={cn("flex-none transition group-hover:border-slate-750", leadingIconClassName)}
className={cn("flex-none transition group-hover:border-charcoal-750", leadingIconClassName)}
/>
<div className="flex flex-1 flex-col">
<Paragraph
variant={variation.label.variant}
className={cn(
"flex-1 text-left transition group-hover:text-bright",
"flex-1 text-left transition group-hover:text-text-bright",
variation.label.className
)}
>
@@ -73,7 +73,7 @@ export function DetailCell({
<Paragraph
variant={variation.description.variant}
className={cn(
"flex-1 text-left text-dimmed transition group-hover:text-bright",
"flex-1 text-left text-text-dimmed transition group-hover:text-text-bright",
variation.description.className
)}
>
@@ -85,7 +85,7 @@ export function DetailCell({
<Icon
icon={trailingIcon}
className={cn(
"h-6 w-6 flex-none transition group-hover:border-slate-750",
"h-6 w-6 flex-none transition group-hover:border-charcoal-750",
trailingIconClassName
)}
/>
@@ -26,7 +26,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-background/90 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
"fixed inset-0 z-50 bg-background-dimmed/90 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
className
)}
{...props}
@@ -43,14 +43,14 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed z-50 grid w-full gap-4 rounded-b-lg border bg-background px-4 pb-6 pt-3 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
"fixed z-50 grid w-full gap-4 rounded-b-lg border bg-background-dimmed px-4 pb-6 pt-3 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
className
)}
{...props}
>
<hr className="absolute left-0 top-12 w-full" />
{children}
<DialogPrimitive.Close className="absolute right-3 top-3 rounded-sm opacity-70 ring-offset-background transition-opacity data-[state=open]:bg-accent data-[state=open]:text-muted-foreground hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none">
<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">
<div className="flex gap-x-2">
<ShortcutKey
shortcut={{
@@ -73,7 +73,10 @@ const DialogContent = React.forwardRef<
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col text-left font-medium text-dimmed", className)} {...props} />
<div
className={cn("flex flex-col text-left font-medium text-text-dimmed", className)}
{...props}
/>
);
DialogHeader.displayName = "DialogHeader";
@@ -103,7 +106,7 @@ const DialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
));
@@ -21,7 +21,7 @@ export function FormTitle({
<div
className={cn(
"mb-4 flex flex-col gap-3 pb-4",
divide ? "border-b border-ui-border" : "",
divide ? "border-b border-grid-bright" : "",
className
)}
>
@@ -16,8 +16,8 @@ const headerVariants = {
};
const textColorVariants = {
bright: "text-bright",
dimmed: "text-dimmed",
bright: "text-text-bright",
dimmed: "text-text-dimmed",
};
export type HeaderVariant = keyof typeof headerVariants;
@@ -46,7 +46,7 @@ export function HelpTrigger({ title }: { title: string }) {
<></>
) : (
<Button
variant="tertiary/small"
variant="minimal/small"
LeadingIcon="lightbulb"
leadingIconClassName="text-amber-400"
onClick={() => setOpen(true)}
@@ -78,9 +78,9 @@ export function HelpContent({
</div>
{allowDismissing && (
<Button
variant="tertiary/small"
variant="minimal/small"
TrailingIcon="close"
trailingIconClassName="text-slate-400"
trailingIconClassName="text-charcoal-400"
onClick={() => setOpen(false)}
>
Dismiss
@@ -88,7 +88,7 @@ export function HelpContent({
)}
</div>
<div className="grow rounded border border-slate-850 bg-midnight-850/80 bg-contain bg-left-top bg-no-repeat p-6">
<div className="grow rounded border border-charcoal-850 bg-background-bright/80 bg-contain bg-left-top bg-no-repeat p-6">
{children}
</div>
</div>
+16 -6
View File
@@ -3,7 +3,10 @@ import { IconNamesOrString, NamedIcon } from "./NamedIcon";
import { cn } from "~/utils/cn";
import { render } from "react-dom";
export type RenderIcon = IconNamesOrString | FunctionComponent<{className?: string}> | React.ReactNode;
export type RenderIcon =
| IconNamesOrString
| FunctionComponent<{ className?: string }>
| React.ReactNode;
type IconProps = {
icon?: RenderIcon;
@@ -17,7 +20,7 @@ export function Icon(props: IconProps) {
if (typeof props.icon === "string") {
return <NamedIcon name={props.icon} className={props.className ?? ""} fallback={<></>} />;
}
if (typeof props.icon === "function") {
const Icon = props.icon;
return <Icon className={props.className} />;
@@ -26,9 +29,16 @@ export function Icon(props: IconProps) {
if (React.isValidElement(props.icon)) {
return <>{props.icon}</>;
}
if (props.icon && typeof props.icon === 'object' && ('type' in props.icon || '$$typeof' in props.icon)) {
return createElement<FunctionComponent<any>>(props.icon as any, { className: props.className } as any);
if (
props.icon &&
typeof props.icon === "object" &&
("type" in props.icon || "$$typeof" in props.icon)
) {
return createElement<FunctionComponent<any>>(
props.icon as any,
{ className: props.className } as any
);
}
console.error("Invalid icon", props);
@@ -39,7 +49,7 @@ export function IconInBox({ boxClassName, ...props }: IconProps & { boxClassName
return (
<div
className={cn(
"grid h-9 w-9 place-content-center rounded-sm border border-slate-750 bg-slate-850",
"grid h-9 w-9 place-content-center rounded-sm border border-charcoal-750 bg-charcoal-850",
boxClassName
)}
>
+29 -28
View File
@@ -3,45 +3,43 @@ import { useImperativeHandle, useRef } from "react";
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"
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";
const inputBase = "h-full w-full 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"
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";
const shortcutBase = "grid h-fit place-content-center border border-dimmed/40 font-normal text-dimmed"
const shortcutBase =
"grid h-fit place-content-center border border-dimmed/40 font-normal text-text-dimmed";
const variants = {
large: {
container:
"px-1 w-full h-10 rounded-[3px] border border-slate-800 bg-slate-850 hover:border-slate-750 hover:bg-slate-800",
input:
"px-2 text-sm",
"px-1 w-full h-10 rounded-[3px] border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-750 hover:bg-charcoal-650",
input: "px-2 text-sm",
iconSize: "h-4 w-4 ml-1",
shortcut:
"mr-1 min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem] select-none",
shortcut: "mr-1 min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem] select-none",
},
medium: {
container: "px-1 h-8 w-full rounded border border-slate-800 bg-slate-850 hover:border-slate-750 hover:bg-slate-800",
input:
"px-1.5 rounded text-sm",
container:
"px-1 h-8 w-full rounded border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-750 hover:bg-charcoal-650",
input: "px-1.5 rounded text-sm",
iconSize: "h-4 w-4 ml-0.5",
shortcut:
"min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem]",
shortcut: "min-w-[22px] rounded-sm py-[3px] px-[5px] text-[0.6rem]",
},
small: {
container: "px-0.5 h-6 w-full rounded border border-slate-800 bg-slate-850 hover:border-slate-750 hover:bg-slate-800",
input:
"px-1 rounded text-xs",
container:
"px-0.5 h-6 w-full rounded border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-750 hover:bg-charcoal-650",
input: "px-1 rounded text-xs",
iconSize: "h-3 w-3 ml-0.5",
shortcut:
"min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]",
shortcut: "min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]",
},
tertiary: {
container: "px-0.5 h-6 w-full rounded border border-transparent hover:border-slate-800 hover:bg-slate-850",
input:
"px-1 rounded text-xs",
container:
"px-0.5 h-6 w-full rounded border border-transparent hover:border-charcoal-800 hover:bg-charcoal-750",
input: "px-1 rounded text-xs",
iconSize: "h-3 w-3 ml-0.5",
shortcut:
"min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]"
shortcut: "min-w-[22px] rounded-[2px] py-px px-[3px] text-[0.5rem]",
},
};
@@ -64,14 +62,17 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
return (
<div
className={cn("flex items-center", containerBase, containerClassName, fullWidth ? "w-full" : "max-w-max")}
className={cn(
"flex items-center",
containerBase,
containerClassName,
fullWidth ? "w-full" : "max-w-max"
)}
onClick={() => innerRef.current && innerRef.current.focus()}
>
{icon && (
<div
className="pointer-events-none flex items-center"
>
<Icon icon={icon} className={cn(iconClassName, "text-dimmed")} />
<div className="pointer-events-none flex items-center">
<Icon icon={icon} className={cn(iconClassName, "text-text-dimmed")} />
</div>
)}
<input
@@ -83,7 +83,7 @@ function ValueButton({ value, href, variant = "secondary" }: ValueButtonStackPro
<Paragraph variant={variation.value}>
<a href={href} className="underline underline-offset-2" target="_blank">
{value}
<ArrowTopRightOnSquareIcon className="ml-1 inline-block h-4 w-4 text-dimmed" />
<ArrowTopRightOnSquareIcon className="ml-1 inline-block h-4 w-4 text-text-dimmed" />
</a>
</Paragraph>
}
@@ -0,0 +1,52 @@
import { AnimatePresence, useAnimate, usePresence } from "framer-motion";
import { useEffect } from "react";
type LoadingBarDividerProps = {
isLoading: boolean;
};
export function LoadingBarDivider({ isLoading }: LoadingBarDividerProps) {
return (
<div className="relative h-px w-full bg-grid-bright">
<AnimationDivider isLoading={isLoading} />
</div>
);
}
export function AnimationDivider({ isLoading }: LoadingBarDividerProps) {
const [scope, animate] = useAnimate();
const [isPresent, safeToRemove] = usePresence();
useEffect(() => {
if (isPresent) {
const enterAnimation = async () => {
await animate(
[
[scope.current, { width: "30%", left: "35%" }, { duration: 1, ease: "easeIn" }],
[scope.current, { width: 0, left: "100%" }, { duration: 1, ease: "easeOut" }],
],
{ repeat: Infinity, repeatType: "reverse" }
);
};
enterAnimation();
} else {
const exitAnimation = async () => {
await animate(scope.current, { opacity: 0 });
safeToRemove();
};
exitAnimation();
}
}, [isPresent, isLoading]);
return (
<AnimatePresence>
{isLoading && (
<div
ref={scope}
className="width-0 absolute left-0 top-0 h-full bg-gradient-to-r from-transparent from-5% via-blue-500 to-transparent to-95%"
/>
)}
</AnimatePresence>
);
}
@@ -74,7 +74,7 @@ import { Spinner } from "./Spinner";
import tablerSpritePath from "./tabler-sprite.svg";
const icons = {
account: (className: string) => <UserCircleIcon className={cn("text-slate-400", className)} />,
account: (className: string) => <UserCircleIcon className={cn("text-charcoal-400", className)} />,
active: (className: string) => <CheckCircleIcon className={cn("text-green-500", className)} />,
"arrow-right": (className: string) => <ArrowRightIcon className={cn("text-white", className)} />,
"arrow-left": (className: string) => <ArrowLeftIcon className={cn("text-white", className)} />,
@@ -82,32 +82,34 @@ const icons = {
beaker: (className: string) => <BeakerIcon className={cn("text-purple-500", className)} />,
bell: (className: string) => <BellAlertIcon className={cn("text-amber-500", className)} />,
billing: (className: string) => <CreditCardIcon className={cn("text-teal-500", className)} />,
browser: (className: string) => <WindowIcon className={cn("text-dimmed", className)} />,
browser: (className: string) => <WindowIcon className={cn("text-text-dimmed", className)} />,
calendar: (className: string) => (
<CalendarDaysIcon className={cn("text-purple-500", className)} />
),
check: (className: string) => <CheckIcon className={cn("text-dimmed", className)} />,
check: (className: string) => <CheckIcon className={cn("text-text-dimmed", className)} />,
"chevron-down": (className: string) => (
<ChevronDownIcon className={cn("text-dimmed", className)} />
<ChevronDownIcon className={cn("text-text-dimmed", className)} />
),
"chevron-up": (className: string) => (
<ChevronUpIcon className={cn("text-text-dimmed", className)} />
),
"chevron-up": (className: string) => <ChevronUpIcon className={cn("text-dimmed", className)} />,
"chevron-left": (className: string) => (
<ChevronLeftIcon className={cn("text-dimmed", className)} />
<ChevronLeftIcon className={cn("text-text-dimmed", className)} />
),
"chevron-right": (className: string) => (
<ChevronRightIcon className={cn("text-dimmed", className)} />
<ChevronRightIcon className={cn("text-text-dimmed", className)} />
),
countdown: (className: string) => <HourglassIcon className={cn("text-amber-400", className)} />,
clock: (className: string) => <ClockIcon className={cn("text-cyan-500", className)} />,
close: (className: string) => <XMarkIcon className={cn("text-dimmed", className)} />,
close: (className: string) => <XMarkIcon className={cn("text-text-dimmed", className)} />,
"connection-alert": (className: string) => (
<BellAlertIcon className={cn("text-amber-500", className)} />
),
docs: (className: string) => <BookOpenIcon className={cn("text-slate-400", className)} />,
docs: (className: string) => <BookOpenIcon className={cn("text-charcoal-400", className)} />,
dynamic: (className: string) => <DynamicTriggerIcon className={cn("text-cyan-500", className)} />,
error: (className: string) => <ErrorIcon className={cn("text-rose-500", className)} />,
"external-link": (className: string) => (
<ArrowTopRightOnSquareIcon className={cn("text-dimmed", className)} />
<ArrowTopRightOnSquareIcon className={cn("text-text-dimmed", className)} />
),
flag: (className: string) => <FlagIcon className={cn("text-sky-500", className)} />,
folder: (className: string) => <FolderIcon className={cn("text-indigo-600", className)} />,
@@ -119,7 +121,7 @@ const icons = {
<HandRaisedIcon className={cn("text-amber-400", className)} />
),
heart: (className: string) => <HeartIcon className={cn("text-rose-500", className)} />,
house: (className: string) => <HomeIcon className={cn("text-dimmed", className)} />,
house: (className: string) => <HomeIcon className={cn("text-text-dimmed", className)} />,
id: (className: string) => <FingerPrintIcon className={cn("text-rose-200", className)} />,
inactive: (className: string) => <XCircleIcon className={cn("text-rose-500", className)} />,
info: (className: string) => <InformationCircleIcon className={cn("text-blue-500", className)} />,
@@ -135,11 +137,11 @@ const icons = {
key: (className: string) => <KeyIcon className={cn("text-amber-400", className)} />,
lightbulb: (className: string) => <LightBulbIcon className={cn("text-amber-400", className)} />,
"clipboard-checked": (className: string) => (
<ClipboardDocumentCheckIcon className={cn("text-dimmed", className)} />
<ClipboardDocumentCheckIcon className={cn("text-text-dimmed", className)} />
),
list: (className: string) => <ListBulletIcon className={cn("text-slate-400", className)} />,
list: (className: string) => <ListBulletIcon className={cn("text-charcoal-400", className)} />,
log: (className: string) => (
<ChatBubbleLeftEllipsisIcon className={cn("text-slate-400", className)} />
<ChatBubbleLeftEllipsisIcon className={cn("text-charcoal-400", className)} />
),
"logo-icon": (className: string) => <LogoIcon className={cn(className)} />,
organization: (className: string) => (
@@ -149,16 +151,20 @@ const icons = {
"plus-small": (className: string) => (
<PlusSmallIcon className={cn("text-green-600", className)} />
),
property: (className: string) => <Cog8ToothIcon className={cn("text-slate-600", className)} />,
property: (className: string) => <Cog8ToothIcon className={cn("text-charcoal-600", className)} />,
pulse: (className: string) => <ActivityIcon className={cn("text-green-600", className)} />,
"qr-code": (className: string) => <QrCodeIcon className={cn("text-amber-400", className)} />,
refresh: (className: string) => <ArrowPathIcon className={cn("text-bright", className)} />,
refresh: (className: string) => <ArrowPathIcon className={cn("text-text-bright", className)} />,
sapling: (className: string) => <SaplingIcon className={cn("text-green-500", className)} />,
search: (className: string) => <MagnifyingGlassIcon className={cn("text-dimmed", className)} />,
settings: (className: string) => <Cog8ToothIcon className={cn("text-slate-600", className)} />,
search: (className: string) => (
<MagnifyingGlassIcon className={cn("text-text-dimmed", className)} />
),
settings: (className: string) => <Cog8ToothIcon className={cn("text-charcoal-600", className)} />,
spinner: (className: string) => <Spinner className={className} color="blue" />,
"spinner-white": (className: string) => <Spinner className={className} color="white" />,
squares: (className: string) => <Square2StackIcon className={cn("text-dimmed", className)} />,
squares: (className: string) => (
<Square2StackIcon className={cn("text-text-dimmed", className)} />
),
star: (className: string) => <StarIcon className={cn("text-yellow-500", className)} />,
stop: (className: string) => <StopIcon className={cn("text-rose-500", className)} />,
team: (className: string) => <UserGroupIcon className={cn("text-blue-500", className)} />,
@@ -258,7 +264,7 @@ export function NamedIconInBox({
return (
<div
className={cn(
"grid place-content-center rounded-sm border border-slate-750 bg-slate-850",
"grid place-content-center rounded-sm border border-charcoal-750 bg-charcoal-850",
className
)}
>
@@ -1,33 +1,46 @@
import { Link } from "@remix-run/react";
import { Header1 } from "./Headers";
import { BreadcrumbIcon } from "./BreadcrumbIcon";
import { ChevronLeftIcon } from "@heroicons/react/24/solid";
import { Paragraph } from "./Paragraph";
import { cn } from "~/utils/cn";
import { NamedIcon } from "./NamedIcon";
import { Tabs, TabsProps } from "./Tabs";
import { Icon, RenderIcon } from "./Icon";
import { Button, LinkButton } from "./Buttons";
import { ArrowUpRightIcon } from "@heroicons/react/20/solid";
import { ChevronLeftIcon } from "@heroicons/react/24/solid";
import { Link, useNavigation } from "@remix-run/react";
import { useOptionalOrganization } from "~/hooks/useOrganizations";
import { cn } from "~/utils/cn";
import { plansPath } from "~/utils/pathBuilder";
import { UpgradePrompt, useShowUpgradePrompt } from "../billing/UpgradePrompt";
import { BreadcrumbIcon } from "./BreadcrumbIcon";
import { LinkButton } from "./Buttons";
import { Header2, Header3 } from "./Headers";
import { NamedIcon } from "./NamedIcon";
import { Paragraph } from "./Paragraph";
import { Tabs, TabsProps } from "./Tabs";
import { LoadingBarDivider } from "./LoadingBarDivider";
type WithChildren = {
children: React.ReactNode;
};
export function PageHeader({ children, hideBorder }: WithChildren & { hideBorder?: boolean }) {
export function NavBar({ children }: WithChildren) {
const organization = useOptionalOrganization();
const showUpgradePrompt = useShowUpgradePrompt(organization);
const navigation = useNavigation();
const isLoading = navigation.state === "loading" || navigation.state === "submitting";
return (
<div className={cn("mx-4 pt-4", hideBorder ? "" : "border-b border-ui-border pb-4")}>
{children}
<div>
<div className="grid h-10 w-full grid-rows-[auto_1px] bg-background-bright">
<div className="flex w-full items-center justify-between pl-3 pr-1">{children}</div>
<LoadingBarDivider isLoading={isLoading} />
</div>
{showUpgradePrompt.shouldShow && organization && (
<UpgradePrompt
runsEnabled={showUpgradePrompt.runsEnabled}
runCountCap={showUpgradePrompt.runCountCap}
planPath={plansPath(organization)}
/>
)}
</div>
);
}
export function PageTitleRow({ children }: WithChildren) {
return <div className="flex items-center justify-between">{children}</div>;
}
type PageTitleProps = {
icon?: RenderIcon;
title: string;
backButton?: {
to: string;
@@ -35,45 +48,31 @@ type PageTitleProps = {
};
};
export function PageTitle({ icon, title, backButton }: PageTitleProps) {
export function PageTitle({ title, backButton }: PageTitleProps) {
return (
<div className="flex items-center gap-2">
{backButton && (
<div className="group flex items-center gap-2">
<Link
to={backButton.to}
className="flex items-center gap-1 text-slate-400 transition group-hover:text-white"
className="text-xs text-text-dimmed transition group-hover:text-text-bright"
>
<ChevronLeftIcon className="h-6" />
<Header1 textColor="dimmed" className="transition group-hover:text-white">
{backButton.text}
</Header1>
{backButton.text}
</Link>
<BreadcrumbIcon className="h-6" />
<BreadcrumbIcon className="h-5" />
</div>
)}
<Header1 className="flex items-center gap-1">
{icon && <Icon icon={icon} className="h-5 w-5" />}
{title}
</Header1>
<Header2 className="flex items-center gap-1">{title}</Header2>
</div>
);
}
export function PageButtons({ children }: WithChildren) {
export function PageAccessories({ children }: WithChildren) {
return <div className="flex items-center gap-3">{children}</div>;
}
export function PageDescription({ children }: WithChildren) {
return (
<Paragraph variant="small" className="mb-0 mt-2">
{children}
</Paragraph>
);
}
export function PageInfoRow({ children }: WithChildren) {
return <div className="mt-2 flex w-full items-center gap-2">{children}</div>;
return <div className="flex w-full items-center gap-2">{children}</div>;
}
export function PageInfoGroup({
@@ -139,7 +138,7 @@ function PageInfoPropertyContent({
export function PageTabs(props: TabsProps) {
return (
<div className="mt-4">
<div className="mb-2 mt-2">
<Tabs {...props} />
</div>
);
@@ -21,12 +21,12 @@ export function PaginationControls({
}
return (
<div className="flex items-center justify-between rounded-b-lg border-t border-slate-850 bg-slate-700/20 py-3 pl-4 pr-3 text-slate-400">
<div className="flex items-center justify-between rounded-b-lg border-t border-charcoal-850 bg-charcoal-700/20 py-3 pl-4 pr-3 text-charcoal-400">
<div className="flex flex-1 justify-between sm:hidden">
{currentPage > 1 && (
<Link
to={pageUrl(location, currentPage - 1)}
className="relative inline-flex items-center rounded-md border border-slate-300 bg-slate-700/20 px-4 py-2 text-xs hover:bg-slate-50"
className="hover:bg-charcoal-50 relative inline-flex items-center rounded-md border border-charcoal-300 bg-charcoal-700/20 px-4 py-2 text-xs"
>
Previous
</Link>
@@ -34,7 +34,7 @@ export function PaginationControls({
{currentPage < totalPages && (
<Link
to={pageUrl(location, currentPage + 1)}
className="relative ml-3 inline-flex items-center rounded-md border border-slate-300 bg-slate-700/20 px-4 py-2 text-xs hover:bg-slate-50"
className="hover:bg-charcoal-50 relative ml-3 inline-flex items-center rounded-md border border-charcoal-300 bg-charcoal-700/20 px-4 py-2 text-xs"
>
Next
</Link>
@@ -42,7 +42,7 @@ export function PaginationControls({
</div>
<div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
<div>
<p className="text-sm text-slate-400">
<p className="text-sm text-charcoal-400">
Showing <span className="font-medium">{(currentPage - 1) * pageSize + 1}</span> to{" "}
<span className="font-medium">{currentPage * pageSize}</span> of{" "}
<span className="font-medium">{totalResults}</span> results
@@ -56,8 +56,8 @@ export function PaginationControls({
<LinkDisabled
to={pageUrl(location, currentPage - 1)}
disabled={currentPage === 1}
className="relative inline-flex items-center rounded-l border border-slate-500 bg-slate-700/20 px-2 text-xs font-medium text-slate-400 transition hover:border-slate-400 hover:bg-slate-400 hover:text-slate-800 focus:z-20"
disabledClassName="opacity-30 cursor-default hover:bg-slate-700/20 hover:border-slate-500 hover:!text-slate-400"
className="relative inline-flex items-center rounded-l border border-charcoal-500 bg-charcoal-700/20 px-2 text-xs font-medium text-charcoal-400 transition hover:border-charcoal-400 hover:bg-charcoal-400 hover:text-charcoal-800 focus:z-20"
disabledClassName="opacity-30 cursor-default hover:bg-charcoal-700/20 hover:border-charcoal-500 hover:!text-charcoal-400"
>
<span className="sr-only">Previous</span>
<ChevronLeftIcon className="h-4 w-4" aria-hidden="true" />
@@ -70,8 +70,8 @@ export function PaginationControls({
<LinkDisabled
to={pageUrl(location, currentPage + 1)}
disabled={currentPage === totalPages}
className="relative inline-flex items-center rounded-r border border-slate-500 bg-slate-700/20 px-2 text-xs font-medium text-slate-400 transition hover:border-slate-400 hover:bg-slate-400 hover:text-slate-800 focus:z-20"
disabledClassName="opacity-30 cursor-default hover:bg-slate-700/20 hover:border-slate-500 hover:!text-slate-400"
className="relative inline-flex items-center rounded-r border border-charcoal-500 bg-charcoal-700/20 px-2 text-xs font-medium text-charcoal-400 transition hover:border-charcoal-400 hover:bg-charcoal-400 hover:text-charcoal-800 focus:z-20"
disabledClassName="opacity-30 cursor-default hover:bg-charcoal-700/20 hover:border-charcoal-500 hover:!text-charcoal-400"
>
<span className="sr-only">Next</span>
<ChevronRightIcon className="h-4 w-4" aria-hidden="true" />
@@ -94,8 +94,9 @@ function pageUrl(location: ReturnType<typeof useLocation>, page: number): string
const baseClass =
"relative inline-flex items-center border px-3.5 py-2 text-xs font-medium focus:z-20 transition";
const unselectedClass =
"bg-slate-700/20 border-slate-500 text-slate-400 hover:bg-slate-400 hover:text-slate-900";
const selectedClass = "z-10 bg-slate-500 border-slate-500 hover:bg-slate-400 text-slate-900";
"bg-charcoal-700/20 border-charcoal-500 text-charcoal-400 hover:bg-charcoal-400 hover:text-charcoal-900";
const selectedClass =
"z-10 bg-charcoal-500 border-charcoal-500 hover:bg-charcoal-400 text-charcoal-900";
function PageLinkComponent({
page,
@@ -120,7 +121,7 @@ function PageLinkComponent({
}
} else {
return (
<span className="inline-flex items-center border-t-2 border-transparent px-4 pt-4 text-xs font-medium text-slate-500">
<span className="inline-flex items-center border-t-2 border-transparent px-4 pt-4 text-xs font-medium text-charcoal-500">
...
</span>
);
@@ -4,64 +4,64 @@ import { IconNamesOrString, NamedIcon } from "./NamedIcon";
const paragraphVariants = {
base: {
text: "font-sans text-base font-normal text-dimmed",
text: "font-sans text-base font-normal text-text-dimmed",
spacing: "mb-3",
},
"base/bright": {
text: "font-sans text-base font-normal text-bright",
text: "font-sans text-base font-normal text-text-bright",
spacing: "mb-3",
},
small: {
text: "font-sans text-sm font-normal text-dimmed",
text: "font-sans text-sm font-normal text-text-dimmed",
spacing: "mb-2",
},
"small/bright": {
text: "font-sans text-sm font-normal text-bright",
text: "font-sans text-sm font-normal text-text-bright",
spacing: "mb-2",
},
"extra-small": {
text: "font-sans text-xs font-normal text-dimmed",
text: "font-sans text-xs font-normal text-text-dimmed",
spacing: "mb-1.5",
},
"extra-small/bright": {
text: "font-sans text-xs font-normal text-bright",
text: "font-sans text-xs font-normal text-text-bright",
spacing: "mb-1.5",
},
"extra-small/mono": {
text: "font-mono text-xs font-normal text-dimmed",
text: "font-mono text-xs font-normal text-text-dimmed",
spacing: "mb-1.5",
},
"extra-small/bright/mono": {
text: "font-mono text-xs text-bright",
text: "font-mono text-xs text-text-bright",
spacing: "mb-1.5",
},
"extra-small/caps": {
text: "font-sans text-xs uppercase tracking-wider font-normal text-dimmed",
text: "font-sans text-xs uppercase tracking-wider font-normal text-text-dimmed",
spacing: "mb-1.5",
},
"extra-small/bright/caps": {
text: "font-sans text-xs uppercase tracking-wider font-normal text-bright",
text: "font-sans text-xs uppercase tracking-wider font-normal text-text-bright",
spacing: "mb-1.5",
},
"extra-extra-small": {
text: "font-sans text-xxs font-normal text-dimmed",
text: "font-sans text-xxs font-normal text-text-dimmed",
spacing: "mb-1",
},
"extra-extra-small/bright": {
text: "font-sans text-xxs font-normal text-bright",
text: "font-sans text-xxs font-normal text-text-bright",
spacing: "mb-1",
},
"extra-extra-small/caps": {
text: "font-sans text-xxs uppercase tracking-wider font-normal text-dimmed",
text: "font-sans text-xxs uppercase tracking-wider font-normal text-text-dimmed",
spacing: "mb-1",
},
"extra-extra-small/bright/caps": {
text: "font-sans text-xxs uppercase tracking-wider font-normal text-bright",
text: "font-sans text-xxs uppercase tracking-wider font-normal text-text-bright",
spacing: "mb-1",
},
"extra-extra-small/dimmed/caps": {
text: "font-sans text-xxs uppercase tracking-wider font-normal text-dimmed",
text: "font-sans text-xxs uppercase tracking-wider font-normal text-text-dimmed",
spacing: "mb-1",
},
};
@@ -21,7 +21,7 @@ const PopoverContent = React.forwardRef<
sideOffset={sideOffset}
avoidCollisions={true}
className={cn(
"z-50 min-w-max rounded-md border border-slate-700 bg-midnight-850 p-4 text-popover-foreground shadow-md outline-none animate-in data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-max rounded-md border border-charcoal-700 bg-background-bright p-4 shadow-md outline-none animate-in data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
style={{
@@ -41,7 +41,7 @@ function PopoverSectionHeader({
variant?: ParagraphVariant;
}) {
return (
<Paragraph variant={variant} className="bg-slate-900 px-2.5 py-1.5">
<Paragraph variant={variant} className="bg-charcoal-900 px-2.5 py-1.5">
{title}
</Paragraph>
);
@@ -71,7 +71,7 @@ function PopoverMenuItem({
fullWidth
textAlignLeft
TrailingIcon={isSelected ? "check" : undefined}
className={isSelected ? "bg-slate-750 group-hover:bg-slate-800" : undefined}
className={isSelected ? "bg-charcoal-750 group-hover:bg-charcoal-800" : undefined}
>
{title}
</LinkButton>
@@ -88,7 +88,7 @@ function PopoverCustomTrigger({
<PopoverTrigger
{...props}
className={cn(
"group flex items-center justify-end gap-1 rounded text-dimmed transition hover:bg-slate-850 hover:text-bright",
"group flex items-center justify-end gap-1 rounded text-text-dimmed transition hover:bg-charcoal-850 hover:text-text-bright",
className
)}
>
@@ -113,7 +113,7 @@ function PopoverArrowTrigger({
<PopoverTrigger
{...props}
className={cn(
"group flex h-6 items-center gap-1 rounded px-2 text-dimmed transition hover:bg-slate-850 hover:text-bright",
"group flex h-6 items-center gap-1 rounded px-2 text-text-dimmed transition hover:bg-charcoal-700 hover:text-text-bright",
fullWidth && "w-full justify-between",
className
)}
@@ -121,7 +121,7 @@ function PopoverArrowTrigger({
<Paragraph
variant="extra-small"
className={cn(
"flex transition group-hover:text-bright",
"flex transition group-hover:text-text-bright",
overflowHidden && "overflow-hidden"
)}
>
@@ -129,7 +129,7 @@ function PopoverArrowTrigger({
</Paragraph>
<ChevronDownIcon
className={cn(
"h-3 w-3 min-w-[0.75rem] text-slate-600 transition group-hover:text-bright",
"h-3 w-3 min-w-[0.75rem] text-charcoal-600 transition group-hover:text-text-bright",
isOpen && "-rotate-180"
)}
/>
@@ -146,11 +146,11 @@ function PopoverVerticalEllipseTrigger({
<PopoverTrigger
{...props}
className={cn(
"group flex items-center justify-end gap-1 rounded px-1.5 py-1.5 text-dimmed transition hover:bg-slate-750 hover:text-bright",
"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",
className
)}
>
<EllipsisVerticalIcon className={cn("h-5 w-5 transition group-hover:text-bright")} />
<EllipsisVerticalIcon className={cn("h-5 w-5 transition group-hover:text-text-bright")} />
</PopoverTrigger>
);
}
@@ -8,47 +8,47 @@ import { Paragraph } from "./Paragraph";
const variants = {
"simple/small": {
button: "w-fit pr-4 data-[disabled]:opacity-70",
label: "text-sm text-bright mt-0.5 select-none",
description: "text-dimmed",
label: "text-sm text-text-bright mt-0.5 select-none",
description: "text-text-dimmed",
inputPosition: "mt-1",
icon: "w-8 h-8 mb-2",
},
simple: {
button: "w-fit pr-4 data-[disabled]:opacity-70",
label: "text-bright select-none",
description: "text-dimmed",
label: "text-text-bright select-none",
description: "text-text-dimmed",
inputPosition: "mt-1",
icon: "w-8 h-8 mb-2",
},
"button/small": {
button:
"flex items-center w-fit h-8 pl-2 pr-3 rounded border border-slate-800 hover:bg-slate-850 hover:border-slate-750 transition data-[disabled]:opacity-70 data-[disabled]:hover:bg-transparent data-[state=checked]:bg-slate-850",
label: "text-sm text-bright select-none",
description: "text-dimmed",
"flex items-center w-fit h-8 pl-2 pr-3 rounded border border-charcoal-800 hover:bg-charcoal-850 hover:border-charcoal-750 transition data-[disabled]:opacity-70 data-[disabled]:hover:bg-transparent data-[state=checked]:bg-charcoal-850",
label: "text-sm text-text-bright select-none",
description: "text-text-dimmed",
inputPosition: "mt-0",
icon: "w-8 h-8 mb-2",
},
button: {
button:
"w-fit py-2 pl-3 pr-4 rounded border border-slate-800 hover:bg-slate-850 hover:border-slate-750 transition data-[state=checked]:bg-slate-850 data-[disabled]:opacity-70",
label: "text-bright select-none",
description: "text-dimmed",
"w-fit py-2 pl-3 pr-4 rounded border border-charcoal-800 hover:bg-charcoal-850 hover:border-charcoal-750 transition data-[state=checked]:bg-charcoal-850 data-[disabled]:opacity-70",
label: "text-text-bright select-none",
description: "text-text-dimmed",
inputPosition: "mt-1",
icon: "w-8 h-8 mb-2",
},
description: {
button:
"w-full p-2.5 hover:bg-slate-850 transition data-[disabled]:opacity-70 data-[state=checked]:bg-slate-850 border-slate-800 border rounded-sm",
label: "text-bright font-semibold -mt-1 text-left",
description: "text-dimmed -mt-0 text-left",
"w-full p-2.5 hover:bg-charcoal-850 transition data-[disabled]:opacity-70 data-[state=checked]:bg-charcoal-850 border-charcoal-800 border rounded-sm",
label: "text-text-bright font-semibold -mt-1 text-left",
description: "text-text-dimmed -mt-0 text-left",
inputPosition: "mt-0",
icon: "w-8 h-8 mb-2",
},
icon: {
button:
"w-full p-2.5 pb-4 hover:bg-slate-850 transition data-[disabled]:opacity-70 data-[state=checked]:bg-slate-850 border-slate-800 border rounded-sm",
label: "text-bright font-semibold -mt-1 text-left",
description: "text-dimmed -mt-0 text-left",
"w-full p-2.5 pb-4 hover:bg-charcoal-850 transition data-[disabled]:opacity-70 data-[state=checked]:bg-charcoal-850 border-charcoal-800 border rounded-sm",
label: "text-text-bright font-semibold -mt-1 text-left",
description: "text-text-dimmed -mt-0 text-left",
inputPosition: "mt-0",
icon: "mb-3",
},
@@ -70,7 +70,7 @@ export function RadioButtonCircle({
return (
<div
className={cn(
"aspect-square h-4 w-4 shrink-0 overflow-hidden rounded-full border border-slate-700 ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring 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-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
boxClassName
)}
>
@@ -129,7 +129,7 @@ export const RadioGroupItem = React.forwardRef<
>
<div
className={cn(
"aspect-square h-4 w-4 shrink-0 overflow-hidden rounded-full border border-slate-700 ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring 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-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
variation.inputPosition
)}
>
@@ -8,7 +8,7 @@ const ResizablePanelGroup = ({
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
<ResizablePrimitive.PanelGroup
className={cn("flex h-full w-full data-[panel-group-direction=vertical]:flex-col", className)}
className={cn("flex w-full data-[panel-group-direction=vertical]:flex-col", className)}
{...props}
/>
);
@@ -24,16 +24,16 @@ const ResizableHandle = ({
}) => (
<ResizablePrimitive.PanelResizeHandle
className={cn(
"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-ring focus-visible:ring-offset-1 [&[data-panel-group-direction=vertical]>div]:rotate-90",
"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-offset-1 [&[data-panel-group-direction=vertical]>div]:rotate-90",
className
)}
{...props}
>
<div className="absolute left-[0.0625rem] top-0 h-full w-px bg-border group-hover:left-0 group-hover:w-0.75 group-hover:bg-indigo-500" />
<div className="absolute left-[0.0625rem] top-0 h-full w-px bg-grid-bright transition group-hover:left-0 group-hover:w-0.75 group-hover:bg-lavender-500" />
{withHandle && (
<div className="z-10 flex h-5 w-3 flex-col items-center justify-center gap-[0.1875rem] bg-background group-hover:hidden">
<div className="z-10 flex h-5 w-3 flex-col items-center justify-center gap-[0.1875rem] bg-background-dimmed group-hover:hidden">
{Array.from({ length: 3 }).map((_, index) => (
<div key={index} className="h-[0.1875rem] w-0.75 rounded-full bg-slate-750" />
<div key={index} className="h-[0.1875rem] w-0.75 rounded-full bg-charcoal-600" />
))}
</div>
)}
@@ -25,7 +25,7 @@ export default function SegmentedControl({
onChange,
}: SegmentedControlProps) {
return (
<div className={cn("flex h-10 rounded bg-slate-850", fullWidth ? "w-full" : "w-fit")}>
<div className={cn("flex h-10 rounded bg-charcoal-700", fullWidth ? "w-full" : "w-fit")}>
<RadioGroup
value={value}
defaultValue={defaultValue ?? options[0].value}
@@ -46,11 +46,11 @@ export default function SegmentedControl({
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-indigo-500 focus-visible:ring-opacity-60"
? "ring-offset-2 focus-visible:ring focus-visible:ring-primary focus-visible:ring-opacity-60"
: "",
checked
? "text-bright"
: "rounded-[2px] text-dimmed transition hover:bg-slate-750/50 hover:text-bright"
? "text-text-bright"
: "rounded-[2px] text-text-dimmed transition hover:bg-charcoal-750/50 hover:text-text-bright"
)
}
>
@@ -64,7 +64,7 @@ export default function SegmentedControl({
<motion.div
layoutId={`segmented-control-${name}`}
transition={{ duration: 0.4, type: "spring" }}
className="absolute inset-0 rounded-[2px] bg-indigo-600 shadow-md"
className="absolute inset-0 rounded-[2px] shadow-md outline outline-3 outline-primary"
></motion.div>
)}
</div>
@@ -6,9 +6,10 @@ import { Check, ChevronDown } from "lucide-react";
import { cn } from "~/utils/cn";
const sizes = {
"secondary/small": "text-xs h-6 bg-tertiary hover:bg-tertiary-foreground pr-2 pl-1 bg-slate-800",
"secondary/small":
"text-xs h-6 bg-tertiary hover:bg-tertiary-foreground pr-2 pl-1 bg-charcoal-800",
medium:
"text-sm h-8 bg-slate-850 border border-slate-800 hover:bg-slate-800 hover:border-slate-750 px-2.5",
"text-sm h-8 bg-charcoal-850 border border-charcoal-800 hover:bg-charcoal-800 hover:border-charcoal-750 px-2.5",
};
export type SelectProps = {
@@ -28,7 +29,7 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"group flex items-center justify-between gap-x-2 rounded text-dimmed ring-offset-background transition placeholder:text-dimmed hover:text-bright focus-visible:bg-tertiary-foreground focus-visible:text-bright focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50",
"ring-offset-background focus-visible:bg-tertiary-foreground focus-visible:ring-ring group flex items-center justify-between gap-x-2 rounded text-text-dimmed transition placeholder:text-text-dimmed hover:text-text-bright focus-visible:text-text-bright focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50",
width === "full" ? "w-full" : "w-min",
sizeClassName,
className
@@ -39,7 +40,7 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Icon asChild>
<ChevronDown
className={cn(
"h-4 w-4 text-dimmed transition group-hover:text-bright group-focus:text-bright"
"h-4 w-4 text-text-dimmed transition group-hover:text-text-bright group-focus:text-text-bright"
)}
/>
</SelectPrimitive.Icon>
@@ -56,7 +57,7 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 min-w-max overflow-hidden rounded-md border border-slate-700 bg-popover text-bright shadow-md animate-in fade-in-40",
"relative z-50 min-w-max overflow-hidden rounded-md border border-charcoal-700 bg-background-dimmed text-text-bright shadow-md animate-in fade-in-40",
position === "popper" && "translate-y-1",
className
)}
@@ -84,7 +85,7 @@ const SelectLabel = React.forwardRef<
<SelectPrimitive.Label
ref={ref}
className={cn(
"-ml-1 -mr-1 mb-1 bg-slate-900 py-1.5 pl-2 pr-2 font-sans text-xxs font-normal uppercase leading-normal tracking-wider text-dimmed first-of-type:-mt-0",
"-ml-1 -mr-1 mb-1 bg-charcoal-900 py-1.5 pl-2 pr-2 font-sans text-xxs font-normal uppercase leading-normal tracking-wider text-text-dimmed first-of-type:-mt-0",
className
)}
{...props}
@@ -99,7 +100,7 @@ const SelectItem = React.forwardRef<
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative my-0.5 flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-12 text-sm outline-none transition first-of-type:my-1 last-of-type:my-1 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-slate-750 focus:bg-slate-750/50",
"relative my-0.5 flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-12 text-sm outline-none transition first-of-type:my-1 last-of-type:my-1 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-charcoal-750 focus:bg-charcoal-750/50",
className
)}
{...props}
@@ -121,7 +122,7 @@ const SelectSeparator = React.forwardRef<
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
className={cn("bg-muted -mx-1 my-1 h-px", className)}
{...props}
/>
));
@@ -41,7 +41,7 @@ const SheetOverlay = React.forwardRef<
>(({ className, children, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-midnight-900/80 transition animate-in duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
"fixed inset-0 z-50 bg-background-dimmed/80 transition duration-200 animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
className
)}
{...props}
@@ -51,7 +51,7 @@ const SheetOverlay = React.forwardRef<
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva(
"fixed z-50 scale-100 gap-4 bg-midnight-900 shadow-lg shadow-white/10 opacity-100 border-l border-ui-border",
"fixed z-50 scale-100 gap-4 bg-background-dimmed shadow-lg shadow-white/10 opacity-100 border-l border-grid-bright",
{
variants: {
position: {
@@ -154,8 +154,8 @@ const SheetContent = React.forwardRef<
{...props}
>
<div className="grid max-h-full grid-rows-[2.75rem_1fr] overflow-hidden">
<div className="flex items-center gap-2 border-b border-ui-border p-2">
<SheetPrimitive.Close className="rounded-sm p-1 transition hover:bg-slate-800 disabled:pointer-events-none">
<div className="flex items-center gap-2 border-b border-grid-bright p-2">
<SheetPrimitive.Close className="rounded-sm p-1 transition hover:bg-charcoal-800 disabled:pointer-events-none">
<NamedIcon name="close" className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
@@ -171,7 +171,7 @@ SheetContent.displayName = SheetPrimitive.Content.displayName;
export const SheetBody = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"overflow-y-auto p-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700",
"overflow-y-auto p-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600",
className
)}
{...props}
@@ -181,7 +181,7 @@ export const SheetBody = ({ className, ...props }: React.HTMLAttributes<HTMLDivE
export const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"mx-4 flex shrink-0 items-center gap-4 border-b border-ui-border py-3.5",
"mx-4 flex shrink-0 items-center gap-4 border-b border-grid-bright py-3.5",
className
)}
{...props}
@@ -194,7 +194,7 @@ export const SheetFooter = ({
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("shrink-0", className)} {...props}>
<div className="mx-4 border-t border-ui-border py-3">{children}</div>
<div className="mx-4 border-t border-grid-bright py-3">{children}</div>
</div>
);
@@ -5,9 +5,9 @@ import { useOperatingSystem } from "./OperatingSystemProvider";
const variants = {
small:
"text-[0.6rem] font-medium min-w-[17px] rounded-[2px] px-1 ml-1 -mr-0.5 grid place-content-center border border-dimmed/40 text-dimmed group-hover:text-bright/80 group-hover:border-dimmed/60 transition uppercase",
"text-[0.6rem] font-medium min-w-[17px] rounded-[2px] px-1 ml-1 -mr-0.5 grid place-content-center border border-dimmed/40 text-text-dimmed group-hover:text-text-bright/80 group-hover:border-dimmed/60 transition uppercase",
medium:
"text-[0.75rem] font-medium min-w-[17px] rounded-[2px] px-1 ml-1 -mr-0.5 grid place-content-center border border-dimmed/40 text-dimmed group-hover:text-bright/80 group-hover:border-dimmed/60 transition uppercase",
"text-[0.75rem] font-medium min-w-[17px] rounded-[2px] px-1 ml-1 -mr-0.5 grid place-content-center border border-dimmed/40 text-text-dimmed group-hover:text-text-bright/80 group-hover:border-dimmed/60 transition uppercase",
};
export type ShortcutKeyVariant = keyof typeof variants;
@@ -0,0 +1,52 @@
import * as RadixSlider from "@radix-ui/react-slider";
import { ComponentProps } from "react";
import { cn } from "~/utils/cn";
import { Icon, RenderIcon } from "./Icon";
const variants = {
tertiary: {
container: "h-6 gap-1 rounded-sm hover:bg-charcoal-700 px-1",
icons: "h-4 w-4 text-text-bright",
root: "h-4",
track: "h-1 bg-grid-bright group-hover:bg-background-dimmed",
range: "bg-transparent group-hover:bg-secondary",
thumb:
"h-3 w-3 border-2 border-text-dimmed bg-grid-bright shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)] hover:border-text-dimmed focus:shadow-[0_1px_3px_4px_rgb(0_0_0_/_0.2),_0_1px_2px_-1px_rgb(0_0_0_/_0.1)]",
},
};
type VariantName = keyof typeof variants;
export type SliderProps = ComponentProps<typeof RadixSlider.Root> & {
LeadingIcon?: RenderIcon;
TrailingIcon?: RenderIcon;
variant: VariantName;
};
export function Slider({ variant, className, LeadingIcon, TrailingIcon, ...props }: SliderProps) {
const variation = variants[variant];
return (
<div className={cn("group flex items-center", variation.container)}>
{LeadingIcon && <Icon icon={LeadingIcon} className={variation.icons} />}
<RadixSlider.Root
className={cn(
"relative flex touch-none select-none items-center",
variation.root,
className
)}
{...props}
>
<RadixSlider.Track className={cn("relative grow rounded-full", variation.track)}>
<RadixSlider.Range className={cn("absolute h-full rounded-full", variation.range)} />
</RadixSlider.Track>
<RadixSlider.Thumb
className={cn(
"block cursor-pointer rounded-full transition focus:outline-none",
variation.thumb
)}
/>
</RadixSlider.Root>
{TrailingIcon && <Icon icon={TrailingIcon} className={variation.icons} />}
</div>
);
}
@@ -21,14 +21,14 @@ export function StepNumber({
<div className={cn("mr-3", className)}>
{active ? (
<div className="flex items-center gap-x-3">
<span className="flex h-6 w-6 items-center justify-center rounded bg-green-600 py-1 text-xs font-semibold text-slate-900 shadow">
<span className="flex h-6 w-6 items-center justify-center rounded bg-green-600 py-1 text-xs font-semibold text-charcoal-900 shadow">
{stepNumber}
</span>
<Header2>{title}</Header2>
</div>
) : (
<div className="flex items-center gap-x-3">
<span className="flex h-6 w-6 items-center justify-center rounded border border-slate-700 bg-slate-800 py-1 text-xs font-semibold text-dimmed shadow">
<span className="flex h-6 w-6 items-center justify-center rounded border border-charcoal-700 bg-charcoal-800 py-1 text-xs font-semibold text-text-dimmed shadow">
{complete ? "✓" : stepNumber}
</span>
@@ -6,16 +6,16 @@ import { cn } from "~/utils/cn";
const variations = {
large: {
container: "flex items-center gap-x-2 rounded-md hover:bg-slate-850 p-2",
container: "flex items-center gap-x-2 rounded-md hover:bg-charcoal-850 p-2",
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-slate-400 group-hover:text-slate-200",
text: "text-sm text-charcoal-400 group-hover:text-charcoal-200",
},
small: {
container: "flex items-center gap-x-1.5 rounded hover:bg-slate-850 pr-1 py-[0.1rem] pl-1.5",
container: "flex items-center gap-x-1.5 rounded hover:bg-charcoal-850 pr-1 py-[0.1rem] pl-1.5",
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-slate-400 group-hover:text-slate-200 hover:cursor-pointer",
text: "text-xs text-charcoal-400 group-hover:text-charcoal-200 hover:cursor-pointer",
},
};
@@ -37,14 +37,14 @@ export const Switch = React.forwardRef<React.ElementRef<typeof SwitchPrimitives.
) : null}
<div
className={cn(
"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-ring group-focus-visible:ring-offset-2 group-focus-visible:ring-offset-background group-disabled:cursor-not-allowed group-disabled:opacity-50 group-data-[state=checked]:bg-primary group-data-[state=unchecked]:bg-slate-700 focus-visible:outline-none",
"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",
root
)}
>
<SwitchPrimitives.Thumb
className={cn(
thumb,
"pointer-events-none block rounded-full bg-slate-200 shadow-lg ring-0 transition-transform"
"pointer-events-none block rounded-full bg-charcoal-200 shadow-lg ring-0 transition group-data-[state=checked]:bg-charcoal-700"
)}
/>
</div>
+10 -10
View File
@@ -16,7 +16,7 @@ export const Table = forwardRef<HTMLTableElement, TableProps>(
return (
<div
className={cn(
"overflow-x-auto whitespace-nowrap rounded-md border border-ui-border scrollbar-thin scrollbar-track-midnight-850 scrollbar-thumb-slate-700",
"overflow-x-auto whitespace-nowrap rounded-md border border-grid-bright scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600",
containerClassName,
fullWidth && "w-full"
)}
@@ -39,7 +39,7 @@ export const TableHeader = forwardRef<HTMLTableSectionElement, TableHeaderProps>
return (
<thead
ref={ref}
className={cn("rounded-t-md", "relative divide-y divide-ui-border bg-slate-850", className)}
className={cn("rounded-t-md", "relative divide-y divide-grid-dimmed", className)}
>
{children}
</thead>
@@ -55,7 +55,7 @@ type TableBodyProps = {
export const TableBody = forwardRef<HTMLTableSectionElement, TableBodyProps>(
({ className, children }, ref) => {
return (
<tbody ref={ref} className={cn("relative divide-y divide-ui-border", className)}>
<tbody ref={ref} className={cn("relative divide-y divide-grid-dimmed", className)}>
{children}
</tbody>
);
@@ -106,7 +106,7 @@ export const TableHeaderCell = forwardRef<HTMLTableCellElement, TableHeaderCellP
ref={ref}
scope="col"
className={cn(
"px-4 py-2 align-middle text-xxs font-normal uppercase tracking-wider text-dimmed",
"px-4 py-2 align-middle text-xxs font-normal uppercase tracking-wider text-text-dimmed",
alignmentClassName,
className
)}
@@ -126,7 +126,7 @@ type TableCellProps = TableCellBasicProps & {
};
const stickyStyles =
"sticky right-0 z-10 w-[2.8rem] min-w-[2.8rem] bg-background before:absolute before:pointer-events-none before:-left-8 before:top-0 before:h-full before:min-w-[2rem] before:bg-gradient-to-r before:from-transparent before:to-background before:content-[''] group-hover:before:to-slate-900";
"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] before:bg-gradient-to-r before:from-transparent before:to-background before:content-[''] group-hover:before:to-charcoal-900";
export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
(
@@ -153,7 +153,7 @@ export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
}
const flexClasses = cn(
"flex w-full whitespace-nowrap px-4 py-3 text-xs text-dimmed",
"flex w-full whitespace-nowrap px-4 py-3 text-xs text-text-dimmed",
alignment === "left"
? "justify-start text-left"
: alignment === "center"
@@ -165,9 +165,9 @@ export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
<td
ref={ref}
className={cn(
"text-xs text-slate-400",
"text-xs text-charcoal-400",
to || onClick || hasAction
? "cursor-pointer group-hover:bg-slate-900"
? "cursor-pointer group-hover:bg-charcoal-900"
: "px-4 py-3 align-middle",
!to && !onClick && alignmentClassName,
isSticky && stickyStyles,
@@ -211,7 +211,7 @@ export const TableCellChevron = forwardRef<
alignment="right"
>
{children}
<ChevronRightIcon className="h-4 w-4 text-dimmed transition group-hover:text-bright" />
<ChevronRightIcon className="h-4 w-4 text-text-dimmed transition group-hover:text-text-bright" />
</TableCell>
);
});
@@ -238,7 +238,7 @@ export const TableCellMenu = forwardRef<
<Popover onOpenChange={(open) => setIsOpen(open)}>
<PopoverVerticalEllipseTrigger isOpen={isOpen} />
<PopoverContent
className="w-fit max-w-[10rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
className="w-fit max-w-[10rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
align="end"
>
<div className="flex flex-col gap-1 p-1">{children}</div>
@@ -8,12 +8,12 @@ export type TabsProps = {
to: string;
}[];
className?: string;
layoutId: string
layoutId: string;
};
export function Tabs({ tabs, className, layoutId }: TabsProps) {
return (
<div className={cn(`flex flex-row gap-x-6 border-b border-ui-border`, className)}>
<div className={cn(`flex flex-row gap-x-6 border-b border-grid-bright`, className)}>
{tabs.map((tab, index) => (
<NavLink key={index} to={tab.to} className="group flex flex-col items-center pt-1" end>
{({ isActive, isPending }) => (
@@ -21,7 +21,7 @@ export function Tabs({ tabs, className, layoutId }: TabsProps) {
<span
className={cn(
"text-sm transition duration-200",
isActive || isPending ? "text-indigo-500" : "text-slate-200"
isActive || isPending ? "text-indigo-500" : "text-charcoal-200"
)}
>
{tab.label}
@@ -29,7 +29,7 @@ export function Tabs({ tabs, className, layoutId }: TabsProps) {
{isActive || isPending ? (
<motion.div layoutId={layoutId} className="mt-1 h-0.5 w-full bg-indigo-500" />
) : (
<div className="mt-1 h-0.5 w-full bg-slate-500 opacity-0 transition duration-200 group-hover:opacity-100" />
<div className="mt-1 h-0.5 w-full bg-charcoal-500 opacity-0 transition duration-200 group-hover:opacity-100" />
)}
</>
)}
@@ -8,7 +8,7 @@ export function TextArea({ className, rows, ...props }: TextAreaProps) {
{...props}
rows={rows ?? 6}
className={cn(
"w-full rounded-md border border-slate-800 bg-slate-850 px-3 text-sm text-bright ring-offset-background transition file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-muted-foreground hover:border-slate-750 hover:bg-slate-800 focus:border-ring focus:outline-none focus:ring-2 focus:ring-ring focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50",
"ring-offset-background placeholder:text-muted-foreground focus:border-ring focus:ring-ring focus-visible:ring-ring w-full rounded-md border border-charcoal-800 bg-charcoal-850 px-3 text-sm text-text-bright transition file:border-0 file:bg-transparent file:text-base file:font-medium hover:border-charcoal-750 hover:bg-charcoal-800 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",
className
)}
/>
@@ -6,7 +6,7 @@ const variations = {
primary:
"text-indigo-500 transition hover:text-indigo-400 inline-flex gap-0.5 items-center group",
secondary:
"text-dimmed transition underline underline-offset-2 decoration-dimmed/50 hover:decoration-dimmed inline-flex gap-0.5 items-center group",
"text-text-dimmed transition underline underline-offset-2 decoration-dimmed/50 hover:decoration-dimmed inline-flex gap-0.5 items-center group",
} as const;
type TextLinkProps = {
@@ -0,0 +1,243 @@
import {
Component,
ComponentPropsWithoutRef,
Fragment,
ReactNode,
createContext,
useCallback,
useContext,
useRef,
useState,
} from "react";
interface MousePosition {
x: number;
y: number;
}
const MousePositionContext = createContext<MousePosition | undefined>(undefined);
export function MousePositionProvider({ children }: { children: ReactNode }) {
const ref = useRef<HTMLDivElement>(null);
const [position, setPosition] = useState<MousePosition | undefined>(undefined);
const handleMouseMove = useCallback(
(e: React.MouseEvent) => {
if (!ref.current) {
setPosition(undefined);
return;
}
const { top, left, width, height } = ref.current.getBoundingClientRect();
const x = (e.clientX - left) / width;
const y = (e.clientY - top) / height;
if (x < 0 || x > 1 || y < 0 || y > 1) {
setPosition(undefined);
return;
}
setPosition({ x, y });
},
[ref.current]
);
return (
<div
ref={ref}
onMouseEnter={handleMouseMove}
onMouseLeave={() => setPosition(undefined)}
onMouseMove={handleMouseMove}
style={{ width: "100%", height: "100%" }}
>
<MousePositionContext.Provider value={position}>{children}</MousePositionContext.Provider>
</div>
);
}
export const useMousePosition = () => {
return useContext(MousePositionContext);
};
type TimelineContextState = {
startMs: number;
durationMs: number;
};
const TimelineContext = createContext<TimelineContextState>({} as TimelineContextState);
function useTimeline() {
return useContext(TimelineContext);
}
type TimelineMousePositionContextState = { x: number; y: number } | undefined;
const TimelineMousePositionContext = createContext<TimelineMousePositionContextState>(undefined);
function useTimelineMousePosition() {
return useContext(TimelineMousePositionContext);
}
export type RootProps = {
/** If the timeline doesn't start at zero. Doesn't impact layout but gives you the times back */
startMs?: number;
durationMs: number;
/** A number between 0 and 1, determines the width between min and max */
scale: number;
minWidth: number;
maxWidth: number;
children?: ReactNode;
className?: string;
};
/** The main element that determines the dimensions for all sub-elements */
export function Root({
startMs = 0,
durationMs,
scale,
minWidth,
maxWidth,
children,
className,
}: RootProps) {
const pixelWidth = calculatePixelWidth(minWidth, maxWidth, scale);
return (
<TimelineContext.Provider value={{ startMs, durationMs }}>
<div
className={className}
style={{
position: "relative",
width: `${pixelWidth}px`,
}}
>
<MousePositionProvider>{children}</MousePositionProvider>
</div>
</TimelineContext.Provider>
);
}
export type RowProps = ComponentPropsWithoutRef<"div">;
/** This simply acts as a container, with position relative.
* This allows you to nest "Rows" and put heights on them */
export function Row({ className, children, ...props }: RowProps) {
return (
<div {...props} className={className} style={{ ...props.style, position: "relative" }}>
{children}
</div>
);
}
export type PointProps = {
ms: number;
className?: string;
children?: (ms: number) => ReactNode;
};
/** A point in time, it has no duration */
export function Point({ ms, className, children }: PointProps) {
const { startMs, durationMs } = useTimeline();
const position = inverseLerp(startMs, startMs + durationMs, ms);
return (
<div
className={className}
style={{
position: "absolute",
left: `${position * 100}%`,
}}
>
{children && children(ms)}
</div>
);
}
export type SpanProps = {
startMs: number;
durationMs: number;
className?: string;
children?: ReactNode;
};
/** As span of time with a start and duration */
export function Span({ startMs, durationMs, className, children }: SpanProps) {
const { startMs: rootStartMs, durationMs: rootDurationMs } = useTimeline();
const position = inverseLerp(rootStartMs, rootStartMs + rootDurationMs, startMs);
const width =
inverseLerp(rootStartMs, rootStartMs + rootDurationMs, startMs + durationMs) - position;
return (
<div
className={className}
style={{
position: "absolute",
left: `${position * 100}%`,
width: `${width * 100}%`,
}}
>
{children}
</div>
);
}
export type EquallyDistributeProps = {
count: number;
children: (ms: number, index: number) => ReactNode;
};
/** Render a child equally distributed across the duration */
export function EquallyDistribute({ count, children }: EquallyDistributeProps) {
const { startMs, durationMs } = useTimeline();
return (
<>
{Array.from({ length: count }).map((_, index) => {
const ms = startMs + (durationMs / (count - 1)) * index;
return <Fragment key={index}>{children(ms, index)}</Fragment>;
})}
</>
);
}
export type FollowCursorProps = {
children: (ms: number) => ReactNode;
};
/** Renders a child that follows the cursor */
export function FollowCursor({ children }: FollowCursorProps) {
const { startMs, durationMs } = useTimeline();
const relativeMousePosition = useMousePosition();
const ms = relativeMousePosition?.x
? lerp(startMs, startMs + durationMs, relativeMousePosition.x)
: undefined;
if (ms === undefined) return null;
return (
<div
style={{
position: "absolute",
top: 0,
left: relativeMousePosition ? `${relativeMousePosition?.x * 100}%` : 0,
height: "100%",
pointerEvents: "none",
}}
>
{children(ms)}
</div>
);
}
/** Gives the total width of the root */
function calculatePixelWidth(minWidth: number, maxWidth: number, scale: number) {
return lerp(minWidth, maxWidth, scale);
}
/** Linearly interpolates between the min/max values, using t.
* It can't go outside the range */
function lerp(min: number, max: number, t: number) {
return min + (max - min) * clamp(t, 0, 1);
}
/** Inverse lerp */
function inverseLerp(min: number, max: number, value: number) {
return (value - min) / (max - min);
}
/** Clamps a value between a min and max */
function clamp(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value));
}
@@ -39,7 +39,7 @@ export function ToastUI({
}) {
return (
<div
className={`self-end rounded-md border border-border bg-background`}
className={`self-end rounded-md border border-grid-bright bg-background-dimmed`}
style={{
width: toastWidth,
}}
@@ -50,9 +50,9 @@ export function ToastUI({
) : (
<ExclamationCircleIcon className="mt-1 h-6 w-6 min-w-[1.5rem] text-rose-600" />
)}
<Paragraph className="py-1 text-dimmed">{message}</Paragraph>
<Paragraph className="py-1 text-text-dimmed">{message}</Paragraph>
<button
className="ms-auto rounded p-2 text-dimmed transition hover:bg-midnight-800 hover:text-bright"
className="hover:bg-midnight-800 ms-auto rounded p-2 text-text-dimmed transition hover:text-text-bright"
onClick={() => toast.dismiss(t)}
>
<XMarkIcon className="h-4 w-4" />
@@ -4,8 +4,8 @@ import { cn } from "~/utils/cn";
const variantClasses = {
basic:
"bg-popover border border-slate-800 rounded-md px-3 py-1.5 text-sm text-bright shadow-md fade-in-50",
dark: "bg-background border border-border rounded px-3 py-2 text-sm text-bright shadow-md fade-in-50",
"bg-background-dimmed border border-charcoal-800 rounded-md px-3 py-1.5 text-sm text-text-bright shadow-md fade-in-50",
dark: "bg-background-dimmed border border-grid-bright rounded px-3 py-2 text-sm text-text-bright shadow-md fade-in-50",
};
type Variant = keyof typeof variantClasses;
@@ -15,7 +15,7 @@ const TooltipProvider = TooltipPrimitive.Provider;
const TooltipArrow = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Arrow>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Arrow>
>(({ ...props }, ref) => <TooltipPrimitive.Arrow className="z-50 fill-popover" {...props} />);
>(({ ...props }, ref) => <TooltipPrimitive.Arrow className="fill-popover z-50" {...props} />);
TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName;
const Tooltip = React.forwardRef<
@@ -98,7 +98,7 @@ export function LoginTooltip({
<TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipContent
className={cn(
"max-w-xs border-slate-800 bg-slate-900 px-5 py-4 backdrop-blur-md",
"max-w-xs border-charcoal-800 bg-charcoal-900 px-5 py-4 backdrop-blur-md",
className
)}
side={side}
@@ -1,5 +1,15 @@
import { VirtualItem, Virtualizer, useVirtualizer } from "@tanstack/react-virtual";
import { Fragment, RefObject, useCallback, useEffect, useReducer, useRef, useState } from "react";
import {
Fragment,
MutableRefObject,
RefObject,
useCallback,
useEffect,
useImperativeHandle,
useReducer,
useRef,
useState,
} from "react";
import { UnmountClosed } from "react-collapse";
import { cn } from "~/utils/cn";
import { Changes, NodeState, NodesState, reducer } from "./reducer";
@@ -14,7 +24,6 @@ import {
export type TreeViewProps<TData> = {
tree: FlatTree<TData>;
parentClassName?: string;
renderNode: (params: {
node: FlatTreeItem<TData>;
state: NodeState;
@@ -25,7 +34,9 @@ export type TreeViewProps<TData> = {
nodes: UseTreeStateOutput["nodes"];
autoFocus?: boolean;
virtualizer: Virtualizer<HTMLElement, Element>;
parentRef: RefObject<any>;
parentRef: MutableRefObject<HTMLElement | null>;
scrollRef?: MutableRefObject<HTMLElement | null>;
onScroll?: (scrollTop: number) => void;
} & Pick<UseTreeStateOutput, "getTreeProps" | "getNodeProps">;
export function TreeView<TData>({
@@ -38,20 +49,43 @@ export function TreeView<TData>({
parentClassName,
virtualizer,
parentRef,
scrollRef,
onScroll,
}: TreeViewProps<TData>) {
useEffect(() => {
if (autoFocus) {
parentRef.current?.focus();
}
}, [autoFocus, parentRef]);
}, [autoFocus, parentRef.current]);
const virtualItems = virtualizer.getVirtualItems();
const scrollCallback = useCallback(
(event: Event) => {
if (!onScroll) return;
const target = event.target as HTMLElement;
onScroll?.(target.scrollTop);
},
[onScroll]
);
useEffect(() => {
//subscribe to parentRef scroll event
if (!scrollRef?.current || onScroll === undefined) return;
scrollRef.current.addEventListener("scroll", scrollCallback);
return () => scrollRef.current?.removeEventListener("scroll", scrollCallback);
}, [scrollRef?.current]);
return (
<div
ref={parentRef}
ref={(element) => {
parentRef.current = element;
if (scrollRef) {
scrollRef.current = element;
}
}}
className={cn(
"w-full overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700 focus-within:outline-none",
"w-full overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600 focus-within:outline-none",
parentClassName
)}
{...getTreeProps()}
@@ -75,7 +109,10 @@ export function TreeView<TData>({
}}
>
{virtualItems.map((virtualItem) => {
const node = tree.find((node) => node.id === virtualItem.key)!;
const node = tree.find((node) => node.id === virtualItem.key);
if (!node) return null;
const state = nodes[node.id];
if (!state) return null;
return (
<div
key={node.id}
@@ -84,10 +121,10 @@ export function TreeView<TData>({
className="overflow-clip [&_.ReactCollapse--collapse]:transition-all"
{...getNodeProps(node.id)}
>
<UnmountClosed key={node.id} isOpened={nodes[node.id].visible}>
<UnmountClosed key={node.id} isOpened={state.visible}>
{renderNode({
node,
state: nodes[node.id],
state,
index: virtualItem.index,
virtualizer: virtualizer,
virtualItem,
@@ -155,6 +192,7 @@ export function useTree<TData>({
estimatedRowHeight,
filter,
}: TreeStateHookProps<TData>): UseTreeStateOutput {
const previousNodeCount = useRef(tree.length);
const previousSelectedId = useRef<string | undefined>(selectedId);
const [state, dispatch] = useReducer(
@@ -176,6 +214,13 @@ export function useTree<TData>({
}
}, [state.changes.collapsedIds]);
useEffect(() => {
if (tree.length !== previousNodeCount.current) {
previousNodeCount.current = tree.length;
dispatch({ type: "UPDATE_TREE", payload: { tree } });
}
}, [previousNodeCount.current, tree.length]);
const virtualizer = useVirtualizer({
count: tree.length,
getItemKey: (index) => tree[index].id,
@@ -365,6 +410,7 @@ export function useTree<TData>({
const getNodeProps = useCallback(
(id: string) => {
const node = state.nodes[id];
if (!node) return {};
const treeItemIndex = tree.findIndex((node) => node.id === id);
const treeItem = tree[treeItemIndex];
return {
@@ -1,6 +1,8 @@
import { FlatTree } from "./TreeView";
import {
applyVisibility,
collapsedIdsFromState,
concreteStateFromInput,
firstVisibleNode,
generateChanges,
lastVisibleNode,
@@ -343,6 +345,17 @@ export function reducer(state: TreeState, action: Action): TreeState {
return state;
}
case "UPDATE_TREE": {
//update the tree but try and keep the selected and expanded states
const selectedId = selectedIdFromState(state.nodes);
const collapsedIds = collapsedIdsFromState(state.nodes);
const newState = concreteStateFromInput({
tree: action.payload.tree,
selectedId,
collapsedIds,
});
return newState;
}
}
throw new Error(`Unhandled action type: ${(action as any).type}`);
@@ -195,7 +195,7 @@ function difference<T>(a: Set<T>, b: Set<T>): Set<T> {
return new Set([...a].filter((x) => !b.has(x)));
}
function collapsedIdsFromState(state: NodesState): string[] {
export function collapsedIdsFromState(state: NodesState): string[] {
return Object.entries(state)
.filter(([_, s]) => s.expanded === false)
.map(([id]) => id);
+10 -8
View File
@@ -29,14 +29,14 @@ export function RunPanel({
<div
className={cn(
"overflow-hidden rounded-md border transition duration-150",
styleName === "normal" && "bg-slate-900",
styleName === "normal" && "bg-charcoal-900",
selected
? "border-green-500"
: styleName === "normal"
? "border-slate-850"
: "border-slate-900",
? "border-charcoal-850"
: "border-charcoal-900",
onClick && "cursor-pointer",
onClick && !selected && "hover:border-slate-500/30",
onClick && !selected && "hover:border-charcoal-500/30",
className
)}
onClick={() => onClick && onClick()}
@@ -63,7 +63,9 @@ export function RunPanelHeader({
<div
className={cn(
"flex items-center justify-between px-2",
styleName === "normal" ? "h-10 border-b border-slate-850 bg-midnight-850 py-2" : "pt-2"
styleName === "normal"
? "h-10 border-b border-charcoal-850 bg-background-bright py-2"
: "pt-2"
)}
>
<div className="flex items-center gap-2">
@@ -140,7 +142,7 @@ export function RunPanelIconSection({
}
export function RunPanelDivider() {
return <div className="mb-4 border-b border-slate-700 pb-4" />;
return <div className="mb-4 border-b border-charcoal-700 pb-4" />;
}
export function RunPanelIconProperty({
@@ -154,7 +156,7 @@ export function RunPanelIconProperty({
}) {
return (
<div className="flex items-center gap-2">
<div className="flex h-8 w-8 items-center justify-center rounded-sm border border-slate-800 bg-slate-850">
<div className="flex h-8 w-8 items-center justify-center rounded-sm border border-charcoal-800 bg-charcoal-850">
{typeof icon === "string" ? <NamedIcon name={icon} className="h-5 w-5" /> : icon}
</div>
<div className="flex flex-col gap-0.5">
@@ -191,7 +193,7 @@ export function RunPanelProperties({
export function TaskSeparator({ depth }: { depth: number }) {
return (
<div className="h-4 w-4 border-r border-slate-600" style={{ marginLeft: `${depth}rem` }} />
<div className="h-4 w-4 border-r border-charcoal-600" style={{ marginLeft: `${depth}rem` }} />
);
}
+78 -79
View File
@@ -29,13 +29,12 @@ import { Header2 } from "../primitives/Headers";
import { Icon } from "../primitives/Icon";
import { NamedIcon } from "../primitives/NamedIcon";
import {
PageButtons,
PageHeader,
PageAccessories,
NavBar,
PageInfoGroup,
PageInfoProperty,
PageInfoRow,
PageTitle,
PageTitleRow,
} from "../primitives/PageHeader";
import { Paragraph } from "../primitives/Paragraph";
import { Popover, PopoverContent, PopoverTrigger } from "../primitives/Popover";
@@ -96,79 +95,79 @@ export function RunOverview({ run, trigger, showRerun, paths, currentUser }: Run
return (
<PageContainer>
<PageHeader>
<PageTitleRow>
<PageTitle
backButton={{
to: paths.back,
text: "Runs",
}}
title={
typeof run.number === "number" ? `Run #${run.number}` : `Run ${run.id.slice(0, 8)}`
}
/>
<PageButtons>
{run.isTest && (
<span className="flex items-center gap-1 text-xs uppercase text-slate-600">
<NamedIcon name="beaker" className="h-4 w-4 text-slate-600" />
Test run
</span>
)}
{showRerun && run.isFinished && (
<RerunPopover
runId={run.id}
runPath={paths.run}
runsPath={paths.runsPath}
environmentType={run.environment.type}
status={run.basicStatus}
/>
)}
{!run.isFinished && <CancelRun runId={run.id} />}
</PageButtons>
</PageTitleRow>
<PageInfoRow>
<PageInfoGroup>
<PageInfoProperty
icon={<RunStatusIcon status={run.status} className="h-4 w-4" />}
label={"Status"}
value={runStatusTitle(run.status)}
<NavBar>
<PageTitle
backButton={{
to: paths.back,
text: "Runs",
}}
title={
typeof run.number === "number" ? `Run #${run.number}` : `Run ${run.id.slice(0, 8)}`
}
/>
<PageAccessories>
{run.isTest && (
<span className="flex items-center gap-1 text-xs uppercase text-charcoal-600">
<NamedIcon name="beaker" className="h-4 w-4 text-charcoal-600" />
Test run
</span>
)}
{showRerun && run.isFinished && (
<RerunPopover
runId={run.id}
runPath={paths.run}
runsPath={paths.runsPath}
environmentType={run.environment.type}
status={run.basicStatus}
/>
<PageInfoProperty
icon={"calendar"}
label={"Started"}
value={run.startedAt ? <DateTime date={run.startedAt} /> : "Not started yet"}
/>
<PageInfoProperty icon={"property"} label={"Version"} value={`v${run.version}`} />
<PageInfoProperty
label={"Env"}
value={<EnvironmentLabel environment={run.environment} userName={usernameForEnv} />}
/>
<PageInfoProperty
icon={"clock"}
label={"Duration"}
value={formatDuration(run.startedAt, run.completedAt, { style: "short" })}
/>
<PageInfoProperty
icon={<Icon icon="alarm-filled" className="h-4 w-4 text-blue-500" />}
label={"Execution Time"}
value={formatDurationMilliseconds(run.executionDuration, { style: "short" })}
/>
<PageInfoProperty
icon={<Icon icon="list-numbers" className="h-4 w-4 text-yellow-500" />}
label={"Execution Count"}
value={<>{run.executionCount}</>}
/>
</PageInfoGroup>
<PageInfoGroup alignment="right">
<Paragraph variant="extra-small" className="whitespace-nowrap text-slate-600">
RUN ID: {run.id}
</Paragraph>
</PageInfoGroup>
</PageInfoRow>
</PageHeader>
)}
{!run.isFinished && <CancelRun runId={run.id} />}
</PageAccessories>
</NavBar>
<PageBody scrollable={false}>
<div className="px-4 pt-4">
<PageInfoRow>
<PageInfoGroup>
<PageInfoProperty
icon={<RunStatusIcon status={run.status} className="h-4 w-4" />}
label={"Status"}
value={runStatusTitle(run.status)}
/>
<PageInfoProperty
icon={"calendar"}
label={"Started"}
value={run.startedAt ? <DateTime date={run.startedAt} /> : "Not started yet"}
/>
<PageInfoProperty icon={"property"} label={"Version"} value={`v${run.version}`} />
<PageInfoProperty
label={"Env"}
value={<EnvironmentLabel environment={run.environment} userName={usernameForEnv} />}
/>
<PageInfoProperty
icon={"clock"}
label={"Duration"}
value={formatDuration(run.startedAt, run.completedAt, { style: "short" })}
/>
<PageInfoProperty
icon={<Icon icon="alarm-filled" className="h-4 w-4 text-blue-500" />}
label={"Execution Time"}
value={formatDurationMilliseconds(run.executionDuration, { style: "short" })}
/>
<PageInfoProperty
icon={<Icon icon="list-numbers" className="h-4 w-4 text-yellow-500" />}
label={"Execution Count"}
value={<>{run.executionCount}</>}
/>
</PageInfoGroup>
<PageInfoGroup alignment="right">
<Paragraph variant="extra-small" className="whitespace-nowrap text-charcoal-600">
RUN ID: {run.id}
</Paragraph>
</PageInfoGroup>
</PageInfoRow>
</div>
<div className="grid h-full grid-cols-2 gap-2">
<div className="flex flex-col gap-6 overflow-y-auto py-4 pl-4 pr-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700">
<div className="flex flex-col gap-6 overflow-y-auto py-4 pl-4 pr-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<div>
{run.status === "SUCCESS" &&
(run.tasks.length === 0 || run.tasks.every((t) => t.noop)) && (
@@ -285,7 +284,7 @@ export function RunOverview({ run, trigger, showRerun, paths, currentUser }: Run
</div>
{/* Detail view */}
<div className="overflow-y-auto py-4 pr-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700">
<div className="overflow-y-auto py-4 pr-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<Header2 className="mb-2">Details</Header2>
{selectedId ? <Outlet /> : <Callout variant="info">Select a task or trigger</Callout>}
</div>
@@ -359,7 +358,7 @@ function RerunPopover({
</div>
)}
<div className="flex flex-col items-start divide-y divide-slate-800">
<div className="flex flex-col items-start divide-y divide-charcoal-800">
<div className="p-4">
<Paragraph variant="small" className="mb-3">
Start a brand new Job run with the same Trigger data as this one. This will re-do
@@ -371,9 +370,9 @@ function RerunPopover({
name={conform.INTENT}
value="start"
fullWidth
className="text-bright"
className="text-text-bright"
>
<BoltIcon className="mr-1 h-3.5 w-3.5 text-bright" />
<BoltIcon className="mr-1 h-3.5 w-3.5 text-text-bright" />
Run again
</Button>
</div>
@@ -389,9 +388,9 @@ function RerunPopover({
name={conform.INTENT}
value="continue"
fullWidth
className="text-bright"
className="text-text-bright"
>
<PlayIcon className="mr-1 h-3.5 w-3.5 text-bright" />
<PlayIcon className="mr-1 h-3.5 w-3.5 text-text-bright" />
Retry Job run
</Button>
</div>
@@ -38,7 +38,7 @@ function taskAttemptStatusClassNameColor(status: TaskAttemptStatus): string {
case "COMPLETED":
return "text-green-500";
case "PENDING":
return "text-slate-400";
return "text-charcoal-400";
case "STARTED":
return "text-blue-500";
case "ERRORED":
+4 -4
View File
@@ -64,7 +64,7 @@ export function TaskCard({
<TaskStatusIcon
status={status}
minimal={true}
className={cn("h-5 w-5", !isSelected && "text-slate-400")}
className={cn("h-5 w-5", !isSelected && "text-charcoal-400")}
/>
)
}
@@ -112,11 +112,11 @@ export function TaskCard({
</RunPanelBody>
{subtasks && subtasks.length > 0 && (
<button
className="mt-4 flex h-10 w-full items-center justify-between gap-2 bg-slate-800 px-2"
className="mt-4 flex h-10 w-full items-center justify-between gap-2 bg-charcoal-800 px-2"
onClick={() => setExpanded((c) => !c)}
>
<div className="flex items-center gap-2">
<Square2StackIcon className="h-5 w-5 text-slate-400" />
<Square2StackIcon className="h-5 w-5 text-charcoal-400" />
<Paragraph variant="small">
{simplur`${expanded ? "Hide" : "Show"} ${subtasks.length} subtask[|s]`}
</Paragraph>
@@ -129,7 +129,7 @@ export function TaskCard({
expanded: { rotate: 180, transition: { ease: "anticipate" } },
}}
>
<ChevronDownIcon className={"h-5 w-5 text-slate-400 transition"} />
<ChevronDownIcon className={"h-5 w-5 text-charcoal-400 transition"} />
</motion.span>
</button>
)}
@@ -5,15 +5,15 @@ export function TaskCardSkeleton() {
<RunPanel>
<RunPanelHeader
icon={undefined}
title={<div className="h-5 w-36 max-w-full rounded border bg-slate-800" />}
title={<div className="h-5 w-36 max-w-full rounded border bg-charcoal-800" />}
/>
<RunPanelBody>
<div className="flex w-full flex-col justify-between gap-y-1">
<div className="flex items-baseline justify-between gap-x-3 pr-3 md:justify-start md:pr-0">
<div className="h-5 w-1/4 rounded border bg-slate-800" />
<div className="h-5 w-1/4 rounded border bg-charcoal-800" />
</div>
<div className="flex flex-wrap items-center gap-x-4 gap-y-1">
<div className="h-2.5 w-1/2 rounded border bg-slate-800" />
<div className="h-2.5 w-1/2 rounded border bg-charcoal-800" />
</div>
</div>
</RunPanelBody>
@@ -41,7 +41,7 @@ function taskStatusClassNameColor(status: TaskStatus): string {
case "COMPLETED":
return "text-green-500";
case "PENDING":
return "text-slate-500";
return "text-charcoal-500";
case "RUNNING":
return "text-blue-500";
case "WAITING":
@@ -49,6 +49,6 @@ function taskStatusClassNameColor(status: TaskStatus): string {
case "ERRORED":
return "text-rose-500";
case "CANCELED":
return "text-slate-500";
return "text-charcoal-500";
}
}
@@ -141,7 +141,7 @@ export function RunsFilters() {
<TimeFrameFilter from={from} to={to} onRangeChanged={handleTimeFrameChange} />
<Button variant="tertiary/small" onClick={() => clearFilters()} LeadingIcon={"close"}>
<Button variant="minimal/small" onClick={() => clearFilters()} LeadingIcon={"close"}>
Clear
</Button>
</div>
@@ -209,7 +209,7 @@ export function filterStatusTitle(status: FilterableStatus): string {
export function filterStatusClassNameColor(status: FilterableStatus): string {
switch (status) {
case "QUEUED":
return "text-slate-500";
return "text-charcoal-500";
case "IN_PROGRESS":
return "text-blue-500";
case "WAITING":
@@ -219,7 +219,7 @@ export function filterStatusClassNameColor(status: FilterableStatus): string {
case "FAILED":
return "text-rose-500";
case "CANCELED":
return "text-slate-500";
return "text-charcoal-500";
case "TIMEDOUT":
return "text-amber-300";
default: {
@@ -100,14 +100,14 @@ export function runStatusClassNameColor(status: JobRunStatus): string {
case "SUCCESS":
return "text-green-500";
case "PENDING":
return "text-slate-500";
return "text-charcoal-500";
case "STARTED":
case "EXECUTING":
case "WAITING_TO_CONTINUE":
case "WAITING_TO_EXECUTE":
return "text-blue-500";
case "QUEUED":
return "text-slate-500";
return "text-charcoal-500";
case "FAILURE":
case "UNRESOLVED_AUTH":
case "INVALID_PAYLOAD":
@@ -121,7 +121,7 @@ export function runStatusClassNameColor(status: JobRunStatus): string {
case "PREPROCESSING":
return "text-blue-500";
case "CANCELED":
return "text-slate-500";
return "text-charcoal-500";
default: {
const _exhaustiveCheck: never = status;
throw new Error(`Non-exhaustive match for value: ${status}`);
@@ -117,9 +117,9 @@ export function RunsTable({
</TableCell>
<TableCell to={path}>
{run.isTest ? (
<CheckIcon className="h-4 w-4 text-slate-400" />
<CheckIcon className="h-4 w-4 text-charcoal-400" />
) : (
<StopIcon className="h-4 w-4 text-slate-850" />
<StopIcon className="h-4 w-4 text-charcoal-850" />
)}
</TableCell>
<TableCell to={path}>{run.version}</TableCell>
@@ -134,9 +134,9 @@ export function RunsTable({
{isLoading && (
<TableBlankRow
colSpan={8}
className="absolute left-0 top-0 flex h-full w-full items-center justify-center gap-2 bg-slate-900/90"
className="absolute left-0 top-0 flex h-full w-full items-center justify-center gap-2 bg-charcoal-900/90"
>
<Spinner /> <span className="text-dimmed">Loading</span>
<Spinner /> <span className="text-text-dimmed">Loading</span>
</TableBlankRow>
)}
</TableBody>

Some files were not shown because too many files have changed in this diff Show More