Skip to content

Commit

Permalink
feat: Add getRawInputElement (react-component#639)
Browse files Browse the repository at this point in the history
* feat: Add raw customize

* feat: Support dropdown

* test: Add test case
  • Loading branch information
zombieJ authored Jun 22, 2021
1 parent caf04cb commit 11f81f3
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 50 deletions.
17 changes: 17 additions & 0 deletions examples/custom-selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* eslint-disable no-console */
import React from 'react';
import Select from '../src';
import '../assets/index.less';

export default () => {
return (
<Select
getRawInputElement={() => <span>Content</span>}
mode="multiple"
options={[{ value: 'light' }, { value: 'bamboo' }]}
allowClear
placeholder="2333"
/>
);
};
/* eslint-enable */
7 changes: 5 additions & 2 deletions src/SelectTrigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export interface SelectTriggerProps {
empty: boolean;

getTriggerDOMNode: () => HTMLElement;
onPopupVisibleChange?: (visible: boolean) => void;
}

const SelectTrigger: React.RefForwardingComponent<RefTriggerProps, SelectTriggerProps> = (
Expand All @@ -91,6 +92,7 @@ const SelectTrigger: React.RefForwardingComponent<RefTriggerProps, SelectTrigger
getPopupContainer,
empty,
getTriggerDOMNode,
onPopupVisibleChange,
...restProps
} = props;

Expand Down Expand Up @@ -129,8 +131,8 @@ const SelectTrigger: React.RefForwardingComponent<RefTriggerProps, SelectTrigger
return (
<Trigger
{...restProps}
showAction={[]}
hideAction={[]}
showAction={onPopupVisibleChange ? ['click'] : []}
hideAction={onPopupVisibleChange ? ['click'] : []}
popupPlacement={direction === 'rtl' ? 'bottomRight' : 'bottomLeft'}
builtinPlacements={builtInPlacements}
prefixCls={dropdownPrefixCls}
Expand All @@ -144,6 +146,7 @@ const SelectTrigger: React.RefForwardingComponent<RefTriggerProps, SelectTrigger
})}
popupStyle={popupStyle}
getTriggerDOMNode={getTriggerDOMNode}
onPopupVisibleChange={onPopupVisibleChange}
>
{children}
</Trigger>
Expand Down
124 changes: 78 additions & 46 deletions src/generate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as React from 'react';
import { useState, useRef, useEffect, useMemo } from 'react';
import KeyCode from 'rc-util/lib/KeyCode';
import isMobile from 'rc-util/lib/isMobile';
import { composeRef } from 'rc-util/lib/ref';
import classNames from 'classnames';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { ScrollTo } from 'rc-virtual-list/lib/List';
Expand Down Expand Up @@ -133,6 +134,8 @@ export interface SelectProps<OptionsType extends object[], ValueType> extends Re
backfill?: boolean;
/** @private Internal usage. Do not use in your production. */
getInputElement?: () => JSX.Element;
/** @private Internal usage. Do not use in your production. */
getRawInputElement?: () => JSX.Element;
optionLabelProp?: string;
maxTagTextLength?: number;
maxTagCount?: number | 'responsive';
Expand Down Expand Up @@ -289,6 +292,7 @@ export default function generateSelector<
backfill,
tabIndex,
getInputElement,
getRawInputElement,
getPopupContainer,

// Dropdown
Expand Down Expand Up @@ -635,9 +639,13 @@ export default function generateSelector<
};

// ============================= Input ==============================
// Only works in `combobox` or `rc-cascader`
// Only works in `combobox`
const customizeInputElement: React.ReactElement =
(typeof getInputElement === 'function' && getInputElement()) || null;
(mode === 'combobox' && typeof getInputElement === 'function' && getInputElement()) || null;

// Used for customize replacement for `rc-cascader`
const customizeRawInputElement: React.ReactElement =
typeof getRawInputElement === 'function' && getRawInputElement();

// ============================== Open ==============================
const [innerOpen, setInnerOpen] = useMergedState<boolean>(undefined, {
Expand Down Expand Up @@ -666,6 +674,14 @@ export default function generateSelector<
}
};

// Used for raw custom input trigger
let onTriggerVisibleChange: null | ((newOpen: boolean) => void);
if (customizeRawInputElement) {
onTriggerVisibleChange = (newOpen: boolean) => {
onToggleOpen(newOpen);
};
}

useSelectTriggerControl(
[containerRef.current, triggerRef.current && triggerRef.current.getPopupElement()],
triggerOpen,
Expand Down Expand Up @@ -931,8 +947,8 @@ export default function generateSelector<

useLayoutEffect(() => {
if (triggerOpen) {
const newWidth = Math.ceil(containerRef.current.offsetWidth);
if (containerWidth !== newWidth) {
const newWidth = Math.ceil(containerRef.current?.offsetWidth);
if (containerWidth !== newWidth && !Number.isNaN(newWidth)) {
setContainerWidth(newWidth);
}
}
Expand Down Expand Up @@ -1034,6 +1050,63 @@ export default function generateSelector<
[`${prefixCls}-show-search`]: mergedShowSearch,
});

const selectorNode = (
<SelectTrigger
ref={triggerRef}
disabled={disabled}
prefixCls={prefixCls}
visible={triggerOpen}
popupElement={popupNode}
containerWidth={containerWidth}
animation={animation}
transitionName={transitionName}
dropdownStyle={dropdownStyle}
dropdownClassName={dropdownClassName}
direction={direction}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
dropdownRender={dropdownRender}
dropdownAlign={dropdownAlign}
getPopupContainer={getPopupContainer}
empty={!mergedOptions.length}
getTriggerDOMNode={() => selectorDomRef.current}
onPopupVisibleChange={onTriggerVisibleChange}
>
{customizeRawInputElement ? (
React.cloneElement(customizeRawInputElement, {
ref: composeRef(selectorDomRef, customizeRawInputElement.props.ref),
})
) : (
<Selector
{...props}
domRef={selectorDomRef}
prefixCls={prefixCls}
inputElement={customizeInputElement}
ref={selectorRef}
id={mergedId}
showSearch={mergedShowSearch}
mode={mode}
accessibilityIndex={accessibilityIndex}
multiple={isMultiple}
tagRender={tagRender}
values={displayValues}
open={mergedOpen}
onToggleOpen={onToggleOpen}
searchValue={mergedSearchValue}
activeValue={activeValue}
onSearch={triggerSearch}
onSearchSubmit={onSearchSubmit}
onSelect={onInternalSelectionSelect}
tokenWithEnter={tokenWithEnter}
/>
)}
</SelectTrigger>
);

// Render raw
if (customizeRawInputElement) {
return selectorNode;
}

return (
<div
className={mergedClassName}
Expand All @@ -1060,48 +1133,7 @@ export default function generateSelector<
{`${mergedRawValue.join(', ')}`}
</span>
)}
<SelectTrigger
ref={triggerRef}
disabled={disabled}
prefixCls={prefixCls}
visible={triggerOpen}
popupElement={popupNode}
containerWidth={containerWidth}
animation={animation}
transitionName={transitionName}
dropdownStyle={dropdownStyle}
dropdownClassName={dropdownClassName}
direction={direction}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
dropdownRender={dropdownRender}
dropdownAlign={dropdownAlign}
getPopupContainer={getPopupContainer}
empty={!mergedOptions.length}
getTriggerDOMNode={() => selectorDomRef.current}
>
<Selector
{...props}
domRef={selectorDomRef}
prefixCls={prefixCls}
inputElement={customizeInputElement}
ref={selectorRef}
id={mergedId}
showSearch={mergedShowSearch}
mode={mode}
accessibilityIndex={accessibilityIndex}
multiple={isMultiple}
tagRender={tagRender}
values={displayValues}
open={mergedOpen}
onToggleOpen={onToggleOpen}
searchValue={mergedSearchValue}
activeValue={activeValue}
onSearch={triggerSearch}
onSearchSubmit={onSearchSubmit}
onSelect={onInternalSelectionSelect}
tokenWithEnter={tokenWithEnter}
/>
</SelectTrigger>
{selectorNode}

{arrowNode}
{clearNode}
Expand Down
18 changes: 18 additions & 0 deletions tests/Select.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,24 @@ describe('Select.Basic', () => {
expect(onCompositionEnd).toHaveBeenCalled();
});

it('getRawInputElement for rc-cascader', () => {
const wrapper = mount(
<Select
getRawInputElement={() => <span className="bamboo" />}
options={[
{
label: <span className="little" />,
value: 'little',
},
]}
open
/>,
);

expect(wrapper.exists('.bamboo')).toBeTruthy();
expect(wrapper.exists('.little')).toBeTruthy();
});

describe('propTypes', () => {
let errorSpy;

Expand Down
2 changes: 0 additions & 2 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ declare module 'rc-menu';
declare module 'rc-util/lib/Children/toArray';

declare module 'dom-scroll-into-view';

declare module 'rc-trigger';

0 comments on commit 11f81f3

Please sign in to comment.