@bodynarf/react.components 0.1.0

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.
Files changed (44) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +25 -0
  2. package/dist/assets/bootstrap-icons.48902042.woff +0 -0
  3. package/dist/assets/bootstrap-icons.a29357cb.woff2 +0 -0
  4. package/dist/assets/index.19ed8c21.css +1 -0
  5. package/dist/assets/index.517219b4.js +2 -0
  6. package/dist/assets/index.517219b4.js.map +1 -0
  7. package/dist/assets/vendor.92043335.js +41 -0
  8. package/dist/assets/vendor.92043335.js.map +1 -0
  9. package/dist/index.html +16 -0
  10. package/index.html +13 -0
  11. package/package.json +44 -0
  12. package/src/components/anchor/anchor.scss +15 -0
  13. package/src/components/anchor/components/anchorWithIcon/anchorWithIcon.tsx +45 -0
  14. package/src/components/anchor/components/simpleAnchor/simpleAnchor.tsx +16 -0
  15. package/src/components/anchor/index.tsx +64 -0
  16. package/src/components/anchor/types.ts +26 -0
  17. package/src/components/button/button.scss +12 -0
  18. package/src/components/button/components/buttonWithIcon/buttonWithIcon.tsx +47 -0
  19. package/src/components/button/components/simpleButton/simpleButton.tsx +16 -0
  20. package/src/components/button/index.tsx +83 -0
  21. package/src/components/button/types.ts +40 -0
  22. package/src/components/dropdown/components/dropdownItem/dropdownItem.tsx +30 -0
  23. package/src/components/dropdown/components/dropdownLabel/dropdownLabel.tsx +60 -0
  24. package/src/components/dropdown/dropdown.scss +89 -0
  25. package/src/components/dropdown/index.tsx +141 -0
  26. package/src/components/dropdown/types.ts +11 -0
  27. package/src/components/icon/icon.scss +18 -0
  28. package/src/components/icon/index.tsx +34 -0
  29. package/src/components/primitives/date/index.tsx +103 -0
  30. package/src/components/primitives/multiline/components/multilineWithLabel/index.tsx +93 -0
  31. package/src/components/primitives/multiline/components/multilineWithoutLabel/index.tsx +51 -0
  32. package/src/components/primitives/multiline/index.tsx +28 -0
  33. package/src/components/primitives/text/components/textWithLabel/index.tsx +92 -0
  34. package/src/components/primitives/text/components/textWithoutLabel/index.tsx +49 -0
  35. package/src/components/primitives/text/index.tsx +21 -0
  36. package/src/components/primitives/types.ts +65 -0
  37. package/src/components/search/index.tsx +127 -0
  38. package/src/components/search/search.scss +24 -0
  39. package/src/components/types.ts +41 -0
  40. package/src/hooks/useComponentOutsideClick.ts +48 -0
  41. package/src/main.tsx +9 -0
  42. package/tsconfig.json +48 -0
  43. package/tsconfig.node.json +8 -0
  44. package/vite.config.ts +14 -0
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <base href="/"/>
5
+ <meta charset="UTF-8">
6
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
7
+ <title>bodynarf/react-components</title>
8
+ <script type="module" crossorigin src="/assets/index.517219b4.js"></script>
9
+ <link rel="modulepreload" href="/assets/vendor.92043335.js">
10
+ <link rel="stylesheet" href="/assets/index.19ed8c21.css">
11
+ </head>
12
+ <body>
13
+ <div id="root"></div>
14
+
15
+ </body>
16
+ </html>
package/index.html ADDED
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <base href="/"/>
5
+ <meta charset="UTF-8">
6
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
7
+ <title>bodynarf/react-components</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@bodynarf/react.components",
3
+ "version": "0.1.0",
4
+ "author": {
5
+ "name": "Artem",
6
+ "email": "bodynar@gmail.com"
7
+ },
8
+ "repository": {
9
+ "url": "https://github.com/bodynar/bodynarf.react-components",
10
+ "type": "git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/bodynar/bodynarf.react-components/issues",
14
+ "email": "bodynar@gmail.com"
15
+ },
16
+ "keywords": [
17
+ "ts",
18
+ "typescript",
19
+ "react",
20
+ "react component",
21
+ "bulma"
22
+ ],
23
+ "scripts": {
24
+ "dev": "vite",
25
+ "build": "tsc && vite build",
26
+ "preview": "vite preview"
27
+ },
28
+ "dependencies": {
29
+ "bootstrap-icons": "^1.8.1",
30
+ "react": "^18.0.0",
31
+ "react-dom": "^18.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@bodynarf/utils": "^1.0.0",
35
+ "@types/node": "^17.0.25",
36
+ "@types/react": "^18.0.5",
37
+ "@types/react-dom": "^18.0.1",
38
+ "@vitejs/plugin-react": "^1.3.0",
39
+ "bulma": "^0.9.3",
40
+ "sass": "^1.50.1",
41
+ "typescript": "^4.6.3",
42
+ "vite": "^2.9.2"
43
+ }
44
+ }
@@ -0,0 +1,15 @@
1
+ .app-anchor {
2
+ text-decoration: none;
3
+ color: inherit;
4
+
5
+ transition: 0.15s ease-in-out color;
6
+
7
+ &:not(&--unhoverable):hover {
8
+ text-decoration: underline;
9
+ color: #3273dc;
10
+ }
11
+
12
+ .app-icon {
13
+ transition: none;
14
+ }
15
+ }
@@ -0,0 +1,45 @@
1
+ import { isNullOrEmpty } from '@bodynarf/utils/common';
2
+
3
+ import Icon from '@app/components/icon';
4
+
5
+ import { AnchorWithIconProps } from "../../types";
6
+
7
+ /** Anchor with icon component */
8
+ export const AnchorWithIcon = ({ href, className, onClick, caption, title, target, icon }: AnchorWithIconProps): JSX.Element => {
9
+ const iconPosition = icon.position || 'left';
10
+
11
+ const iconClassName: string = isNullOrEmpty(caption)
12
+ ? icon.className
13
+ : iconPosition === 'left'
14
+ ? `${icon.className} app-icon--left`
15
+ : `${icon.className} app-icon--right`;
16
+
17
+
18
+ if (iconPosition === 'left') {
19
+ return (
20
+ <a
21
+ href={href}
22
+ className={className}
23
+ title={title}
24
+ target={target}
25
+ onClick={onClick}
26
+ >
27
+ <Icon {...icon} className={iconClassName} />
28
+ {caption}
29
+ </a>
30
+ );
31
+ }
32
+
33
+ return (
34
+ <a
35
+ href={href}
36
+ className={className}
37
+ title={title}
38
+ target={target}
39
+ onClick={onClick}
40
+ >
41
+ {caption}
42
+ <Icon {...icon} className={iconClassName} />
43
+ </a>
44
+ );
45
+ };
@@ -0,0 +1,16 @@
1
+ import { SimpleAnchorProps } from "../../types";
2
+
3
+ /** Simple anchor component, without icon */
4
+ export const SimpleAnchor = ({ href, className, onClick, caption, title, target }: SimpleAnchorProps): JSX.Element => {
5
+ return (
6
+ <a
7
+ className={className}
8
+ href={href}
9
+ title={title}
10
+ target={target}
11
+ onClick={onClick}
12
+ >
13
+ {caption}
14
+ </a>
15
+ );
16
+ };
@@ -0,0 +1,64 @@
1
+ import { isNullOrEmpty, isNullOrUndefined } from '@bodynarf/utils/common';
2
+
3
+ import './anchor.scss';
4
+
5
+ import { ElementIcon } from '../types';
6
+
7
+ import { SimpleAnchor } from './components/simpleAnchor/simpleAnchor';
8
+ import { AnchorWithIcon } from './components/anchorWithIcon/anchorWithIcon';
9
+
10
+ export type AnchorProps = {
11
+ /** Link destination */
12
+ href?: string;
13
+
14
+ /** Link caption */
15
+ caption?: string;
16
+
17
+ /** Click handler */
18
+ onClick?: () => void;
19
+
20
+ /** Configuration od inner icon */
21
+ icon?: ElementIcon;
22
+
23
+ /** Title of anchor */
24
+ title?: string;
25
+
26
+ /** Where to open the linked document */
27
+ target?: '_blank' | '_top';
28
+
29
+ /** Additional class names */
30
+ className?: string;
31
+
32
+ /** Should css hovering effects be disabled */
33
+ disableHovering?: boolean;
34
+ };
35
+
36
+ /** Anchor component */
37
+ export default function Anchor(props: AnchorProps): JSX.Element {
38
+ if (isNullOrUndefined(props.caption) && isNullOrUndefined(props.icon)) {
39
+ throw new Error("No anchor content provided");
40
+ }
41
+
42
+ const className: string = 'app-anchor'
43
+ + (!isNullOrEmpty(props.className) ? ` ${props.className}` : '')
44
+ + (props.disableHovering === true ? ' app-anchor--unhoverable' : '');
45
+
46
+ if (isNullOrUndefined(props.icon)) {
47
+ return (
48
+ <SimpleAnchor
49
+ {...props}
50
+ className={className}
51
+ onClick={props.onClick}
52
+ />
53
+ );
54
+ }
55
+
56
+ return (
57
+ <AnchorWithIcon
58
+ {...props}
59
+ className={className}
60
+ onClick={props.onClick}
61
+ icon={props.icon as ElementIcon}
62
+ />
63
+ );
64
+ }
@@ -0,0 +1,26 @@
1
+ import { ElementIcon } from "../types";
2
+
3
+ export type SimpleAnchorProps = {
4
+ /** Link destination */
5
+ href?: string;
6
+
7
+ /** Class names */
8
+ className: string;
9
+
10
+ /** Click handler */
11
+ onClick?: () => void;
12
+
13
+ /** Link caption */
14
+ caption?: string;
15
+
16
+ /** Title of anchor */
17
+ title?: string;
18
+
19
+ /** Where to open the linked document */
20
+ target?: '_blank' | '_top';
21
+ };
22
+
23
+ export type AnchorWithIconProps = SimpleAnchorProps & {
24
+ /** Configuration of icon */
25
+ icon: ElementIcon;
26
+ };
@@ -0,0 +1,12 @@
1
+ .button {
2
+ transition: 0.15s ease-in-out;
3
+ transition-property: background-color, border-color;
4
+
5
+ &--icon-only {
6
+ padding: 0.55rem;
7
+ }
8
+
9
+ &.is-warning.is-outlined:not([disabled]):hover {
10
+ color: #000;
11
+ }
12
+ }
@@ -0,0 +1,47 @@
1
+
2
+ import { isNullOrEmpty } from '@bodynarf/utils/common';
3
+
4
+ import Icon from '@app/components/icon';
5
+
6
+ import { ButtonWithIconProps } from '../../types';
7
+
8
+ /** Button with icon component */
9
+ export const ButtonWithIcon = ({ className, disabled, onClick, caption, title, icon }: ButtonWithIconProps): JSX.Element => {
10
+ const iconPosition = icon.position || 'left';
11
+
12
+ const iconClassName: string = isNullOrEmpty(caption)
13
+ ? icon.className
14
+ : iconPosition === 'left'
15
+ ? `${icon.className} app-icon--left`
16
+ : `${icon.className} app-icon--right`;
17
+
18
+ className = isNullOrEmpty(caption)
19
+ ? `${className} button--icon-only`
20
+ : className;
21
+
22
+ if (iconPosition === 'left') {
23
+ return (
24
+ <button
25
+ className={className}
26
+ disabled={disabled}
27
+ onClick={onClick}
28
+ title={title}
29
+ >
30
+ <Icon {...icon} className={iconClassName} />
31
+ {caption}
32
+ </button>
33
+ );
34
+ }
35
+
36
+ return (
37
+ <button
38
+ className={className}
39
+ disabled={disabled}
40
+ onClick={onClick}
41
+ title={title}
42
+ >
43
+ {caption}
44
+ <Icon {...icon} className={iconClassName} />
45
+ </button>
46
+ );
47
+ };
@@ -0,0 +1,16 @@
1
+
2
+ import { SimpleButtonProps } from '../../types';
3
+
4
+ /** Simple button component, without icon */
5
+ export const SimpleButton = ({ className, disabled, onClick, caption, title }: SimpleButtonProps): JSX.Element => {
6
+ return (
7
+ <button
8
+ className={className}
9
+ disabled={disabled}
10
+ onClick={onClick}
11
+ title={title}
12
+ >
13
+ {caption}
14
+ </button>
15
+ );
16
+ };
@@ -0,0 +1,83 @@
1
+ import { isNullOrEmpty, isNullOrUndefined, isStringEmpty } from '@bodynarf/utils/common';
2
+
3
+ import './button.scss';
4
+
5
+ import { ElementIcon, IconSize } from '../types';
6
+
7
+ import { ButtonType } from './types';
8
+ import { ButtonWithIcon } from './components/buttonWithIcon/buttonWithIcon';
9
+ import { SimpleButton } from './components/simpleButton/simpleButton';
10
+
11
+ type ButtonProps = {
12
+ /** Button displaying text */
13
+ caption?: string;
14
+
15
+ /** Type of button (color) */
16
+ type: ButtonType;
17
+
18
+ /** Configuration of inner icon */
19
+ icon?: ElementIcon;
20
+
21
+ /** Button size */
22
+ size?: IconSize; // TODO: fix this type using
23
+
24
+ /** Title on hover */
25
+ title?: string;
26
+
27
+ /** Is button uses light version of color */
28
+ light?: boolean;
29
+
30
+ /** Is button outlined */
31
+ outlined?: boolean;
32
+
33
+ /** Should button corners be rounded */
34
+ rounded?: boolean;
35
+
36
+ /** Display loading icon */
37
+ isLoading?: boolean;
38
+
39
+ /** Is button disabled */
40
+ disabled?: boolean;
41
+
42
+ /** Click action handler */
43
+ onClick?: () => void;
44
+ };
45
+
46
+ /**
47
+ * Button component
48
+ * @throws Caption is not defined and icon configuration is not defined at the same time
49
+ */
50
+ export default function Button(props: ButtonProps): JSX.Element {
51
+ if ((isNullOrEmpty(props.caption))
52
+ && (isNullOrUndefined(props.icon) || isStringEmpty(props.icon?.className as string))
53
+ ) {
54
+ throw new Error("No button content provided.");
55
+ }
56
+
57
+ const className: string =
58
+ `button is-${props.type}`
59
+ + (props.light === true ? ' is-light' : '')
60
+ + (!isNullOrUndefined(props.size) ? ` is-${props.size}` : '')
61
+ + (props.outlined === true ? ' is-outlined' : '')
62
+ + (props.rounded === true ? ' is-rounded' : '')
63
+ + (props.isLoading === true ? ' is-loading' : '');
64
+
65
+ if (!isNullOrUndefined(props.icon)) {
66
+ return (
67
+ <ButtonWithIcon
68
+ {...props}
69
+ className={className}
70
+ onClick={props.onClick}
71
+ icon={props.icon as ElementIcon}
72
+ />
73
+ );
74
+ } else {
75
+ return (
76
+ <SimpleButton
77
+ {...props}
78
+ className={className}
79
+ onClick={props.onClick}
80
+ />
81
+ );
82
+ }
83
+ }
@@ -0,0 +1,40 @@
1
+ import { ElementIcon } from "../types";
2
+
3
+ /** Button types according to Bulma framework */
4
+ export type ButtonType =
5
+ 'default' /** color: transparent */
6
+ | 'primary' /** color: seawave green */
7
+ | 'link' /** color: blue-violet */
8
+ | 'info' /** color: sky-blue */
9
+ | 'success' /** color: green */
10
+ | 'warning' /** color: yellow */
11
+ | 'danger' /** color: red */
12
+ | 'white' /** color: white */
13
+ | 'light' /** color: light-gray */
14
+ | 'dark' /** color: dark-gray */
15
+ | 'black' /** color: black */
16
+ | 'text' /** Underline text with color: gray */
17
+ | 'ghost' /** Blue underline text with color: transparent */
18
+ ;
19
+
20
+ export type SimpleButtonProps = {
21
+ /** Button class name*/
22
+ className: string;
23
+
24
+ /** Button click handler */
25
+ onClick?: () => void;
26
+
27
+ /** Button caption */
28
+ caption?: string;
29
+
30
+ /** Disabled attribute value*/
31
+ disabled?: boolean;
32
+
33
+ /** Title on hover */
34
+ title?: string;
35
+ };
36
+
37
+ export type ButtonWithIconProps = SimpleButtonProps & {
38
+ /** Icon configuration */
39
+ icon: ElementIcon;
40
+ };
@@ -0,0 +1,30 @@
1
+ import { SelectableItem } from "../../types";
2
+
3
+ /** Dropdown item props */
4
+ interface DropdownItemProps {
5
+ /** Item to present in dropdown */
6
+ item: SelectableItem;
7
+
8
+ /** Is item selected*/
9
+ selected: boolean;
10
+
11
+ /** Item click handler */
12
+ onClick: (event: React.MouseEvent<HTMLLIElement>) => void;
13
+ }
14
+
15
+ /** Single item in dropdown component */
16
+ const DropdownItem = ({ item, selected, onClick }: DropdownItemProps): JSX.Element => {
17
+ return (
18
+ <li
19
+ key={item.id}
20
+ className={`app-dropdown-item dropdown-item${selected ? " is-active" : ""}`}
21
+ onClick={onClick}
22
+ data-dropdown-item-value={item.value}
23
+ title={item.displayValue}
24
+ >
25
+ {item.displayValue}
26
+ </li>
27
+ );
28
+ };
29
+
30
+ export default DropdownItem;
@@ -0,0 +1,60 @@
1
+ import { MouseEvent } from 'react';
2
+
3
+ import { isNullOrEmpty, isNullOrUndefined } from "@bodynarf/utils/common";
4
+
5
+ import Icon from '@app/components/icon';
6
+
7
+ import { SelectableItem } from "../../types";
8
+
9
+ interface DropdownLabelProps {
10
+ /** Caption when no items selected */
11
+ caption: string;
12
+
13
+ /** Can user deselect */
14
+ deselectable: boolean;
15
+
16
+ /** Selected item */
17
+ selectedItem?: SelectableItem;
18
+
19
+ /** Click handler*/
20
+ onClick: (event: MouseEvent<HTMLLabelElement>) => void;
21
+ }
22
+
23
+ /** Label component */
24
+ const DropdownLabel = ({ caption, selectedItem, onClick, deselectable }: DropdownLabelProps): JSX.Element => {
25
+ const itemSelected = !isNullOrUndefined(selectedItem);
26
+
27
+ const text = itemSelected
28
+ ? selectedItem?.displayValue
29
+ : caption;
30
+
31
+ const deselectVisible = deselectable && itemSelected;
32
+
33
+ const className = [
34
+ "dropdown-trigger",
35
+ "app-dropdown__label",
36
+ itemSelected ? "" : "app-dropdown__label--default",
37
+ "button"
38
+ ].filter(x => !isNullOrEmpty(x))
39
+ .join(" ");
40
+
41
+ return (
42
+ <label
43
+ className={className}
44
+ onClick={onClick}
45
+ >
46
+ {deselectVisible &&
47
+ <Icon className="plus-lg" />
48
+ }
49
+ <span
50
+ className={deselectVisible ? "mx-2" : "mr-2"}
51
+ title={itemSelected ? text : undefined}
52
+ >
53
+ {text}
54
+ </span>
55
+ <Icon className="arrow-up" />
56
+ </label>
57
+ );
58
+ };
59
+
60
+ export default DropdownLabel;
@@ -0,0 +1,89 @@
1
+ .app-dropdown {
2
+ min-width: 7.5rem;
3
+ width: fit-content;
4
+
5
+ &__list {
6
+ position: absolute;
7
+
8
+ opacity: 0;
9
+ visibility: collapse;
10
+
11
+ transition: 0.15s ease-in-out opacity;
12
+ }
13
+
14
+ & .dropdown-menu {
15
+ min-width: 11.75rem;
16
+ }
17
+
18
+ span.dropdown-item {
19
+ color: gray;
20
+ text-align: center;
21
+ }
22
+
23
+ &__label {
24
+ cursor: pointer;
25
+ user-select: none;
26
+ display: flex;
27
+ justify-content: space-between;
28
+
29
+ span {
30
+ white-space: nowrap;
31
+ text-overflow: ellipsis;
32
+ overflow-x: hidden;
33
+ max-width: 15rem;
34
+ }
35
+
36
+ &--default span {
37
+ color: gray;
38
+ font-style: italic;
39
+ }
40
+
41
+ .app-icon {
42
+ &:hover {
43
+ color: #0d6efd;
44
+ }
45
+
46
+ &.bi-arrow-up::before {
47
+ transition: 0.25s ease-in-out transform;
48
+ }
49
+
50
+ &.bi-plus-lg::before {
51
+ transform: rotate(45deg);
52
+ }
53
+ }
54
+ }
55
+
56
+ &-item {
57
+ cursor: pointer;
58
+ user-select: none;
59
+ white-space: normal;
60
+
61
+ transition: 0.25s ease-in-out;
62
+ transition-property: color, background-color;
63
+
64
+ &:hover {
65
+ background-color: rgba(0, 0, 0, 0.05);
66
+ color: #0a0a0a;
67
+ }
68
+ &:active {
69
+ background-color: rgba(0, 0, 0, 0.15);
70
+ color: #0a0a0a;
71
+ }
72
+
73
+ &.is-active {
74
+ background-color: #485fc7;
75
+ color: #fff;
76
+ }
77
+ }
78
+
79
+ &.is-active {
80
+ .app-dropdown__label .app-icon.bi-arrow-up::before {
81
+ transform: rotate(180deg);
82
+ }
83
+
84
+ .app-dropdown__list {
85
+ opacity: 1;
86
+ visibility: visible;
87
+ }
88
+ }
89
+ }