This commit is contained in:
DCCONSTRUCTIONS
2026-04-18 18:39:25 +03:00
commit 3ba092b60c
4944 changed files with 497564 additions and 0 deletions
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import { cn } from "../utils";
import type { TButtonVariant, TButtonSizes } from "./helper";
import { getIconStyling, getButtonStyling } from "./helper";
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: TButtonVariant;
size?: TButtonSizes;
className?: string;
loading?: boolean;
disabled?: boolean;
appendIcon?: any;
prependIcon?: any;
children: React.ReactNode;
}
const Button = React.forwardRef(function Button(props: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) {
const {
variant = "primary",
size = "md",
className = "",
type = "button",
loading = false,
disabled = false,
prependIcon = null,
appendIcon = null,
children,
...rest
} = props;
const buttonStyle = getButtonStyling(variant, size, disabled || loading);
const buttonIconStyle = getIconStyling(size);
return (
<button ref={ref} type={type} className={cn(buttonStyle, className)} disabled={disabled || loading} {...rest}>
{prependIcon && <div className={buttonIconStyle}>{React.cloneElement(prependIcon, { strokeWidth: 2 })}</div>}
{children}
{appendIcon && <div className={buttonIconStyle}>{React.cloneElement(appendIcon, { strokeWidth: 2 })}</div>}
</button>
);
});
Button.displayName = "plane-ui-button";
export { Button };
+131
View File
@@ -0,0 +1,131 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
export type TButtonVariant =
| "primary"
| "accent-primary"
| "outline-primary"
| "neutral-primary"
| "link-primary"
| "danger"
| "accent-danger"
| "outline-danger"
| "link-danger"
| "tertiary-danger"
| "link-neutral";
export type TButtonSizes = "sm" | "md" | "lg" | "xl";
export interface IButtonStyling {
[key: string]: {
default: string;
hover: string;
pressed: string;
disabled: string;
};
}
enum buttonSizeStyling {
sm = `px-3 py-1.5 font-medium text-11 rounded-sm flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
md = `px-4 py-1.5 font-medium text-13 rounded-sm flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
lg = `px-5 py-2 font-medium text-13 rounded-sm flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
xl = `px-5 py-3.5 font-medium text-13 rounded-sm flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
}
enum buttonIconStyling {
sm = "h-3 w-3 flex justify-center items-center overflow-hidden my-0.5 flex-shrink-0",
md = "h-3.5 w-3.5 flex justify-center items-center overflow-hidden my-0.5 flex-shrink-0",
lg = "h-4 w-4 flex justify-center items-center overflow-hidden my-0.5 flex-shrink-0",
xl = "h-4 w-4 flex justify-center items-center overflow-hidden my-0.5 flex-shrink-0 ",
}
export const buttonStyling: IButtonStyling = {
primary: {
default: `text-on-color bg-accent-primary`,
hover: `hover:bg-accent-primary/80`,
pressed: `focus:text-custom-brand-40 focus:bg-accent-primary/80`,
disabled: `cursor-not-allowed !bg-layer-1 !text-on-color-disabled`,
},
"accent-primary": {
default: `bg-accent-primary/20 text-accent-primary`,
hover: `hover:bg-accent-primary/10 hover:text-accent-secondary`,
pressed: `focus:bg-accent-primary/10`,
disabled: `cursor-not-allowed !text-accent-primary/60`,
},
"outline-primary": {
default: `text-accent-primary bg-transparent border border-accent-strong`,
hover: `hover:bg-accent-primary/20`,
pressed: `focus:text-accent-primary focus:bg-accent-primary/30`,
disabled: `cursor-not-allowed !text-accent-primary/60 !border-accent-strong-60 `,
},
"neutral-primary": {
default: `text-secondary bg-surface-1 border border-subtle`,
hover: `hover:bg-surface-2`,
pressed: `focus:text-tertiary focus:bg-surface-2`,
disabled: `cursor-not-allowed !bg-layer-1 !text-placeholder`,
},
"link-primary": {
default: `text-accent-primary bg-surface-1`,
hover: `hover:text-accent-secondary`,
pressed: `focus:text-accent-primary/80 `,
disabled: `cursor-not-allowed !text-accent-primary/60`,
},
danger: {
default: `bg-danger-primary text-on-color`,
hover: ` hover:bg-danger-primary-hover`,
pressed: `focus:bg-danger-primary-active`,
disabled: `cursor-not-allowed bg-layer-disabled! text-disabled!`,
},
"accent-danger": {
default: `text-danger-primary bg-red-50`,
hover: `hover:text-danger-primary hover:bg-red-100`,
pressed: `focus:text-danger-primary focus:bg-red-100`,
disabled: `cursor-not-allowed !bg-layer-1 !text-placeholder`,
},
"outline-danger": {
default: `bg-layer-2 text-danger-primary border border-danger-strong`,
hover: `hover:bg-danger-subtle`,
pressed: `focus:bg-danger-subtle-hover`,
disabled: `cursor-not-allowed text-disabled! border-subtle-1!`,
},
"link-danger": {
default: `text-danger-primary bg-surface-1`,
hover: `hover:text-danger-primary`,
pressed: `focus:text-danger-primary`,
disabled: `cursor-not-allowed !bg-layer-1 !text-placeholder`,
},
"tertiary-danger": {
default: `text-danger-primary bg-surface-1 border border-danger-subtle`,
hover: `hover:bg-red-50 hover:border-danger-subtle`,
pressed: `focus:text-danger-primary`,
disabled: `cursor-not-allowed !bg-layer-1 !text-placeholder`,
},
"link-neutral": {
default: `text-tertiary`,
hover: `hover:text-secondary`,
pressed: `focus:text-primary`,
disabled: `cursor-not-allowed !bg-layer-1 !text-placeholder`,
},
};
export const getButtonStyling = (variant: TButtonVariant, size: TButtonSizes, disabled: boolean = false): string => {
let tempVariant: string = ``;
const currentVariant = buttonStyling[variant];
tempVariant = `${currentVariant.default} ${disabled ? currentVariant.disabled : currentVariant.hover} ${
currentVariant.pressed
}`;
let tempSize: string = ``;
if (size) tempSize = buttonSizeStyling[size];
return `${tempVariant} ${tempSize}`;
};
export const getIconStyling = (size: TButtonSizes): string => {
let icon: string = ``;
if (size) icon = buttonIconStyling[size];
return icon;
};
@@ -0,0 +1,9 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
export * from "./button";
export * from "./helper";
export * from "./toggle-switch";
@@ -0,0 +1,62 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { Switch } from "@headlessui/react";
// helpers
import { cn } from "../utils";
interface IToggleSwitchProps {
value: boolean;
onChange: (value: boolean) => void;
label?: string;
size?: "sm" | "md" | "lg";
disabled?: boolean;
className?: string;
}
function ToggleSwitch(props: IToggleSwitchProps) {
const { value, onChange, label, size = "sm", disabled, className } = props;
return (
<Switch
checked={value}
disabled={disabled}
onChange={onChange}
className={cn(
"relative inline-flex h-6 w-10 flex-shrink-0 cursor-pointer rounded-full border border-subtle bg-layer-1 transition-colors duration-200 ease-in-out focus:outline-none",
{
"h-4 w-7": size === "sm",
"h-5 w-9": size === "md",
"bg-accent-primary": value && !disabled,
"bg-(--text-color-icon-placeholder)": !value && !disabled,
"cursor-not-allowed bg-accent-primary opacity-50": value && disabled,
"cursor-not-allowed bg-(--text-color-icon-placeholder) opacity-50": !value && disabled,
},
className
)}
>
<span className="sr-only">{label}</span>
<span
aria-hidden="true"
className={cn(
"inline-block h-5 w-5 transform self-center rounded-full bg-(--text-color-icon-on-color) ring-0 transition duration-200 ease-in-out",
{
"h-3 w-3 translate-x-3.5": size === "sm" && value,
"h-3 w-3 translate-x-0.5": size === "sm" && !value,
"h-4 w-4 translate-x-4": size === "md" && value,
"h-4 w-4 translate-x-0.5": size === "md" && !value,
"translate-x-4": size === "lg" && value,
"translate-x-0.5": size === "lg" && !value,
}
)}
/>
</Switch>
);
}
ToggleSwitch.displayName = "plane-ui-toggle-switch";
export { ToggleSwitch };