Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #2745: Multiselect inline flex #2744

Merged
merged 2 commits into from
Nov 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions api-generator/components/multiselect.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ const MultiSelectProps = [
default: 'null',
description: 'Property name or getter function that refers to the children options of option group.'
},
{
name: 'inline',
type: 'boolean',
default: 'false',
description: 'Render the items panel inline.'
},
{
name: 'flex',
type: 'boolean',
default: 'false',
description: 'Use flex layout for the items panel.'
},
{
name: 'style',
type: 'string',
Expand All @@ -65,6 +77,12 @@ const MultiSelectProps = [
default: 'null',
description: 'Style class of the element.'
},
{
name: 'itemClassName',
type: 'string',
default: 'null',
description: 'Style class of the items.'
},
{
name: 'panelClassName',
type: 'string',
Expand Down
105 changes: 105 additions & 0 deletions components/doc/multiselect/inlinedoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { useState } from 'react';
import { MultiSelect } from '../../lib/multiselect/MultiSelect';
import { DocSectionCode } from '../common/docsectioncode';
import { DocSectionText } from '../common/docsectiontext';

export function InlineDoc(props) {
const [selectedCities, setSelectedCities] = useState(null);
const cities = [
{ label: 'New York', value: 'NY' },
{ label: 'Rome', value: 'RM' },
{ label: 'London', value: 'LDN' },
{ label: 'Istanbul', value: 'IST' },
{ label: 'Paris', value: 'PRS' }
];

const code = {
basic: `
<MultiSelect inline value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<MultiSelect flex value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<MultiSelect inline flex value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<MultiSelect flex itemClassName="col-4" value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<MultiSelect inline flex itemClassName="col-4" value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
`,
javascript: `
import { useState } from "react";
import { MultiSelect } from 'primereact/multiselect';

export default function InlineDoc() {
const [selectedCities, setSelectedCities] = useState(null);
const cities = [
{ label: 'New York', value: 'NY' },
{ label: 'Rome', value: 'RM' },
{ label: 'London', value: 'LDN' },
{ label: 'Istanbul', value: 'IST' },
{ label: 'Paris', value: 'PRS' }
];

return (
<MultiSelect inline value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<MultiSelect flex value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<MultiSelect inline flex value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<MultiSelect flex itemClassName="col-4" value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<MultiSelect inline flex itemClassName="col-4" value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
);
}
`,
typescript: `
import { useState } from "react";
import { MultiSelect, MultiSelectChangeParams } from 'primereact/multiselect';

export default function InlineDoc() {
const [selectedCities, setSelectedCities] = useState<any>(null);
const cities = [
{ label: 'New York', value: 'NY' },
{ label: 'Rome', value: 'RM' },
{ label: 'London', value: 'LDN' },
{ label: 'Istanbul', value: 'IST' },
{ label: 'Paris', value: 'PRS' }
];

return (
<MultiSelect inline value={selectedCities} options={cities} onChange={(e : MultiSelectChangeParams) => setSelectedCities(e.value)} />
<MultiSelect flex value={selectedCities} options={cities} onChange={(e : MultiSelectChangeParams) => setSelectedCities(e.value)} />
<MultiSelect inline flex value={selectedCities} options={cities} onChange={(e : MultiSelectChangeParams) => setSelectedCities(e.value)} />
<MultiSelect flex itemClassName="col-4" value={selectedCities} options={cities} onChange={(e : MultiSelectChangeParams) => setSelectedCities(e.value)} />
<MultiSelect inline flex itemClassName="col-4" value={selectedCities} options={cities} onChange={(e : MultiSelectChangeParams) => setSelectedCities(e.value)} />
);
}
`
};

return (
<>
<DocSectionText {...props}>
Set the property <i>inline</i> to true to display the items as inline panel. Set the property <i>flex</i> to true to use flex layout for the items panel. Use the property <i>itemClassName</i> to control the class of the items, for
example to display them with a fixed width.
</DocSectionText>
<div className="card flex justify-content-center gap-3">
<div>
inline
<MultiSelect inline value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
</div>
<div>
flex
<br />
<MultiSelect flex value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<br />
<br />
inline + flex
<MultiSelect inline flex value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
</div>
<div>
flex + itemClassName
<br />
<MultiSelect flex itemClassName="col-3" value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
<br />
<br />
inline + flex + itemClassName
<MultiSelect inline flex itemClassName="col-3" value={selectedCities} options={cities} onChange={(e) => setSelectedCities(e.value)} />
</div>
</div>
<DocSectionCode code={code} />
</>
);
}
10 changes: 6 additions & 4 deletions components/lib/calendar/calendar.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ type CalendarIconPosType = 'left' | 'right';

type CalendarEventType = React.SyntheticEvent | undefined | null;

type CalendarValueType = Date | Date[] | string | undefined | null;

interface CalendarChangeTargetOptions {
name: string;
id: string;
value: Date | Date[] | undefined | null;
value: CalendarValueType;
}

interface CalendarChangeParams {
originalEvent: React.SyntheticEvent;
value: Date | Date[] | undefined;
value: CalendarValueType;
stopPropagation(): void;
preventDefault(): void;
target: CalendarChangeTargetOptions;
Expand All @@ -37,7 +39,7 @@ interface CalendarViewChangeParams {

interface CalendarSelectParams {
originalEvent: React.SyntheticEvent;
value: Date | Date[];
value: CalendarValueType;
}

interface CalendarDateTemplateParams {
Expand Down Expand Up @@ -131,7 +133,7 @@ export interface CalendarProps {
tooltipOptions?: TooltipOptions;
touchUI?: boolean;
transitionOptions?: CSSTransitionProps;
value?: Date | Date[];
value?: CalendarValueType;
view?: string;
viewDate?: Date;
visible?: boolean;
Expand Down
16 changes: 16 additions & 0 deletions components/lib/multiselect/MultiSelect.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@
left: 0;
}

.p-multiselect-inline.p-multiselect-panel {
border: none;
position: initial;
background: none;
box-shadow: none;
}

.p-multiselect-inline.p-multiselect-panel .p-multiselect-items {
padding: 0;
}

.p-multiselect-flex.p-multiselect-panel .p-multiselect-items {
display: flex;
flex-wrap: wrap;
}

.p-multiselect-items-wrapper {
overflow: auto;
}
Expand Down
25 changes: 18 additions & 7 deletions components/lib/multiselect/MultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const MultiSelect = React.memo(
React.forwardRef((props, ref) => {
const [filterState, setFilterState] = React.useState('');
const [focusedState, setFocusedState] = React.useState(false);
const [overlayVisibleState, setOverlayVisibleState] = React.useState(false);
const [overlayVisibleState, setOverlayVisibleState] = React.useState(props.inline);
const elementRef = React.useRef(null);
const inputRef = React.useRef(props.inputRef);
const labelRef = React.useRef(null);
Expand Down Expand Up @@ -113,7 +113,7 @@ export const MultiSelect = React.memo(
};

const onClick = (event) => {
if (!props.disabled && !isPanelClicked(event) && !DomHandler.hasClass(event.target, 'p-multiselect-token-icon') && !isClearClicked(event)) {
if (!props.inline && !props.disabled && !isPanelClicked(event) && !DomHandler.hasClass(event.target, 'p-multiselect-token-icon') && !isClearClicked(event)) {
overlayVisibleState ? hide() : show();
DomHandler.focus(inputRef.current);
event.preventDefault();
Expand All @@ -124,6 +124,8 @@ export const MultiSelect = React.memo(
switch (event.which) {
//down
case 40:
if (props.inline) break;

if (!overlayVisibleState && event.altKey) {
show();
event.preventDefault();
Expand All @@ -133,12 +135,14 @@ export const MultiSelect = React.memo(

//space
case 32:
if (props.inline) break;
overlayVisibleState ? hide() : show();
event.preventDefault();
break;

//escape
case 27:
if (props.inline) break;
hide();
break;

Expand Down Expand Up @@ -584,8 +588,8 @@ export const MultiSelect = React.memo(
},
props.className
);
const label = createLabel();
const clearIcon = createClearIcon();
const label = !props.inline && createLabel();
const clearIcon = !props.inline && createClearIcon();

return (
<>
Expand All @@ -607,9 +611,13 @@ export const MultiSelect = React.memo(
{...ariaProps}
/>
</div>
{label}
{clearIcon}
<div className="p-multiselect-trigger">{IconUtils.getJSXIcon(props.dropdownIcon, { className: 'p-multiselect-trigger-icon p-c' }, { props })}</div>
{!props.inline && (
<>
{label}
{clearIcon}
<div className="p-multiselect-trigger">{IconUtils.getJSXIcon(props.dropdownIcon, { className: 'p-multiselect-trigger-icon p-c' }, { props })}</div>
</>
)}
<MultiSelectPanel
ref={overlayRef}
visibleOptions={visibleOptions}
Expand Down Expand Up @@ -665,9 +673,12 @@ MultiSelect.defaultProps = {
filterPlaceholder: null,
filterTemplate: null,
fixedPlaceholder: false,
flex: false,
id: null,
inline: false,
inputId: null,
inputRef: null,
itemClassName: null,
itemTemplate: null,
maxSelectedLabels: null,
name: null,
Expand Down
1 change: 1 addition & 0 deletions components/lib/multiselect/MultiSelectItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const MultiSelectItem = React.memo((props) => {
'p-highlight': props.selected,
'p-disabled': props.disabled
},
props.className,
props.option.className
);
const checkboxClassName = classNames('p-checkbox-box', {
Expand Down
15 changes: 15 additions & 0 deletions components/lib/multiselect/MultiSelectPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const MultiSelectPanel = React.memo(
onKeyDown={props.onOptionKeyDown}
tabIndex={tabIndex}
disabled={disabled}
className={props.itemClassName}
/>
);
});
Expand Down Expand Up @@ -130,6 +131,7 @@ export const MultiSelectPanel = React.memo(
onKeyDown={props.onOptionKeyDown}
tabIndex={tabIndex}
disabled={disabled}
className={props.itemClassName}
/>
);
}
Expand Down Expand Up @@ -188,6 +190,8 @@ export const MultiSelectPanel = React.memo(
const panelClassName = classNames(
'p-multiselect-panel p-component',
{
'p-multiselect-inline': props.inline,
'p-multiselect-flex': props.flex,
'p-multiselect-limited': !allowOptionSelect
},
props.panelClassName
Expand All @@ -196,6 +200,15 @@ export const MultiSelectPanel = React.memo(
const content = createContent();
const footer = createFooter();

if (props.inline) {
return (
<div ref={ref} className={panelClassName} style={props.panelStyle} onClick={props.onClick}>
{content}
{footer}
</div>
);
}

return (
<CSSTransition
nodeRef={ref}
Expand All @@ -220,6 +233,8 @@ export const MultiSelectPanel = React.memo(

const element = createElement();

if (props.inline) return element;

return <Portal element={element} appendTo={props.appendTo} />;
})
);
Expand Down
3 changes: 3 additions & 0 deletions components/lib/multiselect/multiselect.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,11 @@ export interface MultiSelectProps extends Omit<React.DetailedHTMLProps<React.Inp
optionGroupChildren?: string;
optionGroupTemplate?: MultiSelectOptionGroupTemplateType;
display?: MultiSelectDisplayType;
inline?: boolean;
flex?: boolean;
style?: React.CSSProperties;
className?: string;
itemClassName?: string;
panelClassName?: string;
panelStyle?: React.CSSProperties;
virtualScrollerOptions?: VirtualScrollerProps;
Expand Down
6 changes: 6 additions & 0 deletions pages/multiselect/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ApiDoc } from '../../components/doc/multiselect/apidoc';
import { ImportDoc } from '../../components/doc/multiselect/importdoc';
import { BasicDoc } from '../../components/doc/multiselect/basicdoc';
import { ChipsDoc } from '../../components/doc/multiselect/chipsdoc';
import { InlineDoc } from '../../components/doc/multiselect/inlinedoc';
import { GroupedDoc } from '../../components/doc/multiselect/groupeddoc';
import { AdvancedDoc } from '../../components/doc/multiselect/advanceddoc';
import { VirtualDoc } from '../../components/doc/multiselect/virtualdoc';
Expand All @@ -23,6 +24,11 @@ const MultiSelectDemo = () => {
label: 'Basic',
component: BasicDoc
},
{
id: 'inline',
label: 'Inline, flex, itemClassName',
component: InlineDoc
},
{
id: 'chips',
label: 'Chips',
Expand Down