@axzydev/axzy_ui_system 1.2.0 → 1.2.2

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.
Files changed (202) hide show
  1. package/dist/index.css +82 -197
  2. package/dist/index.css.map +1 -1
  3. package/package.json +2 -2
  4. package/src/App.tsx +354 -0
  5. package/src/assets/logo.png +0 -0
  6. package/src/assets/react.svg +1 -0
  7. package/src/components/alert/alert.props.ts +13 -0
  8. package/src/components/alert/alert.stories.tsx +41 -0
  9. package/src/components/alert/alert.tsx +53 -0
  10. package/src/components/avatar/avatar.props.ts +14 -0
  11. package/src/components/avatar/avatar.stories.tsx +46 -0
  12. package/src/components/avatar/avatar.tsx +53 -0
  13. package/src/components/badget/badget.props.ts +12 -0
  14. package/src/components/badget/badget.stories.tsx +76 -0
  15. package/src/components/badget/badget.tsx +61 -0
  16. package/src/components/breadcrumbs/breadcrumbs.props.ts +13 -0
  17. package/src/components/breadcrumbs/breadcrumbs.stories.tsx +21 -0
  18. package/src/components/breadcrumbs/breadcrumbs.tsx +34 -0
  19. package/src/components/button/button.props.ts +18 -0
  20. package/src/components/button/button.stories.tsx +174 -0
  21. package/src/components/button/button.tsx +117 -0
  22. package/src/components/calendar/calendar.props.ts +33 -0
  23. package/src/components/calendar/calendar.stories.tsx +91 -0
  24. package/src/components/calendar/calendar.tsx +608 -0
  25. package/src/components/calendar/index.ts +3 -0
  26. package/src/components/card/card.props.ts +13 -0
  27. package/src/components/card/card.stories.tsx +58 -0
  28. package/src/components/card/card.tsx +79 -0
  29. package/src/components/checkbox/checkbox.props.ts +11 -0
  30. package/src/components/checkbox/checkbox.stories.tsx +54 -0
  31. package/src/components/checkbox/checkbox.tsx +52 -0
  32. package/src/components/confirm-dialog/confirm-dialog.props.ts +14 -0
  33. package/src/components/confirm-dialog/confirm-dialog.stories.tsx +33 -0
  34. package/src/components/confirm-dialog/confirm-dialog.tsx +45 -0
  35. package/src/components/data-table/ITDataTable.stories.tsx +213 -0
  36. package/src/components/data-table/dataTable.props.ts +69 -0
  37. package/src/components/data-table/dataTable.tsx +313 -0
  38. package/src/components/date-picker/date-picker.props.ts +30 -0
  39. package/src/components/date-picker/date-picker.stories.tsx +90 -0
  40. package/src/components/date-picker/datePicker.tsx +307 -0
  41. package/src/components/dialog/dialog.props.ts +9 -0
  42. package/src/components/dialog/dialog.stories.tsx +80 -0
  43. package/src/components/dialog/dialog.tsx +88 -0
  44. package/src/components/divider/divider.props.ts +8 -0
  45. package/src/components/divider/divider.stories.tsx +34 -0
  46. package/src/components/divider/divider.tsx +21 -0
  47. package/src/components/drawer/drawer.props.ts +14 -0
  48. package/src/components/drawer/drawer.stories.tsx +41 -0
  49. package/src/components/drawer/drawer.tsx +53 -0
  50. package/src/components/dropfile/dropfile.stories.tsx +75 -0
  51. package/src/components/dropfile/dropfile.tsx +407 -0
  52. package/src/components/empty-state/empty-state.props.ts +9 -0
  53. package/src/components/empty-state/empty-state.stories.tsx +20 -0
  54. package/src/components/empty-state/empty-state.tsx +21 -0
  55. package/src/components/flex/flex.props.ts +22 -0
  56. package/src/components/flex/flex.stories.tsx +71 -0
  57. package/src/components/flex/flex.tsx +79 -0
  58. package/src/components/form-builder/fieldRenderer.tsx +218 -0
  59. package/src/components/form-builder/formBuilder.context.tsx +70 -0
  60. package/src/components/form-builder/formBuilder.props.ts +43 -0
  61. package/src/components/form-builder/formBuilder.stories.tsx +317 -0
  62. package/src/components/form-builder/formBuilder.tsx +186 -0
  63. package/src/components/form-builder/useFormBuilder.ts +80 -0
  64. package/src/components/form-header/form-header.props.ts +5 -0
  65. package/src/components/form-header/form-header.tsx +38 -0
  66. package/src/components/grid/grid.props.ts +17 -0
  67. package/src/components/grid/grid.stories.tsx +72 -0
  68. package/src/components/grid/grid.tsx +69 -0
  69. package/src/components/image/image.props.ts +7 -0
  70. package/src/components/image/image.tsx +38 -0
  71. package/src/components/input/input.props.ts +49 -0
  72. package/src/components/input/input.stories.tsx +115 -0
  73. package/src/components/input/input.tsx +615 -0
  74. package/src/components/layout/layout.props.ts +10 -0
  75. package/src/components/layout/layout.stories.tsx +114 -0
  76. package/src/components/layout/layout.tsx +80 -0
  77. package/src/components/loader/loader.props.ts +8 -0
  78. package/src/components/loader/loader.stories.tsx +105 -0
  79. package/src/components/loader/loader.tsx +108 -0
  80. package/src/components/navbar/navbar.props.ts +37 -0
  81. package/src/components/navbar/navbar.tsx +328 -0
  82. package/src/components/page/page.props.ts +19 -0
  83. package/src/components/page/page.stories.tsx +98 -0
  84. package/src/components/page/page.tsx +90 -0
  85. package/src/components/page-header/page-header.props.ts +11 -0
  86. package/src/components/page-header/page-header.stories.tsx +61 -0
  87. package/src/components/page-header/page-header.tsx +62 -0
  88. package/src/components/pagination/pagination.props.ts +53 -0
  89. package/src/components/pagination/pagination.stories.tsx +111 -0
  90. package/src/components/pagination/pagination.tsx +241 -0
  91. package/src/components/popover/popover.props.ts +12 -0
  92. package/src/components/popover/popover.stories.tsx +25 -0
  93. package/src/components/popover/popover.tsx +45 -0
  94. package/src/components/progress/progress.props.ts +12 -0
  95. package/src/components/progress/progress.stories.tsx +40 -0
  96. package/src/components/progress/progress.tsx +52 -0
  97. package/src/components/radio/radio.props.ts +16 -0
  98. package/src/components/radio/radio.stories.tsx +50 -0
  99. package/src/components/radio/radio.tsx +58 -0
  100. package/src/components/search-select/index.ts +2 -0
  101. package/src/components/search-select/search-select.props.ts +46 -0
  102. package/src/components/search-select/search-select.stories.tsx +129 -0
  103. package/src/components/search-select/search-select.tsx +229 -0
  104. package/src/components/searchTable/components/EditableCell.tsx +149 -0
  105. package/src/components/searchTable/components/PaginationControls.tsx +86 -0
  106. package/src/components/searchTable/components/PaginationInfo.tsx +20 -0
  107. package/src/components/searchTable/components/SearchAndSortBar.tsx +53 -0
  108. package/src/components/searchTable/components/SearchInput.tsx +33 -0
  109. package/src/components/searchTable/components/SortButton.tsx +50 -0
  110. package/src/components/searchTable/components/TableEmptyState.tsx +22 -0
  111. package/src/components/searchTable/components/TableHeader.tsx +35 -0
  112. package/src/components/searchTable/components/TableHeaderCell.tsx +43 -0
  113. package/src/components/searchTable/components/TableRow.tsx +144 -0
  114. package/src/components/searchTable/searchTable.props.ts +56 -0
  115. package/src/components/searchTable/searchTable.tsx +187 -0
  116. package/src/components/segmented-control/segmented-control.props.ts +18 -0
  117. package/src/components/segmented-control/segmented-control.stories.tsx +63 -0
  118. package/src/components/segmented-control/segmented-control.tsx +52 -0
  119. package/src/components/select/select.props.ts +25 -0
  120. package/src/components/select/select.stories.tsx +86 -0
  121. package/src/components/select/select.tsx +150 -0
  122. package/src/components/sidebar/sidebar.props.ts +28 -0
  123. package/src/components/sidebar/sidebar.stories.tsx +117 -0
  124. package/src/components/sidebar/sidebar.tsx +313 -0
  125. package/src/components/skeleton/skeleton.props.ts +12 -0
  126. package/src/components/skeleton/skeleton.stories.tsx +30 -0
  127. package/src/components/skeleton/skeleton.tsx +45 -0
  128. package/src/components/slide/slide.props.ts +45 -0
  129. package/src/components/slide/slide.stories.tsx +121 -0
  130. package/src/components/slide/slide.tsx +109 -0
  131. package/src/components/slider/slider.props.ts +10 -0
  132. package/src/components/slider/slider.stories.tsx +30 -0
  133. package/src/components/slider/slider.tsx +49 -0
  134. package/src/components/stack/stack.props.ts +19 -0
  135. package/src/components/stack/stack.stories.tsx +79 -0
  136. package/src/components/stack/stack.tsx +79 -0
  137. package/src/components/stat-card/stat-card.props.ts +13 -0
  138. package/src/components/stat-card/stat-card.stories.tsx +41 -0
  139. package/src/components/stat-card/stat-card.tsx +44 -0
  140. package/src/components/stepper/stepper.css +26 -0
  141. package/src/components/stepper/stepper.props.ts +29 -0
  142. package/src/components/stepper/stepper.stories.tsx +155 -0
  143. package/src/components/stepper/stepper.tsx +227 -0
  144. package/src/components/table/table.props.ts +43 -0
  145. package/src/components/table/table.stories.tsx +189 -0
  146. package/src/components/table/table.tsx +376 -0
  147. package/src/components/tabs/tabs.props.ts +18 -0
  148. package/src/components/tabs/tabs.stories.tsx +32 -0
  149. package/src/components/tabs/tabs.tsx +74 -0
  150. package/src/components/text/text.props.ts +9 -0
  151. package/src/components/text/text.tsx +20 -0
  152. package/src/components/textarea/textarea.props.ts +15 -0
  153. package/src/components/textarea/textarea.stories.tsx +27 -0
  154. package/src/components/textarea/textarea.tsx +55 -0
  155. package/src/components/theme-provider/themeProvider.props.ts +28 -0
  156. package/src/components/theme-provider/themeProvider.tsx +1854 -0
  157. package/src/components/time-picker/timePicker.props.ts +16 -0
  158. package/src/components/time-picker/timePicker.stories.tsx +131 -0
  159. package/src/components/time-picker/timePicker.tsx +317 -0
  160. package/src/components/toast/toast.css +32 -0
  161. package/src/components/toast/toast.props.ts +13 -0
  162. package/src/components/toast/toast.stories.tsx +138 -0
  163. package/src/components/toast/toast.tsx +87 -0
  164. package/src/components/tooltip/tooltip.props.ts +11 -0
  165. package/src/components/tooltip/tooltip.stories.tsx +20 -0
  166. package/src/components/tooltip/tooltip.tsx +55 -0
  167. package/src/components/topbar/topbar.props.ts +21 -0
  168. package/src/components/topbar/topbar.stories.tsx +80 -0
  169. package/src/components/topbar/topbar.tsx +205 -0
  170. package/src/components/triple-filter/tripleFilter.props.ts +15 -0
  171. package/src/components/triple-filter/tripleFilter.stories.tsx +32 -0
  172. package/src/components/triple-filter/tripleFilter.tsx +50 -0
  173. package/src/hooks/useClickOutside.ts +21 -0
  174. package/src/hooks/useDebouncedSearch.ts +55 -0
  175. package/src/hooks/useEditableRow.ts +157 -0
  176. package/src/hooks/useTableState.ts +122 -0
  177. package/src/index.css +168 -0
  178. package/src/index.ts +165 -0
  179. package/src/main.tsx +9 -0
  180. package/src/showcases/DataShowcases.tsx +260 -0
  181. package/src/showcases/FeedbackShowcases.tsx +268 -0
  182. package/src/showcases/FormShowcases.tsx +1159 -0
  183. package/src/showcases/HomeShowcase.tsx +324 -0
  184. package/src/showcases/LayoutPrimitivesShowcases.tsx +569 -0
  185. package/src/showcases/NavigationShowcases.tsx +193 -0
  186. package/src/showcases/PageShowcases.tsx +207 -0
  187. package/src/showcases/ShowcaseLayout.tsx +139 -0
  188. package/src/showcases/StructureShowcases.tsx +152 -0
  189. package/src/types/badget.types.ts +37 -0
  190. package/src/types/button.types.ts +16 -0
  191. package/src/types/colors.types.ts +3 -0
  192. package/src/types/field.types.ts +103 -0
  193. package/src/types/formik.types.ts +15 -0
  194. package/src/types/input.types.ts +14 -0
  195. package/src/types/loader.types.ts +9 -0
  196. package/src/types/sizes.types.ts +1 -0
  197. package/src/types/table.types.ts +15 -0
  198. package/src/types/toast.types.ts +8 -0
  199. package/src/types/yup.types.ts +11 -0
  200. package/src/utils/color.utils.ts +99 -0
  201. package/src/utils/styles.ts +120 -0
  202. package/src/utils/table.utils.ts +10 -0
@@ -0,0 +1,53 @@
1
+ export interface ITPaginationProps {
2
+ /**
3
+ * Current active page (1-indexed).
4
+ */
5
+ currentPage: number;
6
+
7
+ /**
8
+ * Total number of pages available.
9
+ */
10
+ totalPages: number;
11
+
12
+ /**
13
+ * Callback fired when a page is clicked or next/prev is activated.
14
+ */
15
+ onPageChange: (page: number) => void;
16
+
17
+ /**
18
+ * Number of visible pages before and after the current page.
19
+ * Default: 1
20
+ */
21
+ siblingCount?: number;
22
+
23
+ /**
24
+ * Semantic color from the theme (primary, secondary, success, danger, warning, info, purple).
25
+ * Default: primary
26
+ */
27
+ color?: string;
28
+
29
+ /**
30
+ * Additional CSS classes for the container.
31
+ */
32
+ className?: string;
33
+
34
+ /**
35
+ * Options for items per page selector.
36
+ */
37
+ itemsPerPageOptions?: number[];
38
+
39
+ /**
40
+ * Current items per page value. Required if itemsPerPageOptions is provided.
41
+ */
42
+ itemsPerPage?: number;
43
+
44
+ /**
45
+ * Callback fired when items per page is changed.
46
+ */
47
+ onItemsPerPageChange?: (value: number) => void;
48
+
49
+ /**
50
+ * Total number of items across all pages. Used to render "1-10 of 50" text.
51
+ */
52
+ totalItems?: number;
53
+ }
@@ -0,0 +1,111 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import ITPagination from "./pagination";
3
+ import { useState } from "react";
4
+
5
+ const meta: Meta<typeof ITPagination> = {
6
+ title: "Components/Data Display/ITPagination",
7
+ component: ITPagination,
8
+ parameters: {
9
+ layout: "centered",
10
+ },
11
+ tags: ["autodocs"],
12
+ argTypes: {
13
+ currentPage: { control: "number" },
14
+ totalPages: { control: "number" },
15
+ siblingCount: { control: "number" },
16
+ color: {
17
+ control: "select",
18
+ options: ["primary", "secondary", "success", "danger", "warning", "info", "purple"],
19
+ },
20
+ },
21
+ };
22
+
23
+ export default meta;
24
+ type Story = StoryObj<typeof ITPagination>;
25
+
26
+ const PaginationWrapper = (args: any) => {
27
+ const [currentPage, setCurrentPage] = useState(args.currentPage || 1);
28
+
29
+ return (
30
+ <ITPagination
31
+ {...args}
32
+ currentPage={currentPage}
33
+ onPageChange={(page) => setCurrentPage(page)}
34
+ />
35
+ );
36
+ };
37
+
38
+ export const Default: Story = {
39
+ render: (args) => <PaginationWrapper {...args} />,
40
+ args: {
41
+ currentPage: 1,
42
+ totalPages: 5,
43
+ color: "primary",
44
+ },
45
+ };
46
+
47
+ export const ManyPages: Story = {
48
+ render: (args) => <PaginationWrapper {...args} />,
49
+ args: {
50
+ currentPage: 1,
51
+ totalPages: 50,
52
+ siblingCount: 1,
53
+ color: "primary",
54
+ },
55
+ };
56
+
57
+ export const MiddlePageOpened: Story = {
58
+ render: (args) => <PaginationWrapper {...args} />,
59
+ args: {
60
+ currentPage: 25,
61
+ totalPages: 50,
62
+ siblingCount: 1,
63
+ color: "primary",
64
+ },
65
+ };
66
+
67
+ export const WithItemsPerPage: Story = {
68
+ render: (args) => {
69
+ const [currentPage, setCurrentPage] = useState(1);
70
+ const [itemsPerPage, setItemsPerPage] = useState(10);
71
+ const totalItems = 500;
72
+ const totalPages = Math.ceil(totalItems / itemsPerPage);
73
+
74
+ return (
75
+ <ITPagination
76
+ {...args}
77
+ currentPage={currentPage}
78
+ totalPages={totalPages}
79
+ onPageChange={(page) => setCurrentPage(page)}
80
+ itemsPerPageOptions={[10, 20, 30, 50]}
81
+ itemsPerPage={itemsPerPage}
82
+ onItemsPerPageChange={(val) => {
83
+ setItemsPerPage(val);
84
+ setCurrentPage(1);
85
+ }}
86
+ totalItems={totalItems}
87
+ />
88
+ );
89
+ },
90
+ args: {
91
+ color: "primary",
92
+ className: "w-[600px]" // Make it wide enough to see the layout
93
+ },
94
+ };
95
+
96
+ export const Colors: Story = {
97
+ render: (args) => (
98
+ <div className="flex flex-col gap-4">
99
+ {(['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'purple'] as const).map((color) => (
100
+ <div key={color} className="flex gap-4 items-center">
101
+ <span className="w-20 text-sm font-bold text-gray-500 capitalize">{color}</span>
102
+ <PaginationWrapper {...args} color={color} />
103
+ </div>
104
+ ))}
105
+ </div>
106
+ ),
107
+ args: {
108
+ currentPage: 1,
109
+ totalPages: 5,
110
+ },
111
+ };
@@ -0,0 +1,241 @@
1
+ import React from "react";
2
+ import clsx from "clsx";
3
+ import { FaChevronLeft, FaChevronRight } from "react-icons/fa";
4
+ import { ITPaginationProps } from "./pagination.props";
5
+ import { theme } from "@/theme/theme";
6
+ import ITSelect from "../select/select";
7
+ import ITText from "@/components/text/text";
8
+
9
+ const DOTS = "...";
10
+
11
+ // Helper hook to calculate page ranges
12
+ const usePagination = ({
13
+ totalPages,
14
+ currentPage,
15
+ siblingCount = 1,
16
+ }: {
17
+ totalPages: number;
18
+ currentPage: number;
19
+ siblingCount?: number;
20
+ }) => {
21
+ return React.useMemo(() => {
22
+ // Pages count is determined as siblingCount + firstPage + lastPage + currentPage + 2*DOTS
23
+ const totalPageNumbers = siblingCount + 5;
24
+
25
+ /*
26
+ Case 1:
27
+ If the number of pages is less than the page numbers we want to show in our
28
+ paginationComponent, we return the range [1..totalPageCount]
29
+ */
30
+ if (totalPageNumbers >= totalPages) {
31
+ return Array.from({ length: totalPages }, (_, i) => i + 1);
32
+ }
33
+
34
+ const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
35
+ const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);
36
+
37
+ const shouldShowLeftDots = leftSiblingIndex > 2;
38
+ const shouldShowRightDots = rightSiblingIndex < totalPages - 2;
39
+
40
+ const firstPageIndex = 1;
41
+ const lastPageIndex = totalPages;
42
+
43
+ /*
44
+ Case 2: No left dots to show, but rights dots to be shown
45
+ */
46
+ if (!shouldShowLeftDots && shouldShowRightDots) {
47
+ let leftItemCount = 3 + 2 * siblingCount;
48
+ let leftRange = Array.from({ length: leftItemCount }, (_, i) => i + 1);
49
+ return [...leftRange, DOTS, totalPages];
50
+ }
51
+
52
+ /*
53
+ Case 3: No right dots to show, but left dots to be shown
54
+ */
55
+ if (shouldShowLeftDots && !shouldShowRightDots) {
56
+ let rightItemCount = 3 + 2 * siblingCount;
57
+ let rightRange = Array.from(
58
+ { length: rightItemCount },
59
+ (_, i) => totalPages - rightItemCount + i + 1
60
+ );
61
+ return [firstPageIndex, DOTS, ...rightRange];
62
+ }
63
+
64
+ /*
65
+ Case 4: Both left and right dots to be shown
66
+ */
67
+ if (shouldShowLeftDots && shouldShowRightDots) {
68
+ let middleRange = Array.from(
69
+ { length: rightSiblingIndex - leftSiblingIndex + 1 },
70
+ (_, i) => leftSiblingIndex + i
71
+ );
72
+ return [firstPageIndex, DOTS, ...middleRange, DOTS, lastPageIndex];
73
+ }
74
+ }, [totalPages, currentPage, siblingCount]);
75
+ };
76
+
77
+ export default function ITPagination({
78
+ currentPage,
79
+ totalPages,
80
+ onPageChange,
81
+ siblingCount = 1,
82
+ color = "primary",
83
+ className = "",
84
+ itemsPerPageOptions,
85
+ itemsPerPage,
86
+ onItemsPerPageChange,
87
+ totalItems,
88
+ }: ITPaginationProps) {
89
+ const paginationRange = usePagination({
90
+ currentPage,
91
+ totalPages,
92
+ siblingCount,
93
+ });
94
+
95
+ // If there are less than 2 pages and no items per page options, we can hide the component
96
+ if (currentPage === 0 || (!itemsPerPageOptions && paginationRange && paginationRange.length < 2)) {
97
+ return null;
98
+ }
99
+
100
+ // Resolve color
101
+ const isSemantic = color in theme.colors;
102
+ const resolvedBgColor = isSemantic
103
+ ? theme.colors[color as keyof typeof theme.colors][500]
104
+ : color;
105
+ const resolvedHoverBgColor = isSemantic
106
+ ? theme.colors[color as keyof typeof theme.colors][50]
107
+ : "#f3f4f6"; // fallback generic gray hover
108
+
109
+ const handleNext = () => {
110
+ if (currentPage < totalPages) {
111
+ onPageChange(currentPage + 1);
112
+ }
113
+ };
114
+
115
+ const handlePrevious = () => {
116
+ if (currentPage > 1) {
117
+ onPageChange(currentPage - 1);
118
+ }
119
+ };
120
+
121
+ // Base styles for list items
122
+ const baseItemClass =
123
+ "flex items-center justify-center w-8 h-8 rounded-full text-sm font-medium transition-colors cursor-pointer select-none";
124
+
125
+ const renderPaginationControls = () => (
126
+ <div className="flex items-center gap-1">
127
+ {/* Previous Button */}
128
+ <div
129
+ className={clsx(
130
+ baseItemClass,
131
+ currentPage === 1
132
+ ? "text-gray-300 cursor-not-allowed"
133
+ : "text-gray-500 hover:bg-gray-100"
134
+ )}
135
+ onClick={handlePrevious}
136
+ aria-disabled={currentPage === 1}
137
+ >
138
+ <FaChevronLeft size={12} />
139
+ </div>
140
+
141
+ {/* Pages */}
142
+ {paginationRange?.map((pageNumber, idx) => {
143
+ if (pageNumber === DOTS) {
144
+ return (
145
+ <ITText
146
+ as="div"
147
+ key={`dots-${idx}`}
148
+ className="flex items-center justify-center w-8 h-8 select-none text-gray-400"
149
+ >
150
+ &#8230;
151
+ </ITText>
152
+ );
153
+ }
154
+
155
+ const isActive = pageNumber === currentPage;
156
+
157
+ return (
158
+ <ITText
159
+ as="div"
160
+ key={pageNumber}
161
+ className={clsx(
162
+ baseItemClass,
163
+ isActive ? "text-white" : "text-gray-600 hover:bg-gray-100"
164
+ )}
165
+ style={{
166
+ backgroundColor: isActive ? resolvedBgColor : undefined,
167
+ ...(isActive ? {} : { "--hover-bg": resolvedHoverBgColor } as React.CSSProperties),
168
+ }}
169
+ onClick={() => onPageChange(pageNumber as number)}
170
+ title={`Page ${pageNumber}`}
171
+ >
172
+ <ITText as="span">{pageNumber}</ITText>
173
+ </ITText>
174
+ );
175
+ })}
176
+
177
+ {/* Next Button */}
178
+ <div
179
+ className={clsx(
180
+ baseItemClass,
181
+ currentPage === totalPages
182
+ ? "text-gray-300 cursor-not-allowed"
183
+ : "text-gray-500 hover:bg-gray-100"
184
+ )}
185
+ onClick={handleNext}
186
+ aria-disabled={currentPage === totalPages}
187
+ >
188
+ <FaChevronRight size={12} />
189
+ </div>
190
+ </div>
191
+ );
192
+
193
+ // If itemsPerPageOptions is provided, wrap in a wider container with the select
194
+ if (itemsPerPageOptions && itemsPerPage && onItemsPerPageChange) {
195
+ const startItem = Math.min((currentPage - 1) * itemsPerPage + 1, totalItems || 0);
196
+ const endItem = Math.min(currentPage * itemsPerPage, totalItems || 0);
197
+
198
+ return (
199
+ <div className={clsx("flex flex-col sm:flex-row justify-between items-center gap-4 w-full", className)}>
200
+ <div className="flex items-center gap-4 text-sm text-gray-500">
201
+ <div className="flex items-center gap-2 bg-gray-50 px-3 py-1 rounded-lg border border-gray-200">
202
+ <ITText as="span" className="text-xs font-medium">Mostrar</ITText>
203
+ <ITSelect
204
+ name="itemsPerPage"
205
+ options={itemsPerPageOptions.map((option) => ({
206
+ value: String(option),
207
+ label: String(option),
208
+ }))}
209
+ value={String(itemsPerPage)}
210
+ onChange={(e) => onItemsPerPageChange(Number(e.target.value))}
211
+ onBlur={() => {}}
212
+ size="small"
213
+ className="!w-14 !h-6 !text-xs !py-0 !px-1! !border-none !bg-transparent !ring-0 focus:!ring-0 cursor-pointer font-bold text-gray-700"
214
+ placeholder=""
215
+ />
216
+ </div>
217
+
218
+ {totalItems !== undefined && (
219
+ <>
220
+ <ITText as="span" className="text-gray-300">|</ITText>
221
+ <ITText as="span" className="text-xs">
222
+ <ITText as="span" className="font-semibold text-gray-700">{startItem}</ITText><ITText as="span"> - </ITText><ITText as="span" className="font-semibold text-gray-700">{endItem}</ITText><ITText as="span"> de </ITText><ITText as="span" className="font-semibold text-gray-900">{totalItems}</ITText>
223
+ </ITText>
224
+ </>
225
+ )}
226
+ </div>
227
+
228
+ <nav aria-label="Pagination">
229
+ {renderPaginationControls()}
230
+ </nav>
231
+ </div>
232
+ );
233
+ }
234
+
235
+ // Otherwise, render just the standard pagination component
236
+ return (
237
+ <nav aria-label="Pagination" className={clsx("inline-flex", className)}>
238
+ {renderPaginationControls()}
239
+ </nav>
240
+ );
241
+ }
@@ -0,0 +1,12 @@
1
+ import { ReactNode } from "react";
2
+
3
+ export type PopoverPosition = "top" | "bottom" | "left" | "right";
4
+
5
+ export interface ITPopoverProps {
6
+ trigger: ReactNode;
7
+ children: ReactNode;
8
+ position?: PopoverPosition;
9
+ isOpen?: boolean;
10
+ onClose?: () => void;
11
+ className?: string;
12
+ }
@@ -0,0 +1,25 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import ITPopover from "./popover";
3
+ import ITButton from "../button/button";
4
+ import ITStack from "../stack/stack";
5
+
6
+ const meta: Meta<typeof ITPopover> = {
7
+ title: "Components/Overlay/ITPopover",
8
+ component: ITPopover,
9
+ tags: ["autodocs"],
10
+ };
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof ITPopover>;
14
+
15
+ export const Default: Story = {
16
+ render: () => (
17
+ <ITPopover trigger={<ITButton label="Abrir Popover" />}>
18
+ <ITStack spacing={2}>
19
+ <p className="text-sm font-semibold">Opciones</p>
20
+ <button className="text-sm text-left text-slate-600 hover:text-slate-800">Editar</button>
21
+ <button className="text-sm text-left text-slate-600 hover:text-slate-800">Eliminar</button>
22
+ </ITStack>
23
+ </ITPopover>
24
+ ),
25
+ };
@@ -0,0 +1,45 @@
1
+ import { useState, useRef } from "react";
2
+ import clsx from "clsx";
3
+ import { ITPopoverProps, PopoverPosition } from "./popover.props";
4
+ import useClickOutside from "@/hooks/useClickOutside";
5
+
6
+ const positionClasses: Record<PopoverPosition, string> = {
7
+ top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
8
+ bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
9
+ left: "right-full top-1/2 -translate-y-1/2 mr-2",
10
+ right: "left-full top-1/2 -translate-y-1/2 ml-2",
11
+ };
12
+
13
+ export default function ITPopover({
14
+ trigger,
15
+ children,
16
+ position = "bottom",
17
+ isOpen: controlledOpen,
18
+ onClose,
19
+ className,
20
+ }: ITPopoverProps) {
21
+ const [internalOpen, setInternalOpen] = useState(false);
22
+ const isControlled = controlledOpen !== undefined;
23
+ const open = isControlled ? controlledOpen : internalOpen;
24
+ const ref = useRef<HTMLDivElement>(null);
25
+
26
+ useClickOutside(ref, () => {
27
+ if (isControlled) onClose?.();
28
+ else setInternalOpen(false);
29
+ });
30
+
31
+ return (
32
+ <div ref={ref} className={clsx("relative inline-flex", className)}>
33
+ <div onClick={() => (isControlled ? onClose?.() : setInternalOpen((p) => !p))} className="cursor-pointer">
34
+ {trigger}
35
+ </div>
36
+ {open && (
37
+ <div className={clsx("absolute z-[200]", positionClasses[position])}>
38
+ <div className="bg-white dark:bg-slate-800 rounded-xl shadow-xl border border-slate-200 dark:border-slate-700 p-3 min-w-[160px]">
39
+ {children}
40
+ </div>
41
+ </div>
42
+ )}
43
+ </div>
44
+ );
45
+ }
@@ -0,0 +1,12 @@
1
+ import { CSSProperties } from "react";
2
+ import { ColorsTypes } from "@/types/colors.types";
3
+
4
+ export interface ITProgressProps {
5
+ value?: number;
6
+ max?: number;
7
+ variant?: "determinate" | "indeterminate";
8
+ color?: ColorsTypes;
9
+ size?: "sm" | "md" | "lg";
10
+ className?: string;
11
+ style?: CSSProperties;
12
+ }
@@ -0,0 +1,40 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import ITProgress from "./progress";
4
+ import ITButton from "../button/button";
5
+
6
+ const meta: Meta<typeof ITProgress> = {
7
+ title: "Components/Feedback/ITProgress",
8
+ component: ITProgress,
9
+ tags: ["autodocs"],
10
+ };
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof ITProgress>;
14
+
15
+ export const Determinate: Story = {
16
+ render: () => {
17
+ const [val, setVal] = useState(0);
18
+ return (
19
+ <div className="space-y-3">
20
+ <ITProgress value={val} />
21
+ <ITButton label="+10%" onClick={() => setVal(v => Math.min(v + 10, 100))} />
22
+ </div>
23
+ );
24
+ },
25
+ };
26
+
27
+ export const Indeterminate: Story = {
28
+ args: { variant: "indeterminate" },
29
+ };
30
+
31
+ export const Colors: Story = {
32
+ render: () => (
33
+ <div className="space-y-3">
34
+ <ITProgress value={70} color="primary" />
35
+ <ITProgress value={70} color="success" />
36
+ <ITProgress value={70} color="danger" />
37
+ <ITProgress value={70} color="warning" />
38
+ </div>
39
+ ),
40
+ };
@@ -0,0 +1,52 @@
1
+ import clsx from "clsx";
2
+ import { ITProgressProps } from "./progress.props";
3
+ import { ColorsTypes } from "@/types/colors.types";
4
+
5
+ const colorMap: Record<ColorsTypes, string> = {
6
+ primary: "bg-primary-500",
7
+ secondary: "bg-secondary-500",
8
+ success: "bg-success-500",
9
+ danger: "bg-danger-500",
10
+ warning: "bg-warning-500",
11
+ info: "bg-info-500",
12
+ purple: "bg-purple-500",
13
+ error: "bg-danger-500",
14
+ gray: "bg-secondary-500",
15
+ };
16
+
17
+ const sizeMap = {
18
+ sm: "h-1",
19
+ md: "h-2",
20
+ lg: "h-3",
21
+ };
22
+
23
+ export default function ITProgress({
24
+ value = 0,
25
+ max = 100,
26
+ variant = "determinate",
27
+ color = "primary",
28
+ size = "md",
29
+ className,
30
+ style,
31
+ }: ITProgressProps) {
32
+ const pct = Math.min(Math.max((value / max) * 100, 0), 100);
33
+
34
+ return (
35
+ <div
36
+ className={clsx("w-full bg-slate-200 dark:bg-slate-700 rounded-full overflow-hidden", sizeMap[size], className)}
37
+ style={style}
38
+ role="progressbar"
39
+ aria-valuenow={variant === "determinate" ? value : undefined}
40
+ aria-valuemax={max}
41
+ >
42
+ <div
43
+ className={clsx(
44
+ "h-full rounded-full transition-all duration-500",
45
+ colorMap[color],
46
+ variant === "indeterminate" && "animate-pulse w-1/2"
47
+ )}
48
+ style={variant === "determinate" ? { width: `${pct}%` } : undefined}
49
+ />
50
+ </div>
51
+ );
52
+ }
@@ -0,0 +1,16 @@
1
+ import { ReactNode } from "react";
2
+
3
+ export interface ITRadioOption {
4
+ value: string;
5
+ label: ReactNode;
6
+ }
7
+
8
+ export interface ITRadioGroupProps {
9
+ name: string;
10
+ value: string;
11
+ onChange: (value: string) => void;
12
+ options: ITRadioOption[];
13
+ disabled?: boolean;
14
+ direction?: "row" | "column";
15
+ className?: string;
16
+ }
@@ -0,0 +1,50 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import ITRadioGroup from "./radio";
4
+ import ITStack from "../stack/stack";
5
+
6
+ const meta: Meta<typeof ITRadioGroup> = {
7
+ title: "Components/Inputs/ITRadioGroup",
8
+ component: ITRadioGroup,
9
+ tags: ["autodocs"],
10
+ };
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof ITRadioGroup>;
14
+
15
+ export const Default: Story = {
16
+ render: () => {
17
+ const [val, setVal] = useState("option1");
18
+ return (
19
+ <ITRadioGroup
20
+ name="example"
21
+ value={val}
22
+ onChange={setVal}
23
+ options={[
24
+ { value: "option1", label: "Opción 1" },
25
+ { value: "option2", label: "Opción 2" },
26
+ { value: "option3", label: "Opción 3" },
27
+ ]}
28
+ />
29
+ );
30
+ },
31
+ };
32
+
33
+ export const Row: Story = {
34
+ render: () => {
35
+ const [val, setVal] = useState("sm");
36
+ return (
37
+ <ITRadioGroup
38
+ name="size"
39
+ value={val}
40
+ onChange={setVal}
41
+ direction="row"
42
+ options={[
43
+ { value: "sm", label: "Chico" },
44
+ { value: "md", label: "Mediano" },
45
+ { value: "lg", label: "Grande" },
46
+ ]}
47
+ />
48
+ );
49
+ },
50
+ };