pixel-react 1.1.9 → 1.2.1
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/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
|
+
}
|