diff --git a/.dumirc.ts b/.dumirc.ts index b6782cffd..0cfd84dbb 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -1,9 +1,15 @@ import { defineConfig } from 'dumi'; +const basePath = process.env.GH_PAGES ? '/picker/' : '/'; +const publicPath = basePath; + export default defineConfig({ favicons: ['https://avatars0.githubusercontent.com/u/9441414?s=200&v=4'], themeConfig: { name: 'Picker', logo: 'https://avatars0.githubusercontent.com/u/9441414?s=200&v=4', }, + outputPath: 'docs-dist', + base: basePath, + publicPath, }); diff --git a/.fatherrc.ts b/.fatherrc.ts new file mode 100644 index 000000000..96268ae1e --- /dev/null +++ b/.fatherrc.ts @@ -0,0 +1,5 @@ +import { defineConfig } from 'father'; + +export default defineConfig({ + plugins: ['@rc-component/father-plugin'], +}); diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 33b1999c7..758659af3 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,15 +1,2 @@ -# These are supported funding model platforms - -github: ant-design # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: ant-design # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -polar: # Replace with a single Polar username -buy_me_a_coffee: # Replace with a single Buy Me a Coffee username -thanks_dev: # Replace with a single thanks.dev username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] +github: ant-design +open_collective: ant-design diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 96ae99a06..3b730ef99 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,32 +1,19 @@ version: 2 updates: -- package-ecosystem: npm - directory: "/" - schedule: - interval: daily - time: "21:00" - open-pull-requests-limit: 10 - ignore: - - dependency-name: eslint - versions: - - 7.18.0 - - 7.19.0 - - 7.20.0 - - 7.21.0 - - 7.22.0 - - 7.23.0 - - 7.24.0 - - dependency-name: "@types/react-dom" - versions: - - 17.0.0 - - 17.0.1 - - 17.0.2 - - dependency-name: "@types/react" - versions: - - 17.0.0 - - 17.0.1 - - 17.0.2 - - 17.0.3 - - dependency-name: less - versions: - - 4.1.0 + - package-ecosystem: npm + directory: '/' + schedule: + interval: weekly + day: monday + time: '21:00' + timezone: Asia/Shanghai + open-pull-requests-limit: 10 + + - package-ecosystem: github-actions + directory: '/' + schedule: + interval: weekly + day: monday + time: '21:00' + timezone: Asia/Shanghai + open-pull-requests-limit: 10 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 42a2c2954..bb9b296f2 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,12 +1,12 @@ -name: "CodeQL" +name: 'CodeQL' on: push: - branches: [ "master" ] + branches: ['master'] pull_request: - branches: [ "master" ] + branches: ['master'] schedule: - - cron: "19 12 * * 0" + - cron: '19 12 * * 0' jobs: analyze: @@ -20,22 +20,24 @@ jobs: strategy: fail-fast: false matrix: - language: [ javascript ] + language: [javascript] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v7 + with: + persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e with: - category: "/language:${{ matrix.language }}" + category: '/language:${{ matrix.language }}' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index f860ff107..000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: ✅ test -on: [push, pull_request] -jobs: - test: - uses: react-component/rc-test/.github/workflows/test.yml@main - secrets: inherit diff --git a/.github/workflows/react-component-ci.yml b/.github/workflows/react-component-ci.yml new file mode 100644 index 000000000..36dacae47 --- /dev/null +++ b/.github/workflows/react-component-ci.yml @@ -0,0 +1,9 @@ +name: ✅ test +on: [push, pull_request] +permissions: + contents: read +jobs: + test: + uses: react-component/rc-test/.github/workflows/test-utoo.yml@main + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/react-doctor.yml b/.github/workflows/react-doctor.yml new file mode 100644 index 000000000..097eb883d --- /dev/null +++ b/.github/workflows/react-doctor.yml @@ -0,0 +1,27 @@ +name: React Doctor + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + push: + branches: [master] + +permissions: + contents: read + pull-requests: write + issues: write + statuses: write + +concurrency: + group: react-doctor-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + react-doctor: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + persist-credentials: false + - uses: millionco/react-doctor@0b4f4f4bd248a154e64eb508a48347f71154b3f3 diff --git a/.github/workflows/surge-preview.yml b/.github/workflows/surge-preview.yml new file mode 100644 index 000000000..4c2f17edc --- /dev/null +++ b/.github/workflows/surge-preview.yml @@ -0,0 +1,54 @@ +name: Surge Preview + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write + checks: write + +jobs: + preview: + runs-on: ubuntu-latest + concurrency: + group: surge-preview-${{ github.event.pull_request.number }} + cancel-in-progress: true + env: + PREVIEW: true + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Check Surge token + id: surge-token + env: + SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} + run: | + if [ -n "$SURGE_TOKEN" ]; then + echo "enabled=true" >> "$GITHUB_OUTPUT" + else + echo "enabled=false" >> "$GITHUB_OUTPUT" + fi + - name: Build preview + if: ${{ steps.surge-token.outputs.enabled == 'true' }} + run: | + npm install + npm run build + - uses: afc163/surge-preview@bf90a5a86111f6311ca42f0a5a0f80fb0fb03cec + if: ${{ steps.surge-token.outputs.enabled == 'true' }} + env: + SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} + with: + surge_token: ${{ env.SURGE_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} + dist: docs-dist + failOnError: false + setCommitStatus: false + - name: Skip Surge preview + if: ${{ steps.surge-token.outputs.enabled != 'true' }} + run: echo "SURGE_TOKEN is not configured; skip Surge preview." diff --git a/.gitignore b/.gitignore index 2c1bace31..073261a5e 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,8 @@ package-lock.json pnpm-lock.yaml coverage/ .doc +docs-dist +.vercel .history # umi .umi @@ -42,4 +44,4 @@ coverage/ .dumi/tmp .dumi/tmp-production -bun.lockb \ No newline at end of file +bun.lockb diff --git a/.prettierignore b/.prettierignore index f7ba274fa..2e2aff9c9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,4 +11,14 @@ _site .umi .doc .umi-production -.umi-test \ No newline at end of file +.umi-test +coverage +docs-dist +dist +.dumi/tmp +.dumi/tmp-production +.vercel +pnpm-lock.yaml +yarn.lock +bun.lockb +*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..bd0a1f722 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-present react-component + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 1066bcb66..446d4d856 100644 --- a/README.md +++ b/README.md @@ -1,158 +1,206 @@ -# @rc-component/picker +
+

@rc-component/picker

+

Ant Design Part of the Ant Design ecosystem.

+

📅 React date, time, range, and panel picker primitives with pluggable date-library generate configs.

+ +

+ NPM version + npm downloads + build status + Codecov + bundle size + dumi +

+
+ +

English | 简体中文

+ +## Highlights + +- Single picker, range picker, and panel-only picker exports. +- Date, time, week, month, quarter, and year modes. +- Pluggable `generateConfig` adapters for date-fns, dayjs, luxon, and moment. +- Locale packages exposed from `@rc-component/picker/locale/*`. +- Controlled value, popup state, panel value, presets, disabled dates, semantic class names, and custom cell rendering. +- TypeScript definitions for picker props, range values, locale, date-library adapters, and refs. +- Used by Ant Design as the shared date and time picker foundation. -[![NPM version][npm-image]][npm-url] [![build status][github-actions-image]][github-actions-url] [![Codecov][codecov-image]][codecov-url] [![npm download][download-image]][download-url] [![bundle size][bundlephobia-image]][bundlephobia-url] - -[npm-image]: http://img.shields.io/npm/v/@rc-component/picker.svg?style=flat-square -[npm-url]: http://npmjs.org/package/@rc-component/picker -[github-actions-image]: https://github.com/react-component/picker/actions/workflows/main.yml/badge.svg -[github-actions-url]: https://github.com/react-component/picker/actions/workflows/main.yml -[codecov-image]: https://img.shields.io/codecov/c/github/react-component/picker/master.svg?style=flat-square -[codecov-url]: https://codecov.io/gh/react-component/picker/branch/master -[david-url]: https://david-dm.org/react-component/picker -[david-image]: https://david-dm.org/react-component/picker/status.svg?style=flat-square -[david-dev-url]: https://david-dm.org/react-component/picker?type=dev -[david-dev-image]: https://david-dm.org/react-component/picker/dev-status.svg?style=flat-square -[download-image]: https://img.shields.io/npm/dm/@rc-component/picker.svg?style=flat-square -[download-url]: https://npmjs.org/package/@rc-component/picker -[bundlephobia-url]: https://bundlephobia.com/result?p=@rc-component/picker -[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@rc-component/picker +## Install -## Live Demo +```bash +npm install @rc-component/picker +``` -https://react-component.github.io/picker/ +Install the date library you plan to use if it is not already in your project: -## Install - -[![@rc-component/picker](https://nodei.co/npm/@rc-component/picker.png)](https://npmjs.org/package/@rc-component/picker) +```bash +npm install dayjs +``` ## Usage -```js +```tsx | pure +import type { Dayjs } from 'dayjs'; +import dayjs from 'dayjs'; import Picker from '@rc-component/picker'; +import dayjsGenerateConfig from '@rc-component/picker/generate/dayjs'; +import enUS from '@rc-component/picker/locale/en_US'; import '@rc-component/picker/assets/index.css'; -import { render } from 'react-dom'; +export default () => ( + generateConfig={dayjsGenerateConfig} locale={enUS} defaultValue={dayjs()} /> +); +``` -render(, mountNode); +```tsx | pure +import type { Dayjs } from 'dayjs'; +import { RangePicker } from '@rc-component/picker'; +import dayjsGenerateConfig from '@rc-component/picker/generate/dayjs'; +import enUS from '@rc-component/picker/locale/en_US'; +export default () => ( + + generateConfig={dayjsGenerateConfig} + locale={enUS} + showTime + presets={[ + { + label: 'Today', + value: () => [dayjsGenerateConfig.getNow(), dayjsGenerateConfig.getNow()], + }, + ]} + /> +); ``` +## Examples + +Run the local dumi site: + +```bash +npm install +npm start +``` + +Then open `http://localhost:8000`. + ## API -### Picker +### Exports + +| Export | Description | +| ------------- | ------------------------------------------------------------- | +| `Picker` | Input picker for one date/time value or multiple values. | +| `RangePicker` | Input picker for start and end date/time values. | +| `PickerPanel` | Panel-only picker without the input trigger. | +| `generate/*` | Date-library adapters for date-fns, dayjs, luxon, and moment. | +| `locale/*` | Locale objects for picker UI text and formats. | +| `interface` | Shared TypeScript types. | + +### Shared Picker Props | Property | Type | Default | Description | | --- | --- | --- | --- | -| prefixCls | String | rc-picker | prefixCls of this component | -| className | String | '' | additional css class of root dom node | -| style | React.CSSProperties | | additional style of root dom node | -| dropdownClassName | String | '' | additional className applied to dropdown | -| popupAlign | Object:alignConfig of [dom-align](https://github.com/yiminghe/dom-align) | | value will be merged into placement's popupAlign config | -| popupStyle | React.CSSProperties | | customize popup style | -| transitionName | String | '' | css class for animation | -| locale | Object | import from '@rc-component/picker/locale/en_US' | @rc-component/picker locale | -| inputReadOnly | boolean | false | set input to read only | -| allowClear | boolean \| { clearIcon?: ReactNode } | false | whether show clear button or customize clear button | -| onClear | () => void | | a callback function, can be executed when the clear button is clicked | -| autoFocus | boolean | false | whether auto focus | -| showTime | boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection | -| picker | time \| date \| week \| month \| year | | control which kind of panel should be shown | -| previewValue | false \| hover | hover | When the user selects the date hover option, the value of the input field undergoes a temporary change | -| format | String \| String[] | depends on whether you set timePicker and your locale | use to format/parse date(without time) value to/from input. When an array is provided, all values are used for parsing and first value for display | -| use12Hours | boolean | false | 12 hours display mode | -| value | moment | | current value like input's value | -| defaultValue | moment | | defaultValue like input's defaultValue | -| open | boolean | false | current open state of picker. controlled prop | -| suffixIcon | ReactNode | | The custom suffix icon | -| prevIcon | ReactNode | | The custom prev icon | -| nextIcon | ReactNode | | The custom next icon | -| superPrevIcon | ReactNode | | The custom super prev icon | -| superNextIcon | ReactNode | | The custom super next icon | -| disabled | boolean | false | whether the picker is disabled | -| placeholder | String | | picker input's placeholder | -| getPopupContainer | function(trigger) | | to set the container of the floating layer, while the default is to create a div element in body | -| onChange | Function(date: moment, dateString: string) | | a callback function, can be executed when the selected time is changing | -| onOpenChange | Function(open:boolean) | | called when open/close picker | -| onFocus | (event:React.FocusEvent\) => void | | called like input's on focus | -| onBlur | (event:React.FocusEvent\) => void | | called like input's on blur | -| onKeyDown | (event:React.KeyboardEvent\, preventDefault: () => void) => void | | input on keydown event | -| direction | String: ltr or rtl | | Layout direction of picker component, it supports RTL direction too. | - -### PickerPanel +| allowClear | `boolean \| { clearIcon?: ReactNode }` | `false` | Show the clear button or customize it. | +| cellRender | `CellRender` | - | Customize date, time, and range cells. | +| changeOnBlur | `boolean` | - | Commit typed values on blur when valid. | +| className | `string` | - | Class name for the picker root. | +| classNames | `SemanticClassNames` | - | Semantic class names for root and popup slots. | +| components | `Components` | - | Component overrides. | +| defaultOpen | `boolean` | - | Initial popup open state. | +| disabledDate | `DisabledDate` | - | Disable selectable dates. | +| format | `string \| string[] \| FormatType[]` | locale dependent | Format and parse input values. | +| generateConfig | `GenerateConfig` | required | Date-library adapter. | +| getPopupContainer | `(node: HTMLElement) => HTMLElement` | - | Popup container. | +| inputReadOnly | `boolean` | - | Make input read-only. | +| locale | `Locale` | required | Locale text and formats. | +| maxDate | `DateType` | - | Latest selectable date. | +| minDate | `DateType` | - | Earliest selectable date. | +| needConfirm | `boolean` | - | Require OK confirmation before change. | +| open | `boolean` | - | Controlled popup open state. | +| picker | `'time' \| 'date' \| 'week' \| 'month' \| 'quarter' \| 'year'` | `date` | Picker mode. | +| pickerValue | `DateType \| [DateType, DateType] \| null` | - | Controlled panel date. | +| placeholder | `string \| [string, string]` | - | Input placeholder. | +| popupClassName | `string` | - | Class name for popup. | +| presets | `ValueDate[]` | - | Preset values. | +| previewValue | `false \| 'hover'` | `hover` | Preview hovered values in input. | +| showNow | `boolean` | - | Show the "now" button. | +| showTime | `boolean \| SharedTimeProps` | `false` | Enable time selection. | +| showToday | `boolean` | - | Show the "today" button. | +| styles | `SemanticStyles` | - | Semantic styles for root and popup slots. | +| suffixIcon | `ReactNode` | - | Custom suffix icon. | +| onOpenChange | `(open: boolean) => void` | - | Triggered when popup open state changes. | +| onPanelChange | `(value, mode) => void` | - | Triggered when panel mode changes. | + +### Picker Props | Property | Type | Default | Description | | --- | --- | --- | --- | -| prefixCls | String | @rc-component/picker | prefixCls of this component | -| className | String | '' | additional css class of root dom | -| style | React.CSSProperties | | additional style of root dom node | -| locale | Object | import from '@rc-component/picker/locale/en_US' | @rc-component/picker locale | -| value | moment | | current value like input's value | -| defaultValue | moment | | defaultValue like input's defaultValue | -| defaultPickerValue | moment | | Set default display picker view date | -| mode | time \| datetime \| date \| week \| month \| year \| decade | | control which kind of panel | -| picker | time \| date \| week \| month \| year | | control which kind of panel | -| tabIndex | Number | 0 | view [tabIndex](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) | -| showTime | boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection | -| showToday | boolean | false | whether to show today button | -| disabledDate | Function(date:moment) => boolean | | whether to disable select of current date | -| dateRender | Function(currentDate:moment, today:moment) => React.Node | | custom rendering function for date cells | -| monthCellRender | Function(currentDate:moment, locale:Locale) => React.Node | | Custom month cell render method | -| renderExtraFooter | (mode) => React.Node | | extra footer | -| onSelect | Function(date: moment) | | a callback function, can be executed when the selected time | -| onPanelChange | Function(value: moment, mode) | | callback when picker panel mode is changed | -| onMouseDown | (event:React.MouseEvent\) => void | | callback when executed onMouseDown event | -| direction | String: ltr or rtl | | Layout direction of picker component, it supports RTL direction too. | - -### RangePicker +| defaultPickerValue | `DateType \| null` | - | Initial panel date whenever the popup opens. | +| defaultValue | `DateType \| DateType[]` | - | Initial selected value. | +| multiple | `boolean` | `false` | Enable multiple selection for supported modes. | +| tagRender | `(props: CustomTagProps) => ReactNode` | - | Customize multiple value tags. | +| value | `DateType \| DateType[] \| null` | - | Controlled selected value. | +| onCalendarChange | `(date, dateString, info) => void` | - | Triggered while calendar selection changes. | +| onChange | `(date, dateString) => void` | - | Triggered when selected value changes. | +| onOk | `(value) => void` | - | Triggered when OK is clicked. | + +### RangePicker Props | Property | Type | Default | Description | -| --- | --- | --- | --- | --- | -| prefixCls | String | rc-picker | prefixCls of this component | -| className | String | '' | additional css class of root dom | -| style | React.CSSProperties | | additional style of root dom node | -| locale | Object | import from '@rc-component/picker/locale/en_US' | @rc-component/picker locale | -| value | moment | | current value like input's value | -| defaultValue | moment | | defaultValue like input's defaultValue | -| defaultPickerValue | moment | | Set default display picker view date | -| separator | String | '~' | set separator between inputs | -| picker | time \| date \| week \| month \| year | | control which kind of panel | -| previewValue | false \| hover | hover | When the user selects the date hover option, the value of the input field undergoes a temporary change | -| placeholder | [String, String] | | placeholder of date input | -| showTime | boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection | -| showTime.defaultValue | [moment, moment] | | to set default time of selected date | -| use12Hours | boolean | false | 12 hours display mode | -| disabledTime | Function(date: moment, type:'start'\|'end'):Object | | | to specify the time that cannot be selected | -| ranges | { String \| [range: string]: moment[] } \| { [range: string]: () => moment[] } | | preseted ranges for quick selection | -| format | String \| String[] | depends on whether you set timePicker and your locale | use to format/parse date(without time) value to/from input. When an array is provided, all values are used for parsing and first value for display | -| allowEmpty | [boolean, boolean] | | allow range picker clearing text | -| selectable | [boolean, boolean] | | whether to selected picker | -| disabled | boolean | false | whether the range picker is disabled | -| onChange | Function(value:[moment], formatString: [string, string]) | | a callback function, can be executed when the selected time is changing | -| onCalendarChange | Function(value:[moment], formatString: [string, string], info: { range:'start'\|'end' }) | | a callback function, can be executed when the start time or the end time of the range is changing | -| onClear | () => void | | a callback function, can be executed when the clear button is clicked | -| direction | String: ltr or rtl | | Layout direction of picker component, it supports RTL direction too. | -| order | boolean | true | (TimeRangePicker only) `false` to disable auto order | - -### showTime-options - -| Property | Type | Default | Description | -| ------------------- | ------- | ------- | ---------------------------------- | -| format | String | | moment format | -| showHour | boolean | true | whether show hour | -| showMinute | boolean | true | whether show minute | -| showSecond | boolean | true | whether show second | -| use12Hours | boolean | false | 12 hours display mode | -| hourStep | Number | 1 | interval between hours in picker | -| minuteStep | Number | 1 | interval between minutes in picker | -| secondStep | Number | 1 | interval between seconds in picker | -| hideDisabledOptions | boolean | false | whether hide disabled options | -| defaultValue | moment | null | default initial value | +| --- | --- | --- | --- | +| allowEmpty | `boolean \| [boolean, boolean]` | `false` | Allow empty start or end values. | +| defaultPickerValue | `[DateType, DateType] \| DateType \| null` | - | Initial panel date whenever the popup opens. | +| defaultValue | `[DateType \| null \| undefined, DateType \| null \| undefined]` | - | Initial selected range. | +| disabled | `boolean \| [boolean, boolean]` | `false` | Disable the whole range or one side. | +| order | `boolean` | `true` | Keep selected range ordered. | +| ranges | `Record RangeValue>` | - | Deprecated preset API. Use `presets`. | +| separator | `ReactNode` | - | Separator between range inputs. | +| value | `[DateType \| null \| undefined, DateType \| null \| undefined] \| null` | - | Controlled selected range. | +| onCalendarChange | `(dates, dateStrings, info) => void` | - | Triggered while range selection changes. | +| onChange | `(dates, dateStrings) => void` | - | Triggered when selected range changes. | +| onOk | `(values) => void` | - | Triggered when OK is clicked. | + +### Time Options + +| Property | Type | Default | Description | +| --- | --- | --- | --- | +| changeOnScroll | `boolean` | `false` | Change time values on scroll. | +| defaultOpenValue | `DateType \| DateType[]` | - | Default time template when selection is empty. | +| disabledTime | `(date, range?, info?) => DisabledTimes` | - | Disable hours, minutes, seconds, or milliseconds. | +| format | `string` | - | Time format. | +| hideDisabledOptions | `boolean` | `false` | Hide disabled time options. | +| hourStep | `number` | `1` | Hour interval. | +| millisecondStep | `number` | `1` | Millisecond interval. | +| minuteStep | `number` | `1` | Minute interval. | +| secondStep | `number` | `1` | Second interval. | +| showHour | `boolean` | `true` | Show hour column. | +| showMillisecond | `boolean` | `false` | Show millisecond column. | +| showMinute | `boolean` | `true` | Show minute column. | +| showNow | `boolean` | - | Show now shortcut. | +| showSecond | `boolean` | `true` | Show second column. | +| use12Hours | `boolean` | `false` | Use 12-hour display. | ## Development -``` +```bash npm install npm start +npm test +npm run tsc +npm run coverage +npm run compile +npm run build +``` + +The dumi site runs at `http://localhost:8000` by default. + +## Release + +```bash +npm run prepublishOnly ``` +The release flow is handled by `@rc-component/np` through the `rc-np` command after the package build. + ## License -@rc-component/picker is released under the MIT license. +@rc-component/picker is released under the [MIT](./LICENSE) license. diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 000000000..a174806ac --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,206 @@ +
+

@rc-component/picker

+

Ant Design Ant Design 生态的一部分。

+

📅 React 日期与时间选择基础组件。

+ +

+ NPM version + npm downloads + build status + Codecov + bundle size + dumi +

+
+ +

English | 简体中文

+ +## 特性 + +- 单一拾取器、范围拾取器和仅面板拾取器导出。 +- 日期、时间、周、月、季度和年模式。 +- 适用于 date-fns、dayjs、luxon 和 moment 的可插拔 `generateConfig` 适配器。 +- 从 `@rc-component/picker/locale/*` 公开的语言环境包。 +- 受控值、弹层状态、面板值、预设、禁用日期、语义 className 和自定义单元格渲染。 +- 选择器属性、范围值、区域设置、日期库适配器和引用的 TypeScript 定义。 +- 被 Ant Design 共享的日期和时间选择器基础能力。 + +## 安装 + +```bash +npm install @rc-component/picker +``` + +如果项目尚未安装日期库,请先安装计划使用的日期库: + +```bash +npm install dayjs +``` + +## 使用 + +```tsx | pure +import type { Dayjs } from 'dayjs'; +import dayjs from 'dayjs'; +import Picker from '@rc-component/picker'; +import dayjsGenerateConfig from '@rc-component/picker/generate/dayjs'; +import enUS from '@rc-component/picker/locale/en_US'; +import '@rc-component/picker/assets/index.css'; +export default () => ( + generateConfig={dayjsGenerateConfig} locale={enUS} defaultValue={dayjs()} /> +); +``` + +```tsx | pure +import type { Dayjs } from 'dayjs'; +import { RangePicker } from '@rc-component/picker'; +import dayjsGenerateConfig from '@rc-component/picker/generate/dayjs'; +import enUS from '@rc-component/picker/locale/en_US'; +export default () => ( + + generateConfig={dayjsGenerateConfig} + locale={enUS} + showTime + presets={[ + { + label: 'Today', + value: () => [dayjsGenerateConfig.getNow(), dayjsGenerateConfig.getNow()], + }, + ]} + /> +); +``` + +## 示例 + +运行本地 dumi 站点: + +```bash +npm install +npm start +``` + +然后打开 `http://localhost:8000`。 + +## API + +### Exports + +| Export | 说明 | +| ------------- | -------------------------------------------------------- | +| `Picker` | 用于单个或多个日期/时间值的输入型选择器。 | +| `RangePicker` | 用于开始和结束日期/时间值的输入型范围选择器。 | +| `PickerPanel` | 仅面板选择器,没有输入触发器。 | +| `generate/*` | 适用于 date-fns、dayjs、luxon 和 moment 的日期库适配器。 | +| `locale/*` | 选择器 UI 文本和格式的区域设置对象。 | +| `interface` | 共享 TypeScript 类型。 | + +### Shared Picker Props + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| allowClear | `boolean \| { clearIcon?: ReactNode }` | `false` | 显示清除按钮或自定义它。 | +| cellRender | `CellRender` | - | 自定义日期、时间和范围单元格。 | +| changeOnBlur | `boolean` | - | 当有效时,提交关于模糊的键入值。 | +| className | `string` | - | 选择器根的 className。 | +| classNames | `SemanticClassNames` | - | 根槽和弹层槽的语义 className。 | +| components | `Components` | - | 组件覆盖配置。 | +| defaultOpen | `boolean` | - | 初始弹层打开状态。 | +| disabledDate | `DisabledDate` | - | 禁用可选择的日期。 | +| format | `string \| string[] \| FormatType[]` | 依赖于语言环境 | 格式化并解析输入值。 | +| generateConfig | `GenerateConfig` | 必需的 | 日期库适配器。 | +| getPopupContainer | `(node: HTMLElement) => HTMLElement` | - | 弹层容器。 | +| inputReadOnly | `boolean` | - | 使输入只读。 | +| locale | `Locale` | 必需的 | 区域设置文本和格式。 | +| maxDate | `DateType` | - | 最晚可选日期。 | +| minDate | `DateType` | - | 最早可选日期。 | +| needConfirm | `boolean` | - | 更改前需要确定确认。 | +| open | `boolean` | - | 受控弹层打开状态。 | +| picker | `'time' \| 'date' \| 'week' \| 'month' \| 'quarter' \| 'year'` | `date` | 选择器模式。 | +| pickerValue | `DateType \| [DateType, DateType] \| null` | - | 受控面板日期。 | +| placeholder | `string \| [string, string]` | - | 输入框占位文本。 | +| popupClassName | `string` | - | 弹层窗口的 className。 | +| presets | `ValueDate[]` | - | 预设值。 | +| previewValue | `false \| 'hover'` | `hover` | 预览输入中的悬停值。 | +| showNow | `boolean` | - | 显示“现在”按钮。 | +| showTime | `boolean \| SharedTimeProps` | `false` | 启用时间选择。 | +| showToday | `boolean` | - | 显示“今天”按钮。 | +| styles | `SemanticStyles` | - | 根槽和弹层槽的语义样式。 | +| suffixIcon | `ReactNode` | - | 自定义后缀图标。 | +| onOpenChange | `(open: boolean) => void` | - | 当弹层窗口打开状态改变时触发。 | +| onPanelChange | `(value, mode) => void` | - | 当面板模式改变时触发。 | + +### Picker Props + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| defaultPickerValue | `DateType \| null` | - | 弹层窗口打开时的初始面板日期。 | +| defaultValue | `DateType \| DateType[]` | - | 初始选中值。 | +| multiple | `boolean` | `false` | 为支持的模式启用多重选择。 | +| tagRender | `(props: CustomTagProps) => ReactNode` | - | 自定义多个值标签。 | +| value | `DateType \| DateType[] \| null` | - | 受控选中值。 | +| onCalendarChange | `(date, dateString, info) => void` | - | 日历选择更改时触发。 | +| onChange | `(date, dateString) => void` | - | 当所选值更改时触发。 | +| onOk | `(value) => void` | - | 单击“确定”时触发。 | + +### RangePicker Props + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| allowEmpty | `boolean \| [boolean, boolean]` | `false` | 允许空的开始值或结束值。 | +| defaultPickerValue | `[DateType, DateType] \| DateType \| null` | - | 弹层窗口打开时的初始面板日期。 | +| defaultValue | `[DateType \| null \| undefined, DateType \| null \| undefined]` | - | 初始选定范围。 | +| disabled | `boolean \| [boolean, boolean]` | `false` | 禁用整个范围或一侧。 | +| order | `boolean` | `true` | 保持选定范围的顺序。 | +| ranges | `Record RangeValue>` | - | 已弃用预设 API。使用 `presets`。 | +| separator | `ReactNode` | - | 范围输入之间的分隔符。 | +| value | `[DateType \| null \| undefined, DateType \| null \| undefined] \| null` | - | 受控选定范围。 | +| onCalendarChange | `(dates, dateStrings, info) => void` | - | 当范围选择更改时触发。 | +| onChange | `(dates, dateStrings) => void` | - | 当选定范围更改时触发。 | +| onOk | `(values) => void` | - | 单击“确定”时触发。 | + +### Time Options + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| changeOnScroll | `boolean` | `false` | 更改滚动上的时间值。 | +| defaultOpenValue | `DateType \| DateType[]` | - | 选择为空时的默认时间模板。 | +| disabledTime | `(date, range?, info?) => DisabledTimes` | - | 禁用小时、分钟、秒或毫秒。 | +| format | `string` | - | Time format. | +| hideDisabledOptions | `boolean` | `false` | 隐藏禁用的时间选项。 | +| hourStep | `number` | `1` | 小时间隔。 | +| millisecondStep | `number` | `1` | 毫秒间隔。 | +| minuteStep | `number` | `1` | 分钟间隔。 | +| secondStep | `number` | `1` | 秒间隔。 | +| showHour | `boolean` | `true` | 显示小时列。 | +| showMillisecond | `boolean` | `false` | 显示毫秒列。 | +| showMinute | `boolean` | `true` | 显示分钟栏。 | +| showNow | `boolean` | - | 现在显示快捷方式。 | +| showSecond | `boolean` | `true` | 显示第二列。 | +| use12Hours | `boolean` | `false` | 使用 12 小时制显示。 | + +## 本地开发 + +```bash +npm install +npm start +npm test +npm run tsc +npm run coverage +npm run compile +npm run build +``` + +dumi 站点默认运行在 `http://localhost:8000`。 + +## 发布 + +```bash +npm run prepublishOnly +``` + +包构建完成后,发布流程由 `@rc-component/np` 通过 `rc-np` 命令处理。 + +## 许可证 + +@rc-component/picker 基于 [MIT](./LICENSE) 许可证发布。 diff --git a/docs/examples/calendar.less b/docs/examples/calendar.less index 5fc2cb4dc..427d77862 100644 --- a/docs/examples/calendar.less +++ b/docs/examples/calendar.less @@ -1,3 +1,3 @@ .rc-picker-cell-selected { background: red; -} \ No newline at end of file +} diff --git a/docs/examples/modes.tsx b/docs/examples/modes.tsx index da7eb8b37..50303d512 100644 --- a/docs/examples/modes.tsx +++ b/docs/examples/modes.tsx @@ -52,4 +52,4 @@ export default () => { ); -}; \ No newline at end of file +}; diff --git a/docs/index.md b/docs/index.md index 68783ad9a..89b37e432 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,8 +1,7 @@ --- hero: - title: rc-picker - description: React picker component + title: '@rc-component/picker' + description: React date, time, range, and panel picker primitives. --- - diff --git a/now.json b/now.json deleted file mode 100644 index 5a7e57a63..000000000 --- a/now.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": 2, - "name": "rc-picker", - "builds": [ - { - "src": "package.json", - "use": "@now/static-build", - "config": { "distDir": "dist" } - } - ], - "routes": [{ "src": "/(.*)", "dest": "/dist/$1" }] -} diff --git a/package.json b/package.json index e9ba88561..6f4efd869 100644 --- a/package.json +++ b/package.json @@ -87,26 +87,28 @@ "homepage": "https://react-component.github.io/picker", "repository": { "type": "git", - "url": "git@github.com:react-component/picker.git" + "url": "https://github.com/react-component/picker.git" }, "bugs": { - "url": "http://github.com/react-component/picker/issues" + "url": "https://github.com/react-component/picker/issues" }, "license": "MIT", "scripts": { "start": "dumi dev", - "build": "dumi build", + "build": "npm run docs:build", + "docs:build": "dumi build", + "docs:deploy": "gh-pages -d docs-dist", "compile": "father build && lessc assets/index.less assets/index.css", "browser-field": "node scripts/update-browser-field.js", - "gh-pages": "npm run build && father doc deploy", + "gh-pages": "cross-env GH_PAGES=1 npm run docs:build && npm run docs:deploy", "prepublishOnly": "npm run compile && npm run browser-field && rc-np", "lint": "eslint src/ --ext .ts,.tsx,.jsx,.js,.md", "lint:tsc": "tsc -p tsconfig.json --noEmit", - "prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"", + "prettier": "prettier --write --ignore-unknown .", "test": "rc-test", "coverage": "father test --coverage", - "now-build": "npm run build", - "prepare": "npx husky" + "prepare": "husky", + "tsc": "tsc --noEmit" }, "dependencies": { "@rc-component/overflow": "^1.0.0", @@ -117,31 +119,33 @@ }, "devDependencies": { "@rc-component/father-plugin": "^2.2.0", - "@rc-component/np": "^1.0.3", - "@testing-library/react": "^16.0.0", - "@types/jest": "^29.4.0", + "@rc-component/np": "^1.0.4", + "@testing-library/react": "^15.0.7", + "@types/jest": "^29.5.14", "@types/luxon": "^3.2.0", - "@types/node": "^24.5.2", - "@types/react": "^18.0.28", - "@types/react-dom": "^18.0.8", + "@types/node": "^26.0.1", + "@types/react": "^18.3.31", + "@types/react-dom": "^18.3.7", + "cross-env": "^10.1.0", "date-fns": "2.x", "dayjs": "1.x", - "dumi": "^2.1.15", - "eslint": "^8.56.0", - "father": "^4.0.0", - "glob": "^10.4.1", - "husky": "^9.0.11", - "less": "^4.2.0", - "lint-staged": "^15.2.7", + "dumi": "^2.4.35", + "eslint": "^8.57.1", + "father": "^4.6.23", + "gh-pages": "^6.3.0", + "glob": "^13.0.6", + "husky": "^9.1.7", + "less": "^4.6.7", + "lint-staged": "^16.4.0", "luxon": "3.x", "mockdate": "^3.0.2", "moment": "^2.24.0", "moment-timezone": "^0.5.45", - "prettier": "^3.1.0", - "rc-test": "^7.0.9", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "typescript": "^5.3.0" + "prettier": "^3.9.0", + "rc-test": "^7.1.3", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "typescript": "^5.9.3" }, "peerDependencies": { "date-fns": ">= 2.x", @@ -330,5 +334,11 @@ "./locale/zh_CN.js": "./es/locale/zh_CN.js", "./locale/zh_TW": "./es/locale/zh_TW.js", "./locale/zh_TW.js": "./es/locale/zh_TW.js" + }, + "publishConfig": { + "access": "public" + }, + "lint-staged": { + "*": "prettier --write --ignore-unknown" } } diff --git a/tsconfig.json b/tsconfig.json index f8266a862..226872a37 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,15 +11,18 @@ "@/*": ["src/*"], "@@/*": [".dumi/tmp/*"], "@rc-component/picker": ["src/index.tsx"] - } + }, + "ignoreDeprecations": "5.0" }, "include": [ ".dumirc.ts", + ".fatherrc.ts", "src/**/*.ts", "src/**/*.tsx", "docs/examples/*.tsx", "tests/**/*.ts", "tests/**/*.tsx", "typings.d.ts" - ] + ], + "exclude": ["node_modules", "lib", "es", "dist", "docs-dist", ".dumi"] } diff --git a/vercel.json b/vercel.json new file mode 100644 index 000000000..5f9139ef4 --- /dev/null +++ b/vercel.json @@ -0,0 +1,6 @@ +{ + "framework": "umijs", + "installCommand": "npm install", + "buildCommand": "npm run build", + "outputDirectory": "docs-dist" +}