From de21b8602da810231f536fc58e4a1649fc2ca6f2 Mon Sep 17 00:00:00 2001 From: Ihor Korenets Date: Fri, 22 Mar 2024 14:08:16 +0200 Subject: [PATCH] [PickerTogglerTag]: made to use in PickerToggler renderItem by default or independently. --- uui-components/src/i18n.tsx | 2 +- uui-components/src/pickers/PickerToggler.tsx | 44 ++++++++------ .../src/pickers/hooks/usePickerInput.ts | 8 +++ uui/components/pickers/PickerToggler.tsx | 60 +++++-------------- uui/components/pickers/PickerTogglerTag.tsx | 45 ++++++++++++++ 5 files changed, 93 insertions(+), 66 deletions(-) create mode 100644 uui/components/pickers/PickerTogglerTag.tsx diff --git a/uui-components/src/i18n.tsx b/uui-components/src/i18n.tsx index b394d9d542..9368d41917 100644 --- a/uui-components/src/i18n.tsx +++ b/uui-components/src/i18n.tsx @@ -10,7 +10,7 @@ export const i18n = { showAll: 'SHOW ALL', }, pickerToggler: { - createItemValue: (length: number, entityName: string) => `${length} ${entityName} selected`, + createCollapsedName: (length: number, entityName: string) => `+ ${length} ${entityName} selected`, }, pickerInput: { defaultPlaceholder: (entity: string) => `Please select ${entity}`, diff --git a/uui-components/src/pickers/PickerToggler.tsx b/uui-components/src/pickers/PickerToggler.tsx index 66e0bc1d83..e057ef045a 100644 --- a/uui-components/src/pickers/PickerToggler.tsx +++ b/uui-components/src/pickers/PickerToggler.tsx @@ -1,9 +1,8 @@ import * as React from 'react'; -import { IPickerToggler, IHasIcon, IHasCX, ICanBeReadonly, Icon, uuiMod, uuiElement, uuiMarkers, DataRowProps, cx, IHasRawProps, ICanFocus, isEventTargetInsideClickable } from '@epam/uui-core'; +import { IPickerToggler, IHasIcon, IHasCX, ICanBeReadonly, Icon, uuiMod, uuiElement, uuiMarkers, cx, IHasRawProps, ICanFocus, isEventTargetInsideClickable, DataRowProps } from '@epam/uui-core'; import { IconContainer } from '../layout'; import css from './PickerToggler.module.scss'; import { i18n } from '../i18n'; -import { useCallback } from 'react'; import { getMaxItems } from './helpers'; export interface PickerTogglerProps @@ -27,6 +26,7 @@ export interface PickerTogglerProps * HTML ID attribute for the toggler input */ id?: string; + collapsedNames?: string[]; } function PickerTogglerComponent(props: PickerTogglerProps, ref: React.ForwardedRef) { @@ -37,7 +37,7 @@ function PickerTogglerComponent(props: PickerTogglerProps toggleContainer.current, [toggleContainer.current]); - const handleClick = useCallback( + const handleClick = React.useCallback( (event: Event) => { if (props.isInteractedOutside(event) && inFocus) { blur(); @@ -104,26 +104,32 @@ function PickerTogglerComponent(props: PickerTogglerProps { const maxItems = getMaxItems(props.maxItems); - if (props.selectedRowsCount > maxItems) { - return props.renderItem?.({ - value: i18n.pickerToggler.createItemValue(props.selectedRowsCount, props.entityName || ''), - onCheck: () => { - props.onClear?.(); + + const multiItems = props.selection?.map((row) => { + const newMultiItems = { ...row, + caption: props.getName(row.value), + isCollapsed: false, + isDisabled: row.isDisabled, + onClear: () => { + row.onCheck?.(row); // When we delete item it disappears from the DOM and focus is passed to the Body. So in this case we have to return focus on the toggleContainer by hand. toggleContainer.current?.focus(); - }, + } }; + return props.renderItem?.(newMultiItems); + }); + + if (props.selectedRowsCount > maxItems) { + const collapsedItem = props.renderItem?.({ + caption: i18n.pickerToggler.createCollapsedName(props.selectedRowsCount - maxItems, props.entityName || ''), + isCollapsed: true, + onClear: null, + isDisabled: false, + id: 'collapsed', } as any); - } else { - return props.selection?.map((row) => { - const newRow = { ...row, - onCheck: () => { - row.onCheck?.(row); - // When we delete item it disappears from the DOM and focus is passed to the Body. So in this case we have to return focus on the toggleContainer by hand. - toggleContainer.current?.focus(); - } }; - return props.renderItem?.(newRow); - }); + multiItems.push(collapsedItem); } + + return multiItems; }; const renderInput = () => { diff --git a/uui-components/src/pickers/hooks/usePickerInput.ts b/uui-components/src/pickers/hooks/usePickerInput.ts index 3c8baaa204..5f6f96925f 100644 --- a/uui-components/src/pickers/hooks/usePickerInput.ts +++ b/uui-components/src/pickers/hooks/usePickerInput.ts @@ -268,11 +268,18 @@ export function usePickerInput(props: UsePickerInputProps[], selectedRows: DataRowProps[]) => { + return [...allSelectedRow.filter((a) => !selectedRows.some((b) => a.id === b.id))] + .map((row: { value: { name?: string } }) => row?.value?.name); + }; + const getTogglerProps = (): PickerTogglerProps => { const selectedRowsCount = view.getSelectedRowsCount(); const allowedMaxItems = getMaxItems(props.maxItems); const itemsToTake = selectedRowsCount > allowedMaxItems ? allowedMaxItems : selectedRowsCount; const selectedRows = getSelectedRows(itemsToTake); + const allSelectedRow = getSelectedRows(); + const collapsedNames = getCollapsedNames(allSelectedRow, selectedRows); const { isDisabled, autoFocus, @@ -312,6 +319,7 @@ export function usePickerInput(props: UsePickerInputProps getName(i), entityName: getEntityName(selectedRowsCount), diff --git a/uui/components/pickers/PickerToggler.tsx b/uui/components/pickers/PickerToggler.tsx index cffcb45096..9eec68d221 100644 --- a/uui/components/pickers/PickerToggler.tsx +++ b/uui/components/pickers/PickerToggler.tsx @@ -1,21 +1,19 @@ import * as React from 'react'; -import { DataRowProps } from '@epam/uui-core'; -import { PickerToggler as UuiPickerToggler, PickerTogglerProps } from '@epam/uui-components'; -import { TextPlaceholder } from '../typography'; -import { systemIcons } from '../../icons/icons'; -import { Tag } from '../widgets'; import * as types from '../types'; -import { getMaxItems } from './helpers'; +import { PickerToggler as UuiPickerToggler, PickerTogglerProps } from '@epam/uui-components'; +import { DataRowProps } from '@epam/uui-core'; +import { PickerTogglerTag, PickerTogglerTagProps } from './PickerTogglerTag'; import css from './PickerToggler.module.scss'; +import { systemIcons } from '../../icons/icons'; const defaultSize = '36'; const defaultMode = types.EditMode.FORM; export interface PickerTogglerMods extends types.IHasEditMode { /** - * Defines component size - * @default 36 - */ + * Defines component size + * @default 36 + */ size?: '24' | '30' | '36' | '42' | '48'; } @@ -28,43 +26,13 @@ function applyPickerTogglerMods(mods: PickerTogglerMods) { } function PickerTogglerComponent(props: PickerTogglerProps & PickerTogglerMods, ref: React.ForwardedRef) { - const getPickerTogglerButtonSize = (propSize: types.ControlSize) => { - switch (propSize) { - case '48': - return '42'; - case '42': - return '36'; - case '36': - return '30'; - case '30': - return '24'; - case '24': - return '18'; - } - }; - - const getCaption = (row: DataRowProps) => { - const maxItems = getMaxItems(props.maxItems); - - if (row.isLoading) { - return ; - } else if (!props.getName || props.selectedRowsCount > maxItems) { - return row.value; - } else { - return props.getName(row.value); - } - }; - - const renderItem = (row: DataRowProps) => ( - { - row.onCheck?.(row); - } } - isDisabled={ props.isDisabled || props.isReadonly || row?.checkbox?.isDisabled } + const renderItem = (itemProps: DataRowProps & PickerTogglerTagProps) => ( + ); diff --git a/uui/components/pickers/PickerTogglerTag.tsx b/uui/components/pickers/PickerTogglerTag.tsx new file mode 100644 index 0000000000..aa98884a3e --- /dev/null +++ b/uui/components/pickers/PickerTogglerTag.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import * as types from '../types'; +import { Tag, TagProps } from '../widgets'; +import { Tooltip } from '../overlays'; + +export interface PickerTogglerTagProps extends TagProps { + size?: types.ControlSize; + collapsedNames?: string; + isCollapsed?: boolean; +} + +const getPickerTogglerButtonSize = (propSize?: types.ControlSize):TagProps['size'] => { + switch (propSize) { + case '48': + return '42'; + case '42': + return '36'; + case '36': + return '30'; + case '30': + return '24'; + case '24': + return '18'; + default: + return '30'; + } +}; + +export function PickerTogglerTag(props: PickerTogglerTagProps) { + const tagProps = { + ...props, + tabIndex: -1, + size: getPickerTogglerButtonSize(props.size), + }; + + if (props.isCollapsed && props.collapsedNames?.length) { + return ( + + + + ); + } + + return ; +}