bee59de3a0
* Don't use the organization max concurrency anymore * Early draft of the concurrency page * WIP adding a new stepper input component * Move stepper to be alphabetical * When max value is reached, disabled the + button * Show placeholder if you delete all numbers * Make all the html input values available to the component * Adds size variants * Move stepper into its own component * Work on showing the extra concurrency * The purchase form styling and functionality (minus actually purchasing) * New style for outline input fields * Concurrency purchasing working * Purchasing concurrency and quota emails working * Improvements to the modal * Show cost breakdown in the modal * Fix for allocated concurrency including DEV * Improved types * Allocating concurrency is working * Live updates total env concurrency * Implemented reset * Fix for concurrency allocation editing across multiple projects * Tabular numbers * Added an error from allocating concurrency * Fixes for allocating concurrency where it didn't calculate correctly * "Increase limit" link to concurrency page * Indent environments * Added Preview limit when updating concurrency for an org * Show error when changing plan fails * Added maximumProjectCount column to Org * Limit project count and display a rich error toast (with title and button now) * Added title and button to toasts. Use it for new project error * @trigger.dev/platform 1.0.20 * Allow submitting zero concurrency so you can downgrade back to nothing * Use the server as the truth for omitted environments * Updated the pricing panels --------- Co-authored-by: James Ritchie <james@trigger.dev>
126 lines
4.3 KiB
TypeScript
126 lines
4.3 KiB
TypeScript
import * as React from "react";
|
|
import { useImperativeHandle, useRef } from "react";
|
|
import { cn } from "~/utils/cn";
|
|
import { Icon, type RenderIcon } from "./Icon";
|
|
|
|
const containerBase =
|
|
"has-[:focus-visible]:outline-none has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-charcoal-650 has-[:focus-visible]:ring-offset-0 has-[:focus]:border-ring has-[:focus]:outline-none has-[:focus]:ring-1 has-[:focus]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ring-offset-background transition cursor-text";
|
|
|
|
const inputBase =
|
|
"h-full w-full text-text-bright bg-transparent file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 disabled:cursor-not-allowed outline-none ring-0 border-none [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:m-0 [&::-webkit-inner-spin-button]:m-0 [&]:[-moz-appearance:textfield]";
|
|
|
|
const variants = {
|
|
large: {
|
|
container:
|
|
"px-1 w-full h-10 rounded-[3px] border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-600 hover:bg-charcoal-650",
|
|
input: "px-2 text-sm",
|
|
iconSize: "size-4 ml-1",
|
|
accessory: "pr-1",
|
|
},
|
|
medium: {
|
|
container:
|
|
"px-1 h-8 w-full rounded border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-600 hover:bg-charcoal-650",
|
|
input: "px-1.5 rounded text-sm",
|
|
iconSize: "size-4 ml-0.5",
|
|
accessory: "pr-1",
|
|
},
|
|
small: {
|
|
container:
|
|
"px-1 h-6 w-full rounded border border-charcoal-800 bg-charcoal-750 hover:border-charcoal-600 hover:bg-charcoal-650",
|
|
input: "px-1 rounded text-xs",
|
|
iconSize: "size-3 ml-0.5",
|
|
accessory: "pr-0.5",
|
|
},
|
|
tertiary: {
|
|
container: "px-1 h-6 w-full rounded hover:bg-charcoal-750",
|
|
input: "px-1 rounded text-xs",
|
|
iconSize: "size-3 ml-0.5",
|
|
accessory: "pr-0.5",
|
|
},
|
|
"secondary-small": {
|
|
container:
|
|
"px-1 h-6 w-full rounded border border-charcoal-600 hover:border-charcoal-550 bg-grid-dimmed hover:bg-charcoal-650",
|
|
input: "px-1 rounded text-xs",
|
|
iconSize: "size-3 ml-0.5",
|
|
accessory: "pr-0.5",
|
|
},
|
|
"outline/large": {
|
|
container: "px-1 h-10 w-full rounded border border-grid-bright hover:border-charcoal-550",
|
|
input: "px-2 rounded text-sm",
|
|
iconSize: "size-4 ml-1",
|
|
accessory: "pr-1",
|
|
},
|
|
"outline/medium": {
|
|
container: "px-1 h-8 w-full rounded border border-grid-bright hover:border-charcoal-550",
|
|
input: "px-1 rounded text-sm",
|
|
iconSize: "size-4 ml-0.5",
|
|
accessory: "pr-1",
|
|
},
|
|
"outline/small": {
|
|
container: "px-1 h-6 w-full rounded border border-grid-bright hover:border-charcoal-550",
|
|
input: "px-1 rounded text-xs",
|
|
iconSize: "size-3 ml-0.5",
|
|
accessory: "pr-0.5",
|
|
},
|
|
};
|
|
|
|
export type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
|
variant?: keyof typeof variants;
|
|
icon?: RenderIcon;
|
|
accessory?: React.ReactNode;
|
|
fullWidth?: boolean;
|
|
containerClassName?: string;
|
|
};
|
|
|
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
(
|
|
{
|
|
className,
|
|
type,
|
|
accessory,
|
|
fullWidth = true,
|
|
variant = "medium",
|
|
icon,
|
|
containerClassName,
|
|
...props
|
|
},
|
|
ref
|
|
) => {
|
|
const innerRef = useRef<HTMLInputElement>(null);
|
|
useImperativeHandle(ref, () => innerRef.current as HTMLInputElement);
|
|
|
|
const variantContainerClassName = variants[variant].container;
|
|
const inputClassName = variants[variant].input;
|
|
const iconClassName = variants[variant].iconSize;
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"flex items-center",
|
|
containerBase,
|
|
variantContainerClassName,
|
|
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-text-dimmed")} />
|
|
</div>
|
|
)}
|
|
<input
|
|
type={type}
|
|
className={cn("grow", inputBase, inputClassName, className)}
|
|
ref={innerRef}
|
|
{...props}
|
|
/>
|
|
{accessory && <div className={cn(variants[variant].accessory)}>{accessory}</div>}
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
Input.displayName = "Input";
|
|
|
|
export { Input };
|