diff --git a/docs/demo/basic.tsx b/docs/demo/basic.tsx
index 649a853..4839882 100644
--- a/docs/demo/basic.tsx
+++ b/docs/demo/basic.tsx
@@ -1,7 +1,7 @@
-import '../../assets/style.less';
-import React from 'react';
import Segmented from 'rc-segmented';
-
+import React from 'react';
+import '../../assets/style.less';
+type Options = 'iOS' | 'Android' | 'Web';
export default function App() {
return (
@@ -21,7 +21,7 @@ export default function App() {
-
options={[
'iOS',
{ label: 'Android', value: 'Android', disabled: true },
diff --git a/src/MotionThumb.tsx b/src/MotionThumb.tsx
index a2e26cc..1a64822 100644
--- a/src/MotionThumb.tsx
+++ b/src/MotionThumb.tsx
@@ -3,7 +3,7 @@ import CSSMotion from 'rc-motion';
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
-import type { SegmentedValue } from '.';
+import type { SegmentedRawValue, SegmentedValue } from '.';
type ThumbReact = {
left: number;
@@ -11,10 +11,10 @@ type ThumbReact = {
width: number;
} | null;
-export interface MotionThumbInterface {
+export interface MotionThumbInterface {
containerRef: React.RefObject;
- value: SegmentedValue;
- getValueIndex: (value: SegmentedValue) => number;
+ value: SegmentedValue;
+ getValueIndex: (value: SegmentedValue) => number;
prefixCls: string;
motionName: string;
onMotionStart: VoidFunction;
@@ -39,7 +39,9 @@ const calcThumbStyle = (
const toPX = (value: number) =>
value !== undefined ? `${value}px` : undefined;
-export default function MotionThumb(props: MotionThumbInterface) {
+export default function MotionThumb(
+ props: MotionThumbInterface,
+) {
const {
prefixCls,
containerRef,
@@ -55,7 +57,7 @@ export default function MotionThumb(props: MotionThumbInterface) {
const [prevValue, setPrevValue] = React.useState(value);
// =========================== Effect ===========================
- const findValueElement = (val: SegmentedValue) => {
+ const findValueElement = (val: SegmentedValue) => {
const index = getValueIndex(val);
const ele = containerRef.current?.querySelectorAll(
diff --git a/src/index.tsx b/src/index.tsx
index 2541a6c..09eab84 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,41 +1,62 @@
-import * as React from 'react';
import classNames from 'classnames';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
-import { composeRef } from 'rc-util/lib/ref';
import omit from 'rc-util/lib/omit';
+import { composeRef } from 'rc-util/lib/ref';
+import * as React from 'react';
import MotionThumb from './MotionThumb';
-export type SegmentedValue = string | number;
+export type SegmentedRawValue = string | number;
-export type SegmentedRawOption = SegmentedValue;
+export type SegmentedValue = T;
-export interface SegmentedLabeledOption {
+export type SegmentedRawOption = SegmentedValue;
+
+export interface SegmentedLabeledOption {
className?: string;
disabled?: boolean;
label: React.ReactNode;
- value: SegmentedRawOption;
+ value: SegmentedRawOption;
/**
* html `title` property for label
*/
title?: string;
}
-type SegmentedOptions = (SegmentedRawOption | SegmentedLabeledOption)[];
+type SegmentedOptions = (
+ | SegmentedRawOption
+ | SegmentedLabeledOption
+)[];
-export interface SegmentedProps
+type InternalSegmentedOptionProps = {
+ prefixCls: string;
+ className?: string;
+ disabled?: boolean;
+ checked: boolean;
+ label: React.ReactNode;
+ title?: string;
+ value: SegmentedRawOption;
+ onChange: (
+ e: React.ChangeEvent,
+ value: SegmentedRawOption,
+ ) => void;
+};
+
+export interface SegmentedProps
extends Omit, 'onChange'> {
- options: SegmentedOptions;
- defaultValue?: SegmentedValue;
- value?: SegmentedValue;
- onChange?: (value: SegmentedValue) => void;
+ options: SegmentedOptions;
+ defaultValue?: SegmentedValue;
+ value?: SegmentedValue;
+ onChange?: (value: SegmentedValue) => void;
disabled?: boolean;
prefixCls?: string;
direction?: 'ltr' | 'rtl';
motionName?: string;
}
-function getValidTitle(option: SegmentedLabeledOption) {
+function getValidTitle(
+ option: SegmentedLabeledOption,
+) {
if (typeof option.title !== 'undefined') {
return option.title;
}
@@ -46,7 +67,9 @@ function getValidTitle(option: SegmentedLabeledOption) {
}
}
-function normalizeOptions(options: SegmentedOptions): SegmentedLabeledOption[] {
+function normalizeOptions(
+ options: SegmentedOptions,
+): SegmentedLabeledOption[] {
return options.map((option) => {
if (typeof option === 'object' && option !== null) {
const validTitle = getValidTitle(option);
@@ -65,19 +88,9 @@ function normalizeOptions(options: SegmentedOptions): SegmentedLabeledOption[] {
});
}
-const InternalSegmentedOption: React.FC<{
- prefixCls: string;
- className?: string;
- disabled?: boolean;
- checked: boolean;
- label: React.ReactNode;
- title?: string;
- value: SegmentedRawOption;
- onChange: (
- e: React.ChangeEvent,
- value: SegmentedRawOption,
- ) => void;
-}> = ({
+const InternalSegmentedOption: React.FC> = <
+ T extends SegmentedRawValue,
+>({
prefixCls,
className,
disabled,
@@ -86,7 +99,7 @@ const InternalSegmentedOption: React.FC<{
title,
value,
onChange,
-}) => {
+}: InternalSegmentedOptionProps) => {
const handleChange = (event: React.ChangeEvent) => {
if (disabled) {
return;
@@ -115,8 +128,11 @@ const InternalSegmentedOption: React.FC<{
);
};
-const Segmented = React.forwardRef(
- (props, ref) => {
+const Segmented = React.forwardRef(
+ (
+ props: React.PropsWithChildren>,
+ ref: React.Ref,
+ ) => {
const {
prefixCls = 'rc-segmented',
direction,
@@ -152,7 +168,7 @@ const Segmented = React.forwardRef(
const handleChange = (
event: React.ChangeEvent,
- val: SegmentedRawOption,
+ val: SegmentedRawOption,
) => {
if (disabled) {
return;
@@ -225,4 +241,10 @@ Segmented.defaultProps = {
options: [],
};
-export default Segmented;
+const TypeSegmented = Segmented as unknown as (
+ props: React.PropsWithChildren> & {
+ ref?: React.Ref;
+ },
+) => React.ReactElement;
+
+export default TypeSegmented;
diff --git a/tests/index.test.tsx b/tests/index.test.tsx
index 59fb1b1..94d9c93 100644
--- a/tests/index.test.tsx
+++ b/tests/index.test.tsx
@@ -2,6 +2,7 @@ import { act, fireEvent, render } from '@testing-library/react';
import * as React from 'react';
import Segmented from '../src';
+type Options = 'iOS' | 'Android' | 'Web';
jest.mock('rc-motion/lib/util/motion', () => {
return {
...jest.requireActual('rc-motion/lib/util/motion'),
@@ -47,7 +48,7 @@ describe('rc-segmented', () => {
it('render segmented ok', () => {
const { container, asFragment } = render(
-
options={[{ label: 'iOS', value: 'iOS' }, 'Android', 'Web']}
/>,
);
@@ -124,7 +125,7 @@ describe('rc-segmented', () => {
it('render segmented with options: 2', () => {
const handleValueChange = jest.fn();
const { container, asFragment } = render(
-
options={['iOS', { label: 'Android', value: 'Android' }, 'Web']}
onChange={(value) => handleValueChange(value)}
/>,
@@ -140,7 +141,7 @@ describe('rc-segmented', () => {
it('render segmented with options: disabled', () => {
const handleValueChange = jest.fn();
const { container, asFragment } = render(
-
options={[
'iOS',
{ label: 'Android', value: 'Android', disabled: true },
@@ -474,7 +475,7 @@ describe('rc-segmented', () => {
it('render segmented with title', () => {
const { asFragment, container } = render(
-
options={[
'Web',
{
@@ -511,7 +512,7 @@ describe('rc-segmented', () => {
it('render with rtl', () => {
const { container } = render(
-
direction="rtl"
options={[{ label: 'iOS', value: 'iOS' }, 'Android', 'Web']}
/>,
diff --git a/tsconfig.json b/tsconfig.json
index 850f783..d652d17 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -19,5 +19,5 @@
"rc-segmented": ["src/index.tsx"]
}
},
- "include": [".dumi/**/*", ".dumirc.ts", "src", "tests", "docs/examples"],
+ "include": [".dumi/**/*", ".dumirc.ts", "src", "tests", "docs/**/*"]
}