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:
@@ -27,7 +27,6 @@ const config: StorybookConfig = {
|
||||
"@storybook/addon-interactions",
|
||||
"storybook-addon-variants",
|
||||
"storybook-addon-designs",
|
||||
"@storybook/addon-docs",
|
||||
{
|
||||
name: "@storybook/addon-styling",
|
||||
options: {
|
||||
|
||||
@@ -19,7 +19,7 @@ const preview: Preview = {
|
||||
values: [
|
||||
{
|
||||
name: "App background",
|
||||
value: "#0B1018",
|
||||
value: "#15171A",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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 |
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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` }} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user