@wakastellar/ui 1.0.12 → 2.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.
Files changed (436) hide show
  1. package/README.md +163 -193
  2. package/dist/charts.cjs.js +1 -0
  3. package/dist/charts.es.js +16 -0
  4. package/dist/cli/commands/add.d.ts +7 -0
  5. package/dist/cli/commands/init.d.ts +6 -0
  6. package/dist/cli/commands/list.d.ts +5 -0
  7. package/dist/cli/commands/search.d.ts +1 -0
  8. package/dist/cli/index.cjs +4844 -0
  9. package/dist/cli/index.d.ts +1 -0
  10. package/dist/cli/utils/config.d.ts +29 -0
  11. package/dist/cli/utils/logger.d.ts +20 -0
  12. package/dist/cli/utils/registry.d.ts +23 -0
  13. package/dist/cn-B-fTneHh.js +1 -0
  14. package/dist/cn-DzRe1GWm.mjs +21 -0
  15. package/dist/components/index.d.ts +122 -0
  16. package/dist/components/waka-3d-pie-chart/index.d.ts +67 -0
  17. package/dist/components/waka-achievement-unlock/index.d.ts +83 -0
  18. package/dist/components/waka-activity-feed/index.d.ts +78 -0
  19. package/dist/components/waka-address-autocomplete/index.d.ts +124 -0
  20. package/dist/components/waka-alert-stack/index.d.ts +58 -0
  21. package/dist/components/waka-allocation-matrix/index.d.ts +193 -0
  22. package/dist/components/waka-approval-chain/index.d.ts +43 -0
  23. package/dist/components/waka-audit-log/index.d.ts +142 -0
  24. package/dist/components/waka-badge-showcase/index.d.ts +51 -0
  25. package/dist/components/waka-biometric-prompt/index.d.ts +84 -0
  26. package/dist/components/waka-bottom-sheet/index.d.ts +61 -0
  27. package/dist/components/waka-breadcrumb-path/index.d.ts +46 -0
  28. package/dist/components/waka-budget-burn/index.d.ts +154 -0
  29. package/dist/components/waka-capacity-planner/index.d.ts +132 -0
  30. package/dist/components/waka-cart-summary/index.d.ts +154 -0
  31. package/dist/components/waka-challenge-timer/index.d.ts +86 -0
  32. package/dist/components/waka-chat-bubble/index.d.ts +127 -0
  33. package/dist/components/waka-checklist/index.d.ts +123 -0
  34. package/dist/components/waka-checkout-stepper/index.d.ts +154 -0
  35. package/dist/components/waka-cohort-table/index.d.ts +130 -0
  36. package/dist/components/waka-combo-counter/index.d.ts +53 -0
  37. package/dist/components/waka-command-bar/index.d.ts +45 -0
  38. package/dist/components/waka-compare-period/index.d.ts +122 -0
  39. package/dist/components/waka-connection-matrix/index.d.ts +117 -0
  40. package/dist/components/waka-contribution-graph/index.d.ts +34 -0
  41. package/dist/components/waka-cost-breakdown/index.d.ts +50 -0
  42. package/dist/components/waka-coupon-input/index.d.ts +105 -0
  43. package/dist/components/waka-credit-card-input/index.d.ts +95 -0
  44. package/dist/components/waka-daily-reward/index.d.ts +76 -0
  45. package/dist/components/waka-deployment-lane/index.d.ts +43 -0
  46. package/dist/components/waka-device-trust/index.d.ts +95 -0
  47. package/dist/components/waka-dock/index.d.ts +44 -0
  48. package/dist/components/waka-empty-state/index.d.ts +85 -0
  49. package/dist/components/waka-error-shake/index.d.ts +49 -0
  50. package/dist/components/waka-feature-announcement/index.d.ts +112 -0
  51. package/dist/components/waka-floating-nav/index.d.ts +51 -0
  52. package/dist/components/waka-flow-diagram/index.d.ts +71 -0
  53. package/dist/components/waka-funnel-chart/index.d.ts +108 -0
  54. package/dist/components/waka-glow-card/index.d.ts +32 -0
  55. package/dist/components/waka-goal-progress/index.d.ts +139 -0
  56. package/dist/components/waka-haptic-button/index.d.ts +45 -0
  57. package/dist/components/waka-health-pulse/index.d.ts +28 -0
  58. package/dist/components/waka-heatmap/index.d.ts +135 -0
  59. package/dist/components/waka-hotspot/index.d.ts +106 -0
  60. package/dist/components/waka-incident-timeline/index.d.ts +38 -0
  61. package/dist/components/waka-invoice-preview/index.d.ts +137 -0
  62. package/dist/components/waka-kpi-dashboard/index.d.ts +80 -0
  63. package/dist/components/waka-leaderboard/index.d.ts +85 -0
  64. package/dist/components/waka-level-progress/index.d.ts +89 -0
  65. package/dist/components/waka-liquid-button/index.d.ts +41 -0
  66. package/dist/components/waka-loading-orbit/index.d.ts +90 -0
  67. package/dist/components/waka-loot-box/index.d.ts +87 -0
  68. package/dist/components/waka-magic-link/index.d.ts +34 -0
  69. package/dist/components/waka-magnetic-button/index.d.ts +56 -0
  70. package/dist/components/waka-mention-input/index.d.ts +106 -0
  71. package/dist/components/waka-metric-sparkline/index.d.ts +46 -0
  72. package/dist/components/waka-milestone-road/index.d.ts +91 -0
  73. package/dist/components/waka-morph-button/index.d.ts +62 -0
  74. package/dist/components/waka-network-topology/index.d.ts +35 -0
  75. package/dist/components/waka-orbital-menu/index.d.ts +61 -0
  76. package/dist/components/waka-order-tracker/index.d.ts +121 -0
  77. package/dist/components/waka-password-strength/index.d.ts +98 -0
  78. package/dist/components/waka-payment-method-picker/index.d.ts +88 -0
  79. package/dist/components/waka-permission-matrix/index.d.ts +197 -0
  80. package/dist/components/waka-phone-input/index.d.ts +93 -0
  81. package/dist/components/waka-pipeline-view/index.d.ts +49 -0
  82. package/dist/components/waka-player-card/index.d.ts +36 -0
  83. package/dist/components/waka-points-popup/index.d.ts +75 -0
  84. package/dist/components/waka-power-up/index.d.ts +103 -0
  85. package/dist/components/waka-presence-indicator/index.d.ts +188 -0
  86. package/dist/components/waka-pricing-table/index.d.ts +77 -0
  87. package/dist/components/waka-product-card/index.d.ts +81 -0
  88. package/dist/components/waka-progress-onboarding/index.d.ts +97 -0
  89. package/dist/components/waka-pull-to-refresh/index.d.ts +45 -0
  90. package/dist/components/waka-quest-card/index.d.ts +110 -0
  91. package/dist/components/waka-quota-bar/index.d.ts +100 -0
  92. package/dist/components/waka-radar-score/index.d.ts +95 -0
  93. package/dist/components/waka-rank-badge/index.d.ts +58 -0
  94. package/dist/components/waka-rating-input/index.d.ts +110 -0
  95. package/dist/components/waka-reaction-picker/index.d.ts +77 -0
  96. package/dist/components/waka-region-map/index.d.ts +27 -0
  97. package/dist/components/waka-resource-gauge/index.d.ts +78 -0
  98. package/dist/components/waka-resource-pool/index.d.ts +81 -0
  99. package/dist/components/waka-rollback-slider/index.d.ts +79 -0
  100. package/dist/components/waka-sankey-diagram/index.d.ts +120 -0
  101. package/dist/components/waka-schedule-picker/index.d.ts +100 -0
  102. package/dist/components/waka-scratch-card/index.d.ts +87 -0
  103. package/dist/components/waka-season-pass/index.d.ts +65 -0
  104. package/dist/components/waka-security-score/index.d.ts +124 -0
  105. package/dist/components/waka-server-rack/index.d.ts +44 -0
  106. package/dist/components/waka-session-manager/index.d.ts +116 -0
  107. package/dist/components/waka-signature-pad/index.d.ts +87 -0
  108. package/dist/components/waka-skeleton-wave/index.d.ts +79 -0
  109. package/dist/components/waka-skill-tree/index.d.ts +78 -0
  110. package/dist/components/waka-sla-tracker/index.d.ts +65 -0
  111. package/dist/components/waka-slider-range/index.d.ts +88 -0
  112. package/dist/components/waka-spin-wheel/index.d.ts +51 -0
  113. package/dist/components/waka-spotlight/index.d.ts +47 -0
  114. package/dist/components/waka-stats-hexagon/index.d.ts +149 -0
  115. package/dist/components/waka-status-matrix/index.d.ts +38 -0
  116. package/dist/components/waka-streak-counter/index.d.ts +27 -0
  117. package/dist/components/waka-success-explosion/index.d.ts +51 -0
  118. package/dist/components/waka-swipe-card/index.d.ts +64 -0
  119. package/dist/components/waka-tabs-morph/index.d.ts +66 -0
  120. package/dist/components/waka-tag-input/index.d.ts +134 -0
  121. package/dist/components/waka-team-banner/index.d.ts +122 -0
  122. package/dist/components/waka-terminal-output/index.d.ts +48 -0
  123. package/dist/components/waka-thread-view/index.d.ts +101 -0
  124. package/dist/components/waka-tilt-card/index.d.ts +36 -0
  125. package/dist/components/waka-tooltip-tour/index.d.ts +118 -0
  126. package/dist/components/waka-tour-guide/index.d.ts +122 -0
  127. package/dist/components/waka-tournament-bracket/index.d.ts +101 -0
  128. package/dist/components/waka-treemap-chart/index.d.ts +104 -0
  129. package/dist/components/waka-two-factor-setup/index.d.ts +93 -0
  130. package/dist/components/waka-typewriter/index.d.ts +98 -0
  131. package/dist/components/waka-typing-indicator/index.d.ts +64 -0
  132. package/dist/components/waka-versus-card/index.d.ts +117 -0
  133. package/dist/components/waka-video-call/index.d.ts +170 -0
  134. package/dist/components/waka-voice-message/index.d.ts +117 -0
  135. package/dist/components/waka-welcome-modal/index.d.ts +120 -0
  136. package/dist/components/waka-xp-bar/index.d.ts +54 -0
  137. package/dist/export.cjs.js +1 -0
  138. package/dist/export.d.ts +3 -1
  139. package/dist/export.es.js +5 -0
  140. package/dist/index-B9GTFkji.js +1 -0
  141. package/dist/index-c0jcWyEL.mjs +466 -0
  142. package/dist/index.cjs.js +2530 -22
  143. package/dist/index.d.ts +1 -0
  144. package/dist/index.es.js +72081 -19512
  145. package/dist/rich-text.cjs.js +1 -0
  146. package/dist/rich-text.es.js +4 -0
  147. package/dist/types-BOWIoR7j.mjs +1111 -0
  148. package/dist/types-D2yCJ91P.js +1 -0
  149. package/dist/useDataTableImport-D8R2HQl6.mjs +229 -0
  150. package/dist/useDataTableImport-S_hhA5Wo.js +9 -0
  151. package/package.json +70 -22
  152. package/src/blocks/activity-timeline/index.tsx +586 -0
  153. package/src/blocks/calendar-view/index.tsx +756 -0
  154. package/src/blocks/chat/index.tsx +1018 -0
  155. package/src/blocks/chat/widget.tsx +504 -0
  156. package/src/blocks/dashboard/index.tsx +522 -0
  157. package/src/blocks/empty-states/index.tsx +452 -0
  158. package/src/blocks/error-pages/index.tsx +426 -0
  159. package/src/blocks/faq/index.tsx +479 -0
  160. package/src/blocks/file-manager/index.tsx +890 -0
  161. package/src/blocks/footer/index.tsx +133 -0
  162. package/src/blocks/header/index.tsx +357 -0
  163. package/src/blocks/headtab/index.tsx +139 -0
  164. package/src/blocks/i18n-editor/index.tsx +1016 -0
  165. package/src/blocks/index.ts +80 -0
  166. package/src/blocks/kanban-board/index.tsx +779 -0
  167. package/src/blocks/landing/index.tsx +677 -0
  168. package/src/blocks/language-selector/index.tsx +88 -0
  169. package/src/blocks/layout/index.tsx +159 -0
  170. package/src/blocks/login/index.tsx +339 -0
  171. package/src/blocks/login/types.ts +131 -0
  172. package/src/blocks/pricing/index.tsx +564 -0
  173. package/src/blocks/profile/index.tsx +746 -0
  174. package/src/blocks/settings/index.tsx +558 -0
  175. package/src/blocks/sidebar/index.tsx +713 -0
  176. package/src/blocks/theme-creator-block/index.tsx +835 -0
  177. package/src/blocks/user-management/index.tsx +1037 -0
  178. package/src/blocks/wizard/index.tsx +719 -0
  179. package/src/components/DataTable/DataTable.tsx +406 -0
  180. package/src/components/DataTable/DataTableAdvanced.tsx +720 -0
  181. package/src/components/DataTable/DataTableBody.tsx +216 -0
  182. package/src/components/DataTable/DataTableCell.tsx +172 -0
  183. package/src/components/DataTable/DataTableColumnResizer.tsx +62 -0
  184. package/src/components/DataTable/DataTableConflictResolver.tsx +478 -0
  185. package/src/components/DataTable/DataTableContextMenu.tsx +219 -0
  186. package/src/components/DataTable/DataTableEditCell.tsx +279 -0
  187. package/src/components/DataTable/DataTableFilterBuilder.tsx +519 -0
  188. package/src/components/DataTable/DataTableFilters.tsx +535 -0
  189. package/src/components/DataTable/DataTableGrouping.tsx +147 -0
  190. package/src/components/DataTable/DataTableHeader.tsx +172 -0
  191. package/src/components/DataTable/DataTablePagination.tsx +125 -0
  192. package/src/components/DataTable/DataTableSelection.tsx +269 -0
  193. package/src/components/DataTable/DataTableSyncStatus.tsx +281 -0
  194. package/src/components/DataTable/DataTableToolbar.tsx +262 -0
  195. package/src/components/DataTable/README.md +446 -0
  196. package/src/components/DataTable/__tests__/DataTableAdvanced.test.tsx +426 -0
  197. package/src/components/DataTable/__tests__/DataTableEdit.test.tsx +329 -0
  198. package/src/components/DataTable/__tests__/useDataTableAdvanced.test.ts +455 -0
  199. package/src/components/DataTable/examples/EditExample.tsx +166 -0
  200. package/src/components/DataTable/formatters/index.ts +335 -0
  201. package/src/components/DataTable/hooks/__tests__/useDataTableEdit.test.ts +239 -0
  202. package/src/components/DataTable/hooks/useDataTable.ts +145 -0
  203. package/src/components/DataTable/hooks/useDataTableAdvanced.ts +342 -0
  204. package/src/components/DataTable/hooks/useDataTableAdvancedFilters.ts +637 -0
  205. package/src/components/DataTable/hooks/useDataTableColumnTemplates.ts +186 -0
  206. package/src/components/DataTable/hooks/useDataTableEdit.ts +167 -0
  207. package/src/components/DataTable/hooks/useDataTableExport.ts +227 -0
  208. package/src/components/DataTable/hooks/useDataTableImport.ts +216 -0
  209. package/src/components/DataTable/hooks/useDataTableOffline.ts +481 -0
  210. package/src/components/DataTable/hooks/useDataTableTheme.ts +213 -0
  211. package/src/components/DataTable/hooks/useDataTableVirtualization.ts +99 -0
  212. package/src/components/DataTable/hooks/useTableLayout.ts +85 -0
  213. package/src/components/DataTable/index.ts +81 -0
  214. package/src/components/DataTable/services/IndexedDBService.ts +504 -0
  215. package/src/components/DataTable/templates/index.tsx +803 -0
  216. package/src/components/DataTable/types.ts +504 -0
  217. package/src/components/DataTable/utils.ts +164 -0
  218. package/src/components/DataTable/workers/exportWorker.ts +213 -0
  219. package/src/components/accordion/index.tsx +61 -0
  220. package/src/components/alert/index.tsx +61 -0
  221. package/src/components/alert-dialog/index.tsx +146 -0
  222. package/src/components/aspect-ratio/index.tsx +12 -0
  223. package/src/components/avatar/index.tsx +54 -0
  224. package/src/components/badge/Badge.stories.tsx +64 -0
  225. package/src/components/badge/index.tsx +38 -0
  226. package/src/components/button/Button.stories.tsx +173 -0
  227. package/src/components/button/index.tsx +56 -0
  228. package/src/components/calendar/index.tsx +73 -0
  229. package/src/components/card/index.tsx +78 -0
  230. package/src/components/checkbox/index.tsx +34 -0
  231. package/src/components/code/index.tsx +229 -0
  232. package/src/components/collapsible/index.tsx +16 -0
  233. package/src/components/command/index.tsx +162 -0
  234. package/src/components/context-menu/index.tsx +204 -0
  235. package/src/components/dialog/index.tsx +126 -0
  236. package/src/components/dropdown-menu/index.tsx +204 -0
  237. package/src/components/error-boundary/ErrorBoundary.tsx +281 -0
  238. package/src/components/error-boundary/index.ts +7 -0
  239. package/src/components/form/index.tsx +183 -0
  240. package/src/components/hover-card/index.tsx +33 -0
  241. package/src/components/index.ts +368 -0
  242. package/src/components/input/Input.stories.tsx +100 -0
  243. package/src/components/input/index.tsx +27 -0
  244. package/src/components/input-otp/index.tsx +277 -0
  245. package/src/components/label/index.tsx +30 -0
  246. package/src/components/language-selector/index.tsx +341 -0
  247. package/src/components/menubar/index.tsx +240 -0
  248. package/src/components/navigation-menu/index.tsx +134 -0
  249. package/src/components/popover/index.tsx +35 -0
  250. package/src/components/progress/index.tsx +32 -0
  251. package/src/components/radio-group/index.tsx +48 -0
  252. package/src/components/scroll-area/index.tsx +52 -0
  253. package/src/components/select/index.tsx +164 -0
  254. package/src/components/separator/index.tsx +35 -0
  255. package/src/components/sheet/index.tsx +147 -0
  256. package/src/components/skeleton/index.tsx +22 -0
  257. package/src/components/slider/index.tsx +32 -0
  258. package/src/components/switch/index.tsx +33 -0
  259. package/src/components/table/index.tsx +117 -0
  260. package/src/components/tabs/index.tsx +59 -0
  261. package/src/components/textarea/index.tsx +30 -0
  262. package/src/components/theme-selector/index.tsx +327 -0
  263. package/src/components/toast/index.tsx +133 -0
  264. package/src/components/toaster/index.tsx +34 -0
  265. package/src/components/toggle/index.tsx +49 -0
  266. package/src/components/tooltip/index.tsx +34 -0
  267. package/src/components/typography/index.tsx +276 -0
  268. package/src/components/waka-3d-pie-chart/index.tsx +486 -0
  269. package/src/components/waka-achievement-unlock/index.tsx +716 -0
  270. package/src/components/waka-activity-feed/index.tsx +686 -0
  271. package/src/components/waka-address-autocomplete/index.tsx +1202 -0
  272. package/src/components/waka-admincrumb/index.tsx +349 -0
  273. package/src/components/waka-alert-stack/index.tsx +827 -0
  274. package/src/components/waka-allocation-matrix/index.tsx +1278 -0
  275. package/src/components/waka-approval-chain/index.tsx +766 -0
  276. package/src/components/waka-audit-log/index.tsx +1475 -0
  277. package/src/components/waka-autocomplete/index.tsx +358 -0
  278. package/src/components/waka-badge-showcase/index.tsx +704 -0
  279. package/src/components/waka-barcode/index.tsx +260 -0
  280. package/src/components/waka-biometric-prompt/index.tsx +765 -0
  281. package/src/components/waka-bottom-sheet/index.tsx +495 -0
  282. package/src/components/waka-breadcrumb/index.tsx +376 -0
  283. package/src/components/waka-breadcrumb-path/index.tsx +513 -0
  284. package/src/components/waka-budget-burn/index.tsx +1234 -0
  285. package/src/components/waka-capacity-planner/index.tsx +1107 -0
  286. package/src/components/waka-carousel/index.tsx +893 -0
  287. package/src/components/waka-cart-summary/index.tsx +1055 -0
  288. package/src/components/waka-challenge-timer/index.tsx +1044 -0
  289. package/src/components/waka-charts/WakaAreaChart.tsx +251 -0
  290. package/src/components/waka-charts/WakaBarChart.tsx +222 -0
  291. package/src/components/waka-charts/WakaChart.tsx +124 -0
  292. package/src/components/waka-charts/WakaLineChart.tsx +219 -0
  293. package/src/components/waka-charts/WakaMiniChart.tsx +133 -0
  294. package/src/components/waka-charts/WakaPieChart.tsx +214 -0
  295. package/src/components/waka-charts/WakaSparkline.tsx +229 -0
  296. package/src/components/waka-charts/dataTableHelpers.ts +109 -0
  297. package/src/components/waka-charts/hooks/useChartTheme.ts +123 -0
  298. package/src/components/waka-charts/hooks/useRechartsLoader.ts +234 -0
  299. package/src/components/waka-charts/index.ts +90 -0
  300. package/src/components/waka-charts/types.ts +330 -0
  301. package/src/components/waka-chat-bubble/index.tsx +1060 -0
  302. package/src/components/waka-checklist/index.tsx +1067 -0
  303. package/src/components/waka-checkout-stepper/index.tsx +976 -0
  304. package/src/components/waka-cohort-table/index.tsx +1011 -0
  305. package/src/components/waka-color-picker/index.tsx +447 -0
  306. package/src/components/waka-combo-counter/index.tsx +864 -0
  307. package/src/components/waka-combobox/index.tsx +497 -0
  308. package/src/components/waka-command-bar/index.tsx +403 -0
  309. package/src/components/waka-compare-period/index.tsx +1230 -0
  310. package/src/components/waka-connection-matrix/index.tsx +1053 -0
  311. package/src/components/waka-contribution-graph/index.tsx +552 -0
  312. package/src/components/waka-cost-breakdown/index.tsx +1065 -0
  313. package/src/components/waka-coupon-input/index.tsx +592 -0
  314. package/src/components/waka-credit-card-input/index.tsx +982 -0
  315. package/src/components/waka-daily-reward/index.tsx +762 -0
  316. package/src/components/waka-date-range-picker/index.tsx +378 -0
  317. package/src/components/waka-datetime-picker/index.tsx +793 -0
  318. package/src/components/waka-datetime-picker.form-integration/index.tsx +402 -0
  319. package/src/components/waka-deployment-lane/index.tsx +673 -0
  320. package/src/components/waka-device-trust/index.tsx +1259 -0
  321. package/src/components/waka-dock/index.tsx +285 -0
  322. package/src/components/waka-drawer/index.tsx +319 -0
  323. package/src/components/waka-empty-state/index.tsx +545 -0
  324. package/src/components/waka-error-shake/index.tsx +398 -0
  325. package/src/components/waka-feature-announcement/index.tsx +991 -0
  326. package/src/components/waka-file-upload/index.tsx +437 -0
  327. package/src/components/waka-floating-nav/index.tsx +413 -0
  328. package/src/components/waka-flow-diagram/index.tsx +508 -0
  329. package/src/components/waka-funnel-chart/index.tsx +823 -0
  330. package/src/components/waka-glow-card/index.tsx +246 -0
  331. package/src/components/waka-goal-progress/index.tsx +1025 -0
  332. package/src/components/waka-haptic-button/index.tsx +388 -0
  333. package/src/components/waka-health-pulse/index.tsx +451 -0
  334. package/src/components/waka-heatmap/index.tsx +1026 -0
  335. package/src/components/waka-hotspot/index.tsx +682 -0
  336. package/src/components/waka-image/index.tsx +373 -0
  337. package/src/components/waka-incident-timeline/index.tsx +686 -0
  338. package/src/components/waka-invoice-preview/index.tsx +829 -0
  339. package/src/components/waka-kanban/index.tsx +646 -0
  340. package/src/components/waka-kpi-dashboard/index.tsx +755 -0
  341. package/src/components/waka-leaderboard/index.tsx +746 -0
  342. package/src/components/waka-level-progress/index.tsx +665 -0
  343. package/src/components/waka-liquid-button/index.tsx +520 -0
  344. package/src/components/waka-loading-orbit/index.tsx +478 -0
  345. package/src/components/waka-loot-box/index.tsx +1091 -0
  346. package/src/components/waka-magic-link/index.tsx +321 -0
  347. package/src/components/waka-magnetic-button/index.tsx +567 -0
  348. package/src/components/waka-mention-input/index.tsx +953 -0
  349. package/src/components/waka-metric-sparkline/index.tsx +627 -0
  350. package/src/components/waka-milestone-road/index.tsx +1064 -0
  351. package/src/components/waka-modal/index.tsx +374 -0
  352. package/src/components/waka-morph-button/index.tsx +495 -0
  353. package/src/components/waka-network-topology/index.tsx +801 -0
  354. package/src/components/waka-notifications/index.tsx +414 -0
  355. package/src/components/waka-number-input/index.tsx +373 -0
  356. package/src/components/waka-orbital-menu/index.tsx +445 -0
  357. package/src/components/waka-order-tracker/index.tsx +1041 -0
  358. package/src/components/waka-pagination/index.tsx +393 -0
  359. package/src/components/waka-password-strength/index.tsx +824 -0
  360. package/src/components/waka-payment-method-picker/index.tsx +715 -0
  361. package/src/components/waka-permission-matrix/index.tsx +1302 -0
  362. package/src/components/waka-phone-input/index.tsx +801 -0
  363. package/src/components/waka-pipeline-view/index.tsx +604 -0
  364. package/src/components/waka-player-card/index.tsx +691 -0
  365. package/src/components/waka-points-popup/index.tsx +366 -0
  366. package/src/components/waka-power-up/index.tsx +1155 -0
  367. package/src/components/waka-presence-indicator/index.tsx +1181 -0
  368. package/src/components/waka-pricing-table/index.tsx +755 -0
  369. package/src/components/waka-product-card/index.tsx +786 -0
  370. package/src/components/waka-progress-onboarding/index.tsx +878 -0
  371. package/src/components/waka-pull-to-refresh/index.tsx +451 -0
  372. package/src/components/waka-qrcode/index.tsx +232 -0
  373. package/src/components/waka-quest-card/index.tsx +1275 -0
  374. package/src/components/waka-quota-bar/index.tsx +693 -0
  375. package/src/components/waka-radar-score/index.tsx +512 -0
  376. package/src/components/waka-rank-badge/index.tsx +813 -0
  377. package/src/components/waka-rating-input/index.tsx +560 -0
  378. package/src/components/waka-reaction-picker/index.tsx +1062 -0
  379. package/src/components/waka-region-map/index.tsx +730 -0
  380. package/src/components/waka-resource-gauge/index.tsx +654 -0
  381. package/src/components/waka-resource-pool/index.tsx +1035 -0
  382. package/src/components/waka-rich-text-editor/index.tsx +594 -0
  383. package/src/components/waka-rollback-slider/index.tsx +891 -0
  384. package/src/components/waka-sankey-diagram/index.tsx +1032 -0
  385. package/src/components/waka-schedule-picker/index.tsx +1060 -0
  386. package/src/components/waka-scratch-card/index.tsx +914 -0
  387. package/src/components/waka-season-pass/index.tsx +886 -0
  388. package/src/components/waka-security-score/index.tsx +1126 -0
  389. package/src/components/waka-segmented-control/index.tsx +238 -0
  390. package/src/components/waka-server-rack/index.tsx +764 -0
  391. package/src/components/waka-session-manager/index.tsx +815 -0
  392. package/src/components/waka-signature-pad/index.tsx +744 -0
  393. package/src/components/waka-skeleton-wave/index.tsx +454 -0
  394. package/src/components/waka-skill-tree/index.tsx +1031 -0
  395. package/src/components/waka-sla-tracker/index.tsx +798 -0
  396. package/src/components/waka-slider-range/index.tsx +765 -0
  397. package/src/components/waka-spin-wheel/index.tsx +671 -0
  398. package/src/components/waka-spinner/index.tsx +284 -0
  399. package/src/components/waka-spotlight/index.tsx +410 -0
  400. package/src/components/waka-stat/index.tsx +428 -0
  401. package/src/components/waka-stats-hexagon/index.tsx +824 -0
  402. package/src/components/waka-status-matrix/index.tsx +565 -0
  403. package/src/components/waka-stepper/index.tsx +489 -0
  404. package/src/components/waka-streak-counter/index.tsx +334 -0
  405. package/src/components/waka-success-explosion/index.tsx +453 -0
  406. package/src/components/waka-swipe-card/index.tsx +574 -0
  407. package/src/components/waka-tabs-morph/index.tsx +509 -0
  408. package/src/components/waka-tag-input/index.tsx +877 -0
  409. package/src/components/waka-team-banner/index.tsx +1183 -0
  410. package/src/components/waka-terminal-output/index.tsx +836 -0
  411. package/src/components/waka-theme-creator/index.tsx +762 -0
  412. package/src/components/waka-theme-manager/index.tsx +654 -0
  413. package/src/components/waka-thread-view/index.tsx +874 -0
  414. package/src/components/waka-tilt-card/index.tsx +250 -0
  415. package/src/components/waka-time-picker/index.tsx +479 -0
  416. package/src/components/waka-timeline/index.tsx +385 -0
  417. package/src/components/waka-tooltip-tour/index.tsx +855 -0
  418. package/src/components/waka-tour-guide/index.tsx +920 -0
  419. package/src/components/waka-tournament-bracket/index.tsx +1276 -0
  420. package/src/components/waka-tree/index.tsx +557 -0
  421. package/src/components/waka-treemap-chart/index.tsx +1031 -0
  422. package/src/components/waka-two-factor-setup/index.tsx +995 -0
  423. package/src/components/waka-typewriter/index.tsx +566 -0
  424. package/src/components/waka-typing-indicator/index.tsx +649 -0
  425. package/src/components/waka-versus-card/index.tsx +1026 -0
  426. package/src/components/waka-video/index.tsx +557 -0
  427. package/src/components/waka-video-call/index.tsx +1087 -0
  428. package/src/components/waka-virtual-list/index.tsx +327 -0
  429. package/src/components/waka-voice-message/index.tsx +1019 -0
  430. package/src/components/waka-welcome-modal/index.tsx +790 -0
  431. package/src/components/waka-xp-bar/index.tsx +799 -0
  432. package/src/styles/base.css +108 -0
  433. package/src/styles/code-highlight.css +82 -86
  434. package/src/styles/globals-v3.css +9 -0
  435. package/src/styles/globals.css +57 -74
  436. package/src/styles/tailwind.preset.js +69 -0
@@ -0,0 +1,1202 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {
5
+ MapPin,
6
+ Search,
7
+ X,
8
+ Clock,
9
+ Navigation,
10
+ Loader2,
11
+ ChevronDown,
12
+ Building2,
13
+ Home,
14
+ Edit3,
15
+ } from "lucide-react"
16
+ import { cn } from "../../utils/cn"
17
+
18
+ // ============================================
19
+ // TYPES
20
+ // ============================================
21
+
22
+ export interface AddressParts {
23
+ /** Street address (e.g., "123 Main St") */
24
+ street: string
25
+ /** City name */
26
+ city: string
27
+ /** State/Province/Region */
28
+ state?: string
29
+ /** Postal/ZIP code */
30
+ zip: string
31
+ /** Country name */
32
+ country: string
33
+ /** Full formatted address */
34
+ formatted: string
35
+ }
36
+
37
+ export interface AddressSuggestion {
38
+ /** Unique identifier */
39
+ id: string
40
+ /** Primary address text */
41
+ primary: string
42
+ /** Secondary text (city, state, etc.) */
43
+ secondary: string
44
+ /** Parsed address parts */
45
+ parts: AddressParts
46
+ /** Address type */
47
+ type?: "home" | "business" | "other"
48
+ /** Distance from user (if location available) */
49
+ distance?: string
50
+ }
51
+
52
+ export interface RecentAddress extends AddressSuggestion {
53
+ /** Timestamp of last use */
54
+ lastUsed: Date
55
+ }
56
+
57
+ export interface WakaAddressAutocompleteProps {
58
+ /** Selected address value */
59
+ value?: AddressSuggestion | null
60
+ /** Callback when address is selected */
61
+ onChange?: (address: AddressSuggestion | null) => void
62
+ /** Callback for search queries (for custom/API implementations) */
63
+ onSearch?: (query: string) => Promise<AddressSuggestion[]>
64
+ /** Placeholder text */
65
+ placeholder?: string
66
+ /** Enable recent addresses feature */
67
+ showRecentAddresses?: boolean
68
+ /** Recent addresses list */
69
+ recentAddresses?: RecentAddress[]
70
+ /** Callback when address is added to recents */
71
+ onAddToRecent?: (address: AddressSuggestion) => void
72
+ /** Enable manual entry fallback */
73
+ allowManualEntry?: boolean
74
+ /** Callback when manual entry is requested */
75
+ onManualEntry?: () => void
76
+ /** Debounce delay in milliseconds */
77
+ debounceMs?: number
78
+ /** Minimum characters before searching */
79
+ minChars?: number
80
+ /** Maximum number of suggestions to show */
81
+ maxSuggestions?: number
82
+ /** No results message */
83
+ noResultsMessage?: string
84
+ /** Loading message */
85
+ loadingMessage?: string
86
+ /** Disabled state */
87
+ disabled?: boolean
88
+ /** Error state */
89
+ error?: string
90
+ /** Additional CSS classes */
91
+ className?: string
92
+ /** Input ID for accessibility */
93
+ id?: string
94
+ /** Input name for forms */
95
+ name?: string
96
+ /** Label text */
97
+ label?: string
98
+ /** Required field */
99
+ required?: boolean
100
+ /** Auto focus on mount */
101
+ autoFocus?: boolean
102
+ }
103
+
104
+ export interface UseAddressAutocompleteOptions {
105
+ /** Custom search function */
106
+ onSearch?: (query: string) => Promise<AddressSuggestion[]>
107
+ /** Debounce delay in milliseconds */
108
+ debounceMs?: number
109
+ /** Minimum characters before searching */
110
+ minChars?: number
111
+ /** Maximum number of suggestions */
112
+ maxSuggestions?: number
113
+ /** Initial recent addresses */
114
+ initialRecentAddresses?: RecentAddress[]
115
+ /** Max recent addresses to store */
116
+ maxRecentAddresses?: number
117
+ /** Storage key for recent addresses */
118
+ storageKey?: string
119
+ }
120
+
121
+ export interface UseAddressAutocompleteReturn {
122
+ /** Current search query */
123
+ query: string
124
+ /** Set search query */
125
+ setQuery: (query: string) => void
126
+ /** Suggestions list */
127
+ suggestions: AddressSuggestion[]
128
+ /** Loading state */
129
+ isLoading: boolean
130
+ /** Error state */
131
+ error: string | null
132
+ /** Recent addresses */
133
+ recentAddresses: RecentAddress[]
134
+ /** Add address to recents */
135
+ addToRecent: (address: AddressSuggestion) => void
136
+ /** Clear recent addresses */
137
+ clearRecentAddresses: () => void
138
+ /** Remove specific recent address */
139
+ removeFromRecent: (id: string) => void
140
+ /** Search function */
141
+ search: (query: string) => Promise<void>
142
+ /** Clear suggestions */
143
+ clearSuggestions: () => void
144
+ /** Parse address string to parts */
145
+ parseAddress: (addressString: string) => AddressParts
146
+ }
147
+
148
+ // ============================================
149
+ // MOCK DATA & UTILITIES
150
+ // ============================================
151
+
152
+ const MOCK_ADDRESSES: AddressSuggestion[] = [
153
+ {
154
+ id: "1",
155
+ primary: "123 Main Street",
156
+ secondary: "New York, NY 10001, USA",
157
+ type: "home",
158
+ parts: {
159
+ street: "123 Main Street",
160
+ city: "New York",
161
+ state: "NY",
162
+ zip: "10001",
163
+ country: "USA",
164
+ formatted: "123 Main Street, New York, NY 10001, USA",
165
+ },
166
+ },
167
+ {
168
+ id: "2",
169
+ primary: "456 Oak Avenue",
170
+ secondary: "Los Angeles, CA 90001, USA",
171
+ type: "business",
172
+ parts: {
173
+ street: "456 Oak Avenue",
174
+ city: "Los Angeles",
175
+ state: "CA",
176
+ zip: "90001",
177
+ country: "USA",
178
+ formatted: "456 Oak Avenue, Los Angeles, CA 90001, USA",
179
+ },
180
+ },
181
+ {
182
+ id: "3",
183
+ primary: "789 Pine Road",
184
+ secondary: "Chicago, IL 60601, USA",
185
+ type: "home",
186
+ parts: {
187
+ street: "789 Pine Road",
188
+ city: "Chicago",
189
+ state: "IL",
190
+ zip: "60601",
191
+ country: "USA",
192
+ formatted: "789 Pine Road, Chicago, IL 60601, USA",
193
+ },
194
+ },
195
+ {
196
+ id: "4",
197
+ primary: "321 Elm Boulevard",
198
+ secondary: "Houston, TX 77001, USA",
199
+ type: "other",
200
+ parts: {
201
+ street: "321 Elm Boulevard",
202
+ city: "Houston",
203
+ state: "TX",
204
+ zip: "77001",
205
+ country: "USA",
206
+ formatted: "321 Elm Boulevard, Houston, TX 77001, USA",
207
+ },
208
+ },
209
+ {
210
+ id: "5",
211
+ primary: "555 Cedar Lane",
212
+ secondary: "Phoenix, AZ 85001, USA",
213
+ type: "home",
214
+ parts: {
215
+ street: "555 Cedar Lane",
216
+ city: "Phoenix",
217
+ state: "AZ",
218
+ zip: "85001",
219
+ country: "USA",
220
+ formatted: "555 Cedar Lane, Phoenix, AZ 85001, USA",
221
+ },
222
+ },
223
+ {
224
+ id: "6",
225
+ primary: "10 Downing Street",
226
+ secondary: "London, SW1A 2AA, UK",
227
+ type: "business",
228
+ parts: {
229
+ street: "10 Downing Street",
230
+ city: "London",
231
+ state: "",
232
+ zip: "SW1A 2AA",
233
+ country: "UK",
234
+ formatted: "10 Downing Street, London, SW1A 2AA, UK",
235
+ },
236
+ },
237
+ {
238
+ id: "7",
239
+ primary: "1600 Pennsylvania Avenue",
240
+ secondary: "Washington, DC 20500, USA",
241
+ type: "business",
242
+ parts: {
243
+ street: "1600 Pennsylvania Avenue",
244
+ city: "Washington",
245
+ state: "DC",
246
+ zip: "20500",
247
+ country: "USA",
248
+ formatted: "1600 Pennsylvania Avenue, Washington, DC 20500, USA",
249
+ },
250
+ },
251
+ {
252
+ id: "8",
253
+ primary: "350 Fifth Avenue",
254
+ secondary: "New York, NY 10118, USA",
255
+ type: "business",
256
+ distance: "2.3 mi",
257
+ parts: {
258
+ street: "350 Fifth Avenue",
259
+ city: "New York",
260
+ state: "NY",
261
+ zip: "10118",
262
+ country: "USA",
263
+ formatted: "350 Fifth Avenue, New York, NY 10118, USA",
264
+ },
265
+ },
266
+ ]
267
+
268
+ /**
269
+ * Parse a raw address string into structured parts
270
+ */
271
+ function parseAddressString(addressString: string): AddressParts {
272
+ // Simple parsing logic - in production, use a proper address parser
273
+ const parts = addressString.split(",").map((p) => p.trim())
274
+
275
+ const street = parts[0] || ""
276
+ const cityStateZip = parts[1] || ""
277
+ const country = parts[parts.length - 1] || ""
278
+
279
+ // Try to extract state and zip from cityStateZip
280
+ const stateZipMatch = cityStateZip.match(/^(.+?)\s+([A-Z]{2})\s+(\d{5}(?:-\d{4})?)$/)
281
+
282
+ let city = ""
283
+ let state = ""
284
+ let zip = ""
285
+
286
+ if (stateZipMatch) {
287
+ city = stateZipMatch[1]
288
+ state = stateZipMatch[2]
289
+ zip = stateZipMatch[3]
290
+ } else {
291
+ city = cityStateZip
292
+ }
293
+
294
+ return {
295
+ street,
296
+ city,
297
+ state,
298
+ zip,
299
+ country: country !== city ? country : "",
300
+ formatted: addressString,
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Mock search function for demonstration
306
+ */
307
+ async function mockSearchAddresses(
308
+ query: string,
309
+ maxResults: number = 5
310
+ ): Promise<AddressSuggestion[]> {
311
+ // Simulate network delay
312
+ await new Promise((resolve) => setTimeout(resolve, 300 + Math.random() * 200))
313
+
314
+ if (!query.trim()) return []
315
+
316
+ const queryLower = query.toLowerCase()
317
+
318
+ const results = MOCK_ADDRESSES.filter(
319
+ (addr) =>
320
+ addr.primary.toLowerCase().includes(queryLower) ||
321
+ addr.secondary.toLowerCase().includes(queryLower) ||
322
+ addr.parts.city.toLowerCase().includes(queryLower) ||
323
+ addr.parts.zip.includes(query)
324
+ ).slice(0, maxResults)
325
+
326
+ return results
327
+ }
328
+
329
+ // ============================================
330
+ // HOOK: useAddressAutocomplete
331
+ // ============================================
332
+
333
+ export function useAddressAutocomplete(
334
+ options: UseAddressAutocompleteOptions = {}
335
+ ): UseAddressAutocompleteReturn {
336
+ const {
337
+ onSearch,
338
+ debounceMs = 300,
339
+ minChars = 2,
340
+ maxSuggestions = 5,
341
+ initialRecentAddresses = [],
342
+ maxRecentAddresses = 5,
343
+ storageKey = "waka-address-recent",
344
+ } = options
345
+
346
+ const [query, setQuery] = React.useState("")
347
+ const [suggestions, setSuggestions] = React.useState<AddressSuggestion[]>([])
348
+ const [isLoading, setIsLoading] = React.useState(false)
349
+ const [error, setError] = React.useState<string | null>(null)
350
+ const [recentAddresses, setRecentAddresses] = React.useState<RecentAddress[]>(
351
+ initialRecentAddresses
352
+ )
353
+
354
+ const debounceTimerRef = React.useRef<NodeJS.Timeout | null>(null)
355
+ const abortControllerRef = React.useRef<AbortController | null>(null)
356
+
357
+ // Load recent addresses from storage on mount
358
+ React.useEffect(() => {
359
+ if (typeof window === "undefined") return
360
+
361
+ try {
362
+ const stored = localStorage.getItem(storageKey)
363
+ if (stored) {
364
+ const parsed = JSON.parse(stored) as RecentAddress[]
365
+ setRecentAddresses(
366
+ parsed.map((addr) => ({
367
+ ...addr,
368
+ lastUsed: new Date(addr.lastUsed),
369
+ }))
370
+ )
371
+ }
372
+ } catch {
373
+ // Ignore storage errors
374
+ }
375
+ }, [storageKey])
376
+
377
+ // Save recent addresses to storage
378
+ const saveRecentAddresses = React.useCallback(
379
+ (addresses: RecentAddress[]) => {
380
+ if (typeof window === "undefined") return
381
+
382
+ try {
383
+ localStorage.setItem(storageKey, JSON.stringify(addresses))
384
+ } catch {
385
+ // Ignore storage errors
386
+ }
387
+ },
388
+ [storageKey]
389
+ )
390
+
391
+ // Search function
392
+ const search = React.useCallback(
393
+ async (searchQuery: string) => {
394
+ if (searchQuery.length < minChars) {
395
+ setSuggestions([])
396
+ return
397
+ }
398
+
399
+ // Cancel previous request
400
+ if (abortControllerRef.current) {
401
+ abortControllerRef.current.abort()
402
+ }
403
+ abortControllerRef.current = new AbortController()
404
+
405
+ setIsLoading(true)
406
+ setError(null)
407
+
408
+ try {
409
+ const searchFn = onSearch || mockSearchAddresses
410
+ const results = await searchFn(searchQuery)
411
+ setSuggestions(results.slice(0, maxSuggestions))
412
+ } catch (err) {
413
+ if (err instanceof Error && err.name !== "AbortError") {
414
+ setError("Failed to search addresses. Please try again.")
415
+ setSuggestions([])
416
+ }
417
+ } finally {
418
+ setIsLoading(false)
419
+ }
420
+ },
421
+ [onSearch, minChars, maxSuggestions]
422
+ )
423
+
424
+ // Debounced search effect
425
+ React.useEffect(() => {
426
+ if (debounceTimerRef.current) {
427
+ clearTimeout(debounceTimerRef.current)
428
+ }
429
+
430
+ if (query.length >= minChars) {
431
+ debounceTimerRef.current = setTimeout(() => {
432
+ search(query)
433
+ }, debounceMs)
434
+ } else {
435
+ setSuggestions([])
436
+ }
437
+
438
+ return () => {
439
+ if (debounceTimerRef.current) {
440
+ clearTimeout(debounceTimerRef.current)
441
+ }
442
+ }
443
+ }, [query, debounceMs, minChars, search])
444
+
445
+ // Add to recent addresses
446
+ const addToRecent = React.useCallback(
447
+ (address: AddressSuggestion) => {
448
+ setRecentAddresses((prev) => {
449
+ const filtered = prev.filter((addr) => addr.id !== address.id)
450
+ const newRecent: RecentAddress = {
451
+ ...address,
452
+ lastUsed: new Date(),
453
+ }
454
+ const updated = [newRecent, ...filtered].slice(0, maxRecentAddresses)
455
+ saveRecentAddresses(updated)
456
+ return updated
457
+ })
458
+ },
459
+ [maxRecentAddresses, saveRecentAddresses]
460
+ )
461
+
462
+ // Clear recent addresses
463
+ const clearRecentAddresses = React.useCallback(() => {
464
+ setRecentAddresses([])
465
+ saveRecentAddresses([])
466
+ }, [saveRecentAddresses])
467
+
468
+ // Remove specific recent address
469
+ const removeFromRecent = React.useCallback(
470
+ (id: string) => {
471
+ setRecentAddresses((prev) => {
472
+ const updated = prev.filter((addr) => addr.id !== id)
473
+ saveRecentAddresses(updated)
474
+ return updated
475
+ })
476
+ },
477
+ [saveRecentAddresses]
478
+ )
479
+
480
+ // Clear suggestions
481
+ const clearSuggestions = React.useCallback(() => {
482
+ setSuggestions([])
483
+ setQuery("")
484
+ }, [])
485
+
486
+ // Parse address utility
487
+ const parseAddress = React.useCallback((addressString: string): AddressParts => {
488
+ return parseAddressString(addressString)
489
+ }, [])
490
+
491
+ return {
492
+ query,
493
+ setQuery,
494
+ suggestions,
495
+ isLoading,
496
+ error,
497
+ recentAddresses,
498
+ addToRecent,
499
+ clearRecentAddresses,
500
+ removeFromRecent,
501
+ search,
502
+ clearSuggestions,
503
+ parseAddress,
504
+ }
505
+ }
506
+
507
+ // ============================================
508
+ // CSS ANIMATIONS
509
+ // ============================================
510
+
511
+ const dropdownAnimationStyles = `
512
+ @keyframes waka-address-dropdown-in {
513
+ from {
514
+ opacity: 0;
515
+ transform: translateY(-8px) scale(0.96);
516
+ }
517
+ to {
518
+ opacity: 1;
519
+ transform: translateY(0) scale(1);
520
+ }
521
+ }
522
+
523
+ @keyframes waka-address-dropdown-out {
524
+ from {
525
+ opacity: 1;
526
+ transform: translateY(0) scale(1);
527
+ }
528
+ to {
529
+ opacity: 0;
530
+ transform: translateY(-8px) scale(0.96);
531
+ }
532
+ }
533
+
534
+ @keyframes waka-address-highlight {
535
+ 0% {
536
+ background-color: transparent;
537
+ }
538
+ 50% {
539
+ background-color: hsl(var(--primary) / 0.1);
540
+ }
541
+ 100% {
542
+ background-color: transparent;
543
+ }
544
+ }
545
+
546
+ @keyframes waka-address-pulse {
547
+ 0%, 100% {
548
+ opacity: 1;
549
+ }
550
+ 50% {
551
+ opacity: 0.5;
552
+ }
553
+ }
554
+
555
+ .waka-address-dropdown-enter {
556
+ animation: waka-address-dropdown-in 0.2s ease-out forwards;
557
+ }
558
+
559
+ .waka-address-dropdown-exit {
560
+ animation: waka-address-dropdown-out 0.15s ease-in forwards;
561
+ }
562
+
563
+ .waka-address-highlight {
564
+ animation: waka-address-highlight 0.5s ease-out;
565
+ }
566
+
567
+ .waka-address-pulse {
568
+ animation: waka-address-pulse 1.5s ease-in-out infinite;
569
+ }
570
+ `
571
+
572
+ // ============================================
573
+ // SUB-COMPONENTS
574
+ // ============================================
575
+
576
+ interface SuggestionItemProps {
577
+ suggestion: AddressSuggestion
578
+ isHighlighted: boolean
579
+ onSelect: () => void
580
+ onMouseEnter: () => void
581
+ }
582
+
583
+ function SuggestionItem({
584
+ suggestion,
585
+ isHighlighted,
586
+ onSelect,
587
+ onMouseEnter,
588
+ }: SuggestionItemProps) {
589
+ const Icon =
590
+ suggestion.type === "home"
591
+ ? Home
592
+ : suggestion.type === "business"
593
+ ? Building2
594
+ : MapPin
595
+
596
+ return (
597
+ <button
598
+ type="button"
599
+ role="option"
600
+ aria-selected={isHighlighted}
601
+ className={cn(
602
+ "w-full flex items-start gap-3 px-3 py-2.5 text-left transition-colors",
603
+ "hover:bg-accent focus:bg-accent focus:outline-none",
604
+ isHighlighted && "bg-accent"
605
+ )}
606
+ onClick={onSelect}
607
+ onMouseEnter={onMouseEnter}
608
+ >
609
+ <div
610
+ className={cn(
611
+ "flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center",
612
+ "bg-primary/10 text-primary"
613
+ )}
614
+ >
615
+ <Icon className="w-4 h-4" />
616
+ </div>
617
+ <div className="flex-1 min-w-0">
618
+ <p className="text-sm font-medium text-foreground truncate">
619
+ {suggestion.primary}
620
+ </p>
621
+ <p className="text-xs text-muted-foreground truncate">
622
+ {suggestion.secondary}
623
+ </p>
624
+ </div>
625
+ {suggestion.distance && (
626
+ <span className="flex-shrink-0 text-xs text-muted-foreground">
627
+ {suggestion.distance}
628
+ </span>
629
+ )}
630
+ </button>
631
+ )
632
+ }
633
+
634
+ interface RecentAddressItemProps {
635
+ address: RecentAddress
636
+ isHighlighted: boolean
637
+ onSelect: () => void
638
+ onRemove: () => void
639
+ onMouseEnter: () => void
640
+ }
641
+
642
+ function RecentAddressItem({
643
+ address,
644
+ isHighlighted,
645
+ onSelect,
646
+ onRemove,
647
+ onMouseEnter,
648
+ }: RecentAddressItemProps) {
649
+ return (
650
+ <div
651
+ role="option"
652
+ aria-selected={isHighlighted}
653
+ className={cn(
654
+ "flex items-center gap-2 px-3 py-2 transition-colors",
655
+ "hover:bg-accent",
656
+ isHighlighted && "bg-accent"
657
+ )}
658
+ onMouseEnter={onMouseEnter}
659
+ >
660
+ <button
661
+ type="button"
662
+ className="flex-1 flex items-start gap-3 text-left focus:outline-none"
663
+ onClick={onSelect}
664
+ >
665
+ <div className="flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-muted text-muted-foreground">
666
+ <Clock className="w-4 h-4" />
667
+ </div>
668
+ <div className="flex-1 min-w-0">
669
+ <p className="text-sm font-medium text-foreground truncate">
670
+ {address.primary}
671
+ </p>
672
+ <p className="text-xs text-muted-foreground truncate">
673
+ {address.secondary}
674
+ </p>
675
+ </div>
676
+ </button>
677
+ <button
678
+ type="button"
679
+ className="flex-shrink-0 p-1.5 rounded-md hover:bg-destructive/10 hover:text-destructive transition-colors"
680
+ onClick={(e) => {
681
+ e.stopPropagation()
682
+ onRemove()
683
+ }}
684
+ aria-label="Remove from recent"
685
+ >
686
+ <X className="w-3.5 h-3.5" />
687
+ </button>
688
+ </div>
689
+ )
690
+ }
691
+
692
+ interface SelectedAddressDisplayProps {
693
+ address: AddressSuggestion
694
+ onClear: () => void
695
+ onEdit: () => void
696
+ disabled?: boolean
697
+ }
698
+
699
+ function SelectedAddressDisplay({
700
+ address,
701
+ onClear,
702
+ onEdit,
703
+ disabled,
704
+ }: SelectedAddressDisplayProps) {
705
+ const Icon =
706
+ address.type === "home"
707
+ ? Home
708
+ : address.type === "business"
709
+ ? Building2
710
+ : MapPin
711
+
712
+ return (
713
+ <div
714
+ className={cn(
715
+ "flex items-start gap-3 p-3 rounded-lg border bg-card",
716
+ "transition-all duration-200",
717
+ disabled && "opacity-50 pointer-events-none"
718
+ )}
719
+ >
720
+ <div className="flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center bg-primary/10 text-primary">
721
+ <Icon className="w-5 h-5" />
722
+ </div>
723
+ <div className="flex-1 min-w-0">
724
+ <p className="text-sm font-medium text-foreground">{address.primary}</p>
725
+ <p className="text-xs text-muted-foreground mt-0.5">
726
+ {address.secondary}
727
+ </p>
728
+ <div className="flex flex-wrap gap-1.5 mt-2">
729
+ {address.parts.city && (
730
+ <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs bg-muted text-muted-foreground">
731
+ {address.parts.city}
732
+ </span>
733
+ )}
734
+ {address.parts.zip && (
735
+ <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs bg-muted text-muted-foreground">
736
+ {address.parts.zip}
737
+ </span>
738
+ )}
739
+ {address.parts.country && (
740
+ <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs bg-muted text-muted-foreground">
741
+ {address.parts.country}
742
+ </span>
743
+ )}
744
+ </div>
745
+ </div>
746
+ <div className="flex-shrink-0 flex items-center gap-1">
747
+ <button
748
+ type="button"
749
+ className="p-1.5 rounded-md hover:bg-accent transition-colors"
750
+ onClick={onEdit}
751
+ aria-label="Edit address"
752
+ >
753
+ <Edit3 className="w-4 h-4 text-muted-foreground" />
754
+ </button>
755
+ <button
756
+ type="button"
757
+ className="p-1.5 rounded-md hover:bg-destructive/10 hover:text-destructive transition-colors"
758
+ onClick={onClear}
759
+ aria-label="Clear address"
760
+ >
761
+ <X className="w-4 h-4" />
762
+ </button>
763
+ </div>
764
+ </div>
765
+ )
766
+ }
767
+
768
+ // ============================================
769
+ // MAIN COMPONENT
770
+ // ============================================
771
+
772
+ export const WakaAddressAutocomplete = React.forwardRef<
773
+ HTMLInputElement,
774
+ WakaAddressAutocompleteProps
775
+ >(
776
+ (
777
+ {
778
+ value,
779
+ onChange,
780
+ onSearch,
781
+ placeholder = "Search for an address...",
782
+ showRecentAddresses = true,
783
+ recentAddresses: externalRecentAddresses,
784
+ onAddToRecent,
785
+ allowManualEntry = true,
786
+ onManualEntry,
787
+ debounceMs = 300,
788
+ minChars = 2,
789
+ maxSuggestions = 5,
790
+ noResultsMessage = "No addresses found",
791
+ loadingMessage = "Searching...",
792
+ disabled = false,
793
+ error,
794
+ className,
795
+ id,
796
+ name,
797
+ label,
798
+ required = false,
799
+ autoFocus = false,
800
+ },
801
+ ref
802
+ ) => {
803
+ const inputRef = React.useRef<HTMLInputElement>(null)
804
+ const dropdownRef = React.useRef<HTMLDivElement>(null)
805
+ const [isOpen, setIsOpen] = React.useState(false)
806
+ const [highlightedIndex, setHighlightedIndex] = React.useState(-1)
807
+ const [isEditing, setIsEditing] = React.useState(!value)
808
+
809
+ // Use the hook
810
+ const {
811
+ query,
812
+ setQuery,
813
+ suggestions,
814
+ isLoading,
815
+ error: searchError,
816
+ recentAddresses: hookRecentAddresses,
817
+ addToRecent,
818
+ removeFromRecent,
819
+ } = useAddressAutocomplete({
820
+ onSearch,
821
+ debounceMs,
822
+ minChars,
823
+ maxSuggestions,
824
+ })
825
+
826
+ const recentAddresses = externalRecentAddresses ?? hookRecentAddresses
827
+ const displayError = error || searchError
828
+
829
+ // Combined ref
830
+ React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement)
831
+
832
+ // Inject styles
833
+ React.useEffect(() => {
834
+ if (typeof document === "undefined") return
835
+
836
+ const styleId = "waka-address-autocomplete-styles"
837
+ if (!document.getElementById(styleId)) {
838
+ const style = document.createElement("style")
839
+ style.id = styleId
840
+ style.textContent = dropdownAnimationStyles
841
+ document.head.appendChild(style)
842
+ }
843
+ }, [])
844
+
845
+ // Calculate what to show in dropdown
846
+ const showRecent =
847
+ showRecentAddresses &&
848
+ recentAddresses.length > 0 &&
849
+ query.length < minChars
850
+ const showSuggestions = suggestions.length > 0 && query.length >= minChars
851
+ const showNoResults =
852
+ !isLoading &&
853
+ query.length >= minChars &&
854
+ suggestions.length === 0
855
+ const showContent = showRecent || showSuggestions || showNoResults || isLoading
856
+
857
+ // All items for keyboard navigation
858
+ const allItems = React.useMemo(() => {
859
+ if (showRecent) {
860
+ return recentAddresses.map((addr) => ({
861
+ type: "recent" as const,
862
+ item: addr,
863
+ }))
864
+ }
865
+ if (showSuggestions) {
866
+ return suggestions.map((addr) => ({
867
+ type: "suggestion" as const,
868
+ item: addr,
869
+ }))
870
+ }
871
+ return []
872
+ }, [showRecent, showSuggestions, recentAddresses, suggestions])
873
+
874
+ // Handle selection
875
+ const handleSelect = React.useCallback(
876
+ (address: AddressSuggestion) => {
877
+ onChange?.(address)
878
+ addToRecent(address)
879
+ onAddToRecent?.(address)
880
+ setQuery("")
881
+ setIsOpen(false)
882
+ setIsEditing(false)
883
+ setHighlightedIndex(-1)
884
+ },
885
+ [onChange, addToRecent, onAddToRecent, setQuery]
886
+ )
887
+
888
+ // Handle clear
889
+ const handleClear = React.useCallback(() => {
890
+ onChange?.(null)
891
+ setQuery("")
892
+ setIsEditing(true)
893
+ inputRef.current?.focus()
894
+ }, [onChange, setQuery])
895
+
896
+ // Handle edit
897
+ const handleEdit = React.useCallback(() => {
898
+ setIsEditing(true)
899
+ setQuery(value?.parts.formatted || "")
900
+ setTimeout(() => inputRef.current?.focus(), 0)
901
+ }, [value, setQuery])
902
+
903
+ // Handle input change
904
+ const handleInputChange = React.useCallback(
905
+ (e: React.ChangeEvent<HTMLInputElement>) => {
906
+ setQuery(e.target.value)
907
+ setHighlightedIndex(-1)
908
+ if (!isOpen) {
909
+ setIsOpen(true)
910
+ }
911
+ },
912
+ [setQuery, isOpen]
913
+ )
914
+
915
+ // Handle focus
916
+ const handleFocus = React.useCallback(() => {
917
+ setIsOpen(true)
918
+ }, [])
919
+
920
+ // Handle blur
921
+ const handleBlur = React.useCallback((e: React.FocusEvent) => {
922
+ // Don't close if clicking inside dropdown
923
+ if (
924
+ dropdownRef.current &&
925
+ dropdownRef.current.contains(e.relatedTarget as Node)
926
+ ) {
927
+ return
928
+ }
929
+ // Delay to allow click events to fire
930
+ setTimeout(() => setIsOpen(false), 150)
931
+ }, [])
932
+
933
+ // Handle keyboard navigation
934
+ const handleKeyDown = React.useCallback(
935
+ (e: React.KeyboardEvent) => {
936
+ if (!isOpen) {
937
+ if (e.key === "ArrowDown" || e.key === "Enter") {
938
+ setIsOpen(true)
939
+ e.preventDefault()
940
+ }
941
+ return
942
+ }
943
+
944
+ switch (e.key) {
945
+ case "ArrowDown":
946
+ e.preventDefault()
947
+ setHighlightedIndex((prev) =>
948
+ prev < allItems.length - 1 ? prev + 1 : prev
949
+ )
950
+ break
951
+ case "ArrowUp":
952
+ e.preventDefault()
953
+ setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : -1))
954
+ break
955
+ case "Enter":
956
+ e.preventDefault()
957
+ if (highlightedIndex >= 0 && allItems[highlightedIndex]) {
958
+ handleSelect(allItems[highlightedIndex].item)
959
+ }
960
+ break
961
+ case "Escape":
962
+ e.preventDefault()
963
+ setIsOpen(false)
964
+ setHighlightedIndex(-1)
965
+ break
966
+ case "Tab":
967
+ setIsOpen(false)
968
+ setHighlightedIndex(-1)
969
+ break
970
+ }
971
+ },
972
+ [isOpen, allItems, highlightedIndex, handleSelect]
973
+ )
974
+
975
+ // Handle manual entry click
976
+ const handleManualEntryClick = React.useCallback(() => {
977
+ onManualEntry?.()
978
+ setIsOpen(false)
979
+ }, [onManualEntry])
980
+
981
+ // If we have a selected value and not editing, show the display
982
+ if (value && !isEditing) {
983
+ return (
984
+ <div className={cn("space-y-1.5", className)}>
985
+ {label && (
986
+ <label
987
+ htmlFor={id}
988
+ className="text-sm font-medium text-foreground"
989
+ >
990
+ {label}
991
+ {required && <span className="text-destructive ml-1">*</span>}
992
+ </label>
993
+ )}
994
+ <SelectedAddressDisplay
995
+ address={value}
996
+ onClear={handleClear}
997
+ onEdit={handleEdit}
998
+ disabled={disabled}
999
+ />
1000
+ {name && (
1001
+ <input
1002
+ type="hidden"
1003
+ name={name}
1004
+ value={value.parts.formatted}
1005
+ />
1006
+ )}
1007
+ </div>
1008
+ )
1009
+ }
1010
+
1011
+ return (
1012
+ <div className={cn("relative space-y-1.5", className)}>
1013
+ {label && (
1014
+ <label
1015
+ htmlFor={id}
1016
+ className="text-sm font-medium text-foreground"
1017
+ >
1018
+ {label}
1019
+ {required && <span className="text-destructive ml-1">*</span>}
1020
+ </label>
1021
+ )}
1022
+
1023
+ {/* Input container */}
1024
+ <div className="relative">
1025
+ <div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
1026
+ {isLoading ? (
1027
+ <Loader2 className="w-4 h-4 text-muted-foreground animate-spin" />
1028
+ ) : (
1029
+ <Search className="w-4 h-4 text-muted-foreground" />
1030
+ )}
1031
+ </div>
1032
+ <input
1033
+ ref={inputRef}
1034
+ type="text"
1035
+ id={id}
1036
+ name={name}
1037
+ role="combobox"
1038
+ aria-expanded={isOpen}
1039
+ aria-haspopup="listbox"
1040
+ aria-autocomplete="list"
1041
+ aria-controls={isOpen ? "address-listbox" : undefined}
1042
+ aria-activedescendant={
1043
+ highlightedIndex >= 0
1044
+ ? `address-option-${highlightedIndex}`
1045
+ : undefined
1046
+ }
1047
+ autoComplete="off"
1048
+ autoFocus={autoFocus}
1049
+ disabled={disabled}
1050
+ required={required}
1051
+ placeholder={placeholder}
1052
+ value={query}
1053
+ onChange={handleInputChange}
1054
+ onFocus={handleFocus}
1055
+ onBlur={handleBlur}
1056
+ onKeyDown={handleKeyDown}
1057
+ className={cn(
1058
+ "flex h-10 w-full rounded-md border border-input bg-background",
1059
+ "pl-10 pr-10 py-2 text-sm",
1060
+ "ring-offset-background",
1061
+ "placeholder:text-muted-foreground",
1062
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
1063
+ "disabled:cursor-not-allowed disabled:opacity-50",
1064
+ "transition-colors",
1065
+ displayError && "border-destructive focus-visible:ring-destructive"
1066
+ )}
1067
+ />
1068
+ <div className="absolute inset-y-0 right-0 flex items-center pr-2">
1069
+ {query && (
1070
+ <button
1071
+ type="button"
1072
+ className="p-1 rounded-md hover:bg-accent transition-colors"
1073
+ onClick={() => {
1074
+ setQuery("")
1075
+ inputRef.current?.focus()
1076
+ }}
1077
+ aria-label="Clear search"
1078
+ >
1079
+ <X className="w-4 h-4 text-muted-foreground" />
1080
+ </button>
1081
+ )}
1082
+ <button
1083
+ type="button"
1084
+ className="p-1 rounded-md hover:bg-accent transition-colors"
1085
+ onClick={() => setIsOpen(!isOpen)}
1086
+ aria-label="Toggle dropdown"
1087
+ >
1088
+ <ChevronDown
1089
+ className={cn(
1090
+ "w-4 h-4 text-muted-foreground transition-transform duration-200",
1091
+ isOpen && "rotate-180"
1092
+ )}
1093
+ />
1094
+ </button>
1095
+ </div>
1096
+ </div>
1097
+
1098
+ {/* Error message */}
1099
+ {displayError && (
1100
+ <p className="text-sm text-destructive">{displayError}</p>
1101
+ )}
1102
+
1103
+ {/* Dropdown */}
1104
+ {isOpen && showContent && (
1105
+ <div
1106
+ ref={dropdownRef}
1107
+ id="address-listbox"
1108
+ role="listbox"
1109
+ className={cn(
1110
+ "absolute z-50 w-full mt-1 py-1 rounded-md border bg-popover shadow-lg",
1111
+ "waka-address-dropdown-enter"
1112
+ )}
1113
+ >
1114
+ {/* Loading state */}
1115
+ {isLoading && (
1116
+ <div className="flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground">
1117
+ <Loader2 className="w-4 h-4 animate-spin" />
1118
+ <span>{loadingMessage}</span>
1119
+ </div>
1120
+ )}
1121
+
1122
+ {/* Recent addresses */}
1123
+ {showRecent && !isLoading && (
1124
+ <div>
1125
+ <div className="px-3 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider">
1126
+ Recent Addresses
1127
+ </div>
1128
+ {recentAddresses.map((address, index) => (
1129
+ <RecentAddressItem
1130
+ key={address.id}
1131
+ address={address}
1132
+ isHighlighted={highlightedIndex === index}
1133
+ onSelect={() => handleSelect(address)}
1134
+ onRemove={() => removeFromRecent(address.id)}
1135
+ onMouseEnter={() => setHighlightedIndex(index)}
1136
+ />
1137
+ ))}
1138
+ </div>
1139
+ )}
1140
+
1141
+ {/* Suggestions */}
1142
+ {showSuggestions && !isLoading && (
1143
+ <div>
1144
+ <div className="px-3 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider">
1145
+ Suggestions
1146
+ </div>
1147
+ {suggestions.map((suggestion, index) => (
1148
+ <SuggestionItem
1149
+ key={suggestion.id}
1150
+ suggestion={suggestion}
1151
+ isHighlighted={highlightedIndex === index}
1152
+ onSelect={() => handleSelect(suggestion)}
1153
+ onMouseEnter={() => setHighlightedIndex(index)}
1154
+ />
1155
+ ))}
1156
+ </div>
1157
+ )}
1158
+
1159
+ {/* No results */}
1160
+ {showNoResults && (
1161
+ <div className="py-6 text-center">
1162
+ <Navigation className="w-8 h-8 mx-auto text-muted-foreground/50 mb-2" />
1163
+ <p className="text-sm text-muted-foreground">{noResultsMessage}</p>
1164
+ {allowManualEntry && onManualEntry && (
1165
+ <button
1166
+ type="button"
1167
+ className="mt-2 text-sm text-primary hover:underline"
1168
+ onClick={handleManualEntryClick}
1169
+ >
1170
+ Enter address manually
1171
+ </button>
1172
+ )}
1173
+ </div>
1174
+ )}
1175
+
1176
+ {/* Manual entry option (when showing recent/suggestions) */}
1177
+ {allowManualEntry && onManualEntry && !showNoResults && !isLoading && (
1178
+ <div className="border-t mt-1 pt-1">
1179
+ <button
1180
+ type="button"
1181
+ className="w-full flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
1182
+ onClick={handleManualEntryClick}
1183
+ >
1184
+ <Edit3 className="w-4 h-4" />
1185
+ <span>Enter address manually</span>
1186
+ </button>
1187
+ </div>
1188
+ )}
1189
+ </div>
1190
+ )}
1191
+ </div>
1192
+ )
1193
+ }
1194
+ )
1195
+
1196
+ WakaAddressAutocomplete.displayName = "WakaAddressAutocomplete"
1197
+
1198
+ // ============================================
1199
+ // EXPORTS
1200
+ // ============================================
1201
+
1202
+ export default WakaAddressAutocomplete