blixify-ui-web-mcp 0.1.1

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 (245) hide show
  1. package/README.md +5 -0
  2. package/lib/components/action/button/README.md +75 -0
  3. package/lib/components/action/button/button.stories.tsx +138 -0
  4. package/lib/components/action/buttonGroup/README.md +79 -0
  5. package/lib/components/action/buttonGroup/buttonGroup.stories.tsx +104 -0
  6. package/lib/components/action/iconButton/README.md +39 -0
  7. package/lib/components/action/iconButton/iconButton.stories.tsx +22 -0
  8. package/lib/components/action/radioList/README.md +98 -0
  9. package/lib/components/action/radioList/radioList.stories.tsx +181 -0
  10. package/lib/components/action/slider/README.md +86 -0
  11. package/lib/components/action/slider/slider.stories.tsx +133 -0
  12. package/lib/components/action/textButton/README.md +34 -0
  13. package/lib/components/action/textButton/textButton.stories.tsx +16 -0
  14. package/lib/components/action/toggle/README.md +39 -0
  15. package/lib/components/action/toggle/toggle.stories.tsx +23 -0
  16. package/lib/components/advanced/collapse/README.md +106 -0
  17. package/lib/components/advanced/collapse/collapse.stories.tsx +165 -0
  18. package/lib/components/advanced/infoWindow/README.md +69 -0
  19. package/lib/components/advanced/infoWindow/infoWindow.stories.tsx +112 -0
  20. package/lib/components/advanced/popUp/README.md +95 -0
  21. package/lib/components/advanced/popUp/popUp.stories.tsx +189 -0
  22. package/lib/components/auth/bareSignIn/README.md +78 -0
  23. package/lib/components/auth/bareSignIn/bareSignIn.stories.tsx +141 -0
  24. package/lib/components/auth/bareSignUp/README.md +130 -0
  25. package/lib/components/auth/bareSignUp/bareSignUp.stories.tsx +159 -0
  26. package/lib/components/auth/signIn/README.md +53 -0
  27. package/lib/components/auth/signIn/signIn.stories.tsx +120 -0
  28. package/lib/components/auth/signUp/README.md +136 -0
  29. package/lib/components/auth/signUp/signUp.stories.tsx +193 -0
  30. package/lib/components/chart/README.md +13 -0
  31. package/lib/components/chart/areaChart/README.md +133 -0
  32. package/lib/components/chart/areaChart/areaChart.stories.tsx +199 -0
  33. package/lib/components/chart/barChart/README.md +193 -0
  34. package/lib/components/chart/barChart/barChart.stories.tsx +318 -0
  35. package/lib/components/chart/boxPlotChart/README.md +124 -0
  36. package/lib/components/chart/boxPlotChart/boxPlotChart.stories.tsx +104 -0
  37. package/lib/components/chart/bubbleChart/README.md +166 -0
  38. package/lib/components/chart/bubbleChart/bubbleChart.stories.tsx +144 -0
  39. package/lib/components/chart/card/README.md +107 -0
  40. package/lib/components/chart/card/card.stories.tsx +89 -0
  41. package/lib/components/chart/ganttChart/README.md +142 -0
  42. package/lib/components/chart/ganttChart/ganttChart.stories.tsx +138 -0
  43. package/lib/components/chart/heatmapChart/README.md +125 -0
  44. package/lib/components/chart/heatmapChart/heatmapChart.stories.tsx +106 -0
  45. package/lib/components/chart/lineChart/README.md +140 -0
  46. package/lib/components/chart/lineChart/lineChart.stories.tsx +152 -0
  47. package/lib/components/chart/pieChart/README.md +117 -0
  48. package/lib/components/chart/pieChart/pieChart.stories.tsx +180 -0
  49. package/lib/components/chart/polarAreaChart/README.md +126 -0
  50. package/lib/components/chart/polarAreaChart/polarAreaChart.stories.tsx +106 -0
  51. package/lib/components/chart/radarChart/README.md +57 -0
  52. package/lib/components/chart/radarChart/radarChart.stories.tsx +36 -0
  53. package/lib/components/chart/radialBar/README.md +152 -0
  54. package/lib/components/chart/radialBar/radialChart.stories.tsx +136 -0
  55. package/lib/components/chart/rangeBarChart/README.md +323 -0
  56. package/lib/components/chart/rangeBarChart/rangeBarChart.stories.tsx +208 -0
  57. package/lib/components/chart/statsCard/README.md +97 -0
  58. package/lib/components/chart/statsCard/statsCard.stories.tsx +183 -0
  59. package/lib/components/chart/treemapChart/README.md +142 -0
  60. package/lib/components/chart/treemapChart/treemapChart.stories.tsx +122 -0
  61. package/lib/components/chat/chatAnchor/README.md +113 -0
  62. package/lib/components/chat/chatAnchor/chatAnchor.stories.tsx +156 -0
  63. package/lib/components/chat/chatBase/README.md +223 -0
  64. package/lib/components/chat/chatBase/chatBase.stories.tsx +306 -0
  65. package/lib/components/chat/chatDashboard/README.md +161 -0
  66. package/lib/components/chat/chatDashboard/chatDashboard.stories.tsx +154 -0
  67. package/lib/components/data/dataGraph/README.md +13 -0
  68. package/lib/components/data/dataGraph/dataGraph.stories.tsx +84 -0
  69. package/lib/components/data/dataReference/README.md +62 -0
  70. package/lib/components/data/dataReference/dataReference.stories.tsx +53 -0
  71. package/lib/components/data/dataTemplate/README.md +903 -0
  72. package/lib/components/data/dataTemplate/dataModule.stories.tsx +196 -0
  73. package/lib/components/data/dataTemplate/dataTemplate.stories.tsx +337 -0
  74. package/lib/components/data/dataWrapper/README.md +1077 -0
  75. package/lib/components/data/dataWrapper/dataWrapper.stories.tsx +831 -0
  76. package/lib/components/data/queryForm/README.md +56 -0
  77. package/lib/components/data/queryForm/queryForm.stories.tsx +85 -0
  78. package/lib/components/data/readQuery/README.md +282 -0
  79. package/lib/components/data/readQuery/readQuery.stories.tsx +256 -0
  80. package/lib/components/data/readQueryHoc/README.md +89 -0
  81. package/lib/components/data/readQueryHoc/readQueryHoc.stories.tsx +33 -0
  82. package/lib/components/data/writeQuery/README.md +178 -0
  83. package/lib/components/data/writeQuery/writeQuery.stories.tsx +209 -0
  84. package/lib/components/design/actionSection/README.md +47 -0
  85. package/lib/components/design/actionSection/actionSection.stories.tsx +74 -0
  86. package/lib/components/design/banner/README.md +43 -0
  87. package/lib/components/design/banner/banner.stories.tsx +34 -0
  88. package/lib/components/design/bannerAction/README.md +44 -0
  89. package/lib/components/design/bannerAction/bannerAction.stories.tsx +25 -0
  90. package/lib/components/design/blogSection/README.md +111 -0
  91. package/lib/components/design/blogSection/blogSection.stories.tsx +72 -0
  92. package/lib/components/design/carouselSection/README.md +82 -0
  93. package/lib/components/design/carouselSection/carouselSection.stories.tsx +52 -0
  94. package/lib/components/design/designList/README.md +60 -0
  95. package/lib/components/design/designList/designList.stories.tsx +44 -0
  96. package/lib/components/design/faq/README.md +121 -0
  97. package/lib/components/design/faq/faq.stories.tsx +103 -0
  98. package/lib/components/design/fileGrid/README.md +87 -0
  99. package/lib/components/design/fileGrid/fileGrid.stories.tsx +110 -0
  100. package/lib/components/design/fileViewer/README.md +40 -0
  101. package/lib/components/design/fileViewer/fileViewer.stories.tsx +28 -0
  102. package/lib/components/design/footerSection/README.md +62 -0
  103. package/lib/components/design/footerSection/footerSection.stories.tsx +84 -0
  104. package/lib/components/design/googleReview/README.md +45 -0
  105. package/lib/components/design/googleReview/googleReview.stories.tsx +33 -0
  106. package/lib/components/design/heroSection/README.md +48 -0
  107. package/lib/components/design/heroSection/heroSection.stories.tsx +85 -0
  108. package/lib/components/design/iconList/README.md +60 -0
  109. package/lib/components/design/iconList/iconList.stories.tsx +41 -0
  110. package/lib/components/design/imageGallery/README.md +56 -0
  111. package/lib/components/design/imageGallery/imageGallery.stories.tsx +45 -0
  112. package/lib/components/design/imageZoom/README.md +47 -0
  113. package/lib/components/design/imageZoom/imageZoom.stories.tsx +30 -0
  114. package/lib/components/design/leftContent/README.md +47 -0
  115. package/lib/components/design/leftContent/leftContent.stories.tsx +32 -0
  116. package/lib/components/design/logoCloud/README.md +42 -0
  117. package/lib/components/design/logoCloud/logoCloud.stories.tsx +44 -0
  118. package/lib/components/design/onboarding/README.md +100 -0
  119. package/lib/components/design/onboarding/onboarding.stories.tsx +113 -0
  120. package/lib/components/design/pricing/README.md +112 -0
  121. package/lib/components/design/pricing/pricing.stories.tsx +86 -0
  122. package/lib/components/design/rightContent/README.md +47 -0
  123. package/lib/components/design/rightContent/rightContent.stories.tsx +32 -0
  124. package/lib/components/design/sectionList/README.md +51 -0
  125. package/lib/components/design/sectionList/sectionList.stories.tsx +35 -0
  126. package/lib/components/design/teams/README.md +62 -0
  127. package/lib/components/design/teams/teams.stories.tsx +61 -0
  128. package/lib/components/display/alert/README.md +82 -0
  129. package/lib/components/display/alert/alert.stories.tsx +71 -0
  130. package/lib/components/display/calendar/README.md +113 -0
  131. package/lib/components/display/calendar/calendar.stories.tsx +159 -0
  132. package/lib/components/display/clipboard/README.md +36 -0
  133. package/lib/components/display/clipboard/clipboard.stories.tsx +32 -0
  134. package/lib/components/display/detailList/README.md +61 -0
  135. package/lib/components/display/detailList/detailList.stories.tsx +131 -0
  136. package/lib/components/display/drawer/README.md +74 -0
  137. package/lib/components/display/drawer/drawer.stories.tsx +66 -0
  138. package/lib/components/display/emptyState/README.md +38 -0
  139. package/lib/components/display/emptyState/emptyState.stories.tsx +22 -0
  140. package/lib/components/display/feeds/README.md +80 -0
  141. package/lib/components/display/feeds/feeds.stories.tsx +75 -0
  142. package/lib/components/display/flexTable/README.md +160 -0
  143. package/lib/components/display/flexTable/flexTable.stories.tsx +313 -0
  144. package/lib/components/display/grid/README.md +121 -0
  145. package/lib/components/display/grid/grid.stories.tsx +234 -0
  146. package/lib/components/display/kanban/README.md +78 -0
  147. package/lib/components/display/kanban/kanban.stories.tsx +133 -0
  148. package/lib/components/display/list/README.md +109 -0
  149. package/lib/components/display/list/list.stories.tsx +188 -0
  150. package/lib/components/display/loading/README.md +25 -0
  151. package/lib/components/display/loading/loading.stories.tsx +15 -0
  152. package/lib/components/display/mapView/README.md +99 -0
  153. package/lib/components/display/mapView/mapView.stories.tsx +225 -0
  154. package/lib/components/display/modal/README.md +43 -0
  155. package/lib/components/display/modal/modal.stories.tsx +81 -0
  156. package/lib/components/display/notification/README.md +43 -0
  157. package/lib/components/display/notification/notification.stories.tsx +30 -0
  158. package/lib/components/display/progress/README.md +25 -0
  159. package/lib/components/display/progress/progress.stories.tsx +15 -0
  160. package/lib/components/display/stepper/README.md +96 -0
  161. package/lib/components/display/stepper/stepper.stories.tsx +175 -0
  162. package/lib/components/display/table/README.md +176 -0
  163. package/lib/components/display/table/table.stories.tsx +330 -0
  164. package/lib/components/display/tag/README.md +74 -0
  165. package/lib/components/display/tag/tag.stories.tsx +79 -0
  166. package/lib/components/input/address/README.md +132 -0
  167. package/lib/components/input/address/address.stories.tsx +147 -0
  168. package/lib/components/input/canvas/README.md +131 -0
  169. package/lib/components/input/canvas/canvas.stories.tsx +165 -0
  170. package/lib/components/input/checkbox/README.md +61 -0
  171. package/lib/components/input/checkbox/checkbox.stories.tsx +30 -0
  172. package/lib/components/input/datePicker/README.md +124 -0
  173. package/lib/components/input/datePicker/datePicker.stories.tsx +197 -0
  174. package/lib/components/input/form/README.md +55 -0
  175. package/lib/components/input/form/form.stories.tsx +143 -0
  176. package/lib/components/input/markdown/README.md +146 -0
  177. package/lib/components/input/markdown/markdown.stories.tsx +40 -0
  178. package/lib/components/input/monthYearPicker/README.md +69 -0
  179. package/lib/components/input/monthYearPicker/monthYearPicker.stories.tsx +47 -0
  180. package/lib/components/input/netPromoterScore/README.md +52 -0
  181. package/lib/components/input/netPromoterScore/netPromoterScore.stories.tsx +30 -0
  182. package/lib/components/input/rating/README.md +51 -0
  183. package/lib/components/input/rating/rating.stories.tsx +30 -0
  184. package/lib/components/input/select/README.md +108 -0
  185. package/lib/components/input/select/select.stories.tsx +286 -0
  186. package/lib/components/input/signature/README.md +106 -0
  187. package/lib/components/input/signature/signature.stories.tsx +188 -0
  188. package/lib/components/input/textArea/README.md +55 -0
  189. package/lib/components/input/textArea/textArea.stories.tsx +30 -0
  190. package/lib/components/input/textInput/README.md +81 -0
  191. package/lib/components/input/textInput/textinput.stories.tsx +231 -0
  192. package/lib/components/input/uploadInput/README.md +134 -0
  193. package/lib/components/input/uploadInput/uploadInput.stories.tsx +193 -0
  194. package/lib/components/navigation/bars/README.md +40 -0
  195. package/lib/components/navigation/bars/bars.stories.tsx +85 -0
  196. package/lib/components/navigation/bottomTab/README.md +53 -0
  197. package/lib/components/navigation/bottomTab/bottomTab.stories.tsx +32 -0
  198. package/lib/components/navigation/breadcrumb/README.md +48 -0
  199. package/lib/components/navigation/breadcrumb/breadcrumb.stories.tsx +27 -0
  200. package/lib/components/navigation/header/README.md +149 -0
  201. package/lib/components/navigation/header/header.stories.tsx +107 -0
  202. package/lib/components/navigation/leftNav/README.md +91 -0
  203. package/lib/components/navigation/leftNav/leftNav.stories.tsx +106 -0
  204. package/lib/components/navigation/tabs/README.md +75 -0
  205. package/lib/components/navigation/tabs/tabs.stories.tsx +60 -0
  206. package/lib/components/structure/column/README.md +40 -0
  207. package/lib/components/structure/column/column.stories.tsx +26 -0
  208. package/lib/components/structure/container/README.md +34 -0
  209. package/lib/components/structure/container/container.stories.tsx +19 -0
  210. package/lib/components/structure/link/README.md +55 -0
  211. package/lib/components/structure/link/link.stories.tsx +30 -0
  212. package/lib/components/structure/text/README.md +42 -0
  213. package/lib/components/structure/text/text.stories.tsx +39 -0
  214. package/lib/components/template/authPage/authPage.stories.tsx +68 -0
  215. package/lib/components/template/landingPage/landingPage.stories.tsx +288 -0
  216. package/lib/components/template/productListing/productListing.stories.tsx +297 -0
  217. package/lib/components/template/productOverview/productOverview.stories.tsx +194 -0
  218. package/lib/components/template/shoppingCart/shoppingCart.stories.tsx +252 -0
  219. package/lib/components/template/userHomePage/userHomePage.stories.tsx +605 -0
  220. package/lib/components/template/userRankingDashboard/userRankingDashboard.stories.tsx +481 -0
  221. package/lib/mcp/server.d.ts +2 -0
  222. package/lib/mcp/server.d.ts.map +1 -0
  223. package/lib/mcp/server.js +33 -0
  224. package/lib/mcp/server.js.map +1 -0
  225. package/lib/mcp/tools/getBackendComponent.d.ts +13 -0
  226. package/lib/mcp/tools/getBackendComponent.d.ts.map +1 -0
  227. package/lib/mcp/tools/getBackendComponent.js +119 -0
  228. package/lib/mcp/tools/getBackendComponent.js.map +1 -0
  229. package/lib/mcp/tools/getComponent.d.ts +17 -0
  230. package/lib/mcp/tools/getComponent.d.ts.map +1 -0
  231. package/lib/mcp/tools/getComponent.js +118 -0
  232. package/lib/mcp/tools/getComponent.js.map +1 -0
  233. package/lib/mcp/tools/listComponents.d.ts +13 -0
  234. package/lib/mcp/tools/listComponents.d.ts.map +1 -0
  235. package/lib/mcp/tools/listComponents.js +113 -0
  236. package/lib/mcp/tools/listComponents.js.map +1 -0
  237. package/lib/mcp/tools/listTemplates.d.ts +13 -0
  238. package/lib/mcp/tools/listTemplates.d.ts.map +1 -0
  239. package/lib/mcp/tools/listTemplates.js +68 -0
  240. package/lib/mcp/tools/listTemplates.js.map +1 -0
  241. package/lib/mcp/tools/suggestTemplate.d.ts +17 -0
  242. package/lib/mcp/tools/suggestTemplate.d.ts.map +1 -0
  243. package/lib/mcp/tools/suggestTemplate.js +212 -0
  244. package/lib/mcp/tools/suggestTemplate.js.map +1 -0
  245. package/package.json +23 -0
@@ -0,0 +1,313 @@
1
+ import { storiesOf } from "@storybook/react";
2
+ import React, { useRef, useState } from "react";
3
+ import { FlexTable, FlexTableItem } from ".";
4
+ import { StorySection } from "../../storyUtils";
5
+ import { Button } from "../../action/button";
6
+ import { DataStructure, FlexMathQuery } from "../../data/dataTemplate/model";
7
+ import { Form } from "../../input/form";
8
+ import README from "./README.md";
9
+
10
+ const stories = storiesOf("Display", module);
11
+
12
+ stories.addParameters({
13
+ readme: {
14
+ content: README,
15
+ },
16
+ });
17
+
18
+ const researchCategories = [
19
+ {
20
+ title: "Test 1",
21
+ description: "Principal investigator and research staff salaries",
22
+ },
23
+ { title: "Test 2", description: "Travel and transportation expenses" },
24
+ { title: "Test 3", description: "Facility and space rental costs" },
25
+ // { title: "Test 4", description: "Laboratory supplies and materials" },
26
+ // { title: "Test 5", description: "Equipment maintenance and calibration" },
27
+ // { title: "Test 6", description: "Consulting and professional services" },
28
+ // {
29
+ // title: "Test 7",
30
+ // description: "Major equipment purchases and capital items",
31
+ // },
32
+ ];
33
+
34
+ const sampleData: FlexTableItem[] = [
35
+ {
36
+ id: "1",
37
+ title: "Principal Investigator Salary",
38
+ category: "Test 1",
39
+ year1: 50000,
40
+ year2: 52000,
41
+ year3: 54000,
42
+ },
43
+ {
44
+ id: "2",
45
+ title: "Research Assistant Salary",
46
+ category: "Test 1",
47
+ year1: 30000,
48
+ year2: 31000,
49
+ year3: 32000,
50
+ },
51
+ {
52
+ id: "3",
53
+ title: "Conference Travel",
54
+ category: "Test 2",
55
+ year1: 5000,
56
+ year2: 5500,
57
+ year3: 6000,
58
+ },
59
+ {
60
+ id: "4",
61
+ title: "Field Work Travel",
62
+ category: "Test 2",
63
+ year1: 8000,
64
+ year2: 8500,
65
+ year3: 9000,
66
+ },
67
+ {
68
+ id: "5",
69
+ title: "Laboratory Rent",
70
+ category: "Test 3",
71
+ year1: 12000,
72
+ year2: 12000,
73
+ year3: 12000,
74
+ },
75
+ {
76
+ id: "6",
77
+ title: "Chemicals and Reagents",
78
+ category: "Test 4",
79
+ year1: 15000,
80
+ year2: 16000,
81
+ year3: 17000,
82
+ },
83
+ {
84
+ id: "7",
85
+ title: "Equipment Calibration",
86
+ category: "Test 5",
87
+ year1: 3000,
88
+ year2: 3200,
89
+ year3: 3400,
90
+ },
91
+ {
92
+ id: "8",
93
+ title: "Statistical Analysis Consultant",
94
+ category: "Test 6",
95
+ year1: 5000,
96
+ year2: 5000,
97
+ year3: 5000,
98
+ },
99
+ {
100
+ id: "9",
101
+ title: "Microscope",
102
+ category: "Test 7",
103
+ year1: 25000,
104
+ year2: 0,
105
+ year3: 0,
106
+ },
107
+ {
108
+ id: "10",
109
+ title: "Computer Workstation",
110
+ category: "Test 7",
111
+ year1: 8000,
112
+ year2: 0,
113
+ year3: 4000,
114
+ },
115
+ ];
116
+
117
+ const baseColumns: DataStructure[] = [
118
+ { id: "year1", name: "Year 1", type: "number" },
119
+ { id: "year2", name: "Year 2", type: "number" },
120
+ { id: "year3", name: "Year 3", type: "number" },
121
+ ];
122
+
123
+ const columnsWithMath: DataStructure[] = [
124
+ ...baseColumns,
125
+ {
126
+ id: "year4",
127
+ name: "Year 4",
128
+ type: "list",
129
+ listSelection: ["Option 1", "Option 2", "Option 3"],
130
+ },
131
+ {
132
+ id: "conditionalYear",
133
+ name: "Conditional Year",
134
+ type: "math",
135
+ mathExpression: "year4 == 'Option 1' ? year2 * 2 + 1 : year3 * 3",
136
+ },
137
+ ];
138
+
139
+ const flexMathQueries: FlexMathQuery[] = [
140
+ { type: "categoryPercentage", targetCategory: "Test 2", maxPercentage: 50 },
141
+ {
142
+ type: "columnPercentage",
143
+ targetColumn: "year1",
144
+ maxPercentage: 70,
145
+ subtotalColumns: ["year2", "year3"],
146
+ },
147
+ ];
148
+
149
+ stories.add("FlexTable", () => {
150
+ const [data, setData] = useState<FlexTableItem[]>(sampleData);
151
+ const [viewOnlyData] = useState<FlexTableItem[]>(sampleData);
152
+
153
+ const formRef = useRef<any>(null);
154
+ const tableRef = useRef<any>(null);
155
+
156
+ return (
157
+ <>
158
+ <StorySection
159
+ title="Default (Editable)"
160
+ description="Basic editable table with category subtotals. Each category row shows a subtotal. onChangeData fires whenever a cell value changes."
161
+ >
162
+ <div className="w-full">
163
+ <FlexTable
164
+ label="Research Budget"
165
+ data={data}
166
+ columns={baseColumns}
167
+ categoryList={researchCategories}
168
+ onChangeData={setData}
169
+ />
170
+ </div>
171
+ </StorySection>
172
+
173
+ <StorySection
174
+ title="With Grand Total"
175
+ description="showGrandTotal=true adds a grand total row summing all numeric columns across all categories."
176
+ >
177
+ <div className="w-full">
178
+ <FlexTable
179
+ label="Research Budget with Grand Total"
180
+ data={data}
181
+ columns={baseColumns}
182
+ categoryList={researchCategories}
183
+ showGrandTotal={true}
184
+ onChangeData={setData}
185
+ />
186
+ </div>
187
+ </StorySection>
188
+
189
+ <StorySection
190
+ title="Without Total Row"
191
+ description="showTotal=false hides the per-category subtotal rows."
192
+ >
193
+ <div className="w-full">
194
+ <FlexTable
195
+ label="Research Budget (No Subtotals)"
196
+ data={data}
197
+ columns={baseColumns}
198
+ categoryList={researchCategories}
199
+ showTotal={false}
200
+ onChangeData={setData}
201
+ />
202
+ </div>
203
+ </StorySection>
204
+
205
+ <StorySection
206
+ title="Math & List Columns"
207
+ description="Columns with type='math' compute values from a mathExpression using other column values. type='list' renders a dropdown. The math column here evaluates conditionally based on the list selection."
208
+ >
209
+ <div className="w-full">
210
+ <FlexTable
211
+ label="Budget with Computed Columns"
212
+ data={data}
213
+ columns={columnsWithMath}
214
+ categoryList={researchCategories}
215
+ onChangeData={setData}
216
+ />
217
+ </div>
218
+ </StorySection>
219
+
220
+ <StorySection
221
+ title="FlexMathQueries Validation"
222
+ description="flexMathQueries adds validation rules. categoryPercentage checks that a category's total does not exceed a percentage of the grand total. columnPercentage checks that one column does not exceed a percentage of the sum of other columns."
223
+ >
224
+ <div className="w-full space-y-3">
225
+ <Button
226
+ text="Submit & Validate"
227
+ size="small"
228
+ type="normal"
229
+ onClick={() => formRef.current?.handleSubmit()}
230
+ />
231
+ <Form ref={formRef} refList={[tableRef]}>
232
+ <FlexTable
233
+ label="Budget with Validation Rules"
234
+ ref={tableRef}
235
+ data={data}
236
+ columns={columnsWithMath}
237
+ categoryList={researchCategories}
238
+ flexMathQueries={flexMathQueries}
239
+ onChangeData={setData}
240
+ onAddColumn={(col) => console.log("column added", col)}
241
+ onRemoveColumn={(key) => console.log("column removed", key)}
242
+ />
243
+ </Form>
244
+ </div>
245
+ </StorySection>
246
+
247
+ <StorySection
248
+ title="Max Rows per Category"
249
+ description="maxRows limits how many rows can be added per category. The add-row button is hidden once the limit is reached."
250
+ >
251
+ <div className="w-full">
252
+ <FlexTable
253
+ label="Budget (Max 2 rows per category)"
254
+ data={data}
255
+ columns={baseColumns}
256
+ categoryList={researchCategories}
257
+ maxRows={2}
258
+ onChangeData={setData}
259
+ />
260
+ </div>
261
+ </StorySection>
262
+
263
+ <StorySection
264
+ title="Hide Category Column"
265
+ description="hideCategory=true removes the category grouping and renders a flat list of rows."
266
+ >
267
+ <div className="w-full">
268
+ <FlexTable
269
+ label="Flat Budget Table"
270
+ data={data}
271
+ columns={baseColumns}
272
+ hideCategory={true}
273
+ onChangeData={setData}
274
+ />
275
+ </div>
276
+ </StorySection>
277
+
278
+ <StorySection
279
+ title="View Only (disabled)"
280
+ description="disabled=true removes all editing controls — no add/remove row, no cell editing, no add column."
281
+ >
282
+ <div className="w-full">
283
+ <FlexTable
284
+ label="Budget (Read Only)"
285
+ data={viewOnlyData}
286
+ columns={baseColumns}
287
+ categoryList={researchCategories}
288
+ showGrandTotal={true}
289
+ disabled={true}
290
+ />
291
+ </div>
292
+ </StorySection>
293
+
294
+ <StorySection
295
+ title="Dark Mode"
296
+ description="Pass darkMode=true and optionally darkModeBg to customise the background."
297
+ dark
298
+ >
299
+ <div className="w-full">
300
+ <FlexTable
301
+ label="Budget (Dark Mode)"
302
+ data={data}
303
+ columns={baseColumns}
304
+ categoryList={researchCategories}
305
+ darkMode={true}
306
+ darkModeBg="bg-gray-900"
307
+ onChangeData={setData}
308
+ />
309
+ </div>
310
+ </StorySection>
311
+ </>
312
+ );
313
+ });
@@ -0,0 +1,121 @@
1
+ # Grid
2
+
3
+ <!-- STORY -->
4
+
5
+ <hr>
6
+
7
+ A card grid with pagination, horizontal scroll, infinity scroll, drag-and-drop, custom card slots, and dark mode support.
8
+
9
+ ##### Import
10
+
11
+ ```js
12
+ import { Grid, GridClass } from "blixify-ui-web/display/grid";
13
+ ```
14
+
15
+ ##### Usage
16
+
17
+ Cursor pagination with horizontal scroll:
18
+
19
+ ```jsx
20
+ const [pageIndex, setPageIndex] = useState(0);
21
+
22
+ <Grid
23
+ data={data}
24
+ limit={5}
25
+ loading={false}
26
+ paginationType="cursor"
27
+ cursor={cursor}
28
+ pageIndex={pageIndex}
29
+ size={data.length}
30
+ itemClassName="shadow-md rounded"
31
+ horizontalScroll={true}
32
+ infinityScroll
33
+ handleUpdatePageIndex={(value) => {
34
+ if (typeof value === "number") setPageIndex(value);
35
+ else setPageIndex((p) => (value === "next" ? p + 1 : p - 1));
36
+ }}
37
+ />;
38
+ ```
39
+
40
+ Page pagination with custom column layout:
41
+
42
+ ```jsx
43
+ <Grid
44
+ data={data}
45
+ limit={5}
46
+ loading={false}
47
+ paginationType="page"
48
+ size={data.length}
49
+ pageIndex={pageIndex}
50
+ gridColClassName="sm:grid-cols-2 sm:gap-x-6 lg:grid-cols-3"
51
+ handleUpdatePageIndex={handlePagination}
52
+ />
53
+ ```
54
+
55
+ Custom ribbon on card image:
56
+
57
+ ```jsx
58
+ <Grid
59
+ data={data}
60
+ loading={false}
61
+ renderImgRibbon={(id) => {
62
+ if (id === "1")
63
+ return (
64
+ <div className="absolute top-5 -left-9 w-[50%] bg-yellow-500 text-white text-xs font-bold p-2 text-center transform -rotate-45">
65
+ WINNER
66
+ </div>
67
+ );
68
+ return null;
69
+ }}
70
+ />
71
+ ```
72
+
73
+ ##### Required props
74
+
75
+ | Name | Type | Description |
76
+ | --------- | ------------- | ------------------------------------- |
77
+ | `data` | `GridClass[]` | Array of card data items |
78
+ | `loading` | `boolean` | Shows a loading indicator when `true` |
79
+
80
+ ##### Optional props
81
+
82
+ | Name | Type | Default | Description |
83
+ | ----------------------- | ------------------------------------------ | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
84
+ | `paginationType` | `"cursor" \| "page"` | — | Enables pagination. `"cursor"` for token-based, `"page"` for numbered pages |
85
+ | `limit` | `number` | `5` | Cards per page. Required when `paginationType` is set |
86
+ | `pageIndex` | `number` | — | Current page index (0-based). Required when `paginationType` is set |
87
+ | `cursor` | `any` | — | Pass a truthy value while more pages exist; falsy on the last page. Required when `paginationType="cursor"` |
88
+ | `size` | `number` | — | Total record count. Required when `paginationType="page"` to calculate page count |
89
+ | `hidePagination` | `boolean` | `false` | Hide the pagination bar |
90
+ | `horizontalScroll` | `boolean` | `false` | Render cards in a horizontally scrollable row instead of a grid |
91
+ | `infinityScroll` | `boolean` | `false` | Auto-load the next page when the user scrolls to the end. Requires `horizontalScroll=true` and `paginationType="cursor"` |
92
+ | `gridColClassName` | `string` | `"sm:grid-cols-2 sm:gap-x-6 lg:grid-cols-3 lg:gap-x-8"` | Tailwind grid column classes |
93
+ | `className` | `string` | — | Tailwind classes for the outer container |
94
+ | `itemClassName` | `string` | — | Tailwind classes applied to each card, e.g. `"shadow-md rounded"` |
95
+ | `selectedItemClassName` | `string` | — | Tailwind classes applied to cards whose id is in `selectedItemsId` |
96
+ | `selectedItemsId` | `string[]` | — | IDs of cards to highlight with `selectedItemClassName` |
97
+ | `customGridItems` | `{ [id: string]: ReactElement }` | — | Replace a specific card with custom JSX. The key must match the item's `id` in `data` |
98
+ | `imgContain` | `boolean` | `false` | Use `object-contain` instead of `object-cover` for card images |
99
+ | `darkMode` | `boolean` | `false` | Enable dark mode card styling |
100
+ | `darkModeBg` | `string` | — | Tailwind bg class for dark mode cards, e.g. `"bg-gray-800"` |
101
+ | `newTabUrl` | `string` | — | Prepends a link to each card. The card `id` is appended to this URL |
102
+ | `addNewTabUrl` | `string` | — | URL appended to the add-card action link |
103
+ | `draggable` | `boolean` | `false` | Enable drag-and-drop reordering of cards |
104
+ | `droppable` | `boolean` | `false` | Allow cards to be dropped onto other cards |
105
+ | `renderImgRibbon` | `(id: string) => ReactElement \| null` | — | Return a ReactElement to overlay on the card image (e.g. a ribbon badge), or `null` for no ribbon |
106
+ | `onClickData` | `(id: string) => void` | — | Called with the card id when a card is clicked |
107
+ | `onDropData` | `(dragId: string, dropId: string) => void` | — | Called when a card is dropped onto another card |
108
+ | `handleUpdatePageIndex` | `(value: string \| number) => void` | — | Called with `"next"`, `"prev"`, or a page number when the user navigates |
109
+
110
+ ##### GridClass
111
+
112
+ | Name | Type | Required | Description |
113
+ | -------------- | ------------------------ | -------- | --------------------------------------------------- |
114
+ | `id` | `string` | yes | Unique card identifier |
115
+ | `title` | `string` | yes | Card heading |
116
+ | `description` | `string \| ReactElement` | yes | Card body text or custom JSX |
117
+ | `tags` | `string[]` | no | Tag badges shown on the card |
118
+ | `imageUrl` | `string` | no | URL for the card image |
119
+ | `imageElement` | `ReactElement` | no | Custom image element — overrides `imageUrl` |
120
+ | `ribbonText` | `string` | no | Built-in ribbon badge text shown on the card image |
121
+ | `extra` | `any` | no | Additional data passed through for custom rendering |
@@ -0,0 +1,234 @@
1
+ import { storiesOf } from "@storybook/react";
2
+ import React, { useState } from "react";
3
+ import { Grid, GridClass } from ".";
4
+ import { StorySection } from "../../storyUtils";
5
+ import README from "./README.md";
6
+
7
+ const stories = storiesOf("Display", module);
8
+
9
+ stories.addParameters({
10
+ readme: {
11
+ content: README,
12
+ },
13
+ });
14
+
15
+ const data: GridClass[] = [
16
+ {
17
+ id: "1",
18
+ title: "Nike Court Vision",
19
+ description: "Nike Men's Court Vision Low Next Nature Shoes - Black",
20
+ tags: ["Sports"],
21
+ imageUrl: "https://cf.shopee.com.my/file/fef57a9b0d40a0626def6fdc993c442a",
22
+ },
23
+ {
24
+ id: "2",
25
+ title: "Nike Air Max 2X",
26
+ description: "Nike Men's Air Max 2X Shoes - White",
27
+ tags: ["Sports", "Promotions"],
28
+ imageUrl: "https://cf.shopee.com.my/file/6df4da8ff019bfa08c1bcba4bbe3c983",
29
+ },
30
+ {
31
+ id: "3",
32
+ title: "Nike Air Max 2X",
33
+ description: "Nike Men's Air Max 2X Shoes - White",
34
+ tags: ["Sports", "Promotions"],
35
+ imageUrl: "https://cf.shopee.com.my/file/6df4da8ff019bfa08c1bcba4bbe3c983",
36
+ },
37
+ {
38
+ id: "4",
39
+ title: "Nike Air Max 2X",
40
+ description: "Nike Men's Air Max 2X Shoes - White",
41
+ tags: ["Sports", "Promotions"],
42
+ ribbonText: "SALE",
43
+ imageUrl: "https://cf.shopee.com.my/file/6df4da8ff019bfa08c1bcba4bbe3c983",
44
+ },
45
+ {
46
+ id: "5",
47
+ title: "Nike Air Max 2X",
48
+ description: "Nike Men's Air Max 2X Shoes - White",
49
+ tags: ["Sports"],
50
+ imageUrl: "https://cf.shopee.com.my/file/6df4da8ff019bfa08c1bcba4bbe3c983",
51
+ },
52
+ {
53
+ id: "6",
54
+ title: "Nike Air Max 2X",
55
+ description: "Nike Men's Air Max 2X Shoes - White",
56
+ tags: ["Sports", "Promotions"],
57
+ imageUrl: "https://cf.shopee.com.my/file/6df4da8ff019bfa08c1bcba4bbe3c983",
58
+ },
59
+ {
60
+ id: "7",
61
+ title: "Nike Air Max 2X",
62
+ description: "Nike Men's Air Max 2X Shoes - White",
63
+ tags: ["Sports"],
64
+ imageUrl: "https://cf.shopee.com.my/file/6df4da8ff019bfa08c1bcba4bbe3c983",
65
+ },
66
+ {
67
+ id: "8",
68
+ title: "Nike Air Max 2X",
69
+ description: "Nike Men's Air Max 2X Shoes - White",
70
+ tags: ["Sports", "Promotions"],
71
+ imageUrl: "https://cf.shopee.com.my/file/6df4da8ff019bfa08c1bcba4bbe3c983",
72
+ },
73
+ ];
74
+
75
+ const renderImgRibbon = (id: string) => {
76
+ if (id === "1" || id === "2") {
77
+ return (
78
+ <div className="absolute top-5 -left-9 w-[50%] bg-yellow-500 text-white text-xs font-bold p-2 text-center transform -rotate-45">
79
+ <span>WINNER</span>
80
+ </div>
81
+ );
82
+ }
83
+ return null;
84
+ };
85
+
86
+ stories.add("Grid", () => {
87
+ const [pageIndex, setPageIndex] = useState(0);
88
+ const limit = 5;
89
+ const cursor = true;
90
+
91
+ const handlePagination = (value: string | number) => {
92
+ if (typeof value === "number") {
93
+ setPageIndex(value);
94
+ } else {
95
+ setPageIndex((prev) => (value === "next" ? prev + 1 : prev - 1));
96
+ }
97
+ };
98
+
99
+ return (
100
+ <>
101
+ <StorySection
102
+ title="Cursor Pagination"
103
+ description="Use paginationType='cursor' for token-based pagination (e.g. Firestore). Pass a truthy cursor while more data exists. infinityScroll combined with horizontalScroll auto-loads the next page when the user scrolls to the end."
104
+ >
105
+ <div className="w-full">
106
+ <Grid
107
+ data={data}
108
+ limit={limit}
109
+ loading={false}
110
+ paginationType="cursor"
111
+ cursor={cursor}
112
+ pageIndex={pageIndex}
113
+ size={data.length}
114
+ itemClassName="shadow-md rounded"
115
+ horizontalScroll={true}
116
+ infinityScroll
117
+ handleUpdatePageIndex={handlePagination}
118
+ renderImgRibbon={renderImgRibbon}
119
+ />
120
+ </div>
121
+ </StorySection>
122
+
123
+ <StorySection
124
+ title="Custom Grid Item"
125
+ description="Pass customGridItems with a map of id → ReactElement to replace a specific card with fully custom content. The key must match the item's id in data."
126
+ >
127
+ <div className="w-full">
128
+ <Grid
129
+ data={data}
130
+ limit={limit}
131
+ loading={false}
132
+ paginationType="cursor"
133
+ cursor={cursor}
134
+ pageIndex={pageIndex}
135
+ size={data.length}
136
+ itemClassName="shadow-md rounded"
137
+ horizontalScroll={true}
138
+ handleUpdatePageIndex={handlePagination}
139
+ customGridItems={{
140
+ "2": (
141
+ <div className="w-48 h-full flex flex-col justify-center items-center bg-primary-700 text-white p-4 font-thin">
142
+ <h1 className="text-3xl">Custom item</h1>
143
+ <p className="mt-6 text-sm">
144
+ This card replaces the default grid item for id="2".
145
+ </p>
146
+ </div>
147
+ ),
148
+ }}
149
+ />
150
+ </div>
151
+ </StorySection>
152
+
153
+ <StorySection
154
+ title="Page Pagination"
155
+ description="Use paginationType='page' for numbered page navigation. Pass size (total record count) so the component can calculate the page count. gridColClassName overrides the default 3-column layout."
156
+ >
157
+ <div className="w-full">
158
+ <Grid
159
+ data={data}
160
+ limit={limit}
161
+ loading={false}
162
+ paginationType="page"
163
+ size={data.length}
164
+ pageIndex={pageIndex}
165
+ gridColClassName="sm:grid-cols-2 sm:gap-x-6 lg:grid-cols-3"
166
+ handleUpdatePageIndex={handlePagination}
167
+ renderImgRibbon={renderImgRibbon}
168
+ />
169
+ </div>
170
+ </StorySection>
171
+
172
+ <StorySection
173
+ title="Dark Mode"
174
+ description="Pass darkMode=true and darkModeBg to customise the card background colour."
175
+ dark
176
+ >
177
+ <div className="w-full">
178
+ <Grid
179
+ data={data}
180
+ limit={limit}
181
+ loading={false}
182
+ paginationType="page"
183
+ size={data.length}
184
+ pageIndex={pageIndex}
185
+ gridColClassName="sm:grid-cols-2 sm:gap-x-6 lg:grid-cols-3"
186
+ darkMode={true}
187
+ darkModeBg="bg-gray-800"
188
+ handleUpdatePageIndex={handlePagination}
189
+ renderImgRibbon={renderImgRibbon}
190
+ />
191
+ </div>
192
+ </StorySection>
193
+
194
+ <StorySection
195
+ title="Ribbon Text"
196
+ description="Set ribbonText on a GridClass item to show a built-in ribbon badge on the card image. Use renderImgRibbon for a fully custom ribbon element."
197
+ >
198
+ <div className="w-full">
199
+ <Grid
200
+ data={data.slice(0, 4)}
201
+ limit={limit}
202
+ loading={false}
203
+ renderImgRibbon={renderImgRibbon}
204
+ />
205
+ </div>
206
+ </StorySection>
207
+
208
+ <StorySection
209
+ title="Selected Items"
210
+ description="Pass selectedItemsId and selectedItemClassName to highlight specific cards. Useful for multi-select UIs."
211
+ >
212
+ <div className="w-full">
213
+ <Grid
214
+ data={data.slice(0, 4)}
215
+ limit={limit}
216
+ loading={false}
217
+ selectedItemsId={["1", "3"]}
218
+ selectedItemClassName="ring-2 ring-primary-500"
219
+ onClickData={(id) => console.log("clicked", id)}
220
+ />
221
+ </div>
222
+ </StorySection>
223
+
224
+ <StorySection
225
+ title="Loading State"
226
+ description="Pass loading=true to show a loading indicator while data is being fetched."
227
+ >
228
+ <div className="w-full">
229
+ <Grid data={[]} loading={true} />
230
+ </div>
231
+ </StorySection>
232
+ </>
233
+ );
234
+ });