@koaris/bloom-ui 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "@koaris/eslint-config"
3
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,55 @@
1
+ # @koaris/bloom-ui
2
+
3
+ ## 1.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Optimized Tailwind configuration and build process to reduce bundle size.
8
+
9
+ ## 1.1.1
10
+
11
+ ### Patch Changes
12
+
13
+ - fix card css
14
+
15
+ ## 1.1.0
16
+
17
+ ### Minor Changes
18
+
19
+ - button and checkbox improvements
20
+
21
+ ## 1.0.5
22
+
23
+ ### Patch Changes
24
+
25
+ - Create link component
26
+
27
+ ## 1.0.4
28
+
29
+ ### Patch Changes
30
+
31
+ - creating form component
32
+
33
+ ## 1.0.3
34
+
35
+ ### Patch Changes
36
+
37
+ - Update components and add new colors
38
+
39
+ ## 1.0.2
40
+
41
+ ### Patch Changes
42
+
43
+ - fix tailwind post css
44
+
45
+ ## 1.0.1
46
+
47
+ ### Patch Changes
48
+
49
+ - recreating package
50
+
51
+ ## 1.0.0
52
+
53
+ ### Major Changes
54
+
55
+ - Creating bloom-ui
package/README.md CHANGED
@@ -10,12 +10,16 @@ Install the following package:
10
10
  npm i @koaris/bloom-ui
11
11
  ```
12
12
  ## Usage
13
- View docs [here](https://guilhermesalviano.github.io/bloom-ui).
14
-
15
- Import design system css in your css file:
13
+ This design system is built with Tailwind, and you can import it into your project as follows:
14
+ In your CSS file:
16
15
  ```css
17
- @import "@koaris/bloom-ui/dist/tailwind.css";
16
+ @import "@koaris/bloom-ui/tailwind.css";
17
+ ```
18
+ Or in your JavaScript file:
19
+ ```js
20
+ import '@koaris/bloom-ui/tailwind.css'
18
21
  ```
22
+
19
23
  Components usage:
20
24
  ```tsx
21
25
  import { Text } from '@koaris/bloom-ui'
package/package.json CHANGED
@@ -1,20 +1,22 @@
1
1
  {
2
2
  "name": "@koaris/bloom-ui",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Bloom-ui is a public design system from the Koaris Project developed with React, Typescript, and Tailwind.",
5
5
  "source": "./src/index.ts",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.mjs",
8
- "types": "./dist/index.d.ts",
6
+ "type": "module",
7
+ "main": "dist/index.mjs",
9
8
  "scripts": {
10
- "build": "tailwindcss -i ./src/styles/tailwind.css -o ./dist/tailwind.css -c ./tailwind.config.ts --minify && tsup src/index.tsx --format esm,cjs --dts --external react",
11
- "dev": "concurrently --kill-others \"tailwindcss -i ./src/styles/tailwind.css -o ./dist/tailwind.css -c ./tailwind.config.ts --minify --watch \" \"tsup src/index.tsx --format esm, cjs --dts --external react --watch\"",
9
+ "build": "NODE_ENV=production tailwindcss -i ./src/styles/tailwind.css -o ./tailwind.css -c ./tailwind.config.ts --minify && tsup",
10
+ "dev": "concurrently --kill-others \"tailwindcss -i ./src/styles/tailwind.css -o ./tailwind.css -c ./tailwind.config.ts --minify --watch \" \"tsup --watch\"",
12
11
  "lint": "eslint src/**/*.ts* --fix"
13
12
  },
14
- "files": [
15
- "dist",
16
- "README.md"
17
- ],
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/index.mjs",
16
+ "require": "./dist/index.d.ts"
17
+ },
18
+ "./tailwind.css": "./tailwind.css"
19
+ },
18
20
  "keywords": [
19
21
  "design-system",
20
22
  "react"
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: { config: './tailwind.config.ts' },
4
+ autoprefixer: {},
5
+ },
6
+ }
@@ -0,0 +1,27 @@
1
+ import { twMerge } from 'tailwind-merge'
2
+ import { FaUser } from 'react-icons/fa'
3
+
4
+ export interface AvatarProps {
5
+ src?: string
6
+ alt?: string
7
+ className?: string
8
+ }
9
+
10
+ export const Avatar = ({ className, ...rest }: AvatarProps) => {
11
+ return (
12
+ <div
13
+ className={twMerge(
14
+ `
15
+ rounded-full w-16 h-16 overflow-hidden flex items-center
16
+ bg-neutral-600 justify-center`,
17
+ className,
18
+ )}
19
+ >
20
+ {rest.src ? (
21
+ <img className="w-full h-full object-cover rounded-full" {...rest} />
22
+ ) : (
23
+ <FaUser color="#FFFFFF" size={24} />
24
+ )}
25
+ </div>
26
+ )
27
+ }
@@ -0,0 +1,33 @@
1
+ import { DetailedHTMLProps, HTMLAttributes } from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export interface BoxProps
5
+ extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
6
+ children: React.ReactNode
7
+ tag?: 'div' | 'section' | 'article' | 'aside' | 'header' | 'footer'
8
+ variant?: 'primary' | 'secondary'
9
+ }
10
+
11
+ export const Box = ({
12
+ className,
13
+ children,
14
+ tag = 'div',
15
+ variant = 'secondary',
16
+ }: BoxProps) => {
17
+ const Tag = tag as React.ElementType
18
+
19
+ return (
20
+ <Tag
21
+ className={twMerge(
22
+ 'p-6 rounded-md bottom-1 border-2',
23
+ variant === 'primary' &&
24
+ 'text-neutral-800 bg-neutral-200 border-neutral-300',
25
+ variant === 'secondary' &&
26
+ 'text-neutral-200 bg-neutral-600 border-neutral-800',
27
+ className,
28
+ )}
29
+ >
30
+ {children}
31
+ </Tag>
32
+ )
33
+ }
@@ -0,0 +1,43 @@
1
+ import { DetailedHTMLProps, ButtonHTMLAttributes } from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ /**
5
+ * Primary UI component for user interaction
6
+ */
7
+ export interface ButtonProps
8
+ extends DetailedHTMLProps<
9
+ ButtonHTMLAttributes<HTMLButtonElement>,
10
+ HTMLButtonElement
11
+ > {
12
+ size?: 'sm' | 'md'
13
+ variant?: 'primary' | 'secondary'
14
+ disabled?: boolean
15
+ children: string | JSX.Element
16
+ }
17
+
18
+ export const Button = ({
19
+ className,
20
+ variant = 'primary',
21
+ size = 'md',
22
+ disabled,
23
+ onClick,
24
+ ...rest
25
+ }: ButtonProps) => {
26
+ return (
27
+ <button
28
+ className={twMerge(
29
+ 'flex gap-4 items-center justify-center rounded-sm px-8 py-2 text-md font-medium hover:shadow-md hover:shadow-neutral-500 w-full max-w-[180px]',
30
+ variant === 'primary' &&
31
+ 'bg-orange-500 text-neutral hover:bg-orange-700',
32
+ variant === 'secondary' &&
33
+ 'bg-neutral text-orange-500 border border-orange-500 hover:text-orange-100 hover:bg-orange-500',
34
+ size === 'sm' && 'px-6 py-1',
35
+ typeof rest.children !== 'string' && 'px-4',
36
+ disabled === true && 'opacity-50 cursor-not-allowed',
37
+ className,
38
+ )}
39
+ onClick={onClick}
40
+ {...rest}
41
+ />
42
+ )
43
+ }
@@ -0,0 +1,59 @@
1
+ import { DetailedHTMLProps, HTMLAttributes } from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ /**
5
+ * Primary UI component for user interaction
6
+ */
7
+ export interface CardProps
8
+ extends DetailedHTMLProps<
9
+ HTMLAttributes<HTMLDivElement>, // Change the type to HTMLDivElement
10
+ HTMLDivElement
11
+ > {
12
+ selected?: boolean
13
+ disabled?: boolean
14
+ direction?: string
15
+ size?: string
16
+ imageSize: string
17
+ image?: string // Add the image property
18
+ onClick?: () => void
19
+ }
20
+
21
+ export const Card = ({
22
+ className,
23
+ selected = false,
24
+ direction,
25
+ size,
26
+ disabled,
27
+ imageSize,
28
+ onClick,
29
+ ...rest
30
+ }: CardProps) => {
31
+ return (
32
+ <div
33
+ className={twMerge(
34
+ 'flex items-center justify-center rounded-lg cursor-pointer bg-neutral',
35
+ 'hover:shadow-md hover:shadow-neutral-500 border border-neutral-500 text-neutral-1000',
36
+ size === 'medium' && 'w-64 px-8 py-4',
37
+ size === 'large' && 'w-96 pr-5',
38
+ direction === 'col' && 'flex-col',
39
+ selected === true && 'border-2 border-orange-500',
40
+ disabled === true && 'opacity-50 cursor-not-allowed',
41
+ className,
42
+ )}
43
+ onClick={onClick}
44
+ >
45
+ <img src={rest.image} alt={rest.title} width={imageSize} height="auto" />
46
+ <aside
47
+ className={twMerge(
48
+ direction === 'col' && 'text-center',
49
+ 'flex flex-col gap-2',
50
+ )}
51
+ >
52
+ <h1 className="text-xl font-bold font-default leading-tight">
53
+ {rest.title}
54
+ </h1>
55
+ <p className="text-justify">{rest.content}</p>
56
+ </aside>
57
+ </div>
58
+ )
59
+ }
@@ -0,0 +1,49 @@
1
+ import { DetailedHTMLProps, HTMLAttributes, useState } from 'react'
2
+ import { FiCheck } from 'react-icons/fi'
3
+ import { twMerge } from 'tailwind-merge'
4
+
5
+ export interface CheckboxProps
6
+ extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
7
+ disabled?: boolean
8
+ required?: boolean
9
+ }
10
+
11
+ export const Checkbox = ({ className, required, disabled }: CheckboxProps) => {
12
+ const [selected, setSelected] = useState(false)
13
+
14
+ const handleCheckboxChange = (value: boolean) => {
15
+ setSelected(!value)
16
+ }
17
+
18
+ return (
19
+ <div className="flex items-center justify-center">
20
+ <label
21
+ className={twMerge(
22
+ className,
23
+ 'relative border-2 w-5 h-5 flex items-center justify-center rounded-sm hover:border-orange-500 hover:cursor-pointer',
24
+ selected
25
+ ? 'bg-orange-500 border-orange-500'
26
+ : 'border-neutral-500 hover:shadow-md hover:shadow-orange-500',
27
+ disabled === true && 'opacity-50 cursor-not-allowed',
28
+ )}
29
+ >
30
+ <input
31
+ type="checkbox"
32
+ required={required}
33
+ checked={selected}
34
+ onChange={() => handleCheckboxChange(selected)}
35
+ disabled={disabled}
36
+ className={twMerge(
37
+ 'relative z-10 hidden',
38
+ selected
39
+ ? 'bg-orange-500 border-orange-500'
40
+ : 'border-neutral-500 hover:shadow-md hover:shadow-orange-500',
41
+ )}
42
+ />
43
+ {selected && (
44
+ <FiCheck color="#FFFFFF" size={14} style={{ strokeWidth: 4 }} />
45
+ )}
46
+ </label>
47
+ </div>
48
+ )
49
+ }
@@ -0,0 +1,38 @@
1
+ import { DetailedHTMLProps, FormHTMLAttributes } from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export interface FormProps
5
+ extends DetailedHTMLProps<
6
+ FormHTMLAttributes<HTMLFormElement>,
7
+ HTMLFormElement
8
+ > {
9
+ children: React.ReactNode
10
+ variant?: 'primary' | 'secondary'
11
+ orientation?: 'row' | 'col'
12
+ handleSubmit?: (event: React.FormEvent<HTMLFormElement>) => void
13
+ }
14
+
15
+ export const Form = ({
16
+ className,
17
+ children,
18
+ variant = 'secondary',
19
+ orientation = 'row',
20
+ ...rest
21
+ }: FormProps) => {
22
+ return (
23
+ <form
24
+ className={twMerge(
25
+ 'flex flex-row gap-2 p-6 rounded-md border-2',
26
+ variant === 'primary' &&
27
+ 'text-neutral-800 bg-neutral-200 border-neutral-300',
28
+ variant === 'secondary' &&
29
+ 'text-neutral-200 bg-neutral-600 border-neutral-800',
30
+ orientation === 'col' && 'flex-col',
31
+ className,
32
+ )}
33
+ {...rest}
34
+ >
35
+ {children}
36
+ </form>
37
+ )
38
+ }
@@ -0,0 +1,53 @@
1
+ import { DetailedHTMLProps, HTMLAttributes } from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export interface HeadingProps
5
+ extends DetailedHTMLProps<HTMLAttributes<HTMLHeadElement>, HTMLHeadElement> {
6
+ children: React.ReactNode
7
+ color?: string
8
+ size?:
9
+ | 'sm'
10
+ | 'md'
11
+ | 'lg'
12
+ | 'xl'
13
+ | '2xl'
14
+ | '3xl'
15
+ | '4xl'
16
+ | '5xl'
17
+ | '6xl'
18
+ | '7xl'
19
+ | '8xl'
20
+ | '9xl'
21
+ tag?: 'h1' | 'h2' | 'h3' | 'h4'
22
+ }
23
+
24
+ export const Heading = ({
25
+ children,
26
+ color = 'neutral-800',
27
+ size = 'lg',
28
+ tag = 'h2',
29
+ className,
30
+ }: HeadingProps) => {
31
+ const fontSize = {
32
+ sm: 'text-sm',
33
+ md: 'text-md',
34
+ lg: 'text-lg',
35
+ xl: 'text-xl',
36
+ '2xl': 'text-2xl',
37
+ '3xl': 'text-3xl',
38
+ '4xl': 'text-4xl',
39
+ '5xl': 'text-5xl',
40
+ '6xl': 'text-6xl',
41
+ '7xl': 'text-7xl',
42
+ '8xl': 'text-8xl',
43
+ '9xl': 'text-9xl',
44
+ }[size]
45
+
46
+ const Tag = tag as React.ElementType
47
+
48
+ return (
49
+ <Tag className={twMerge(`text-${color} ${fontSize}`, className)}>
50
+ {children}
51
+ </Tag>
52
+ )
53
+ }
@@ -0,0 +1,154 @@
1
+ import {
2
+ DetailedHTMLProps,
3
+ InputHTMLAttributes,
4
+ useEffect,
5
+ useState,
6
+ forwardRef,
7
+ } from 'react'
8
+ import { twMerge } from 'tailwind-merge'
9
+ import { FiCheck, FiX } from 'react-icons/fi'
10
+ import masks from '../Shared/masks'
11
+
12
+ /**
13
+ * Primary UI component for user interaction
14
+ */
15
+ export interface InputProps
16
+ extends DetailedHTMLProps<
17
+ InputHTMLAttributes<HTMLInputElement>,
18
+ HTMLInputElement
19
+ > {
20
+ disabled?: boolean
21
+ placeholder?: string
22
+ value?: string
23
+ validated?: boolean
24
+ error: boolean
25
+ required?: boolean
26
+ type: 'text' | 'password' | 'date' | 'cpf' | 'phone' | 'cnpj' | 'cep'
27
+ }
28
+
29
+ const Input = forwardRef<HTMLInputElement, InputProps>(
30
+ (
31
+ {
32
+ className,
33
+ disabled,
34
+ placeholder,
35
+ value,
36
+ validated,
37
+ error,
38
+ required,
39
+ type,
40
+ onClick,
41
+ ...rest
42
+ }: InputProps,
43
+ ref,
44
+ ) => {
45
+ const [selected, setSelected] = useState(false)
46
+ const [inputValue, setInputValue] = useState(value)
47
+ const [hasNumber, setHasNumber] = useState(false)
48
+ const [hasSpecialCharacteres, setHasSpecialCharacteres] = useState(false)
49
+ const [hasEightCharacteres, setHasEightCharacteres] = useState(false)
50
+
51
+ const handleFocus = () => {
52
+ setSelected(!selected)
53
+ }
54
+
55
+ const handleBlur = () => {
56
+ setSelected(false)
57
+ }
58
+
59
+ const handleInput = (event: React.FocusEvent<HTMLInputElement>) => {
60
+ setInputValue(event.currentTarget.value)
61
+ checkPassword(event.currentTarget.value as string)
62
+ }
63
+
64
+ useEffect(() => {
65
+ setInputValue(value)
66
+ }, [value])
67
+
68
+ const checkPassword = (value: string) => {
69
+ setHasSpecialCharacteres(value?.match(masks.password[0]) !== null)
70
+ setHasNumber(value?.match(masks.password[1]) !== null)
71
+ setHasEightCharacteres(value?.match(masks.password[2]) !== null)
72
+ }
73
+
74
+ return (
75
+ <>
76
+ {type === 'text' || type === 'password' || type === 'date' ? (
77
+ <>
78
+ <input
79
+ type={type}
80
+ required={required}
81
+ ref={ref}
82
+ className={twMerge(
83
+ 'flex items-center justify-center border-2 border-neutral rounded-sm w-full px-3 py-2 text-md hover:shadow-md hover:shadow-neutral-500 focus:outline-none',
84
+ className,
85
+ disabled === true && 'opacity-50 cursor-not-allowed',
86
+ selected === true && 'border-2 border-orange-500',
87
+ validated === true && 'border-2 border-green-900',
88
+ error === true && 'border-2 border-red-900',
89
+ )}
90
+ onClick={onClick}
91
+ onFocus={handleFocus}
92
+ onChange={handleInput}
93
+ onBlur={handleBlur}
94
+ placeholder={placeholder}
95
+ value={inputValue}
96
+ {...rest}
97
+ />
98
+ {type === 'password' &&
99
+ (!hasEightCharacteres ||
100
+ !hasSpecialCharacteres ||
101
+ !hasNumber) && (
102
+ <ul className="py-1">
103
+ <li className="flex items-center px-2">
104
+ {hasEightCharacteres ? <FiCheck /> : <FiX />}
105
+ <span className="px-1">Pelo menos 8 caracteres</span>
106
+ </li>
107
+ <li className="flex items-center px-2">
108
+ {hasSpecialCharacteres ? <FiCheck /> : <FiX />}
109
+ <span className="px-1">
110
+ Pelo menos 1 símbolo (@, !, $, etc)
111
+ </span>
112
+ </li>
113
+ <li className="flex items-center px-2">
114
+ {hasNumber ? <FiCheck /> : <FiX />}
115
+ <span className="px-1">Deve conter 1 número</span>
116
+ </li>
117
+ </ul>
118
+ )}
119
+ </>
120
+ ) : (
121
+ <input
122
+ type={type}
123
+ required={required}
124
+ ref={ref}
125
+ className={twMerge(
126
+ 'flex items-center justify-center border-2 border-neutral rounded-sm w-full px-3 py-2 text-md hover:shadow-md hover:shadow-neutral-500 focus:outline-none',
127
+ className,
128
+ disabled === true && 'opacity-50 cursor-not-allowed',
129
+ selected === true && 'border-2 border-orange-500',
130
+ error === true && 'border-2 border-red-900',
131
+ )}
132
+ onClick={onClick}
133
+ onFocus={handleFocus}
134
+ onChange={handleInput}
135
+ onBlur={handleBlur}
136
+ placeholder={placeholder}
137
+ value={inputValue}
138
+ {...rest}
139
+ /* mask={masks[type as keyof typeof masks]} */
140
+ />
141
+ )}
142
+ {error === true && (
143
+ <label htmlFor={rest.id} className="text-red-900">
144
+ Campo inválido.
145
+ </label>
146
+ )}
147
+ </>
148
+ )
149
+ },
150
+ )
151
+
152
+ Input.displayName = 'Input'
153
+
154
+ export { Input }
@@ -0,0 +1,39 @@
1
+ import { DetailedHTMLProps, AnchorHTMLAttributes } from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ /**
5
+ * Primary UI component for user interaction
6
+ */
7
+ export interface LinkProps
8
+ extends DetailedHTMLProps<
9
+ AnchorHTMLAttributes<HTMLAnchorElement>,
10
+ HTMLAnchorElement
11
+ > {
12
+ url: string
13
+ newPage: boolean
14
+ disabled?: boolean
15
+ children: string | JSX.Element
16
+ }
17
+
18
+ export const Link = ({
19
+ className,
20
+ disabled,
21
+ url,
22
+ newPage = true,
23
+ onClick,
24
+ ...rest
25
+ }: LinkProps) => {
26
+ return (
27
+ <a
28
+ className={twMerge(
29
+ 'text-blue-800 font-bold',
30
+ typeof rest.children !== 'string' && 'px-4',
31
+ disabled === true && 'opacity-50 cursor-not-allowed',
32
+ className,
33
+ )}
34
+ href={url}
35
+ {...(newPage && { target: '_blank', rel: 'noopener noreferrer' })}
36
+ {...rest}
37
+ />
38
+ )
39
+ }
@@ -0,0 +1,35 @@
1
+ import { DetailedHTMLProps, HTMLAttributes } from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+ import { Text } from '../Text'
4
+
5
+ export interface MultiStepProps
6
+ extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
7
+ size: number
8
+ currentStep?: number
9
+ }
10
+
11
+ export const MultiStep = ({ className, size, currentStep }: MultiStepProps) => {
12
+ return (
13
+ <div className="w-full">
14
+ <Text tag="label" color="neutral-100" size="xs">
15
+ {`Passo ${currentStep} de ${size}`}
16
+ </Text>
17
+ <div className={`grid gap-2 grid-cols-${size} grid-flow-col mt-1`}>
18
+ {Array.from(Array(size).keys()).map((_, index) => {
19
+ return (
20
+ <div
21
+ key={index}
22
+ className={twMerge(
23
+ 'h-1 rounded-full',
24
+ currentStep && index < currentStep
25
+ ? 'bg-orange-500'
26
+ : 'bg-neutral-500',
27
+ className,
28
+ )}
29
+ />
30
+ )
31
+ })}
32
+ </div>
33
+ </div>
34
+ )
35
+ }