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

feat(radio): v14 #2786

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 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
8 changes: 0 additions & 8 deletions src/packages/radio/demo.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Demo3 from './demos/taro/demo3'
import Demo4 from './demos/taro/demo4'
import Demo5 from './demos/taro/demo5'
import Demo6 from './demos/taro/demo6'
import Demo7 from './demos/taro/demo7'
import Demo8 from './demos/taro/demo8'
import Demo9 from './demos/taro/demo9'
import Demo10 from './demos/taro/demo10'
Expand All @@ -23,7 +22,6 @@ const RadioDemo = () => {
'74fc5d8a': '基础用法',
bb7486f4: '选项',
c1bae1ec: '水平使用',
'8a2e2847': '自定义尺寸',
'70ffa5d8': '自定义图标',
'70ffa5d9': '自定义图标,通过Group实现列表形式',
'0f261484': '触发事件',
Expand All @@ -37,7 +35,6 @@ const RadioDemo = () => {
'74fc5d8a': '基礎用法',
bb7486f4: '選項',
c1bae1ec: '水準使用',
'8a2e2847': '自定義尺寸',
'70ffa5d8': '自定義圖標',
'70ffa5d9': '自定義圖標,通過Group實現列表形式',
'0f261484': '觸發事件',
Expand All @@ -51,7 +48,6 @@ const RadioDemo = () => {
'74fc5d8a': 'Basic Usage',
bb7486f4: 'Options',
c1bae1ec: 'Horizontal use',
'8a2e2847': 'Custom size',
'70ffa5d8': 'Custom Icon',
'70ffa5d9': 'Custom Icon, render list in Group mode',
'0f261484': 'Trigger Event',
Expand Down Expand Up @@ -87,10 +83,6 @@ const RadioDemo = () => {
</Cell>
<View className="h2">{translated.c1bae1ec}</View>
<Demo6 />
<View className="h2">{translated['8a2e2847']}</View>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个不支持了吗

<Cell>
<Demo7 />
</Cell>
<View className="h2">{translated['70ffa5d8']}</View>
<Cell>
<Demo8 />
Expand Down
8 changes: 0 additions & 8 deletions src/packages/radio/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import Demo3 from './demos/h5/demo3'
import Demo4 from './demos/h5/demo4'
import Demo5 from './demos/h5/demo5'
import Demo6 from './demos/h5/demo6'
import Demo7 from './demos/h5/demo7'
import Demo8 from './demos/h5/demo8'
import Demo9 from './demos/h5/demo9'
import Demo10 from './demos/h5/demo10'
Expand All @@ -20,7 +19,6 @@ const RadioDemo = () => {
'74fc5d8a': '基础用法',
bb7486f4: '选项',
c1bae1ec: '水平使用',
'8a2e2847': '自定义尺寸',
'70ffa5d8': '自定义图标',
'70ffa5d9': '自定义图标,通过Group实现列表形式',
'0f261484': '触发事件',
Expand All @@ -34,7 +32,6 @@ const RadioDemo = () => {
'74fc5d8a': '基礎用法',
bb7486f4: '選項',
c1bae1ec: '水準使用',
'8a2e2847': '自定義尺寸',
'70ffa5d8': '自定義圖標',
'70ffa5d9': '自定義圖標,通過Group實現列表形式',
'0f261484': '觸發事件',
Expand All @@ -48,7 +45,6 @@ const RadioDemo = () => {
'74fc5d8a': 'Basic Usage',
bb7486f4: 'Options',
c1bae1ec: 'Horizontal use',
'8a2e2847': 'Custom size',
'70ffa5d8': 'Custom Icon',
'70ffa5d9': 'Custom Icon, render list in Group mode',
'0f261484': 'Trigger Event',
Expand Down Expand Up @@ -83,10 +79,6 @@ const RadioDemo = () => {
</Cell>
<h2>{translated.c1bae1ec}</h2>
<Demo6 />
<h2>{translated['8a2e2847']}</h2>
<Cell>
<Demo7 />
</Cell>
<h2>{translated['70ffa5d8']}</h2>
<Cell>
<Demo8 />
Expand Down
16 changes: 0 additions & 16 deletions src/packages/radio/demos/h5/demo7.tsx

This file was deleted.

16 changes: 0 additions & 16 deletions src/packages/radio/demos/taro/demo7.tsx

This file was deleted.

24 changes: 24 additions & 0 deletions src/packages/radio/icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { createElement } from 'react'
import { mergeProps } from '@/utils/merge-props'

interface IconProps {
tag: any
classPrefix: string
name: 'normal' | 'disabled' | 'checked' | 'checked-disabled'
}
Comment on lines +4 to +8
Copy link

@coderabbitai coderabbitai bot Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议改进 IconProps 接口中 tag 属性的类型定义

目前 tag 属性使用 any 类型会降低类型安全性。建议将其限制为具体的 HTML 元素类型或 React 组件类型。

建议修改如下:

interface IconProps {
-  tag: any
+  tag: keyof JSX.IntrinsicElements | React.ComponentType
  classPrefix: string
  name: 'normal' | 'disabled' | 'checked' | 'checked-disabled'
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface IconProps {
tag: any
classPrefix: string
name: 'normal' | 'disabled' | 'checked' | 'checked-disabled'
}
interface IconProps {
tag: keyof JSX.IntrinsicElements | React.ComponentType
classPrefix: string
name: 'normal' | 'disabled' | 'checked' | 'checked-disabled'
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

看这个类型可不可以使用

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@irisSong,可以使用这个类型定义。将 tag 的类型改为 keyof JSX.IntrinsicElements | React.ComponentType 可以提高类型安全性,确保 tag 只能是特定的 HTML 元素类型或 React 组件类型。修改后的代码如下:

interface IconProps {
  tag: keyof JSX.IntrinsicElements | React.ComponentType
  classPrefix: string
  name: 'normal' | 'disabled' | 'checked' | 'checked-disabled'
}

这样修改后,代码会更加严谨,有助于捕获类型错误。

Comment on lines +4 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议改进 IconProps 接口中 tag 属性的类型定义

目前 tag 属性使用 any 类型会降低类型安全性。建议使用更具体的类型定义。

建议修改如下:

interface IconProps {
-  tag: any
+  tag: keyof JSX.IntrinsicElements | React.ComponentType
  classPrefix: string
  name: 'normal' | 'disabled' | 'checked' | 'checked-disabled'
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface IconProps {
tag: any
classPrefix: string
name: 'normal' | 'disabled' | 'checked' | 'checked-disabled'
}
interface IconProps {
tag: keyof JSX.IntrinsicElements | React.ComponentType
classPrefix: string
name: 'normal' | 'disabled' | 'checked' | 'checked-disabled'
}


const Icon = (props: Partial<IconProps>) => {
const { tag, classPrefix, name } = mergeProps(
{
tag: 'div',
classPrefix: 'nut-radio-self',
name: 'normal',
},
props
)
return createElement<{ className: string }>(tag, {
className: `${classPrefix} ${classPrefix}-${name}`,
})
}

export default Icon
61 changes: 61 additions & 0 deletions src/packages/radio/radio.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,66 @@
@import '../radiogroup/radiogroup.scss';

.nut-radio-self {
width: 16px;
height: 16px;
position: relative;
box-sizing: border-box;
border-radius: 50%;

&-normal {
border: 0.5px solid $color-text-disabled;
background: $color-background-overlay;
}

&-checked,
&-disabled,
&-checked-disabled {
&:after {
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
content: ' ';
background: $color-primary-text;
}
}

&-checked {
background: linear-gradient(
90deg,
$color-primary-stop-1 0%,
$color-primary-stop-2 100%
);
box-shadow: 0px 2px 4px 0px rgba(255, 15, 35, 0.2);

&:after {
width: 8px;
height: 8px;
border-radius: 50%;
}
}

&-disabled {
background: $color-text-disabled;

&:after {
width: 8px;
height: 1.5px;
}
}

&-checked-disabled {
background: $color-primary-disabled-special;

&:after {
width: 8px;
height: 8px;
border-radius: 50%;
}
}
}

.nut-radio {
display: flex;
align-items: center;
Expand Down
35 changes: 19 additions & 16 deletions src/packages/radio/radio.taro.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import React, { FC, useContext } from 'react'
import {
CheckChecked,
CheckDisabled,
CheckNormal,
} from '@nutui/icons-react-taro'
import classNames, { Mapping } from 'classnames'
import { ITouchEvent, View } from '@tarojs/components'
import RadioContext from '../radiogroup/context'
import { BasicComponent, ComponentDefaults } from '@/utils/typings'
import { usePropsValue } from '@/utils/use-props-value'
import { RadioPosition, RadioShape } from '@/packages/radio/types'
import Icon from '@/packages/radio/icon'

export interface RadioProps extends BasicComponent {
disabled: boolean
Expand Down Expand Up @@ -88,26 +84,33 @@ export const Radio: FC<
}
const renderIcon = () => {
const { icon, activeIcon } = props

function renderIconByDisabledProperty() {
return disabled ? (
<Icon name="checked-disabled" tag={View} />
) : (
<Icon name="checked" tag={View} />
)
}

if (disabled && !checkedStatement) {
return <CheckDisabled className={classNames(color())} />
return <Icon name="disabled" tag={View} />
}
if (checkedStatement) {
return React.isValidElement(activeIcon) ? (
React.cloneElement<any>(activeIcon, {
...activeIcon.props,
className: classNames(color()),
})
) : (
<CheckChecked className={classNames(color())} />
)
return React.isValidElement(activeIcon)
? React.cloneElement<any>(activeIcon, {
...activeIcon.props,
className: classNames(color()),
})
: renderIconByDisabledProperty()
}
return React.isValidElement(icon) ? (
React.cloneElement<any>(icon, {
...icon.props,
className: classNames(color()),
})
) : (
<CheckNormal className={classNames(color())} />
<Icon name="normal" tag={View} />
Comment on lines +95 to +113
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

优化图标渲染逻辑的条件判断

当前的条件判断结构较为复杂,建议重构以提高可读性和可维护性。

建议采用以下方式重构:

- if (disabled && !checkedStatement) {
-   return <Icon name="disabled" tag={View} />
- }
- if (checkedStatement) {
-   return React.isValidElement(activeIcon)
-     ? React.cloneElement<any>(activeIcon, {
-         ...activeIcon.props,
-         className: classNames(color()),
-       })
-     : renderIconByDisabledProperty()
- }
- return React.isValidElement(icon) ? (
-   React.cloneElement<any>(icon, {
-     ...icon.props,
-     className: classNames(color()),
-   })
- ) : (
-   <Icon name="normal" tag={View} />
- )
+ const getIcon = () => {
+   if (disabled && !checkedStatement) return 'disabled'
+   if (checkedStatement) return disabled ? 'checked-disabled' : 'checked'
+   return 'normal'
+ }
+ 
+ const customIcon = checkedStatement ? activeIcon : icon
+ if (React.isValidElement(customIcon)) {
+   return React.cloneElement<any>(customIcon, {
+     ...customIcon.props,
+     className: classNames(color()),
+   })
+ }
+ 
+ return <Icon name={getIcon()} tag={View} />

Committable suggestion skipped: line range outside the PR's diff.

)
}
const renderLabel = () => {
Expand All @@ -117,7 +120,7 @@ export const Radio: FC<
return (
<>
{renderIcon()}
<View className={labelcls}>{children}</View>
{children ? <View className={labelcls}>{children}</View> : null}
</>
)
}
Expand Down
31 changes: 19 additions & 12 deletions src/packages/radio/radio.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { FunctionComponent, MouseEventHandler, useContext } from 'react'
import { CheckChecked, CheckNormal, CheckDisabled } from '@nutui/icons-react'
import classNames from 'classnames'
import RadioContext from '../radiogroup/context'
import { BasicComponent, ComponentDefaults } from '@/utils/typings'
import { usePropsValue } from '@/utils/use-props-value'
import { RadioPosition, RadioShape } from '@/packages/radio/types'
import Icon from '@/packages/radio/icon'

export interface RadioProps extends BasicComponent {
disabled: boolean
Expand Down Expand Up @@ -83,26 +83,33 @@ export const Radio: FunctionComponent<
}
const renderIcon = () => {
const { icon, activeIcon } = props

function renderIconByDisabledProperty() {
return disabled ? (
<Icon name="checked-disabled" />
) : (
<Icon name="checked" />
)
}

if (disabled && !checkedStatement) {
return <CheckDisabled className={classNames(color())} />
return <Icon name="disabled" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议统一图标渲染的条件判断结构

当前的条件判断结构较为分散,建议统一处理逻辑以提高可读性。

建议重构为:

-if (disabled && !checkedStatement) {
-  return <Icon name="disabled" />
-}
-if (checkedStatement) {
-  return React.isValidElement(activeIcon)
-    ? React.cloneElement<any>(activeIcon, {
-        ...activeIcon.props,
-        className: classNames(color()),
-      })
-    : renderIconByDisabledProperty()
-}
-return React.isValidElement(icon) ? (
-  React.cloneElement<any>(icon, {
-    ...icon.props,
-    className: classNames(color()),
-  })
-) : (
-  <Icon name="normal" />
-)
+const renderIcon = () => {
+  if (disabled && !checkedStatement) {
+    return <Icon name="disabled" />
+  }
+  
+  const currentIcon = checkedStatement ? activeIcon : icon
+  if (React.isValidElement(currentIcon)) {
+    return React.cloneElement<any>(currentIcon, {
+      ...currentIcon.props,
+      className: classNames(color()),
+    })
+  }
+  
+  if (checkedStatement) {
+    return renderCheckedIcon()
+  }
+  
+  return <Icon name="normal" />
+}

Also applies to: 99-104, 112-112

}
if (checkedStatement) {
return React.isValidElement(activeIcon) ? (
React.cloneElement<any>(activeIcon, {
...activeIcon.props,
className: classNames(color()),
})
) : (
<CheckChecked className={classNames(color())} />
)
return React.isValidElement(activeIcon)
? React.cloneElement<any>(activeIcon, {
...activeIcon.props,
className: classNames(color()),
})
: renderIconByDisabledProperty()
}
return React.isValidElement(icon) ? (
React.cloneElement<any>(icon, {
...icon.props,
className: classNames(color()),
})
) : (
<CheckNormal className={classNames(color())} />
<Icon name="normal" />
Comment on lines +94 to +112
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议简化图标渲染逻辑

当前的图标渲染逻辑包含多层嵌套条件,可以通过提取通用逻辑来简化代码。

建议重构如下:

-if (disabled && !checkedStatement) {
-  return <Icon name="disabled" />
-}
-if (checkedStatement) {
-  return React.isValidElement(activeIcon)
-    ? React.cloneElement<any>(activeIcon, {
-        ...activeIcon.props,
-        className: classNames(color()),
-      })
-    : renderIconByDisabledProperty()
-}
-return React.isValidElement(icon) ? (
-  React.cloneElement<any>(icon, {
-    ...icon.props,
-    className: classNames(color()),
-  })
-) : (
-  <Icon name="normal" />
-)
+const iconName = getIconName(disabled, checkedStatement)
+const customIcon = checkedStatement ? activeIcon : icon
+
+return React.isValidElement(customIcon)
+  ? React.cloneElement<any>(customIcon, {
+      ...customIcon.props,
+      className: classNames(color()),
+    })
+  : <Icon name={iconName} />

需要在组件外部添加辅助函数:

function getIconName(disabled: boolean, checked: boolean): string {
  if (disabled && !checked) return 'disabled'
  if (checked) return disabled ? 'checked-disabled' : 'checked'
  return 'normal'
}

)
}
const renderLabel = () => {
Expand All @@ -112,7 +119,7 @@ export const Radio: FunctionComponent<
return (
<>
{renderIcon()}
<div className={labelcls}>{children}</div>
{children ? <div className={labelcls}>{children}</div> : null}
</>
)
}
Expand Down
Loading