Skip to content

Commit

Permalink
Fix primefaces#4716: TreeSelect showClear and clearIcon
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed Aug 14, 2023
1 parent 995bdee commit 8ffc9fe
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 14 deletions.
107 changes: 107 additions & 0 deletions components/doc/treeselect/clearicondoc.js
Original file line number Diff line number Diff line change
@@ -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: `
<TreeSelect value={selectedNodeKey} onChange={(e) => setSelectedNodeKey(e.value)} options={nodes}
className="md:w-20rem w-full" placeholder="Select Item"></TreeSelect>
`,
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 (
<div className="card flex justify-content-center">
<TreeSelect value={selectedNodeKey} onChange={(e) => setSelectedNodeKey(e.value)} options={nodes}
className="md:w-20rem w-full" placeholder="Select Item" showClear></TreeSelect>
</div>
);
}
`,
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<TreeNode[] | null>(null);
const [selectedNodeKey, setSelectedNodeKey] = useState<string>(null);
useEffect(() => {
NodeService.getTreeNodes().then((data) => setNodes(data));
}, []);
return (
<div className="card flex justify-content-center">
<TreeSelect value={selectedNodeKey} options={nodes} onChange={(e : TreeSelectChangeEvent) => setSelectedNodeKey(e.value)}
className="md:w-20rem w-full" placeholder="Select Item" showClear></TreeSelect>
</div>
);
}
`,
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 (
<>
<DocSectionText {...props}>
<p>
When <i>showClear</i> is enabled, a clear icon is added to reset the TreeSelect.
</p>
</DocSectionText>
<div className="card flex justify-content-center">
<TreeSelect value={selectedNodeKey} onChange={(e) => setSelectedNodeKey(e.value)} options={nodes} className="md:w-20rem w-full" placeholder="Select Item" showClear></TreeSelect>
</div>
<DocSectionCode code={code} service={['NodeService']} />
</>
);
}
8 changes: 1 addition & 7 deletions components/lib/dropdown/DropdownBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
48 changes: 44 additions & 4 deletions components/lib/treeselect/TreeSelect.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
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';
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) => {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -485,6 +506,23 @@ export const TreeSelect = React.memo(
return <div {...triggerProps}>{dropdownIcon}</div>;
};

const createClearIcon = () => {
if (props.value != null && props.showClear && !props.disabled) {
const clearIconProps = mergeProps(
{
className: cx('clearIcon'),
onPointerUp: clear
},
ptm('clearIcon')
);
const icon = props.clearIcon || <TimesIcon {...clearIconProps} />;

return IconUtils.getJSXIcon(icon, { ...clearIconProps }, { props });
}

return null;
};

const createContent = () => {
const emptyMessageProps = mergeProps(
{
Expand Down Expand Up @@ -665,6 +703,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();
Expand All @@ -673,6 +712,7 @@ export const TreeSelect = React.memo(
<div {...rootProps}>
{keyboardHelper}
{labelElement}
{clearIcon}
{dropdownIcon}
<TreeSelectPanel
ref={overlayRef}
Expand Down
4 changes: 4 additions & 0 deletions components/lib/treeselect/TreeSelectBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const classes = {
'p-treeselect p-component p-inputwrapper',
{
'p-treeselect-chip': props.display === 'chip',
'p-treeselect-clearable': props.showClear && !props.disabled,
'p-disabled': props.disabled,
'p-focus': focusedState,
'p-inputwrapper-filled': !isValueEmpty,
Expand Down Expand Up @@ -35,6 +36,7 @@ const classes = {
filter: 'p-treeselect-filter p-inputtext p-component',
filterIcon: 'p-treeselect-filter-icon',
closeIcon: 'p-treeselect-close-icon',
clearIcon: 'p-treeselect-clear-icon p-clickable',
closeButton: 'p-treeselect-close p-link',
header: 'p-treeselect-header',
wrapper: 'p-treeselect-items-wrapper'
Expand Down Expand Up @@ -139,6 +141,7 @@ export const TreeSelectBase = ComponentBase.extend({
ariaLabelledBy: null,
className: null,
closeIcon: null,
clearIcon: null,
disabled: false,
display: 'comma',
dropdownIcon: null,
Expand Down Expand Up @@ -175,6 +178,7 @@ export const TreeSelectBase = ComponentBase.extend({
resetFilterOnHide: false,
scrollHeight: '400px',
selectionMode: 'single',
showClear: false,
style: null,
tabIndex: null,
togglerTemplate: null,
Expand Down
13 changes: 13 additions & 0 deletions components/lib/treeselect/treeselect.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ export interface TreeSelectPassThroughOptions {
* uses to pass attributes to the close button's DOM element.
*/
closeButton?: TreeSelectPassThroughType<React.SVGProps<SVGSVGElement> | React.HTMLAttributes<HTMLButtonElement>>;
/**
* uses to pass attributes to the clear icon's DOM element.
*/
clearIcon?: TreeSelectPassThroughType<React.SVGProps<SVGSVGElement> | React.HTMLAttributes<HTMLSpanElement>>;
}

/**
Expand Down Expand Up @@ -303,6 +307,10 @@ export interface TreeSelectProps extends Omit<React.DetailedHTMLProps<React.Inpu
* @readonly
*/
children?: React.ReactNode | undefined;
/**
* Icon of the dropdown.
*/
clearIcon?: IconType<TreeSelectProps> | undefined;
/**
* Icon of the close button.
*/
Expand Down Expand Up @@ -439,6 +447,11 @@ export interface TreeSelectProps extends Omit<React.DetailedHTMLProps<React.Inpu
* @defaultValue 400px
*/
scrollHeight?: string | undefined;
/**
* When enabled, a clear icon is displayed to clear the value.
* @defaultValue false
*/
showClear?: boolean | undefined;
/**
* Defines the selection mode, valid values "single", "multiple", and "checkbox".
*/
Expand Down
12 changes: 9 additions & 3 deletions pages/treeselect/index.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 = () => {
Expand Down Expand Up @@ -43,6 +44,11 @@ const TreeSelectDemo = () => {
label: 'Filter',
component: FilterDoc
},
{
id: 'clearicon',
label: 'Clear Icon',
component: ClearIconDoc
},
{
id: 'controlled',
label: 'Controlled',
Expand Down

0 comments on commit 8ffc9fe

Please sign in to comment.