pixel-react 1.1.9 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/components/AppHeader/types.d.ts +7 -7
- package/lib/components/Drawer/Drawer.stories.d.ts +2 -0
- package/lib/components/Drawer/Types.d.ts +11 -0
- package/lib/components/Icon/Icon.stories.d.ts +1 -0
- package/lib/components/Icon/types.d.ts +1 -0
- package/lib/components/InputWithDropdown/types.d.ts +1 -1
- package/lib/components/LabelEditTextField/LabelEditTextField.d.ts +5 -0
- package/lib/components/LabelEditTextField/LabelEditTextField.stories.d.ts +11 -0
- package/lib/components/LabelEditTextField/index.d.ts +1 -0
- package/lib/components/LabelEditTextField/types.d.ts +38 -0
- package/lib/components/Select/Select.d.ts +1 -1
- package/lib/components/Select/components/Dropdown/Dropdown.d.ts +1 -1
- package/lib/components/Select/components/Dropdown/dropdownTypes.d.ts +2 -0
- package/lib/components/Select/types.d.ts +11 -4
- package/lib/components/Table/Table.d.ts +1 -1
- package/lib/components/Table/Table.stories.d.ts +2 -0
- package/lib/components/Table/Types.d.ts +16 -0
- package/lib/index.d.ts +92 -16
- package/lib/index.esm.js +399 -152
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +399 -151
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/getSelectOptionValue/getSelectOptionValue.d.ts +8 -0
- package/package.json +1 -1
- package/src/assets/Themes/BaseTheme.scss +10 -0
- package/src/assets/Themes/DarkTheme.scss +19 -7
- package/src/assets/icons/eye_closed.svg +3 -0
- package/src/components/AppHeader/AppHeader.scss +14 -3
- package/src/components/AppHeader/AppHeader.stories.tsx +28 -28
- package/src/components/AppHeader/AppHeader.tsx +11 -11
- package/src/components/AppHeader/types.ts +7 -7
- package/src/components/Button/Button.scss +1 -0
- package/src/components/Checkbox/Checkbox.tsx +1 -1
- package/src/components/Drawer/Drawer.scss +13 -10
- package/src/components/Drawer/Drawer.stories.tsx +28 -0
- package/src/components/Drawer/Drawer.tsx +30 -7
- package/src/components/Drawer/Types.ts +11 -0
- package/src/components/Icon/Icon.stories.tsx +27 -0
- package/src/components/Icon/Icon.tsx +5 -1
- package/src/components/Icon/Icons.scss +14 -2
- package/src/components/Icon/iconList.ts +2 -0
- package/src/components/Icon/types.ts +1 -0
- package/src/components/InputWithDropdown/types.ts +1 -1
- package/src/components/LabelEditTextField/LabelEditTextField.scss +85 -0
- package/src/components/LabelEditTextField/LabelEditTextField.stories.tsx +136 -0
- package/src/components/LabelEditTextField/LabelEditTextField.tsx +207 -0
- package/src/components/LabelEditTextField/index.ts +1 -0
- package/src/components/LabelEditTextField/types.ts +38 -0
- package/src/components/Modal/Modal.tsx +8 -1
- package/src/components/Modal/modal.scss +10 -2
- package/src/components/Select/Select.stories.tsx +5 -3
- package/src/components/Select/Select.tsx +13 -5
- package/src/components/Select/components/Dropdown/Dropdown.tsx +3 -1
- package/src/components/Select/components/Dropdown/dropdownTypes.ts +3 -0
- package/src/components/Select/types.ts +12 -5
- package/src/components/Table/Table.scss +16 -4
- package/src/components/Table/Table.stories.tsx +36 -12
- package/src/components/Table/Table.tsx +33 -16
- package/src/components/Table/Types.ts +121 -105
- package/src/index.ts +2 -0
- package/src/utils/getSelectOptionValue/getSelectOptionValue.ts +31 -0
@@ -30,6 +30,8 @@ const Drawer: FC<DrawerProps> = ({
|
|
30
30
|
onCloseIconClick,
|
31
31
|
customHeader,
|
32
32
|
customFooter,
|
33
|
+
tertiaryButtonProps = { left: {}, right: {} },
|
34
|
+
zIndex = 999,
|
33
35
|
}: DrawerProps) => {
|
34
36
|
const [isExpanded, setIsExpanded] = useState(_isExpanded);
|
35
37
|
|
@@ -72,11 +74,16 @@ const Drawer: FC<DrawerProps> = ({
|
|
72
74
|
className={classNames('ff-drawer', `ff-drawer--${drawerSize}`, {
|
73
75
|
'ff-drawer--open': isOpen,
|
74
76
|
})}
|
77
|
+
style={{ zIndex }}
|
75
78
|
>
|
76
79
|
{showHeader && (
|
77
|
-
<div
|
80
|
+
<div
|
81
|
+
className={classNames('ff-drawer-header', {
|
82
|
+
'ff-custom-header': customHeader,
|
83
|
+
})}
|
84
|
+
>
|
78
85
|
{customHeader ? (
|
79
|
-
customHeader
|
86
|
+
<div className="ff-custom-header">{customHeader}</div>
|
80
87
|
) : (
|
81
88
|
<div className="ff-drawer-action-section">
|
82
89
|
<div className="ff-action-button">
|
@@ -106,9 +113,7 @@ const Drawer: FC<DrawerProps> = ({
|
|
106
113
|
className="ff-expand-collapse-button"
|
107
114
|
onClick={onClose}
|
108
115
|
>
|
109
|
-
{backButtonIcon
|
110
|
-
backButtonIcon
|
111
|
-
) : (
|
116
|
+
{backButtonIcon || (
|
112
117
|
<Icon
|
113
118
|
name="error"
|
114
119
|
height={16}
|
@@ -124,7 +129,7 @@ const Drawer: FC<DrawerProps> = ({
|
|
124
129
|
<div className="ff-close-icon">
|
125
130
|
<Icon
|
126
131
|
name="close"
|
127
|
-
hoverEffect={
|
132
|
+
hoverEffect={true}
|
128
133
|
onClick={onCloseIconClick || onClose}
|
129
134
|
height={6}
|
130
135
|
width={6}
|
@@ -146,7 +151,11 @@ const Drawer: FC<DrawerProps> = ({
|
|
146
151
|
</div>
|
147
152
|
|
148
153
|
{isFooterRequired && (
|
149
|
-
<div
|
154
|
+
<div
|
155
|
+
className={classNames('ff-drawer-footer', {
|
156
|
+
'ff-custom-footer': customFooter,
|
157
|
+
})}
|
158
|
+
>
|
150
159
|
{customFooter || footerContent || (
|
151
160
|
<>
|
152
161
|
<div className="button-container">
|
@@ -166,8 +175,22 @@ const Drawer: FC<DrawerProps> = ({
|
|
166
175
|
transparentBackground={true}
|
167
176
|
/>
|
168
177
|
)}
|
178
|
+
{tertiaryButtonProps.left?.label && (
|
179
|
+
<Button
|
180
|
+
{...tertiaryButtonProps.left}
|
181
|
+
variant="tertiary"
|
182
|
+
transparentBackground={true}
|
183
|
+
/>
|
184
|
+
)}
|
169
185
|
</div>
|
170
186
|
<div className="button-container">
|
187
|
+
{tertiaryButtonProps.right?.label && (
|
188
|
+
<Button
|
189
|
+
{...tertiaryButtonProps.right}
|
190
|
+
variant="tertiary"
|
191
|
+
transparentBackground={true}
|
192
|
+
/>
|
193
|
+
)}
|
171
194
|
{secondaryButtonProps.label && (
|
172
195
|
<Button
|
173
196
|
{...secondaryButtonProps}
|
@@ -109,4 +109,15 @@ export interface DrawerProps {
|
|
109
109
|
* If provided, this will render in place of the default footer.
|
110
110
|
*/
|
111
111
|
customFooter?: ReactNode;
|
112
|
+
/**
|
113
|
+
* Tertiary button properties (optional)
|
114
|
+
*/
|
115
|
+
tertiaryButtonProps?: {
|
116
|
+
left?: BtnPropsCommon;
|
117
|
+
right?: BtnPropsCommon;
|
118
|
+
};
|
119
|
+
/**
|
120
|
+
* Custom z-index for the drawer
|
121
|
+
*/
|
122
|
+
zIndex?: number;
|
112
123
|
}
|
@@ -6,6 +6,12 @@ const meta: Meta<typeof Icon> = {
|
|
6
6
|
title: 'Components/Icon',
|
7
7
|
component: Icon,
|
8
8
|
tags: ['autodocs'],
|
9
|
+
argTypes: {
|
10
|
+
variant: {
|
11
|
+
control: { type: 'select' },
|
12
|
+
options: ['light', 'dark'],
|
13
|
+
},
|
14
|
+
},
|
9
15
|
};
|
10
16
|
|
11
17
|
type Story = StoryObj<typeof Icon>;
|
@@ -34,4 +40,25 @@ export const AllIcons: Story = {
|
|
34
40
|
},
|
35
41
|
};
|
36
42
|
|
43
|
+
export const DarkVariantIcons: Story = {
|
44
|
+
args: {
|
45
|
+
name: 'hamburger_menu',
|
46
|
+
color: 'var(--ff-icon-color-dark-variant)',
|
47
|
+
variant: 'dark',
|
48
|
+
hoverEffect: true,
|
49
|
+
},
|
50
|
+
render: (args) => {
|
51
|
+
const backgroundColor = args.variant === 'dark' ? '#000' : '#fff';
|
52
|
+
const iconColor = args.variant === 'dark' ? 'var(--ff-icon-color-dark-variant)' : 'var(--brand-color)';
|
53
|
+
|
54
|
+
return (
|
55
|
+
<div style={{ backgroundColor, padding: '20px' }}>
|
56
|
+
<Icon {...args} color={iconColor} height={16} width={16} />
|
57
|
+
</div>
|
58
|
+
);
|
59
|
+
},
|
60
|
+
};
|
61
|
+
|
62
|
+
|
63
|
+
|
37
64
|
export default meta;
|
@@ -15,6 +15,7 @@ const Icon = forwardRef<HTMLSpanElement, IconProps>(
|
|
15
15
|
hoverEffect = false,
|
16
16
|
className = '',
|
17
17
|
disabled = false,
|
18
|
+
variant = "light",
|
18
19
|
...props
|
19
20
|
},
|
20
21
|
ref
|
@@ -28,6 +29,8 @@ const Icon = forwardRef<HTMLSpanElement, IconProps>(
|
|
28
29
|
return null;
|
29
30
|
}
|
30
31
|
|
32
|
+
const iconColor = variant === "dark" ? 'var(--ff-icon-color-dark-variant)' : color;
|
33
|
+
|
31
34
|
return (
|
32
35
|
<span
|
33
36
|
ref={ref}
|
@@ -36,11 +39,12 @@ const Icon = forwardRef<HTMLSpanElement, IconProps>(
|
|
36
39
|
className={classNames('ff-icon-container', {
|
37
40
|
'ff-icon-click': !!hoverEffect,
|
38
41
|
'ff-icon-disabled': disabled,
|
42
|
+
'ff-icon-dark': variant === "dark",
|
39
43
|
[className]: !!className,
|
40
44
|
})}
|
41
45
|
{...props}
|
42
46
|
>
|
43
|
-
<IconComponent height="100%" width="100%" style={{ color:
|
47
|
+
<IconComponent height="100%" width="100%" style={{ color: iconColor }} />
|
44
48
|
</span>
|
45
49
|
);
|
46
50
|
}
|
@@ -1,13 +1,25 @@
|
|
1
|
-
|
2
1
|
.ff-icon-container {
|
3
2
|
display: flex;
|
4
3
|
justify-content: center;
|
5
4
|
align-items: center;
|
5
|
+
padding: 4px;
|
6
|
+
box-sizing: content-box;
|
7
|
+
&.ff-icon-dark {
|
8
|
+
background-color: var(--brand-color);
|
9
|
+
&.ff-icon-click {
|
10
|
+
&:hover {
|
11
|
+
border-radius: 4px;
|
12
|
+
background-color: white;
|
13
|
+
svg path {
|
14
|
+
fill: var(--brand-color);
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
6
19
|
}
|
7
20
|
|
8
21
|
.ff-icon-click {
|
9
22
|
cursor: pointer;
|
10
|
-
padding: 4px;
|
11
23
|
box-sizing: content-box;
|
12
24
|
&:hover {
|
13
25
|
border-radius: 4px;
|
@@ -113,6 +113,7 @@ import NotificationIcon from '../../assets/icons/notification_icon.svg?react';
|
|
113
113
|
import NLPHelpIcon from '../../assets/icons/nlp_help_icon.svg?react';
|
114
114
|
import UpdateIcon from '../../assets/icons/update_icon.svg?react';
|
115
115
|
import AddFile from '../../assets/icons/add_file.svg?react';
|
116
|
+
import EyeClosed from '../../assets/icons/eye_closed.svg?react';
|
116
117
|
|
117
118
|
import CloneIcon from '../../assets/icons/clone_icon.svg?react';
|
118
119
|
import MoveIcon from '../../assets/icons/move_icon.svg?react';
|
@@ -236,5 +237,6 @@ Components['linked_defects'] = LinkedDefects;
|
|
236
237
|
Components['no_access_icon'] = NoAccessIcon;
|
237
238
|
Components['full_access_icon'] = FullAccessIcon;
|
238
239
|
Components['view_access_icon'] = ViewAccessIcon;
|
240
|
+
Components['eye_closed'] = EyeClosed;
|
239
241
|
|
240
242
|
export default Components;
|
@@ -74,7 +74,7 @@ export interface InputWithDropdownProps {
|
|
74
74
|
/**
|
75
75
|
* onChange handler for dropdown changes
|
76
76
|
*/
|
77
|
-
onDropdownChangeHandler?: (option:
|
77
|
+
onDropdownChangeHandler?: (option: any) => void;
|
78
78
|
|
79
79
|
/**
|
80
80
|
* onInputBlurHandler action for input field
|
@@ -0,0 +1,85 @@
|
|
1
|
+
@use '../../assets/styles/fonts' as *;
|
2
|
+
|
3
|
+
.ff-label-edit-text-field {
|
4
|
+
display: flex;
|
5
|
+
flex-direction: column;
|
6
|
+
position: relative;
|
7
|
+
width: 100%;
|
8
|
+
.ff-label-text-field {
|
9
|
+
display: flex;
|
10
|
+
gap: 10px;
|
11
|
+
}
|
12
|
+
.ff-label-text-field-with-dropdown,
|
13
|
+
.ff-label-text-field-without-dropdown {
|
14
|
+
position: relative;
|
15
|
+
display: flex;
|
16
|
+
align-items: center;
|
17
|
+
}
|
18
|
+
.ff-label {
|
19
|
+
position: absolute;
|
20
|
+
top: 0;
|
21
|
+
left: 10px;
|
22
|
+
transform: translateY(-50%);
|
23
|
+
transition: all 0.2s ease;
|
24
|
+
color: var(--label-edit-text-label-color);
|
25
|
+
background: var(--label-edit-text-background-color);
|
26
|
+
padding: 0 4px;
|
27
|
+
@extend .font-size-8;
|
28
|
+
}
|
29
|
+
.ff-textfield-label {
|
30
|
+
@extend .ff-label;
|
31
|
+
top: 5px;
|
32
|
+
}
|
33
|
+
|
34
|
+
.display-text {
|
35
|
+
padding: 4px;
|
36
|
+
border-radius: 4px;
|
37
|
+
cursor: pointer;
|
38
|
+
display: flex;
|
39
|
+
align-items: center;
|
40
|
+
}
|
41
|
+
|
42
|
+
.ff-text-field,
|
43
|
+
.dropdown {
|
44
|
+
border: 1px solid var(--label-edit-text-label-color);
|
45
|
+
outline: none;
|
46
|
+
width: fit-content;
|
47
|
+
@extend .fontSm;
|
48
|
+
}
|
49
|
+
.ff-text-field {
|
50
|
+
padding: 0 4px;
|
51
|
+
border-radius: 4px;
|
52
|
+
}
|
53
|
+
.ff-text-dropdown-field {
|
54
|
+
flex: 2;
|
55
|
+
border: 1px solid var(--label-edit-text-label-color);
|
56
|
+
border-radius: 4px 0 0 4px;
|
57
|
+
border-right: none;
|
58
|
+
border-width: thin;
|
59
|
+
padding-left: 5px;
|
60
|
+
@extend .fontSm;
|
61
|
+
&:focus-visible {
|
62
|
+
outline: none;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
.dropdown {
|
66
|
+
border-radius: 0 4px 4px 0;
|
67
|
+
flex: 1;
|
68
|
+
height: inherit;
|
69
|
+
}
|
70
|
+
|
71
|
+
.ff-icon-container {
|
72
|
+
display: flex;
|
73
|
+
gap: 8px;
|
74
|
+
align-items: center;
|
75
|
+
.confirm-icon,
|
76
|
+
.cancel-icon {
|
77
|
+
cursor: pointer;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
.error-text {
|
82
|
+
margin: 0;
|
83
|
+
color: var(--label-edit-error-text);
|
84
|
+
}
|
85
|
+
}
|
@@ -0,0 +1,136 @@
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
2
|
+
import LabelEditTextField from './LabelEditTextField';
|
3
|
+
import '../../assets/styles/_colors.scss';
|
4
|
+
import './LabelEditTextField.scss';
|
5
|
+
|
6
|
+
const meta: Meta<typeof LabelEditTextField> = {
|
7
|
+
title: 'Components/LabelEditTextField',
|
8
|
+
component: LabelEditTextField,
|
9
|
+
parameters: {
|
10
|
+
layout: 'centered',
|
11
|
+
},
|
12
|
+
tags: ['autodocs'],
|
13
|
+
};
|
14
|
+
|
15
|
+
type Story = StoryObj<typeof LabelEditTextField>;
|
16
|
+
export const textField: Story = {
|
17
|
+
render: () => {
|
18
|
+
const handleConfirmAction = (inputValue: string) => {
|
19
|
+
//DEMO: we are getting that value from LabelEditTextField
|
20
|
+
console.log('Confirmed input value:', inputValue);
|
21
|
+
};
|
22
|
+
return (
|
23
|
+
<LabelEditTextField
|
24
|
+
label="Add Module"
|
25
|
+
text="Verify The Function Of Categories For" //it might be state
|
26
|
+
confirmIcon={{
|
27
|
+
name: 'update_icon',
|
28
|
+
onClick: () => {},
|
29
|
+
}}
|
30
|
+
cancelIcon={{
|
31
|
+
name: 'close',
|
32
|
+
onClick: () => {},
|
33
|
+
}}
|
34
|
+
width="300px"
|
35
|
+
height="22px"
|
36
|
+
confirmAction={handleConfirmAction}
|
37
|
+
/>
|
38
|
+
);
|
39
|
+
},
|
40
|
+
};
|
41
|
+
export const textFieldWithOutLabel: Story = {
|
42
|
+
render: () => {
|
43
|
+
const handleConfirmAction = (inputValue: string) => {
|
44
|
+
//DEMO: we are getting that value from LabelEditTextField
|
45
|
+
console.log('Confirmed input value:', inputValue);
|
46
|
+
};
|
47
|
+
return (
|
48
|
+
<LabelEditTextField
|
49
|
+
text="Verify The Function Of Categories For" //it might be state
|
50
|
+
confirmIcon={{
|
51
|
+
name: 'update_icon',
|
52
|
+
onClick: () => {},
|
53
|
+
}}
|
54
|
+
cancelIcon={{
|
55
|
+
name: 'close',
|
56
|
+
onClick: () => {},
|
57
|
+
}}
|
58
|
+
width="300px"
|
59
|
+
height="22px"
|
60
|
+
confirmAction={handleConfirmAction}
|
61
|
+
/>
|
62
|
+
);
|
63
|
+
},
|
64
|
+
};
|
65
|
+
export const textFieldWithDropdown: Story = {
|
66
|
+
render: () => {
|
67
|
+
const handleConfirmAction = (inputValue: string, dropdownValue: string) => {
|
68
|
+
//DEMO: we are getting that value from LabelEditTextField
|
69
|
+
console.log(
|
70
|
+
'Confirmed input value and dropdown value:',
|
71
|
+
inputValue,
|
72
|
+
dropdownValue
|
73
|
+
);
|
74
|
+
};
|
75
|
+
return (
|
76
|
+
<LabelEditTextField
|
77
|
+
label="Add Module"
|
78
|
+
text="Verify The Function Of Categories For"
|
79
|
+
confirmIcon={{
|
80
|
+
name: 'update_icon',
|
81
|
+
onClick: () => {},
|
82
|
+
}}
|
83
|
+
cancelIcon={{
|
84
|
+
name: 'close',
|
85
|
+
onClick: () => {},
|
86
|
+
}}
|
87
|
+
variant="textFieldWithDropdown"
|
88
|
+
dropdownData={[
|
89
|
+
{ id: 1, value: 'web', label: 'Web & Mobile' },
|
90
|
+
{ id: 2, value: 'desktop', label: 'Desktop' },
|
91
|
+
]}
|
92
|
+
width="300px"
|
93
|
+
height="22px"
|
94
|
+
confirmAction={handleConfirmAction}
|
95
|
+
/>
|
96
|
+
);
|
97
|
+
},
|
98
|
+
};
|
99
|
+
export const textFieldWithHighlight: Story = {
|
100
|
+
render: () => {
|
101
|
+
const handleConfirmAction = (inputValue: string, dropdownValue: string) => {
|
102
|
+
//DEMO: we are getting that value from LabelEditTextField
|
103
|
+
|
104
|
+
console.log(
|
105
|
+
'Confirmed input value and dropdown value:',
|
106
|
+
inputValue,
|
107
|
+
dropdownValue
|
108
|
+
);
|
109
|
+
};
|
110
|
+
return (
|
111
|
+
<LabelEditTextField
|
112
|
+
label="Add Module"
|
113
|
+
text="Verify The Function Of Categories For"
|
114
|
+
highlightText="The Function"
|
115
|
+
confirmIcon={{
|
116
|
+
name: 'update_icon',
|
117
|
+
onClick: () => {},
|
118
|
+
}}
|
119
|
+
cancelIcon={{
|
120
|
+
name: 'close',
|
121
|
+
onClick: () => {},
|
122
|
+
}}
|
123
|
+
variant="textFieldWithDropdown"
|
124
|
+
dropdownData={[
|
125
|
+
{ id: 1, value: 'web', label: 'Web & Mobile' },
|
126
|
+
{ id: 2, value: 'desktop', label: 'Desktop' },
|
127
|
+
]}
|
128
|
+
width="400px"
|
129
|
+
height="22px"
|
130
|
+
confirmAction={handleConfirmAction}
|
131
|
+
/>
|
132
|
+
);
|
133
|
+
},
|
134
|
+
};
|
135
|
+
|
136
|
+
export default meta;
|
@@ -0,0 +1,207 @@
|
|
1
|
+
import React, { useState, useRef, useEffect, FC } from 'react';
|
2
|
+
import './LabelEditTextField.scss';
|
3
|
+
import { LabelEditTextFieldTypes } from './types';
|
4
|
+
import Typography from '../Typography';
|
5
|
+
import HighlightText from '../HighlightText';
|
6
|
+
import Icon from '../Icon';
|
7
|
+
|
8
|
+
const getErrorMessage = (
|
9
|
+
inputValue: string,
|
10
|
+
text: string,
|
11
|
+
customError?: string
|
12
|
+
): string => {
|
13
|
+
if (inputValue === text) {
|
14
|
+
return 'No changes were made.';
|
15
|
+
} else if (!inputValue) {
|
16
|
+
return 'Text is required';
|
17
|
+
} else if (inputValue.length < 3) {
|
18
|
+
return 'Please enter at least 3 characters.';
|
19
|
+
} else if (customError) {
|
20
|
+
return customError;
|
21
|
+
}
|
22
|
+
return '';
|
23
|
+
};
|
24
|
+
const LabelEditTextField: FC<LabelEditTextFieldTypes> = ({
|
25
|
+
label,
|
26
|
+
text,
|
27
|
+
highlightText,
|
28
|
+
customError,
|
29
|
+
confirmIcon,
|
30
|
+
cancelIcon,
|
31
|
+
variant = 'textField',
|
32
|
+
dropdownData = [],
|
33
|
+
width = '300px',
|
34
|
+
height = '22px',
|
35
|
+
confirmAction,
|
36
|
+
}) => {
|
37
|
+
const [isEditing, setIsEditing] = useState(false);
|
38
|
+
const [inputValue, setInputValue] = useState(text);
|
39
|
+
const [dropdownValue, setDropdownValue] = useState(
|
40
|
+
dropdownData[0]?.value ?? ''
|
41
|
+
);
|
42
|
+
const [showError, setShowError] = useState('');
|
43
|
+
const [isTextFieldModified, setIsTextFieldModified] = useState(false);
|
44
|
+
const [isDropdownModified, setIsDropdownModified] = useState(false);
|
45
|
+
const containerRef = useRef<HTMLDivElement | null>(null);
|
46
|
+
const cancelRef = useRef<HTMLDivElement | null>(null); // New ref for cancel icon
|
47
|
+
|
48
|
+
const handleDoubleClick = () => {
|
49
|
+
setIsEditing(true);
|
50
|
+
setShowError('');
|
51
|
+
};
|
52
|
+
|
53
|
+
const handleConfirm = () => {
|
54
|
+
const errorMessage = getErrorMessage(inputValue, text, customError);
|
55
|
+
|
56
|
+
if (errorMessage && isEditing) {
|
57
|
+
setShowError(errorMessage);
|
58
|
+
} else {
|
59
|
+
setIsEditing(false);
|
60
|
+
setShowError('');
|
61
|
+
if (confirmAction) confirmAction(inputValue, dropdownValue);
|
62
|
+
}
|
63
|
+
};
|
64
|
+
|
65
|
+
const handleCancel = () => {
|
66
|
+
setInputValue(text);
|
67
|
+
setDropdownValue(dropdownData[0]?.value ?? '');
|
68
|
+
setIsEditing(false);
|
69
|
+
setShowError('');
|
70
|
+
setIsTextFieldModified(false);
|
71
|
+
setIsDropdownModified(false);
|
72
|
+
};
|
73
|
+
|
74
|
+
const handleTextFieldChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
75
|
+
setInputValue(e.target.value);
|
76
|
+
setIsTextFieldModified(true);
|
77
|
+
setShowError('');
|
78
|
+
};
|
79
|
+
|
80
|
+
const handleDropdownChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
81
|
+
setDropdownValue(e.target.value);
|
82
|
+
setIsDropdownModified(true);
|
83
|
+
setShowError('');
|
84
|
+
};
|
85
|
+
|
86
|
+
const handleBlur = (e: MouseEvent) => {
|
87
|
+
if (
|
88
|
+
containerRef.current &&
|
89
|
+
!containerRef.current.contains(e.target as Node) &&
|
90
|
+
cancelRef.current !== e.target // Exclude clicks on cancel icon
|
91
|
+
) {
|
92
|
+
const errorMessage = getErrorMessage(inputValue, text, customError);
|
93
|
+
|
94
|
+
if (errorMessage && isEditing) {
|
95
|
+
setShowError(errorMessage);
|
96
|
+
} else {
|
97
|
+
setIsEditing(false);
|
98
|
+
setShowError('');
|
99
|
+
}
|
100
|
+
}
|
101
|
+
};
|
102
|
+
|
103
|
+
useEffect(() => {
|
104
|
+
document.addEventListener('click', handleBlur);
|
105
|
+
return () => {
|
106
|
+
document.removeEventListener('click', handleBlur);
|
107
|
+
};
|
108
|
+
}, [inputValue]);
|
109
|
+
|
110
|
+
return (
|
111
|
+
<div
|
112
|
+
className="ff-label-edit-text-field"
|
113
|
+
ref={containerRef}
|
114
|
+
style={{ width }}
|
115
|
+
>
|
116
|
+
{isEditing ? (
|
117
|
+
<div className="ff-label-text-field">
|
118
|
+
{variant === 'textFieldWithDropdown' ? (
|
119
|
+
<div
|
120
|
+
className={`ff-label-text-field-with-dropdown ${
|
121
|
+
isEditing ? 'open' : ''
|
122
|
+
}`}
|
123
|
+
style={{ height }}
|
124
|
+
>
|
125
|
+
<input
|
126
|
+
type="text"
|
127
|
+
value={inputValue}
|
128
|
+
onChange={handleTextFieldChange}
|
129
|
+
className={`ff-text-dropdown-field ${
|
130
|
+
isTextFieldModified ? 'modified' : ''
|
131
|
+
}`}
|
132
|
+
placeholder=" "
|
133
|
+
style={{
|
134
|
+
width,
|
135
|
+
}}
|
136
|
+
/>
|
137
|
+
{label && <label className="ff-label">{label}</label>}
|
138
|
+
<select
|
139
|
+
value={dropdownValue}
|
140
|
+
onChange={handleDropdownChange}
|
141
|
+
className={`dropdown ${isDropdownModified ? 'modified' : ''}`}
|
142
|
+
>
|
143
|
+
{dropdownData.map((item) => (
|
144
|
+
<option key={item.id} value={item.value}>
|
145
|
+
{item.label}
|
146
|
+
</option>
|
147
|
+
))}
|
148
|
+
</select>
|
149
|
+
</div>
|
150
|
+
) : (
|
151
|
+
<div className="ff-label-text-field-without-dropdown">
|
152
|
+
<input
|
153
|
+
type="text"
|
154
|
+
value={inputValue}
|
155
|
+
onChange={handleTextFieldChange}
|
156
|
+
className={`ff-text-field ${
|
157
|
+
isTextFieldModified ? 'modified' : ''
|
158
|
+
}`}
|
159
|
+
placeholder=" "
|
160
|
+
style={{ width, height }}
|
161
|
+
/>
|
162
|
+
<label className="ff-textfield-label">{label}</label>
|
163
|
+
</div>
|
164
|
+
)}
|
165
|
+
<div className="ff-icon-container">
|
166
|
+
{confirmIcon && (
|
167
|
+
<Icon
|
168
|
+
color="var(--label-edit-confirm-icon)"
|
169
|
+
height={20}
|
170
|
+
width={20}
|
171
|
+
name={confirmIcon.name}
|
172
|
+
className="confirm-icon"
|
173
|
+
onClick={handleConfirm}
|
174
|
+
/>
|
175
|
+
)}
|
176
|
+
{cancelIcon && (
|
177
|
+
<Icon
|
178
|
+
color="var(--label-edit-cancel-icon)"
|
179
|
+
height={12}
|
180
|
+
width={20}
|
181
|
+
name={cancelIcon.name}
|
182
|
+
className="cancel-icon"
|
183
|
+
onClick={handleCancel}
|
184
|
+
ref={cancelRef}
|
185
|
+
/>
|
186
|
+
)}
|
187
|
+
</div>
|
188
|
+
</div>
|
189
|
+
) : (
|
190
|
+
<span
|
191
|
+
className="display-text"
|
192
|
+
onDoubleClick={handleDoubleClick}
|
193
|
+
role="button"
|
194
|
+
>
|
195
|
+
<HighlightText text={inputValue} highlight={highlightText} />
|
196
|
+
</span>
|
197
|
+
)}
|
198
|
+
{showError && isEditing && (
|
199
|
+
<Typography as="p" fontSize={8} className="error-text">
|
200
|
+
{showError}
|
201
|
+
</Typography>
|
202
|
+
)}
|
203
|
+
</div>
|
204
|
+
);
|
205
|
+
};
|
206
|
+
|
207
|
+
export default LabelEditTextField;
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default } from './LabelEditTextField';
|
@@ -0,0 +1,38 @@
|
|
1
|
+
export interface IconProps {
|
2
|
+
/** Name of the icon to be displayed. */
|
3
|
+
name: string;
|
4
|
+
/** Optional click handler function for the icon. */
|
5
|
+
onClick?: () => void;
|
6
|
+
}
|
7
|
+
export interface DropdownOption {
|
8
|
+
/** Unique identifier for the dropdown option. */
|
9
|
+
id: number;
|
10
|
+
/** Value associated with the dropdown option. */
|
11
|
+
value: string;
|
12
|
+
/** Label displayed for the dropdown option. */
|
13
|
+
label: string;
|
14
|
+
}
|
15
|
+
export interface LabelEditTextFieldTypes {
|
16
|
+
/** Label text displayed above the input field. */
|
17
|
+
label?: string;
|
18
|
+
/** Initial text displayed in the input field. */
|
19
|
+
text: string;
|
20
|
+
/** Text to be highlighted within the displayed text, if provided. */
|
21
|
+
highlightText?: string;
|
22
|
+
/** Custom error message to be displayed, if applicable. */
|
23
|
+
customError?: string;
|
24
|
+
/** Confirm icon properties including icon name and click handler. */
|
25
|
+
confirmIcon?: IconProps;
|
26
|
+
/** Cancel icon properties including icon name and click handler. */
|
27
|
+
cancelIcon?: IconProps;
|
28
|
+
/** Type of input field - standard text field or text field with a dropdown. */
|
29
|
+
variant?: 'textFieldWithDropdown' | 'textField';
|
30
|
+
/** Array of dropdown options used if the dropdown variant is selected. */
|
31
|
+
dropdownData?: DropdownOption[];
|
32
|
+
/** Width of the input field component. */
|
33
|
+
width?: string;
|
34
|
+
/** Height of the input field component. */
|
35
|
+
height?: string;
|
36
|
+
/** Function called when confirming input changes, with input and dropdown values as arguments. */
|
37
|
+
confirmAction?: (inputValue: string, dropdownValue: string) => void;
|
38
|
+
}
|