@fvc/picker 1.1.2 → 1.1.3-next-ec65dfb844e6183b3d7f417eee613cfe5ecfd997
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +357 -0
- package/package.json +19 -3
package/README.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# @fvc/picker
|
|
2
|
+
|
|
3
|
+
`@fvc/picker` provides date and time picker components for FE-VIS applications — `DatePicker`, `RangeDatePicker`, and `TimePicker` — built on Ant Design and dayjs. It adds controlled date-enabling/disabling, a compact display mode, and range shortcut chips on top of the standard antd API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @fvc/picker
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Peer Dependencies
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun add react antd
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`@fvc/icons` is a direct dependency and is included automatically.
|
|
18
|
+
|
|
19
|
+
## Import
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { DatePicker, RangeDatePicker, TimePicker, Shortcut } from '@fvc/picker';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { DatePicker } from '@fvc/picker';
|
|
29
|
+
|
|
30
|
+
export function BirthDateField() {
|
|
31
|
+
return (
|
|
32
|
+
<DatePicker
|
|
33
|
+
format="DD.MM.YYYY"
|
|
34
|
+
onChange={(_, dateString) => console.log(dateString)}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## DatePicker
|
|
41
|
+
|
|
42
|
+
### Additional props
|
|
43
|
+
|
|
44
|
+
| Prop | Type | Default | Description |
|
|
45
|
+
| --- | --- | --- | --- |
|
|
46
|
+
| `disabledDates` | `IDateActivationConf` | — | Dates outside this config are disabled. |
|
|
47
|
+
| `enabledDates` | `IDateActivationConf` | — | Only dates matching this config are enabled. |
|
|
48
|
+
| `error` | `boolean` | `false` | Applies error border and background styling. |
|
|
49
|
+
| `short` | `boolean` | `false` | Compact width mode (`max-width: 140px`). |
|
|
50
|
+
| `hideYear` | `boolean` | `false` | Hides the year row in the dropdown panel. |
|
|
51
|
+
| `format` | `string` | antd default | Display and parse format string (dayjs tokens). |
|
|
52
|
+
| `testId` | `string` | `'datepicker'` | Maps to `data-testid` on the wrapper element. |
|
|
53
|
+
|
|
54
|
+
All antd `DatePickerProps` are forwarded.
|
|
55
|
+
|
|
56
|
+
### `IDateActivationConf`
|
|
57
|
+
|
|
58
|
+
Controls which dates are selectable. `disabledDates` and `enabledDates` can be
|
|
59
|
+
used independently or together — `enabledDates` takes precedence when both are provided.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
interface IDateActivationConf {
|
|
63
|
+
dates?: string[]; // exact dates: ['2024-12-25', '2024-12-31']
|
|
64
|
+
ranges?: [string, string][]; // date ranges: [['2024-01-01', '2024-01-07']]
|
|
65
|
+
fromDate?: string; // disable everything before this date
|
|
66
|
+
tillDate?: string; // disable everything after this date
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Common Usage
|
|
71
|
+
|
|
72
|
+
#### Error state
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
<DatePicker format="DD.MM.YYYY" error={hasValidationError} />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Compact mode
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
<DatePicker format="DD.MM.YYYY" short />
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Restrict selectable dates
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
// Only allow future dates
|
|
88
|
+
<DatePicker
|
|
89
|
+
format="DD.MM.YYYY"
|
|
90
|
+
disabledDates={{ tillDate: dayjs().format('YYYY-MM-DD') }}
|
|
91
|
+
/>
|
|
92
|
+
|
|
93
|
+
// Enable only specific dates
|
|
94
|
+
<DatePicker
|
|
95
|
+
format="DD.MM.YYYY"
|
|
96
|
+
enabledDates={{
|
|
97
|
+
dates: ['2024-06-15', '2024-06-16'],
|
|
98
|
+
ranges: [['2024-07-01', '2024-07-07']],
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## RangeDatePicker
|
|
106
|
+
|
|
107
|
+
### Additional props
|
|
108
|
+
|
|
109
|
+
| Prop | Type | Default | Description |
|
|
110
|
+
| --- | --- | --- | --- |
|
|
111
|
+
| `format` | `string` | `'DD.MM.YYYY'` | Display format shown in the input. |
|
|
112
|
+
| `outputFormat` | `string` | `'YYYY-MM-DD'` | Format of the strings passed to `onChange`. |
|
|
113
|
+
| `shortcuts` | `ShortcutDef[]` | — | Shortcut chips rendered below the picker. |
|
|
114
|
+
| `updateValueBeforeChange` | `BeforeChangeFunction` | — | Intercepts and transforms the value before `onChange` fires. |
|
|
115
|
+
| `disabledDates` | `IDateActivationConf` | — | Same as `DatePicker`. |
|
|
116
|
+
| `enabledDates` | `IDateActivationConf` | — | Same as `DatePicker`. |
|
|
117
|
+
| `testId` | `string` | `'rangepicker'` | Maps to `data-testid` on the wrapper. |
|
|
118
|
+
|
|
119
|
+
> `format` controls what the user sees in the input. `outputFormat` controls what `onChange` receives. Set these independently when your API expects a format different from the display format.
|
|
120
|
+
|
|
121
|
+
### Shortcuts
|
|
122
|
+
|
|
123
|
+
Shortcut chips render below the picker. Selecting a chip sets the range value and highlights it. Picking dates manually clears the active shortcut.
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
type ShortcutDef = {
|
|
127
|
+
label: string;
|
|
128
|
+
value?: [Dayjs | null, Dayjs | null];
|
|
129
|
+
renderType?: 'DEFAULT' | 'MONTHLY' | 'YEARLY' | 'QUARTERLY';
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Common Usage
|
|
134
|
+
|
|
135
|
+
#### Basic range
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<RangeDatePicker
|
|
139
|
+
format="DD.MM.YYYY"
|
|
140
|
+
outputFormat="YYYY-MM-DD"
|
|
141
|
+
onChange={(_, [start, end]) => console.log(start, end)}
|
|
142
|
+
/>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### With shortcuts
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import dayjs from 'dayjs';
|
|
149
|
+
|
|
150
|
+
<RangeDatePicker
|
|
151
|
+
shortcuts={[
|
|
152
|
+
{
|
|
153
|
+
label: 'This week',
|
|
154
|
+
value: [dayjs().startOf('week'), dayjs().endOf('week')],
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
label: 'This month',
|
|
158
|
+
value: [dayjs().startOf('month'), dayjs().endOf('month')],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
label: 'Last 30 days',
|
|
162
|
+
value: [dayjs().subtract(30, 'day'), dayjs()],
|
|
163
|
+
},
|
|
164
|
+
]}
|
|
165
|
+
/>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## TimePicker
|
|
171
|
+
|
|
172
|
+
| Prop | Type | Default | Description |
|
|
173
|
+
| --- | --- | --- | --- |
|
|
174
|
+
| `format` | `string` | `'HH:mm'` | Time display and parse format. |
|
|
175
|
+
| `testId` | `string` | `'timepicker'` | Maps to `data-testid` on the wrapper. |
|
|
176
|
+
|
|
177
|
+
All antd `TimePickerProps` are forwarded.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<TimePicker
|
|
181
|
+
format="HH:mm"
|
|
182
|
+
onChange={(_, timeString) => setTime(timeString)}
|
|
183
|
+
/>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Shortcut
|
|
189
|
+
|
|
190
|
+
A standalone clickable chip. Used internally by `RangeDatePicker` but available for building custom range layouts.
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
<Shortcut
|
|
194
|
+
label="Last 30 days"
|
|
195
|
+
isSelected={isActive}
|
|
196
|
+
onClick={handleShortcut}
|
|
197
|
+
testId="shortcut-30d"
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| Prop | Type | Default | Description |
|
|
202
|
+
| --- | --- | --- | --- |
|
|
203
|
+
| `label` | `string` | — | Chip text. |
|
|
204
|
+
| `isSelected` | `boolean` | `false` | Highlighted/active state. |
|
|
205
|
+
| `onClick` | `() => void` | — | Click handler. |
|
|
206
|
+
| `testId` | `string` | — | Maps to `data-testid`. |
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Ref Forwarding
|
|
211
|
+
|
|
212
|
+
All three picker components forward refs typed as `PickerRef`.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
interface PickerRef {
|
|
216
|
+
nativeElement: HTMLDivElement;
|
|
217
|
+
focus: (options?: FocusOptions) => void;
|
|
218
|
+
blur: VoidFunction;
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
const ref = useRef<PickerRef>(null);
|
|
224
|
+
<DatePicker ref={ref} />
|
|
225
|
+
ref.current?.focus();
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Consumer Example
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { DatePicker, RangeDatePicker } from '@fvc/picker';
|
|
232
|
+
import dayjs from 'dayjs';
|
|
233
|
+
|
|
234
|
+
interface ReportFilterProps {
|
|
235
|
+
onDateChange: (date: string) => void;
|
|
236
|
+
onRangeChange: (start: string, end: string) => void;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function ReportFilter({ onDateChange, onRangeChange }: ReportFilterProps) {
|
|
240
|
+
return (
|
|
241
|
+
<div className="report-filter">
|
|
242
|
+
<DatePicker
|
|
243
|
+
format="DD.MM.YYYY"
|
|
244
|
+
disabledDates={{ tillDate: dayjs().subtract(1, 'year').format('YYYY-MM-DD') }}
|
|
245
|
+
onChange={(_, dateString) => onDateChange(dateString as string)}
|
|
246
|
+
testId="report-date"
|
|
247
|
+
/>
|
|
248
|
+
|
|
249
|
+
<RangeDatePicker
|
|
250
|
+
format="DD.MM.YYYY"
|
|
251
|
+
outputFormat="YYYY-MM-DD"
|
|
252
|
+
shortcuts={[
|
|
253
|
+
{ label: 'Last 7 days', value: [dayjs().subtract(7, 'd'), dayjs()] },
|
|
254
|
+
{ label: 'Last 30 days', value: [dayjs().subtract(30, 'd'), dayjs()] },
|
|
255
|
+
]}
|
|
256
|
+
onChange={(_, [start, end]) => onRangeChange(start, end)}
|
|
257
|
+
testId="report-range"
|
|
258
|
+
/>
|
|
259
|
+
</div>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Testing
|
|
265
|
+
|
|
266
|
+
`testId` maps to `data-testid` on the wrapper `<div>`. Shortcut chips use
|
|
267
|
+
`{testId}-shortcut-{index}` automatically.
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
<DatePicker testId="birth-date" />
|
|
271
|
+
<RangeDatePicker testId="contract-range" />
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
screen.getByTestId('birth-date');
|
|
276
|
+
screen.getByTestId('contract-range');
|
|
277
|
+
screen.getByTestId('contract-range-shortcut-0');
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## CSS Customisation
|
|
281
|
+
|
|
282
|
+
The component exposes CSS custom properties for theming. Override them in your global styles:
|
|
283
|
+
|
|
284
|
+
```css
|
|
285
|
+
:root {
|
|
286
|
+
/* Layout */
|
|
287
|
+
--picker-width: 100%;
|
|
288
|
+
--picker-max-width-short: 140px;
|
|
289
|
+
--picker-main-padding: 0px;
|
|
290
|
+
--picker-main-outlined-input-padding: 5px 13px;
|
|
291
|
+
|
|
292
|
+
/* Colours */
|
|
293
|
+
--picker-main-color: var(--blue-600);
|
|
294
|
+
--picker-main-border: 1px solid var(--gray-400);
|
|
295
|
+
--picker-main-outlined-input-background: var(--neutral-0);
|
|
296
|
+
|
|
297
|
+
/* Border radius */
|
|
298
|
+
--picker-main-border-radius: 20px;
|
|
299
|
+
--picker-cell-in-view-border-radius: 20px;
|
|
300
|
+
--picker-today-default-radius: 2px;
|
|
301
|
+
--picker-ok-border-radius: 30px;
|
|
302
|
+
|
|
303
|
+
/* Typography */
|
|
304
|
+
--picker-input-font-weight: 500;
|
|
305
|
+
|
|
306
|
+
/* Error state */
|
|
307
|
+
--picker-error-background-color: var(--red-100);
|
|
308
|
+
--picker-error-border-color: var(--red-800);
|
|
309
|
+
--picker-error-suffix-color: var(--red-800);
|
|
310
|
+
|
|
311
|
+
/* OK button */
|
|
312
|
+
--picker-ok-background-color: var(--green-600);
|
|
313
|
+
--picker-ok-box-shadow: none;
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
For the full variable list see [`src/styles/variables.scss`](./src/styles/variables.scss).
|
|
318
|
+
|
|
319
|
+
The `Shortcut` chip has its own custom properties:
|
|
320
|
+
|
|
321
|
+
```css
|
|
322
|
+
:root {
|
|
323
|
+
--shortcut-background-color: var(--gray-250);
|
|
324
|
+
--shortcut-selected-background-color: var(--blue-150);
|
|
325
|
+
--shortcut-text-color: var(--black-1000);
|
|
326
|
+
--shortcut-border-radius: 16px;
|
|
327
|
+
--shortcut-padding: 4px 10px;
|
|
328
|
+
--shortcut-font-size: 14px;
|
|
329
|
+
--shortcut-font-weight: 400;
|
|
330
|
+
--shortcut-line-height: 16px;
|
|
331
|
+
--shortcut-hover-opacity: 0.85;
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
For the full variable list see [`src/styles/ShortcutVariables.scss`](./src/styles/ShortcutVariables.scss).
|
|
336
|
+
|
|
337
|
+
## CSS Classes
|
|
338
|
+
|
|
339
|
+
| Class | Component | Applied when |
|
|
340
|
+
| --- | --- | --- |
|
|
341
|
+
| `.fvc-datepicker` | `DatePicker` | Always — root wrapper |
|
|
342
|
+
| `.fvc-datepicker-short` | `DatePicker` | `short` is `true` |
|
|
343
|
+
| `.fvc-datepicker-dropdown` | `DatePicker` | Dropdown panel |
|
|
344
|
+
| `.fvc-datepicker-dropdown-hide-year` | `DatePicker` | `hideYear` is `true` |
|
|
345
|
+
| `.fvc-rangepicker` | `RangeDatePicker` | Always — root wrapper |
|
|
346
|
+
| `.fvc-rangepicker-input` | `RangeDatePicker` | Each date input within the range picker |
|
|
347
|
+
| `.fvc-timepicker` | `TimePicker` | Always — root wrapper |
|
|
348
|
+
| `.fvc-shortcut` | `Shortcut` | Always — chip element |
|
|
349
|
+
| `.fvc-shortcut-selected` | `Shortcut` | `isSelected` is `true` |
|
|
350
|
+
|
|
351
|
+
## Development
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
bun run lint
|
|
355
|
+
bun run type-check
|
|
356
|
+
bun run test
|
|
357
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fvc/picker",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3-next-ec65dfb844e6183b3d7f417eee613cfe5ecfd997",
|
|
4
4
|
"main": "./dist/lib/index.js",
|
|
5
5
|
"types": "./dist/lib/picker/src/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -27,10 +27,26 @@
|
|
|
27
27
|
"test": "bun test --preload ../../tests/happydom.ts --preload ../../tests/testing-library.tsx"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@fvc/icons": "
|
|
30
|
+
"@fvc/icons": "1.1.4-next-ec65dfb844e6183b3d7f417eee613cfe5ecfd997"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"react": "^18.0.0",
|
|
34
34
|
"antd": "^5.0.0"
|
|
35
|
-
}
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"react",
|
|
38
|
+
"react-component",
|
|
39
|
+
"fvc",
|
|
40
|
+
"fe-vis-core",
|
|
41
|
+
"ui",
|
|
42
|
+
"datepicker",
|
|
43
|
+
"date-picker",
|
|
44
|
+
"rangepicker",
|
|
45
|
+
"range-picker",
|
|
46
|
+
"timepicker",
|
|
47
|
+
"time-picker",
|
|
48
|
+
"date",
|
|
49
|
+
"design-system",
|
|
50
|
+
"antd"
|
|
51
|
+
]
|
|
36
52
|
}
|