@open-mercato/core 0.7.1-develop.7203.1.9396450b78 β†’ 0.8.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 (416) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/build.mjs +11 -0
  3. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +23 -0
  4. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  5. package/dist/modules/customers/backend/customers/companies/[id]/page.js +2 -1
  6. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  7. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +2 -1
  8. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  9. package/dist/modules/customers/backend/customers/people/[id]/page.js +2 -2
  10. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  11. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +2 -1
  12. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  13. package/dist/modules/customers/lib/detailHelpers.js +4 -0
  14. package/dist/modules/customers/lib/detailHelpers.js.map +2 -2
  15. package/dist/modules/customers/lib/todoCompatibility.js +5 -0
  16. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  17. package/dist/modules/design_system/backend/design-system/page.meta.js +3 -2
  18. package/dist/modules/design_system/backend/design-system/page.meta.js.map +2 -2
  19. package/dist/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
  20. package/dist/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
  21. package/dist/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
  22. package/dist/modules/design_system/gallery/assets/avatar-photo.png +0 -0
  23. package/dist/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
  24. package/dist/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
  25. package/dist/modules/design_system/gallery/assets/crypto-ada.png +0 -0
  26. package/dist/modules/design_system/gallery/assets/crypto-avax.png +0 -0
  27. package/dist/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
  28. package/dist/modules/design_system/gallery/assets/crypto-brand.png +0 -0
  29. package/dist/modules/design_system/gallery/assets/crypto-btc.png +0 -0
  30. package/dist/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
  31. package/dist/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
  32. package/dist/modules/design_system/gallery/assets/crypto-dot.png +0 -0
  33. package/dist/modules/design_system/gallery/assets/crypto-eth.png +0 -0
  34. package/dist/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
  35. package/dist/modules/design_system/gallery/assets/crypto-history.svg +3 -0
  36. package/dist/modules/design_system/gallery/assets/crypto-link.svg +3 -0
  37. package/dist/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
  38. package/dist/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
  39. package/dist/modules/design_system/gallery/assets/crypto-more.svg +3 -0
  40. package/dist/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
  41. package/dist/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
  42. package/dist/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
  43. package/dist/modules/design_system/gallery/assets/crypto-sol.png +0 -0
  44. package/dist/modules/design_system/gallery/assets/crypto-star.svg +3 -0
  45. package/dist/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
  46. package/dist/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
  47. package/dist/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
  48. package/dist/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
  49. package/dist/modules/design_system/gallery/assets/crypto-trx.png +0 -0
  50. package/dist/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
  51. package/dist/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
  52. package/dist/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
  53. package/dist/modules/design_system/gallery/assets/crypto-wei.png +0 -0
  54. package/dist/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
  55. package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js +67 -0
  56. package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js.map +7 -0
  57. package/dist/modules/design_system/gallery/assets/github-logo.png +0 -0
  58. package/dist/modules/design_system/gallery/assets/images.d.js +1 -0
  59. package/dist/modules/design_system/gallery/assets/images.d.js.map +7 -0
  60. package/dist/modules/design_system/gallery/assets/input-european-union.svg +18 -0
  61. package/dist/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
  62. package/dist/modules/design_system/gallery/assets/marketing-artwork.js +83 -0
  63. package/dist/modules/design_system/gallery/assets/marketing-artwork.js.map +7 -0
  64. package/dist/modules/design_system/gallery/assets/menu-apex.svg +18 -0
  65. package/dist/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
  66. package/dist/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
  67. package/dist/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
  68. package/dist/modules/design_system/gallery/assets/select-google.png +0 -0
  69. package/dist/modules/design_system/gallery/assets/select-poland.png +0 -0
  70. package/dist/modules/design_system/gallery/assets/selection-artwork.js +5 -0
  71. package/dist/modules/design_system/gallery/assets/selection-artwork.js.map +7 -0
  72. package/dist/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
  73. package/dist/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
  74. package/dist/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
  75. package/dist/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
  76. package/dist/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
  77. package/dist/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
  78. package/dist/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
  79. package/dist/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
  80. package/dist/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
  81. package/dist/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
  82. package/dist/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
  83. package/dist/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
  84. package/dist/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
  85. package/dist/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
  86. package/dist/modules/design_system/gallery/assets/style-studio-product.png +0 -0
  87. package/dist/modules/design_system/gallery/assets/upload-images.js +8 -0
  88. package/dist/modules/design_system/gallery/assets/upload-images.js.map +7 -0
  89. package/dist/modules/design_system/gallery/components/BrandStylePreview.js +74 -0
  90. package/dist/modules/design_system/gallery/components/BrandStylePreview.js.map +7 -0
  91. package/dist/modules/design_system/gallery/components/CodeSnippet.js +31 -31
  92. package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +2 -2
  93. package/dist/modules/design_system/gallery/components/ColorPlayground.js +408 -0
  94. package/dist/modules/design_system/gallery/components/ColorPlayground.js.map +7 -0
  95. package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js +166 -0
  96. package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js.map +7 -0
  97. package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js +182 -0
  98. package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js.map +7 -0
  99. package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js +80 -0
  100. package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js.map +7 -0
  101. package/dist/modules/design_system/gallery/components/ComponentIndex.js +42 -0
  102. package/dist/modules/design_system/gallery/components/ComponentIndex.js.map +7 -0
  103. package/dist/modules/design_system/gallery/components/DesignSystemHome.js +69 -0
  104. package/dist/modules/design_system/gallery/components/DesignSystemHome.js.map +7 -0
  105. package/dist/modules/design_system/gallery/components/EntryCard.js +47 -41
  106. package/dist/modules/design_system/gallery/components/EntryCard.js.map +2 -2
  107. package/dist/modules/design_system/gallery/components/EntryHeader.js +68 -0
  108. package/dist/modules/design_system/gallery/components/EntryHeader.js.map +7 -0
  109. package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js +19 -0
  110. package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js.map +7 -0
  111. package/dist/modules/design_system/gallery/components/FoundationGuide.js +46 -0
  112. package/dist/modules/design_system/gallery/components/FoundationGuide.js.map +7 -0
  113. package/dist/modules/design_system/gallery/components/GalleryLink.js +21 -0
  114. package/dist/modules/design_system/gallery/components/GalleryLink.js.map +7 -0
  115. package/dist/modules/design_system/gallery/components/GalleryNavigation.js +82 -0
  116. package/dist/modules/design_system/gallery/components/GalleryNavigation.js.map +7 -0
  117. package/dist/modules/design_system/gallery/components/GalleryShell.js +147 -67
  118. package/dist/modules/design_system/gallery/components/GalleryShell.js.map +3 -3
  119. package/dist/modules/design_system/gallery/components/IconsBrowser.js +33 -0
  120. package/dist/modules/design_system/gallery/components/IconsBrowser.js.map +7 -0
  121. package/dist/modules/design_system/gallery/components/SourceLibrary.js +93 -0
  122. package/dist/modules/design_system/gallery/components/SourceLibrary.js.map +7 -0
  123. package/dist/modules/design_system/gallery/components/StyleAgents.js +139 -0
  124. package/dist/modules/design_system/gallery/components/StyleAgents.js.map +7 -0
  125. package/dist/modules/design_system/gallery/components/ThemeTokenReference.js +101 -0
  126. package/dist/modules/design_system/gallery/components/ThemeTokenReference.js.map +7 -0
  127. package/dist/modules/design_system/gallery/components/VariantPreview.js +2 -2
  128. package/dist/modules/design_system/gallery/components/VariantPreview.js.map +2 -2
  129. package/dist/modules/design_system/gallery/components/colorPreview.js +58 -0
  130. package/dist/modules/design_system/gallery/components/colorPreview.js.map +7 -0
  131. package/dist/modules/design_system/gallery/components/colorStudio.js +218 -0
  132. package/dist/modules/design_system/gallery/components/colorStudio.js.map +7 -0
  133. package/dist/modules/design_system/gallery/components/styleAgentBranding.js +46 -0
  134. package/dist/modules/design_system/gallery/components/styleAgentBranding.js.map +7 -0
  135. package/dist/modules/design_system/gallery/components/themeTokens.js +32 -0
  136. package/dist/modules/design_system/gallery/components/themeTokens.js.map +7 -0
  137. package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js +194 -0
  138. package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js.map +7 -0
  139. package/dist/modules/design_system/gallery/demos/ai-product.js +423 -0
  140. package/dist/modules/design_system/gallery/demos/ai-product.js.map +7 -0
  141. package/dist/modules/design_system/gallery/demos/buttons.js +289 -0
  142. package/dist/modules/design_system/gallery/demos/buttons.js.map +7 -0
  143. package/dist/modules/design_system/gallery/demos/cryptocurrency.js +1708 -0
  144. package/dist/modules/design_system/gallery/demos/cryptocurrency.js.map +7 -0
  145. package/dist/modules/design_system/gallery/demos/feeds-code.generated.js +92 -0
  146. package/dist/modules/design_system/gallery/demos/feeds-code.generated.js.map +7 -0
  147. package/dist/modules/design_system/gallery/demos/feeds.js +148 -0
  148. package/dist/modules/design_system/gallery/demos/feeds.js.map +7 -0
  149. package/dist/modules/design_system/gallery/demos/file-upload.js +72 -0
  150. package/dist/modules/design_system/gallery/demos/file-upload.js.map +7 -0
  151. package/dist/modules/design_system/gallery/demos/foundation-reference-data.js +4875 -0
  152. package/dist/modules/design_system/gallery/demos/foundation-reference-data.js.map +7 -0
  153. package/dist/modules/design_system/gallery/demos/foundation-references.js +188 -0
  154. package/dist/modules/design_system/gallery/demos/foundation-references.js.map +7 -0
  155. package/dist/modules/design_system/gallery/demos/key-components-code.generated.js +6 -0
  156. package/dist/modules/design_system/gallery/demos/key-components-code.generated.js.map +7 -0
  157. package/dist/modules/design_system/gallery/demos/key-components.js +127 -0
  158. package/dist/modules/design_system/gallery/demos/key-components.js.map +7 -0
  159. package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js +98 -0
  160. package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js.map +7 -0
  161. package/dist/modules/design_system/gallery/demos/marketing-controls.js +134 -0
  162. package/dist/modules/design_system/gallery/demos/marketing-controls.js.map +7 -0
  163. package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js +195 -0
  164. package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js.map +7 -0
  165. package/dist/modules/design_system/gallery/demos/marketing-widgets.js +586 -0
  166. package/dist/modules/design_system/gallery/demos/marketing-widgets.js.map +7 -0
  167. package/dist/modules/design_system/gallery/demos/menu-fixtures.js +40 -0
  168. package/dist/modules/design_system/gallery/demos/menu-fixtures.js.map +7 -0
  169. package/dist/modules/design_system/gallery/demos/menu-sources.js +295 -0
  170. package/dist/modules/design_system/gallery/demos/menu-sources.js.map +7 -0
  171. package/dist/modules/design_system/gallery/demos/menus.js +150 -0
  172. package/dist/modules/design_system/gallery/demos/menus.js.map +7 -0
  173. package/dist/modules/design_system/gallery/demos/rating-reviews.js +53 -0
  174. package/dist/modules/design_system/gallery/demos/rating-reviews.js.map +7 -0
  175. package/dist/modules/design_system/gallery/demos/registered-icons.js +104 -0
  176. package/dist/modules/design_system/gallery/demos/registered-icons.js.map +7 -0
  177. package/dist/modules/design_system/gallery/demos/selection-cards.js +99 -0
  178. package/dist/modules/design_system/gallery/demos/selection-cards.js.map +7 -0
  179. package/dist/modules/design_system/gallery/demos/shell-code.generated.js +189 -0
  180. package/dist/modules/design_system/gallery/demos/shell-code.generated.js.map +7 -0
  181. package/dist/modules/design_system/gallery/demos/shell.js +330 -0
  182. package/dist/modules/design_system/gallery/demos/shell.js.map +7 -0
  183. package/dist/modules/design_system/gallery/demos/source-artwork.js +120 -0
  184. package/dist/modules/design_system/gallery/demos/source-artwork.js.map +7 -0
  185. package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js +125 -0
  186. package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js.map +7 -0
  187. package/dist/modules/design_system/gallery/demos/source-filters.js +185 -0
  188. package/dist/modules/design_system/gallery/demos/source-filters.js.map +7 -0
  189. package/dist/modules/design_system/gallery/demos/source-icons.js +69 -0
  190. package/dist/modules/design_system/gallery/demos/source-icons.js.map +7 -0
  191. package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js +150 -0
  192. package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js.map +7 -0
  193. package/dist/modules/design_system/gallery/demos/table-cells.js +181 -0
  194. package/dist/modules/design_system/gallery/demos/table-cells.js.map +7 -0
  195. package/dist/modules/design_system/gallery/demos/text-inputs.js +113 -0
  196. package/dist/modules/design_system/gallery/demos/text-inputs.js.map +7 -0
  197. package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js +41 -0
  198. package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js.map +7 -0
  199. package/dist/modules/design_system/gallery/demos/time-picker.js +41 -0
  200. package/dist/modules/design_system/gallery/demos/time-picker.js.map +7 -0
  201. package/dist/modules/design_system/gallery/entries/banners.js +123 -60
  202. package/dist/modules/design_system/gallery/entries/banners.js.map +2 -2
  203. package/dist/modules/design_system/gallery/entries/buttons-snippets.js +433 -0
  204. package/dist/modules/design_system/gallery/entries/buttons-snippets.js.map +7 -0
  205. package/dist/modules/design_system/gallery/entries/buttons.js +127 -148
  206. package/dist/modules/design_system/gallery/entries/buttons.js.map +2 -2
  207. package/dist/modules/design_system/gallery/entries/charts.js +532 -186
  208. package/dist/modules/design_system/gallery/entries/charts.js.map +2 -2
  209. package/dist/modules/design_system/gallery/entries/dates.js +191 -3
  210. package/dist/modules/design_system/gallery/entries/dates.js.map +2 -2
  211. package/dist/modules/design_system/gallery/entries/detail.js +118 -77
  212. package/dist/modules/design_system/gallery/entries/detail.js.map +2 -2
  213. package/dist/modules/design_system/gallery/entries/display.js +1449 -240
  214. package/dist/modules/design_system/gallery/entries/display.js.map +2 -2
  215. package/dist/modules/design_system/gallery/entries/feedback.js +1058 -132
  216. package/dist/modules/design_system/gallery/entries/feedback.js.map +2 -2
  217. package/dist/modules/design_system/gallery/entries/filters.js +231 -103
  218. package/dist/modules/design_system/gallery/entries/filters.js.map +2 -2
  219. package/dist/modules/design_system/gallery/entries/foundations.js +368 -57
  220. package/dist/modules/design_system/gallery/entries/foundations.js.map +2 -2
  221. package/dist/modules/design_system/gallery/entries/icons.js +29 -91
  222. package/dist/modules/design_system/gallery/entries/icons.js.map +2 -2
  223. package/dist/modules/design_system/gallery/entries/inputs-snippets.js +154 -0
  224. package/dist/modules/design_system/gallery/entries/inputs-snippets.js.map +7 -0
  225. package/dist/modules/design_system/gallery/entries/inputs.js +2043 -306
  226. package/dist/modules/design_system/gallery/entries/inputs.js.map +2 -2
  227. package/dist/modules/design_system/gallery/entries/library.js +20 -0
  228. package/dist/modules/design_system/gallery/entries/library.js.map +7 -0
  229. package/dist/modules/design_system/gallery/entries/messages.js +121 -103
  230. package/dist/modules/design_system/gallery/entries/messages.js.map +2 -2
  231. package/dist/modules/design_system/gallery/entries/navigation.js +676 -103
  232. package/dist/modules/design_system/gallery/entries/navigation.js.map +2 -2
  233. package/dist/modules/design_system/gallery/entries/notifications.js +131 -78
  234. package/dist/modules/design_system/gallery/entries/notifications.js.map +2 -2
  235. package/dist/modules/design_system/gallery/entries/overlays.js +611 -203
  236. package/dist/modules/design_system/gallery/entries/overlays.js.map +2 -2
  237. package/dist/modules/design_system/gallery/entries/scaffolding.js +1175 -120
  238. package/dist/modules/design_system/gallery/entries/scaffolding.js.map +2 -2
  239. package/dist/modules/design_system/gallery/entries/schedule.js +79 -54
  240. package/dist/modules/design_system/gallery/entries/schedule.js.map +2 -2
  241. package/dist/modules/design_system/gallery/presentation.js +108 -0
  242. package/dist/modules/design_system/gallery/presentation.js.map +7 -0
  243. package/dist/modules/design_system/gallery/registry.js +8 -5
  244. package/dist/modules/design_system/gallery/registry.js.map +2 -2
  245. package/dist/modules/design_system/gallery/source-library.generated.js +5 -0
  246. package/dist/modules/design_system/gallery/source-library.generated.js.map +7 -0
  247. package/dist/modules/design_system/i18n/de.json +7167 -0
  248. package/dist/modules/design_system/i18n/en.json +7167 -0
  249. package/dist/modules/design_system/i18n/es.json +7167 -0
  250. package/dist/modules/design_system/i18n/ko.json +7167 -0
  251. package/dist/modules/design_system/i18n/pl.json +7167 -0
  252. package/dist/modules/push_notifications/lib/queue.js +1 -31
  253. package/dist/modules/push_notifications/lib/queue.js.map +2 -2
  254. package/jest.config.cjs +1 -0
  255. package/jest.mocks/image.js +1 -0
  256. package/package.json +8 -9
  257. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +50 -0
  258. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +2 -1
  259. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +2 -1
  260. package/src/modules/customers/backend/customers/people/[id]/page.tsx +2 -2
  261. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +2 -1
  262. package/src/modules/customers/lib/detailHelpers.ts +4 -0
  263. package/src/modules/customers/lib/todoCompatibility.ts +10 -0
  264. package/src/modules/design_system/backend/design-system/page.meta.ts +3 -2
  265. package/src/modules/design_system/gallery/assets/README.md +30 -0
  266. package/src/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
  267. package/src/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
  268. package/src/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
  269. package/src/modules/design_system/gallery/assets/avatar-photo.png +0 -0
  270. package/src/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
  271. package/src/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
  272. package/src/modules/design_system/gallery/assets/crypto-ada.png +0 -0
  273. package/src/modules/design_system/gallery/assets/crypto-avax.png +0 -0
  274. package/src/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
  275. package/src/modules/design_system/gallery/assets/crypto-brand.png +0 -0
  276. package/src/modules/design_system/gallery/assets/crypto-btc.png +0 -0
  277. package/src/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
  278. package/src/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
  279. package/src/modules/design_system/gallery/assets/crypto-dot.png +0 -0
  280. package/src/modules/design_system/gallery/assets/crypto-eth.png +0 -0
  281. package/src/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
  282. package/src/modules/design_system/gallery/assets/crypto-history.svg +3 -0
  283. package/src/modules/design_system/gallery/assets/crypto-link.svg +3 -0
  284. package/src/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
  285. package/src/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
  286. package/src/modules/design_system/gallery/assets/crypto-more.svg +3 -0
  287. package/src/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
  288. package/src/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
  289. package/src/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
  290. package/src/modules/design_system/gallery/assets/crypto-sol.png +0 -0
  291. package/src/modules/design_system/gallery/assets/crypto-star.svg +3 -0
  292. package/src/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
  293. package/src/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
  294. package/src/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
  295. package/src/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
  296. package/src/modules/design_system/gallery/assets/crypto-trx.png +0 -0
  297. package/src/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
  298. package/src/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
  299. package/src/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
  300. package/src/modules/design_system/gallery/assets/crypto-wei.png +0 -0
  301. package/src/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
  302. package/src/modules/design_system/gallery/assets/cryptocurrency-artwork.ts +63 -0
  303. package/src/modules/design_system/gallery/assets/github-logo.png +0 -0
  304. package/src/modules/design_system/gallery/assets/images.d.ts +9 -0
  305. package/src/modules/design_system/gallery/assets/input-european-union.svg +18 -0
  306. package/src/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
  307. package/src/modules/design_system/gallery/assets/marketing-artwork.ts +79 -0
  308. package/src/modules/design_system/gallery/assets/menu-apex.svg +18 -0
  309. package/src/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
  310. package/src/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
  311. package/src/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
  312. package/src/modules/design_system/gallery/assets/select-google.png +0 -0
  313. package/src/modules/design_system/gallery/assets/select-poland.png +0 -0
  314. package/src/modules/design_system/gallery/assets/selection-artwork.ts +1 -0
  315. package/src/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
  316. package/src/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
  317. package/src/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
  318. package/src/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
  319. package/src/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
  320. package/src/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
  321. package/src/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
  322. package/src/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
  323. package/src/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
  324. package/src/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
  325. package/src/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
  326. package/src/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
  327. package/src/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
  328. package/src/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
  329. package/src/modules/design_system/gallery/assets/style-studio-product.png +0 -0
  330. package/src/modules/design_system/gallery/assets/upload-images.ts +5 -0
  331. package/src/modules/design_system/gallery/components/BrandStylePreview.tsx +94 -0
  332. package/src/modules/design_system/gallery/components/CodeSnippet.tsx +34 -34
  333. package/src/modules/design_system/gallery/components/ColorPlayground.tsx +324 -0
  334. package/src/modules/design_system/gallery/components/ColorStudioCompositions.tsx +96 -0
  335. package/src/modules/design_system/gallery/components/ColorStudioSpecimen.tsx +164 -0
  336. package/src/modules/design_system/gallery/components/CompactEntrySpecimen.tsx +65 -0
  337. package/src/modules/design_system/gallery/components/ComponentIndex.tsx +47 -0
  338. package/src/modules/design_system/gallery/components/DesignSystemHome.tsx +66 -0
  339. package/src/modules/design_system/gallery/components/EntryCard.tsx +52 -37
  340. package/src/modules/design_system/gallery/components/EntryHeader.tsx +80 -0
  341. package/src/modules/design_system/gallery/components/FamilyEntryPreview.tsx +22 -0
  342. package/src/modules/design_system/gallery/components/FoundationGuide.tsx +43 -0
  343. package/src/modules/design_system/gallery/components/GalleryLink.tsx +19 -0
  344. package/src/modules/design_system/gallery/components/GalleryNavigation.tsx +86 -0
  345. package/src/modules/design_system/gallery/components/GalleryShell.tsx +149 -63
  346. package/src/modules/design_system/gallery/components/IconsBrowser.tsx +40 -0
  347. package/src/modules/design_system/gallery/components/SourceLibrary.tsx +90 -0
  348. package/src/modules/design_system/gallery/components/StyleAgents.tsx +115 -0
  349. package/src/modules/design_system/gallery/components/ThemeTokenReference.tsx +76 -0
  350. package/src/modules/design_system/gallery/components/VariantPreview.tsx +2 -2
  351. package/src/modules/design_system/gallery/components/colorPreview.ts +63 -0
  352. package/src/modules/design_system/gallery/components/colorStudio.ts +260 -0
  353. package/src/modules/design_system/gallery/components/styleAgentBranding.ts +42 -0
  354. package/src/modules/design_system/gallery/components/themeTokens.ts +26 -0
  355. package/src/modules/design_system/gallery/demos/ai-product-code.generated.ts +3 -0
  356. package/src/modules/design_system/gallery/demos/ai-product.tsx +188 -0
  357. package/src/modules/design_system/gallery/demos/buttons.tsx +331 -0
  358. package/src/modules/design_system/gallery/demos/cryptocurrency.tsx +1943 -0
  359. package/src/modules/design_system/gallery/demos/feeds-code.generated.ts +4 -0
  360. package/src/modules/design_system/gallery/demos/feeds.tsx +82 -0
  361. package/src/modules/design_system/gallery/demos/file-upload.tsx +61 -0
  362. package/src/modules/design_system/gallery/demos/foundation-reference-data.ts +4872 -0
  363. package/src/modules/design_system/gallery/demos/foundation-references.tsx +140 -0
  364. package/src/modules/design_system/gallery/demos/key-components-code.generated.ts +3 -0
  365. package/src/modules/design_system/gallery/demos/key-components.tsx +106 -0
  366. package/src/modules/design_system/gallery/demos/marketing-controls-code.generated.ts +3 -0
  367. package/src/modules/design_system/gallery/demos/marketing-controls.tsx +92 -0
  368. package/src/modules/design_system/gallery/demos/marketing-widgets-code.generated.ts +3 -0
  369. package/src/modules/design_system/gallery/demos/marketing-widgets.tsx +189 -0
  370. package/src/modules/design_system/gallery/demos/menu-fixtures.tsx +48 -0
  371. package/src/modules/design_system/gallery/demos/menu-sources.ts +5 -0
  372. package/src/modules/design_system/gallery/demos/menus.tsx +122 -0
  373. package/src/modules/design_system/gallery/demos/rating-reviews.tsx +44 -0
  374. package/src/modules/design_system/gallery/demos/registered-icons.tsx +117 -0
  375. package/src/modules/design_system/gallery/demos/selection-cards.tsx +97 -0
  376. package/src/modules/design_system/gallery/demos/shell-code.generated.ts +3 -0
  377. package/src/modules/design_system/gallery/demos/shell.tsx +183 -0
  378. package/src/modules/design_system/gallery/demos/source-artwork.tsx +88 -0
  379. package/src/modules/design_system/gallery/demos/source-filters-code.generated.ts +3 -0
  380. package/src/modules/design_system/gallery/demos/source-filters.tsx +119 -0
  381. package/src/modules/design_system/gallery/demos/source-icons.tsx +62 -0
  382. package/src/modules/design_system/gallery/demos/table-cells-code.generated.ts +3 -0
  383. package/src/modules/design_system/gallery/demos/table-cells.tsx +144 -0
  384. package/src/modules/design_system/gallery/demos/text-inputs.tsx +90 -0
  385. package/src/modules/design_system/gallery/demos/time-picker-code.generated.ts +5 -0
  386. package/src/modules/design_system/gallery/demos/time-picker.tsx +31 -0
  387. package/src/modules/design_system/gallery/entries/banners.tsx +143 -94
  388. package/src/modules/design_system/gallery/entries/buttons-snippets.ts +432 -0
  389. package/src/modules/design_system/gallery/entries/buttons.tsx +125 -196
  390. package/src/modules/design_system/gallery/entries/charts.tsx +545 -233
  391. package/src/modules/design_system/gallery/entries/dates.tsx +171 -3
  392. package/src/modules/design_system/gallery/entries/detail.tsx +142 -115
  393. package/src/modules/design_system/gallery/entries/display.tsx +1691 -319
  394. package/src/modules/design_system/gallery/entries/feedback.tsx +1082 -169
  395. package/src/modules/design_system/gallery/entries/filters.tsx +238 -143
  396. package/src/modules/design_system/gallery/entries/foundations.tsx +404 -61
  397. package/src/modules/design_system/gallery/entries/icons.tsx +31 -117
  398. package/src/modules/design_system/gallery/entries/inputs-snippets.ts +156 -0
  399. package/src/modules/design_system/gallery/entries/inputs.tsx +2146 -503
  400. package/src/modules/design_system/gallery/entries/library.tsx +16 -0
  401. package/src/modules/design_system/gallery/entries/messages.tsx +114 -106
  402. package/src/modules/design_system/gallery/entries/navigation.tsx +782 -192
  403. package/src/modules/design_system/gallery/entries/notifications.tsx +145 -120
  404. package/src/modules/design_system/gallery/entries/overlays.tsx +696 -280
  405. package/src/modules/design_system/gallery/entries/scaffolding.tsx +1192 -158
  406. package/src/modules/design_system/gallery/entries/schedule.tsx +72 -58
  407. package/src/modules/design_system/gallery/presentation.ts +106 -0
  408. package/src/modules/design_system/gallery/registry.ts +8 -6
  409. package/src/modules/design_system/gallery/source-library.generated.ts +2 -0
  410. package/src/modules/design_system/i18n/de.json +7112 -3
  411. package/src/modules/design_system/i18n/en.json +7112 -3
  412. package/src/modules/design_system/i18n/es.json +7112 -3
  413. package/src/modules/design_system/i18n/ko.json +7112 -3
  414. package/src/modules/design_system/i18n/pl.json +7112 -3
  415. package/src/modules/push_notifications/AGENTS.md +9 -5
  416. package/src/modules/push_notifications/lib/queue.ts +15 -44
@@ -0,0 +1,3 @@
1
+ // Generated by yarn storybook:generate from source-filters.tsx.
2
+ const source = "import * as React from 'react'\nimport { CalendarDays, Check, Filter, Settings2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { ButtonGroup } from '@open-mercato/ui/primitives/button-group'\nimport { Calendar } from '@open-mercato/ui/primitives/calendar'\nimport { CheckboxField } from '@open-mercato/ui/primitives/checkbox-field'\nimport { FilterToolbar, FilterPanelItem, FilterPanelHeader, FilterPanelFooter } from '@open-mercato/ui/primitives/filter-toolbar'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { SearchInput } from '@open-mercato/ui/primitives/search-input'\nimport { SegmentedControl, SegmentedControlItem } from '@open-mercato/ui/primitives/segmented-control'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@open-mercato/ui/primitives/table'\n\ntype Filters = { date?: Date; statuses: string[] }\nconst transactionRows = [\n { name: 'Apex', type: 'income', date: new Date(2023, 7, 11), amount: 1240, status: 'completed' },\n { name: 'Spotify', type: 'expense', date: new Date(2023, 7, 10), amount: -12, status: 'completed' },\n { name: 'Synergy', type: 'income', date: new Date(2023, 7, 9), amount: 680, status: 'pending' },\n { name: 'Loom', type: 'expense', date: new Date(2023, 7, 11), amount: -18, status: 'pending' },\n]\nconst emptyFilters = (): Filters => ({ statuses: [] })\n\nfunction useFilterCopy() {\n const t = useT()\n return (key: string, values?: Record<string, string | number>) => t(`design_system.gallery.examples.sourceFilters.${key}`, values)\n}\n\nexport function SourceFilterPanel({ value, onApply }: { value?: Filters; onApply?: (value: Filters) => void }) {\n const copy = useFilterCopy()\n const [draft, setDraft] = React.useState<Filters>(() => value ?? emptyFilters())\n const [section, setSection] = React.useState('date')\n const [applied, setApplied] = React.useState(false)\n const panelId = React.useId()\n function clear() { setDraft(emptyFilters()); setApplied(false) }\n return <div className=\"w-full min-w-0 bg-background\" data-example=\"filter-panel\">\n <div className=\"flex flex-col sm:flex-row\">\n <div className=\"flex shrink-0 flex-wrap gap-1 border-b border-border p-3 sm:w-[224px] sm:flex-col sm:border-b-0 sm:border-r\" role=\"group\" aria-label={copy('categories')}>\n <FilterPanelItem active={section === 'date'} leading={<CalendarDays />} aria-controls={panelId} onClick={() => setSection('date')}>{copy('date')}</FilterPanelItem>\n <FilterPanelItem active={section === 'status'} leading={<Check />} aria-controls={panelId} onClick={() => setSection('status')}>{copy('status')}</FilterPanelItem>\n </div>\n <section id={panelId} aria-label={copy(section)} className=\"min-w-0 flex-1\">\n <FilterPanelHeader title={copy(section)} leading={section === 'date' ? <CalendarDays /> : <Check />} action={<Button variant=\"link\" className=\"h-auto p-0 text-sm leading-5 text-muted-foreground\" onClick={clear}>{copy('clear')}</Button>} />\n <div className=\"flex min-h-80 justify-center px-5 pb-5\">\n {section === 'date'\n ? <Calendar mode=\"single\" defaultMonth={draft.date ?? new Date(2023, 7, 1)} selected={draft.date} onSelect={date => { setDraft(previous => ({ ...previous, date })); setApplied(false) }} />\n : <div className=\"w-full space-y-4 pt-2\">{['completed', 'pending'].map(status => <CheckboxField key={status} label={copy(status)} checked={draft.statuses.includes(status)} onCheckedChange={checked => { setDraft(previous => ({ ...previous, statuses: checked ? [...previous.statuses, status] : previous.statuses.filter(item => item !== status) })); setApplied(false) }} />)}</div>}\n </div>\n </section>\n </div>\n <FilterPanelFooter><Button variant=\"outline\" onClick={clear}>{copy('clear')}</Button><Button variant=\"primary-filled\" onClick={() => { onApply?.(draft); setApplied(true) }}>{copy('apply')}</Button></FilterPanelFooter>\n {applied ? <p role=\"status\" className=\"px-5 pb-4 text-sm text-muted-foreground\">{copy('applied')}</p> : null}\n </div>\n}\n\nexport function SourceFilterParts() {\n const copy = useFilterCopy()\n const [active, setActive] = React.useState(2)\n const [notice, setNotice] = React.useState('')\n return <div className=\"grid w-full gap-6\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n {['Default', 'Hover', 'Active'].map((state, index) => <div key={state} className=\"w-[200px] max-w-full space-y-2\">\n <p className=\"text-xs text-muted-foreground\">{state}</p>\n <FilterPanelItem active={active === index} leading={<CalendarDays />} onClick={() => setActive(index)}>{copy('date')}</FilterPanelItem>\n </div>)}\n </div>\n <div className=\"w-[400px] max-w-full rounded-md border border-border\"><FilterPanelHeader title={copy('date')} leading={<CalendarDays />} action={<Button variant=\"link\" className=\"h-auto p-0 text-sm leading-5 text-muted-foreground\" onClick={() => { setActive(-1); setNotice(copy('cleared')) }}>{copy('clear')}</Button>} /></div>\n <div className=\"w-[472px] max-w-full\"><FilterPanelFooter><Button variant=\"outline\" onClick={() => { setActive(-1); setNotice(copy('cleared')) }}>{copy('clear')}</Button><Button variant=\"primary-filled\" onClick={() => setNotice(copy('applied'))}>{copy('apply')}</Button></FilterPanelFooter></div>\n <p role=\"status\" className=\"min-h-5 text-sm text-muted-foreground\">{notice}</p>\n </div>\n}\n\nexport function SourceFilterToolbar({ type }: { type: 'table' | 'calendar' }) {\n const copy = useFilterCopy()\n const [query, setQuery] = React.useState('')\n const [category, setCategory] = React.useState('all')\n const [sort, setSort] = React.useState('recent')\n const [period, setPeriod] = React.useState('7')\n const [endDate, setEndDate] = React.useState(new Date(2023, 7, 11))\n const [filters, setFilters] = React.useState<Filters>(emptyFilters)\n const [filterOpen, setFilterOpen] = React.useState(false)\n const [showAmount, setShowAmount] = React.useState(true)\n const [dateOpen, setDateOpen] = React.useState(false)\n const periodStart = new Date(endDate)\n periodStart.setDate(endDate.getDate() - Number(period))\n const rows = transactionRows.filter(row => {\n if (category !== 'all' && row.type !== category) return false\n if (query && !row.name.toLowerCase().includes(query.trim().toLowerCase())) return false\n if (filters.statuses.length && !filters.statuses.includes(row.status)) return false\n if (filters.date && row.date.toDateString() !== filters.date.toDateString()) return false\n return type !== 'calendar' || (row.date >= periodStart && row.date <= endDate)\n }).sort((a, b) => sort === 'amount' ? b.amount - a.amount : b.date.getTime() - a.date.getTime())\n const dateText = `${periodStart.toLocaleDateString()} – ${endDate.toLocaleDateString()}`\n return <div className=\"w-full min-w-0 space-y-4\" data-example={`filter-toolbar-${type}`}>\n <FilterToolbar className=\"max-w-[1104px]\" leading={type === 'table'\n ? <SegmentedControl value={category} onValueChange={setCategory} aria-label={copy('transactionType')} className=\"h-9 w-80 max-w-full rounded-md border-0 p-1\">\n {['all', 'income', 'expense'].map(item => <SegmentedControlItem key={item} value={item} className=\"flex-1 rounded-sm\">{copy(item)}</SegmentedControlItem>)}\n </SegmentedControl>\n : <><Button variant=\"outline\" onClick={() => { setEndDate(new Date()); setPeriod('0') }}>{copy('today')}</Button>\n <ButtonGroup size={36} aria-label={copy('dateRange')} className=\"max-w-full flex-wrap\">\n <Select value={period} onValueChange={setPeriod}><SelectTrigger className=\"w-auto\" aria-label={copy('period')}><SelectValue /></SelectTrigger><SelectContent>{['0', '7', '30'].map(item => <SelectItem key={item} value={item}>{copy(`period${item}`)}</SelectItem>)}</SelectContent></Select>\n <Popover open={dateOpen} onOpenChange={setDateOpen}><PopoverTrigger asChild><Button variant=\"outline\" aria-label={copy('dateRange')}><CalendarDays className=\"size-5\" aria-hidden=\"true\" /><span className=\"truncate\">{dateText}</span></Button></PopoverTrigger><PopoverContent className=\"w-auto p-3\" align=\"start\"><Calendar mode=\"single\" selected={endDate} defaultMonth={endDate} onSelect={date => { if (date) { setEndDate(date); setDateOpen(false) } }} /></PopoverContent></Popover>\n </ButtonGroup></>}>\n <SearchInput value={query} onChange={setQuery} aria-label={copy('search')} placeholder={copy('search')} className=\"w-[300px] max-w-full [&>svg]:size-5\" />\n <Popover open={filterOpen} onOpenChange={setFilterOpen}><PopoverTrigger asChild><Button variant=\"outline\"><Filter className=\"size-5\" aria-hidden=\"true\" />{copy('filter')}</Button></PopoverTrigger>\n <PopoverContent className=\"w-[640px] max-w-[calc(100vw-24px)] overflow-hidden p-0\" align=\"end\"><SourceFilterPanel value={filters} onApply={value => { setFilters(value); setFilterOpen(false) }} /></PopoverContent>\n </Popover>\n <Popover><PopoverTrigger asChild><IconButton variant=\"outline\" aria-label={copy('settings')}><Settings2 className=\"size-5\" /></IconButton></PopoverTrigger><PopoverContent className=\"w-60\"><CheckboxField checked={showAmount} onCheckedChange={checked => setShowAmount(checked === true)} label={copy('showAmount')} /></PopoverContent></Popover>\n {type === 'table' ? <Select value={sort} onValueChange={setSort}><SelectTrigger className=\"w-40\" aria-label={copy('sort')}><SelectValue /></SelectTrigger><SelectContent><SelectItem value=\"recent\">{copy('recent')}</SelectItem><SelectItem value=\"amount\">{copy('amount')}</SelectItem></SelectContent></Select> : null}\n </FilterToolbar>\n <p role=\"status\" className=\"text-sm text-muted-foreground\">{copy('results', { count: rows.length })}</p>\n <div className=\"overflow-x-auto rounded-md border border-border\"><Table className=\"min-w-[440px]\">\n <TableHeader><TableRow><TableHead>{copy('name')}</TableHead><TableHead>{copy('status')}</TableHead>{showAmount ? <TableHead className=\"text-right\">{copy('amount')}</TableHead> : null}</TableRow></TableHeader>\n <TableBody>{rows.map(row => <TableRow key={row.name}><TableCell className=\"font-medium\">{row.name}</TableCell><TableCell>{copy(row.status)}</TableCell>{showAmount ? <TableCell className=\"text-right tabular-nums\">{row.amount.toLocaleString(undefined, { style: 'currency', currency: 'USD' })}</TableCell> : null}</TableRow>)}</TableBody>\n </Table></div>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => { setQuery(''); setCategory('all'); setFilters(emptyFilters()); setPeriod('7'); setEndDate(new Date(2023, 7, 11)); setSort('recent'); setShowAmount(true) }}>{copy('reset')}</Button>\n </div>\n}\n"
3
+ export const sourceFilterExampleCode = (expression: string) => source + "\n" + expression + "\n"
@@ -0,0 +1,119 @@
1
+ import * as React from 'react'
2
+ import { CalendarDays, Check, Filter, Settings2 } from 'lucide-react'
3
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
4
+ import { Button } from '@open-mercato/ui/primitives/button'
5
+ import { ButtonGroup } from '@open-mercato/ui/primitives/button-group'
6
+ import { Calendar } from '@open-mercato/ui/primitives/calendar'
7
+ import { CheckboxField } from '@open-mercato/ui/primitives/checkbox-field'
8
+ import { FilterToolbar, FilterPanelItem, FilterPanelHeader, FilterPanelFooter } from '@open-mercato/ui/primitives/filter-toolbar'
9
+ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
10
+ import { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'
11
+ import { SearchInput } from '@open-mercato/ui/primitives/search-input'
12
+ import { SegmentedControl, SegmentedControlItem } from '@open-mercato/ui/primitives/segmented-control'
13
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'
14
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@open-mercato/ui/primitives/table'
15
+
16
+ type Filters = { date?: Date; statuses: string[] }
17
+ const transactionRows = [
18
+ { name: 'Apex', type: 'income', date: new Date(2023, 7, 11), amount: 1240, status: 'completed' },
19
+ { name: 'Spotify', type: 'expense', date: new Date(2023, 7, 10), amount: -12, status: 'completed' },
20
+ { name: 'Synergy', type: 'income', date: new Date(2023, 7, 9), amount: 680, status: 'pending' },
21
+ { name: 'Loom', type: 'expense', date: new Date(2023, 7, 11), amount: -18, status: 'pending' },
22
+ ]
23
+ const emptyFilters = (): Filters => ({ statuses: [] })
24
+
25
+ function useFilterCopy() {
26
+ const t = useT()
27
+ return (key: string, values?: Record<string, string | number>) => t(`design_system.gallery.examples.sourceFilters.${key}`, values)
28
+ }
29
+
30
+ export function SourceFilterPanel({ value, onApply }: { value?: Filters; onApply?: (value: Filters) => void }) {
31
+ const copy = useFilterCopy()
32
+ const [draft, setDraft] = React.useState<Filters>(() => value ?? emptyFilters())
33
+ const [section, setSection] = React.useState('date')
34
+ const [applied, setApplied] = React.useState(false)
35
+ const panelId = React.useId()
36
+ function clear() { setDraft(emptyFilters()); setApplied(false) }
37
+ return <div className="w-full min-w-0 bg-background" data-example="filter-panel">
38
+ <div className="flex flex-col sm:flex-row">
39
+ <div className="flex shrink-0 flex-wrap gap-1 border-b border-border p-3 sm:w-[224px] sm:flex-col sm:border-b-0 sm:border-r" role="group" aria-label={copy('categories')}>
40
+ <FilterPanelItem active={section === 'date'} leading={<CalendarDays />} aria-controls={panelId} onClick={() => setSection('date')}>{copy('date')}</FilterPanelItem>
41
+ <FilterPanelItem active={section === 'status'} leading={<Check />} aria-controls={panelId} onClick={() => setSection('status')}>{copy('status')}</FilterPanelItem>
42
+ </div>
43
+ <section id={panelId} aria-label={copy(section)} className="min-w-0 flex-1">
44
+ <FilterPanelHeader title={copy(section)} leading={section === 'date' ? <CalendarDays /> : <Check />} action={<Button variant="link" className="h-auto p-0 text-sm leading-5 text-muted-foreground" onClick={clear}>{copy('clear')}</Button>} />
45
+ <div className="flex min-h-80 justify-center px-5 pb-5">
46
+ {section === 'date'
47
+ ? <Calendar mode="single" defaultMonth={draft.date ?? new Date(2023, 7, 1)} selected={draft.date} onSelect={date => { setDraft(previous => ({ ...previous, date })); setApplied(false) }} />
48
+ : <div className="w-full space-y-4 pt-2">{['completed', 'pending'].map(status => <CheckboxField key={status} label={copy(status)} checked={draft.statuses.includes(status)} onCheckedChange={checked => { setDraft(previous => ({ ...previous, statuses: checked ? [...previous.statuses, status] : previous.statuses.filter(item => item !== status) })); setApplied(false) }} />)}</div>}
49
+ </div>
50
+ </section>
51
+ </div>
52
+ <FilterPanelFooter><Button variant="outline" onClick={clear}>{copy('clear')}</Button><Button variant="primary-filled" onClick={() => { onApply?.(draft); setApplied(true) }}>{copy('apply')}</Button></FilterPanelFooter>
53
+ {applied ? <p role="status" className="px-5 pb-4 text-sm text-muted-foreground">{copy('applied')}</p> : null}
54
+ </div>
55
+ }
56
+
57
+ export function SourceFilterParts() {
58
+ const copy = useFilterCopy()
59
+ const [active, setActive] = React.useState(2)
60
+ const [notice, setNotice] = React.useState('')
61
+ return <div className="grid w-full gap-6">
62
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
63
+ {['Default', 'Hover', 'Active'].map((state, index) => <div key={state} className="w-[200px] max-w-full space-y-2">
64
+ <p className="text-xs text-muted-foreground">{state}</p>
65
+ <FilterPanelItem active={active === index} leading={<CalendarDays />} onClick={() => setActive(index)}>{copy('date')}</FilterPanelItem>
66
+ </div>)}
67
+ </div>
68
+ <div className="w-[400px] max-w-full rounded-md border border-border"><FilterPanelHeader title={copy('date')} leading={<CalendarDays />} action={<Button variant="link" className="h-auto p-0 text-sm leading-5 text-muted-foreground" onClick={() => { setActive(-1); setNotice(copy('cleared')) }}>{copy('clear')}</Button>} /></div>
69
+ <div className="w-[472px] max-w-full"><FilterPanelFooter><Button variant="outline" onClick={() => { setActive(-1); setNotice(copy('cleared')) }}>{copy('clear')}</Button><Button variant="primary-filled" onClick={() => setNotice(copy('applied'))}>{copy('apply')}</Button></FilterPanelFooter></div>
70
+ <p role="status" className="min-h-5 text-sm text-muted-foreground">{notice}</p>
71
+ </div>
72
+ }
73
+
74
+ export function SourceFilterToolbar({ type }: { type: 'table' | 'calendar' }) {
75
+ const copy = useFilterCopy()
76
+ const [query, setQuery] = React.useState('')
77
+ const [category, setCategory] = React.useState('all')
78
+ const [sort, setSort] = React.useState('recent')
79
+ const [period, setPeriod] = React.useState('7')
80
+ const [endDate, setEndDate] = React.useState(new Date(2023, 7, 11))
81
+ const [filters, setFilters] = React.useState<Filters>(emptyFilters)
82
+ const [filterOpen, setFilterOpen] = React.useState(false)
83
+ const [showAmount, setShowAmount] = React.useState(true)
84
+ const [dateOpen, setDateOpen] = React.useState(false)
85
+ const periodStart = new Date(endDate)
86
+ periodStart.setDate(endDate.getDate() - Number(period))
87
+ const rows = transactionRows.filter(row => {
88
+ if (category !== 'all' && row.type !== category) return false
89
+ if (query && !row.name.toLowerCase().includes(query.trim().toLowerCase())) return false
90
+ if (filters.statuses.length && !filters.statuses.includes(row.status)) return false
91
+ if (filters.date && row.date.toDateString() !== filters.date.toDateString()) return false
92
+ return type !== 'calendar' || (row.date >= periodStart && row.date <= endDate)
93
+ }).sort((a, b) => sort === 'amount' ? b.amount - a.amount : b.date.getTime() - a.date.getTime())
94
+ const dateText = `${periodStart.toLocaleDateString()} – ${endDate.toLocaleDateString()}`
95
+ return <div className="w-full min-w-0 space-y-4" data-example={`filter-toolbar-${type}`}>
96
+ <FilterToolbar className="max-w-[1104px]" leading={type === 'table'
97
+ ? <SegmentedControl value={category} onValueChange={setCategory} aria-label={copy('transactionType')} className="h-9 w-80 max-w-full rounded-md border-0 p-1">
98
+ {['all', 'income', 'expense'].map(item => <SegmentedControlItem key={item} value={item} className="flex-1 rounded-sm">{copy(item)}</SegmentedControlItem>)}
99
+ </SegmentedControl>
100
+ : <><Button variant="outline" onClick={() => { setEndDate(new Date()); setPeriod('0') }}>{copy('today')}</Button>
101
+ <ButtonGroup size={36} aria-label={copy('dateRange')} className="max-w-full flex-wrap">
102
+ <Select value={period} onValueChange={setPeriod}><SelectTrigger className="w-auto" aria-label={copy('period')}><SelectValue /></SelectTrigger><SelectContent>{['0', '7', '30'].map(item => <SelectItem key={item} value={item}>{copy(`period${item}`)}</SelectItem>)}</SelectContent></Select>
103
+ <Popover open={dateOpen} onOpenChange={setDateOpen}><PopoverTrigger asChild><Button variant="outline" aria-label={copy('dateRange')}><CalendarDays className="size-5" aria-hidden="true" /><span className="truncate">{dateText}</span></Button></PopoverTrigger><PopoverContent className="w-auto p-3" align="start"><Calendar mode="single" selected={endDate} defaultMonth={endDate} onSelect={date => { if (date) { setEndDate(date); setDateOpen(false) } }} /></PopoverContent></Popover>
104
+ </ButtonGroup></>}>
105
+ <SearchInput value={query} onChange={setQuery} aria-label={copy('search')} placeholder={copy('search')} className="w-[300px] max-w-full [&>svg]:size-5" />
106
+ <Popover open={filterOpen} onOpenChange={setFilterOpen}><PopoverTrigger asChild><Button variant="outline"><Filter className="size-5" aria-hidden="true" />{copy('filter')}</Button></PopoverTrigger>
107
+ <PopoverContent className="w-[640px] max-w-[calc(100vw-24px)] overflow-hidden p-0" align="end"><SourceFilterPanel value={filters} onApply={value => { setFilters(value); setFilterOpen(false) }} /></PopoverContent>
108
+ </Popover>
109
+ <Popover><PopoverTrigger asChild><IconButton variant="outline" aria-label={copy('settings')}><Settings2 className="size-5" /></IconButton></PopoverTrigger><PopoverContent className="w-60"><CheckboxField checked={showAmount} onCheckedChange={checked => setShowAmount(checked === true)} label={copy('showAmount')} /></PopoverContent></Popover>
110
+ {type === 'table' ? <Select value={sort} onValueChange={setSort}><SelectTrigger className="w-40" aria-label={copy('sort')}><SelectValue /></SelectTrigger><SelectContent><SelectItem value="recent">{copy('recent')}</SelectItem><SelectItem value="amount">{copy('amount')}</SelectItem></SelectContent></Select> : null}
111
+ </FilterToolbar>
112
+ <p role="status" className="text-sm text-muted-foreground">{copy('results', { count: rows.length })}</p>
113
+ <div className="overflow-x-auto rounded-md border border-border"><Table className="min-w-[440px]">
114
+ <TableHeader><TableRow><TableHead>{copy('name')}</TableHead><TableHead>{copy('status')}</TableHead>{showAmount ? <TableHead className="text-right">{copy('amount')}</TableHead> : null}</TableRow></TableHeader>
115
+ <TableBody>{rows.map(row => <TableRow key={row.name}><TableCell className="font-medium">{row.name}</TableCell><TableCell>{copy(row.status)}</TableCell>{showAmount ? <TableCell className="text-right tabular-nums">{row.amount.toLocaleString(undefined, { style: 'currency', currency: 'USD' })}</TableCell> : null}</TableRow>)}</TableBody>
116
+ </Table></div>
117
+ <Button size="sm" variant="ghost" onClick={() => { setQuery(''); setCategory('all'); setFilters(emptyFilters()); setPeriod('7'); setEndDate(new Date(2023, 7, 11)); setSort('recent'); setShowAmount(true) }}>{copy('reset')}</Button>
118
+ </div>
119
+ }
@@ -0,0 +1,62 @@
1
+ import * as React from 'react'
2
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
3
+ import { SourceIcon, sourceIconNames, type SourceIconName } from '@open-mercato/ui/assets/source-icons'
4
+ import { Button } from '@open-mercato/ui/primitives/button'
5
+ import { Pagination } from '@open-mercato/ui/primitives/pagination'
6
+ import { SearchInput } from '@open-mercato/ui/primitives/search-input'
7
+ import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
8
+ import { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'
9
+
10
+ const pageSize = 48
11
+
12
+ function SourceIconTile({ name }: { name: SourceIconName }) {
13
+ const t = useT()
14
+ const [notice, setNotice] = React.useState('')
15
+ const code = `import { SourceIcon } from '@open-mercato/ui/assets/source-icons'\n\n<SourceIcon name="${name}" aria-hidden="true" className="size-6" />`
16
+ async function copy() {
17
+ try { await navigator.clipboard.writeText(code); setNotice(t('design_system.gallery.sourceIcons.copied')) }
18
+ catch { setNotice(t('design_system.gallery.copyFailed')) }
19
+ }
20
+ return <Popover onOpenChange={() => setNotice('')}>
21
+ <PopoverTrigger asChild>
22
+ <Button type="button" variant="ghost" aria-label={name} className="h-auto min-h-28 min-w-0 flex-col gap-3 whitespace-normal rounded-lg border border-border bg-background p-3">
23
+ <SourceIcon name={name} aria-hidden="true" className="size-7" />
24
+ <span className="max-w-full break-words text-center text-xs font-normal leading-4 text-muted-foreground">{name}</span>
25
+ </Button>
26
+ </PopoverTrigger>
27
+ <PopoverContent className="w-72 space-y-4 p-4" aria-label={name}>
28
+ <div className="flex h-24 items-center justify-center rounded-md bg-muted"><SourceIcon name={name} aria-hidden="true" className="size-10" /></div>
29
+ <p className="break-words font-medium">{name}</p>
30
+ <Button type="button" variant="outline" size="sm" onClick={copy}>{t('design_system.gallery.sourceIcons.copy')}</Button>
31
+ {notice ? <p role="status" className="text-xs text-muted-foreground">{notice}</p> : null}
32
+ </PopoverContent>
33
+ </Popover>
34
+ }
35
+
36
+ export function SourceIconCatalogue({ collectionControl }: { collectionControl?: React.ReactNode } = {}) {
37
+ const t = useT()
38
+ const [query, setQuery] = React.useState('')
39
+ const [page, setPage] = React.useState(1)
40
+ const filtered = sourceIconNames.filter(name => name.includes(query.trim().toLowerCase()))
41
+ const visible = filtered.slice((page - 1) * pageSize, page * pageSize)
42
+ return <div className="w-full min-w-0 space-y-5" data-example="source-icon-catalogue">
43
+ <div className="flex flex-wrap items-center gap-4">
44
+ {collectionControl}
45
+ <SearchInput value={query} onChange={value => { setQuery(value); setPage(1) }} aria-label={t('design_system.gallery.iconSearchPlaceholder')} placeholder={t('design_system.gallery.iconSearchPlaceholder')} className="w-80 max-w-full" />
46
+ <p className="text-sm text-muted-foreground" role="status">{t('design_system.gallery.iconCountFiltered', { visible: filtered.length, total: sourceIconNames.length })}</p>
47
+ </div>
48
+ {visible.length ? <div className="grid grid-cols-2 gap-3 sm:grid-cols-4 lg:grid-cols-6">
49
+ {visible.map(name => <SourceIconTile key={name} name={name} />)}
50
+ </div> : <EmptyState title={t('design_system.gallery.noResults')} />}
51
+ {filtered.length > pageSize ? <Pagination page={page} pageSize={pageSize} total={filtered.length} showPageSize={false} onPageChange={setPage} aria-label={t('design_system.gallery.sourceIcons.pages')} /> : null}
52
+ </div>
53
+ }
54
+
55
+ export const sourceIconCatalogueCode = `import { SourceIcon, sourceIconNames } from '@open-mercato/ui/assets/source-icons'
56
+
57
+ <div className="grid grid-cols-4 gap-4">
58
+ {sourceIconNames.map(name => <div key={name} className="flex items-center gap-2">
59
+ <SourceIcon name={name} aria-hidden="true" />
60
+ <span>{name}</span>
61
+ </div>)}
62
+ </div>`
@@ -0,0 +1,3 @@
1
+ // Generated by yarn storybook:generate from table-cells.tsx.
2
+ const source = "import * as React from 'react'\nimport { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Avatar, AvatarStack } from '@open-mercato/ui/primitives/avatar'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { ButtonGroup } from '@open-mercato/ui/primitives/button-group'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\nimport { Rating } from '@open-mercato/ui/primitives/rating'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@open-mercato/ui/primitives/table'\n\nconst examples = ['Leading', 'Regular', 'Passive', 'Empty', 'Button', 'ButtonGroup', 'Switch', 'Rating', 'Progress', 'StatusBadge', 'BadgeGroup', 'AvatarStack']\n\nexport function TableHeaderExamples() {\n const t = useT()\n const [selected, setSelected] = React.useState(false)\n const [order, setOrder] = React.useState<'none' | 'ascending' | 'descending'>('none')\n const SortIcon = order === 'ascending' ? ArrowUp : order === 'descending' ? ArrowDown : ArrowUpDown\n return <div className=\"w-full space-y-3 overflow-x-auto\">\n <Table className=\"w-[576px] table-fixed\">\n <TableCaption className=\"sr-only\">{t('design_system.gallery.samples.table.caption')}</TableCaption>\n <TableHeader><TableRow>\n <TableHead scope=\"col\" aria-sort={order} className=\"h-9 w-64 bg-muted px-3 py-0\">\n <div className=\"flex items-center gap-2.5\">\n <Checkbox checked={selected} onCheckedChange={value => setSelected(value === true)} aria-label={t('design_system.gallery.samples.table.selection')} />\n <Button size=\"sm\" variant=\"ghost\" className=\"h-auto gap-0.5 p-0 font-normal\" onClick={() => setOrder(value => value === 'none' ? 'ascending' : value === 'ascending' ? 'descending' : 'none')}>\n {t('design_system.gallery.samples.table.component')}<SortIcon className=\"size-5\" aria-hidden=\"true\" />\n </Button>\n </div>\n </TableHead>\n <TableHead scope=\"col\" className=\"h-9 w-64 bg-muted px-3 py-0\">\n <div className=\"flex items-center gap-2.5\">\n <Checkbox disabled aria-label={t('design_system.gallery.samples.table.selection')} />\n <Button disabled size=\"sm\" variant=\"ghost\" className=\"h-auto gap-0.5 p-0 font-normal\">{t('design_system.gallery.samples.table.preview')}<ArrowUpDown className=\"size-5\" aria-hidden=\"true\" /></Button>\n </div>\n </TableHead>\n <TableHead scope=\"col\" className=\"h-9 w-16 bg-muted px-3 py-0\"><span className=\"sr-only\">{t('design_system.gallery.samples.table.empty')}</span></TableHead>\n </TableRow></TableHeader>\n <TableBody><TableRow data-state={selected ? 'selected' : undefined}>\n <TableCell>{t('design_system.gallery.variantTitles.default')}</TableCell><TableCell>{t('design_system.gallery.variantTitles.disabled')}</TableCell><TableCell>{t('design_system.gallery.variantTitles.empty')}</TableCell>\n </TableRow></TableBody>\n </Table>\n <p role=\"status\" className=\"text-sm text-muted-foreground\">{t('design_system.gallery.samples.table.selected', { count: Number(selected) })}</p>\n </div>\n}\n\nexport function TableCellExamples({ rowHeight }: { rowHeight: 48 | 64 }) {\n const t = useT()\n const [selected, setSelected] = React.useState(['Leading'])\n const [order, setOrder] = React.useState<'none' | 'ascending' | 'descending'>('none')\n const [rating, setRating] = React.useState(3)\n const [notice, setNotice] = React.useState('')\n const typeLabel = (type: string) => t(`design_system.gallery.samples.table.types.${type}`, type)\n const sorted = order === 'none' ? examples : [...examples].sort((a, b) => order === 'ascending' ? typeLabel(a).localeCompare(typeLabel(b)) : typeLabel(b).localeCompare(typeLabel(a)))\n const SortIcon = order === 'ascending' ? ArrowUp : order === 'descending' ? ArrowDown : ArrowUpDown\n const label = t('design_system.gallery.samples.table.label')\n function cell(type: string) {\n switch (type) {\n case 'Leading': return <div className=\"flex items-center gap-3\"><Avatar label=\"Wei Chen\" size={rowHeight === 48 ? 32 : 40} /><div className=\"space-y-0.5\"><p className=\"text-sm font-medium leading-5\">Wei Chen</p>{rowHeight === 64 ? <p className=\"text-xs leading-4 text-muted-foreground\">{t('design_system.gallery.samples.table.description')}</p> : null}</div></div>\n case 'Regular': return <span>{label}</span>\n case 'Passive': return <span className=\"text-muted-foreground\">{label}</span>\n case 'Empty': return <span className=\"sr-only\">{t('design_system.gallery.samples.table.empty')}</span>\n case 'Button': return <Button size=\"sm\" variant=\"outline\" onClick={() => setNotice(t('design_system.gallery.samples.table.actionDone'))}>{t('design_system.gallery.samples.table.action')}</Button>\n case 'ButtonGroup': return <ButtonGroup size={32} aria-label={t('design_system.gallery.samples.table.actions')}><Button variant=\"outline\" size=\"icon\" aria-label={t('design_system.gallery.examples.navigation.previous')} onClick={() => setNotice(t('design_system.gallery.examples.navigation.previous'))}><ChevronLeft /></Button><Button variant=\"outline\" size=\"icon\" aria-label={t('design_system.gallery.examples.navigation.next')} onClick={() => setNotice(t('design_system.gallery.examples.navigation.next'))}><ChevronRight /></Button></ButtonGroup>\n case 'Switch': return <Switch aria-label={t('design_system.gallery.samples.table.enabled')} defaultChecked />\n case 'Rating': return <Rating value={rating} onChange={setRating} aria-label={t('design_system.gallery.samples.rating.label')} />\n case 'Progress': return <Progress value={65} className=\"w-40\" aria-label={t('design_system.gallery.examples.navigation.progress')} />\n case 'StatusBadge': return <StatusBadge variant=\"success\" appearance=\"light\" dot>{t('design_system.gallery.samples.table.ready')}</StatusBadge>\n case 'BadgeGroup': return <div className=\"flex gap-1\"><Badge size={20} appearance=\"lighter\" tone=\"purple\">CRM</Badge><Badge size={20} appearance=\"lighter\" tone=\"sky\">ERP</Badge><Badge size={20} appearance=\"stroke\" tone=\"neutral\" numeric>+2</Badge></div>\n case 'AvatarStack': return <AvatarStack max={3} size={24}><Avatar size={24} label=\"Wei Chen\" /><Avatar size={24} label=\"Laura Perez\" /><Avatar size={24} label=\"Omar Haddad\" /><Avatar size={24} label=\"James Brown\" /></AvatarStack>\n default: return null\n }\n }\n return <div className=\"w-full min-w-0 space-y-3\">\n <div className=\"w-full overflow-x-auto rounded-lg border border-border\">\n <Table className=\"min-w-[600px]\">\n <TableCaption className=\"sr-only\">{t('design_system.gallery.samples.table.caption')}</TableCaption>\n <TableHeader><TableRow>\n <TableHead className=\"h-9 w-12 py-1\"><span className=\"sr-only\">{t('design_system.gallery.samples.table.selection')}</span></TableHead>\n <TableHead aria-sort={order} className=\"h-9 py-1\"><Button variant=\"ghost\" size=\"sm\" className=\"h-auto gap-2 p-0\" onClick={() => setOrder(value => value === 'ascending' ? 'descending' : 'ascending')}>{t('design_system.gallery.samples.table.component')}<SortIcon aria-hidden=\"true\" className=\"size-5\" /></Button></TableHead>\n <TableHead className=\"h-9 py-1\">{t('design_system.gallery.samples.table.preview')}</TableHead>\n </TableRow></TableHeader>\n <TableBody>{sorted.map(type => <TableRow key={type} data-state={selected.includes(type) ? 'selected' : undefined} className=\"data-[state=selected]:bg-muted\">\n <TableCell className={rowHeight === 48 ? 'h-12 py-0' : 'h-16 py-0'}><Checkbox checked={selected.includes(type)} aria-label={t('design_system.gallery.samples.table.select', { name: typeLabel(type) })} onCheckedChange={checked => setSelected(value => checked ? [...value, type] : value.filter(item => item !== type))} /></TableCell>\n <TableCell className=\"font-medium\">{typeLabel(type)}</TableCell>\n <TableCell className={rowHeight === 48 ? 'h-12 py-0' : 'h-16 py-0'}>{cell(type)}</TableCell>\n </TableRow>)}</TableBody>\n </Table>\n </div>\n <p role=\"status\" className=\"min-h-5 text-sm text-muted-foreground\">{notice || t('design_system.gallery.samples.table.selected', { count: selected.length })}</p>\n </div>\n}\n\nexport function tableCellExampleCode(rowHeight: 48 | 64) {\n return `import * as React from 'react'\nimport { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight } from 'lucide-react'\nimport { Avatar, AvatarStack } from '@open-mercato/ui/primitives/avatar'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { ButtonGroup } from '@open-mercato/ui/primitives/button-group'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\nimport { Rating } from '@open-mercato/ui/primitives/rating'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Table, TableHeader, TableBody, TableHead, TableRow, TableCell } from '@open-mercato/ui/primitives/table'\n\nfunction Example() {\n const [rating, setRating] = React.useState(3)\n const [selected, setSelected] = React.useState<string[]>(['Leading'])\n const [action, setAction] = React.useState('')\n const [order, setOrder] = React.useState<'none' | 'ascending' | 'descending'>('none')\n const SortIcon = order === 'ascending' ? ArrowUp : order === 'descending' ? ArrowDown : ArrowUpDown\n const cells = [\n { name: 'Leading', content: <div className=\"flex items-center gap-3\"><Avatar size={${rowHeight === 48 ? 32 : 40}} label=\"Wei Chen\" /><div><p className=\"font-medium\">Wei Chen</p>${rowHeight === 64 ? '<p className=\"text-xs text-muted-foreground\">Account manager</p>' : ''}</div></div> },\n { name: 'Regular', content: <span>Label</span> },\n { name: 'Passive', content: <span className=\"text-muted-foreground\">Label</span> },\n { name: 'Empty', content: null },\n { name: 'Button', content: <Button size=\"sm\" variant=\"outline\" onClick={() => setAction('Opened')}>Open</Button> },\n { name: 'ButtonGroup', content: <ButtonGroup size={32} aria-label=\"Related actions\"><Button variant=\"outline\" size=\"icon\" aria-label=\"Previous\" onClick={() => setAction('Previous')}><ChevronLeft /></Button><Button variant=\"outline\" size=\"icon\" aria-label=\"Next\" onClick={() => setAction('Next')}><ChevronRight /></Button></ButtonGroup> },\n { name: 'Switch', content: <Switch defaultChecked aria-label=\"Enabled\" /> },\n { name: 'Rating', content: <Rating value={rating} onChange={setRating} aria-label=\"Rating\" /> },\n { name: 'Progress', content: <Progress value={65} className=\"w-40\" aria-label=\"Progress\" /> },\n { name: 'StatusBadge', content: <StatusBadge variant=\"success\" appearance=\"light\" dot>Ready</StatusBadge> },\n { name: 'BadgeGroup', content: <div className=\"flex gap-1\"><Badge size={20} appearance=\"lighter\" tone=\"purple\">CRM</Badge><Badge size={20} appearance=\"lighter\" tone=\"sky\">ERP</Badge><Badge size={20} appearance=\"stroke\" tone=\"neutral\" numeric>+2</Badge></div> },\n { name: 'AvatarStack', content: <AvatarStack max={3} size={24}><Avatar size={24} label=\"Wei Chen\" /><Avatar size={24} label=\"Laura Perez\" /><Avatar size={24} label=\"Omar Haddad\" /><Avatar size={24} label=\"James Brown\" /></AvatarStack> },\n ]\n const sorted = order === 'none' ? cells : [...cells].sort((a, b) => order === 'ascending' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name))\n return <div className=\"w-full overflow-x-auto\">\n <Table className=\"min-w-[600px]\">\n <TableHeader><TableRow><TableHead>Selection</TableHead><TableHead aria-sort={order}><Button size=\"sm\" variant=\"ghost\" onClick={() => setOrder(value => value === 'ascending' ? 'descending' : 'ascending')}>Component<SortIcon /></Button></TableHead><TableHead>Preview</TableHead></TableRow></TableHeader>\n <TableBody>{sorted.map(cell => <TableRow key={cell.name} data-state={selected.includes(cell.name) ? 'selected' : undefined} className=\"data-[state=selected]:bg-muted\">\n <TableCell className=\"${rowHeight === 48 ? 'h-12 py-0' : 'h-16 py-0'}\"><Checkbox checked={selected.includes(cell.name)} aria-label={\\`Select \\${cell.name}\\`} onCheckedChange={checked => setSelected(value => checked ? [...value, cell.name] : value.filter(name => name !== cell.name))} /></TableCell>\n <TableCell className=\"font-medium\">{cell.name}</TableCell>\n <TableCell className=\"${rowHeight === 48 ? 'h-12 py-0' : 'h-16 py-0'}\">{cell.content}</TableCell>\n </TableRow>)}</TableBody>\n </Table>\n <p role=\"status\">{action || selected.length + ' rows selected'}</p>\n </div>\n}`\n}\n"
3
+ export const tableHeaderExampleCode = source + "\n<TableHeaderExamples />\n"
@@ -0,0 +1,144 @@
1
+ import * as React from 'react'
2
+ import { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight } from 'lucide-react'
3
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
4
+ import { Avatar, AvatarStack } from '@open-mercato/ui/primitives/avatar'
5
+ import { Badge } from '@open-mercato/ui/primitives/badge'
6
+ import { Button } from '@open-mercato/ui/primitives/button'
7
+ import { ButtonGroup } from '@open-mercato/ui/primitives/button-group'
8
+ import { Checkbox } from '@open-mercato/ui/primitives/checkbox'
9
+ import { Progress } from '@open-mercato/ui/primitives/progress'
10
+ import { Rating } from '@open-mercato/ui/primitives/rating'
11
+ import { StatusBadge } from '@open-mercato/ui/primitives/status-badge'
12
+ import { Switch } from '@open-mercato/ui/primitives/switch'
13
+ import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@open-mercato/ui/primitives/table'
14
+
15
+ const examples = ['Leading', 'Regular', 'Passive', 'Empty', 'Button', 'ButtonGroup', 'Switch', 'Rating', 'Progress', 'StatusBadge', 'BadgeGroup', 'AvatarStack']
16
+
17
+ export function TableHeaderExamples() {
18
+ const t = useT()
19
+ const [selected, setSelected] = React.useState(false)
20
+ const [order, setOrder] = React.useState<'none' | 'ascending' | 'descending'>('none')
21
+ const SortIcon = order === 'ascending' ? ArrowUp : order === 'descending' ? ArrowDown : ArrowUpDown
22
+ return <div className="w-full space-y-3 overflow-x-auto">
23
+ <Table className="w-[576px] table-fixed">
24
+ <TableCaption className="sr-only">{t('design_system.gallery.samples.table.caption')}</TableCaption>
25
+ <TableHeader><TableRow>
26
+ <TableHead scope="col" aria-sort={order} className="h-9 w-64 bg-muted px-3 py-0">
27
+ <div className="flex items-center gap-2.5">
28
+ <Checkbox checked={selected} onCheckedChange={value => setSelected(value === true)} aria-label={t('design_system.gallery.samples.table.selection')} />
29
+ <Button size="sm" variant="ghost" className="h-auto gap-0.5 p-0 font-normal" onClick={() => setOrder(value => value === 'none' ? 'ascending' : value === 'ascending' ? 'descending' : 'none')}>
30
+ {t('design_system.gallery.samples.table.component')}<SortIcon className="size-5" aria-hidden="true" />
31
+ </Button>
32
+ </div>
33
+ </TableHead>
34
+ <TableHead scope="col" className="h-9 w-64 bg-muted px-3 py-0">
35
+ <div className="flex items-center gap-2.5">
36
+ <Checkbox disabled aria-label={t('design_system.gallery.samples.table.selection')} />
37
+ <Button disabled size="sm" variant="ghost" className="h-auto gap-0.5 p-0 font-normal">{t('design_system.gallery.samples.table.preview')}<ArrowUpDown className="size-5" aria-hidden="true" /></Button>
38
+ </div>
39
+ </TableHead>
40
+ <TableHead scope="col" className="h-9 w-16 bg-muted px-3 py-0"><span className="sr-only">{t('design_system.gallery.samples.table.empty')}</span></TableHead>
41
+ </TableRow></TableHeader>
42
+ <TableBody><TableRow data-state={selected ? 'selected' : undefined}>
43
+ <TableCell>{t('design_system.gallery.variantTitles.default')}</TableCell><TableCell>{t('design_system.gallery.variantTitles.disabled')}</TableCell><TableCell>{t('design_system.gallery.variantTitles.empty')}</TableCell>
44
+ </TableRow></TableBody>
45
+ </Table>
46
+ <p role="status" className="text-sm text-muted-foreground">{t('design_system.gallery.samples.table.selected', { count: Number(selected) })}</p>
47
+ </div>
48
+ }
49
+
50
+ export function TableCellExamples({ rowHeight }: { rowHeight: 48 | 64 }) {
51
+ const t = useT()
52
+ const [selected, setSelected] = React.useState(['Leading'])
53
+ const [order, setOrder] = React.useState<'none' | 'ascending' | 'descending'>('none')
54
+ const [rating, setRating] = React.useState(3)
55
+ const [notice, setNotice] = React.useState('')
56
+ const typeLabel = (type: string) => t(`design_system.gallery.samples.table.types.${type}`, type)
57
+ const sorted = order === 'none' ? examples : [...examples].sort((a, b) => order === 'ascending' ? typeLabel(a).localeCompare(typeLabel(b)) : typeLabel(b).localeCompare(typeLabel(a)))
58
+ const SortIcon = order === 'ascending' ? ArrowUp : order === 'descending' ? ArrowDown : ArrowUpDown
59
+ const label = t('design_system.gallery.samples.table.label')
60
+ function cell(type: string) {
61
+ switch (type) {
62
+ case 'Leading': return <div className="flex items-center gap-3"><Avatar label="Wei Chen" size={rowHeight === 48 ? 32 : 40} /><div className="space-y-0.5"><p className="text-sm font-medium leading-5">Wei Chen</p>{rowHeight === 64 ? <p className="text-xs leading-4 text-muted-foreground">{t('design_system.gallery.samples.table.description')}</p> : null}</div></div>
63
+ case 'Regular': return <span>{label}</span>
64
+ case 'Passive': return <span className="text-muted-foreground">{label}</span>
65
+ case 'Empty': return <span className="sr-only">{t('design_system.gallery.samples.table.empty')}</span>
66
+ case 'Button': return <Button size="sm" variant="outline" onClick={() => setNotice(t('design_system.gallery.samples.table.actionDone'))}>{t('design_system.gallery.samples.table.action')}</Button>
67
+ case 'ButtonGroup': return <ButtonGroup size={32} aria-label={t('design_system.gallery.samples.table.actions')}><Button variant="outline" size="icon" aria-label={t('design_system.gallery.examples.navigation.previous')} onClick={() => setNotice(t('design_system.gallery.examples.navigation.previous'))}><ChevronLeft /></Button><Button variant="outline" size="icon" aria-label={t('design_system.gallery.examples.navigation.next')} onClick={() => setNotice(t('design_system.gallery.examples.navigation.next'))}><ChevronRight /></Button></ButtonGroup>
68
+ case 'Switch': return <Switch aria-label={t('design_system.gallery.samples.table.enabled')} defaultChecked />
69
+ case 'Rating': return <Rating value={rating} onChange={setRating} aria-label={t('design_system.gallery.samples.rating.label')} />
70
+ case 'Progress': return <Progress value={65} className="w-40" aria-label={t('design_system.gallery.examples.navigation.progress')} />
71
+ case 'StatusBadge': return <StatusBadge variant="success" appearance="light" dot>{t('design_system.gallery.samples.table.ready')}</StatusBadge>
72
+ case 'BadgeGroup': return <div className="flex gap-1"><Badge size={20} appearance="lighter" tone="purple">CRM</Badge><Badge size={20} appearance="lighter" tone="sky">ERP</Badge><Badge size={20} appearance="stroke" tone="neutral" numeric>+2</Badge></div>
73
+ case 'AvatarStack': return <AvatarStack max={3} size={24}><Avatar size={24} label="Wei Chen" /><Avatar size={24} label="Laura Perez" /><Avatar size={24} label="Omar Haddad" /><Avatar size={24} label="James Brown" /></AvatarStack>
74
+ default: return null
75
+ }
76
+ }
77
+ return <div className="w-full min-w-0 space-y-3">
78
+ <div className="w-full overflow-x-auto rounded-lg border border-border">
79
+ <Table className="min-w-[600px]">
80
+ <TableCaption className="sr-only">{t('design_system.gallery.samples.table.caption')}</TableCaption>
81
+ <TableHeader><TableRow>
82
+ <TableHead className="h-9 w-12 py-1"><span className="sr-only">{t('design_system.gallery.samples.table.selection')}</span></TableHead>
83
+ <TableHead aria-sort={order} className="h-9 py-1"><Button variant="ghost" size="sm" className="h-auto gap-2 p-0" onClick={() => setOrder(value => value === 'ascending' ? 'descending' : 'ascending')}>{t('design_system.gallery.samples.table.component')}<SortIcon aria-hidden="true" className="size-5" /></Button></TableHead>
84
+ <TableHead className="h-9 py-1">{t('design_system.gallery.samples.table.preview')}</TableHead>
85
+ </TableRow></TableHeader>
86
+ <TableBody>{sorted.map(type => <TableRow key={type} data-state={selected.includes(type) ? 'selected' : undefined} className="data-[state=selected]:bg-muted">
87
+ <TableCell className={rowHeight === 48 ? 'h-12 py-0' : 'h-16 py-0'}><Checkbox checked={selected.includes(type)} aria-label={t('design_system.gallery.samples.table.select', { name: typeLabel(type) })} onCheckedChange={checked => setSelected(value => checked ? [...value, type] : value.filter(item => item !== type))} /></TableCell>
88
+ <TableCell className="font-medium">{typeLabel(type)}</TableCell>
89
+ <TableCell className={rowHeight === 48 ? 'h-12 py-0' : 'h-16 py-0'}>{cell(type)}</TableCell>
90
+ </TableRow>)}</TableBody>
91
+ </Table>
92
+ </div>
93
+ <p role="status" className="min-h-5 text-sm text-muted-foreground">{notice || t('design_system.gallery.samples.table.selected', { count: selected.length })}</p>
94
+ </div>
95
+ }
96
+
97
+ export function tableCellExampleCode(rowHeight: 48 | 64) {
98
+ return `import * as React from 'react'
99
+ import { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight } from 'lucide-react'
100
+ import { Avatar, AvatarStack } from '@open-mercato/ui/primitives/avatar'
101
+ import { Badge } from '@open-mercato/ui/primitives/badge'
102
+ import { Button } from '@open-mercato/ui/primitives/button'
103
+ import { ButtonGroup } from '@open-mercato/ui/primitives/button-group'
104
+ import { Checkbox } from '@open-mercato/ui/primitives/checkbox'
105
+ import { Progress } from '@open-mercato/ui/primitives/progress'
106
+ import { Rating } from '@open-mercato/ui/primitives/rating'
107
+ import { StatusBadge } from '@open-mercato/ui/primitives/status-badge'
108
+ import { Switch } from '@open-mercato/ui/primitives/switch'
109
+ import { Table, TableHeader, TableBody, TableHead, TableRow, TableCell } from '@open-mercato/ui/primitives/table'
110
+
111
+ function Example() {
112
+ const [rating, setRating] = React.useState(3)
113
+ const [selected, setSelected] = React.useState<string[]>(['Leading'])
114
+ const [action, setAction] = React.useState('')
115
+ const [order, setOrder] = React.useState<'none' | 'ascending' | 'descending'>('none')
116
+ const SortIcon = order === 'ascending' ? ArrowUp : order === 'descending' ? ArrowDown : ArrowUpDown
117
+ const cells = [
118
+ { name: 'Leading', content: <div className="flex items-center gap-3"><Avatar size={${rowHeight === 48 ? 32 : 40}} label="Wei Chen" /><div><p className="font-medium">Wei Chen</p>${rowHeight === 64 ? '<p className="text-xs text-muted-foreground">Account manager</p>' : ''}</div></div> },
119
+ { name: 'Regular', content: <span>Label</span> },
120
+ { name: 'Passive', content: <span className="text-muted-foreground">Label</span> },
121
+ { name: 'Empty', content: null },
122
+ { name: 'Button', content: <Button size="sm" variant="outline" onClick={() => setAction('Opened')}>Open</Button> },
123
+ { name: 'ButtonGroup', content: <ButtonGroup size={32} aria-label="Related actions"><Button variant="outline" size="icon" aria-label="Previous" onClick={() => setAction('Previous')}><ChevronLeft /></Button><Button variant="outline" size="icon" aria-label="Next" onClick={() => setAction('Next')}><ChevronRight /></Button></ButtonGroup> },
124
+ { name: 'Switch', content: <Switch defaultChecked aria-label="Enabled" /> },
125
+ { name: 'Rating', content: <Rating value={rating} onChange={setRating} aria-label="Rating" /> },
126
+ { name: 'Progress', content: <Progress value={65} className="w-40" aria-label="Progress" /> },
127
+ { name: 'StatusBadge', content: <StatusBadge variant="success" appearance="light" dot>Ready</StatusBadge> },
128
+ { name: 'BadgeGroup', content: <div className="flex gap-1"><Badge size={20} appearance="lighter" tone="purple">CRM</Badge><Badge size={20} appearance="lighter" tone="sky">ERP</Badge><Badge size={20} appearance="stroke" tone="neutral" numeric>+2</Badge></div> },
129
+ { name: 'AvatarStack', content: <AvatarStack max={3} size={24}><Avatar size={24} label="Wei Chen" /><Avatar size={24} label="Laura Perez" /><Avatar size={24} label="Omar Haddad" /><Avatar size={24} label="James Brown" /></AvatarStack> },
130
+ ]
131
+ const sorted = order === 'none' ? cells : [...cells].sort((a, b) => order === 'ascending' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name))
132
+ return <div className="w-full overflow-x-auto">
133
+ <Table className="min-w-[600px]">
134
+ <TableHeader><TableRow><TableHead>Selection</TableHead><TableHead aria-sort={order}><Button size="sm" variant="ghost" onClick={() => setOrder(value => value === 'ascending' ? 'descending' : 'ascending')}>Component<SortIcon /></Button></TableHead><TableHead>Preview</TableHead></TableRow></TableHeader>
135
+ <TableBody>{sorted.map(cell => <TableRow key={cell.name} data-state={selected.includes(cell.name) ? 'selected' : undefined} className="data-[state=selected]:bg-muted">
136
+ <TableCell className="${rowHeight === 48 ? 'h-12 py-0' : 'h-16 py-0'}"><Checkbox checked={selected.includes(cell.name)} aria-label={\`Select \${cell.name}\`} onCheckedChange={checked => setSelected(value => checked ? [...value, cell.name] : value.filter(name => name !== cell.name))} /></TableCell>
137
+ <TableCell className="font-medium">{cell.name}</TableCell>
138
+ <TableCell className="${rowHeight === 48 ? 'h-12 py-0' : 'h-16 py-0'}">{cell.content}</TableCell>
139
+ </TableRow>)}</TableBody>
140
+ </Table>
141
+ <p role="status">{action || selected.length + ' rows selected'}</p>
142
+ </div>
143
+ }`
144
+ }
@@ -0,0 +1,90 @@
1
+ import * as React from 'react'
2
+ import { Calendar, Globe, Info, Send, Smile, User } from 'lucide-react'
3
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
4
+ import { Input } from '@open-mercato/ui/primitives/input'
5
+ import { Textarea } from '@open-mercato/ui/primitives/textarea'
6
+ import { FormField } from '@open-mercato/ui/primitives/form-field'
7
+ import { EmailInput } from '@open-mercato/ui/primitives/email-input'
8
+ import { SearchInput } from '@open-mercato/ui/primitives/search-input'
9
+ import { PasswordInput } from '@open-mercato/ui/primitives/password-input'
10
+ import { WebsiteInput } from '@open-mercato/ui/primitives/website-input'
11
+ import { AmountInput, type AmountValue } from '@open-mercato/ui/primitives/amount-input'
12
+ import { CardInput } from '@open-mercato/ui/primitives/card-input'
13
+ import { ButtonInput } from '@open-mercato/ui/primitives/button-input'
14
+ import { CompactButton } from '@open-mercato/ui/primitives/compact-button'
15
+ import { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'
16
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'
17
+ import { PhoneNumberField, type PhoneCountry } from '@open-mercato/ui/backend/inputs/PhoneNumberField'
18
+ import unitedStates from '../assets/menu-united-states.svg'
19
+ import europeanUnion from '../assets/input-european-union.svg'
20
+ import poland from '../assets/select-poland.png'
21
+
22
+ const inputKinds = ['basic', 'email', 'phone', 'card', 'website', 'amount', 'date', 'search', 'password', 'button', 'dropdown', 'emoji'] as const
23
+ type InputKind = typeof inputKinds[number]
24
+ type InputState = 'placeholder' | 'filled' | 'error' | 'disabled'
25
+ type InputSize = 32 | 36 | 40
26
+
27
+ function assetSource(asset: string | { src: string }) { return typeof asset === 'string' ? asset : asset.src }
28
+
29
+ export function SourceTextInput({ kind, size = 36, state = 'filled' }: { kind: InputKind; size?: InputSize; state?: InputState }) {
30
+ const t = useT()
31
+ const id = React.useId()
32
+ const isPlaceholder = state === 'placeholder'
33
+ const disabled = state === 'disabled'
34
+ const [text, setText] = React.useState(isPlaceholder ? '' : kind === 'basic' || kind === 'dropdown' ? 'Alex Morgan' : kind === 'email' ? 'alex@example.com' : kind === 'website' ? 'openmercato.com' : kind === 'password' ? 'LocalExample2026!' : kind === 'emoji' ? 'πŸ™‚' : kind === 'date' ? '2026-09-18' : t('design_system.gallery.samples.textInput.sample'))
35
+ const [phone, setPhone] = React.useState<string | undefined>(isPlaceholder ? undefined : '+1 212 555 1234')
36
+ const [amount, setAmount] = React.useState<AmountValue>({ amount: isPlaceholder ? '' : '1250.00', currency: 'EUR' })
37
+ const [card, setCard] = React.useState(isPlaceholder ? '' : '4242424242424242')
38
+ const [permission, setPermission] = React.useState('view')
39
+ const [emojiOpen, setEmojiOpen] = React.useState(false)
40
+ const [feedback, setFeedback] = React.useState('')
41
+ const dateRef = React.useRef<HTMLInputElement>(null)
42
+ const label = t(`design_system.gallery.samples.textInput.${kind}`)
43
+ const error = state === 'error' ? t('design_system.gallery.samples.textInput.error') : undefined
44
+ const hint = t('design_system.gallery.samples.textInput.hint')
45
+ const common = { id, size, disabled, 'aria-label': label, 'aria-invalid': !!error, 'aria-describedby': `${id}-${error ? 'error' : 'desc'}`, placeholder: t('design_system.gallery.samples.textInput.placeholder') }
46
+ const countries: PhoneCountry[] = [
47
+ { iso2: 'US', dialCode: '+1', label: t('design_system.gallery.samples.textInput.us'), flag: 'πŸ‡ΊπŸ‡Έ' },
48
+ { iso2: 'PL', dialCode: '+48', label: t('design_system.gallery.samples.textInput.pl'), flag: 'πŸ‡΅πŸ‡±' },
49
+ ]
50
+ let control: React.ReactNode
51
+ if (kind === 'phone') {
52
+ control = <PhoneNumberField id={id} size={size} ariaLabel={label} ariaDescribedBy={`${id}-desc`} countryLabel={t('design_system.gallery.samples.textInput.country')} value={phone} onValueChange={setPhone} countries={countries} disabled={disabled} externalError={error} renderCountryIcon={country => <img src={assetSource(country.iso2 === 'PL' ? poland : unitedStates)} alt="" className="size-5 object-contain" />} />
53
+ } else if (kind === 'email') {
54
+ control = <EmailInput {...common} value={text} onChange={event => setText(event.target.value)} />
55
+ } else if (kind === 'website') {
56
+ control = <WebsiteInput {...common} value={text} onChange={event => setText(event.target.value)} />
57
+ } else if (kind === 'amount') {
58
+ control = <AmountInput {...common} value={amount} onChange={setAmount} currencies={[{ code: 'USD', symbol: '$', label: 'USD' }, { code: 'EUR', symbol: '€', label: 'EUR' }]} renderCurrencyIcon={currency => <img src={assetSource(currency.code === 'EUR' ? europeanUnion : unitedStates)} alt="" className="size-5 object-contain" />} />
59
+ } else if (kind === 'card') {
60
+ control = <CardInput {...common} value={card} onChange={setCard} />
61
+ } else if (kind === 'search') {
62
+ control = <SearchInput {...common} value={text} onChange={setText} />
63
+ } else if (kind === 'password') {
64
+ control = <PasswordInput {...common} value={text} onChange={event => setText(event.target.value)} />
65
+ } else if (kind === 'button') {
66
+ control = <ButtonInput {...common} value={text} onChange={event => setText(event.target.value)} leftIcon={<User />} trailingAction={<CompactButton size={24} appearance="ghost" disabled={disabled} aria-label={t('design_system.gallery.samples.textInput.send')} onClick={() => setFeedback(t('design_system.gallery.samples.textInput.sent'))}><Send className="size-5" /></CompactButton>} />
67
+ } else if (kind === 'dropdown') {
68
+ control = <Input {...common} value={text} onChange={event => setText(event.target.value)} leftIcon={<User />} trailing={<Select value={permission} onValueChange={setPermission} disabled={disabled}><SelectTrigger size={32} aria-label={t('design_system.gallery.samples.textInput.permission')} className="h-5 w-auto gap-1 border-0 rounded-none bg-transparent p-0 text-muted-foreground shadow-none"><Globe className="size-5" aria-hidden="true" /><SelectValue /></SelectTrigger><SelectContent><SelectItem value="view">{t('design_system.gallery.samples.textInput.view')}</SelectItem><SelectItem value="edit">{t('design_system.gallery.samples.textInput.edit')}</SelectItem></SelectContent></Select>} />
69
+ } else if (kind === 'emoji') {
70
+ control = <Input {...common} value={text} onChange={event => setText(event.target.value)} leading={<Popover open={emojiOpen} onOpenChange={setEmojiOpen}><PopoverTrigger asChild><CompactButton size={20} appearance="ghost" disabled={disabled} aria-label={t('design_system.gallery.samples.textInput.chooseEmoji')}><Smile className="size-5" /></CompactButton></PopoverTrigger><PopoverContent className="flex w-fit gap-2 p-2">{['πŸ™‚', 'πŸŽ‰', 'πŸš€', '❀️'].map(emoji => <CompactButton key={emoji} size={24} appearance="ghost" aria-label={emoji} onClick={() => { setText(current => `${current}${emoji}`); setEmojiOpen(false) }}>{emoji}</CompactButton>)}</PopoverContent></Popover>} />
71
+ } else if (kind === 'date') {
72
+ control = <Input {...common} ref={dateRef} type="date" value={text} onChange={event => setText(event.target.value)} inputClassName="[&::-webkit-calendar-picker-indicator]:hidden" leading={<CompactButton size={20} appearance="ghost" disabled={disabled} aria-label={t('design_system.gallery.samples.textInput.chooseDate')} onClick={() => { dateRef.current?.focus(); dateRef.current?.showPicker?.() }}><Calendar className="size-5" /></CompactButton>} />
73
+ } else {
74
+ control = <Input {...common} value={text} onChange={event => setText(event.target.value)} leftIcon={<User />} rightIcon={state === 'error' ? <Info /> : undefined} />
75
+ }
76
+ return <div className="min-w-0" data-input-kind={kind} data-input-size={size}><FormField id={id} label={label} description={hint} error={kind === 'phone' ? undefined : error} disabled={disabled}>{control}</FormField>{feedback ? <output className="mt-2 block text-xs text-muted-foreground" aria-live="polite">{feedback}</output> : null}{kind === 'phone' && phone ? <output className="mt-2 block text-xs text-muted-foreground" aria-live="polite">{phone}</output> : null}</div>
77
+ }
78
+
79
+ export function SourceTextInputMatrix({ size = 36, state = 'filled' }: { size?: InputSize; state?: InputState }) {
80
+ return <div className="grid w-full grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3">{inputKinds.map(kind => <SourceTextInput key={`${kind}-${size}-${state}`} kind={kind} size={size} state={state} />)}</div>
81
+ }
82
+
83
+ export function SourcePhoneExamples({ state = 'filled' }: { state?: InputState }) {
84
+ return <div className="grid w-full grid-cols-1 gap-6 lg:grid-cols-3">{([32, 36, 40] as const).map(size => <SourceTextInput key={`${size}-${state}`} kind="phone" size={size} state={state} />)}</div>
85
+ }
86
+
87
+ export function SourceTextareaExamples() {
88
+ const t = useT()
89
+ return <div className="grid w-full grid-cols-1 gap-6 lg:grid-cols-2">{(['placeholder', 'filled', 'error', 'disabled'] as const).map(state => <FormField key={state} label={t('design_system.gallery.samples.textInput.note')} description={t('design_system.gallery.samples.textInput.hint')} disabled={state === 'disabled'} error={state === 'error' ? t('design_system.gallery.samples.textInput.error') : undefined}><Textarea appearance="source" showCount maxLength={200} placeholder={t('design_system.gallery.samples.textInput.placeholder')} defaultValue={state === 'placeholder' ? '' : t('design_system.gallery.samples.textInput.noteValue')} /></FormField>)}</div>
90
+ }
@@ -0,0 +1,5 @@
1
+ // Generated by yarn storybook:generate from time-picker.tsx.
2
+ const source = "import * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { TimePickerDurationChip, TimePickerSlot, TimePickerStatusChip } from '@open-mercato/ui/primitives/time-picker'\n\nconst states = ['default', 'hover', 'selected', 'disabled'] as const\nconst statuses = ['available', 'busy', 'in-meeting', 'offline'] as const\n\nexport function TimePickerStatusExamples() {\n const t = useT()\n const [active, setActive] = React.useState<string[]>(statuses.map(status => `selected-${status}`))\n return <div className=\"grid w-full gap-4 sm:grid-cols-2\">{states.map(state => <div key={state} className=\"space-y-2\">\n <p className=\"text-xs text-muted-foreground\">{state}</p><div className=\"flex flex-wrap gap-2\">{statuses.map(status => {\n const key = `${state}-${status}`\n return <TimePickerStatusChip key={key} variant={status} label={t(`design_system.gallery.samples.time.${status}`)} state={state === 'selected' ? 'default' : state} selected={active.includes(key)} disabled={state === 'disabled'} onSelect={() => setActive(value => value.includes(key) ? value.filter(item => item !== key) : [...value, key])} />\n })}</div>\n </div>)}</div>\n}\n\nexport function TimePickerDurationExamples() {\n const t = useT()\n const [active, setActive] = React.useState<string[]>(['selected'])\n return <div className=\"flex flex-wrap gap-4\">{states.map(state => <div key={state} className=\"space-y-2\"><p className=\"text-xs text-muted-foreground\">{state === 'selected' ? 'active' : state}</p><TimePickerDurationChip value={30} label={t('design_system.gallery.samples.time.duration')} state={state === 'selected' ? 'default' : state} selected={active.includes(state)} disabled={state === 'disabled'} onSelect={() => setActive(value => value.includes(state) ? value.filter(item => item !== state) : [...value, state])} /></div>)}</div>\n}\n\nexport function TimePickerSlotExamples() {\n const [active, setActive] = React.useState<string[]>(['right-selected', 'center-selected'])\n return <div className=\"grid w-full gap-5 sm:grid-cols-2\">{(['right', 'center'] as const).map(position => <div key={position} className=\"w-full max-w-80 space-y-2\"><p className=\"text-xs text-muted-foreground\">{position}</p>{states.map(state => {\n const key = `${position}-${state}`\n return <TimePickerSlot key={key} value=\"09:30\" rightText=\"09:30\" checkPosition={position} state={state === 'selected' ? 'default' : state} selected={active.includes(key)} disabled={state === 'disabled'} onSelect={() => setActive(value => value.includes(key) ? value.filter(item => item !== key) : [...value, key])} />\n })}</div>)}</div>\n}\n"
3
+ export const timePickerStatusCode = source + "\n<TimePickerStatusExamples />\n"
4
+ export const timePickerDurationCode = source + "\n<TimePickerDurationExamples />\n"
5
+ export const timePickerSlotCode = source + "\n<TimePickerSlotExamples />\n"
@@ -0,0 +1,31 @@
1
+ import * as React from 'react'
2
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
3
+ import { TimePickerDurationChip, TimePickerSlot, TimePickerStatusChip } from '@open-mercato/ui/primitives/time-picker'
4
+
5
+ const states = ['default', 'hover', 'selected', 'disabled'] as const
6
+ const statuses = ['available', 'busy', 'in-meeting', 'offline'] as const
7
+
8
+ export function TimePickerStatusExamples() {
9
+ const t = useT()
10
+ const [active, setActive] = React.useState<string[]>(statuses.map(status => `selected-${status}`))
11
+ return <div className="grid w-full gap-4 sm:grid-cols-2">{states.map(state => <div key={state} className="space-y-2">
12
+ <p className="text-xs text-muted-foreground">{state}</p><div className="flex flex-wrap gap-2">{statuses.map(status => {
13
+ const key = `${state}-${status}`
14
+ return <TimePickerStatusChip key={key} variant={status} label={t(`design_system.gallery.samples.time.${status}`)} state={state === 'selected' ? 'default' : state} selected={active.includes(key)} disabled={state === 'disabled'} onSelect={() => setActive(value => value.includes(key) ? value.filter(item => item !== key) : [...value, key])} />
15
+ })}</div>
16
+ </div>)}</div>
17
+ }
18
+
19
+ export function TimePickerDurationExamples() {
20
+ const t = useT()
21
+ const [active, setActive] = React.useState<string[]>(['selected'])
22
+ return <div className="flex flex-wrap gap-4">{states.map(state => <div key={state} className="space-y-2"><p className="text-xs text-muted-foreground">{state === 'selected' ? 'active' : state}</p><TimePickerDurationChip value={30} label={t('design_system.gallery.samples.time.duration')} state={state === 'selected' ? 'default' : state} selected={active.includes(state)} disabled={state === 'disabled'} onSelect={() => setActive(value => value.includes(state) ? value.filter(item => item !== state) : [...value, state])} /></div>)}</div>
23
+ }
24
+
25
+ export function TimePickerSlotExamples() {
26
+ const [active, setActive] = React.useState<string[]>(['right-selected', 'center-selected'])
27
+ return <div className="grid w-full gap-5 sm:grid-cols-2">{(['right', 'center'] as const).map(position => <div key={position} className="w-full max-w-80 space-y-2"><p className="text-xs text-muted-foreground">{position}</p>{states.map(state => {
28
+ const key = `${position}-${state}`
29
+ return <TimePickerSlot key={key} value="09:30" rightText="09:30" checkPosition={position} state={state === 'selected' ? 'default' : state} selected={active.includes(key)} disabled={state === 'disabled'} onSelect={() => setActive(value => value.includes(key) ? value.filter(item => item !== key) : [...value, key])} />
30
+ })}</div>)}</div>
31
+ }