ToolJet/frontend/src/Editor/Components/Table/CustomSelect.jsx
Kiran Ashok d9f796a85a
Fix : Table string column max height not getting applied , ui fixes in image column type (#9436)
* initial commit

* redesign column manager

* redesign string column type in the table

* setting the color of the input field for string column type

* setting bg of popover body

* design the style tab of string column type

* adjusted spaces in the properties and style elements in the column manager

* fixed typo error

* removed unwanted line

* redesign the validation section of number column type manager

* added the placeholder value for string column type min and max length in the validation section

* replace text with icons for horizontal alignment in the style tab

* show delete icon outside of the menu actions for each column in the table inspector

* Revert "redesign string column type in the table"

This reverts commit a440f3fc23, which made changes for string column type in the table component on the canvas

* making flex fill for horizontal alignment icon buttons in the style tab of column manager in the table

* Bug fixed: event manager is not working in toggle column type

* added decimalPlaces prop to number column manager

* added text color and cell background props to style tab of number column manager

* added few validations for number

* created boolean column type consisting input checkbox working as toggle switch

* allow user to select boolean column type from the component manager and display boolean column in the table on the canvas

* making boolean component content consistent with the horizontal alignment

* making Boolean column type functional

adding editable and non-editale content to display on table

* fixed horizonta alignment issue for boolean column

* Revert "fixed horizonta alignment issue for boolean column"

This reverts commit d41499a1ed.

* making input elment vertically aligned

* added generalised cell interaction

* added props for boolean column types in column manager of table

* added feature to provide custom bg to toggle switch

* removed default value to toggleOnBg and toggleOffBg

* conditionally displaying hardcoded value if toggleBg is not available

* change the file and component name of existing customSelect to customDropdown

* change component name

* remove unwanted code

* added select option to the column type dropdown field

* Created component for select column type

* rendering custom select component for select column type

* adding border danger for invalid value

* redesign column manager for select column type

* bug fixed : disabling control to select column type when editable is off

* changing the name key to label for select column type options

* partially implemented make default option feature

* Implemented default option for select column type

* added text color prop in a styles tab to select column type

* adding indigo color to selected option to select column type menu list

* avoid breaking table if array is not provided for dynamicOption field

* Organize the column manager and related files

* Added validation fields in text column manager

* Added text color to text area column type

* Added validation to text column type

* Avoiding bg to persisit for few column tupes, who dont have background field

* bug fixed : other valdiation are not rendering for string column type

* bug fixed: on hover always danger border is displayed for each cell

* Cell density enhancement

* Returning empty array for validationList for few column types

* handling validation list by merging organizing-column-manager

* Changed the select component in the properties tab of column popover

* Changed the UI of validation according to new design also few column popover and action popover UI changes

* Adding fx code block in style tab for text color and cell bg color

* fixing UI of select column type column manager according to new UI

* creating multiselect column and its UI wrt option list

* added overlay trigger to multiselect

* feat : added design theme new colors

* fix : adding new color swatch to input components

* fix : colors, number input with height less than 16px and number input arrow in padding none mdode

* fix : icon color

* few color fixes

* overlay trigger for multi-select column type

* color picker bug fixed

* color changes

* column type input text is not consistent with the dark theme- fixed

* custom select single value container bug

* manking add new row, hide column and download popover background consistent with the dark theme

* Making add new row consistent with the updated UI changes

* feat : link column type table

* fix: default underline color

* fix : sentence casing

* added missing transformation field

* fixes

* fixing tab opening condition

* fix : backward compatibility

* supported multiple badges , tags for cell density propety

* added feature to duplicate column

* made td container overflow hidden

* provided 100% width to Image fit prop in styles tab

* oveflow hidden prop changes

* revert unwanted change

* change the gap between columns in column lists inside table inspector

* Deprecate few column types in table

* Fix

* Fixes

* change the functionality of cell density feature

* Fix msg

* Revamp date picker in table

* Fix

* fix : darkmode colors

* revert

* Supported cell density feature for multiple badges

* making badges aligned center

* madde tags column consistent with cell density feature

* Remove imports

* enhancing the code density feature

* Making radio column consistent with the cell density feature

* Fix unixtimestamp

* Fix CSS issues

* making tags and badges overlay appear only when content is overflowed

* CSS fixes

* Fix

* Fix dark mode issues

* making background transparent for deprecating columns

making overlay visible on horizontal overflow

* New revamped styles

* fix box shadow

* Migrations to move visibility and disabled states from styles to properties

* undo change

* refactored custom select component

* patch fixes

* bug fixed action popover was inconsistent with dark theme

* testing

* update custom select column type

* fixes

* Avoided options being populated in columns

* removed consoles

* making custom select align center vertically

* on focus , we used to see on hover effect, avoiding it

* Design review changes

* Made text and string text-container according to design

* vertical positioning of select and multiselect

* overlay for deprecated columns

* regex placeholder

* Icons for number column type

* Design feedback changes

* Design fixes

* box shadow on select menu list in canvas

* added missing feature of decimal places and make increment and decrement icon vertically aligned

* Fix design issues

* Solve lint issues

* Design theme revert changes

* color of column list on hover and active is updated

* fixes

* changing the font weight of labels in styles tab of column manager

* fix

* Revert design theme

* label change

* horizontal alignment of select and datepicker columns

* Reverted back to textarea for text column type

* sync package-lock.json

* fix import

* UI fixes

* Css changes

* feat: Update default table data (#9312)

* updated default table data

* fix : table breaking

* fix : datepikcer crashing table

* fix : data

* fix :: table image height (#9307)

* fix  : Table datepicker UI fixes (#9324)

* fix : datepicker ui

* update

* fix

* refactor: removed unused codes

* add overlay

* add overlay

* Fix datepicker when date and time both are disabled

* Fix paddings and margins

* fix : default states in datepicker in table (#9335)

* feat : update default data with images (#9338)

* Design feedback

* fix multiselect column type issues

* Fix datepicker header width

* Fix dropdown

* Fix options loading state

* Fix select issues

* Fix multiselect default option

* fix overlay trigger multiselect

* fix: fixed issue with overlay display and cell content alignment for text

* fix: Multiselect popover (#9394)

* fix: popover only needed when content overflows

* fix

* fix: update zindex of edited text column

* fix: show overlay when text in string overlay overflows

* Fix multiselect default options

* fix: show error message for editable string cloumn

* Fix date issues

* Fix date fixes

* fix: more info popup close on mouse leave

* Fix unix timestamp issue

* Migration: set default demo data for older data (#9423)

* fix: backfill old default data if user did not add a data

* fix: backfill old default data if user did not add a data

* Table column redeisgn demo string fix (#9415)

* fix: show error message on table input validations

* fix: error message alignments

* Fix import issue for datepicker

* wrapping the contant of multiselect selected options (#9429)

* Fix popover in multiselect

* Add multiselect popover only when content wrap is false or max row height is custom

* Fix : String column types bug fixes (#9431)

* Bug fixed: Hiding max row height field when content wrap is disabled

* Removed border bottom when striped table style is applied

* removed unwanted code

* Apply hover effect on non-editable cell

* fix : Max column hight is not working in table

* Fixed width applied to multiselect popover

* fix : Remove height and width from table image column /  backward compatibility (#9425)

* fix : table column image , remove width and height

* optimise

* adding default object fit

* fix :  text is overlaping issue

* fix : non editable string column text alignment

* remove logs

* fix : text overflow

* fix : text padding

---------

Co-authored-by: manishkushare <kushare.manish9@gmail.com>
Co-authored-by: Nakul Nagargade <nakul@tooljet.com>
Co-authored-by: Nakul Nagargade <133095394+nakulnagargade@users.noreply.github.com>
Co-authored-by: Johnson Cherian <johnsonc.dev@gmail.com>
Co-authored-by: Manish Kushare <37823141+manishkushare@users.noreply.github.com>
2024-04-19 18:02:20 +05:30

345 lines
9.9 KiB
JavaScript

import React, { useRef, useState, useEffect, useMemo } from 'react';
import Select from '@/_ui/Select';
import { components } from 'react-select';
import defaultStyles from '@/_ui/Select/styles';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import { Checkbox } from '@/_ui/CheckBox/CheckBox';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import { isArray, isString } from 'lodash';
const { MenuList } = components;
const SCALING_FACTOR = 0.6;
const FONT_SIZE = 12;
const PADDING = 12;
const MARGIN = 10; // including left and right side
export const CustomSelect = ({
options,
value,
onChange,
fuzzySearch = false,
placeholder,
disabled,
className,
darkMode,
defaultOptionsList,
textColor = '',
isMulti,
containerWidth,
optionsLoadingState = false,
horizontalAlignment = 'left',
isEditable,
showPopoverIfOverflow,
isMaxRowHeightAuto,
}) => {
const containerRef = useRef(null);
const inputRef = useRef(null); // Ref for the input search box
const [isFocused, setIsFocused] = useState(false);
useEffect(() => {
const handleDocumentClick = (event) => {
if (isMulti && !containerRef.current?.contains(event.target)) {
setIsFocused(false);
}
};
document.addEventListener('mousedown', handleDocumentClick);
return () => {
document.removeEventListener('mousedown', handleDocumentClick);
};
}, []);
useEffect(() => {
// Focus the input search box when the menu list is open and the component is focused
if (isFocused && inputRef.current) {
inputRef.current.focus();
}
}, [isFocused]);
const customStyles = {
...defaultStyles(darkMode, '100%'),
...(isMulti && {
multiValue: (provided) => ({
...provided,
display: 'inline-block', // Display selected options inline
marginRight: '4px', // Add some space between options
}),
}),
valueContainer: (provided) => ({
...provided,
...(isMulti && {
marginBottom: '0',
display: 'flex',
flexWrap: 'no-wrap',
overflow: 'hidden',
flexDirection: 'row',
}),
justifyContent: horizontalAlignment,
}),
menuList: (base) => ({
...base,
backgroundColor: 'var(--surfaces-surface-01) ',
color: 'var(--text-primary)',
cursor: 'pointer',
overflow: 'auto',
}),
multiValueLabel: (provided) => ({
...provided,
padding: '2px 6px',
background: 'var(--surfaces-surface-03)',
margin: '0 5px',
borderRadius: '6px',
color: textColor || 'var(--text-primary)',
fontSize: '12px',
}),
singleValue: (provided) => ({
...provided,
padding: '2px 6px',
background: 'var(--surfaces-surface-03)',
margin: '0 5px',
borderRadius: '6px',
color: textColor || 'var(--text-primary)',
fontSize: '12px',
}),
};
const customComponents = {
MenuList: (props) => <CustomMenuList {...props} optionsLoadingState={optionsLoadingState} inputRef={inputRef} />,
Option: CustomMultiSelectOption,
DropdownIndicator: isEditable ? DropdownIndicator : null,
...(isMulti && {
MultiValueRemove,
MultiValueContainer: CustomMultiValueContainer,
}),
};
const defaultValue = useMemo(() => {
if (defaultOptionsList.length >= 1) {
return !isMulti ? defaultOptionsList[defaultOptionsList.length - 1] : defaultOptionsList;
}
}, [isMulti, defaultOptionsList]);
const _value = useMemo(() => {
// Return null to show default value
if (!value) {
return null;
}
if (isMulti && value?.length) {
if (isArray(value)) {
return options?.filter((option) =>
value?.find((val) => {
if (val.hasOwnProperty('value')) {
return option.value === val.value;
} else return option.value === val;
})
);
} else {
return []; // Return empty array to not show default value in case of wrong value to be set
}
} else {
// Condition for single select
return options?.find((option) => option.value === value) || [];
}
}, [options, value, isMulti]);
const calculateIfPopoverRequired = (value, containerWidth) => {
let totalWidth = 0;
// // Calculate total width of all span elements
for (const option of value) {
const valueWidth = option.label.length * FONT_SIZE * SCALING_FACTOR + PADDING + MARGIN;
// Check if max row height is auto and then if any of the options width exceeds container width, return true
if (isMaxRowHeightAuto) {
if (valueWidth > containerWidth) {
return true;
}
} else {
totalWidth += valueWidth + 4; // Adding 4px to be safe
}
}
return totalWidth > containerWidth; // Return based on total width comparison
};
return (
<OverlayTrigger
placement="bottom"
overlay={
isMulti && showPopoverIfOverflow && (_value?.length || defaultValue?.length) && !isFocused ? (
getOverlay(_value ? _value : defaultValue, containerWidth, darkMode)
) : (
<div></div>
)
}
trigger={
isMulti &&
!isFocused &&
showPopoverIfOverflow &&
calculateIfPopoverRequired(_value ? _value : defaultValue, containerWidth - 40) && ['hover', 'focus']
} //container width -24 -16 gives that select container size
rootClose={true}
>
<div className="w-100 h-100 d-flex align-items-center">
<Select
options={options}
hasSearch={false}
fuzzySearch={fuzzySearch}
isDisabled={disabled}
className={className}
components={customComponents}
value={_value}
onMenuInputFocus={() => setIsFocused(true)}
onChange={(value) => {
setIsFocused(false);
onChange(value);
}}
useCustomStyles={true}
styles={customStyles}
defaultValue={defaultValue}
placeholder={placeholder}
isMulti={isMulti}
hideSelectedOptions={false}
isClearable={false}
clearIndicator={false}
{...{
menuIsOpen: isFocused || undefined,
isFocused: isFocused || undefined,
}}
/>
</div>
</OverlayTrigger>
);
};
const CustomMenuList = ({ optionsLoadingState, children, selectProps, inputRef, ...props }) => {
const { onInputChange, inputValue, onMenuInputFocus } = selectProps;
return (
<div className="table-select-custom-menu-list" onClick={(e) => e.stopPropagation()}>
<div className="table-select-column-type-search-box-wrapper ">
{!inputValue && (
<span className="">
<SolidIcon name="search" width="14" />
</span>
)}
<input
autoCorrect="off"
autoComplete="off"
spellCheck="false"
type="text"
value={inputValue}
onChange={(e) =>
onInputChange(e.currentTarget.value, {
action: 'input-change',
})
}
onMouseDown={(e) => {
e.stopPropagation();
e.target.focus();
}}
onTouchEnd={(e) => {
e.stopPropagation();
e.target.focus();
}}
onFocus={onMenuInputFocus}
placeholder="Search..."
className="table-select-column-type-search-box"
ref={inputRef} // Assign the ref to the input search box
/>
</div>
<MenuList {...props} selectProps={selectProps}>
{optionsLoadingState ? (
<div class="text-center py-4">
<div class="spinner-border text-primary" role="status">
<span class="sr-only"></span>
</div>
</div>
) : (
children
)}
</MenuList>
</div>
);
};
const CustomMultiSelectOption = ({ innerRef, innerProps, children, isSelected, ...props }) => {
return (
<div ref={innerRef} {...innerProps} className="option-wrapper d-flex">
{props.isMulti ? (
<Checkbox label="" isChecked={isSelected} onChange={(e) => e.stopPropagation()} key="" value={children} />
) : (
<div style={{ visibility: isSelected ? 'visible' : 'hidden' }}>
<Checkbox label="" isChecked={isSelected} onChange={(e) => e.stopPropagation()} key="" value={children} />
</div>
)}
{children}
</div>
);
};
const MultiValueRemove = (props) => {
const { innerProps } = props;
return <div {...innerProps} />;
};
const CustomMultiValueContainer = (props) => {
return (
<div
style={{
display: 'flex',
flexWrap: 'wrap',
alignItems: 'center',
gap: '4px',
}}
>
{props.children}
</div>
);
};
const getOverlay = (value, containerWidth, darkMode) => {
const getLabel = (option) => {
if (option?.hasOwnProperty('label')) {
return option.label;
} else if (isString(option)) {
return option;
} else return '';
};
return Array.isArray(value) ? (
<div
style={{
maxWidth: '266px',
}}
className={`overlay-cell-table overlay-multiselect-table ${darkMode && 'dark-theme'}`}
>
{value?.map((option) => {
return (
<span
style={{
padding: '2px 6px',
background: 'var(--surfaces-surface-03)',
borderRadius: '6px',
color: 'var(--text-primary)',
fontSize: '12px',
}}
key={getLabel(option)}
>
{getLabel(option)}
</span>
);
})}
</div>
) : (
<div></div>
);
};
const DropdownIndicator = (props) => {
return (
<div {...props} className="cell-icon-display">
{/* Your custom SVG */}
{props.selectProps.menuIsOpen ? (
<SolidIcon name="arrowUpTriangle" width="16" height="16" fill={'#6A727C'} />
) : (
<SolidIcon name="arrowDownTriangle" width="16" height="16" fill={'#6A727C'} />
)}
</div>
);
};