@aurora-ds/components 0.17.17 → 0.18.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
@@ -62,6 +62,7 @@ function App() {
62
62
  | **Menu** | Dropdown menu component positioned absolutely |
63
63
  | **Page** | Page layout component |
64
64
  | **PageSection** | Section within a page |
65
+ | **Pagination** | Pagination component for navigating between pages |
65
66
  | **Separator** | Visual separator component |
66
67
  | **Select** | Dropdown select component for single item selection |
67
68
  | **DatePicker** | Date picker component with calendar popup |
@@ -22,6 +22,7 @@ export * from '@components/overlay/modal';
22
22
  export * from '@components/navigation/drawer-item';
23
23
  export * from '@components/navigation/breadcrumb';
24
24
  export * from '@components/navigation/tabs';
25
+ export * from '@components/navigation/pagination';
25
26
  export type { ButtonVariants, ButtonVariantStyle } from '@interfaces/button.types';
26
27
  export type { TextVariants, TextVariantStyle } from '@interfaces/text.types';
27
28
  export type { ChipVariant, ChipColor, ChipSize } from '@interfaces/chip.types';
@@ -0,0 +1,6 @@
1
+ import { FC } from 'react';
2
+ import { PaginationProps } from '@components/navigation/pagination/Pagination.props';
3
+ /**
4
+ * Pagination component for navigating between pages
5
+ */
6
+ export declare const Pagination: FC<PaginationProps>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Props for the Pagination component
3
+ */
4
+ export type PaginationProps = {
5
+ /** Current page number (1-indexed) */
6
+ currentPage: number;
7
+ /** Total number of pages */
8
+ totalPages: number;
9
+ /** Callback when page changes */
10
+ onPageChange: (page: number) => void;
11
+ /** Callback when clicking the previous button */
12
+ onPrevious?: (page: number) => void;
13
+ /** Callback when clicking the next button */
14
+ onNext?: (page: number) => void;
15
+ /** Maximum number of page buttons to display */
16
+ maxVisiblePages?: number;
17
+ /** Accessibility label for the pagination */
18
+ ariaLabel?: string;
19
+ };
20
+ export type PaginationStyleParams = {
21
+ disabled?: boolean;
22
+ };
@@ -0,0 +1,7 @@
1
+ import { PaginationStyleParams } from '@components/navigation/pagination/Pagination.props';
2
+ export declare const PAGINATION_STYLES: {
3
+ root: string;
4
+ pageButton: (args_0: PaginationStyleParams) => string;
5
+ activePageButton: string;
6
+ navigationButton: (args_0: PaginationStyleParams) => string;
7
+ };
@@ -0,0 +1,2 @@
1
+ export * from '@components/navigation/pagination/Pagination';
2
+ export type { PaginationProps } from '@components/navigation/pagination/Pagination.props';
@@ -1,13 +1,10 @@
1
1
  import { ReactNode } from 'react';
2
+ import { ButtonProps } from '@/components';
2
3
  export type ModalProps = {
3
4
  isOpen: boolean;
4
5
  onClose: () => void;
5
6
  label: string;
6
7
  children: ReactNode;
7
8
  isForm?: boolean;
8
- action?: {
9
- label: string;
10
- onClick: () => void;
11
- disabled?: boolean;
12
- };
9
+ action?: ButtonProps;
13
10
  };
package/dist/cjs/index.js CHANGED
@@ -942,25 +942,29 @@ const CalendarIcon = () => {
942
942
  };
943
943
 
944
944
  const ChevronDownIcon = () => {
945
- return (jsxRuntime.jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '100%', height: '100%', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', "stroke-width": '2', "stroke-linecap": 'round', "stroke-linejoin": 'round', className: 'lucide lucide-chevron-down-icon lucide-chevron-down', children: jsxRuntime.jsx("path", { d: 'm6 9 6 6 6-6' }) }));
945
+ return (jsxRuntime.jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '100%', height: '100%', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: 'lucide lucide-chevron-down-icon lucide-chevron-down', children: jsxRuntime.jsx("path", { d: 'm6 9 6 6 6-6' }) }));
946
+ };
947
+
948
+ const ChevronLeftIcon = () => {
949
+ return (jsxRuntime.jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: 'lucide lucide-chevron-left-icon lucide-chevron-left', children: jsxRuntime.jsx("path", { d: 'm15 18-6-6 6-6' }) }));
946
950
  };
947
951
 
948
952
  const ChevronRightIcon = () => {
949
- return (jsxRuntime.jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', "stroke-width": '2', "stroke-linecap": 'round', "stroke-linejoin": 'round', className: 'lucide lucide-chevron-right-icon lucide-chevron-right', children: jsxRuntime.jsx("path", { d: 'm9 18 6-6-6-6' }) }));
953
+ return (jsxRuntime.jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: 'lucide lucide-chevron-right-icon lucide-chevron-right', children: jsxRuntime.jsx("path", { d: 'm9 18 6-6-6-6' }) }));
950
954
  };
951
955
 
952
956
  const CloseIcon = () => (jsxRuntime.jsx("svg", { width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg', children: jsxRuntime.jsx("path", { d: 'M12 4L4 12M4 4l8 8', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' }) }));
953
957
 
954
958
  const EyeIcon = () => {
955
- return (jsxRuntime.jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', "stroke-width": '2', "stroke-linecap": 'round', "stroke-linejoin": 'round', className: 'lucide lucide-eye-icon lucide-eye', children: [jsxRuntime.jsx("path", { d: 'M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0' }), jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '3' })] }));
959
+ return (jsxRuntime.jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: 'lucide lucide-eye-icon lucide-eye', children: [jsxRuntime.jsx("path", { d: 'M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0' }), jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '3' })] }));
956
960
  };
957
961
 
958
962
  const EyeOffIcon = () => {
959
- return (jsxRuntime.jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', "stroke-width": '2', "stroke-linecap": 'round', "stroke-linejoin": 'round', className: 'lucide lucide-eye-off-icon lucide-eye-off', children: [jsxRuntime.jsx("path", { d: 'M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49' }), jsxRuntime.jsx("path", { d: 'M14.084 14.158a3 3 0 0 1-4.242-4.242' }), jsxRuntime.jsx("path", { d: 'M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143' }), jsxRuntime.jsx("path", { d: 'm2 2 20 20' })] }));
963
+ return (jsxRuntime.jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: 'lucide lucide-eye-off-icon lucide-eye-off', children: [jsxRuntime.jsx("path", { d: 'M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49' }), jsxRuntime.jsx("path", { d: 'M14.084 14.158a3 3 0 0 1-4.242-4.242' }), jsxRuntime.jsx("path", { d: 'M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143' }), jsxRuntime.jsx("path", { d: 'm2 2 20 20' })] }));
960
964
  };
961
965
 
962
966
  const MoreHorizontalIcon = () => {
963
- return (jsxRuntime.jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', "stroke-width": '2', "stroke-linecap": 'round', "stroke-linejoin": 'round', className: 'lucide lucide-ellipsis-icon lucide-ellipsis', children: [jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '1' }), jsxRuntime.jsx("circle", { cx: '19', cy: '12', r: '1' }), jsxRuntime.jsx("circle", { cx: '5', cy: '12', r: '1' })] }));
967
+ return (jsxRuntime.jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: 'lucide lucide-ellipsis-icon lucide-ellipsis', children: [jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '1' }), jsxRuntime.jsx("circle", { cx: '19', cy: '12', r: '1' }), jsxRuntime.jsx("circle", { cx: '5', cy: '12', r: '1' })] }));
964
968
  };
965
969
 
966
970
  /**
@@ -2176,15 +2180,16 @@ const Modal = ({ isOpen, onClose, label, children, isForm, action }) => {
2176
2180
  document.addEventListener('keydown', handleKeyDown);
2177
2181
  return () => document.removeEventListener('keydown', handleKeyDown);
2178
2182
  }, [isOpen, onClose]);
2179
- // components
2180
- const Wrapper = isForm ? Form$1 : React.Fragment;
2181
- const wrapperProps = isForm ? {
2182
- onSubmit: (e) => {
2183
- e.preventDefault();
2184
- action?.onClick();
2183
+ // actions
2184
+ const safeInvokeAction = (e) => {
2185
+ if (action && typeof action.onClick === 'function') {
2186
+ // Cast e to required MouseEvent type (or undefined) and call
2187
+ action.onClick(e);
2185
2188
  }
2186
- } : {};
2187
- return reactDom.createPortal(jsxRuntime.jsx(React.Fragment, { children: isVisible ? (jsxRuntime.jsx("div", { className: MODAL_STYLES.background(isFadingIn), ref: modalRef, children: jsxRuntime.jsx("div", { className: MODAL_STYLES.content(isFadingIn), children: jsxRuntime.jsxs(Wrapper, { ...wrapperProps, children: [jsxRuntime.jsxs(Stack, { justify: 'space-between', height: BUTTON_SIZE, width: '100%', children: [jsxRuntime.jsx(Text, { variant: 'h3', children: label }), !action && (jsxRuntime.jsx(IconButton, { icon: jsxRuntime.jsx(CloseIcon, {}), onClick: onClose, size: 'small', variant: 'text', textColor: 'text' }))] }), children, action && (jsxRuntime.jsxs(Stack, { justify: 'flex-end', width: '100%', children: [jsxRuntime.jsx(Button, { label: 'Cancel', onClick: onClose, variant: 'outlined' }), jsxRuntime.jsx(Button, { label: action.label, onClick: !isForm ? action.onClick : undefined, type: isForm ? 'submit' : 'button', disabled: action.disabled })] }))] }) }) })) : null }), document.body);
2189
+ };
2190
+ // body of the modal
2191
+ const body = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs(Stack, { justify: 'space-between', height: BUTTON_SIZE, width: '100%', children: [jsxRuntime.jsx(Text, { variant: 'h3', children: label }), !action && (jsxRuntime.jsx(IconButton, { icon: jsxRuntime.jsx(CloseIcon, {}), onClick: onClose, size: 'small', variant: 'text', textColor: 'text' }))] }), children, action && (jsxRuntime.jsxs(Stack, { justify: 'flex-end', width: '100%', children: [jsxRuntime.jsx(Button, { label: 'Cancel', onClick: onClose, variant: 'outlined' }), jsxRuntime.jsx(Button, { label: action.label, onClick: !isForm ? action.onClick : undefined, type: isForm ? 'submit' : 'button', disabled: action.disabled })] }))] }));
2192
+ return reactDom.createPortal(jsxRuntime.jsx(React.Fragment, { children: isVisible ? (jsxRuntime.jsx("div", { className: MODAL_STYLES.background(isFadingIn), ref: modalRef, children: jsxRuntime.jsx("div", { className: MODAL_STYLES.content(isFadingIn), children: isForm ? (jsxRuntime.jsx(Form$1, { onSubmit: (e) => { e.preventDefault(); safeInvokeAction(); }, children: body })) : (jsxRuntime.jsx(React.Fragment, { children: body })) }) })) : null }), document.body);
2188
2193
  };
2189
2194
  Modal.displayName = 'Modal';
2190
2195
 
@@ -2500,6 +2505,135 @@ const TabItem = ({ label, isActive = false, onClick, startIcon, endIcon }) => {
2500
2505
  };
2501
2506
  TabItem.displayName = 'TabItem';
2502
2507
 
2508
+ const PAGINATION_STYLES = theme.createStyles((theme) => ({
2509
+ root: {
2510
+ display: 'flex',
2511
+ alignItems: 'center',
2512
+ gap: theme.spacing.xs,
2513
+ },
2514
+ pageButton: ({ disabled = false }) => ({
2515
+ display: 'inline-flex',
2516
+ alignItems: 'center',
2517
+ justifyContent: 'center',
2518
+ boxSizing: 'border-box',
2519
+ padding: theme.spacing.sm,
2520
+ borderRadius: theme.radius.md,
2521
+ cursor: disabled ? 'not-allowed' : 'pointer',
2522
+ transition: `background-color ${theme.transition.fast}, color ${theme.transition.fast}`,
2523
+ minHeight: '40px',
2524
+ maxHeight: '40px',
2525
+ minWidth: '40px',
2526
+ maxWidth: '40px',
2527
+ fontFamily: 'inherit',
2528
+ fontSize: theme.fontSize.md,
2529
+ fontWeight: theme.fontWeight.medium,
2530
+ backgroundColor: 'transparent',
2531
+ color: theme.colors.text,
2532
+ border: 'none',
2533
+ opacity: disabled ? 0.4 : 1,
2534
+ ':hover': disabled ? {} : {
2535
+ backgroundColor: theme.colors.surfaceHover,
2536
+ },
2537
+ ':active': disabled ? {} : {
2538
+ backgroundColor: theme.colors.surfaceActive,
2539
+ },
2540
+ }),
2541
+ activePageButton: {
2542
+ backgroundColor: theme.colors.primary,
2543
+ color: theme.colors.onPrimary,
2544
+ ':hover': {
2545
+ backgroundColor: theme.colors.primary,
2546
+ },
2547
+ ':active': {
2548
+ backgroundColor: theme.colors.primary,
2549
+ },
2550
+ },
2551
+ navigationButton: ({ disabled = false }) => ({
2552
+ display: 'inline-flex',
2553
+ alignItems: 'center',
2554
+ justifyContent: 'center',
2555
+ boxSizing: 'border-box',
2556
+ padding: theme.spacing.sm,
2557
+ borderRadius: theme.radius.md,
2558
+ cursor: disabled ? 'not-allowed' : 'pointer',
2559
+ transition: `background-color ${theme.transition.fast}, color ${theme.transition.fast}`,
2560
+ minHeight: '40px',
2561
+ maxHeight: '40px',
2562
+ minWidth: '40px',
2563
+ maxWidth: '40px',
2564
+ fontFamily: 'inherit',
2565
+ backgroundColor: 'transparent',
2566
+ color: theme.colors.text,
2567
+ border: 'none',
2568
+ opacity: disabled ? 0.4 : 1,
2569
+ ':hover': disabled ? {} : {
2570
+ backgroundColor: theme.colors.surfaceHover,
2571
+ },
2572
+ ':active': disabled ? {} : {
2573
+ backgroundColor: theme.colors.surfaceActive,
2574
+ },
2575
+ }),
2576
+ }));
2577
+
2578
+ /**
2579
+ * Pagination component for navigating between pages
2580
+ */
2581
+ const Pagination = ({ currentPage, totalPages, onPageChange, onPrevious, onNext, maxVisiblePages = 7, ariaLabel = 'Pagination' }) => {
2582
+ const styles = PAGINATION_STYLES;
2583
+ // Calculate which page numbers to display
2584
+ const visiblePages = React.useMemo(() => {
2585
+ const pages = [];
2586
+ if (totalPages <= maxVisiblePages) {
2587
+ // Show all pages if they fit
2588
+ for (let i = 1; i <= totalPages; i++) {
2589
+ pages.push(i);
2590
+ }
2591
+ }
2592
+ else {
2593
+ // Calculate range around current page
2594
+ const halfVisible = Math.floor(maxVisiblePages / 2);
2595
+ const start = Math.max(1, currentPage - halfVisible);
2596
+ const end = Math.min(totalPages, start + maxVisiblePages - 1);
2597
+ // Adjust start if we're near the end
2598
+ const adjustedStart = end === totalPages ? Math.max(1, end - maxVisiblePages + 1) : start;
2599
+ for (let i = adjustedStart; i <= end; i++) {
2600
+ pages.push(i);
2601
+ }
2602
+ }
2603
+ return pages;
2604
+ }, [currentPage, totalPages, maxVisiblePages]);
2605
+ const handlePrevious = () => {
2606
+ if (currentPage > 1) {
2607
+ const newPage = currentPage - 1;
2608
+ if (onPrevious) {
2609
+ onPrevious(newPage);
2610
+ }
2611
+ onPageChange(newPage);
2612
+ }
2613
+ };
2614
+ const handleNext = () => {
2615
+ if (currentPage < totalPages) {
2616
+ const newPage = currentPage + 1;
2617
+ if (onNext) {
2618
+ onNext(newPage);
2619
+ }
2620
+ onPageChange(newPage);
2621
+ }
2622
+ };
2623
+ const handlePageClick = (page) => {
2624
+ if (page !== currentPage) {
2625
+ onPageChange(page);
2626
+ }
2627
+ };
2628
+ const isPreviousDisabled = currentPage === 1;
2629
+ const isNextDisabled = currentPage === totalPages;
2630
+ return (jsxRuntime.jsxs("nav", { "aria-label": ariaLabel, className: styles.root, children: [jsxRuntime.jsx("button", { type: 'button', onClick: handlePrevious, disabled: isPreviousDisabled, "aria-label": 'Page précédente', className: styles.navigationButton({ disabled: isPreviousDisabled }), children: jsxRuntime.jsx(ChevronLeftIcon, {}) }), visiblePages.map((page) => {
2631
+ const isActive = page === currentPage;
2632
+ return (jsxRuntime.jsx("button", { type: 'button', onClick: () => handlePageClick(page), "aria-label": `Page ${page}`, "aria-current": isActive ? 'page' : undefined, className: `${styles.pageButton({ disabled: false })} ${isActive ? styles.activePageButton : ''}`, children: page }, page));
2633
+ }), jsxRuntime.jsx("button", { type: 'button', onClick: handleNext, disabled: isNextDisabled, "aria-label": 'Page suivante', className: styles.navigationButton({ disabled: isNextDisabled }), children: jsxRuntime.jsx(ChevronRightIcon, {}) })] }));
2634
+ };
2635
+ Pagination.displayName = 'Pagination';
2636
+
2503
2637
  exports.Accordion = Accordion;
2504
2638
  exports.Avatar = Avatar;
2505
2639
  exports.AvatarGroup = AvatarGroup;
@@ -2524,6 +2658,7 @@ exports.MenuItem = MenuItem;
2524
2658
  exports.Modal = Modal;
2525
2659
  exports.Page = Page;
2526
2660
  exports.PageSection = PageSection;
2661
+ exports.Pagination = Pagination;
2527
2662
  exports.Select = Select;
2528
2663
  exports.Separator = Separator;
2529
2664
  exports.Skeleton = Skeleton;