@tecsinapse/cortex-react 1.0.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/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # `cortex-react`
2
+
3
+ > TODO: description
4
+
5
+ ## Usage
6
+
7
+ ```
8
+ const index = require('cortex-react');
9
+
10
+ // TODO: DEMONSTRATE API
11
+ ```
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@tecsinapse/cortex-react",
3
+ "version": "1.0.2",
4
+ "description": "React components based in @tecsinapse/cortex-core",
5
+ "author": "ryancarloscorrea <ryancarlos38@gmail.com>",
6
+ "homepage": "https://github.com/tecsinapse/design-system#readme",
7
+ "license": "MIT",
8
+ "main": "src/index.ts",
9
+ "files": [
10
+ "src"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/tecsinapse/design-system.git"
15
+ },
16
+ "scripts": {
17
+ "dev": "rollup --config --watch",
18
+ "dev:dts": "tsc --project tsconfig.build.json --watch",
19
+ "build:es": "rollup --config",
20
+ "build:dts": "tsc --project tsconfig.build.json"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/tecsinapse/design-system/issues"
24
+ },
25
+ "peerDependencies": {
26
+ "tailwind": ">=3.3.0"
27
+ },
28
+ "dependencies": {
29
+ "@tecsinapse/cortex-core": "0.1.10",
30
+ "clsx": "*",
31
+ "react-icons": "^5.2.1"
32
+ }
33
+ }
@@ -0,0 +1,45 @@
1
+ import React, { forwardRef, HTMLProps } from 'react';
2
+ import { badge, BadgeVariants, containerBadge } from '@tecsinapse/cortex-core';
3
+
4
+ interface BadgeProps {
5
+ value: string | number;
6
+ variants?: BadgeVariants;
7
+ }
8
+
9
+ interface BadgeAnchorProps extends BadgeProps {
10
+ children: JSX.Element;
11
+ }
12
+
13
+ export const Badge = forwardRef<
14
+ HTMLDivElement,
15
+ BadgeProps & Omit<HTMLProps<HTMLDivElement>, 'className'>
16
+ >((props, ref) => {
17
+ const { value, variants, ...rest } = props;
18
+ return (
19
+ <div
20
+ ref={ref}
21
+ className={badge({
22
+ className: `relative ${variants?.className}`,
23
+ intent: variants?.intent,
24
+ })}
25
+ {...rest}
26
+ >
27
+ {value}
28
+ </div>
29
+ );
30
+ });
31
+
32
+ export const BadgeAnchor = forwardRef<
33
+ HTMLDivElement,
34
+ BadgeAnchorProps & Omit<HTMLProps<HTMLDivElement>, 'className'>
35
+ >((props, ref) => {
36
+ const { value, variants, children, ...rest } = props;
37
+ return (
38
+ <div className={containerBadge()}>
39
+ {children}
40
+ <div ref={ref} className={badge(variants)} {...rest}>
41
+ {value}
42
+ </div>
43
+ </div>
44
+ );
45
+ });
@@ -0,0 +1,19 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { button, ButtonVariants } from '@tecsinapse/cortex-core';
3
+
4
+ interface ButtonProps {
5
+ variants?: ButtonVariants;
6
+ children?: JSX.Element;
7
+ }
8
+
9
+ export const Button = forwardRef<
10
+ HTMLButtonElement,
11
+ ButtonProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className'>
12
+ >((props, ref) => {
13
+ const { variants, children, ...rest } = props;
14
+ return (
15
+ <button className={button(variants)} ref={ref} {...rest}>
16
+ {children}
17
+ </button>
18
+ );
19
+ });
@@ -0,0 +1,18 @@
1
+ import React, { forwardRef, HTMLProps } from 'react'
2
+ import { card } from '@tecsinapse/cortex-core'
3
+
4
+ interface CardProps {
5
+ children: JSX.Element
6
+ }
7
+ export const Card = forwardRef<HTMLDivElement, CardProps & HTMLProps<HTMLDivElement>>((props, ref) => {
8
+ const { children, className, ...rest } = props
9
+ return (
10
+ <div
11
+ className={card({ className })}
12
+ ref={ref}
13
+ {...rest}
14
+ >
15
+ {children}
16
+ </div>
17
+ )
18
+ })
@@ -0,0 +1,31 @@
1
+ import React, { forwardRef, HTMLProps } from 'react'
2
+ import { hint, HintVariants } from '@tecsinapse/cortex-core'
3
+
4
+ interface HintProps {
5
+ children?: never
6
+ description: string
7
+ variants?: HintVariants
8
+ }
9
+
10
+ interface HintPropsWithChildrenProps {
11
+ children: JSX.Element
12
+ description?: never
13
+ variants?: HintVariants
14
+ }
15
+
16
+ type HintPropsUnion = HintProps | HintPropsWithChildrenProps
17
+
18
+ export const Hint = forwardRef<HTMLDivElement, HintPropsUnion & Omit<HTMLProps<HTMLDivElement>, 'className'>>(
19
+ (props, ref) => {
20
+ const { description, children, variants } = props
21
+ return (
22
+ <div
23
+ className={hint(variants)}
24
+ ref={ref}
25
+ >
26
+ {description ? <p>{description}</p> : <></>}
27
+ {children}
28
+ </div>
29
+ )
30
+ }
31
+ )
@@ -0,0 +1,110 @@
1
+ import React from 'react';
2
+ import {
3
+ input,
4
+ InputBaseVariants,
5
+ inputBox,
6
+ labelStyle,
7
+ } from '@tecsinapse/cortex-core';
8
+ import { clsx } from 'clsx';
9
+
10
+ const getValidChildren = (children: React.ReactNode) => {
11
+ return React.Children.toArray(children).filter(el =>
12
+ React.isValidElement(el)
13
+ ) as React.ReactElement[];
14
+ };
15
+
16
+ interface InputPropsBase {
17
+ variants?: InputBaseVariants;
18
+ label?: string;
19
+ }
20
+
21
+ export interface InputProps
22
+ extends React.InputHTMLAttributes<HTMLInputElement>,
23
+ InputPropsBase {}
24
+
25
+ export const Box = React.forwardRef<HTMLInputElement, InputProps>(
26
+ ({ id, name, variants, label, placeholder, className, ...rest }, ref) => {
27
+ return (
28
+ <div className={'flex w-full flex-col'}>
29
+ <input
30
+ id={id ?? name}
31
+ name={name}
32
+ placeholder={placeholder ?? ' '}
33
+ className={clsx(inputBox(placeholder, label, className))}
34
+ {...rest}
35
+ ref={ref}
36
+ />
37
+ <label
38
+ htmlFor={id ?? name}
39
+ className={labelStyle({ intent: variants?.intent, placeholder })}
40
+ >
41
+ {label}
42
+ </label>
43
+ </div>
44
+ );
45
+ }
46
+ );
47
+
48
+ type DivBaseProps = React.HTMLAttributes<HTMLDivElement>;
49
+ type InputFaceProps = DivBaseProps & Pick<InputPropsBase, 'variants'>;
50
+
51
+ export const Face = React.forwardRef<HTMLDivElement, InputFaceProps>(
52
+ ({ children, variants, className, ...rest }, ref) => {
53
+ const clones = getValidChildren(children).map(el => {
54
+ return React.cloneElement(el, { ...el.props, variants });
55
+ });
56
+ return (
57
+ <div
58
+ {...rest}
59
+ className={clsx(input(variants), className)}
60
+ id={'input-face'}
61
+ ref={ref}
62
+ >
63
+ {clones}
64
+ </div>
65
+ );
66
+ }
67
+ );
68
+
69
+ export const Root = React.forwardRef<HTMLInputElement, InputProps>(
70
+ ({ variants, className, ...rest }, ref) => {
71
+ return (
72
+ <Face variants={variants} className={className}>
73
+ <Box {...rest} ref={ref} />
74
+ </Face>
75
+ );
76
+ }
77
+ );
78
+
79
+ type InputElementsProps = DivBaseProps & {
80
+ children: React.ReactNode;
81
+ className?: string;
82
+ };
83
+
84
+ export const Left = React.forwardRef<HTMLDivElement, InputElementsProps>(
85
+ ({ children, className, ...rest }, ref) => {
86
+ return (
87
+ <div className={clsx(className, 'mr-2.5')} {...rest} ref={ref}>
88
+ {children}
89
+ </div>
90
+ );
91
+ }
92
+ );
93
+
94
+ export const Right = React.forwardRef<HTMLDivElement, InputElementsProps>(
95
+ ({ children, className, ...rest }, ref) => {
96
+ return (
97
+ <div className={clsx(className, 'ml-2.5')} {...rest} ref={ref}>
98
+ {children}
99
+ </div>
100
+ );
101
+ }
102
+ );
103
+
104
+ export const Input = {
105
+ Root,
106
+ Face,
107
+ Box,
108
+ Left,
109
+ Right,
110
+ };
@@ -0,0 +1,24 @@
1
+ import React, { forwardRef, InputHTMLAttributes } from 'react'
2
+ import { modal, overlay } from '@tecsinapse/cortex-core'
3
+
4
+ interface ModalProps {
5
+ open: boolean
6
+ onClose: () => void
7
+ children: JSX.Element
8
+ }
9
+
10
+ export const Modal = forwardRef<HTMLDivElement, ModalProps & InputHTMLAttributes<HTMLInputElement>>((props, ref) => {
11
+ const { open, onClose, children, className } = props
12
+ return (
13
+ <div
14
+ ref={ref}
15
+ {...props}
16
+ >
17
+ <div
18
+ className={overlay({ show: open })}
19
+ onClick={onClose}
20
+ ></div>
21
+ <dialog className={modal({ open, className })}>{children}</dialog>
22
+ </div>
23
+ )
24
+ })
@@ -0,0 +1,86 @@
1
+ // import { Button, Input, searchInputVariants } from '@web/modules/core'
2
+ import React, { useEffect, useState } from 'react';
3
+ // import CircularLoading from './CircularLoading'
4
+ import { Input, Button } from './';
5
+ import { AiOutlineLoading } from 'react-icons/ai';
6
+ import { IoSearchOutline } from 'react-icons/io5';
7
+ import { useDebouncedState } from '../hooks';
8
+
9
+ interface SearchInputProps {
10
+ label?: string;
11
+ placeholder?: string;
12
+ isSubmitting?: boolean;
13
+ onChange?: (value: string) => void;
14
+ onClick?: (value: string) => void;
15
+ BOUNCE_TIMEOUT?: number;
16
+ }
17
+
18
+ const inputFace = 'bg-white w-full';
19
+ const inputLeft = 'flex items-center';
20
+ const SearchInput = ({
21
+ label,
22
+ placeholder,
23
+ isSubmitting = false,
24
+ onChange,
25
+ onClick,
26
+ BOUNCE_TIMEOUT = 1000,
27
+ }: SearchInputProps) => {
28
+ const [bouncedText, setBouncedText] = useState<string>('');
29
+ const [searchInput, setSearchInput] = useDebouncedState<string>(
30
+ '',
31
+ setBouncedText,
32
+ BOUNCE_TIMEOUT
33
+ );
34
+
35
+ useEffect(() => {
36
+ if (onChange) {
37
+ onChange(bouncedText);
38
+ }
39
+ }, [bouncedText]);
40
+
41
+ const handleEnterKey = (e: React.KeyboardEvent) => {
42
+ if (e.key === 'Enter' && onClick && searchInput) {
43
+ onClick(searchInput);
44
+ }
45
+ };
46
+
47
+ return (
48
+ <div className="flex flex-row w-full space-x-mili">
49
+ <Input.Face variants={{ className: inputFace }}>
50
+ {!onClick && (
51
+ <Input.Left className={inputLeft}>
52
+ <IoSearchOutline />
53
+ </Input.Left>
54
+ )}
55
+ <Input.Box
56
+ placeholder={placeholder}
57
+ label={label}
58
+ onChange={e => setSearchInput(e.target.value)}
59
+ onKeyDown={handleEnterKey}
60
+ disabled={isSubmitting}
61
+ />
62
+ </Input.Face>
63
+ {onClick && (
64
+ <Button
65
+ variants={{
66
+ intent: 'primary',
67
+ size: 'square',
68
+ className: 'h-11',
69
+ }}
70
+ onClick={() => onClick(searchInput)}
71
+ disabled={!searchInput || isSubmitting}
72
+ >
73
+ {isSubmitting ? (
74
+ <div className={'animate-spin'}>
75
+ <AiOutlineLoading />
76
+ </div>
77
+ ) : (
78
+ <IoSearchOutline />
79
+ )}
80
+ </Button>
81
+ )}
82
+ </div>
83
+ );
84
+ };
85
+
86
+ export default SearchInput;
@@ -0,0 +1,170 @@
1
+ import { option as styleOption, selectVariants } from '@tecsinapse/cortex-core';
2
+ import React, {
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
9
+ import { Hint } from './Hint';
10
+ import { IoChevronDownOutline } from 'react-icons/io5';
11
+ import { IoIosCloseCircleOutline } from 'react-icons/io';
12
+ import SearchInput from './SearchInput';
13
+
14
+ interface CommonProps<T> {
15
+ label: string;
16
+ value: T | undefined;
17
+ onSelect: (value: T) => void;
18
+ keyExtractor: (value: T) => string;
19
+ labelExtractor: (value: T) => string;
20
+ onSearch?: (search: string) => void;
21
+ disabled?: boolean;
22
+ grouped?: boolean;
23
+ variant?: 'error' | 'default';
24
+ hint?: string;
25
+ placeholderSearchInput?: string;
26
+ }
27
+
28
+ interface GroupedProps<T> {
29
+ options?: Map<string, T[]>;
30
+ groupedLabelExtractor: (value: string) => string;
31
+ grouped: true;
32
+ }
33
+
34
+ interface DefaultProps<T> {
35
+ options?: T[];
36
+ groupedLabelExtractor?: never;
37
+ grouped?: never;
38
+ }
39
+
40
+ type ConditionalProps<T> = GroupedProps<T> | DefaultProps<T>;
41
+
42
+ type SelectProps<T> = CommonProps<T> & ConditionalProps<T>;
43
+
44
+ const { button, dropdown, groupedTitle, containerGrouped, hintBody } =
45
+ selectVariants();
46
+
47
+ export const Select = <T,>(props: SelectProps<T>) => {
48
+ const {
49
+ label,
50
+ keyExtractor,
51
+ labelExtractor,
52
+ options,
53
+ value,
54
+ onSelect,
55
+ onSearch,
56
+ disabled,
57
+ grouped,
58
+ groupedLabelExtractor,
59
+ hint,
60
+ placeholderSearchInput,
61
+ variant = 'default',
62
+ } = props;
63
+ const [open, setOpen] = useState(false);
64
+ const placeholder = useMemo(
65
+ () => (value ? labelExtractor(value) : label),
66
+ [label, labelExtractor, value]
67
+ );
68
+ const ref = useRef<HTMLDivElement>(null);
69
+
70
+ const handleClickOutside = useCallback((event: any) => {
71
+ if (ref.current && !ref.current.contains(event.target)) {
72
+ setOpen(false);
73
+ }
74
+ }, []);
75
+
76
+ const handleSelect = useCallback(
77
+ (option: T) => {
78
+ onSelect(option);
79
+ setOpen(false);
80
+ },
81
+ [onSelect]
82
+ );
83
+
84
+ useEffect(() => {
85
+ document.addEventListener('click', handleClickOutside, true);
86
+ return () => {
87
+ document.removeEventListener('click', handleClickOutside, true);
88
+ };
89
+ }, [handleClickOutside]);
90
+
91
+ const Option = ({ option }: { option: T }) => (
92
+ <li
93
+ onClick={() => handleSelect(option)}
94
+ className={styleOption()}
95
+ role={'option'}
96
+ >
97
+ {labelExtractor(option)}
98
+ </li>
99
+ );
100
+
101
+ const GroupedOptions = ({ options }: { options?: Map<string, T[]> }) => (
102
+ <>
103
+ {[...(options ?? [])].map(([key, value]) => (
104
+ <div key={key} className={containerGrouped()}>
105
+ <span className={groupedTitle()}>{groupedLabelExtractor?.(key)}</span>
106
+ {value.map((option: T) => (
107
+ <Option option={option} key={keyExtractor(option)} />
108
+ ))}
109
+ </div>
110
+ ))}
111
+ </>
112
+ );
113
+
114
+ const DefaultOptions = ({ options }: { options?: T[] }) => (
115
+ <>
116
+ {(options ?? []).map(option => (
117
+ <Option option={option} key={keyExtractor(option)} />
118
+ ))}
119
+ </>
120
+ );
121
+
122
+ return (
123
+ <div className="w-full relative bg-white" ref={ref}>
124
+ <button
125
+ className={button({ disabled, intent: variant })}
126
+ onClick={() => setOpen(prevState => !prevState)}
127
+ disabled={disabled}
128
+ >
129
+ <span>{placeholder}</span>
130
+ <IoChevronDownOutline />
131
+ </button>
132
+ <ul
133
+ role={'select'}
134
+ className={dropdown({
135
+ open,
136
+ })}
137
+ >
138
+ {onSearch ? (
139
+ <div className="m-mili">
140
+ <SearchInput
141
+ placeholder={placeholderSearchInput}
142
+ onChange={onSearch}
143
+ />
144
+ </div>
145
+ ) : (
146
+ <></>
147
+ )}
148
+ {grouped ? (
149
+ <GroupedOptions options={options} />
150
+ ) : (
151
+ <DefaultOptions options={options} />
152
+ )}
153
+ </ul>
154
+ {hint && (
155
+ <Hint
156
+ variants={{
157
+ intent: variant,
158
+ }}
159
+ >
160
+ <div className={hintBody()}>
161
+ {variant === 'error' ? <IoIosCloseCircleOutline /> : <></>}
162
+ <span>{hint}</span>
163
+ </div>
164
+ </Hint>
165
+ )}
166
+ </div>
167
+ );
168
+ };
169
+
170
+ export default Select;
@@ -0,0 +1,26 @@
1
+ import React, { forwardRef } from 'react'
2
+ import { snackbar, SnackbarVariants } from '@tecsinapse/cortex-core'
3
+
4
+ interface SnackbarProps {
5
+ variants?: SnackbarVariants
6
+ children: JSX.Element
7
+ show: boolean
8
+ }
9
+
10
+ export const Snackbar = forwardRef<HTMLDivElement, SnackbarProps>((props, ref) => {
11
+ const { children, show, variants } = props
12
+ return (
13
+ <>
14
+ {show ? (
15
+ <div
16
+ className={snackbar(variants)}
17
+ ref={ref}
18
+ >
19
+ {children}
20
+ </div>
21
+ ) : (
22
+ <></>
23
+ )}
24
+ </>
25
+ )
26
+ })
@@ -0,0 +1,19 @@
1
+ import React, { forwardRef, HTMLProps } from 'react'
2
+ import { tag, TagVariants } from '@tecsinapse/cortex-core'
3
+
4
+ interface TagProps {
5
+ variants?: TagVariants
6
+ label: string
7
+ }
8
+
9
+ export const Tag = forwardRef<HTMLDivElement, TagProps & HTMLProps<HTMLDivElement>>((props, ref) => {
10
+ const { label, variants } = props
11
+ return (
12
+ <div
13
+ className={tag(variants)}
14
+ ref={ref}
15
+ >
16
+ <p>{label}</p>
17
+ </div>
18
+ )
19
+ })