diff --git a/components/doc/treeselect/clearicondoc.js b/components/doc/treeselect/clearicondoc.js new file mode 100644 index 0000000000..0d9b51f97d --- /dev/null +++ b/components/doc/treeselect/clearicondoc.js @@ -0,0 +1,107 @@ +import React, { useEffect, useState } from 'react'; +import { NodeService } from '../../../service/NodeService'; +import { TreeSelect } from '../../lib/treeselect/TreeSelect'; +import { DocSectionCode } from '../common/docsectioncode'; +import { DocSectionText } from '../common/docsectiontext'; + +export function ClearIconDoc(props) { + const [nodes, setNodes] = useState(null); + const [selectedNodeKey, setSelectedNodeKey] = useState(null); + + useEffect(() => { + NodeService.getTreeNodes().then((data) => setNodes(data)); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + const code = { + basic: ` + setSelectedNodeKey(e.value)} options={nodes} + className="md:w-20rem w-full" placeholder="Select Item"> + `, + javascript: ` +import React, { useState, useEffect } from "react"; +import { TreeSelect } from 'primereact/treeselect'; +import { NodeService } from './service/NodeService'; + +export default function BasicDemo() { + const [nodes, setNodes] = useState(null); + const [selectedNodeKey, setSelectedNodeKey] = useState(null); + + useEffect(() => { + NodeService.getTreeNodes().then((data) => setNodes(data)); + }, []); + + return ( +
+ setSelectedNodeKey(e.value)} options={nodes} + className="md:w-20rem w-full" placeholder="Select Item" showClear> +
+ ); +} + `, + typescript: ` +import React, { useState, useEffect } from "react"; +import { TreeSelect, TreeSelectChangeEvent } from 'primereact/treeselect'; +import { TreeNode } from 'primereact/treenode'; +import { NodeService } from './service/NodeService'; + +export default function BasicDemo() { + const [nodes, setNodes] = useState(null); + const [selectedNodeKey, setSelectedNodeKey] = useState(null); + + useEffect(() => { + NodeService.getTreeNodes().then((data) => setNodes(data)); + }, []); + + return ( +
+ setSelectedNodeKey(e.value)} + className="md:w-20rem w-full" placeholder="Select Item" showClear> +
+ ); +} + `, + data: ` +/* NodeService */ +{ + key: '0', + label: 'Documents', + data: 'Documents Folder', + icon: 'pi pi-fw pi-inbox', + children: [ + { + key: '0-0', + label: 'Work', + data: 'Work Folder', + icon: 'pi pi-fw pi-cog', + children: [ + { key: '0-0-0', label: 'Expenses.doc', icon: 'pi pi-fw pi-file', data: 'Expenses Document' }, + { key: '0-0-1', label: 'Resume.doc', icon: 'pi pi-fw pi-file', data: 'Resume Document' } + ] + }, + { + key: '0-1', + label: 'Home', + data: 'Home Folder', + icon: 'pi pi-fw pi-home', + children: [{ key: '0-1-0', label: 'Invoices.txt', icon: 'pi pi-fw pi-file', data: 'Invoices for this month' }] + } + ] +}, +... +` + }; + + return ( + <> + +

+ When showClear is enabled, a clear icon is added to reset the TreeSelect. +

+
+
+ setSelectedNodeKey(e.value)} options={nodes} className="md:w-20rem w-full" placeholder="Select Item" showClear> +
+ + + ); +} diff --git a/components/lib/dropdown/Dropdown.js b/components/lib/dropdown/Dropdown.js index b265bc1b99..3c225863aa 100644 --- a/components/lib/dropdown/Dropdown.js +++ b/components/lib/dropdown/Dropdown.js @@ -1,6 +1,6 @@ import * as React from 'react'; -import PrimeReact, { FilterService } from '../api/Api'; -import { PrimeReactContext } from '../api/Api'; +import PrimeReact, { FilterService, PrimeReactContext } from '../api/Api'; +import { useHandleStyle } from '../componentbase/ComponentBase'; import { useMountEffect, useOverlayListener, useUnmountEffect, useUpdateEffect } from '../hooks/Hooks'; import { ChevronDownIcon } from '../icons/chevrondown'; import { TimesIcon } from '../icons/times'; @@ -9,7 +9,6 @@ import { Tooltip } from '../tooltip/Tooltip'; import { DomHandler, IconUtils, ObjectUtils, ZIndexUtils, mergeProps } from '../utils/Utils'; import { DropdownBase } from './DropdownBase'; import { DropdownPanel } from './DropdownPanel'; -import { useHandleStyle } from '../componentbase/ComponentBase'; export const Dropdown = React.memo( React.forwardRef((inProps, ref) => { @@ -624,6 +623,7 @@ export const Dropdown = React.memo( props, show, hide, + clear, focus: () => DomHandler.focus(focusInputRef.current), getElement: () => elementRef.current, getOverlay: () => overlayRef.current, diff --git a/components/lib/dropdown/DropdownBase.js b/components/lib/dropdown/DropdownBase.js index ae3a84a28f..2bb561227a 100644 --- a/components/lib/dropdown/DropdownBase.js +++ b/components/lib/dropdown/DropdownBase.js @@ -23,7 +23,6 @@ const classes = { 'p-dropdown-label-empty': label === null && !props.placeholder }), trigger: 'p-dropdown-trigger', - clearIcon: 'p-dropdown-clear-icon p-clickable', emptyMessage: 'p-dropdown-empty-message', itemGroup: 'p-dropdown-item-group', dropdownIcon: 'p-dropdown-trigger-icon p-clickable', @@ -60,12 +59,6 @@ const styles = ` user-select: none; } -.p-dropdown-clear-icon { - position: absolute; - top: 50%; - margin-top: -.5rem; -} - .p-dropdown-trigger { display: flex; align-items: center; @@ -128,6 +121,7 @@ input.p-dropdown-label { position: relative; } +.p-dropdown-clear-icon, .p-dropdown-filter-icon, .p-dropdown-filter-clear-icon { position: absolute; diff --git a/components/lib/dropdown/dropdown.d.ts b/components/lib/dropdown/dropdown.d.ts index 5c70430b8c..a6083f7698 100644 --- a/components/lib/dropdown/dropdown.d.ts +++ b/components/lib/dropdown/dropdown.d.ts @@ -508,6 +508,18 @@ export declare class Dropdown extends React.Component { * Used to focus the component. */ public focus(): void; + /** + * Clear the currently selected value. + */ + public clear(): void; + /** + * Show the dropdown overlay panel. + */ + public show(): void; + /** + * Hide the dropdown overlay panel. + */ + public hide(): void; /** * Used to get container element. * @return {HTMLDivElement} Container element diff --git a/components/lib/treeselect/TreeSelect.js b/components/lib/treeselect/TreeSelect.js index ff6ab9854f..fcac6b4ee0 100644 --- a/components/lib/treeselect/TreeSelect.js +++ b/components/lib/treeselect/TreeSelect.js @@ -1,6 +1,6 @@ import * as React from 'react'; -import PrimeReact, { localeOption } from '../api/Api'; -import { PrimeReactContext } from '../api/Api'; +import PrimeReact, { PrimeReactContext, localeOption } from '../api/Api'; +import { useHandleStyle } from '../componentbase/ComponentBase'; import { useMountEffect, useOverlayListener, useUnmountEffect, useUpdateEffect } from '../hooks/Hooks'; import { ChevronDownIcon } from '../icons/chevrondown'; import { SearchIcon } from '../icons/search'; @@ -8,10 +8,9 @@ import { TimesIcon } from '../icons/times'; import { OverlayService } from '../overlayservice/OverlayService'; import { Ripple } from '../ripple/Ripple'; import { Tree } from '../tree/Tree'; -import { DomHandler, IconUtils, ObjectUtils, ZIndexUtils, classNames, mergeProps } from '../utils/Utils'; +import { DomHandler, IconUtils, ObjectUtils, ZIndexUtils, mergeProps } from '../utils/Utils'; import { TreeSelectBase } from './TreeSelectBase'; import { TreeSelectPanel } from './TreeSelectPanel'; -import { useHandleStyle } from '../componentbase/ComponentBase'; export const TreeSelect = React.memo( React.forwardRef((inProps, ref) => { @@ -110,6 +109,28 @@ export const TreeSelect = React.memo( } }; + const clear = (event) => { + if (props.onChange) { + selfChange.current = true; + + props.onChange({ + originalEvent: event, + value: undefined, + stopPropagation: () => { + event.stopPropagation(); + }, + preventDefault: () => { + event.preventDefault(); + }, + target: { + name: props.name, + id: props.id, + value: undefined + } + }); + } + }; + const onNodeSelect = (node) => { props.onNodeSelect && props.onNodeSelect(node); isSingleSelectionMode && hide(); @@ -323,6 +344,9 @@ export const TreeSelect = React.memo( React.useImperativeHandle(ref, () => ({ props, + clear, + show, + hide, focus: () => DomHandler.focus(focusInputRef.current), getElement: () => elementRef.current })); @@ -485,6 +509,23 @@ export const TreeSelect = React.memo( return
{dropdownIcon}
; }; + const createClearIcon = () => { + if (props.value != null && props.showClear && !props.disabled) { + const clearIconProps = mergeProps( + { + className: cx('clearIcon'), + onPointerUp: clear + }, + ptm('clearIcon') + ); + const icon = props.clearIcon || ; + + return IconUtils.getJSXIcon(icon, { ...clearIconProps }, { props }); + } + + return null; + }; + const createContent = () => { const emptyMessageProps = mergeProps( { @@ -665,6 +706,7 @@ export const TreeSelect = React.memo( const keyboardHelper = createKeyboardHelper(); const labelElement = createLabel(); const dropdownIcon = createDropdownIcon(); + const clearIcon = createClearIcon(); const content = createContent(); const header = createHeader(); const footer = createFooter(); @@ -673,6 +715,7 @@ export const TreeSelect = React.memo(
{keyboardHelper} {labelElement} + {clearIcon} {dropdownIcon} | React.HTMLAttributes>; + /** + * uses to pass attributes to the clear icon's DOM element. + */ + clearIcon?: TreeSelectPassThroughType | React.HTMLAttributes>; } /** @@ -303,6 +307,10 @@ export interface TreeSelectProps extends Omit | undefined; /** * Icon of the close button. */ @@ -439,6 +447,11 @@ export interface TreeSelectProps extends Omit { * Used to focus the component. */ public focus(): void; + /** + * Clear the currently selected value. + */ + public clear(): void; + /** + * Show the dropdown overlay panel. + */ + public show(): void; + /** + * Hide the dropdown overlay panel. + */ + public hide(): void; /** * Used to get container element. * @return {HTMLDivElement} Container element diff --git a/pages/treeselect/index.js b/pages/treeselect/index.js index de6d283f6e..d3602b42a7 100644 --- a/pages/treeselect/index.js +++ b/pages/treeselect/index.js @@ -1,10 +1,10 @@ import DocApiTable from '../../components/doc/common/docapitable'; -import { PTDoc } from '../../components/doc/treeselect/pt/ptdoc'; -import { Wireframe } from '../../components/doc/treeselect/pt/wireframe'; import { DocComponent } from '../../components/doc/common/doccomponent'; import { AccessibilityDoc } from '../../components/doc/treeselect/accessibilitydoc'; import { BasicDoc } from '../../components/doc/treeselect/basicdoc'; import { CheckboxDoc } from '../../components/doc/treeselect/checkboxdoc'; +import { ClearIconDoc } from '../../components/doc/treeselect/clearicondoc'; +import { ControlledDoc } from '../../components/doc/treeselect/controlleddoc'; import { DisabledDoc } from '../../components/doc/treeselect/disableddoc'; import { FilterDoc } from '../../components/doc/treeselect/filterdoc'; import { FloatLabelDoc } from '../../components/doc/treeselect/floatlabeldoc'; @@ -13,7 +13,8 @@ import { HookFormDoc } from '../../components/doc/treeselect/form/hookformdoc'; import { ImportDoc } from '../../components/doc/treeselect/importdoc'; import { InvalidDoc } from '../../components/doc/treeselect/invaliddoc'; import { MultipleDoc } from '../../components/doc/treeselect/multipledoc'; -import { ControlledDoc } from '../../components/doc/treeselect/controlleddoc'; +import { PTDoc } from '../../components/doc/treeselect/pt/ptdoc'; +import { Wireframe } from '../../components/doc/treeselect/pt/wireframe'; import { StyleDoc } from '../../components/doc/treeselect/styledoc'; const TreeSelectDemo = () => { @@ -43,6 +44,11 @@ const TreeSelectDemo = () => { label: 'Filter', component: FilterDoc }, + { + id: 'clearicon', + label: 'Clear Icon', + component: ClearIconDoc + }, { id: 'controlled', label: 'Controlled',