Compare commits

...
Author SHA1 Message Date
Wahyu Kurniawan 0f7c6c73c9 ️ feat: make the button component to forward ref 2024-02-20 22:59:31 +07:00
Wahyu Kurniawan 2ac657c32e 🎨 style: add new spacing 2024-02-19 21:16:55 +07:00
Wahyu Kurniawan 943d427db1 📝 docs: add button icon only on component example 2024-02-19 21:16:39 +07:00
Wahyu Kurniawan 7e6b74208d ️ feat: create cloneIcon util function 2024-02-19 21:16:17 +07:00
Wahyu Kurniawan 2b78cab849 ️ feat: create custom icon and plus icon component 2024-02-19 21:15:51 +07:00
Wahyu Kurniawan 862862a9c5 🎨 style: update button and button icon only theme 2024-02-19 21:15:29 +07:00
Wahyu Kurniawan 6a3af491ca Merge branch 'andrehadianto/design-system-components' of https://github.com/snowball-tools/snowballtools-base into ayungavis/T-4834-button 2024-02-19 20:20:31 +07:00
Wahyu Kurniawan 92f7d07f3f 🔧 chore: install tailwind-variants and @fontsource/inter 2024-02-19 20:16:25 +07:00
Wahyu Kurniawan 6fb94aeb11 🔧 chore: add absolute path 2024-02-19 20:15:56 +07:00
Wahyu Kurniawan 1c848c5a89 🎨 style: add inter font family 2024-02-19 20:15:32 +07:00
Wahyu Kurniawan 17b5ff9a74 🎨 style: add colors, font family, and shadow 2024-02-19 20:14:49 +07:00
Wahyu Kurniawan 56adfc4806 🐛 fix: typescript error 2024-02-19 20:14:23 +07:00
Wahyu Kurniawan b51d050ee6 📝 docs: add button to the example comopnent pag 2024-02-19 20:13:56 +07:00
Wahyu Kurniawan 8021ff7ff9 🔧 chore: add vscode settings on frontend package 2024-02-19 20:13:36 +07:00
Wahyu Kurniawan e850435c2d ️ feat: create button component 2024-02-19 20:13:11 +07:00
Zachery 8a43698c43 Merge pull request #75 from snowball-tools/zachery/T-4843-focus-ring-class
feat: focus ring class
2024-02-19 20:55:52 +08:00
Zachery Ng 499dba2c5e chore: add full palette colors 2024-02-19 20:12:25 +08:00
Zachery Ng d498ba7e9a feat: focus ring class 2024-02-19 20:05:29 +08:00
Andre H 082353e9bc ️ feat: implement components page to act as storybook 2024-02-16 12:31:13 +07:00
17 changed files with 727 additions and 5 deletions
+39
View File
@@ -0,0 +1,39 @@
{
// eslint extension options
"eslint.enable": true,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"css.customData": [".vscode/tailwind.json"],
// prettier extension setting
"editor.formatOnSave": true,
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.rulers": [80],
"editor.codeActionsOnSave": [
"source.addMissingImports",
"source.fixAll",
"source.organizeImports"
],
// Show in vscode "Problems" tab when there are errors
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
// Use absolute import for typescript files
"typescript.preferences.importModuleSpecifier": "non-relative",
// IntelliSense for taiwind variants
"tailwindCSS.experimental.classRegex": [
["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
]
}
+2
View File
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@fontsource/inter": "^5.0.16",
"@material-tailwind/react": "^2.1.7",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
@@ -29,6 +30,7 @@
"react-router-dom": "^6.20.1",
"react-scripts": "5.0.1",
"react-timer-hook": "^3.0.7",
"tailwind-variants": "^0.2.0",
"typescript": "^4.9.5",
"usehooks-ts": "^2.10.0",
"vertical-stepper-nav": "^1.0.2",
@@ -1,9 +1,8 @@
import React, { useState } from 'react';
import { Card, Collapse, Typography } from '@material-tailwind/react';
import { Environment, EnvironmentVariable } from 'gql-client/dist/src/types';
import EditEnvironmentVariableRow from './EditEnvironmentVariableRow';
import { Environment, EnvironmentVariable } from 'gql-client';
interface DisplayEnvironmentVariablesProps {
environment: Environment;
@@ -0,0 +1,162 @@
import { tv } from 'tailwind-variants';
import type { VariantProps } from 'tailwind-variants';
/**
* Defines the theme for a button component.
*/
export const buttonTheme = tv(
{
base: [
'h-fit',
'inline-flex',
'items-center',
'justify-center',
'whitespace-nowrap',
'focus-ring',
'disabled:cursor-not-allowed',
],
variants: {
size: {
lg: ['gap-2', 'py-3.5', 'px-5', 'text-base', 'tracking-[-0.011em]'],
md: ['gap-2', 'py-3.25', 'px-5', 'text-sm', 'tracking-[-0.006em]'],
sm: ['gap-1', 'py-2', 'px-3', 'text-xs'],
xs: ['gap-1', 'py-1', 'px-2', 'text-xs'],
},
fullWidth: {
true: 'w-full',
},
shape: {
default: '',
rounded: 'rounded-full',
},
iconOnly: {
true: '',
},
variant: {
primary: [
'text-elements-on-primary',
'border',
'border-transparent',
'bg-controls-primary',
'shadow-button',
'hover:bg-controls-primary-hovered',
'focus-visible:bg-controls-primary-hovered',
'disabled:text-elements-on-disabled',
'disabled:bg-controls-disabled',
'disabled:border-transparent',
'disabled:shadow-none',
],
secondary: [
'text-elements-on-secondary',
'border',
'border-transparent',
'bg-controls-secondary',
'hover:bg-controls-secondary-hovered',
'focus-visible:bg-controls-secondary-hovered',
'disabled:text-elements-on-disabled',
'disabled:bg-controls-disabled',
'disabled:border-transparent',
'disabled:shadow-none',
],
tertiary: [
'text-elements-on-tertiary',
'border',
'border-border-interactive/10',
'bg-transparent',
'hover:bg-controls-tertiary-hovered',
'hover:border-border-interactive-hovered',
'hover:border-border-interactive-hovered/[0.14]',
'focus-visible:bg-controls-tertiary-hovered',
'focus-visible:border-border-interactive-hovered',
'focus-visible:border-border-interactive-hovered/[0.14]',
'disabled:text-elements-on-disabled',
'disabled:bg-controls-disabled',
'disabled:border-transparent',
'disabled:shadow-none',
],
ghost: [
'text-elements-on-tertiary',
'border',
'border-transparent',
'bg-transparent',
'hover:bg-controls-tertiary-hovered',
'hover:border-border-interactive-hovered',
'hover:border-border-interactive-hovered/[0.14]',
'focus-visible:bg-controls-tertiary-hovered',
'focus-visible:border-border-interactive-hovered',
'focus-visible:border-border-interactive-hovered/[0.14]',
'disabled:text-elements-on-disabled',
'disabled:bg-controls-disabled',
'disabled:border-transparent',
'disabled:shadow-none',
],
danger: [
'text-elements-on-danger',
'border',
'border-transparent',
'bg-border-danger',
'hover:bg-controls-danger-hovered',
'focus-visible:bg-controls-danger-hovered',
'disabled:text-elements-on-disabled',
'disabled:bg-controls-disabled',
'disabled:border-transparent',
'disabled:shadow-none',
],
'danger-ghost': [
'text-elements-danger',
'border',
'border-transparent',
'bg-transparent',
'hover:bg-controls-tertiary-hovered',
'hover:border-border-interactive-hovered',
'hover:border-border-interactive-hovered/[0.14]',
'focus-visible:bg-controls-tertiary-hovered',
'focus-visible:border-border-interactive-hovered',
'focus-visible:border-border-interactive-hovered/[0.14]',
'disabled:text-elements-on-disabled',
'disabled:bg-controls-disabled',
'disabled:border-transparent',
'disabled:shadow-none',
],
unstyled: [],
},
},
compoundVariants: [
{
size: 'lg',
iconOnly: true,
class: ['py-3.5', 'px-3.5'],
},
{
size: 'md',
iconOnly: true,
class: ['py-3.25', 'px-3.25'],
},
{
size: 'sm',
iconOnly: true,
class: ['py-2', 'px-2'],
},
{
size: 'xs',
iconOnly: true,
class: ['py-1', 'px-1'],
},
],
defaultVariants: {
size: 'md',
variant: 'primary',
fullWidth: false,
iconOnly: false,
shape: 'rounded',
},
},
{
responsiveVariants: true,
},
);
/**
* Represents the type of a button theme, which is derived from the `buttonTheme` variant props.
*/
export type ButtonTheme = VariantProps<typeof buttonTheme>;
@@ -0,0 +1,178 @@
import React, { forwardRef, useCallback } from 'react';
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
import { buttonTheme } from './Button.theme';
import type { ButtonTheme } from './Button.theme';
import { Link } from 'react-router-dom';
import { cloneIcon } from 'utils/cloneIcon';
/**
* Represents the properties of a base button component.
*/
interface ButtonBaseProps {
/**
* The optional left icon element for a component.
* @type {ReactNode}
*/
leftIcon?: ReactNode;
/**
* The optional right icon element to display.
* @type {ReactNode}
*/
rightIcon?: ReactNode;
}
/**
* Interface for the props of a button link component.
*/
export interface ButtonLinkProps
extends Omit<ComponentPropsWithoutRef<'a'>, 'color'> {
/**
* Specifies the optional property `as` with a value of `'a'`.
* @type {'a'}
*/
as?: 'a';
/**
* Indicates whether the item is external or not.
* @type {boolean}
*/
external?: boolean;
/**
* The URL of a web page or resource.
* @type {string}
*/
href: string;
}
export interface ButtonProps
extends Omit<ComponentPropsWithoutRef<'button'>, 'color'> {
/**
* Specifies the optional property `as` with a value of `'button'`.
* @type {'button'}
*/
as?: 'button';
}
/**
* Interface representing the props for a button component.
* Extends the ComponentPropsWithoutRef<'button'> and ButtonTheme interfaces.
*/
export type ButtonOrLinkProps = (ButtonLinkProps | ButtonProps) &
ButtonBaseProps &
ButtonTheme;
/**
* A custom button component that can be used in React applications.
*/
const Button = forwardRef<
HTMLButtonElement | HTMLAnchorElement,
ButtonOrLinkProps
>(
(
{
children,
className,
leftIcon,
rightIcon,
fullWidth,
iconOnly,
shape,
variant,
...props
},
ref,
) => {
// Conditionally render between <NextLink>, <a> or <button> depending on props
// useCallback to prevent unnecessary re-rendering
const Component = useCallback(
({ children: _children, ..._props }: ButtonOrLinkProps) => {
if (_props.as === 'a') {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { external, href, as, ...baseLinkProps } = _props;
// External link
if (external) {
const externalLinkProps = {
target: '_blank',
rel: 'noopener',
href,
...baseLinkProps,
};
return <a {...externalLinkProps}>{_children}</a>;
}
// Internal link
return (
<Link {...baseLinkProps} to={href}>
{_children}
</Link>
);
} else {
const { ...buttonProps } = _props;
// @ts-expect-error - as prop is not a valid prop for button elements
return <button {...buttonProps}>{_children}</button>;
}
},
[],
);
/**
* Extracts specific style properties from the given props object and returns them as a new object.
*/
const styleProps = (({
variant = 'primary',
size = 'md',
fullWidth = false,
iconOnly = false,
shape = 'rounded',
as,
}) => ({
variant,
size,
fullWidth,
iconOnly,
shape,
as,
}))({ ...props, fullWidth, iconOnly, shape, variant });
/**
* Validates that a button component has either children or an aria-label prop.
*/
if (typeof children === 'undefined' && !props['aria-label']) {
throw new Error(
'Button components must have either children or an aria-label prop',
);
}
const iconSize = useCallback(() => {
switch (styleProps.size) {
case 'lg':
return { width: 20, height: 20 };
case 'sm':
case 'xs':
return { width: 16, height: 16 };
case 'md':
default: {
return { width: 18, height: 18 };
}
}
}, [styleProps.size])();
return (
<Component
{...props}
// @ts-expect-error - ref is not a valid prop for button elements
ref={ref}
className={buttonTheme({ ...styleProps, class: className })}
>
{cloneIcon(leftIcon, { ...iconSize })}
{children}
{cloneIcon(rightIcon, { ...iconSize })}
</Component>
);
},
);
Button.displayName = 'Button';
export { Button };
@@ -0,0 +1,2 @@
export * from './Button';
export * from './Button.theme';
@@ -0,0 +1,30 @@
import React, { ComponentPropsWithoutRef } from 'react';
export interface CustomIconProps extends ComponentPropsWithoutRef<'svg'> {
size?: number | string; // width and height will both be set as the same value
name?: string;
}
export const CustomIcon = ({
children,
width = 24,
height = 24,
size,
viewBox = '0 0 24 24',
name,
...rest
}: CustomIconProps) => {
return (
<svg
aria-labelledby={name}
height={size || height}
role="presentation"
viewBox={viewBox}
width={size || width}
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
{children}
</svg>
);
};
@@ -0,0 +1,21 @@
import React from 'react';
import { CustomIcon, CustomIconProps } from './CustomIcon';
export const PlusIcon = (props: CustomIconProps) => {
return (
<CustomIcon
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.66666 9.99999C1.66666 5.39762 5.39762 1.66666 9.99999 1.66666C14.6024 1.66666 18.3333 5.39762 18.3333 9.99999C18.3333 14.6024 14.6024 18.3333 9.99999 18.3333C5.39762 18.3333 1.66666 14.6024 1.66666 9.99999ZM10.625 6.46483C10.625 6.11966 10.3452 5.83983 9.99999 5.83983C9.65481 5.83983 9.37499 6.11966 9.37499 6.46483V9.37537H6.46446C6.11928 9.37537 5.83946 9.65519 5.83946 10.0004C5.83946 10.3455 6.11928 10.6254 6.46446 10.6254H9.37499V13.5359C9.37499 13.8811 9.65481 14.1609 9.99999 14.1609C10.3452 14.1609 10.625 13.8811 10.625 13.5359V10.6254H13.5355C13.8807 10.6254 14.1605 10.3455 14.1605 10.0004C14.1605 9.65519 13.8807 9.37537 13.5355 9.37537H10.625V6.46483Z"
fill="currentColor"
/>
</CustomIcon>
);
};
@@ -0,0 +1,24 @@
# 1. What icons are compatible with this component?
- Viewbox "0 0 24 24": From where you're exporting from, please make sure the icon is using viewBox="0 0 24 24" before downloading/exporting. Not doing so will result in incorrect icon scaling
# 2. How to add a new icon?
**2.1 Sanitising the icon**
1. Duplicate a current icon e.g. CrossIcon and rename it accordingly.
2. Rename the function inside the new file you duplicated too
3. Replace the markup with your SVG markup (make sure it complies with the above section's rule)
4. Depending on the svg you pasted...
A. If the `<svg>` has only 1 child, remove the `<svg>` parent entirely so you only have the path left
B. If your component has more than 1 paths, rename `<svg>` tag with the `<g>` tag. Then, remove all attributes of this `<g>` tag so that it's just `<g>`
5. Usually, icons are single colored. If that's the case, replace all fill/stroke color with `currentColor`. E.g. <path d="..." fill="currentColor">. Leave the other attributes without removing them.
6. If your icon has more than one colour, then it's up to you to decide whether we want to use tailwind to help set the fill and stroke colors
7. Lastly, export your icon in `index.ts` by following what was done for CrossIcon
8. Make sure to provide a name to the `<CustomIcon>` component for accessibility sake
9. Done!
**2.3 Use your newly imported icon**
1. You can change simply use `<BellIcon size="32" />` to quickly change both width and height with the same value (square). For custom viewBox, width and height, simply provide all three props.
2. Coloring the icon: Simply add a className with text color. E.g. `<BellIcon className="text-gray-500" />`
@@ -0,0 +1,2 @@
export * from './PlusIcon';
export * from './CustomIcon';
+6
View File
@@ -1,3 +1,9 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.focus-ring {
@apply focus-visible:ring-[3px] focus-visible:ring-snowball-200 focus-visible:ring-offset-1 focus-visible:ring-offset-snowball-500 focus-visible:outline-none;
}
}
+1
View File
@@ -7,6 +7,7 @@ import { GQLClient } from 'gql-client';
import { ThemeProvider } from '@material-tailwind/react';
import './index.css';
import '@fontsource/inter';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { GQLClientProvider } from './context/GQLClientContext';
@@ -0,0 +1,77 @@
import { Button, ButtonOrLinkProps } from 'components/shared/Button';
import { PlusIcon } from 'components/shared/CustomIcon';
import React from 'react';
const Page = () => {
return (
<div className="relative h-full min-h-full">
<div className="flex flex-col items-center justify-center max-w-7xl mx-auto px-20 py-20">
<h1 className="text-4xl font-bold">Manual Storybook</h1>
<p className="mt-4 text-lg text-center text-gray-500">
Get started by editing{' '}
<code className="p-2 font-mono text-sm bg-gray-100 rounded-md">
packages/frontend/src/pages/components/index.tsx
</code>
</p>
<div className="w-full h border border-gray-200 px-20 my-10" />
{/* Insert Components here */}
<div className="flex flex-col gap-10 items-center justify-between">
<h1 className="text-2xl font-bold">Button</h1>
<div className="flex flex-col gap-10">
{['primary', 'secondary', 'tertiary', 'danger'].map(
(variant, index) => (
<div className="flex gap-5 flex-wrap" key={index}>
{['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => (
<Button
leftIcon={<PlusIcon />}
rightIcon={<PlusIcon />}
variant={variant as ButtonOrLinkProps['variant']}
size={
size !== 'disabled'
? (size as ButtonOrLinkProps['size'])
: 'md'
}
key={`${variant}-${size}`}
disabled={size === 'disabled'}
>
Button
</Button>
))}
</div>
),
)}
{[
'primary',
'secondary',
'tertiary',
'ghost',
'danger',
'danger-ghost',
].map((variant, index) => (
<div className="flex gap-5 flex-wrap" key={index}>
{['lg', 'md', 'sm', 'xs', 'disabled'].map((size) => (
<Button
iconOnly
variant={variant as ButtonOrLinkProps['variant']}
size={
size !== 'disabled'
? (size as ButtonOrLinkProps['size'])
: 'md'
}
key={`${variant}-${size}`}
disabled={size === 'disabled'}
>
<PlusIcon />
</Button>
))}
</div>
))}
</div>
</div>
</div>
</div>
);
};
export default Page;
+17
View File
@@ -0,0 +1,17 @@
import { Children, cloneElement, isValidElement } from 'react';
import type { Attributes, ReactElement, ReactNode } from 'react';
/**
* Clones an icon element with optional additional props.
* @param {ReactNode} icon - The icon element to clone.
* @param {Attributes & P} [props] - Additional props to apply to the cloned icon.
* @returns {ReactNode} - The cloned icon element with the applied props.
*/
export function cloneIcon<P extends object>(
icon: ReactNode,
props?: Attributes & P,
): ReactNode {
return Children.map(icon, (child) =>
isValidElement(child) ? cloneElement(child as ReactElement, props) : child,
);
}
+143 -1
View File
@@ -8,7 +8,149 @@ export default withMT({
'../../node_modules/@material-tailwind/react/theme/components/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {},
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
extend: {
colors: {
emerald: {
100: '#d1fae5',
200: '#a9f1d0',
300: '#6ee7b7',
400: '#34d399',
50: '#ecfdf5',
500: '#10b981',
600: '#059669',
700: '#047857',
800: '#065f46',
900: '#064e3b',
},
gray: {
0: '#ffffff',
100: '#f1f5f9',
200: '#e2e9f0',
300: '#cbd6e1',
400: '#94a7b8',
50: '#f8fafc',
500: '#60788f',
600: '#475969',
700: '#334555',
800: '#1b2d3e',
900: '#0b1d2e',
},
orange: {
100: '#ffedd5',
200: '#fed7aa',
300: '#fdba74',
400: '#fb923c',
50: '#fff7ed',
500: '#f97316',
600: '#ea580c',
700: '#c2410c',
800: '#9a3412',
900: '#7c2d12',
},
rose: {
100: '#ffe4e6',
200: '#fecdd3',
300: '#fda4af',
400: '#fb7185',
50: '#fff1f2',
500: '#f43f5e',
600: '#e11d48',
700: '#be123c',
800: '#9f1239',
900: '#881337',
},
snowball: {
100: '#e1f1fe',
200: '#ddeefd',
300: '#cfe6fc',
400: '#74bafb',
50: '#ecf6fe',
500: '#47a4fa',
600: '#0f86f5',
700: '#0977dc',
800: '#075185',
900: '#0a3a5c',
},
base: {
bg: '#ffffff',
'bg-alternate': '#f8fafc',
'bg-emphasized': '#f1f5f9',
'bg-emphasized-danger': '#fff1f2',
'bg-emphasized-info': '#ecf6fe',
'bg-emphasized-success': '#ecfdf5',
'bg-emphasized-warning': '#fff7ed',
},
border: {
active: '#0f86f5',
danger: '#e11d48',
'danger-light': '#ffe4e6',
'info-light': '#ddeefd',
interactive: '#082f561a',
'interactive-hovered': '#082f5624',
separator: '#082f560f',
'success-light': '#d1fae5',
'warning-light': '#ffedd5',
},
controls: {
danger: '#e11d48',
'danger-hovered': '#be123c',
disabled: '#e2e9f0',
'disabled-active': '#74bafb',
elevated: '#ffffff',
inset: '#e2e9f0',
'inset-hovered': '#cbd6e1',
primary: '#0f86f5',
'primary-hovered': '#0977dc',
secondary: '#ddeefd',
'secondary-hovered': '#cfe6fc',
tertiary: '#ffffff',
'tertiary-hovered': '#f8fafc',
},
elements: {
danger: '#e11d48',
disabled: '#94a7b8',
'high-em': '#0b1d2e',
info: '#0f86f5',
link: '#0f86f5',
'link-hovered': '#0977dc',
'low-em': '#60788f',
'mid-em': '#475969',
'on-danger': '#ffffff',
'on-disabled': '#60788f',
'on-disabled-active': '#0a3a5c',
'on-emphasized-danger': '#9f1239',
'on-emphasized-info': '#0a3a5c',
'on-emphasized-success': '#065f46',
'on-emphasized-warning': '#9a3412',
'on-high-contrast': '#ffffff',
'on-primary': '#ffffff',
'on-secondary': '#0977dc',
'on-secondary-tinted': '#075185',
'on-tertiary': '#1b2d3e',
success: '#059669',
warning: '#ea580c',
},
surface: {
card: '#ffffff',
'card-hovered': '#f8fafc',
floating: '#ffffff',
'floating-hovered': '#f1f5f9',
'high-contrast': '#0b1d2e',
},
},
boxShadow: {
button:
'inset 0px 0px 4px rgba(255, 255, 255, 0.25), inset 0px -2px 0px rgba(0, 0, 0, 0.04)',
},
spacing: {
2.5: '0.625rem',
3.25: '0.8125rem',
3.5: '0.875rem',
},
},
},
plugins: [],
});
+2 -1
View File
@@ -14,7 +14,8 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
"jsx": "react-jsx",
"baseUrl": "src"
},
"include": ["src"]
}
+20 -1
View File
@@ -1280,7 +1280,7 @@
resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz"
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
"@babel/runtime@^7.10.4", "@babel/runtime@^7.3.1":
"@babel/runtime@^7.10.4", "@babel/runtime@^7.23.7", "@babel/runtime@^7.3.1":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
@@ -2068,6 +2068,11 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9"
integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==
"@fontsource/inter@^5.0.16":
version "5.0.16"
resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-5.0.16.tgz#b858508cdb56dcbbf3166903122851e2fbd16b50"
integrity sha512-qF0aH5UiZvCmneX5orJbVRoc2VTyLTV3X/7laMp03Qt28L+B9tFlZODOGUL64wDWc69YVdi1LeJB0cIgd51lvw==
"@gar/promisify@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
@@ -15026,6 +15031,20 @@ tailwind-merge@1.8.1:
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-1.8.1.tgz#0e56c8afbab2491f72e06381043ffec8b720ba04"
integrity sha512-+fflfPxvHFr81hTJpQ3MIwtqgvefHZFUHFiIHpVIRXvG/nX9+gu2P7JNlFu2bfDMJ+uHhi/pUgzaYacMoXv+Ww==
tailwind-merge@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.2.1.tgz#3f10f296a2dba1d88769de8244fafd95c3324aeb"
integrity sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==
dependencies:
"@babel/runtime" "^7.23.7"
tailwind-variants@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/tailwind-variants/-/tailwind-variants-0.2.0.tgz#5c3a24afb6e8bb23f33aa3aa3550add8848de8a3"
integrity sha512-EuW5Sic7c0tzp+p5rJwAgb7398Jb0hi4zkyCstOoZPW0DWwr+EWkNtnZYEo5CjgE1tazHUzyt4oIhss64UXRVA==
dependencies:
tailwind-merge "^2.2.0"
tailwindcss@^3.0.2, tailwindcss@^3.3.6:
version "3.3.6"
resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.6.tgz"