burnout-components
Version:
A component library for burnout brands that contains common react-components and util functions used throughout it and it's portfolio companies
825 lines (626 loc) • 19.9 kB
Markdown
- [Features](
- [Installing](
- [Styling](
- [Client Components](
- [BurnoutButton](
- [MonsterButton](
- [Client Form Components](
- [Server Functions/Components](
- [Info](
- [BurnoutBreadCrumbNext](
- [BurnoutButtonLinkNext](
- [BurnoutInfoCard](
- [BurnoutStyledLinkNext](
- [GlassMonsterBreadCrumbNext](
- [GlassMonsterInfoCard](
- [MonsterBreadCrumbNext](
- [MonsterButtonLinkNext](
- [MonsterGlassButtonLinkNext](
- [MonsterInfoCard](
- [MonsterStyledLinkNext](
- [TailwindLoadingSpinner](
- [Utility Functions](
- [capitalizeFirstLetters](
- [createDollarAmount](
- [createQueryStringNext](
- [extractNumber](
- [formatPhone](
- [trimStringProperties](
- [truncateText](
- [tryCatch](
- Styled Client/Server Components for Next.js/React (can be used with any React
app)
- Styled Client Form Components for use with Zod and TanStack Form
- Utility functions that can run on either client or server
- Intended for standardized styling and form validation across Burnout Brand
companies for both internal and external use
- Reduces boilerplate in Burnout Brands projects
Using npm:
```bash
npm install burnout-components
```
Using yarn:
```bash
yarn add burnout-components
```
Using bun:
```bash
bun install burnout-components
```
Using pnpm:
```bash
pnpm add burnout-components
```
Using bower:
```bash
bower install burnout-components
```
Once the package is installed, you can import the library using `import`
approach:
```typescript
import { MonsterButton } from 'burnout-components';
import { tryCatch } from 'burnout-components/server';
```
You must have Tailwind CSS installed and configured in your project. You must
then import `burnout-components/styles.css` in either your root layout or your
App file.
These must be used with the 'use client' directive at the top of the file if you
are using react 18 (I think) and above.
Props and their types:
- children: React.ReactNode
- colorSwap: boolean (optional, default false)
- disabled: boolean (optional, default false)
- fit: boolean (optional, default false, false = full width, true = fit to
content)
- onClick: () => void (optional)
- red: boolean (optional, default false)
- type: 'button' | 'submit' (optional, default submit)
```tsx
import { BurnoutButton } from 'burnout-compoents';
function SomeButton() {}
return (
<BurnoutButton type='button' fit red onClick={() => console.log('Clicked!')}>
Click Me!
</BurnoutButton>
)
}
```
Props and their types:
- children: React.ReactNode
- colorSwap: boolean (optional, default false)
- disabled: boolean (optional, default false)
- fit: boolean (optional, default false, false = full width, true = fit to
content)
- onClick: () => void (optional)
- red: boolean (optional, default false)
- type: 'button' | 'submit' (optional, default submit)
```tsx
import { MonsterButton } from 'burnout-compoents';
function SomeButton() {}
return (
<MonsterButton type='button' fit red onClick={() => console.log('Clicked!')}>
Click Me!
</MonsterButton>
)
}
```
These are styled components that are meant to be used with Zod and TanStack
Form.
They all need to be wrapped with `FormWrapper` and must before the end of
`FormWrapper` must contain a submit button like this:
```tsx
<form.AppForm>
<form.MonsterSubmitButton text='Update Role' submittingText='Updating...' />
</form.AppForm>
```
All available components are:
- FormWrapper
- BurnoutCheckbox
- BurnoutMultiSelect
- BurnoutRichTextEditor
- BurnoutSelectField
- BurnoutSubmitButton
- BurnoutTextAreaField
- BurnoutTextField
- GlassCheckbox
- GlassRichTextEditor
- GlassSelectField
- GlassTextAreaField
- GlassTextField
- MonsterCheckbox
- MonsterMultiSelect
- MonsterRichTextEditor
- MonsterSelectField
- MonsterSubmitButton
- MonsterGlassSubmitButton
- MonsterTextAreaField
- MonsterTextField
Props and their types:
- colorSwap: boolean (optional, default false)
- text: string (optional, default 'Submit')
- submittingText: string (optional, default 'Submitting...')
The MonsterGlassSubmitButton uses the TanStack Form context to determine if the form can be submitted. If there are validation errors, clicking the button will display an alert with all error messages. The button automatically disables when the form is submitting or when validation fails.
```tsx
import { FormWrapper, useAppForm } from 'burnout-components';
import { z } from 'zod';
function Form() {
const form = useAppForm({
defaultValues: {
name: '',
email: ''
},
onSubmit: async ({ value }) => {
// Submit form data
},
validators: {
onChange: {
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email address')
}
}
});
return (
<FormWrapper handleSubmit={form.handleSubmit}>
<form.AppField name='name'>
{(field) => (
<field.GlassTextField label='Name' />
)}
</form.AppField>
<form.AppField name='email'>
{(field) => (
<field.GlassTextField label='Email' type='email' />
)}
</form.AppField>
<form.AppForm>
<form.MonsterGlassSubmitButton
colorSwap
text='Create Account'
submittingText='Creating...'
/>
</form.AppForm>
</FormWrapper>
);
}
```
```tsx
import { FormWrapper } from 'burnout-compoents';
import { useAppForm } from 'burnout-compoents/';
import { z } from 'zod';
// You can also import like this but I prefer the former
import { FormWrapper, useAppForm } from 'burnout-compoents';
function Form() {
const form = useAppForm({
defaultValues: {
name: '',
email: '',
manager: false,
role: 'user',
favoriteColors: [''],
descriptionLong: '',
descriptionShort: ''
},
onSubmit: async ({ value }) => {
const {
name,
email,
manager,
role,
favoriteColors,
descriptionLong,
descriptionShort
} = value;
// Do something with the form values
// This function does not have to be async
},
validators: {
onChange: {
// You would normally define this somewhere else and import it in
// Field Errors are built in and can be checked either on change or on submit, I like on change
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email address'),
manager: z.boolean(),
role: z.enum(['user', 'admin'], 'Role is required'),
favoriteColors: z
.array(z.string())
.min(1, 'At least one color is required'),
descriptionLong: z.string().max(500, 'Description is too long'),
descriptionShort: z.string().max(100, 'Description is too long')
}
}
});
const colorOptions = ['red', 'green', 'blue'];
return (
<FormWrapper handleSubmit={form.handleSubmit}>
{/* Required is optional by default it is true on all relevant fields except checkboxes */}
{/* Type is optional by default it is text on all relevant fields */}
<form.AppField name='name'>
{(field) => (
<field.MonsterTextField label='Name' autoFocus type='text' />
)}
</form.AppField>
<form.AppField name='email'>
{(field) => (
<field.MonsterTextField label='Email' required={false} type='email' />
)}
</form.AppField>
<form.AppField name='manager'>
{(field) => <field.MonsterCheckbox label='Is Manager' />}
</form.AppField>
<form.AppField name='role'>
{(field) => (
<field.MonsterSelectField label='Role'>
<option value='user'>User</option>
<option value='admin'>Admin</option>
</field.MonsterSelectField>
)}
</form.AppField>
<form.AppField name='favoriteColors'>
{(field) => (
<field.MonsterMultiSelect
label='Favorite Colors'
options={colorOptions}
/>
)}
</form.AppField>
<form.AppField name='descriptionLong'>
{(field) => <field.MonsterRichTextEditor label='Long Description' />}
</form.AppField>
<form.AppField name='descriptionShort'>
{(field) => <field.MonsterTextAreaField label='Short Description' />}
</form.AppField>
<form.AppForm>
{/* default text is 'Submit' and default submitting text is 'Submitting...' */}
<form.MonsterSubmitButton
text='Create Person'
submittingText='Creating...'
/>
</form.AppForm>
</FormWrapper>
);
}
```
Props and their types:
- children: React.ReactNode
- label: string
```tsx
import { Info } from 'burnout-compoents/server'
return TellingYouSomething() {
return (
<Info label='Something Important'>
<p>This is some important information</p>
</Info>
)
}
```
**_This is only for use in Next.js projects_**
Props and their types:
- colorSwap: boolean(optional, default false)
- fromLabel: string;
- href: string
- linkLabel: string
```tsx
import { BurnoutBreadCrumbNext } from 'burnout-compoents/server'
return LinkingYouToSomething() {
return (
<BurnoutBreadCrumbNext fromLabel='I am here' href='/somewhere' linkLabel='Somewhere' />
)
}
```
**_This is only for use in Next.js projects_**
Props and their types:
- children: React.ReactNode
- colorSwap: boolean (optional, default false)
- href: string
- prefetch: boolean (optional, default true)
- target: '\_blank' | '\_self' | '\_parent' | '\_top' (optional, default \_self)
```tsx
import { BurnoutButtonLinkNext } from 'burnout-compoents/server'
return LinkingYouToSomething() {
return (
<BurnoutButtonLinkNext prefetch={false} href='/somewhere' target='_blank'>
This goes somewhere
</BurnoutButtonLinkNext>
)
}
```
Props and their types:
- children: React.ReactNode
- colorSwap: boolean (optional, default false)
- header: string
```tsx
import { BurnoutInfoCard } from 'burnout-compoents/server'
return SomeCard() {
return (
<BurnoutInfoCard header='Some Header'>
<div className='w-full flex flex-col gap-3'>
<BurnoutInfoCard colorSwap header='Some Nested Header'>
<p>This is some nested information</p>
</MonsterInfoCard>
</div>
</MonsterButtonLinkNext>
)
}
```
**_This is only for use in Next.js projects_**
Props and their types:
- children: React.ReactNode
- href: string
- prefetch: boolean (optional, default true)
- target: '\_blank' | '\_self' | '\_parent' | '\_top' (optional, default \_self)
```tsx
import { BurnoutStyledLinkNext } from 'burnout-compoents/server'
return LinkingYouToSomething() {
return (
<BurnoutStyledLinkNext prefetch={false} href='/somewhere' target='_blank'>
This goes somewhere
</BurnoutStyledLinkNext>
)
}
```
**_This is only for use in Next.js projects_**
Props and their types:
- colorSwap: boolean (optional, default false);
- fromLabel: string;
- href: string
- linkLabel: string
```tsx
import { MonsterBreadCrumbNext } from 'burnout-compoents/server'
return LinkingYouToSomething() {
return (
<MonsterBreadCrumbNext fromLabel='I am here' href='/somewhere' linkLabel='Somewhere' />
)
}
```
**_This is only for use in Next.js projects_**
Props and their types:
- children: React.ReactNode
- colorSwap: boolean (optional, default false)
- href: string
- prefetch: boolean (optional, default true)
- target: '\_blank' | '\_self' | '\_parent' | '\_top' (optional, default \_self)
```tsx
import { MonsterButtonLinkNext } from 'burnout-compoents/server'
return LinkingYouToSomething() {
return (
<MonsterButtonLinkNext prefetch={false} href='/somewhere' target='_blank'>
This goes somewhere
</MonsterButtonLinkNext>
)
}
```
**_This is only for use in Next.js projects_**
Props and their types:
- children: React.ReactNode
- colorSwap: boolean (optional, default false)
- href: string
- prefetch: boolean (optional, default true)
- target: '\_blank' | '\_self' | '\_parent' | '\_top' (optional, default \_self)
```tsx
import { MonsterGlassButtonLinkNext } from 'burnout-compoents/server'
return LinkingYouToSomething() {
return (
<MonsterGlassButtonLinkNext prefetch={false} href='/somewhere' target='_blank'>
This goes somewhere
</MonsterGlassButtonLinkNext>
)
}
```
Props and their types:
- children: React.ReactNode
- colorSwap: boolean (optional, default false)
- header: string
```tsx
import { MonsterInfoCard } from 'burnout-compoents/server'
return SomeCard() {
return (
<MonsterInfoCard header='Some Header'>
<div className='w-full flex flex-col gap-3'>
<MonsterInfoCard colorSwap header='Some Nested Header'>
<p>This is some nested information</p>
</MonsterInfoCard>
</div>
</MonsterButtonLinkNext>
)
}
```
**_This is only for use in Next.js projects_**
Props and their types:
- children: React.ReactNode
- href: string
- prefetch: boolean (optional, default true)
- target: '\_blank' | '\_self' | '\_parent' | '\_top' (optional, default \_self)
```tsx
import { MonsterStyledLinkNext } from 'burnout-compoents/server'
return LinkingYouToSomething() {
return (
<MonsterStyledLinkNext prefetch={false} href='/somewhere' target='_blank'>
This goes somewhere
</MonsterStyledLinkNext>
)
}
```
**_This is only for use in Next.js projects_**
Props and their types:
- colorSwap: boolean (optional, default false)
- fromLabel: string
- href: string
- linkLabel: string
The GlassMonsterBreadCrumbNext component provides a glass-style breadcrumb navigation element using the MonsterGlassButtonLinkNext component internally. It displays a link button followed by a chevron icon and the current page label.
```tsx
import { GlassMonsterBreadCrumbNext } from 'burnout-compoents/server'
return BreadcrumbNavigation() {
return (
<GlassMonsterBreadCrumbNext
colorSwap
fromLabel="Current Page"
href="/previous-page"
linkLabel="Previous Page"
/>
)
}
```
Props and their types:
- children: React.ReactNode
- colorSwap: boolean (optional, default false)
- header: number | string
The GlassMonsterInfoCard component provides a glass-style card with a header and content area. It features a backdrop blur effect and subtle transparency for a modern UI appearance.
```tsx
import { GlassMonsterInfoCard } from 'burnout-compoents/server'
return InformationDisplay() {
return (
<GlassMonsterInfoCard header="Important Information">
<div className="space-y-4">
<p>This is some important information displayed in a glass-style card.</p>
<GlassMonsterInfoCard colorSwap header="Additional Details">
<p>You can nest these cards for hierarchical information display.</p>
</GlassMonsterInfoCard>
</div>
</GlassMonsterInfoCard>
)
}
```
This is small and meant for inline loading spinners. It is not a full page
loading.
Props and their types:
- fill: 'blue' | 'green' | 'red' | 'yellow' (optional, default blue)
```tsx
import { TailwindLoadingSpinner } from 'burnout-compoents/server'
return SomeBadLoading() {
return (
<TailwindLoadingSpinner fill='red' />
)
}
```
```typescript
import { capitalizeFirstLetters } from 'burnout-components/server';
const text = 'hello world';
const newText = capitalizeFirstLetters(text);
console.log(newText); // Output: Hello World
```
```typescript
import { createDollarAmount } from 'burnout-components/server';
const amountString = '200';
const newAmountOne = createDollarAmount(amountString);
console.log(newAmountOne); // Output: $200.00
const amountNumber = 200;
const newAmountTwo = createDollarAmount(amountNumber);
console.log(newAmountTwo); // Output: $200.00
```
This is only for use in Next.js projects.
```typescript
import { createQueryStringNext } from 'burnout-components/server';
import { usePathname, useRouter, useSearchParams } from 'next/naviagtion';
const pathname = usePathname();
const router = useRouter();
const searchParams = useSearchParams();
function handleSubmit(search: string) {
router.push(`${pathname}?${createQueryString('search', search, searchParams}`)
}
```
```typescript
import { extractNumber } from 'burnout-components/server';
const inputString = '2,812.30 refund minus 25% CANCELLATION FEE.';
const result = extractNumber(inputString);
console.log(result); // Output: 2812.3
```
```typescript
import { formatPhone } from 'burnout-components/server';
const phoneNumber = '1234567890';
const formattedPhone = formatPhone(phoneNumber);
console.log(formattedPhone); // Output: (123) 456-7890
```
```typescript
import { trimStringProperties } from 'burnout-components/server';
const userInput = {
name: ' John Doe ',
email: ' john@example.com ',
age: 30,
isActive: true,
description: ' This is a description with extra spaces '
};
const trimmedData = trimStringProperties(userInput);
console.log(trimmedData);
// Output: {
// name: 'John Doe',
// email: 'john@example.com',
// age: 30,
// isActive: true,
// description: 'This is a description with extra spaces'
// }
```
```typescript
import { truncateText } from 'burnout-components/server';
const text 'Imagine this is super long text that needs to be truncated';
const truncatedText = truncateText(text, 20);
console.log(truncatedText); // Output: Imagine this is super long...
```
```typescript
import { tryCatch } from 'burnout-components/server';
async function something() {
const { data, error } = await tryCatch(async () => {
const response = await fetch('/api/some-endpoint');
return response.json();
});
if (error) {
console.error('Error fetching data:', error);
} else {
console.log('Fetched data:', data);
}
}
```