# @ray-js/loop-picker

简体中文 | [English](./README.md)

> 适用于小程序的循环选择器组件，支持单列/多列选择、循环滚动、自定义样式和时间相关类型。

## NPM 包
[![latest](https://img.shields.io/npm/v/@ray-js/loop-picker/latest.svg)](https://www.npmjs.com/package/@ray-js/loop-picker) [![download](https://img.shields.io/npm/dt/@ray-js/loop-picker.svg)](https://www.npmjs.com/package/@ray-js/loop-picker)

**NPM 包地址：** [@ray-js/loop-picker](https://www.npmjs.com/package/@ray-js/loop-picker)

## 安装

### npm 安装

```bash
npm install @ray-js/loop-picker
// 或者
$ yarn add @ray-js/loop-picker
```

## 快速开始

### 基础用法

```tsx
import React, { useState } from 'react';
import LoopPicker from '@ray-js/loop-picker';

function BasicExample() {
  const [columns] = useState(['选项1', '选项2', '选项3']);

  const onChange = (e) => {
    const { value, index } = e.detail;
    console.log('选择变化:', value, index);
  };

  return (
    <LoopPicker 
      columns={columns} 
      onChange={onChange} 
    />
  );
}
```

### 循环滚动

启用循环滚动后，选择器可以无限循环滚动，适合需要无限滚动的场景：

```tsx
import React, { useState } from 'react';
import LoopPicker from '@ray-js/loop-picker';

function LoopExample() {
  const [loopColumns] = useState(Array.from({ length: 60 }, (_, i) => i));

  return (
    <LoopPicker 
      loop
      columns={loopColumns}
      title="循环选择器"
      showToolbar
    />
  );
}
```

### 时间类型

组件支持内置的时间相关类型：

#### 日期选择器

```tsx
import React from 'react';
import LoopPicker from '@ray-js/loop-picker';

function DateExample() {
  const onDateChange = (e) => {
    console.log('日期变化:', e.detail.value);
  };

  return (
    <LoopPicker 
      type="date" 
      title="选择日期" 
      onChange={onDateChange} 
    />
  );
}
```

#### 时间选择器（24小时制）

```tsx
import React from 'react';
import LoopPicker from '@ray-js/loop-picker';

function TimeExample() {
  const onTimeChange = (e) => {
    console.log('时间变化:', e.detail.value);
  };

  return (
    <LoopPicker 
      type="time" 
      title="选择时间" 
      onChange={onTimeChange} 
    />
  );
}
```

#### 日期时间选择器

```tsx
import React from 'react';
import LoopPicker from '@ray-js/loop-picker';

function DateTimeExample() {
  const onDatetimeChange = (e) => {
    console.log('日期时间变化:', e.detail.value);
  };

  return (
    <LoopPicker 
      type="datetime" 
      title="选择日期时间" 
      onChange={onDatetimeChange} 
    />
  );
}
```

### 循环滚动 + 时间选择

```tsx
import React from 'react';
import LoopPicker from '@ray-js/loop-picker';

function LoopTimeExample() {

  return (
    <LoopPicker 
      loop
      type="time" 
      title="循环时间选择"
      showToolbar
    />
  );
}
```

### 禁用状态和加载状态

```tsx
import React, { useState } from 'react';
import LoopPicker from '@ray-js/loop-picker';

function DisabledAndLoadingExample() {
  const [columns] = useState(['选项1', '选项2', '选项3']);

  return (
    <>
      {/* 禁用状态 */}
      <LoopPicker 
        disabled
        columns={columns}
        title="禁用状态"
      />

      {/* 加载状态 */}
      <LoopPicker 
        loading
        columns={columns}
        title="加载中..."
      />
    </>
  );
}
```

### 动态修改数据

```tsx
import React, { useState, useRef } from 'react';
import LoopPicker from '@ray-js/loop-picker';

function DynamicExample() {
  const [dynamicColumns, setDynamicColumns] = useState(['A', 'B', 'C']);
  const [currentSet, setCurrentSet] = useState(0);
  const pickerRef = useRef(null);

  const sets = [
    ['A', 'B', 'C'],
    ['选项1', '选项2', '选项3', '选项4'],
    ['苹果', '香蕉', '橙子', '葡萄', '草莓']
  ];

  const changeColumns = () => {
    const nextSet = (currentSet + 1) % sets.length;
    setCurrentSet(nextSet);
    setDynamicColumns(sets[nextSet]);
  };

  const setPickerValue = () => {
    if (pickerRef.current) {
      // 设置选中值
      pickerRef.current.setValues(['B']);
      // 或者设置选中索引
      // pickerRef.current.setIndexes([1]);
    }
  };

  const onDynamicChange = (e) => {
    console.log('动态选择变化:', e.detail.value);
  };

  return (
    <>
      <LoopPicker 
        ref={pickerRef}
        columns={dynamicColumns}
        title="动态数据"
        showToolbar
        onChange={onDynamicChange}
      />
      
      <button onClick={changeColumns}>切换数据</button>
      <button onClick={setPickerValue}>设置值</button>
    </>
  );
}
```

### 多列选择

```tsx
import React, { useState } from 'react';
import LoopPicker from '@ray-js/loop-picker';

function MultiColumnExample() {
  const [dateColumns] = useState([
    {
      values: ['2020', '2021', '2022', '2023', '2024'],
      defaultIndex: 3,
      unit: '',
    },
    {
      values: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
      defaultIndex: 0,
      unit: '',
    },
    {
      values: Array.from({ length: 31 }, (_, i) => String(i + 1).padStart(2, '0')),
      defaultIndex: 0,
      unit: '',
    },
  ]);

  return (
    <LoopPicker 
      columns={dateColumns} 
      title="选择日期" 
    />
  );
}
```

### 高级多列选择

支持更复杂的多列配置，包括单位、默认索引、禁用状态等：

```tsx
import React, { useState } from 'react';
import LoopPicker from '@ray-js/loop-picker';

function AdvancedMultiColumnExample() {
  const [advancedColumns] = useState([
    {
      values: ['北京', '上海', '广州', '深圳'],
      defaultIndex: 0,
      unit: '',
      style: 'color: #1890ff;'
    },
    {
      values: ['红色', '绿色', '蓝色', '黄色'],
      defaultIndex: 1,
      unit: '',
      disabled: false
    },
    {
      values: ['S', 'M', 'L', 'XL'],
      defaultIndex: 2,
      unit: '码'
    }
  ]);

  const onAdvancedChange = (e) => {
    console.log('高级多列选择:', e.detail.value);
  };

  return (
    <LoopPicker 
      columns={advancedColumns}
      title="高级多列选择"
      showToolbar
      onChange={onAdvancedChange}
    />
  );
}
```

## 数据格式

### 单列数据

#### 简单字符串数组

```javascript
const columns = ['苹果', '香蕉', '橙子'];
```

#### 对象数组（推荐）

```javascript
const columns = [
  { text: '苹果', value: 'apple' },
  { text: '香蕉', value: 'banana' },
  { text: '橙子', value: 'orange', disabled: true }
];
```

### 多列数据

#### 标准多列配置

```javascript
const columns = [
  {
    values: ['2023', '2024', '2025'],  // 第一列：年份
    defaultIndex: 0,
    unit: ''
  },
  {
    values: ['01', '02', '03', '04'],  // 第二列：月份
    defaultIndex: 0,
    unit: ''
  },
  {
    values: ['01', '02', '03', '04'],  // 第三列：日期
    defaultIndex: 0,
    unit: ''
  }
];
```

#### 高级多列配置

```javascript
const columns = [
  {
    values: [
      { text: '2023年', value: 2023 },
      { text: '2024年', value: 2024 },
      { text: '2025年', value: 2025 }
    ],
    defaultIndex: 1,
    unit: '',
    order: 0,
    disabled: false,
    style: 'color: #333;',
    fontStyle: 'font-weight: bold;'
  },
  {
    values: ['01月', '02月', '03月', '04月', '05月', '06月'],
    defaultIndex: 0,
    unit: '',
    order: 1
  },
  {
    values: Array.from({ length: 31 }, (_, i) => ({
      text: `${i + 1}日`,
      value: i + 1,
      disabled: i > 30  // 禁用某些选项
    })),
    defaultIndex: 0,
    unit: '',
    order: 2
  }
];
```

### 列配置对象属性

| 属性           | 类型      | 默认值  | 说明                           |
| -------------- | --------- | ------- | ------------------------------ |
| `values`       | `Array`   | `[]`    | 列的选项数据                   |
| `defaultIndex` | `Number`  | `0`     | 默认选中的索引                 |
| `unit`         | `String`  | `''`    | 单位文本                       |
| `order`        | `Number`  | `0`     | 列的排序（类似 flex order）    |
| `disabled`     | `Boolean` | `false` | 是否禁用该列                   |
| `style`        | `String`  | `''`    | 列容器的自定义样式             |
| `fontStyle`    | `String`  | `''`    | 列文字的自定义样式             |

### 选项对象属性

| 属性       | 类型      | 默认值  | 说明               |
| ---------- | --------- | ------- | ------------------ |
| `text`     | `String`  | `''`    | 显示的文本         |
| `value`    | `Any`     | -       | 选项的值           |
| `disabled` | `Boolean` | `false` | 是否禁用该选项     |

## 样式定制

### CSS 变量

组件支持通过 CSS 变量进行样式定制：

```css
.loop-picker {
  /* 基础变量 */
  --loop-picker-background: #fff;
  --loop-picker-height: 220px;
  
  /* 工具栏 */
  --loop-picker-toolbar-height: 44px;
  --loop-picker-toolbar-background: #f7f8fa;
  --loop-picker-toolbar-border-color: #ebedf0;
  
  /* 按钮 */
  --loop-picker-button-padding: 0 16px;
  --loop-picker-button-font-size: 16px;
  --loop-picker-cancel-color: #646566;
  --loop-picker-confirm-color: #1890ff;
  
  /* 标题 */
  --loop-picker-title-font-size: 16px;
  --loop-picker-title-color: #323233;
  
  /* 选项 */
  --loop-picker-option-height: 44px;
  --loop-picker-option-font-size: 16px;
  --loop-picker-option-color: #646566;
  --loop-picker-option-disabled-color: #c8c9cc;
  
  /* 选中项 */
  --loop-picker-active-color: #323233;
  --loop-picker-active-font-weight: 500;
  
  /* 遮罩 */
  --loop-picker-mask-background: linear-gradient(180deg, 
    rgba(255, 255, 255, 0.9), 
    rgba(255, 255, 255, 0.4));
}
```

### 自定义样式示例

```tsx
import React, { useState } from 'react';
import LoopPicker from '@ray-js/loop-picker';
import './CustomPicker.css'; // 引入自定义样式

function CustomStyleExample() {
  const [columns] = useState(['选项1', '选项2', '选项3']);

  return (
    <LoopPicker 
      className="custom-picker"
      columns={columns}
      activeStyle="color: #ff6b6b; font-weight: bold;"
      fontStyle="color: #666;"
      pickerStyle="background: #f8f9fa;"
    />
  );
}
```

```css
.custom-picker {
  --loop-picker-confirm-color: #ff6b6b;
  --loop-picker-active-color: #ff6b6b;
  --loop-picker-background: #f8f9fa;
}
```

### 属性

| 属性                  | 类型              | 默认值     | 说明                                                     |
| --------------------- | ----------------- | ---------- | -------------------------------------------------------- |
| `type`                | `String`          | `'normal'` | 选择器类型：`'normal'`、`'date'`、`'time'`、`'datetime'` |
| `columns`             | `Array`           | `[]`       | 选择器数据源                                             |
| `loop`                | `Boolean`         | `false`    | 是否启用循环滚动                                         |
| `defaultIndex`        | `Number \| Array` | `0`        | 默认选中索引                                             |
| `title`               | `String`          | `''`       | 工具栏标题                                               |
| `cancelButtonText`    | `String`          | `'取消'`   | 取消按钮文字                                             |
| `confirmButtonText`   | `String`          | `'确认'`   | 确认按钮文字                                             |
| `loading`             | `Boolean`         | `false`    | 显示加载状态                                             |
| `disabled`            | `Boolean`         | `false`    | 禁用选择器                                               |
| `itemHeight`          | `Number`          | `44`       | 每个选项的高度（px）                                     |
| `visibleItemCount`    | `Number`          | `5`        | 可见选项数量                                             |
| `showToolbar`         | `Boolean`         | `false`    | 显示工具栏                                               |
| `toolbarPosition`     | `String`          | `'top'`    | 工具栏位置：`'top'` 或 `'bottom'`                        |
| `activeStyle`         | `String`          | `''`       | 选中项样式                                               |
| `fontStyle`           | `String`          | `''`       | 选项字体样式                                             |
| `pickerStyle`         | `String`          | `''`       | 容器样式                                                 |

### 事件

| 事件        | 说明               | 参数               |
| ----------- | ------------------ | ------------------ |
| `onChange`  | 选择变化时触发     | `{ value, index }` |
| `onConfirm` | 点击确认按钮时触发 | `{ value, index }` |
| `onCancel`  | 点击取消按钮时触发 | `{ value, index }` |

### 方法

| 方法                           | 说明             | 参数                        | 返回值      |
| ------------------------------ | ---------------- | --------------------------- | ----------- |
| `getValues()`                  | 获取当前选中值   | -                           | `Any|Array` |
| `getIndexes()`                 | 获取当前选中索引 | -                           | `Array`     |
| `setValues(values)`            | 设置选中值       | `values: Array|Any`         | -           |
| `setIndexes(indexes)`          | 设置选中索引     | `indexes: Array`            | -           |
| `getColumnValue(index)`        | 获取指定列的值   | `index: Number`             | `Any`       |
| `getColumnValues(index)`       | 获取指定列所有值 | `index: Number`             | `Array`     |
| `setColumnValue(index, value)` | 设置指定列的值   | `index: Number, value: Any` | -           |
| `setColumnValues(index, values)` | 设置指定列所有值 | `index: Number, values: Array` | -       |

## 开发

### 环境要求

- Node.js 16+
- 涂鸦小程序基础库 2.0.0+

### 本地开发

```bash
# 安装依赖
npm install
//或者
yarn install

# 安装依赖
npm install
//或者
yarn install

# 运行example
npm run start:tuya
//或者
yarn start:tuya

# 构建生产版本
npm run build
//或者
yarn build
```

## 更新日志

### 版本历史

- **v1.0.0** (2025-10-13)
  - ✨ 初始版本发布