@paygreen/pgui 3.0.14 → 3.1.0

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 CHANGED
@@ -11,7 +11,7 @@ Core UI components library for Paygreen applications built with React and Chakra
11
11
  npm install @paygreen/pgui
12
12
 
13
13
  # Install required peer dependencies
14
- npm install react react-dom @chakra-ui/react @emotion/react @emotion/styled framer-motion
14
+ npm install react react-dom @chakra-ui/react @emotion/react @emotion/styled
15
15
  ```
16
16
 
17
17
  ### Component-Specific Dependencies
@@ -20,7 +20,7 @@ Install additional packages based on the components you want to use:
20
20
 
21
21
  | Component | Required Package | Command |
22
22
  |-----------|------------------|---------|
23
- | **DatePicker** | `react-datepicker` | `npm install react-datepicker` |
23
+ | **DatePicker** | `@internationalized/date` | `npm install @internationalized/date` |
24
24
  | **Select** | `react-select` | `npm install react-select` |
25
25
  | **InputPhone** | `react-phone-number-input` | `npm install react-phone-number-input` |
26
26
  | **InputMask** | `use-mask-input` | `npm install use-mask-input` |
@@ -31,7 +31,7 @@ Install additional packages based on the components you want to use:
31
31
 
32
32
  ```bash
33
33
  # Install everything at once
34
- npm install @paygreen/pgui react react-dom @chakra-ui/react @emotion/react @emotion/styled framer-motion react-datepicker react-select react-phone-number-input use-mask-input @tanstack/react-table react-icons
34
+ npm install @paygreen/pgui react react-dom @chakra-ui/react @emotion/react @emotion/styled @internationalized/date react-select react-phone-number-input use-mask-input @tanstack/react-table react-icons
35
35
  ```
36
36
 
37
37
  ## 🎨 CSS Setup
@@ -58,6 +58,87 @@ function App() {
58
58
  }
59
59
  ```
60
60
 
61
+ ## 🎞️ Animations
62
+
63
+ pgui exposes a curated set of named `animationStyle` presets so consumer apps stop hardcoding durations, easings and keyframes. Apps pick a semantic name; pgui owns the underlying values.
64
+
65
+ ### Available animationStyles
66
+
67
+ ```tsx
68
+ <Box animationStyle="slideInTop" />
69
+ <Box animationStyle={{ _open: 'slideInTop', _closed: 'slideOutTop' }} />
70
+ ```
71
+
72
+ | Name | Composition |
73
+ | --- | --- |
74
+ | `fadeIn` | `fade-in` + `fast` + `decelerate` |
75
+ | `fadeOut` | `fade-out` + `fast` + `accelerate` |
76
+ | `slideInTop` / `slideInBottom` / `slideInLeft` / `slideInRight` | `slide-from-X, fade-in` + `moderate` + `decelerate` |
77
+ | `slideOutTop` / `slideOutBottom` / `slideOutLeft` / `slideOutRight` | `slide-to-X, fade-out` + `fast` + `accelerate` |
78
+
79
+ Slide distances are Chakra's subtle 0.5rem variants. For panel-sized motion (drawers, full overlays), reach for Chakra's built-in `slide-fade-in/out` (placement-driven) directly.
80
+
81
+ Enter and exit are intentionally asymmetric: `slideIn*` runs at `moderate` (200ms) with `decelerate` so content settles in; `slideOut*` runs at `fast` (150ms) with `accelerate` so dismissals feel snappy. Fade follows the same enter-slower / exit-faster pacing.
82
+
83
+ Override `animationDuration` / `animationTimingFunction` at the call site if a specific case needs a different pacing:
84
+
85
+ ```tsx
86
+ <Box animationStyle="slideInTop" animationDuration="slow" />
87
+ ```
88
+
89
+ ### Internal tokens
90
+
91
+ pgui consumes Chakra v3's default duration scale (`fastest` 50ms → `slowest` 500ms) as-is + one off-scale `durations.spin` (800ms) for continuous Spinner / Loader rotation, and adds 3 design-semantic easings (`standard` / `accelerate` / `decelerate`, Material-inspired) alongside Chakra's CSS-keyword easings. These tokens drive the `animationStyle` presets above and pgui-internal component transitions — they are not exposed as a JS bridge for consumer apps.
92
+
93
+ ### Convention
94
+
95
+ Any duration or easing in pgui or consumer apps should come from a pgui token rather than a hardcoded value (`200ms`, `cubic-bezier(...)`). Reviewed at PR time.
96
+
97
+ ### Enter + exit animations
98
+
99
+ For elements that mount/unmount based on state (dropdowns, error messages, overlays), use Chakra v3's `<Presence>` with conditional `animationName`. This keeps the element mounted during the exit animation, then unmounts on `animationend`. No JS animation lib required.
100
+
101
+ ```tsx
102
+ import { Presence } from '@chakra-ui/react';
103
+
104
+ <Presence
105
+ present={open}
106
+ animationName={{
107
+ _open: 'slide-from-top, fade-in',
108
+ _closed: 'slide-to-top, fade-out',
109
+ }}
110
+ animationDuration="moderate"
111
+ animationTimingFunction="standard"
112
+ >
113
+ {/* content */}
114
+ </Presence>
115
+ ```
116
+
117
+ The `slide-from-X` / `slide-to-X`, `fade-in` / `fade-out` and `scale-in` / `scale-out` keyframes ship with Chakra v3 — combine them comma-separated for compound entry/exit.
118
+
119
+ ### Tokens consumed by pgui components
120
+
121
+ For reference when migrating an app: the components below already pull from the tokens above.
122
+
123
+ | Component | Tokens consumed |
124
+ | --- | --- |
125
+ | `ActionsButton` | `durations.faster`, `easings.standard` |
126
+ | `CardExpandable` (card root) | `durations.moderate` |
127
+ | `CardExpandable` (header hover) | `durations.moderate` |
128
+ | `CardExpandable` (chevron) | `durations.fast`, `easings.standard` |
129
+ | `DataList` (rows) | `durations.moderate` |
130
+ | `DataTable` (row wrapper) | `durations.moderate` |
131
+ | `DatePicker` (calendar enter/exit) | `scale-fade-in` / `scale-fade-out` via the Chakra v3 `datePicker` slot recipe defaults |
132
+ | `FieldWrapper` (error message enter/exit) | `<Presence>` + `slide-from-top, fade-in` / `slide-to-top, fade-out` + `durations.fast` |
133
+ | `InputPhone` (country dropdown enter/exit) | `<Presence>` + `slide-from-top, fade-in` / `slide-to-top, fade-out` + `durations.moderate` |
134
+ | `InputPhone` (option list item) | `durations.faster`, `easings.standard` |
135
+ | `Loader` | `durations.spin` |
136
+ | `Pagination` (page text hover) | `durations.moderate` |
137
+ | `Sidebar` (slide open/close) | `durations.slow` (configurable), `easings.standard` |
138
+ | `Sidebar` (nav item hover) | `durations.moderate`, `easings.standard` |
139
+
140
+ See the `Foundations / Animations` story in Storybook for live previews.
141
+
61
142
  ## 🔧 Development & Testing
62
143
 
63
144
  ### Why YALC?
@@ -159,14 +240,6 @@ import '@paygreen/pgui/src/styles/index.css';
159
240
 
160
241
  If you get an error about missing packages, install the required dependency for the component you're using (see table above).
161
242
 
162
- ### Source Map Warnings
163
-
164
- If you see warnings about react-datepicker source maps, add this to your `.env` file:
165
-
166
- ```bash
167
- GENERATE_SOURCEMAP=false
168
- ```
169
-
170
243
  ## 📋 Requirements
171
244
 
172
245
  - **React** ^18.0.0 || ^19.0.0
@@ -1,85 +1,91 @@
1
+ import { ReactNode } from 'react';
2
+ import { DatePickerLocale } from './utils';
3
+ export { formatDateRange, formatDateValue, fromDateValue, localeToBcp47, parseDateInput, toCalendarDate, toCalendarDateTime, } from './utils';
4
+ export type { DatePickerLocale } from './utils';
1
5
  /**
2
- * Props for the DatePicker component
6
+ * Preset values recognized natively by Ark UI's date-picker state
7
+ * machine (mirrors `@zag-js/date-utils`' `DateRangePreset`).
3
8
  */
4
- export interface DatePickerProps {
5
- /** ID attribute for the input element */
6
- id?: string;
7
- /** Name attribute for form integration */
8
- name?: string;
9
- /** The selected date or date range */
10
- value?: Date | [Date | null, Date | null] | null;
11
- /** Label text displayed above the input */
12
- label?: string;
13
- /** Callback fired when the date selection changes */
14
- onChange?: (date: Date | [Date | null, Date | null] | null) => void;
15
- /** Whether to enable date range selection */
16
- isRange?: boolean;
17
- /** Whether to enable time selection */
18
- isDateTime?: boolean;
19
- /** Custom placeholder text for the input (overrides default format placeholder) */
9
+ export type DatePickerPresetValue = 'thisWeek' | 'lastWeek' | 'thisMonth' | 'lastMonth' | 'thisQuarter' | 'lastQuarter' | 'thisYear' | 'lastYear' | 'last3Days' | 'last7Days' | 'last14Days' | 'last30Days' | 'last90Days';
10
+ /** A quick-select preset rendered as a button beside the calendar. */
11
+ export interface DatePickerPreset {
12
+ value: DatePickerPresetValue;
13
+ label: string;
14
+ }
15
+ interface CommonProps {
16
+ /**
17
+ * Standard field label rendered above the inputs via `<ArkDatePicker.Label>`
18
+ * (Chakra v3 convention). Use this for the canonical "label above field"
19
+ * pattern. Can coexist with `inputLabel`.
20
+ */
21
+ label?: ReactNode;
22
+ /**
23
+ * Floating label rendered on the top border of the input area, matching
24
+ * the pgui `<Input>` / `<InputMask>` visual pattern.
25
+ *
26
+ * Distinct from `label`: `label` sits *above* the field (Chakra default),
27
+ * `inputLabel` sits *on* the field's border (pgui floating style). Both
28
+ * may be used together if needed.
29
+ */
30
+ inputLabel?: ReactNode;
31
+ /** Placeholder text shown when no date is selected */
20
32
  placeholder?: string;
21
- /** Locale string for date formatting and translations ('en', 'fr') */
22
- locale?: 'en' | 'fr';
23
- /** Minimum selectable date */
24
- minDate?: Date;
25
- /** Maximum selectable date */
26
- maxDate?: Date;
27
- /** Custom date format string */
28
- dateFormat?: string;
29
- /** Callback fired when the input loses focus */
33
+ /** Locale for month/weekday names and date formatting */
34
+ locale?: DatePickerLocale;
35
+ /** Earliest selectable date */
36
+ minDate?: Date | null;
37
+ /** Latest selectable date */
38
+ maxDate?: Date | null;
39
+ /** Fired when the input loses focus */
30
40
  onBlur?: () => void;
31
- /** Whether the input is disabled */
41
+ /** Disable the whole picker */
32
42
  disabled?: boolean;
33
- /** Whether to show a clear button when there's a value (default: true) */
43
+ /** Mark the inputs as required (HTML form validation) */
44
+ required?: boolean;
45
+ /** Show a clear button when a value is set (default: true) */
34
46
  clearable?: boolean;
35
- /** Whether the input is in invalid state */
47
+ /** Form field name (in range mode, `${name}.start` / `${name}.end`) */
48
+ name?: string;
49
+ /** Marks the inputs as invalid (`aria-invalid` propagated) */
36
50
  invalid?: boolean;
51
+ /**
52
+ * Optional quick-select presets rendered as a sidebar inside the
53
+ * popper. When omitted, the calendar is shown without a sidebar.
54
+ */
55
+ presets?: DatePickerPreset[];
56
+ }
57
+ interface SingleProps extends CommonProps {
58
+ isRange?: false;
59
+ /**
60
+ * Add a time input below the day calendar (selection emits a `Date`
61
+ * with both date and time set). Only available in single mode.
62
+ */
63
+ withTime?: boolean;
64
+ value?: Date | null;
65
+ onChange?: (date: Date | null) => void;
37
66
  }
67
+ interface RangeProps extends CommonProps {
68
+ isRange: true;
69
+ value?: [Date | null, Date | null] | null;
70
+ onChange?: (range: [Date | null, Date | null] | null) => void;
71
+ }
72
+ export type DatePickerProps = SingleProps | RangeProps;
38
73
  /**
39
- * A reusable date picker component with floating label and range selection support
40
- *
41
- * @example
42
- * ```tsx
43
- * // Default behavior - shows format as placeholder (mm/dd/yyyy)
44
- * <DatePicker
45
- * label="Birthdate"
46
- * value={date}
47
- * onChange={setDate}
48
- * />
49
- *
50
- * // French locale - shows French format (jj/mm/aaaa)
51
- * <DatePicker
52
- * label="Date de naissance"
53
- * value={date}
54
- * onChange={setDate}
55
- * locale="fr"
56
- * />
57
- *
58
- * // With custom placeholder (overrides default format)
59
- * <DatePicker
60
- * label="Birthdate"
61
- * value={date}
62
- * onChange={setDate}
63
- * placeholderText="Choose your birthdate"
64
- * />
74
+ * `DatePicker` is the pgui facade over Chakra v3's compound
75
+ * `ArkDatePicker.*` namespace (Ark UI / @zag-js/date-picker). It exposes a
76
+ * single-component API that speaks JS `Date` (compat-friendly with legacy
77
+ * app code) and bundles:
65
78
  *
66
- * // To disable the clear button:
67
- * <DatePicker
68
- * label="Birthdate"
69
- * value={date}
70
- * onChange={setDate}
71
- * clearable={false}
72
- * />
79
+ * - locale-aware format/parse (`dd/MM/yyyy` FR, `MM/dd/yyyy` EN, tolerant
80
+ * to `/`, `-`, `.`)
81
+ * - 3 views (day / month / year) with the default Chakra header
82
+ * - 1 read-only input in range mode displaying `"DD MMM YYYY - DD MMM YYYY"`;
83
+ * submission via hidden inputs `${name}.start` / `${name}.end` (ISO)
84
+ * - calendar popper inside `<Portal>` (avoids clip by overflow / modals)
85
+ * - conditional Clear trigger + calendar Trigger via `ArkDatePicker.Context`
73
86
  *
74
- * // With name and id for form integration:
75
- * <DatePicker
76
- * id="birthdate-input"
77
- * name="birthdate"
78
- * label="Birthdate"
79
- * value={date}
80
- * onChange={setDate}
81
- * />
82
- * ```
87
+ * For one-off custom layouts, import `DatePicker` directly from
88
+ * `@chakra-ui/react` and assemble the parts yourself.
83
89
  */
84
- declare const DatePicker: import('react').ForwardRefExoticComponent<DatePickerProps & import('react').RefAttributes<HTMLInputElement>>;
90
+ declare const DatePicker: import('react').ForwardRefExoticComponent<DatePickerProps & import('react').RefAttributes<HTMLDivElement>>;
85
91
  export { DatePicker };
@@ -0,0 +1,48 @@
1
+ import { CalendarDate, CalendarDateTime, DateValue } from '@internationalized/date';
2
+ /**
3
+ * Locale strings accepted by `DatePicker`. Internally mapped to
4
+ * BCP-47 via `localeToBcp47`.
5
+ */
6
+ export type DatePickerLocale = 'en' | 'fr';
7
+ /**
8
+ * Convert a JS `Date` (or null/undefined) to an `@internationalized/date`
9
+ * `CalendarDate`. Returns `undefined` for nullish inputs so the value can
10
+ * be passed straight to `<DatePicker.Root min|max>` props.
11
+ */
12
+ export declare const toCalendarDate: (date: Date | null | undefined) => CalendarDate | undefined;
13
+ /**
14
+ * Like `toCalendarDate` but preserves the hour/minute of the input —
15
+ * used by `DatePicker` when `withTime` is enabled.
16
+ */
17
+ export declare const toCalendarDateTime: (date: Date | null | undefined) => CalendarDateTime | undefined;
18
+ /**
19
+ * Convert a `DateValue` back to a local-time-zone JS `Date`. Returns
20
+ * `null` for nullish inputs so consumers can rely on a single sentinel.
21
+ */
22
+ export declare const fromDateValue: (value: DateValue | undefined) => Date | null;
23
+ /**
24
+ * Map the simple `'en' | 'fr'` locale used by pgui to the BCP-47 tag
25
+ * required by `@internationalized/date` and the Chakra DatePicker.
26
+ */
27
+ export declare const localeToBcp47: (locale: DatePickerLocale) => string;
28
+ /**
29
+ * Format a `DateValue` for the editable single-mode input, locale-aware
30
+ * (`dd/MM/yyyy` for FR, `MM/dd/yyyy` for EN). When `withTime`, appends
31
+ * 2-digit `HH:mm`.
32
+ */
33
+ export declare const formatDateValue: (date: DateValue, bcp47Locale: string, withTime: boolean) => string;
34
+ /**
35
+ * Format a `DateValue[]` (range) into a single display string like
36
+ * `"18 Oct 2026 - 18 Nov 2026"` (or French `"18 oct. 2026 - 18 nov. 2026"`).
37
+ * For a partial range with only the start date, returns `"18 Oct 2026 - …"`
38
+ * so the user has a visual cue that the end date is still pending.
39
+ * Returns empty string when no date has been picked.
40
+ */
41
+ export declare const formatDateRange: (values: DateValue[], bcp47Locale: string) => string;
42
+ /**
43
+ * Parse a user-typed date string into a `CalendarDate`. Accepts `/`, `-`,
44
+ * or `.` as separators. The day/month ordering follows the locale:
45
+ * `dd/MM/yyyy` for FR, `MM/dd/yyyy` for EN. Returns `undefined` for any
46
+ * malformed or out-of-range input.
47
+ */
48
+ export declare const parseDateInput: (raw: string, locale: DatePickerLocale) => CalendarDate | undefined;
@@ -15,7 +15,7 @@ export interface LoaderProps extends SpinnerProps {
15
15
  *
16
16
  * @example
17
17
  * ```tsx
18
- * <Loader size="lg" loading={false} />
18
+ * <Loader size="lg" loading={false} />
19
19
  * ```
20
20
  */
21
21
  declare const Loader: {
@@ -14,7 +14,7 @@ export interface SidebarTheme {
14
14
  boxShadow?: string;
15
15
  /** Z-index value */
16
16
  zIndex?: number | string;
17
- /** Transition duration for animations */
17
+ /** Transition duration for animations. Prefer a pgui token name (e.g. `'slow'`); a raw CSS duration is accepted for legacy callers. */
18
18
  transitionDuration?: string;
19
19
  }
20
20
  /**
@@ -1,3 +1,4 @@
1
1
  export { useDataTable } from './use-data-table';
2
2
  export { useDebouncedLoadOptions } from './use-debounced-load-options';
3
3
  export { useHasScrollbar } from './use-has-scrollbar';
4
+ export { useOutsideClick } from './use-outside-click';
@@ -0,0 +1,13 @@
1
+ import { RefObject } from 'react';
2
+ interface UseOutsideClickProps {
3
+ ref: RefObject<HTMLElement | null>;
4
+ handler: (event: Event) => void;
5
+ enabled?: boolean;
6
+ }
7
+ /**
8
+ * Hook that fires a handler when a pointer interaction occurs outside the
9
+ * referenced element. Handles both mouse and touch input, and ignores
10
+ * emulated mouse events that follow a touch sequence.
11
+ */
12
+ export declare function useOutsideClick({ ref, handler, enabled, }: UseOutsideClickProps): void;
13
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './styled-system';
1
2
  export * from './components';
2
3
  export { default as PGUISystemContext } from './theme';
3
4
  export * from './utils';