@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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +25 -0
- package/dist/assets/bootstrap-icons.48902042.woff +0 -0
- package/dist/assets/bootstrap-icons.a29357cb.woff2 +0 -0
- package/dist/assets/index.19ed8c21.css +1 -0
- package/dist/assets/index.517219b4.js +2 -0
- package/dist/assets/index.517219b4.js.map +1 -0
- package/dist/assets/vendor.92043335.js +41 -0
- package/dist/assets/vendor.92043335.js.map +1 -0
- package/dist/index.html +16 -0
- package/index.html +13 -0
- package/package.json +44 -0
- package/src/components/anchor/anchor.scss +15 -0
- package/src/components/anchor/components/anchorWithIcon/anchorWithIcon.tsx +45 -0
- package/src/components/anchor/components/simpleAnchor/simpleAnchor.tsx +16 -0
- package/src/components/anchor/index.tsx +64 -0
- package/src/components/anchor/types.ts +26 -0
- package/src/components/button/button.scss +12 -0
- package/src/components/button/components/buttonWithIcon/buttonWithIcon.tsx +47 -0
- package/src/components/button/components/simpleButton/simpleButton.tsx +16 -0
- package/src/components/button/index.tsx +83 -0
- package/src/components/button/types.ts +40 -0
- package/src/components/dropdown/components/dropdownItem/dropdownItem.tsx +30 -0
- package/src/components/dropdown/components/dropdownLabel/dropdownLabel.tsx +60 -0
- package/src/components/dropdown/dropdown.scss +89 -0
- package/src/components/dropdown/index.tsx +141 -0
- package/src/components/dropdown/types.ts +11 -0
- package/src/components/icon/icon.scss +18 -0
- package/src/components/icon/index.tsx +34 -0
- package/src/components/primitives/date/index.tsx +103 -0
- package/src/components/primitives/multiline/components/multilineWithLabel/index.tsx +93 -0
- package/src/components/primitives/multiline/components/multilineWithoutLabel/index.tsx +51 -0
- package/src/components/primitives/multiline/index.tsx +28 -0
- package/src/components/primitives/text/components/textWithLabel/index.tsx +92 -0
- package/src/components/primitives/text/components/textWithoutLabel/index.tsx +49 -0
- package/src/components/primitives/text/index.tsx +21 -0
- package/src/components/primitives/types.ts +65 -0
- package/src/components/search/index.tsx +127 -0
- package/src/components/search/search.scss +24 -0
- package/src/components/types.ts +41 -0
- package/src/hooks/useComponentOutsideClick.ts +48 -0
- package/src/main.tsx +9 -0
- package/tsconfig.json +48 -0
- package/tsconfig.node.json +8 -0
- package/vite.config.ts +14 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { MouseEvent, useCallback, useId, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import './dropdown.scss';
|
|
4
|
+
|
|
5
|
+
import { isNullOrEmpty, isNullOrUndefined } from '@bodynarf/utils/common';
|
|
6
|
+
|
|
7
|
+
import { useComponentOutsideClick } from '@app/hooks/useComponentOutsideClick';
|
|
8
|
+
|
|
9
|
+
import { SelectableItem } from './types';
|
|
10
|
+
import { BaseElementProps } from '../types';
|
|
11
|
+
|
|
12
|
+
import DropdownItem from './components/dropdownItem/dropdownItem';
|
|
13
|
+
import DropdownLabel from './components/dropdownLabel/dropdownLabel';
|
|
14
|
+
|
|
15
|
+
type DropdownProps = BaseElementProps & {
|
|
16
|
+
/** Items which can be selected */
|
|
17
|
+
items: Array<SelectableItem>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Selected value.
|
|
21
|
+
* Must be stored outside
|
|
22
|
+
*/
|
|
23
|
+
value?: SelectableItem;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Action to update selected value,
|
|
27
|
+
* which stored outside
|
|
28
|
+
*/
|
|
29
|
+
onSelect: (item?: SelectableItem) => void;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Caption.
|
|
33
|
+
* Appears only no element selected
|
|
34
|
+
*/
|
|
35
|
+
caption: string;
|
|
36
|
+
|
|
37
|
+
/** Hide dropdown list when its opened and user click outside */
|
|
38
|
+
hideOnOuterClick: boolean;
|
|
39
|
+
|
|
40
|
+
/** Can user deselect */
|
|
41
|
+
deselectable?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Dropdown component */
|
|
45
|
+
const Dropdown = ({ value, items, onSelect, caption, deselectable, className, hideOnOuterClick }: DropdownProps): JSX.Element => {
|
|
46
|
+
const id = useId();
|
|
47
|
+
|
|
48
|
+
const [isListVisible, setListVisible] = useState<boolean>(false);
|
|
49
|
+
|
|
50
|
+
const onItemClick = useCallback(
|
|
51
|
+
(event: React.MouseEvent<HTMLLIElement>) => {
|
|
52
|
+
const target = event.target as HTMLLIElement;
|
|
53
|
+
|
|
54
|
+
if (isNullOrUndefined(target)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const dataValue = target.dataset['dropdownItemValue'];
|
|
59
|
+
|
|
60
|
+
if (isNullOrEmpty(dataValue)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const item = items.find(x => x.value === dataValue);
|
|
65
|
+
|
|
66
|
+
if (isNullOrUndefined(item)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (value === item) {
|
|
71
|
+
setListVisible(false);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
onSelect(item);
|
|
76
|
+
setListVisible(false);
|
|
77
|
+
}, [setListVisible, value, items, onSelect]);
|
|
78
|
+
|
|
79
|
+
const onLabelClick = useCallback(
|
|
80
|
+
(event: MouseEvent<HTMLLabelElement>): void => {
|
|
81
|
+
const target = event.target as HTMLElement;
|
|
82
|
+
|
|
83
|
+
if (isNullOrUndefined(target)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (target.classList.contains("bi-plus-lg")) {
|
|
88
|
+
onSelect(undefined);
|
|
89
|
+
} else {
|
|
90
|
+
setListVisible(state => !state);
|
|
91
|
+
}
|
|
92
|
+
}, [onSelect, setListVisible]);
|
|
93
|
+
|
|
94
|
+
useComponentOutsideClick(
|
|
95
|
+
`[data-dropdown-id="${id}"]`, isListVisible,
|
|
96
|
+
() => setListVisible(false),
|
|
97
|
+
hideOnOuterClick,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const classNames: string = [
|
|
101
|
+
"app-dropdown",
|
|
102
|
+
isListVisible ? "is-active" : "",
|
|
103
|
+
className,
|
|
104
|
+
"dropdown"
|
|
105
|
+
]
|
|
106
|
+
.filter(x => !isNullOrEmpty(x))
|
|
107
|
+
.join(" ");
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div
|
|
111
|
+
key={id}
|
|
112
|
+
className={classNames}
|
|
113
|
+
data-dropdown-id={id}
|
|
114
|
+
>
|
|
115
|
+
<DropdownLabel
|
|
116
|
+
caption={caption}
|
|
117
|
+
deselectable={deselectable === true}
|
|
118
|
+
selectedItem={value}
|
|
119
|
+
onClick={onLabelClick}
|
|
120
|
+
/>
|
|
121
|
+
<div className="dropdown-menu">
|
|
122
|
+
{items.length > 0
|
|
123
|
+
? <ul className="dropdown-content">
|
|
124
|
+
{items.map(item =>
|
|
125
|
+
<DropdownItem
|
|
126
|
+
key={item.id}
|
|
127
|
+
item={item}
|
|
128
|
+
selected={value?.value === item.value}
|
|
129
|
+
onClick={onItemClick}
|
|
130
|
+
/>
|
|
131
|
+
)}
|
|
132
|
+
</ul>
|
|
133
|
+
: <span className="dropdown-content dropdown-item">No items found</span>
|
|
134
|
+
}
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export default Dropdown;
|
|
141
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import './icon.scss';
|
|
2
|
+
|
|
3
|
+
import { IconSize } from '../types';
|
|
4
|
+
|
|
5
|
+
/** Icon component props */
|
|
6
|
+
type IconProps = {
|
|
7
|
+
/**
|
|
8
|
+
* Class name for icon.
|
|
9
|
+
* Used to display icon from bootstrap-icons
|
|
10
|
+
*/
|
|
11
|
+
className: string;
|
|
12
|
+
|
|
13
|
+
/** Icon size */
|
|
14
|
+
size?: IconSize;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const sizeToClassMap: Map<IconSize, string> = new Map([
|
|
18
|
+
['small', ' app-icon--smal'],
|
|
19
|
+
['medium', ''],
|
|
20
|
+
['large', ' app-icon--large']
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Icon component. Based on bootstrap icons
|
|
25
|
+
*/
|
|
26
|
+
export default function Icon(props: IconProps): JSX.Element {
|
|
27
|
+
const size: IconSize = props.size || 'medium';
|
|
28
|
+
|
|
29
|
+
const className = `app-icon bi bi-${props.className}${sizeToClassMap.get(size)}`;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<i className={className}></i>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { ChangeEvent, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { generateGuid } from '@bodynarf/utils/guid';
|
|
4
|
+
import { getClassName } from '@bodynarf/utils/component';
|
|
5
|
+
import { isStringEmpty } from '@bodynarf/utils/common';
|
|
6
|
+
|
|
7
|
+
import { BaseInputElementProps, InputLabel } from '../types';
|
|
8
|
+
|
|
9
|
+
/** Date input conponent props type */
|
|
10
|
+
type DateProps = Omit<BaseInputElementProps<Date | undefined>, 'placeholder'> & {
|
|
11
|
+
/** Label configuration */
|
|
12
|
+
label: InputLabel;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Date input component */
|
|
16
|
+
const DatePicker = (props: DateProps): JSX.Element => {
|
|
17
|
+
const onValueChange = useCallback(
|
|
18
|
+
(event: ChangeEvent<HTMLInputElement>) =>
|
|
19
|
+
props.onValueChange(
|
|
20
|
+
isStringEmpty(event.target.value)
|
|
21
|
+
? undefined
|
|
22
|
+
: new Date(event.target.value)
|
|
23
|
+
),
|
|
24
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
25
|
+
[props.onValueChange]
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const id = props.name || generateGuid();
|
|
29
|
+
|
|
30
|
+
const size = `is-${(props.size || 'normal')}`;
|
|
31
|
+
|
|
32
|
+
const className = getClassName([
|
|
33
|
+
props.className,
|
|
34
|
+
size,
|
|
35
|
+
props.rounded === true ? 'is-rounded' : '',
|
|
36
|
+
"input",
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const inputContainerClassName = getClassName([
|
|
40
|
+
"control",
|
|
41
|
+
props.loading === true ? 'is-loading' : '',
|
|
42
|
+
(props.style || 'default') === 'default' ? '' : `is-${props.style}`
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const label = props.label;
|
|
46
|
+
const defaultValue = props.defaultValue?.toISOString().split("T")[0];
|
|
47
|
+
|
|
48
|
+
if (label.horizontal === true) {
|
|
49
|
+
return (
|
|
50
|
+
<div className="app-input field is-horizontal">
|
|
51
|
+
<div className={`field-label ${size}`}>
|
|
52
|
+
<label
|
|
53
|
+
className="label"
|
|
54
|
+
htmlFor={id}
|
|
55
|
+
>
|
|
56
|
+
{label.caption}
|
|
57
|
+
</label>
|
|
58
|
+
</div>
|
|
59
|
+
<div className="field-body">
|
|
60
|
+
<div className="field">
|
|
61
|
+
<div className={inputContainerClassName}>
|
|
62
|
+
<input
|
|
63
|
+
type="date"
|
|
64
|
+
className={className}
|
|
65
|
+
readOnly={props.readonly}
|
|
66
|
+
disabled={props.disabled}
|
|
67
|
+
defaultValue={defaultValue}
|
|
68
|
+
onChange={onValueChange}
|
|
69
|
+
name={id}
|
|
70
|
+
id={id}
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div className="app-input field">
|
|
81
|
+
<label
|
|
82
|
+
className={`label ${size}`}
|
|
83
|
+
htmlFor={id}
|
|
84
|
+
>
|
|
85
|
+
{label.caption}
|
|
86
|
+
</label>
|
|
87
|
+
<div className={inputContainerClassName}>
|
|
88
|
+
<input
|
|
89
|
+
type="date"
|
|
90
|
+
className={className}
|
|
91
|
+
readOnly={props.readonly}
|
|
92
|
+
disabled={props.disabled}
|
|
93
|
+
defaultValue={props.defaultValue?.toLocaleDateString()}
|
|
94
|
+
onChange={onValueChange}
|
|
95
|
+
name={id}
|
|
96
|
+
id={id}
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export default DatePicker;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ChangeEvent, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { generateGuid } from '@bodynarf/utils/guid';
|
|
4
|
+
import { getClassName } from '@bodynarf/utils/component';
|
|
5
|
+
|
|
6
|
+
import { MultilineProps } from '../..';
|
|
7
|
+
|
|
8
|
+
/** Multiline textual input component with describing label */
|
|
9
|
+
const MultilineWithLabel = (props: MultilineProps): JSX.Element => {
|
|
10
|
+
const onValueChange = useCallback(
|
|
11
|
+
(event: ChangeEvent<HTMLTextAreaElement>) => props.onValueChange(event.target.value),
|
|
12
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
13
|
+
[props.onValueChange]
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const id = props.name || generateGuid();
|
|
17
|
+
|
|
18
|
+
const size = `is-${(props.size || 'normal')}`;
|
|
19
|
+
|
|
20
|
+
const className = getClassName([
|
|
21
|
+
props.className,
|
|
22
|
+
size,
|
|
23
|
+
props.rounded === true ? 'is-rounded' : '',
|
|
24
|
+
"textarea",
|
|
25
|
+
props.fixed === true ? 'has-fixed-size': '',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const inputContainerClassName = getClassName([
|
|
29
|
+
"control",
|
|
30
|
+
props.loading === true ? 'is-loading' : '',
|
|
31
|
+
(props.style || 'default') === 'default' ? '' : `is-${props.style}`
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const label = props.label!;
|
|
35
|
+
|
|
36
|
+
if (label.horizontal === true) {
|
|
37
|
+
return (
|
|
38
|
+
<div className="app-input field is-horizontal">
|
|
39
|
+
<div className={`field-label ${size}`}>
|
|
40
|
+
<label
|
|
41
|
+
className="label"
|
|
42
|
+
htmlFor={id}
|
|
43
|
+
>
|
|
44
|
+
{label.caption}
|
|
45
|
+
</label>
|
|
46
|
+
</div>
|
|
47
|
+
<div className="field-body">
|
|
48
|
+
<div className="field">
|
|
49
|
+
<div className={inputContainerClassName}>
|
|
50
|
+
<textarea
|
|
51
|
+
className={className}
|
|
52
|
+
placeholder={props.placeholder}
|
|
53
|
+
readOnly={props.readonly}
|
|
54
|
+
disabled={props.disabled}
|
|
55
|
+
defaultValue={props.defaultValue}
|
|
56
|
+
onChange={onValueChange}
|
|
57
|
+
name={id}
|
|
58
|
+
id={id}
|
|
59
|
+
rows={props.rows}
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="app-input field">
|
|
70
|
+
<label
|
|
71
|
+
className={`label ${size}`}
|
|
72
|
+
htmlFor={id}
|
|
73
|
+
>
|
|
74
|
+
{label.caption}
|
|
75
|
+
</label>
|
|
76
|
+
<div className={inputContainerClassName}>
|
|
77
|
+
<textarea
|
|
78
|
+
className={className}
|
|
79
|
+
placeholder={props.placeholder}
|
|
80
|
+
readOnly={props.readonly}
|
|
81
|
+
disabled={props.disabled}
|
|
82
|
+
defaultValue={props.defaultValue}
|
|
83
|
+
onChange={onValueChange}
|
|
84
|
+
name={id}
|
|
85
|
+
id={id}
|
|
86
|
+
rows={props.rows}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default MultilineWithLabel;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ChangeEvent, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { generateGuid } from '@bodynarf/utils/guid';
|
|
4
|
+
import { getClassName } from '@bodynarf/utils/component';
|
|
5
|
+
|
|
6
|
+
import { MultilineProps } from '../..';
|
|
7
|
+
|
|
8
|
+
/** Multiline textual input component without describing label*/
|
|
9
|
+
const MultilineWithoutLabel = (props: MultilineProps): JSX.Element => {
|
|
10
|
+
const onValueChange = useCallback(
|
|
11
|
+
(event: ChangeEvent<HTMLTextAreaElement>) => props.onValueChange(event.target.value),
|
|
12
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
13
|
+
[props.onValueChange]
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const id = props.name || generateGuid();
|
|
17
|
+
|
|
18
|
+
const size = `is-${(props.size || 'normal')}`;
|
|
19
|
+
|
|
20
|
+
const className = getClassName([
|
|
21
|
+
props.className,
|
|
22
|
+
size,
|
|
23
|
+
props.rounded === true ? 'is-rounded' : '',
|
|
24
|
+
"textarea",
|
|
25
|
+
props.fixed === true ? 'has-fixed-size': '',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const inputContainerClassName = getClassName([
|
|
29
|
+
"control",
|
|
30
|
+
props.loading === true ? 'is-loading' : '',
|
|
31
|
+
(props.style || 'default') === 'default' ? '' : `is-${props.style}`
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="field">
|
|
36
|
+
<div className={inputContainerClassName}>
|
|
37
|
+
<textarea
|
|
38
|
+
className={className}
|
|
39
|
+
placeholder={props.placeholder}
|
|
40
|
+
defaultValue={props.defaultValue}
|
|
41
|
+
onChange={onValueChange}
|
|
42
|
+
id={id}
|
|
43
|
+
name={id}
|
|
44
|
+
rows={props.rows}
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default MultilineWithoutLabel;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { isNullOrUndefined } from '@bodynarf/utils/common';
|
|
2
|
+
|
|
3
|
+
import { BaseInputElementProps } from '../types';
|
|
4
|
+
|
|
5
|
+
import MultilineWithoutLabel from './components/multilineWithoutLabel';
|
|
6
|
+
import MultilineWithLabel from './components/multilineWithLabel';
|
|
7
|
+
|
|
8
|
+
/** Multiline textual input conponent props type */
|
|
9
|
+
export type MultilineProps = BaseInputElementProps<string> & {
|
|
10
|
+
/** Is input should be resizable */
|
|
11
|
+
fixed?: boolean;
|
|
12
|
+
|
|
13
|
+
/** Number of initial rows count */
|
|
14
|
+
rows?: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Multiline textual input component */
|
|
18
|
+
const Multiline = (props: MultilineProps): JSX.Element => {
|
|
19
|
+
if (isNullOrUndefined(props.label)) {
|
|
20
|
+
return (<MultilineWithoutLabel {...props} />);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return (<MultilineWithLabel {...props} />);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default Multiline;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ChangeEvent, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { generateGuid } from '@bodynarf/utils/guid';
|
|
4
|
+
import { getClassName } from '@bodynarf/utils/component';
|
|
5
|
+
|
|
6
|
+
import { TextProps } from '../..';
|
|
7
|
+
|
|
8
|
+
/** Textual input with describing label */
|
|
9
|
+
const TextWithLabel = (props: TextProps): JSX.Element => {
|
|
10
|
+
const onValueChange = useCallback(
|
|
11
|
+
(event: ChangeEvent<HTMLInputElement>) => props.onValueChange(event.target.value),
|
|
12
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
13
|
+
[props.onValueChange]
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const id = props.name || generateGuid();
|
|
17
|
+
|
|
18
|
+
const size = `is-${(props.size || 'normal')}`;
|
|
19
|
+
|
|
20
|
+
const className = getClassName([
|
|
21
|
+
props.className,
|
|
22
|
+
size,
|
|
23
|
+
props.rounded === true ? 'is-rounded' : '',
|
|
24
|
+
"input",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
const inputContainerClassName = getClassName([
|
|
28
|
+
"control",
|
|
29
|
+
props.loading === true ? 'is-loading' : '',
|
|
30
|
+
(props.style || 'default') === 'default' ? '' : `is-${props.style}`
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const label = props.label!;
|
|
34
|
+
|
|
35
|
+
if (label.horizontal === true) {
|
|
36
|
+
return (
|
|
37
|
+
<div className="app-input field is-horizontal">
|
|
38
|
+
<div className={`field-label ${size}`}>
|
|
39
|
+
<label
|
|
40
|
+
className="label"
|
|
41
|
+
htmlFor={id}
|
|
42
|
+
>
|
|
43
|
+
{label.caption}
|
|
44
|
+
</label>
|
|
45
|
+
</div>
|
|
46
|
+
<div className="field-body">
|
|
47
|
+
<div className="field">
|
|
48
|
+
<div className={inputContainerClassName}>
|
|
49
|
+
<input
|
|
50
|
+
className={className}
|
|
51
|
+
type="text"
|
|
52
|
+
placeholder={props.placeholder}
|
|
53
|
+
readOnly={props.readonly}
|
|
54
|
+
disabled={props.disabled}
|
|
55
|
+
defaultValue={props.defaultValue}
|
|
56
|
+
onChange={onValueChange}
|
|
57
|
+
name={id}
|
|
58
|
+
id={id}
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className="app-input field">
|
|
69
|
+
<label
|
|
70
|
+
className={`label ${size}`}
|
|
71
|
+
htmlFor={id}
|
|
72
|
+
>
|
|
73
|
+
{label.caption}
|
|
74
|
+
</label>
|
|
75
|
+
<div className={inputContainerClassName}>
|
|
76
|
+
<input
|
|
77
|
+
className={className}
|
|
78
|
+
type="text"
|
|
79
|
+
placeholder={props.placeholder}
|
|
80
|
+
readOnly={props.readonly}
|
|
81
|
+
disabled={props.disabled}
|
|
82
|
+
defaultValue={props.defaultValue}
|
|
83
|
+
onChange={onValueChange}
|
|
84
|
+
name={id}
|
|
85
|
+
id={id}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default TextWithLabel;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ChangeEvent, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { generateGuid } from '@bodynarf/utils/guid';
|
|
4
|
+
import { getClassName } from '@bodynarf/utils/component';
|
|
5
|
+
|
|
6
|
+
import { TextProps } from '../..';
|
|
7
|
+
|
|
8
|
+
/** Textual input without describing label */
|
|
9
|
+
const TextWithoutLabel = (props: TextProps): JSX.Element => {
|
|
10
|
+
const onValueChange = useCallback(
|
|
11
|
+
(event: ChangeEvent<HTMLInputElement>) => props.onValueChange(event.target.value),
|
|
12
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
13
|
+
[props.onValueChange]
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const className = getClassName([
|
|
17
|
+
props.className,
|
|
18
|
+
`is-${(props.size || 'normal')}`,
|
|
19
|
+
props.rounded === true ? 'is-rounded' : '',
|
|
20
|
+
"input",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const containerClassName = getClassName([
|
|
24
|
+
"app-input",
|
|
25
|
+
"control",
|
|
26
|
+
props.loading === true ? 'is-loading' : '',
|
|
27
|
+
(props.style || 'default') === 'default' ? '' : `is-${props.style}`
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const id = props.name || generateGuid();
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className={containerClassName}>
|
|
34
|
+
<input
|
|
35
|
+
className={className}
|
|
36
|
+
type="text"
|
|
37
|
+
placeholder={props.placeholder}
|
|
38
|
+
readOnly={props.readonly}
|
|
39
|
+
disabled={props.disabled}
|
|
40
|
+
defaultValue={props.defaultValue}
|
|
41
|
+
onChange={onValueChange}
|
|
42
|
+
name={id}
|
|
43
|
+
id={id}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default TextWithoutLabel;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isNullOrUndefined } from '@bodynarf/utils/common';
|
|
2
|
+
|
|
3
|
+
import { BaseInputElementProps } from '../types';
|
|
4
|
+
|
|
5
|
+
import TextWithLabel from './components/textWithLabel';
|
|
6
|
+
import TextWithoutLabel from './components/textWithoutLabel';
|
|
7
|
+
|
|
8
|
+
/** Text input conponent props type */
|
|
9
|
+
export type TextProps = BaseInputElementProps<string>;
|
|
10
|
+
|
|
11
|
+
/** Textual input component */
|
|
12
|
+
const Text = (props: TextProps): JSX.Element => {
|
|
13
|
+
if (isNullOrUndefined(props.label)) {
|
|
14
|
+
return (<TextWithoutLabel {...props}/>);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return (<TextWithLabel {...props}/>);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default Text;
|