@intlayer/design-system 9.4.1 → 9.4.3

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 (215) hide show
  1. package/README.md +1 -1
  2. package/dist/esm/api/index.mjs +2 -2
  3. package/dist/esm/api/useUser/index.mjs +1 -1
  4. package/dist/esm/components/Avatar/index.mjs +1 -1
  5. package/dist/esm/components/Avatar/index.mjs.map +1 -1
  6. package/dist/esm/components/Badge/index.mjs +3 -3
  7. package/dist/esm/components/Badge/index.mjs.map +1 -1
  8. package/dist/esm/components/Breadcrumb/index.mjs +5 -5
  9. package/dist/esm/components/Breadcrumb/index.mjs.map +1 -1
  10. package/dist/esm/components/Browser/Browser.mjs +4 -4
  11. package/dist/esm/components/Browser/Browser.mjs.map +1 -1
  12. package/dist/esm/components/Button/Button.mjs +1 -0
  13. package/dist/esm/components/Button/Button.mjs.map +1 -1
  14. package/dist/esm/components/Carousel/index.mjs +5 -5
  15. package/dist/esm/components/Carousel/index.mjs.map +1 -1
  16. package/dist/esm/components/CollapsibleTable/CollapsibleTable.mjs +5 -5
  17. package/dist/esm/components/CollapsibleTable/CollapsibleTable.mjs.map +1 -1
  18. package/dist/esm/components/Container/index.mjs +2 -2
  19. package/dist/esm/components/Container/index.mjs.map +1 -1
  20. package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +1 -1
  21. package/dist/esm/components/DictionaryEditor/ItemLayout.mjs +1 -1
  22. package/dist/esm/components/DictionaryEditor/ItemLayout.mjs.map +1 -1
  23. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +7 -7
  24. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs.map +1 -1
  25. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +1 -1
  26. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +9 -9
  27. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs.map +1 -1
  28. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +4 -4
  29. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs.map +1 -1
  30. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs +1 -1
  31. package/dist/esm/components/DictionaryFieldEditor/{ContentEditor.mjs → NodeEditor.mjs} +9 -9
  32. package/dist/esm/components/DictionaryFieldEditor/NodeEditor.mjs.map +1 -0
  33. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +3 -3
  34. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs.map +1 -1
  35. package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs +2 -2
  36. package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs.map +1 -1
  37. package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs +2 -2
  38. package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs.map +1 -1
  39. package/dist/esm/components/DictionaryFieldEditor/index.mjs +3 -3
  40. package/dist/esm/components/Footer/index.mjs +5 -3
  41. package/dist/esm/components/Footer/index.mjs.map +1 -1
  42. package/dist/esm/components/HeightResizer/index.mjs +1 -1
  43. package/dist/esm/components/HeightResizer/index.mjs.map +1 -1
  44. package/dist/esm/components/IDE/Code.mjs +1 -1
  45. package/dist/esm/components/IDE/Code.mjs.map +1 -1
  46. package/dist/esm/components/IDE/CodeBlockHighlight.mjs +1 -1
  47. package/dist/esm/components/IDE/CodeBlockHighlight.mjs.map +1 -1
  48. package/dist/esm/components/IDE/CodeBlockServer.mjs +1 -1
  49. package/dist/esm/components/IDE/CodeBlockServer.mjs.map +1 -1
  50. package/dist/esm/components/IDE/CodeBlockShiki.mjs +6 -48
  51. package/dist/esm/components/IDE/CodeBlockShiki.mjs.map +1 -1
  52. package/dist/esm/components/IDE/FileList.mjs +1 -1
  53. package/dist/esm/components/IDE/FileList.mjs.map +1 -1
  54. package/dist/esm/components/IDE/FileTree.mjs +1 -1
  55. package/dist/esm/components/IDE/FileTree.mjs.map +1 -1
  56. package/dist/esm/components/IDE/IDE.mjs +10 -7
  57. package/dist/esm/components/IDE/IDE.mjs.map +1 -1
  58. package/dist/esm/components/IDE/MarkDownRender.mjs +4 -3
  59. package/dist/esm/components/IDE/MarkDownRender.mjs.map +1 -1
  60. package/dist/esm/components/IDE/shikiBundle.mjs +61 -0
  61. package/dist/esm/components/IDE/shikiBundle.mjs.map +1 -0
  62. package/dist/esm/components/IDE/shikiLanguages.mjs +2 -39
  63. package/dist/esm/components/IDE/shikiLanguages.mjs.map +1 -1
  64. package/dist/esm/components/IDE/shikiThemes.mjs.map +1 -1
  65. package/dist/esm/components/Input/Checkbox.mjs +4 -4
  66. package/dist/esm/components/Input/Checkbox.mjs.map +1 -1
  67. package/dist/esm/components/Input/Input.mjs +2 -2
  68. package/dist/esm/components/Input/Input.mjs.map +1 -1
  69. package/dist/esm/components/Input/OTPInput.mjs +1 -1
  70. package/dist/esm/components/Input/OTPInput.mjs.map +1 -1
  71. package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs +2 -2
  72. package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs.map +1 -1
  73. package/dist/esm/components/LanguageBackground/LanguageSection.mjs +6 -8
  74. package/dist/esm/components/LanguageBackground/LanguageSection.mjs.map +1 -1
  75. package/dist/esm/components/LanguageBackground/index.mjs +2 -2
  76. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs +2 -2
  77. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs.map +1 -1
  78. package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs +8 -3
  79. package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs.map +1 -1
  80. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +9 -8
  81. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
  82. package/dist/esm/components/MarkdownEditor/MarkdownEditor.mjs +2 -2
  83. package/dist/esm/components/MarkdownEditor/MarkdownEditor.mjs.map +1 -1
  84. package/dist/esm/components/MarkdownEditor/extensions.mjs +1 -1
  85. package/dist/esm/components/MarkdownEditor/extensions.mjs.map +1 -1
  86. package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs +3 -3
  87. package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs.map +1 -1
  88. package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs +2 -2
  89. package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs.map +1 -1
  90. package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs +2 -2
  91. package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs.map +1 -1
  92. package/dist/esm/components/MarkdownEditor/novel/extensions/code-block-shiki.mjs +197 -0
  93. package/dist/esm/components/MarkdownEditor/novel/extensions/code-block-shiki.mjs.map +1 -0
  94. package/dist/esm/components/MarkdownEditor/novel/extensions/index.mjs +2 -1
  95. package/dist/esm/components/MarkdownEditor/novel/extensions/index.mjs.map +1 -1
  96. package/dist/esm/components/MarkdownEditor/novel/index.mjs +2 -1
  97. package/dist/esm/components/Modal/Modal.mjs +0 -1
  98. package/dist/esm/components/Modal/Modal.mjs.map +1 -1
  99. package/dist/esm/components/Navbar/DesktopNavbar.mjs +3 -3
  100. package/dist/esm/components/Navbar/DesktopNavbar.mjs.map +1 -1
  101. package/dist/esm/components/Navbar/MobileNavbar.mjs +1 -1
  102. package/dist/esm/components/Navbar/MobileNavbar.mjs.map +1 -1
  103. package/dist/esm/components/Pattern/GridDistortionPattern.mjs +2 -2
  104. package/dist/esm/components/Pattern/GridDistortionPattern.mjs.map +1 -1
  105. package/dist/esm/components/Pattern/GridPattern.mjs +1 -1
  106. package/dist/esm/components/Pattern/GridPattern.mjs.map +1 -1
  107. package/dist/esm/components/RightDrawer/RightDrawer.mjs +2 -2
  108. package/dist/esm/components/RightDrawer/RightDrawer.mjs.map +1 -1
  109. package/dist/esm/components/Select/Multiselect.mjs +2 -2
  110. package/dist/esm/components/Select/Multiselect.mjs.map +1 -1
  111. package/dist/esm/components/Select/Select.mjs +2 -2
  112. package/dist/esm/components/Select/Select.mjs.map +1 -1
  113. package/dist/esm/components/Status/index.mjs +28 -0
  114. package/dist/esm/components/Status/index.mjs.map +1 -0
  115. package/dist/esm/components/Steps/index.mjs +3 -3
  116. package/dist/esm/components/Steps/index.mjs.map +1 -1
  117. package/dist/esm/components/SwitchSelector/SwitchSelector.mjs +12 -11
  118. package/dist/esm/components/SwitchSelector/SwitchSelector.mjs.map +1 -1
  119. package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs +6 -6
  120. package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs.map +1 -1
  121. package/dist/esm/components/Tab/Tab.mjs +1 -1
  122. package/dist/esm/components/Tab/Tab.mjs.map +1 -1
  123. package/dist/esm/components/TabSelector/TabSelector.mjs +5 -5
  124. package/dist/esm/components/TabSelector/TabSelector.mjs.map +1 -1
  125. package/dist/esm/components/Table/SmartTable.mjs +2 -2
  126. package/dist/esm/components/Table/SmartTable.mjs.map +1 -1
  127. package/dist/esm/components/Table/Table.mjs +1 -1
  128. package/dist/esm/components/Table/Table.mjs.map +1 -1
  129. package/dist/esm/components/Table/TableElements.mjs +2 -2
  130. package/dist/esm/components/Table/TableElements.mjs.map +1 -1
  131. package/dist/esm/components/Tag/index.mjs +1 -1
  132. package/dist/esm/components/Tag/index.mjs.map +1 -1
  133. package/dist/esm/components/TechLogo/TechLogo.mjs +2 -0
  134. package/dist/esm/components/TechLogo/TechLogo.mjs.map +1 -1
  135. package/dist/esm/components/TechLogo/logos/HTMX.mjs +31 -0
  136. package/dist/esm/components/TechLogo/logos/HTMX.mjs.map +1 -0
  137. package/dist/esm/components/TechLogo/logos/Remix.mjs +22 -0
  138. package/dist/esm/components/TechLogo/logos/Remix.mjs.map +1 -0
  139. package/dist/esm/components/TechLogo/techLogos.mjs +5 -1
  140. package/dist/esm/components/TechLogo/techLogos.mjs.map +1 -1
  141. package/dist/esm/components/Terminal/Terminal.mjs +4 -4
  142. package/dist/esm/components/Terminal/Terminal.mjs.map +1 -1
  143. package/dist/esm/components/TextArea/TextArea.mjs.map +1 -1
  144. package/dist/esm/components/Toaster/Toast.mjs +4 -4
  145. package/dist/esm/components/Toaster/Toast.mjs.map +1 -1
  146. package/dist/esm/components/index.mjs +8 -7
  147. package/dist/esm/hooks/usePersistedStore.mjs +6 -18
  148. package/dist/esm/hooks/usePersistedStore.mjs.map +1 -1
  149. package/dist/esm/providers/ReactQueryProvider.mjs +10 -2
  150. package/dist/esm/providers/ReactQueryProvider.mjs.map +1 -1
  151. package/dist/esm/providers/ThemeProvider.mjs +27 -15
  152. package/dist/esm/providers/ThemeProvider.mjs.map +1 -1
  153. package/dist/esm/routes.mjs +24 -6
  154. package/dist/esm/routes.mjs.map +1 -1
  155. package/dist/esm/structured-data/buildItemListJsonLd.mjs +27 -0
  156. package/dist/esm/structured-data/buildItemListJsonLd.mjs.map +1 -0
  157. package/dist/esm/structured-data/index.mjs +2 -1
  158. package/dist/types/components/Badge/index.d.ts.map +1 -1
  159. package/dist/types/components/Button/Button.d.ts +2 -2
  160. package/dist/types/components/Button/Button.d.ts.map +1 -1
  161. package/dist/types/components/Container/index.d.ts.map +1 -1
  162. package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts.map +1 -1
  163. package/dist/types/components/DictionaryFieldEditor/NodeEditor.d.ts +10 -0
  164. package/dist/types/components/DictionaryFieldEditor/NodeEditor.d.ts.map +1 -0
  165. package/dist/types/components/DictionaryFieldEditor/index.d.ts +2 -2
  166. package/dist/types/components/Footer/index.d.ts.map +1 -1
  167. package/dist/types/components/Form/FormBase.d.ts +1 -1
  168. package/dist/types/components/IDE/CodeBlockServer.d.ts +1 -1
  169. package/dist/types/components/IDE/CodeBlockServer.d.ts.map +1 -1
  170. package/dist/types/components/IDE/CodeBlockShiki.d.ts.map +1 -1
  171. package/dist/types/components/IDE/MarkDownRender.d.ts +1 -0
  172. package/dist/types/components/IDE/MarkDownRender.d.ts.map +1 -1
  173. package/dist/types/components/IDE/shikiBundle.d.ts +74 -0
  174. package/dist/types/components/IDE/shikiBundle.d.ts.map +1 -0
  175. package/dist/types/components/IDE/shikiLanguages.d.ts +2 -39
  176. package/dist/types/components/IDE/shikiLanguages.d.ts.map +1 -1
  177. package/dist/types/components/IDE/shikiThemes.d.ts +3 -1
  178. package/dist/types/components/IDE/shikiThemes.d.ts.map +1 -1
  179. package/dist/types/components/LanguageBackground/LanguageSection.d.ts +5 -1
  180. package/dist/types/components/LanguageBackground/LanguageSection.d.ts.map +1 -1
  181. package/dist/types/components/LanguageBackground/index.d.ts +2 -2
  182. package/dist/types/components/MarkDownRender/MarkDownIframe.d.ts.map +1 -1
  183. package/dist/types/components/MarkDownRender/MarkDownRender.d.ts.map +1 -1
  184. package/dist/types/components/MarkdownEditor/novel/extensions/code-block-shiki.d.ts +15 -0
  185. package/dist/types/components/MarkdownEditor/novel/extensions/code-block-shiki.d.ts.map +1 -0
  186. package/dist/types/components/MarkdownEditor/novel/extensions/index.d.ts +2 -1
  187. package/dist/types/components/MarkdownEditor/novel/extensions/index.d.ts.map +1 -1
  188. package/dist/types/components/MarkdownEditor/novel/index.d.ts +2 -1
  189. package/dist/types/components/Status/index.d.ts +8 -0
  190. package/dist/types/components/Status/index.d.ts.map +1 -0
  191. package/dist/types/components/SwitchSelector/SwitchSelector.d.ts +3 -3
  192. package/dist/types/components/SwitchSelector/SwitchSelector.d.ts.map +1 -1
  193. package/dist/types/components/Table/TableElements.d.ts.map +1 -1
  194. package/dist/types/components/TechLogo/TechLogo.d.ts.map +1 -1
  195. package/dist/types/components/TechLogo/logos/HTMX.d.ts +6 -0
  196. package/dist/types/components/TechLogo/logos/HTMX.d.ts.map +1 -0
  197. package/dist/types/components/TechLogo/logos/Remix.d.ts +6 -0
  198. package/dist/types/components/TechLogo/logos/Remix.d.ts.map +1 -0
  199. package/dist/types/components/TechLogo/techLogos.d.ts +4 -2
  200. package/dist/types/components/TechLogo/types.d.ts +1 -1
  201. package/dist/types/components/TextArea/TextArea.d.ts +1 -1
  202. package/dist/types/components/index.d.ts +3 -2
  203. package/dist/types/providers/ThemeProvider.d.ts.map +1 -1
  204. package/dist/types/routes.d.ts +21 -3
  205. package/dist/types/routes.d.ts.map +1 -1
  206. package/dist/types/structured-data/buildItemListJsonLd.d.ts +36 -0
  207. package/dist/types/structured-data/buildItemListJsonLd.d.ts.map +1 -0
  208. package/dist/types/structured-data/index.d.ts +2 -1
  209. package/package.json +68 -54
  210. package/src/components/MarkdownEditor/markdown-editor.css +17 -17
  211. package/styles/globals.css +71 -0
  212. package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs.map +0 -1
  213. package/dist/types/components/DictionaryFieldEditor/ContentEditor.d.ts +0 -10
  214. package/dist/types/components/DictionaryFieldEditor/ContentEditor.d.ts.map +0 -1
  215. package/tailwind.css +0 -269
package/README.md CHANGED
@@ -278,4 +278,4 @@ Contribute on [GitHub](https://github.com/aymericzip/intlayer), [GitLab](https:/
278
278
 
279
279
  If you like Intlayer, give us a ⭐ on GitHub. It helps others discover the project! [See why GitHub Stars matter](https://github.com/aymericzip/intlayer/blob/main/CONTRIBUTING.md#why-github-stars-matter-).
280
280
 
281
- [![Star History Chart](https://star-history.dera.page/svg?repos=aymericzip/intlayer&type=Date)](https://star-history.dera.page/#aymericzip/intlayer&Date)
281
+ [![Star History Chart](https://api.star-history.com/svg?repos=aymericzip/intlayer&type=Date)](https://api.star-history.com/#aymericzip/intlayer&Date)
@@ -1,12 +1,13 @@
1
1
  import { useAiAPI, useAnalyticsAPI, useAssetAPI, useAuditAPI, useBitbucketAPI, useDictionaryAPI, useEditorAPI, useEnvironmentAPI, useGithubAPI, useGitlabAPI, useIntlayerAuth, useIntlayerOAuth, useIntlayerOAuthOptions, useNewsletterAPI, useOAuthAPI, useOrganizationAPI, useProjectAPI, useReviewerAPI, useSearchAPI, useShowcaseProjectAPI, useStripeAPI, useTagAPI, useTranslateAPI, useUserAPI } from "./useIntlayerAPI.mjs";
2
+ import { useAddPasskey, useAskResetPassword, useChangePassword, useDeletePasskey, useDeleteSSOProvider, useDisableTwoFactor, useEnableTwoFactor, useGetUserByAccount, useGetVerifyEmailStatus, useLinkSocial, useListAccounts, useListPasskeys, useListSSOProviders, useLogin, useLogout, useRegister, useRegisterSSO, useResetPassword, useSignInMagicLink, useSignInPasskey, useSignInSSO, useUnlinkAccount, useVerifyBackupCode, useVerifyEmail, useVerifyTotp } from "./hooks/auth.mjs";
2
3
  import { useOAuth2 } from "./useAuth/useOAuth2.mjs";
3
4
  import { useSession } from "./useAuth/useSession.mjs";
4
5
  import { useAuth } from "./useAuth/useAuth.mjs";
6
+ import { useUser } from "./useUser/index.mjs";
5
7
  import { useAskDocQuestion, useAuditContentDeclaration, useAuditContentDeclarationField, useAuditContentDeclarationMetadata, useAuditTag, useAutocomplete, useChat, useCustomQuery, useGetAIStats, useTranslateJSONDeclaration } from "./hooks/ai.mjs";
6
8
  import { useGetAnalyticsAudience, useGetContentStats, useGetExperimentResults } from "./hooks/analytics.mjs";
7
9
  import { useDeleteAsset, useGetAssetById, useGetAssets, useUpdateAsset, useUploadAsset } from "./hooks/asset.mjs";
8
10
  import { useAuditScan, useGetRecursiveAuditStatus, useStartRecursiveAudit } from "./hooks/audit.mjs";
9
- import { useAddPasskey, useAskResetPassword, useChangePassword, useDeletePasskey, useDeleteSSOProvider, useDisableTwoFactor, useEnableTwoFactor, useGetUserByAccount, useGetVerifyEmailStatus, useLinkSocial, useListAccounts, useListPasskeys, useListSSOProviders, useLogin, useLogout, useRegister, useRegisterSSO, useResetPassword, useSignInMagicLink, useSignInPasskey, useSignInSSO, useUnlinkAccount, useVerifyBackupCode, useVerifyEmail, useVerifyTotp } from "./hooks/auth.mjs";
10
11
  import { useBitbucketAuth, useBitbucketCheckConfig, useBitbucketGetConfigFile, useBitbucketRepos } from "./hooks/bitbucket.mjs";
11
12
  import { useAddDictionary, useDeleteDictionary, useGetDictionaries, useGetDictionariesKeys, useGetDictionary, useInfiniteGetDictionaries, usePushDictionaries, useUpdateDictionary } from "./hooks/dictionary.mjs";
12
13
  import { useGetDiscussions, useGetDiscussionsData } from "./hooks/discussions.mjs";
@@ -24,6 +25,5 @@ import { useAcceptAffiliateInvitation, useCancelSubscription, useCreatePortalSes
24
25
  import { useAddTag, useDeleteTag, useGetTags, useUpdateTag } from "./hooks/tag.mjs";
25
26
  import { useFillAllTranslations, usePauseTranslationJob, useResumeTranslationJob, useStopTranslationJob } from "./hooks/translate.mjs";
26
27
  import { useCreateUser, useDeleteUser, useGetUserById, useGetUsers, useUpdateUser, useUploadUserAvatar } from "./hooks/user.mjs";
27
- import { useUser } from "./useUser/index.mjs";
28
28
 
29
29
  export { useAcceptAffiliateInvitation, useAddDictionary, useAddEnvironment, useAddNewAccessKey, useAddOrganization, useAddOrganizationMember, useAddPasskey, useAddProject, useAddTag, useAiAPI, useAnalyticsAPI, useAskDocQuestion, useAskResetPassword, useAssetAPI, useAuditAPI, useAuditContentDeclaration, useAuditContentDeclarationField, useAuditContentDeclarationMetadata, useAuditScan, useAuditTag, useAuth, useAutocomplete, useBitbucketAPI, useBitbucketAuth, useBitbucketCheckConfig, useBitbucketGetConfigFile, useBitbucketRepos, useCancelSubscription, useChangePassword, useChat, useContactReviewer, useCreateMission, useCreatePortalSession, useCreatePromoCode, useCreateUser, useCustomQuery, useDeleteAccessKey, useDeleteAsset, useDeleteDictionary, useDeleteEnvironment, useDeleteOrganization, useDeleteOrganizationById, useDeletePasskey, useDeleteProject, useDeleteProjectById, useDeletePromoCode, useDeleteReviewerProfile, useDeleteSSOProvider, useDeleteShowcaseProject, useDeleteTag, useDeleteUser, useDictionaryAPI, useDisableTwoFactor, useEditorAPI, useEnableTwoFactor, useEnvironmentAPI, useEstimateMission, useFillAllTranslations, useGetAIStats, useGetAdminReviewers, useGetAffiliate, useGetAffiliateAccountSession, useGetAffiliateById, useGetAffiliateInvitation, useGetAffiliateInvitations, useGetAffiliateOnboardingLink, useGetAffiliatePromoCode, useGetAffiliateStats, useGetAffiliates, useGetAnalyticsAudience, useGetAssetById, useGetAssets, useGetCIConfig, useGetChatHistory, useGetContentStats, useGetDictionaries, useGetDictionariesKeys, useGetDictionary, useGetDiscussions, useGetDiscussionsData, useGetEditorDictionaries, useGetExperimentResults, useGetInvoices, useGetMissionById, useGetMyMissions, useGetMyReviewerProfile, useGetNewsletterStatus, useGetOrganizations, useGetOtherShowcaseProjects, useGetPaymentMethod, useGetPricing, useGetProjectInsights, useGetProjects, useGetPromoCodeById, useGetPromoCodes, useGetRecursiveAuditStatus, useGetReviewerById, useGetReviewerMarketplace, useGetReviewerPriceDistribution, useGetReviewerReviews, useGetShowcaseProjectById, useGetShowcaseProjects, useGetSubscription, useGetTags, useGetUserByAccount, useGetUserById, useGetUsers, useGetVerifyEmailStatus, useGithubAPI, useGithubAuth, useGithubCheckConfig, useGithubGetAuthUrl, useGithubGetConfigFile, useGithubRepos, useGithubToken, useGitlabAPI, useGitlabAuth, useGitlabCheckConfig, useGitlabGetConfigFile, useGitlabProjects, useGrantAffiliateAccess, useInfiniteGetDictionaries, useIntlayerAuth, useIntlayerOAuth, useIntlayerOAuthOptions, useLinkSocial, useListAccounts, useListPasskeys, useListSSOProviders, useLogin, useLogout, useMigrateEnvironment, useNewsletterAPI, useOAuth2, useOAuthAPI, useOrganizationAPI, usePauseTranslationJob, useProjectAPI, usePushCIConfig, usePushDictionaries, usePushProjectConfiguration, useRefreshAccessKey, useRegister, useRegisterAsReviewer, useRegisterSSO, useResetPassword, useResetToProductionEnvironment, useResumeTranslationJob, useReviewerAPI, useSearchAPI, useSearchDoc, useSelectEnvironment, useSelectOrganization, useSelectProject, useSendAffiliateInvitation, useSendReviewerMessage, useSession, useShowcaseProjectAPI, useSignInMagicLink, useSignInPasskey, useSignInSSO, useStartRecursiveAudit, useStopTranslationJob, useStripeAPI, useSubmitReview, useSubmitShowcaseProject, useSubscribeToNewsletter, useTagAPI, useToggleShowcaseDownvote, useToggleShowcaseUpvote, useTranslateAPI, useTranslateJSONDeclaration, useTriggerBuild, useTriggerWebhook, useUnlinkAccount, useUnselectOrganization, useUnselectProject, useUnsubscribeFromNewsletter, useUpdateAffiliateStatus, useUpdateAsset, useUpdateDictionary, useUpdateEnvironment, useUpdateMemberAccess, useUpdateMissionStatus, useUpdateOrganization, useUpdateOrganizationMailerConfig, useUpdateOrganizationMembers, useUpdateOrganizationMembersById, useUpdateProject, useUpdateProjectMembers, useUpdatePromoCode, useUpdateReviewerProfile, useUpdateShowcaseProject, useUpdateTag, useUpdateUser, useUploadAsset, useUploadReviewerCoverPicture, useUploadReviewerMainPicture, useUploadUserAvatar, useUser, useUserAPI, useValidateReviewerProfile, useVerifyBackupCode, useVerifyEmail, useVerifyTotp, useWriteDictionary };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { useAuth } from "../useAuth/useAuth.mjs";
4
3
  import { useLogout } from "../hooks/auth.mjs";
4
+ import { useAuth } from "../useAuth/useAuth.mjs";
5
5
 
6
6
  //#region src/api/useUser/index.ts
7
7
  const useUser = () => {
@@ -92,7 +92,7 @@ const Avatar = ({ fullname, className, isLoading = false, isLoggedIn = true, src
92
92
  children: /* @__PURE__ */ jsx("div", {
93
93
  className: "relative flex aspect-square size-full flex-row items-center justify-center",
94
94
  children: /* @__PURE__ */ jsxs("div", {
95
- className: "absolute top-0 left-0 flex aspect-square size-full flex-col items-center justify-center rounded-full bg-text text-text-opposite",
95
+ className: "absolute top-0 left-0 flex aspect-square size-full flex-col items-center justify-center rounded-full bg-text text-foreground-opposite",
96
96
  children: [
97
97
  displayLoader && /* @__PURE__ */ jsx(Loader, {
98
98
  className: "w-3/4",
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Avatar/index.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport { User } from 'lucide-react';\nimport type { ComponentProps, FC, HTMLAttributes } from 'react';\nimport { useMemo } from 'react';\nimport { Loader } from '../Loader';\n\n/**\n * Props for the Avatar component\n */\nexport interface AvatarProps extends Omit<ComponentProps<'button'>, 'onClick'> {\n /** Image source URL for the avatar */\n src?: string;\n /** Full name used to generate initials and alt text */\n fullname?: string;\n /** Displays a loading spinner when true */\n isLoading?: boolean;\n /** Whether the user is authenticated */\n isLoggedIn?: boolean;\n /** Size variant of the avatar */\n size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n /** Click handler - when provided, makes the avatar clickable */\n onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n /** Alternative text for accessibility */\n alt?: string;\n /** Whether the avatar should be focusable when not clickable */\n focusable?: boolean;\n /** Whether the avatar should be hoverable */\n hoverable?: boolean;\n}\n\n/**\n * @description Gets the capital letters from a name.\n * @param name - The name to extract capitals from.\n * @param separator - The separator to split the name (default is an empty string, which splits by each character).\n * @returns {string[]} An array of capital letters from the name.\n */\nexport const getCapitals = (name: string, separator = ' '): string[] => {\n if (!name) return [];\n\n const parts =\n separator === ' '\n ? name.trim().split(/\\s+/) // handle multiple spaces\n : name.split(separator);\n\n return parts.filter(Boolean).map((word) => word.charAt(0).toUpperCase());\n};\n\n/**\n * Container component that renders either a button or div based on interactivity\n */\nconst Container: FC<\n HTMLAttributes<HTMLElement> & {\n isClickable: boolean;\n onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n }\n> = ({ isClickable, onClick, ...props }) => {\n if (isClickable && onClick) {\n return (\n <button\n {...(props as ComponentProps<'button'>)}\n onClick={onClick}\n type=\"button\"\n />\n );\n }\n\n return (\n <div\n {...(props as ComponentProps<'div'>)}\n role={\n props.tabIndex !== undefined || props['aria-label'] ? 'img' : undefined\n }\n />\n );\n};\n\n/**\n * Avatar component that displays user profile images, initials, or a default user icon.\n * Supports loading states, authentication states, and accessibility features.\n *\n * @example\n * ```tsx\n * // With image\n * <Avatar src=\"https://example.com/avatar.jpg\" fullname=\"John Doe\" />\n *\n * // With initials\n * <Avatar fullname=\"John Doe\" />\n *\n * // Clickable avatar\n * <Avatar\n * fullname=\"John Doe\"\n * onClick={(e) => console.log('Avatar clicked')}\n * />\n *\n * // Loading state\n * <Avatar isLoading fullname=\"John Doe\" />\n * ```\n */\nexport const Avatar: FC<AvatarProps> = ({\n fullname,\n className,\n isLoading = false,\n isLoggedIn = true,\n src,\n onClick,\n size = 'md',\n alt,\n focusable = false,\n hoverable = false,\n ...props\n}) => {\n const isImageDefined = Boolean(src);\n const isNameDefined = Boolean((fullname ?? '').length > 0);\n const capitals = fullname ? getCapitals(fullname) : undefined;\n\n // Display logic\n const displayLoader = isLoading;\n const displayAvatar = isLoggedIn && !displayLoader && isImageDefined;\n const displayInitials =\n isLoggedIn && !displayLoader && !displayAvatar && isNameDefined;\n const displayUserIcon =\n isLoggedIn && !displayLoader && !displayAvatar && !displayInitials;\n\n const isClickable = onClick !== undefined;\n\n // Accessibility attributes\n const accessibilityProps = useMemo(() => {\n const baseProps: Record<string, any> = {};\n\n if (displayAvatar && alt) {\n baseProps['aria-label'] = alt;\n } else if (displayAvatar && fullname) {\n baseProps['aria-label'] = `Avatar of ${fullname}`;\n } else if (displayInitials && fullname) {\n baseProps['aria-label'] = `Avatar initials for ${fullname}`;\n } else if (displayUserIcon) {\n baseProps['aria-label'] = 'Default user avatar';\n } else if (displayLoader) {\n baseProps['aria-label'] = 'Loading avatar';\n baseProps['aria-busy'] = true;\n }\n\n if (!isClickable && focusable) {\n baseProps.tabIndex = 0;\n }\n\n if (isClickable) {\n baseProps['aria-describedby'] = 'avatar-description';\n }\n\n return baseProps;\n }, [\n displayAvatar,\n displayInitials,\n displayUserIcon,\n displayLoader,\n alt,\n fullname,\n isClickable,\n focusable,\n ]);\n\n return (\n <Container\n isClickable={isClickable}\n className={cn(\n `rounded-full border-[1.3px] border-text p-0.38 ring-offset-0 transition-ring duration-200`,\n size === 'sm' && 'size-7 border-[1px] p-0.25',\n size === 'md' && 'size-9',\n size === 'lg' && 'size-12',\n size === 'xl' && 'size-16',\n size === '2xl' && 'size-24',\n isClickable &&\n `cursor-pointer hover:opacity-80 focus:outline-none focus:ring-3 focus:ring-text/50 focus:ring-offset-2`,\n hoverable &&\n `hover:opacity-80 hover:ring-4 hover:ring-text/30 hover:ring-offset-2`,\n !isClickable &&\n focusable &&\n `focus:outline-none focus:ring-3 focus:ring-text/50 focus:ring-offset-2`,\n displayLoader && 'animate-pulse',\n className\n )}\n onClick={onClick}\n {...accessibilityProps}\n {...props}\n >\n <div className=\"relative flex aspect-square size-full flex-row items-center justify-center\">\n <div className=\"absolute top-0 left-0 flex aspect-square size-full flex-col items-center justify-center rounded-full bg-text text-text-opposite\">\n {displayLoader && (\n <Loader className=\"w-3/4\" aria-label=\"Loading user avatar\" />\n )}\n\n {displayAvatar && (\n <img\n className=\"size-full rounded-full object-cover\"\n src={src}\n srcSet={src}\n alt={alt ?? `Avatar of ${fullname}`}\n width={59}\n height={59}\n loading=\"lazy\"\n draggable={false}\n />\n )}\n\n {displayInitials && (\n <div\n className={cn(\n 'flex size-full items-center justify-center gap-[0.1rem] font-bold max-md:py-1',\n size === 'sm' && 'text-xs',\n size === 'md' && 'text-sm',\n size === 'lg' && 'text-base',\n size === 'xl' && 'text-lg',\n size === '2xl' && 'text-xl'\n )}\n >\n {capitals?.map((capital, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Capitals are primitives and order is fixed\n <span key={`${capital}-${index}`}>{capital}</span>\n ))}\n </div>\n )}\n\n {displayUserIcon && (\n <User\n size={cn(\n size === 'sm' && 14,\n size === 'md' && 25,\n size === 'lg' && 30,\n size === 'xl' && 40\n )}\n aria-label=\"Default user icon\"\n />\n )}\n </div>\n </div>\n </Container>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAoCA,MAAa,eAAe,MAAc,YAAY,QAAkB;CACtE,IAAI,CAAC,MAAM,OAAO,CAAC;CAOnB,QAJE,cAAc,MACV,KAAK,KAAK,CAAC,CAAC,MAAM,KAAK,IACvB,KAAK,MAAM,SAAS,EAEd,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC;AACzE;;;;AAKA,MAAM,aAKD,EAAE,aAAa,SAAS,GAAG,YAAY;CAC1C,IAAI,eAAe,SACjB,OACE,oBAAC,UAAD;EACE,GAAK;EACI;EACT,MAAK;CACN;CAIL,OACE,oBAAC,OAAD;EACE,GAAK;EACL,MACE,MAAM,aAAa,UAAa,MAAM,gBAAgB,QAAQ;CAEjE;AAEL;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,UAA2B,EACtC,UACA,WACA,YAAY,OACZ,aAAa,MACb,KACA,SACA,OAAO,MACP,KACA,YAAY,OACZ,YAAY,OACZ,GAAG,YACC;CACJ,MAAM,iBAAiB,QAAQ,GAAG;CAClC,MAAM,gBAAgB,SAAS,YAAY,GAAE,CAAE,SAAS,CAAC;CACzD,MAAM,WAAW,WAAW,YAAY,QAAQ,IAAI;CAGpD,MAAM,gBAAgB;CACtB,MAAM,gBAAgB,cAAc,CAAC,iBAAiB;CACtD,MAAM,kBACJ,cAAc,CAAC,iBAAiB,CAAC,iBAAiB;CACpD,MAAM,kBACJ,cAAc,CAAC,iBAAiB,CAAC,iBAAiB,CAAC;CAErD,MAAM,cAAc,YAAY;CAGhC,MAAM,qBAAqB,cAAc;EACvC,MAAM,YAAiC,CAAC;EAExC,IAAI,iBAAiB,KACnB,UAAU,gBAAgB;OACrB,IAAI,iBAAiB,UAC1B,UAAU,gBAAgB,aAAa;OAClC,IAAI,mBAAmB,UAC5B,UAAU,gBAAgB,uBAAuB;OAC5C,IAAI,iBACT,UAAU,gBAAgB;OACrB,IAAI,eAAe;GACxB,UAAU,gBAAgB;GAC1B,UAAU,eAAe;EAC3B;EAEA,IAAI,CAAC,eAAe,WAClB,UAAU,WAAW;EAGvB,IAAI,aACF,UAAU,sBAAsB;EAGlC,OAAO;CACT,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,OACE,oBAAC,WAAD;EACe;EACb,WAAW,GACT,6FACA,SAAS,QAAQ,8BACjB,SAAS,QAAQ,UACjB,SAAS,QAAQ,WACjB,SAAS,QAAQ,WACjB,SAAS,SAAS,WAClB,eACE,0GACF,aACE,wEACF,CAAC,eACC,aACA,0EACF,iBAAiB,iBACjB,SACF;EACS;EACT,GAAI;EACJ,GAAI;YAEJ,oBAAC,OAAD;GAAK,WAAU;aACb,qBAAC,OAAD;IAAK,WAAU;cAAf;KACG,iBACC,oBAAC,QAAD;MAAQ,WAAU;MAAQ,cAAW;KAAuB;KAG7D,iBACC,oBAAC,OAAD;MACE,WAAU;MACL;MACL,QAAQ;MACR,KAAK,OAAO,aAAa;MACzB,OAAO;MACP,QAAQ;MACR,SAAQ;MACR,WAAW;KACZ;KAGF,mBACC,oBAAC,OAAD;MACE,WAAW,GACT,iFACA,SAAS,QAAQ,WACjB,SAAS,QAAQ,WACjB,SAAS,QAAQ,aACjB,SAAS,QAAQ,WACjB,SAAS,SAAS,SACpB;gBAEC,UAAU,KAAK,SAAS,UAEvB,oBAAC,QAAD,YAAmC,QAAc,GAAtC,GAAG,QAAQ,GAAG,OAAwB,CAClD;KACE;KAGN,mBACC,oBAAC,MAAD;MACE,MAAM,GACJ,SAAS,QAAQ,IACjB,SAAS,QAAQ,IACjB,SAAS,QAAQ,IACjB,SAAS,QAAQ,EACnB;MACA,cAAW;KACZ;IAEA;;EACF;CACI;AAEf"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Avatar/index.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport { User } from 'lucide-react';\nimport type { ComponentProps, FC, HTMLAttributes } from 'react';\nimport { useMemo } from 'react';\nimport { Loader } from '../Loader';\n\n/**\n * Props for the Avatar component\n */\nexport interface AvatarProps extends Omit<ComponentProps<'button'>, 'onClick'> {\n /** Image source URL for the avatar */\n src?: string;\n /** Full name used to generate initials and alt text */\n fullname?: string;\n /** Displays a loading spinner when true */\n isLoading?: boolean;\n /** Whether the user is authenticated */\n isLoggedIn?: boolean;\n /** Size variant of the avatar */\n size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n /** Click handler - when provided, makes the avatar clickable */\n onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n /** Alternative text for accessibility */\n alt?: string;\n /** Whether the avatar should be focusable when not clickable */\n focusable?: boolean;\n /** Whether the avatar should be hoverable */\n hoverable?: boolean;\n}\n\n/**\n * @description Gets the capital letters from a name.\n * @param name - The name to extract capitals from.\n * @param separator - The separator to split the name (default is an empty string, which splits by each character).\n * @returns {string[]} An array of capital letters from the name.\n */\nexport const getCapitals = (name: string, separator = ' '): string[] => {\n if (!name) return [];\n\n const parts =\n separator === ' '\n ? name.trim().split(/\\s+/) // handle multiple spaces\n : name.split(separator);\n\n return parts.filter(Boolean).map((word) => word.charAt(0).toUpperCase());\n};\n\n/**\n * Container component that renders either a button or div based on interactivity\n */\nconst Container: FC<\n HTMLAttributes<HTMLElement> & {\n isClickable: boolean;\n onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n }\n> = ({ isClickable, onClick, ...props }) => {\n if (isClickable && onClick) {\n return (\n <button\n {...(props as ComponentProps<'button'>)}\n onClick={onClick}\n type=\"button\"\n />\n );\n }\n\n return (\n <div\n {...(props as ComponentProps<'div'>)}\n role={\n props.tabIndex !== undefined || props['aria-label'] ? 'img' : undefined\n }\n />\n );\n};\n\n/**\n * Avatar component that displays user profile images, initials, or a default user icon.\n * Supports loading states, authentication states, and accessibility features.\n *\n * @example\n * ```tsx\n * // With image\n * <Avatar src=\"https://example.com/avatar.jpg\" fullname=\"John Doe\" />\n *\n * // With initials\n * <Avatar fullname=\"John Doe\" />\n *\n * // Clickable avatar\n * <Avatar\n * fullname=\"John Doe\"\n * onClick={(e) => console.log('Avatar clicked')}\n * />\n *\n * // Loading state\n * <Avatar isLoading fullname=\"John Doe\" />\n * ```\n */\nexport const Avatar: FC<AvatarProps> = ({\n fullname,\n className,\n isLoading = false,\n isLoggedIn = true,\n src,\n onClick,\n size = 'md',\n alt,\n focusable = false,\n hoverable = false,\n ...props\n}) => {\n const isImageDefined = Boolean(src);\n const isNameDefined = Boolean((fullname ?? '').length > 0);\n const capitals = fullname ? getCapitals(fullname) : undefined;\n\n // Display logic\n const displayLoader = isLoading;\n const displayAvatar = isLoggedIn && !displayLoader && isImageDefined;\n const displayInitials =\n isLoggedIn && !displayLoader && !displayAvatar && isNameDefined;\n const displayUserIcon =\n isLoggedIn && !displayLoader && !displayAvatar && !displayInitials;\n\n const isClickable = onClick !== undefined;\n\n // Accessibility attributes\n const accessibilityProps = useMemo(() => {\n const baseProps: Record<string, any> = {};\n\n if (displayAvatar && alt) {\n baseProps['aria-label'] = alt;\n } else if (displayAvatar && fullname) {\n baseProps['aria-label'] = `Avatar of ${fullname}`;\n } else if (displayInitials && fullname) {\n baseProps['aria-label'] = `Avatar initials for ${fullname}`;\n } else if (displayUserIcon) {\n baseProps['aria-label'] = 'Default user avatar';\n } else if (displayLoader) {\n baseProps['aria-label'] = 'Loading avatar';\n baseProps['aria-busy'] = true;\n }\n\n if (!isClickable && focusable) {\n baseProps.tabIndex = 0;\n }\n\n if (isClickable) {\n baseProps['aria-describedby'] = 'avatar-description';\n }\n\n return baseProps;\n }, [\n displayAvatar,\n displayInitials,\n displayUserIcon,\n displayLoader,\n alt,\n fullname,\n isClickable,\n focusable,\n ]);\n\n return (\n <Container\n isClickable={isClickable}\n className={cn(\n `rounded-full border-[1.3px] border-text p-0.38 ring-offset-0 transition-ring duration-200`,\n size === 'sm' && 'size-7 border-[1px] p-0.25',\n size === 'md' && 'size-9',\n size === 'lg' && 'size-12',\n size === 'xl' && 'size-16',\n size === '2xl' && 'size-24',\n isClickable &&\n `cursor-pointer hover:opacity-80 focus:outline-none focus:ring-3 focus:ring-text/50 focus:ring-offset-2`,\n hoverable &&\n `hover:opacity-80 hover:ring-4 hover:ring-text/30 hover:ring-offset-2`,\n !isClickable &&\n focusable &&\n `focus:outline-none focus:ring-3 focus:ring-text/50 focus:ring-offset-2`,\n displayLoader && 'animate-pulse',\n className\n )}\n onClick={onClick}\n {...accessibilityProps}\n {...props}\n >\n <div className=\"relative flex aspect-square size-full flex-row items-center justify-center\">\n <div className=\"absolute top-0 left-0 flex aspect-square size-full flex-col items-center justify-center rounded-full bg-text text-foreground-opposite\">\n {displayLoader && (\n <Loader className=\"w-3/4\" aria-label=\"Loading user avatar\" />\n )}\n\n {displayAvatar && (\n <img\n className=\"size-full rounded-full object-cover\"\n src={src}\n srcSet={src}\n alt={alt ?? `Avatar of ${fullname}`}\n width={59}\n height={59}\n loading=\"lazy\"\n draggable={false}\n />\n )}\n\n {displayInitials && (\n <div\n className={cn(\n 'flex size-full items-center justify-center gap-[0.1rem] font-bold max-md:py-1',\n size === 'sm' && 'text-xs',\n size === 'md' && 'text-sm',\n size === 'lg' && 'text-base',\n size === 'xl' && 'text-lg',\n size === '2xl' && 'text-xl'\n )}\n >\n {capitals?.map((capital, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Capitals are primitives and order is fixed\n <span key={`${capital}-${index}`}>{capital}</span>\n ))}\n </div>\n )}\n\n {displayUserIcon && (\n <User\n size={cn(\n size === 'sm' && 14,\n size === 'md' && 25,\n size === 'lg' && 30,\n size === 'xl' && 40\n )}\n aria-label=\"Default user icon\"\n />\n )}\n </div>\n </div>\n </Container>\n );\n};\n"],"mappings":"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"}
@@ -14,14 +14,14 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
14
14
  secondary: "border-secondary bg-secondary text-secondary hover:bg-secondary-300",
15
15
  success: "border-success bg-success text-success hover:bg-success-500",
16
16
  error: "border-error bg-error text-error hover:bg-error-500",
17
- neutral: "border-neutral bg-neutral text-neutral hover:bg-neutral-600",
17
+ neutral: "border-neutral bg-neutral text-muted-foreground hover:bg-neutral-600",
18
18
  light: "border-white bg-white text-white hover:bg-neutral-500",
19
19
  dark: "border-neutral-800 bg-neutral-800 text-neutral-800 hover:bg-neutral-900",
20
- text: "border-text bg-text text-text hover:opacity-80",
20
+ text: "border-text bg-text text-foreground hover:opacity-80",
21
21
  custom: ""
22
22
  },
23
23
  variant: {
24
- default: "rounded-lg text-text-opposite",
24
+ default: "rounded-lg text-foreground-opposite",
25
25
  none: "border-none bg-opacity-0 text-inherit hover:bg-opacity-0",
26
26
  outline: "rounded-lg border-[1.3px] bg-opacity-0 hover:bg-opacity-30",
27
27
  hoverable: "rounded-lg border-none bg-opacity-0 transition hover:bg-opacity-10"
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Badge/index.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport type { HTMLAttributes } from 'react';\n\n/**\n * Badge color variants enum\n * @description Defines the available color themes for the badge component\n */\nexport type BadgeColor =\n | 'primary'\n | 'secondary'\n | 'success'\n | 'error'\n | 'neutral'\n | 'light'\n | 'dark'\n | 'text'\n | 'custom';\n\n/**\n * Badge visual variants enum\n * @description Defines the available visual styles for the badge component\n */\nexport type BadgeVariant = 'default' | 'none' | 'outline' | 'hoverable';\n\n/**\n * Badge size variants enum\n * @description Defines the available sizes for the badge component\n */\nexport type BadgeSize = 'sm' | 'md' | 'lg';\n\n/**\n * Badge component variants using class-variance-authority\n * @description Defines the styling variants for different badge combinations\n */\nexport const badgeVariants = cva(\n 'inline-flex items-center rounded-md border px-2.5 py-0.5 font-semibold text-xs transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2',\n {\n variants: {\n color: {\n primary: 'border-primary bg-primary text-primary hover:bg-primary-500',\n secondary:\n 'border-secondary bg-secondary text-secondary hover:bg-secondary-300',\n success: 'border-success bg-success text-success hover:bg-success-500',\n error: 'border-error bg-error text-error hover:bg-error-500',\n neutral: 'border-neutral bg-neutral text-neutral hover:bg-neutral-600',\n light: 'border-white bg-white text-white hover:bg-neutral-500',\n dark: 'border-neutral-800 bg-neutral-800 text-neutral-800 hover:bg-neutral-900',\n text: 'border-text bg-text text-text hover:opacity-80',\n custom: '',\n },\n variant: {\n default: 'rounded-lg text-text-opposite',\n none: 'border-none bg-opacity-0 text-inherit hover:bg-opacity-0',\n outline: 'rounded-lg border-[1.3px] bg-opacity-0 hover:bg-opacity-30',\n hoverable:\n 'rounded-lg border-none bg-opacity-0 transition hover:bg-opacity-10',\n },\n size: {\n sm: 'px-2 py-0.5 text-xs',\n md: 'px-2.5 py-0.5 text-xs',\n lg: 'px-3 py-1 text-sm',\n },\n },\n defaultVariants: {\n variant: 'default',\n color: 'primary',\n size: 'md',\n },\n }\n);\n\n/**\n * Badge component props interface\n * @description Comprehensive props for the Badge component with accessibility and interactive features\n */\nexport type BadgeProps = HTMLAttributes<HTMLElement> & {\n /** The content to display inside the badge */\n children?: React.ReactNode;\n /** Color theme variant */\n color?: BadgeColor | `${BadgeColor}`;\n /** Visual style variant */\n variant?: BadgeVariant | `${BadgeVariant}`;\n /** Size of the badge */\n size?: BadgeSize | `${BadgeSize}`;\n /** Whether the badge is clickable */\n clickable?: boolean;\n /** Whether the badge is dismissible (shows close button) */\n dismissible?: boolean;\n /** Click handler for the badge */\n onClick?: (event: React.MouseEvent<HTMLElement>) => void;\n /** Click handler for the dismiss button */\n onDismiss?: () => void;\n /** ARIA label for accessibility */\n 'aria-label'?: string;\n /** Badge role for accessibility (default: 'status') */\n role?: 'status' | 'button' | 'generic';\n /** Whether badge should be focusable */\n tabIndex?: number;\n};\n\n/**\n * Utility type for badge variant props\n */\nexport type BadgeVariantProps = VariantProps<typeof badgeVariants>;\n\n/**\n * Badge component for displaying status indicators, labels, and notifications\n *\n * @description A flexible badge component that supports multiple visual styles, colors, and interactive features.\n * It maintains accessibility standards and provides comprehensive customization options.\n *\n * @example\n * ```tsx\n * // Basic badge\n * <Badge>New</Badge>\n *\n * // Colored badge\n * <Badge color=\"error\">Error</Badge>\n *\n * // Clickable badge\n * <Badge clickable onClick={() => console.log('clicked')}>\n * Clickable\n * </Badge>\n *\n * // Dismissible badge\n * <Badge dismissible onDismiss={() => console.log('dismissed')}>\n * Dismissible\n * </Badge>\n * ```\n */\nexport const Badge: React.FC<BadgeProps> = ({\n className,\n variant = 'default',\n color = 'primary',\n size = 'md',\n children,\n clickable = false,\n dismissible = false,\n onClick,\n onDismiss,\n role,\n tabIndex,\n 'aria-label': ariaLabel,\n ...props\n}) => {\n const Component = clickable ? 'button' : 'span';\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {\n if (clickable && onClick && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n onClick(event as any);\n }\n };\n\n const handleDismiss = (event: React.MouseEvent) => {\n event.stopPropagation();\n onDismiss?.();\n };\n\n return (\n <Component\n className={cn(\n badgeVariants({ variant, color, size }),\n clickable &&\n 'cursor-pointer hover:opacity-80 focus-visible:ring-2 focus-visible:ring-offset-2',\n dismissible && 'pr-1',\n className\n )}\n onClick={clickable ? onClick : undefined}\n onKeyDown={clickable ? handleKeyDown : undefined}\n role={role || (clickable ? 'button' : 'status')}\n tabIndex={clickable ? (tabIndex ?? 0) : tabIndex}\n aria-label={ariaLabel || (clickable ? `${children} button` : undefined)}\n {...props}\n >\n {children}\n {dismissible && (\n <button\n type=\"button\"\n className=\"ml-1 inline-flex h-4 w-4 items-center justify-center rounded-full hover:bg-black/10 focus:outline-none focus:ring-1 focus:ring-offset-1\"\n onClick={handleDismiss}\n aria-label={`Remove ${children} badge`}\n >\n <svg\n className=\"size-3\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-label=\"Remove badge\"\n >\n <title>Remove badge</title>\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </Component>\n );\n};\n"],"mappings":";;;;;;;;;AAmCA,MAAa,gBAAgB,IAC3B,0KACA;CACE,UAAU;EACR,OAAO;GACL,SAAS;GACT,WACE;GACF,SAAS;GACT,OAAO;GACP,SAAS;GACT,OAAO;GACP,MAAM;GACN,MAAM;GACN,QAAQ;EACV;EACA,SAAS;GACP,SAAS;GACT,MAAM;GACN,SAAS;GACT,WACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;CACF;CACA,iBAAiB;EACf,SAAS;EACT,OAAO;EACP,MAAM;CACR;AACF,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,MAAa,SAA+B,EAC1C,WACA,UAAU,WACV,QAAQ,WACR,OAAO,MACP,UACA,YAAY,OACZ,cAAc,OACd,SACA,WACA,MACA,UACA,cAAc,WACd,GAAG,YACC;CACJ,MAAM,YAAY,YAAY,WAAW;CAEzC,MAAM,iBAAiB,UAA4C;EACjE,IAAI,aAAa,YAAY,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;GACxE,MAAM,eAAe;GACrB,QAAQ,KAAY;EACtB;CACF;CAEA,MAAM,iBAAiB,UAA4B;EACjD,MAAM,gBAAgB;EACtB,YAAY;CACd;CAEA,OACE,qBAAC,WAAD;EACE,WAAW,GACT,cAAc;GAAE;GAAS;GAAO;EAAK,CAAC,GACtC,aACE,oFACF,eAAe,QACf,SACF;EACA,SAAS,YAAY,UAAU;EAC/B,WAAW,YAAY,gBAAgB;EACvC,MAAM,SAAS,YAAY,WAAW;EACtC,UAAU,YAAa,YAAY,IAAK;EACxC,cAAY,cAAc,YAAY,GAAG,SAAS,WAAW;EAC7D,GAAI;YAbN,CAeG,UACA,eACC,oBAAC,UAAD;GACE,MAAK;GACL,WAAU;GACV,SAAS;GACT,cAAY,UAAU,SAAS;aAE/B,qBAAC,OAAD;IACE,WAAU;IACV,SAAQ;IACR,MAAK;IACL,cAAW;cAJb,CAME,oBAAC,SAAD,YAAO,eAAmB,IAC1B,oBAAC,QAAD;KACE,UAAS;KACT,GAAE;KACF,UAAS;IACV,EACE;;EACC,EAED;;AAEf"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Badge/index.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport type { HTMLAttributes } from 'react';\n\n/**\n * Badge color variants enum\n * @description Defines the available color themes for the badge component\n */\nexport type BadgeColor =\n | 'primary'\n | 'secondary'\n | 'success'\n | 'error'\n | 'neutral'\n | 'light'\n | 'dark'\n | 'text'\n | 'custom';\n\n/**\n * Badge visual variants enum\n * @description Defines the available visual styles for the badge component\n */\nexport type BadgeVariant = 'default' | 'none' | 'outline' | 'hoverable';\n\n/**\n * Badge size variants enum\n * @description Defines the available sizes for the badge component\n */\nexport type BadgeSize = 'sm' | 'md' | 'lg';\n\n/**\n * Badge component variants using class-variance-authority\n * @description Defines the styling variants for different badge combinations\n */\nexport const badgeVariants = cva(\n 'inline-flex items-center rounded-md border px-2.5 py-0.5 font-semibold text-xs transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2',\n {\n variants: {\n color: {\n primary: 'border-primary bg-primary text-primary hover:bg-primary-500',\n secondary:\n 'border-secondary bg-secondary text-secondary hover:bg-secondary-300',\n success: 'border-success bg-success text-success hover:bg-success-500',\n error: 'border-error bg-error text-error hover:bg-error-500',\n neutral:\n 'border-neutral bg-neutral text-muted-foreground hover:bg-neutral-600',\n light: 'border-white bg-white text-white hover:bg-neutral-500',\n dark: 'border-neutral-800 bg-neutral-800 text-neutral-800 hover:bg-neutral-900',\n text: 'border-text bg-text text-foreground hover:opacity-80',\n custom: '',\n },\n variant: {\n default: 'rounded-lg text-foreground-opposite',\n none: 'border-none bg-opacity-0 text-inherit hover:bg-opacity-0',\n outline: 'rounded-lg border-[1.3px] bg-opacity-0 hover:bg-opacity-30',\n hoverable:\n 'rounded-lg border-none bg-opacity-0 transition hover:bg-opacity-10',\n },\n size: {\n sm: 'px-2 py-0.5 text-xs',\n md: 'px-2.5 py-0.5 text-xs',\n lg: 'px-3 py-1 text-sm',\n },\n },\n defaultVariants: {\n variant: 'default',\n color: 'primary',\n size: 'md',\n },\n }\n);\n\n/**\n * Badge component props interface\n * @description Comprehensive props for the Badge component with accessibility and interactive features\n */\nexport type BadgeProps = HTMLAttributes<HTMLElement> & {\n /** The content to display inside the badge */\n children?: React.ReactNode;\n /** Color theme variant */\n color?: BadgeColor | `${BadgeColor}`;\n /** Visual style variant */\n variant?: BadgeVariant | `${BadgeVariant}`;\n /** Size of the badge */\n size?: BadgeSize | `${BadgeSize}`;\n /** Whether the badge is clickable */\n clickable?: boolean;\n /** Whether the badge is dismissible (shows close button) */\n dismissible?: boolean;\n /** Click handler for the badge */\n onClick?: (event: React.MouseEvent<HTMLElement>) => void;\n /** Click handler for the dismiss button */\n onDismiss?: () => void;\n /** ARIA label for accessibility */\n 'aria-label'?: string;\n /** Badge role for accessibility (default: 'status') */\n role?: 'status' | 'button' | 'generic';\n /** Whether badge should be focusable */\n tabIndex?: number;\n};\n\n/**\n * Utility type for badge variant props\n */\nexport type BadgeVariantProps = VariantProps<typeof badgeVariants>;\n\n/**\n * Badge component for displaying status indicators, labels, and notifications\n *\n * @description A flexible badge component that supports multiple visual styles, colors, and interactive features.\n * It maintains accessibility standards and provides comprehensive customization options.\n *\n * @example\n * ```tsx\n * // Basic badge\n * <Badge>New</Badge>\n *\n * // Colored badge\n * <Badge color=\"error\">Error</Badge>\n *\n * // Clickable badge\n * <Badge clickable onClick={() => console.log('clicked')}>\n * Clickable\n * </Badge>\n *\n * // Dismissible badge\n * <Badge dismissible onDismiss={() => console.log('dismissed')}>\n * Dismissible\n * </Badge>\n * ```\n */\nexport const Badge: React.FC<BadgeProps> = ({\n className,\n variant = 'default',\n color = 'primary',\n size = 'md',\n children,\n clickable = false,\n dismissible = false,\n onClick,\n onDismiss,\n role,\n tabIndex,\n 'aria-label': ariaLabel,\n ...props\n}) => {\n const Component = clickable ? 'button' : 'span';\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {\n if (clickable && onClick && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n onClick(event as any);\n }\n };\n\n const handleDismiss = (event: React.MouseEvent) => {\n event.stopPropagation();\n onDismiss?.();\n };\n\n return (\n <Component\n className={cn(\n badgeVariants({ variant, color, size }),\n clickable &&\n 'cursor-pointer hover:opacity-80 focus-visible:ring-2 focus-visible:ring-offset-2',\n dismissible && 'pr-1',\n className\n )}\n onClick={clickable ? onClick : undefined}\n onKeyDown={clickable ? handleKeyDown : undefined}\n role={role || (clickable ? 'button' : 'status')}\n tabIndex={clickable ? (tabIndex ?? 0) : tabIndex}\n aria-label={ariaLabel || (clickable ? `${children} button` : undefined)}\n {...props}\n >\n {children}\n {dismissible && (\n <button\n type=\"button\"\n className=\"ml-1 inline-flex h-4 w-4 items-center justify-center rounded-full hover:bg-black/10 focus:outline-none focus:ring-1 focus:ring-offset-1\"\n onClick={handleDismiss}\n aria-label={`Remove ${children} badge`}\n >\n <svg\n className=\"size-3\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-label=\"Remove badge\"\n >\n <title>Remove badge</title>\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </Component>\n );\n};\n"],"mappings":";;;;;;;;;AAmCA,MAAa,gBAAgB,IAC3B,0KACA;CACE,UAAU;EACR,OAAO;GACL,SAAS;GACT,WACE;GACF,SAAS;GACT,OAAO;GACP,SACE;GACF,OAAO;GACP,MAAM;GACN,MAAM;GACN,QAAQ;EACV;EACA,SAAS;GACP,SAAS;GACT,MAAM;GACN,SAAS;GACT,WACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;CACF;CACA,iBAAiB;EACf,SAAS;EACT,OAAO;EACP,MAAM;CACR;AACF,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,MAAa,SAA+B,EAC1C,WACA,UAAU,WACV,QAAQ,WACR,OAAO,MACP,UACA,YAAY,OACZ,cAAc,OACd,SACA,WACA,MACA,UACA,cAAc,WACd,GAAG,YACC;CACJ,MAAM,YAAY,YAAY,WAAW;CAEzC,MAAM,iBAAiB,UAA4C;EACjE,IAAI,aAAa,YAAY,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;GACxE,MAAM,eAAe;GACrB,QAAQ,KAAY;EACtB;CACF;CAEA,MAAM,iBAAiB,UAA4B;EACjD,MAAM,gBAAgB;EACtB,YAAY;CACd;CAEA,OACE,qBAAC,WAAD;EACE,WAAW,GACT,cAAc;GAAE;GAAS;GAAO;EAAK,CAAC,GACtC,aACE,oFACF,eAAe,QACf,SACF;EACA,SAAS,YAAY,UAAU;EAC/B,WAAW,YAAY,gBAAgB;EACvC,MAAM,SAAS,YAAY,WAAW;EACtC,UAAU,YAAa,YAAY,IAAK;EACxC,cAAY,cAAc,YAAY,GAAG,SAAS,WAAW;EAC7D,GAAI;YAbN,CAeG,UACA,eACC,oBAAC,UAAD;GACE,MAAK;GACL,WAAU;GACV,SAAS;GACT,cAAY,UAAU,SAAS;aAE/B,qBAAC,OAAD;IACE,WAAU;IACV,SAAQ;IACR,MAAK;IACL,cAAW;cAJb,CAME,oBAAC,SAAD,YAAO,eAAmB,IAC1B,oBAAC,QAAD;KACE,UAAS;KACT,GAAE;KACF,UAAS;IACV,EACE;;EACC,EAED;;AAEf"}
@@ -22,8 +22,8 @@ const getColorClass = (color) => {
22
22
  neutral: "text-neutral",
23
23
  light: "text-white",
24
24
  dark: "text-neutral-800",
25
- text: "text-text",
26
- "text-inverse": "text-text-opposite",
25
+ text: "text-muted-foreground",
26
+ "text-inverse": "text-foreground-opposite",
27
27
  error: "text-error",
28
28
  success: "text-success",
29
29
  custom: ""
@@ -106,7 +106,7 @@ const ButtonLink = ({ children: text, onClick, color, position, className, ...pr
106
106
  */
107
107
  const Span = ({ children, position, className, ...props }) => /* @__PURE__ */ jsxs("span", {
108
108
  itemProp: "item",
109
- className: cn("inline-flex items-center", "font-medium text-neutral-700", "transition-colors duration-200", className),
109
+ className: cn("inline-flex items-center", "font-medium text-muted-foreground", "transition-colors duration-200", className),
110
110
  children: [/* @__PURE__ */ jsx("span", {
111
111
  itemProp: "name",
112
112
  ...props,
@@ -187,7 +187,7 @@ const Breadcrumb = ({ links, className, color = "text", locale, elementType = "p
187
187
  let section = /* @__PURE__ */ jsx(Span, {
188
188
  position: index + 1,
189
189
  "aria-current": ariaCurrent,
190
- className: cn("transition-colors duration-200", isActive && "text-neutral-900"),
190
+ className: cn("transition-colors duration-200", isActive && "text-foreground"),
191
191
  children: text
192
192
  }, text);
193
193
  if (isLink) section = /* @__PURE__ */ jsx(LinkLink, {
@@ -196,7 +196,7 @@ const Breadcrumb = ({ links, className, color = "text", locale, elementType = "p
196
196
  position: index + 1,
197
197
  locale,
198
198
  "aria-current": ariaCurrent,
199
- className: cn(isActive && "cursor-default text-neutral-900"),
199
+ className: cn(isActive && "cursor-default text-foreground"),
200
200
  children: text
201
201
  }, text);
202
202
  else if (isButton) section = /* @__PURE__ */ jsx(ButtonLink, {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Breadcrumb/index.tsx"],"sourcesContent":["'use client';\n\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { getIntlayer } from 'intlayer';\nimport { ChevronRightIcon } from 'lucide-react';\nimport { type FC, Fragment, type HTMLAttributes, type ReactNode } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Button, type ButtonProps } from '../Button';\nimport { Link, type LinkColor } from '../Link';\n\n/**\n * Props for LinkLink sub-component that renders breadcrumb items as links\n */\ntype LinkLinkProps = {\n /**\n * Position of the breadcrumb item in the list (1-based index)\n */\n position: number;\n /**\n * Locale for internationalization\n */\n locale?: LocalesValues;\n /**\n * URL to navigate to\n */\n href?: string;\n /**\n * Link color\n */\n color?: LinkColor | `${LinkColor}`;\n /**\n * Click handler\n */\n onClick?: () => void;\n /**\n * Children content\n */\n children?: string;\n /**\n * Additional CSS classes\n */\n className?: string;\n} & Omit<\n HTMLAttributes<HTMLAnchorElement>,\n 'href' | 'onClick' | 'color' | 'children' | 'className'\n>;\n\n/**\n * Maps LinkColor to corresponding Tailwind text color classes\n */\nconst getColorClass = (color?: LinkColor | `${LinkColor}`): string => {\n if (!color) return '';\n\n const colorMap: Record<LinkColor, string> = {\n primary: 'text-primary',\n secondary: 'text-secondary',\n neutral: 'text-neutral',\n light: 'text-white',\n dark: 'text-neutral-800',\n text: 'text-text',\n 'text-inverse': 'text-text-opposite',\n error: 'text-error',\n success: 'text-success',\n custom: '',\n };\n\n return colorMap[color as LinkColor] || '';\n};\n\n/**\n * Breadcrumb variant styles using class-variance-authority\n */\nconst breadcrumbVariants = cva('flex flex-row flex-wrap items-center', {\n variants: {\n size: {\n xs: 'gap-1 text-xs',\n sm: 'gap-1.5 text-sm',\n md: 'gap-2 text-base',\n lg: 'gap-3 text-lg',\n xl: 'gap-4 text-xl',\n },\n spacing: {\n compact: 'gap-1',\n normal: 'gap-2',\n loose: 'gap-4',\n },\n },\n defaultVariants: {\n size: 'md',\n spacing: 'normal',\n },\n});\n\n/**\n * LinkLink sub-component for breadcrumb items that navigate to other pages\n */\nconst LinkLink: FC<LinkLinkProps> = ({\n href,\n lang,\n children,\n onClick,\n color,\n position,\n locale,\n className,\n ...props\n}) => {\n const content = getIntlayer('breadcrumb');\n const linkLabel = content.linkLabel;\n\n return (\n <>\n <Link\n href={href}\n locale={locale}\n color={color}\n onClick={onClick}\n itemProp=\"item\"\n isExternalLink={false}\n itemScope\n itemType=\"https://schema.org/WebPage\"\n {...props}\n label={`${linkLabel} ${children}`}\n itemID={href}\n size=\"custom\"\n >\n <span itemProp=\"name\">{children}</span>\n </Link>\n <meta itemProp=\"position\" content={position.toString()} />\n </>\n );\n};\n\n/**\n * Props for ButtonLink sub-component that renders breadcrumb items as interactive buttons\n */\ntype ButtonButtonProps = {\n /**\n * Text content for the breadcrumb button\n */\n children: string;\n /**\n * Position of the breadcrumb item in the list (1-based index)\n */\n position: number;\n} & Omit<ButtonProps, 'children' | 'label'>;\n\n/**\n * ButtonLink sub-component for breadcrumb items with click handlers\n */\nconst ButtonLink: FC<ButtonButtonProps> = ({\n children: text,\n onClick,\n color,\n position,\n className,\n ...props\n}) => {\n const { linkLabel } = useIntlayer('breadcrumb');\n\n return (\n <>\n <Button\n onClick={onClick}\n variant=\"link\"\n label={`${linkLabel} ${text}`}\n color={color}\n itemProp=\"item\"\n size=\"custom\"\n {...props}\n >\n <span itemProp=\"name\">{text}</span>\n </Button>\n <meta itemProp=\"position\" content={position.toString()} />\n </>\n );\n};\n\n/**\n * Props for Span sub-component that renders static breadcrumb text\n */\ntype SpanProps = {\n /**\n * Text content for the static breadcrumb item\n */\n children: string;\n /**\n * Position of the breadcrumb item in the list (1-based index)\n */\n position: number;\n} & HTMLAttributes<HTMLSpanElement>;\n\n/**\n * Span sub-component for static breadcrumb text items\n */\nconst Span: FC<SpanProps> = ({ children, position, className, ...props }) => (\n <span\n itemProp=\"item\"\n className={cn(\n 'inline-flex items-center',\n 'font-medium text-neutral-700',\n 'transition-colors duration-200',\n className\n )}\n >\n <span itemProp=\"name\" {...props}>\n {children}\n </span>\n <meta itemProp=\"position\" content={position.toString()} />\n </span>\n);\n\n/**\n * Detailed breadcrumb link configuration with optional href or onClick\n */\ntype DetailedBreadcrumbLink = {\n /**\n * URL to navigate to when the breadcrumb item is clicked\n */\n href?: string;\n /**\n * Text content to display for this breadcrumb item\n */\n text: string;\n /**\n * Custom click handler function for interactive breadcrumb items\n */\n onClick?: () => void;\n};\n\n/**\n * Union type representing different breadcrumb item configurations:\n * - string: Simple text breadcrumb item\n * - DetailedBreadcrumbLink: Object with href, text, and/or onClick properties\n */\nexport type BreadcrumbLink = string | DetailedBreadcrumbLink;\n\nexport type BreadcrumbProps = {\n /**\n * Array of breadcrumb items\n */\n links: BreadcrumbLink[];\n /**\n * Color scheme for breadcrumb links\n * @default \"text\"\n */\n color?: LinkColor | `${LinkColor}`;\n /**\n * Locale for internationalization\n */\n locale?: LocalesValues;\n /**\n * Element type for ARIA current attribute\n * @default 'page'\n */\n elementType?: 'page' | 'location';\n /**\n * Custom separator between breadcrumb items\n * @default ChevronRightIcon\n */\n separator?: ReactNode;\n /**\n * ARIA label for breadcrumb navigation\n * @default 'breadcrumb'\n */\n ariaLabel?: string;\n /**\n * Whether to include structured data markup\n * @default true\n */\n includeStructuredData?: boolean;\n /**\n * Maximum number of breadcrumb items to show before truncation\n */\n maxItems?: number;\n} & VariantProps<typeof breadcrumbVariants> &\n HTMLAttributes<HTMLOListElement>;\n\n/**\n * Breadcrumb component providing navigational context with accessibility features\n *\n * Features:\n * - Supports links, buttons, and static text elements\n * - Full keyboard navigation support\n * - ARIA attributes for screen readers\n * - Schema.org structured data for SEO\n * - Customizable separators and styling\n * - Internationalization support\n * - Responsive design variants\n *\n * @example\n * ```tsx\n * <Breadcrumb\n * links={[\n * 'Home',\n * { href: '/products', text: 'Products' },\n * { onClick: handleCategory, text: 'Electronics' },\n * 'Smartphones'\n * ]}\n * size=\"md\"\n * ariaLabel=\"Product navigation\"\n * />\n * ```\n */\nexport const Breadcrumb: FC<BreadcrumbProps> = ({\n links,\n className,\n color = 'text',\n locale,\n elementType = 'page',\n separator = <ChevronRightIcon size={10} />,\n ariaLabel = 'breadcrumb',\n includeStructuredData = true,\n maxItems,\n size,\n spacing,\n ...props\n}) => {\n const displayLinks =\n maxItems && links.length > maxItems\n ? [...links.slice(0, 1), '...', ...links.slice(-(maxItems - 2))]\n : links;\n\n return (\n <nav aria-label={ariaLabel}>\n <ol\n className={cn(breadcrumbVariants({ size, spacing }), className)}\n {...(includeStructuredData && {\n itemScope: true,\n itemType: 'http://schema.org/BreadcrumbList',\n })}\n {...props}\n >\n {displayLinks.map((link, index) => {\n const isLastLink = index === displayLinks.length - 1;\n const isLink =\n typeof link === 'object' && typeof link.href === 'string';\n const isButton =\n typeof link === 'object' && typeof link.onClick === 'function';\n const isActive = index === displayLinks.length - 1;\n const ariaCurrent = isActive ? elementType : undefined;\n const isTruncated = link === '...';\n\n const text = (link as DetailedBreadcrumbLink).text ?? link;\n\n const separatorColorClass = getColorClass(color);\n\n if (isTruncated) {\n return (\n <Fragment key={`truncated-${text}`}>\n <li className=\"flex items-center\" aria-hidden=\"true\">\n <span className=\"text-neutral-500\">…</span>\n </li>\n {!isLastLink && (\n <li aria-hidden=\"true\" className=\"flex items-center\">\n <span className={cn(separatorColorClass)}>{separator}</span>\n </li>\n )}\n </Fragment>\n );\n }\n\n let section = (\n <Span\n key={text}\n position={index + 1}\n aria-current={ariaCurrent}\n className={cn(\n 'transition-colors duration-200',\n isActive && 'text-neutral-900'\n )}\n >\n {text}\n </Span>\n );\n\n if (isLink) {\n section = (\n <LinkLink\n key={text}\n href={link.href!}\n color={color}\n position={index + 1}\n locale={locale}\n aria-current={ariaCurrent}\n className={cn(isActive && 'cursor-default text-neutral-900')}\n >\n {text}\n </LinkLink>\n );\n } else if (isButton) {\n section = (\n <ButtonLink\n key={text}\n onClick={link.onClick!}\n color={color}\n position={index + 1}\n aria-current={ariaCurrent}\n className={cn(isActive && 'cursor-default text-neutral-900')}\n >\n {text}\n </ButtonLink>\n );\n }\n\n const listElement = (\n <li\n {...(includeStructuredData && {\n itemProp: 'itemListElement',\n itemScope: true,\n itemType: 'https://schema.org/ListItem',\n })}\n key={text}\n className=\"flex items-center\"\n >\n {section}\n </li>\n );\n\n if (isLastLink) {\n return listElement;\n }\n\n return (\n <Fragment key={text}>\n {listElement}\n <li aria-hidden=\"true\" className=\"flex items-center\">\n <span className={cn(separatorColorClass)}>{separator}</span>\n </li>\n </Fragment>\n );\n })}\n </ol>\n </nav>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Breadcrumb/index.tsx"],"sourcesContent":["'use client';\n\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { getIntlayer } from 'intlayer';\nimport { ChevronRightIcon } from 'lucide-react';\nimport { type FC, Fragment, type HTMLAttributes, type ReactNode } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Button, type ButtonProps } from '../Button';\nimport { Link, type LinkColor } from '../Link';\n\n/**\n * Props for LinkLink sub-component that renders breadcrumb items as links\n */\ntype LinkLinkProps = {\n /**\n * Position of the breadcrumb item in the list (1-based index)\n */\n position: number;\n /**\n * Locale for internationalization\n */\n locale?: LocalesValues;\n /**\n * URL to navigate to\n */\n href?: string;\n /**\n * Link color\n */\n color?: LinkColor | `${LinkColor}`;\n /**\n * Click handler\n */\n onClick?: () => void;\n /**\n * Children content\n */\n children?: string;\n /**\n * Additional CSS classes\n */\n className?: string;\n} & Omit<\n HTMLAttributes<HTMLAnchorElement>,\n 'href' | 'onClick' | 'color' | 'children' | 'className'\n>;\n\n/**\n * Maps LinkColor to corresponding Tailwind text color classes\n */\nconst getColorClass = (color?: LinkColor | `${LinkColor}`): string => {\n if (!color) return '';\n\n const colorMap: Record<LinkColor, string> = {\n primary: 'text-primary',\n secondary: 'text-secondary',\n neutral: 'text-neutral',\n light: 'text-white',\n dark: 'text-neutral-800',\n text: 'text-muted-foreground',\n 'text-inverse': 'text-foreground-opposite',\n error: 'text-error',\n success: 'text-success',\n custom: '',\n };\n\n return colorMap[color as LinkColor] || '';\n};\n\n/**\n * Breadcrumb variant styles using class-variance-authority\n */\nconst breadcrumbVariants = cva('flex flex-row flex-wrap items-center', {\n variants: {\n size: {\n xs: 'gap-1 text-xs',\n sm: 'gap-1.5 text-sm',\n md: 'gap-2 text-base',\n lg: 'gap-3 text-lg',\n xl: 'gap-4 text-xl',\n },\n spacing: {\n compact: 'gap-1',\n normal: 'gap-2',\n loose: 'gap-4',\n },\n },\n defaultVariants: {\n size: 'md',\n spacing: 'normal',\n },\n});\n\n/**\n * LinkLink sub-component for breadcrumb items that navigate to other pages\n */\nconst LinkLink: FC<LinkLinkProps> = ({\n href,\n lang,\n children,\n onClick,\n color,\n position,\n locale,\n className,\n ...props\n}) => {\n const content = getIntlayer('breadcrumb');\n const linkLabel = content.linkLabel;\n\n return (\n <>\n <Link\n href={href}\n locale={locale}\n color={color}\n onClick={onClick}\n itemProp=\"item\"\n isExternalLink={false}\n itemScope\n itemType=\"https://schema.org/WebPage\"\n {...props}\n label={`${linkLabel} ${children}`}\n itemID={href}\n size=\"custom\"\n >\n <span itemProp=\"name\">{children}</span>\n </Link>\n <meta itemProp=\"position\" content={position.toString()} />\n </>\n );\n};\n\n/**\n * Props for ButtonLink sub-component that renders breadcrumb items as interactive buttons\n */\ntype ButtonButtonProps = {\n /**\n * Text content for the breadcrumb button\n */\n children: string;\n /**\n * Position of the breadcrumb item in the list (1-based index)\n */\n position: number;\n} & Omit<ButtonProps, 'children' | 'label'>;\n\n/**\n * ButtonLink sub-component for breadcrumb items with click handlers\n */\nconst ButtonLink: FC<ButtonButtonProps> = ({\n children: text,\n onClick,\n color,\n position,\n className,\n ...props\n}) => {\n const { linkLabel } = useIntlayer('breadcrumb');\n\n return (\n <>\n <Button\n onClick={onClick}\n variant=\"link\"\n label={`${linkLabel} ${text}`}\n color={color}\n itemProp=\"item\"\n size=\"custom\"\n {...props}\n >\n <span itemProp=\"name\">{text}</span>\n </Button>\n <meta itemProp=\"position\" content={position.toString()} />\n </>\n );\n};\n\n/**\n * Props for Span sub-component that renders static breadcrumb text\n */\ntype SpanProps = {\n /**\n * Text content for the static breadcrumb item\n */\n children: string;\n /**\n * Position of the breadcrumb item in the list (1-based index)\n */\n position: number;\n} & HTMLAttributes<HTMLSpanElement>;\n\n/**\n * Span sub-component for static breadcrumb text items\n */\nconst Span: FC<SpanProps> = ({ children, position, className, ...props }) => (\n <span\n itemProp=\"item\"\n className={cn(\n 'inline-flex items-center',\n 'font-medium text-muted-foreground',\n 'transition-colors duration-200',\n className\n )}\n >\n <span itemProp=\"name\" {...props}>\n {children}\n </span>\n <meta itemProp=\"position\" content={position.toString()} />\n </span>\n);\n\n/**\n * Detailed breadcrumb link configuration with optional href or onClick\n */\ntype DetailedBreadcrumbLink = {\n /**\n * URL to navigate to when the breadcrumb item is clicked\n */\n href?: string;\n /**\n * Text content to display for this breadcrumb item\n */\n text: string;\n /**\n * Custom click handler function for interactive breadcrumb items\n */\n onClick?: () => void;\n};\n\n/**\n * Union type representing different breadcrumb item configurations:\n * - string: Simple text breadcrumb item\n * - DetailedBreadcrumbLink: Object with href, text, and/or onClick properties\n */\nexport type BreadcrumbLink = string | DetailedBreadcrumbLink;\n\nexport type BreadcrumbProps = {\n /**\n * Array of breadcrumb items\n */\n links: BreadcrumbLink[];\n /**\n * Color scheme for breadcrumb links\n * @default \"text\"\n */\n color?: LinkColor | `${LinkColor}`;\n /**\n * Locale for internationalization\n */\n locale?: LocalesValues;\n /**\n * Element type for ARIA current attribute\n * @default 'page'\n */\n elementType?: 'page' | 'location';\n /**\n * Custom separator between breadcrumb items\n * @default ChevronRightIcon\n */\n separator?: ReactNode;\n /**\n * ARIA label for breadcrumb navigation\n * @default 'breadcrumb'\n */\n ariaLabel?: string;\n /**\n * Whether to include structured data markup\n * @default true\n */\n includeStructuredData?: boolean;\n /**\n * Maximum number of breadcrumb items to show before truncation\n */\n maxItems?: number;\n} & VariantProps<typeof breadcrumbVariants> &\n HTMLAttributes<HTMLOListElement>;\n\n/**\n * Breadcrumb component providing navigational context with accessibility features\n *\n * Features:\n * - Supports links, buttons, and static text elements\n * - Full keyboard navigation support\n * - ARIA attributes for screen readers\n * - Schema.org structured data for SEO\n * - Customizable separators and styling\n * - Internationalization support\n * - Responsive design variants\n *\n * @example\n * ```tsx\n * <Breadcrumb\n * links={[\n * 'Home',\n * { href: '/products', text: 'Products' },\n * { onClick: handleCategory, text: 'Electronics' },\n * 'Smartphones'\n * ]}\n * size=\"md\"\n * ariaLabel=\"Product navigation\"\n * />\n * ```\n */\nexport const Breadcrumb: FC<BreadcrumbProps> = ({\n links,\n className,\n color = 'text',\n locale,\n elementType = 'page',\n separator = <ChevronRightIcon size={10} />,\n ariaLabel = 'breadcrumb',\n includeStructuredData = true,\n maxItems,\n size,\n spacing,\n ...props\n}) => {\n const displayLinks =\n maxItems && links.length > maxItems\n ? [...links.slice(0, 1), '...', ...links.slice(-(maxItems - 2))]\n : links;\n\n return (\n <nav aria-label={ariaLabel}>\n <ol\n className={cn(breadcrumbVariants({ size, spacing }), className)}\n {...(includeStructuredData && {\n itemScope: true,\n itemType: 'http://schema.org/BreadcrumbList',\n })}\n {...props}\n >\n {displayLinks.map((link, index) => {\n const isLastLink = index === displayLinks.length - 1;\n const isLink =\n typeof link === 'object' && typeof link.href === 'string';\n const isButton =\n typeof link === 'object' && typeof link.onClick === 'function';\n const isActive = index === displayLinks.length - 1;\n const ariaCurrent = isActive ? elementType : undefined;\n const isTruncated = link === '...';\n\n const text = (link as DetailedBreadcrumbLink).text ?? link;\n\n const separatorColorClass = getColorClass(color);\n\n if (isTruncated) {\n return (\n <Fragment key={`truncated-${text}`}>\n <li className=\"flex items-center\" aria-hidden=\"true\">\n <span className=\"text-neutral-500\">…</span>\n </li>\n {!isLastLink && (\n <li aria-hidden=\"true\" className=\"flex items-center\">\n <span className={cn(separatorColorClass)}>{separator}</span>\n </li>\n )}\n </Fragment>\n );\n }\n\n let section = (\n <Span\n key={text}\n position={index + 1}\n aria-current={ariaCurrent}\n className={cn(\n 'transition-colors duration-200',\n isActive && 'text-foreground'\n )}\n >\n {text}\n </Span>\n );\n\n if (isLink) {\n section = (\n <LinkLink\n key={text}\n href={link.href!}\n color={color}\n position={index + 1}\n locale={locale}\n aria-current={ariaCurrent}\n className={cn(isActive && 'cursor-default text-foreground')}\n >\n {text}\n </LinkLink>\n );\n } else if (isButton) {\n section = (\n <ButtonLink\n key={text}\n onClick={link.onClick!}\n color={color}\n position={index + 1}\n aria-current={ariaCurrent}\n className={cn(isActive && 'cursor-default text-neutral-900')}\n >\n {text}\n </ButtonLink>\n );\n }\n\n const listElement = (\n <li\n {...(includeStructuredData && {\n itemProp: 'itemListElement',\n itemScope: true,\n itemType: 'https://schema.org/ListItem',\n })}\n key={text}\n className=\"flex items-center\"\n >\n {section}\n </li>\n );\n\n if (isLastLink) {\n return listElement;\n }\n\n return (\n <Fragment key={text}>\n {listElement}\n <li aria-hidden=\"true\" className=\"flex items-center\">\n <span className={cn(separatorColorClass)}>{separator}</span>\n </li>\n </Fragment>\n );\n })}\n </ol>\n </nav>\n );\n};\n"],"mappings":"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"}
@@ -210,7 +210,7 @@ const Browser = ({ initialUrl = "https://example.com", path, className, style, s
210
210
  style,
211
211
  "aria-label": ariaLabel ?? content.ariaLabel.value,
212
212
  children: [/* @__PURE__ */ jsxs("div", {
213
- className: "relative z-10 flex shrink-0 items-center gap-3 rounded-t-xl bg-text/15 px-4 py-2",
213
+ className: "relative z-10 flex shrink-0 items-center gap-3 rounded-t-xl border-b bg-card px-4 py-2",
214
214
  children: [
215
215
  /* @__PURE__ */ jsxs("div", {
216
216
  className: "flex items-center gap-1",
@@ -235,7 +235,7 @@ const Browser = ({ initialUrl = "https://example.com", path, className, style, s
235
235
  /* @__PURE__ */ jsxs("form", {
236
236
  onSubmit: handleSubmit,
237
237
  noValidate: true,
238
- className: cn(inputVariants(), "flex w-full gap-2 rounded-xl p-0.5! supports-[corner-shape:squircle]:rounded-2xl", "bg-neutral/10 text-text/50 placeholder:text-neutral/80"),
238
+ className: cn(inputVariants(), "flex w-full gap-2 rounded-xl p-0.5! supports-[corner-shape:squircle]:rounded-2xl", "bg-neutral/10 text-foreground/50 placeholder:text-neutral/80"),
239
239
  children: [
240
240
  /* @__PURE__ */ jsx("label", {
241
241
  htmlFor: "browser-url",
@@ -319,10 +319,10 @@ const Browser = ({ initialUrl = "https://example.com", path, className, style, s
319
319
  className: "max-h-64 divide-y divide-dotted divide-neutral/30 overflow-y-auto p-1 text-center",
320
320
  "aria-label": content.sitemapButtonLabel.value,
321
321
  children: sitemapLoading ? /* @__PURE__ */ jsx("li", {
322
- className: "px-3 py-4 text-center text-neutral text-xs",
322
+ className: "px-3 py-4 text-center text-muted-foreground text-xs",
323
323
  children: content.sitemapLoading
324
324
  }) : sitemapError || !sitemapLoading && filteredSitemapUrls.length === 0 ? /* @__PURE__ */ jsx("li", {
325
- className: "px-3 py-4 text-center text-neutral text-xs",
325
+ className: "px-3 py-4 text-center text-muted-foreground text-xs",
326
326
  children: sitemapError ? content.sitemapError : content.sitemapEmpty
327
327
  }) : filteredSitemapUrls.map((url) => /* @__PURE__ */ jsx("li", {
328
328
  className: "py-0.5",
@@ -1 +1 @@
1
- {"version":3,"file":"Browser.mjs","names":[],"sources":["../../../../src/components/Browser/Browser.tsx"],"sourcesContent":["'use client';\n\nimport { Container } from '@components/Container';\nimport { cn } from '@utils/cn';\nimport { ArrowLeft, ArrowRight, RotateCw, ScanSearch } from 'lucide-react';\nimport {\n type CSSProperties,\n type HTMLAttributes,\n type RefObject,\n type SubmitEvent,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Button } from '../Button';\nimport { ClickOutsideDiv } from '../ClickOutsideDiv';\nimport { DropDown } from '../DropDown';\nimport { Input, inputVariants } from '../Input';\n\nexport type BrowserProps = {\n initialUrl?: string;\n path?: string;\n className?: string;\n style?: CSSProperties;\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n 'aria-label'?: string;\n /**\n * Value of the iframe `sandbox` attribute. Pass `null` to omit the attribute\n * entirely — required for same-origin embeds, where `allow-scripts` plus\n * `allow-same-origin` sandboxes nothing (the frame can reach its parent and\n * strip the attribute) and browsers warn about the combination.\n */\n sandbox?: string | null;\n ref?: RefObject<HTMLIFrameElement | null>;\n domainRestriction?: string;\n} & HTMLAttributes<HTMLIFrameElement>;\n\nconst UrlPath = ({ url }: { url: string }) => {\n const parts = getUrlPath(url).split('/').filter(Boolean);\n\n if (parts.length === 0) return <span>/</span>;\n\n return parts.flatMap((part, index, array) => [\n <span key={`part-${index}`}>{part}</span>,\n index < array.length - 1 && (\n <span key={`sep-${index}`} className=\"mx-2 text-neutral\">\n /\n </span>\n ),\n ]);\n};\n\nconst getUrlPath = (url: string) => {\n try {\n const { pathname, search, hash } = new URL(url);\n\n return `${pathname}${search}${hash}` || '/';\n } catch {\n return url;\n }\n};\n\nexport const Browser = ({\n initialUrl = 'https://example.com',\n path,\n className,\n style,\n size = 'md',\n 'aria-label': ariaLabel,\n sandbox = 'allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads',\n ref,\n domainRestriction,\n ...props\n}: BrowserProps) => {\n // --- State -----------------------------------------------------------------\n const [inputUrl, setInputUrl] = useState(initialUrl);\n const [currentUrl, setCurrentUrl] = useState(initialUrl);\n\n // History Management\n const [history, setHistory] = useState<string[]>([initialUrl]);\n const [currentIndex, setCurrentIndex] = useState(0);\n\n const [error, setError] = useState<string | null>(null);\n const [submitted, setSubmitted] = useState(false);\n const internalIframeRef = useRef<HTMLIFrameElement>(null);\n\n // Sitemap explorer state\n const [sitemapOpen, setSitemapOpen] = useState(false);\n const [sitemapUrls, setSitemapUrls] = useState<string[]>([]);\n const [sitemapSearch, setSitemapSearch] = useState('');\n const [sitemapLoading, setSitemapLoading] = useState(false);\n const [sitemapFetched, setSitemapFetched] = useState(false);\n const [sitemapError, setSitemapError] = useState(false);\n const sitemapInputRef = useRef<HTMLInputElement>(null);\n\n useImperativeHandle(ref, () => internalIframeRef.current!, []);\n const content = useIntlayer('browser');\n\n // --- Effects ---------------------------------------------------------------\n\n // Reset everything if initialUrl changes completely\n useEffect(() => {\n setInputUrl(initialUrl);\n setCurrentUrl(initialUrl);\n setHistory([initialUrl]);\n setCurrentIndex(0);\n setError(null);\n setSubmitted(false);\n setSitemapOpen(false);\n setSitemapUrls([]);\n setSitemapSearch('');\n setSitemapFetched(false);\n setSitemapError(false);\n }, [initialUrl]);\n\n // Sync external path changes with the URL bar and History\n useEffect(() => {\n if (!path) return;\n\n try {\n const baseOrigin = domainRestriction ?? initialUrl;\n const origin = new URL(baseOrigin).origin;\n const fullUrl = `${origin}${path}`;\n\n // Update Input (Always update the visual bar)\n setInputUrl(fullUrl);\n\n // Check internal iframe state to avoid reload if already there\n let isAlreadyAtUrl = false;\n if (internalIframeRef.current?.contentWindow) {\n try {\n const currentIframeHref =\n internalIframeRef.current.contentWindow.location.href;\n if (new URL(currentIframeHref).href === new URL(fullUrl).href) {\n isAlreadyAtUrl = true;\n }\n } catch {\n // Cross-origin access ignored\n }\n }\n\n // Update History Stack regardless of isAlreadyAtUrl so arrow navigation is always correct\n if (history[currentIndex] !== fullUrl) {\n setHistory((prev) => {\n const newHistory = prev.slice(0, currentIndex + 1);\n newHistory.push(fullUrl);\n return newHistory;\n });\n setCurrentIndex((prev) => prev + 1);\n }\n\n // Navigate only if NOT already there to avoid refreshing on internal iframe navigation\n if (!isAlreadyAtUrl) {\n setCurrentUrl(fullUrl);\n }\n\n setError(null);\n } catch {\n // Ignore invalid paths\n }\n }, [path, domainRestriction, initialUrl]); // Removed currentIndex dependency to prevent loops\n\n // Imperatively keep the iframe src in sync with currentUrl.\n // React's attribute reconciliation can be unreliable for cross-origin iframes after\n // internal navigation, so this effect is the source of truth for navigation.\n useEffect(() => {\n const iframe = internalIframeRef.current;\n if (!iframe) return;\n if (iframe.src !== currentUrl) {\n iframe.src = currentUrl;\n }\n }, [currentUrl]);\n\n // --- Navigation Logic ------------------------------------------------------\n\n const handleNavigateTo = (url: string) => {\n try {\n const validated = normalizeUrl(url);\n\n // If we are navigating to the exact same URL, just reload\n if (validated === currentUrl) {\n handleReload();\n return;\n }\n\n setCurrentUrl(validated);\n setInputUrl(validated);\n setError(null);\n\n // Update History: Slice future if we went back, then push new\n const newHistory = history.slice(0, currentIndex + 1);\n newHistory.push(validated);\n setHistory(newHistory);\n setCurrentIndex(newHistory.length - 1);\n } catch (e) {\n if (\n e instanceof Error &&\n e.message === 'URL does not match allowed domain' &&\n domainRestriction\n ) {\n setError(\n content.domainRestrictionError?.value ??\n `Only URLs from ${domainRestriction} are allowed.`\n );\n } else {\n setError(content.errorMessage.value);\n }\n }\n };\n\n const handleBack = () => {\n if (currentIndex > 0) {\n const newIndex = currentIndex - 1;\n const prevUrl = history[newIndex];\n setCurrentIndex(newIndex);\n setCurrentUrl(prevUrl);\n setInputUrl(prevUrl);\n setError(null);\n }\n };\n\n const handleForward = () => {\n if (currentIndex < history.length - 1) {\n const newIndex = currentIndex + 1;\n const nextUrl = history[newIndex];\n setCurrentIndex(newIndex);\n setCurrentUrl(nextUrl);\n setInputUrl(nextUrl);\n setError(null);\n }\n };\n\n const handleSubmit = (e: SubmitEvent<HTMLFormElement>) => {\n e.preventDefault();\n setSubmitted(true);\n handleNavigateTo(inputUrl);\n };\n\n const handleReload = () => {\n const iframe = internalIframeRef.current;\n if (!iframe) return;\n const src = iframe.src;\n iframe.src = '';\n setTimeout(() => {\n if (internalIframeRef.current) internalIframeRef.current.src = src;\n }, 50);\n };\n\n // --- Validation Helpers ----------------------------------------------------\n const isValidHostname = (host: string) => {\n if (host === 'localhost') return true;\n if (/^(\\d{1,3}\\.){3}\\d{1,3}$/.test(host)) return true;\n if (/^[a-f0-9:]+$/i.test(host)) return true;\n if (!/^[a-z0-9.-]+$/i.test(host)) return false;\n if (/^[-.]/.test(host) || /[-.]$/.test(host)) return false;\n if (host.includes('..')) return false;\n if (!host.includes('.')) return false;\n return true;\n };\n\n const getRestrictionOrigin = (): URL | null => {\n if (!domainRestriction) return null;\n try {\n return new URL(domainRestriction);\n } catch {\n return null;\n }\n };\n\n const normalizeUrl = (raw: string) => {\n const trimmed = raw.trim();\n if (!trimmed || /\\s/.test(trimmed)) throw new Error('Invalid');\n\n const restrictionOrigin = getRestrictionOrigin();\n const isRelativePath = trimmed.startsWith('/') && !trimmed.startsWith('//');\n\n if (isRelativePath) {\n if (restrictionOrigin) {\n return new URL(`${restrictionOrigin.origin}${trimmed}`).toString();\n }\n return new URL(`${new URL(currentUrl).origin}${trimmed}`).toString();\n }\n\n const hasProtocol = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed);\n const candidate = hasProtocol ? trimmed : `https://${trimmed}`;\n const url = new URL(candidate);\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new Error('Only http(s) is allowed');\n }\n\n if (!isValidHostname(url.hostname)) throw new Error('Invalid host');\n\n if (restrictionOrigin) {\n const urlMatches =\n url.hostname === restrictionOrigin.hostname &&\n url.protocol === restrictionOrigin.protocol &&\n (restrictionOrigin.port === '' ||\n url.port === restrictionOrigin.port ||\n url.host === restrictionOrigin.host);\n\n if (!urlMatches) throw new Error('URL does not match allowed domain');\n }\n\n return url.toString();\n };\n\n const handleSitemapToggle = async () => {\n const nextOpen = !sitemapOpen;\n setSitemapOpen(nextOpen);\n\n if (nextOpen && !sitemapFetched) {\n setSitemapLoading(true);\n setSitemapError(false);\n setSitemapFetched(true);\n try {\n const { extractUrlFromSitemap } = await import(\n './extractUrlFromSitemap'\n );\n const urls = await extractUrlFromSitemap(currentUrl);\n setSitemapUrls(urls);\n if (urls.length === 0) setSitemapError(false);\n } catch {\n setSitemapError(true);\n } finally {\n setSitemapLoading(false);\n }\n setTimeout(() => sitemapInputRef.current?.focus(), 50);\n }\n };\n\n const filteredSitemapUrls = useMemo(() => {\n const query = sitemapSearch.trim().toLowerCase();\n if (!query) return sitemapUrls;\n return sitemapUrls.filter((url) => url.toLowerCase().includes(query));\n }, [sitemapUrls, sitemapSearch]);\n\n const showError = submitted && !!error;\n const canGoBack = currentIndex > 0;\n const canGoForward = currentIndex < history.length - 1;\n\n return (\n <section\n className={cn(\n 'flex w-full flex-col overflow-hidden rounded-xl bg-background shadow-[0_4px_12px_rgba(0,0,0,0.4),0_0_1px_rgba(0,0,0,0.2)]',\n className\n )}\n style={style}\n aria-label={ariaLabel ?? content.ariaLabel.value}\n >\n {/* Top bar */}\n <div className=\"relative z-10 flex shrink-0 items-center gap-3 rounded-t-xl bg-text/15 px-4 py-2\">\n {/* Navigation Controls */}\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n onClick={handleBack}\n disabled={!canGoBack}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.backButtonLabel.value}\n Icon={ArrowLeft}\n />\n <Button\n type=\"button\"\n onClick={handleForward}\n disabled={!canGoForward}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.forwardButtonLabel.value}\n Icon={ArrowRight}\n />\n </div>\n\n {/* URL Bar */}\n <form\n onSubmit={handleSubmit}\n noValidate\n className={cn(\n inputVariants(),\n 'flex w-full gap-2 rounded-xl p-0.5! supports-[corner-shape:squircle]:rounded-2xl',\n 'bg-neutral/10 text-text/50 placeholder:text-neutral/80'\n )}\n >\n <label htmlFor=\"browser-url\" className=\"sr-only\">\n {content.urlLabel}\n </label>\n <Input\n id=\"browser-url\"\n type=\"text\"\n inputMode=\"url\"\n spellCheck={false}\n autoCapitalize=\"off\"\n variant=\"invisible\"\n className=\"ml-3 p-0!\"\n size=\"sm\"\n autoCorrect=\"off\"\n value={inputUrl}\n onChange={(e) => {\n setInputUrl(e.target.value);\n if (showError) setError(null);\n }}\n placeholder={content.urlPlaceholder.value}\n aria-label={content.urlLabel.value}\n aria-invalid={showError}\n aria-describedby={showError ? 'browser-url-error' : undefined}\n />\n\n <Button\n type=\"button\"\n onClick={handleReload}\n variant=\"hoverable\"\n size=\"icon-md\"\n className=\"p-1!\"\n label=\"content.reloadButtonTitle.value\"\n Icon={RotateCw}\n />\n\n {/* invisible submit */}\n <button type=\"submit\" className=\"sr-only absolute\" tabIndex={-1} />\n </form>\n\n {/* Sitemap Explorer */}\n <ClickOutsideDiv\n onClickOutSide={() => setSitemapOpen(false)}\n disabled={!sitemapOpen}\n role=\"none\"\n >\n <DropDown identifier=\"sitemap-explorer\">\n <DropDown.Trigger\n identifier=\"sitemap-explorer-trigger\"\n type=\"button\"\n color=\"text\"\n onClick={handleSitemapToggle}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.sitemapButtonLabel.value}\n Icon={ScanSearch}\n />\n\n <DropDown.Panel\n identifier=\"sitemap-explorer\"\n isHidden={!sitemapOpen}\n align=\"end\"\n isFocusable\n isOverable\n >\n <Container\n className=\"min-w-28 rounded-md!\"\n roundedSize=\"xl\"\n border\n borderColor=\"neutral\"\n >\n <div className=\"p-2\">\n <Input\n type=\"search\"\n ref={sitemapInputRef}\n aria-label={content.sitemapSearchAriaLabel.value}\n placeholder={content.sitemapSearchPlaceholder.value}\n onChange={(e) => setSitemapSearch(e.target.value)}\n value={sitemapSearch}\n size=\"sm\"\n />\n </div>\n <ul\n className=\"max-h-64 divide-y divide-dotted divide-neutral/30 overflow-y-auto p-1 text-center\"\n aria-label={content.sitemapButtonLabel.value}\n >\n {sitemapLoading ? (\n <li className=\"px-3 py-4 text-center text-neutral text-xs\">\n {content.sitemapLoading}\n </li>\n ) : sitemapError ||\n (!sitemapLoading && filteredSitemapUrls.length === 0) ? (\n <li className=\"px-3 py-4 text-center text-neutral text-xs\">\n {sitemapError\n ? content.sitemapError\n : content.sitemapEmpty}\n </li>\n ) : (\n filteredSitemapUrls.map((url) => (\n <li key={url} className=\"py-0.5\">\n <Button\n variant=\"hoverable\"\n color=\"text\"\n size=\"sm\"\n className=\"w-full text-left\"\n label={url}\n onClick={() => {\n handleNavigateTo(url);\n setSitemapOpen(false);\n }}\n >\n <span className=\"max-w-64 truncate text-left text-base\">\n <UrlPath url={url} />\n </span>\n </Button>\n </li>\n ))\n )}\n </ul>\n </Container>\n </DropDown.Panel>\n </DropDown>\n </ClickOutsideDiv>\n\n {/* Error Message Tooltip */}\n {showError && (\n <div className=\"absolute top-full left-4 z-20 mt-1\">\n <p\n id=\"browser-url-error\"\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"rounded-md bg-red-900/90 px-3 py-1.5 text-red-100 text-xs shadow-md backdrop-blur-sm\"\n >\n {error}\n </p>\n </div>\n )}\n </div>\n\n {/* Iframe */}\n <div className=\"relative z-0 flex min-h-0 w-full flex-1 flex-col overflow-hidden rounded-b-xl bg-background\">\n <iframe\n ref={internalIframeRef}\n src={currentUrl}\n title={content.iframeTitle.value}\n className=\"size-full flex-1\"\n sandbox={sandbox ?? undefined}\n loading=\"lazy\"\n aria-live=\"polite\"\n {...props}\n />\n </div>\n </section>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"Browser.mjs","names":[],"sources":["../../../../src/components/Browser/Browser.tsx"],"sourcesContent":["'use client';\n\nimport { Container } from '@components/Container';\nimport { cn } from '@utils/cn';\nimport { ArrowLeft, ArrowRight, RotateCw, ScanSearch } from 'lucide-react';\nimport {\n type CSSProperties,\n type HTMLAttributes,\n type RefObject,\n type SubmitEvent,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { Button } from '../Button';\nimport { ClickOutsideDiv } from '../ClickOutsideDiv';\nimport { DropDown } from '../DropDown';\nimport { Input, inputVariants } from '../Input';\n\nexport type BrowserProps = {\n initialUrl?: string;\n path?: string;\n className?: string;\n style?: CSSProperties;\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n 'aria-label'?: string;\n /**\n * Value of the iframe `sandbox` attribute. Pass `null` to omit the attribute\n * entirely — required for same-origin embeds, where `allow-scripts` plus\n * `allow-same-origin` sandboxes nothing (the frame can reach its parent and\n * strip the attribute) and browsers warn about the combination.\n */\n sandbox?: string | null;\n ref?: RefObject<HTMLIFrameElement | null>;\n domainRestriction?: string;\n} & HTMLAttributes<HTMLIFrameElement>;\n\nconst UrlPath = ({ url }: { url: string }) => {\n const parts = getUrlPath(url).split('/').filter(Boolean);\n\n if (parts.length === 0) return <span>/</span>;\n\n return parts.flatMap((part, index, array) => [\n <span key={`part-${index}`}>{part}</span>,\n index < array.length - 1 && (\n <span key={`sep-${index}`} className=\"mx-2 text-neutral\">\n /\n </span>\n ),\n ]);\n};\n\nconst getUrlPath = (url: string) => {\n try {\n const { pathname, search, hash } = new URL(url);\n\n return `${pathname}${search}${hash}` || '/';\n } catch {\n return url;\n }\n};\n\nexport const Browser = ({\n initialUrl = 'https://example.com',\n path,\n className,\n style,\n size = 'md',\n 'aria-label': ariaLabel,\n sandbox = 'allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads',\n ref,\n domainRestriction,\n ...props\n}: BrowserProps) => {\n // --- State -----------------------------------------------------------------\n const [inputUrl, setInputUrl] = useState(initialUrl);\n const [currentUrl, setCurrentUrl] = useState(initialUrl);\n\n // History Management\n const [history, setHistory] = useState<string[]>([initialUrl]);\n const [currentIndex, setCurrentIndex] = useState(0);\n\n const [error, setError] = useState<string | null>(null);\n const [submitted, setSubmitted] = useState(false);\n const internalIframeRef = useRef<HTMLIFrameElement>(null);\n\n // Sitemap explorer state\n const [sitemapOpen, setSitemapOpen] = useState(false);\n const [sitemapUrls, setSitemapUrls] = useState<string[]>([]);\n const [sitemapSearch, setSitemapSearch] = useState('');\n const [sitemapLoading, setSitemapLoading] = useState(false);\n const [sitemapFetched, setSitemapFetched] = useState(false);\n const [sitemapError, setSitemapError] = useState(false);\n const sitemapInputRef = useRef<HTMLInputElement>(null);\n\n useImperativeHandle(ref, () => internalIframeRef.current!, []);\n const content = useIntlayer('browser');\n\n // --- Effects ---------------------------------------------------------------\n\n // Reset everything if initialUrl changes completely\n useEffect(() => {\n setInputUrl(initialUrl);\n setCurrentUrl(initialUrl);\n setHistory([initialUrl]);\n setCurrentIndex(0);\n setError(null);\n setSubmitted(false);\n setSitemapOpen(false);\n setSitemapUrls([]);\n setSitemapSearch('');\n setSitemapFetched(false);\n setSitemapError(false);\n }, [initialUrl]);\n\n // Sync external path changes with the URL bar and History\n useEffect(() => {\n if (!path) return;\n\n try {\n const baseOrigin = domainRestriction ?? initialUrl;\n const origin = new URL(baseOrigin).origin;\n const fullUrl = `${origin}${path}`;\n\n // Update Input (Always update the visual bar)\n setInputUrl(fullUrl);\n\n // Check internal iframe state to avoid reload if already there\n let isAlreadyAtUrl = false;\n if (internalIframeRef.current?.contentWindow) {\n try {\n const currentIframeHref =\n internalIframeRef.current.contentWindow.location.href;\n if (new URL(currentIframeHref).href === new URL(fullUrl).href) {\n isAlreadyAtUrl = true;\n }\n } catch {\n // Cross-origin access ignored\n }\n }\n\n // Update History Stack regardless of isAlreadyAtUrl so arrow navigation is always correct\n if (history[currentIndex] !== fullUrl) {\n setHistory((prev) => {\n const newHistory = prev.slice(0, currentIndex + 1);\n newHistory.push(fullUrl);\n return newHistory;\n });\n setCurrentIndex((prev) => prev + 1);\n }\n\n // Navigate only if NOT already there to avoid refreshing on internal iframe navigation\n if (!isAlreadyAtUrl) {\n setCurrentUrl(fullUrl);\n }\n\n setError(null);\n } catch {\n // Ignore invalid paths\n }\n }, [path, domainRestriction, initialUrl]); // Removed currentIndex dependency to prevent loops\n\n // Imperatively keep the iframe src in sync with currentUrl.\n // React's attribute reconciliation can be unreliable for cross-origin iframes after\n // internal navigation, so this effect is the source of truth for navigation.\n useEffect(() => {\n const iframe = internalIframeRef.current;\n if (!iframe) return;\n if (iframe.src !== currentUrl) {\n iframe.src = currentUrl;\n }\n }, [currentUrl]);\n\n // --- Navigation Logic ------------------------------------------------------\n\n const handleNavigateTo = (url: string) => {\n try {\n const validated = normalizeUrl(url);\n\n // If we are navigating to the exact same URL, just reload\n if (validated === currentUrl) {\n handleReload();\n return;\n }\n\n setCurrentUrl(validated);\n setInputUrl(validated);\n setError(null);\n\n // Update History: Slice future if we went back, then push new\n const newHistory = history.slice(0, currentIndex + 1);\n newHistory.push(validated);\n setHistory(newHistory);\n setCurrentIndex(newHistory.length - 1);\n } catch (e) {\n if (\n e instanceof Error &&\n e.message === 'URL does not match allowed domain' &&\n domainRestriction\n ) {\n setError(\n content.domainRestrictionError?.value ??\n `Only URLs from ${domainRestriction} are allowed.`\n );\n } else {\n setError(content.errorMessage.value);\n }\n }\n };\n\n const handleBack = () => {\n if (currentIndex > 0) {\n const newIndex = currentIndex - 1;\n const prevUrl = history[newIndex];\n setCurrentIndex(newIndex);\n setCurrentUrl(prevUrl);\n setInputUrl(prevUrl);\n setError(null);\n }\n };\n\n const handleForward = () => {\n if (currentIndex < history.length - 1) {\n const newIndex = currentIndex + 1;\n const nextUrl = history[newIndex];\n setCurrentIndex(newIndex);\n setCurrentUrl(nextUrl);\n setInputUrl(nextUrl);\n setError(null);\n }\n };\n\n const handleSubmit = (e: SubmitEvent<HTMLFormElement>) => {\n e.preventDefault();\n setSubmitted(true);\n handleNavigateTo(inputUrl);\n };\n\n const handleReload = () => {\n const iframe = internalIframeRef.current;\n if (!iframe) return;\n const src = iframe.src;\n iframe.src = '';\n setTimeout(() => {\n if (internalIframeRef.current) internalIframeRef.current.src = src;\n }, 50);\n };\n\n // --- Validation Helpers ----------------------------------------------------\n const isValidHostname = (host: string) => {\n if (host === 'localhost') return true;\n if (/^(\\d{1,3}\\.){3}\\d{1,3}$/.test(host)) return true;\n if (/^[a-f0-9:]+$/i.test(host)) return true;\n if (!/^[a-z0-9.-]+$/i.test(host)) return false;\n if (/^[-.]/.test(host) || /[-.]$/.test(host)) return false;\n if (host.includes('..')) return false;\n if (!host.includes('.')) return false;\n return true;\n };\n\n const getRestrictionOrigin = (): URL | null => {\n if (!domainRestriction) return null;\n try {\n return new URL(domainRestriction);\n } catch {\n return null;\n }\n };\n\n const normalizeUrl = (raw: string) => {\n const trimmed = raw.trim();\n if (!trimmed || /\\s/.test(trimmed)) throw new Error('Invalid');\n\n const restrictionOrigin = getRestrictionOrigin();\n const isRelativePath = trimmed.startsWith('/') && !trimmed.startsWith('//');\n\n if (isRelativePath) {\n if (restrictionOrigin) {\n return new URL(`${restrictionOrigin.origin}${trimmed}`).toString();\n }\n return new URL(`${new URL(currentUrl).origin}${trimmed}`).toString();\n }\n\n const hasProtocol = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed);\n const candidate = hasProtocol ? trimmed : `https://${trimmed}`;\n const url = new URL(candidate);\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new Error('Only http(s) is allowed');\n }\n\n if (!isValidHostname(url.hostname)) throw new Error('Invalid host');\n\n if (restrictionOrigin) {\n const urlMatches =\n url.hostname === restrictionOrigin.hostname &&\n url.protocol === restrictionOrigin.protocol &&\n (restrictionOrigin.port === '' ||\n url.port === restrictionOrigin.port ||\n url.host === restrictionOrigin.host);\n\n if (!urlMatches) throw new Error('URL does not match allowed domain');\n }\n\n return url.toString();\n };\n\n const handleSitemapToggle = async () => {\n const nextOpen = !sitemapOpen;\n setSitemapOpen(nextOpen);\n\n if (nextOpen && !sitemapFetched) {\n setSitemapLoading(true);\n setSitemapError(false);\n setSitemapFetched(true);\n try {\n const { extractUrlFromSitemap } = await import(\n './extractUrlFromSitemap'\n );\n const urls = await extractUrlFromSitemap(currentUrl);\n setSitemapUrls(urls);\n if (urls.length === 0) setSitemapError(false);\n } catch {\n setSitemapError(true);\n } finally {\n setSitemapLoading(false);\n }\n setTimeout(() => sitemapInputRef.current?.focus(), 50);\n }\n };\n\n const filteredSitemapUrls = useMemo(() => {\n const query = sitemapSearch.trim().toLowerCase();\n if (!query) return sitemapUrls;\n return sitemapUrls.filter((url) => url.toLowerCase().includes(query));\n }, [sitemapUrls, sitemapSearch]);\n\n const showError = submitted && !!error;\n const canGoBack = currentIndex > 0;\n const canGoForward = currentIndex < history.length - 1;\n\n return (\n <section\n className={cn(\n 'flex w-full flex-col overflow-hidden rounded-xl bg-background shadow-[0_4px_12px_rgba(0,0,0,0.4),0_0_1px_rgba(0,0,0,0.2)]',\n className\n )}\n style={style}\n aria-label={ariaLabel ?? content.ariaLabel.value}\n >\n {/* Top bar */}\n <div className=\"relative z-10 flex shrink-0 items-center gap-3 rounded-t-xl border-b bg-card px-4 py-2\">\n {/* Navigation Controls */}\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n onClick={handleBack}\n disabled={!canGoBack}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.backButtonLabel.value}\n Icon={ArrowLeft}\n />\n <Button\n type=\"button\"\n onClick={handleForward}\n disabled={!canGoForward}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.forwardButtonLabel.value}\n Icon={ArrowRight}\n />\n </div>\n\n {/* URL Bar */}\n <form\n onSubmit={handleSubmit}\n noValidate\n className={cn(\n inputVariants(),\n 'flex w-full gap-2 rounded-xl p-0.5! supports-[corner-shape:squircle]:rounded-2xl',\n 'bg-neutral/10 text-foreground/50 placeholder:text-neutral/80'\n )}\n >\n <label htmlFor=\"browser-url\" className=\"sr-only\">\n {content.urlLabel}\n </label>\n <Input\n id=\"browser-url\"\n type=\"text\"\n inputMode=\"url\"\n spellCheck={false}\n autoCapitalize=\"off\"\n variant=\"invisible\"\n className=\"ml-3 p-0!\"\n size=\"sm\"\n autoCorrect=\"off\"\n value={inputUrl}\n onChange={(e) => {\n setInputUrl(e.target.value);\n if (showError) setError(null);\n }}\n placeholder={content.urlPlaceholder.value}\n aria-label={content.urlLabel.value}\n aria-invalid={showError}\n aria-describedby={showError ? 'browser-url-error' : undefined}\n />\n\n <Button\n type=\"button\"\n onClick={handleReload}\n variant=\"hoverable\"\n size=\"icon-md\"\n className=\"p-1!\"\n label=\"content.reloadButtonTitle.value\"\n Icon={RotateCw}\n />\n\n {/* invisible submit */}\n <button type=\"submit\" className=\"sr-only absolute\" tabIndex={-1} />\n </form>\n\n {/* Sitemap Explorer */}\n <ClickOutsideDiv\n onClickOutSide={() => setSitemapOpen(false)}\n disabled={!sitemapOpen}\n role=\"none\"\n >\n <DropDown identifier=\"sitemap-explorer\">\n <DropDown.Trigger\n identifier=\"sitemap-explorer-trigger\"\n type=\"button\"\n color=\"text\"\n onClick={handleSitemapToggle}\n variant=\"hoverable\"\n size=\"icon-md\"\n label={content.sitemapButtonLabel.value}\n Icon={ScanSearch}\n />\n\n <DropDown.Panel\n identifier=\"sitemap-explorer\"\n isHidden={!sitemapOpen}\n align=\"end\"\n isFocusable\n isOverable\n >\n <Container\n className=\"min-w-28 rounded-md!\"\n roundedSize=\"xl\"\n border\n borderColor=\"neutral\"\n >\n <div className=\"p-2\">\n <Input\n type=\"search\"\n ref={sitemapInputRef}\n aria-label={content.sitemapSearchAriaLabel.value}\n placeholder={content.sitemapSearchPlaceholder.value}\n onChange={(e) => setSitemapSearch(e.target.value)}\n value={sitemapSearch}\n size=\"sm\"\n />\n </div>\n <ul\n className=\"max-h-64 divide-y divide-dotted divide-neutral/30 overflow-y-auto p-1 text-center\"\n aria-label={content.sitemapButtonLabel.value}\n >\n {sitemapLoading ? (\n <li className=\"px-3 py-4 text-center text-muted-foreground text-xs\">\n {content.sitemapLoading}\n </li>\n ) : sitemapError ||\n (!sitemapLoading && filteredSitemapUrls.length === 0) ? (\n <li className=\"px-3 py-4 text-center text-muted-foreground text-xs\">\n {sitemapError\n ? content.sitemapError\n : content.sitemapEmpty}\n </li>\n ) : (\n filteredSitemapUrls.map((url) => (\n <li key={url} className=\"py-0.5\">\n <Button\n variant=\"hoverable\"\n color=\"text\"\n size=\"sm\"\n className=\"w-full text-left\"\n label={url}\n onClick={() => {\n handleNavigateTo(url);\n setSitemapOpen(false);\n }}\n >\n <span className=\"max-w-64 truncate text-left text-base\">\n <UrlPath url={url} />\n </span>\n </Button>\n </li>\n ))\n )}\n </ul>\n </Container>\n </DropDown.Panel>\n </DropDown>\n </ClickOutsideDiv>\n\n {/* Error Message Tooltip */}\n {showError && (\n <div className=\"absolute top-full left-4 z-20 mt-1\">\n <p\n id=\"browser-url-error\"\n role=\"alert\"\n aria-live=\"assertive\"\n className=\"rounded-md bg-red-900/90 px-3 py-1.5 text-red-100 text-xs shadow-md backdrop-blur-sm\"\n >\n {error}\n </p>\n </div>\n )}\n </div>\n\n {/* Iframe */}\n <div className=\"relative z-0 flex min-h-0 w-full flex-1 flex-col overflow-hidden rounded-b-xl bg-background\">\n <iframe\n ref={internalIframeRef}\n src={currentUrl}\n title={content.iframeTitle.value}\n className=\"size-full flex-1\"\n sandbox={sandbox ?? undefined}\n loading=\"lazy\"\n aria-live=\"polite\"\n {...props}\n />\n </div>\n </section>\n );\n};\n"],"mappings":"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"}
@@ -44,6 +44,7 @@ const buttonVariants = cva("relative inline-flex cursor-pointer items-center jus
44
44
  light: "hover-white-500/20 text-white ring-white/20 *:text-text-light",
45
45
  dark: "text-neutral-800 ring-text-light/50 *:text-text-light",
46
46
  text: "text-text ring-text/20 *:text-text-opposite",
47
+ foreground: "text-foreground ring-foreground/20 *:text-foreground-opposite",
47
48
  current: "hover-current-500/10 text-current ring-current/10 *:text-text-light",
48
49
  "text-inverse": "text-text-opposite ring-text-opposite/20 *:text-text",
49
50
  error: "hover-error-500/20 text-error ring-error/20 *:text-text-light",
@@ -1 +1 @@
1
- {"version":3,"file":"Button.mjs","names":[],"sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport type { LucideIcon } from 'lucide-react';\nimport type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react';\nimport { Loader } from '../Loader';\n\n/**\n * Button size variants for different use cases\n */\nexport type ButtonSize =\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | 'icon-sm'\n | 'icon-md'\n | 'icon-lg'\n | 'icon-xl'\n | 'custom';\n\nconst buttonIconVariants = cva('flex-none shrink-0', {\n variants: {\n size: {\n xs: 'size-2',\n sm: 'size-3',\n md: 'size-4',\n lg: 'size-5',\n xl: 'size-6',\n 'icon-sm': 'size-3',\n 'icon-md': 'size-4',\n 'icon-lg': 'size-5',\n 'icon-xl': 'size-6',\n custom: '',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n});\n\n/**\n * Button visual style variants\n */\nexport type ButtonVariant =\n | 'default'\n | 'none'\n | 'outline'\n | 'link'\n | 'invisible-link'\n | 'hoverable'\n | 'fade'\n | 'input';\n\n/**\n * Button color themes that work with the design system\n */\nexport type ButtonColor =\n | 'primary'\n | 'secondary'\n | 'neutral'\n | 'light'\n | 'dark'\n | 'text'\n | 'card'\n | 'text-inverse'\n | 'current'\n | 'error'\n | 'success'\n | 'custom';\n\n/**\n * Text alignment options for button content\n */\nexport type ButtonTextAlign = 'left' | 'center' | 'right';\n\n/**\n * Enhanced button variants with improved accessibility and focus states\n */\nexport const buttonVariants = cva(\n 'relative inline-flex cursor-pointer items-center justify-center font-medium ring-0 transition-all duration-300 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n size: {\n xs: 'min-h-7 px-3 text-xs max-md:py-1',\n sm: 'min-h-7 px-3 text-xs max-md:py-1',\n md: 'min-h-8 px-6 text-sm max-md:py-2',\n lg: 'min-h-10 px-8 text-lg max-md:py-3',\n xl: 'min-h-11 px-10 text-xl max-md:py-4',\n 'icon-sm': 'p-1.5',\n 'icon-md': 'p-1.5',\n 'icon-lg': 'p-1.5',\n 'icon-xl': 'p-3',\n custom: '',\n },\n color: {\n primary:\n 'hover-primary-500/20 text-primary ring-primary-500/20 *:text-text-light',\n secondary:\n 'hover-secondary-500/20 text-secondary ring-secondary-500/20 *:text-text-light',\n neutral: 'text-neutral ring-neutral-500/5 *:text-text-light',\n card: 'hover-card-500/20 text-card ring-card-500/20 *:text-text-light',\n light: 'hover-white-500/20 text-white ring-white/20 *:text-text-light',\n dark: 'text-neutral-800 ring-text-light/50 *:text-text-light',\n text: 'text-text ring-text/20 *:text-text-opposite',\n current:\n 'hover-current-500/10 text-current ring-current/10 *:text-text-light',\n 'text-inverse': 'text-text-opposite ring-text-opposite/20 *:text-text',\n error: 'hover-error-500/20 text-error ring-error/20 *:text-text-light',\n success:\n 'hover-success-500/20 text-success ring-success/20 *:text-text-light',\n custom: '',\n },\n roundedSize: {\n none: 'rounded-none',\n sm: 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n md: 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n lg: 'rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl',\n xl: 'rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl',\n '2xl':\n 'rounded-4xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[2.5rem]',\n '3xl':\n 'rounded-[2.5rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[3rem]',\n '4xl':\n 'rounded-[3rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[4rem]',\n '5xl':\n 'rounded-[4rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[5rem]',\n full: 'rounded-full',\n },\n variant: {\n default: [\n 'bg-current',\n 'hover:bg-current/90',\n 'hover:ring-5',\n 'aria-selected:ring-5',\n ],\n\n outline: [\n 'rounded-2xl border-[1.3px] border-current bg-current/0 *:text-current!',\n 'hover:bg-current/20 focus-visible:bg-current/20',\n 'hover:ring-5 focus-visible:ring-5',\n 'aria-selected:ring-5',\n ],\n\n none: 'border-none bg-current/0 text-inherit hover:bg-current/0',\n\n link: 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent hover:underline',\n\n 'invisible-link':\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent',\n\n hoverable:\n 'rounded-lg border-none bg-current/0 transition *:text-current! hover:bg-current/10 aria-[current]:bg-current/5',\n\n fade: [\n 'rounded-lg border-none bg-current/10 ring-current/5 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5',\n 'hover:ring-5 focus-visible:ring-5',\n 'aria-selected:ring-5',\n ],\n input: [\n // base styles\n 'text-text',\n 'w-full select-text resize-none rounded-2xl text-base shadow-none outline-none supports-[corner-shape:squircle]:rounded-4xl',\n 'transition-shadow duration-100 md:text-sm',\n 'ring-0', // base ring\n 'disabled:opacity-50',\n\n 'text-text',\n 'bg-neutral-50 dark:bg-neutral-950',\n 'ring-neutral-100 dark:ring-neutral-700',\n\n // Hover ring (similar spirit to your input)\n 'hover:ring-3', // width\n 'aria-selected:ring-4',\n 'focus-visible:ring-3',\n 'disabled:ring-0',\n\n // Focus ring + animation\n 'focus-visible:outline-none',\n\n // Remove any weird box-shadow\n '[box-shadow:none] focus:[box-shadow:none]',\n\n // aria-invalid border color\n 'aria-invalid:border-error',\n ],\n },\n\n textAlign: {\n left: 'justify-start text-left',\n center: 'justify-center text-center',\n right: 'justify-end text-right',\n },\n\n isFullWidth: {\n true: 'w-full',\n false: '',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n color: 'text',\n roundedSize: 'md',\n textAlign: 'center',\n isFullWidth: false,\n },\n }\n);\n\n/**\n * Enhanced Button component props with comprehensive type safety and accessibility features\n */\nexport type ButtonProps = DetailedHTMLProps<\n ButtonHTMLAttributes<HTMLButtonElement>,\n HTMLButtonElement\n> &\n VariantProps<typeof buttonVariants> & {\n /**\n * Accessible label for screen readers and assistive technologies.\n * This is required for accessibility compliance.\n */\n label: string | null;\n\n /**\n * Optional icon to display on the left side of the button\n */\n Icon?: FC | LucideIcon;\n\n /**\n * Optional icon to display on the right side of the button\n */\n IconRight?: FC | LucideIcon;\n\n /**\n * Additional CSS classes for icon styling\n */\n iconClassName?: string;\n\n /**\n * Shows loading spinner and disables button interaction when true\n */\n isLoading?: boolean;\n\n /**\n * Marks the button as active (useful for navigation or toggle states)\n */\n isActive?: boolean;\n\n /**\n * Marks the button as selected\n */\n isSelected?: boolean;\n\n /**\n * Makes the button span the full width of its container\n */\n isFullWidth?: boolean;\n\n /**\n * Additional description for complex buttons (optional)\n */\n 'aria-describedby'?: string;\n\n /**\n * Expanded state for collapsible sections (optional)\n */\n 'aria-expanded'?: boolean;\n\n /**\n * Controls whether the button has popup/menu (optional)\n */\n 'aria-haspopup'?:\n | boolean\n | 'true'\n | 'false'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog';\n\n /**\n * Indicates if button controls are currently pressed (for toggle buttons)\n */\n 'aria-pressed'?: boolean;\n };\n\n/**\n * Button Component - A comprehensive, accessible button component\n *\n * Features:\n * - Full accessibility compliance with ARIA attributes\n * - Multiple variants and sizes for different use cases\n * - Icon support (left and right positioning)\n * - Loading states with spinner\n * - Keyboard navigation support\n * - Focus management with visible indicators\n * - Responsive design adaptations\n *\n * @example\n * ```tsx\n * // Basic button\n * <Button label=\"Click me\">Click me</Button>\n *\n * // Button with icon and loading state\n * <Button\n * label=\"Save document\"\n * Icon={SaveIcon}\n * isLoading={saving}\n * disabled={!hasChanges}\n * >\n * Save\n * </Button>\n *\n * // Error action button\n * <Button\n * variant=\"outline\"\n * color=\"error\"\n * label=\"Delete item permanently\"\n * aria-describedby=\"delete-warning\"\n * >\n * Delete\n * </Button>\n * ```\n */\nexport const Button: FC<ButtonProps> = ({\n variant,\n size,\n color,\n children,\n Icon,\n IconRight,\n iconClassName,\n isLoading = false,\n isActive,\n isSelected,\n isFullWidth,\n roundedSize,\n textAlign,\n disabled,\n label,\n className,\n type = 'button',\n 'aria-describedby': ariaDescribedBy,\n 'aria-expanded': ariaExpanded,\n 'aria-haspopup': ariaHasPopup,\n 'aria-pressed': ariaPressed,\n ...props\n}) => {\n const isLink = variant === 'link' || variant === 'invisible-link';\n const isIconOnly = !children && (Icon || IconRight);\n\n const accessibilityProps = {\n 'aria-label': isIconOnly ? (label ?? undefined) : undefined,\n 'aria-labelledby': !isIconOnly ? undefined : undefined,\n 'aria-describedby': ariaDescribedBy,\n 'aria-expanded': ariaExpanded,\n 'aria-haspopup': ariaHasPopup,\n 'aria-pressed': isActive !== undefined ? isActive : ariaPressed,\n 'aria-busy': isLoading,\n 'aria-current': (isActive ? 'page' : undefined) as 'page' | undefined,\n 'aria-disabled': disabled || isLoading,\n 'aria-selected': isSelected,\n };\n\n const isSquareButton =\n size === 'icon-sm' ||\n size === 'icon-md' ||\n size === 'icon-lg' ||\n size === 'icon-xl';\n\n return (\n <button\n disabled={isLoading || disabled}\n role={isLink ? 'link' : 'button'}\n type={type}\n className={buttonVariants({\n variant,\n size,\n color,\n isFullWidth,\n roundedSize,\n textAlign: textAlign ?? (IconRight ? 'left' : 'center'),\n className,\n })}\n {...accessibilityProps}\n {...props}\n >\n {Icon && !isLoading && (\n <Icon\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'mr-3', iconClassName),\n })}\n aria-hidden=\"true\"\n />\n )}\n\n <div\n className={cn(\n 'flex items-center justify-center transition-[width] duration-300',\n isLoading && size === 'sm' && 'w-3',\n isLoading && size === 'md' && 'w-4',\n isLoading && size === 'lg' && 'w-6',\n isLoading && size === 'xl' && 'w-8'\n )}\n >\n <Loader\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'mr-3', iconClassName),\n })}\n isLoading={isLoading}\n aria-hidden=\"true\"\n data-testid=\"loader\"\n />\n </div>\n\n {children && (\n <span className=\"flex-1 truncate whitespace-nowrap\">{children}</span>\n )}\n\n {!children && isIconOnly && <span className=\"sr-only\">{label}</span>}\n\n {IconRight && (\n <IconRight\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'ml-3', iconClassName),\n })}\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n};\n"],"mappings":";;;;;;AAqBA,MAAM,qBAAqB,IAAI,sBAAsB;CACnD,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,WAAW;EACX,WAAW;EACX,WAAW;EACX,WAAW;EACX,QAAQ;CACV,EACF;CACA,iBAAiB,EACf,MAAM,KACR;AACF,CAAC;;;;AAwCD,MAAa,iBAAiB,IAC5B,8LACA;CACE,UAAU;EACR,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,WAAW;GACX,WAAW;GACX,WAAW;GACX,WAAW;GACX,QAAQ;EACV;EACA,OAAO;GACL,SACE;GACF,WACE;GACF,SAAS;GACT,MAAM;GACN,OAAO;GACP,MAAM;GACN,MAAM;GACN,SACE;GACF,gBAAgB;GAChB,OAAO;GACP,SACE;GACF,QAAQ;EACV;EACA,aAAa;GACX,MAAM;GACN,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,OACE;GACF,OACE;GACF,OACE;GACF,OACE;GACF,MAAM;EACR;EACA,SAAS;GACP,SAAS;IACP;IACA;IACA;IACA;GACF;GAEA,SAAS;IACP;IACA;IACA;IACA;GACF;GAEA,MAAM;GAEN,MAAM;GAEN,kBACE;GAEF,WACE;GAEF,MAAM;IACJ;IACA;IACA;GACF;GACA,OAAO;IAEL;IACA;IACA;IACA;IACA;IAEA;IACA;IACA;IAGA;IACA;IACA;IACA;IAGA;IAGA;IAGA;GACF;EACF;EAEA,WAAW;GACT,MAAM;GACN,QAAQ;GACR,OAAO;EACT;EAEA,aAAa;GACX,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;EACP,aAAa;EACb,WAAW;EACX,aAAa;CACf;AACF,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsHA,MAAa,UAA2B,EACtC,SACA,MACA,OACA,UACA,MACA,WACA,eACA,YAAY,OACZ,UACA,YACA,aACA,aACA,WACA,UACA,OACA,WACA,OAAO,UACP,oBAAoB,iBACpB,iBAAiB,cACjB,iBAAiB,cACjB,gBAAgB,aAChB,GAAG,YACC;CACJ,MAAM,SAAS,YAAY,UAAU,YAAY;CACjD,MAAM,aAAa,CAAC,aAAa,QAAQ;CAEzC,MAAM,qBAAqB;EACzB,cAAc,aAAc,SAAS,SAAa;EAClD,mBAAmB,CAAC,aAAa,SAAY;EAC7C,oBAAoB;EACpB,iBAAiB;EACjB,iBAAiB;EACjB,gBAAgB,aAAa,SAAY,WAAW;EACpD,aAAa;EACb,gBAAiB,WAAW,SAAS;EACrC,iBAAiB,YAAY;EAC7B,iBAAiB;CACnB;CAEA,MAAM,iBACJ,SAAS,aACT,SAAS,aACT,SAAS,aACT,SAAS;CAEX,OACE,qBAAC,UAAD;EACE,UAAU,aAAa;EACvB,MAAM,SAAS,SAAS;EAClB;EACN,WAAW,eAAe;GACxB;GACA;GACA;GACA;GACA;GACA,WAAW,cAAc,YAAY,SAAS;GAC9C;EACF,CAAC;EACD,GAAI;EACJ,GAAI;YAdN;GAgBG,QAAQ,CAAC,aACR,oBAAC,MAAD;IACE,WAAW,mBAAmB;KAC5B;KACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;IACxD,CAAC;IACD,eAAY;GACb;GAGH,oBAAC,OAAD;IACE,WAAW,GACT,oEACA,aAAa,SAAS,QAAQ,OAC9B,aAAa,SAAS,QAAQ,OAC9B,aAAa,SAAS,QAAQ,OAC9B,aAAa,SAAS,QAAQ,KAChC;cAEA,oBAAC,QAAD;KACE,WAAW,mBAAmB;MAC5B;MACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;KACxD,CAAC;KACU;KACX,eAAY;KACZ,eAAY;IACb;GACE;GAEJ,YACC,oBAAC,QAAD;IAAM,WAAU;IAAqC;GAAe;GAGrE,CAAC,YAAY,cAAc,oBAAC,QAAD;IAAM,WAAU;cAAW;GAAY;GAElE,aACC,oBAAC,WAAD;IACE,WAAW,mBAAmB;KAC5B;KACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;IACxD,CAAC;IACD,eAAY;GACb;EAEG;;AAEZ"}
1
+ {"version":3,"file":"Button.mjs","names":[],"sources":["../../../../src/components/Button/Button.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport type { LucideIcon } from 'lucide-react';\nimport type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react';\nimport { Loader } from '../Loader';\n\n/**\n * Button size variants for different use cases\n */\nexport type ButtonSize =\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | 'icon-sm'\n | 'icon-md'\n | 'icon-lg'\n | 'icon-xl'\n | 'custom';\n\nconst buttonIconVariants = cva('flex-none shrink-0', {\n variants: {\n size: {\n xs: 'size-2',\n sm: 'size-3',\n md: 'size-4',\n lg: 'size-5',\n xl: 'size-6',\n 'icon-sm': 'size-3',\n 'icon-md': 'size-4',\n 'icon-lg': 'size-5',\n 'icon-xl': 'size-6',\n custom: '',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n});\n\n/**\n * Button visual style variants\n */\nexport type ButtonVariant =\n | 'default'\n | 'none'\n | 'outline'\n | 'link'\n | 'invisible-link'\n | 'hoverable'\n | 'fade'\n | 'input';\n\n/**\n * Button color themes that work with the design system\n */\nexport type ButtonColor =\n | 'primary'\n | 'secondary'\n | 'neutral'\n | 'light'\n | 'dark'\n | 'text'\n | 'foreground'\n | 'card'\n | 'text-inverse'\n | 'current'\n | 'error'\n | 'success'\n | 'custom';\n\n/**\n * Text alignment options for button content\n */\nexport type ButtonTextAlign = 'left' | 'center' | 'right';\n\n/**\n * Enhanced button variants with improved accessibility and focus states\n */\nexport const buttonVariants = cva(\n 'relative inline-flex cursor-pointer items-center justify-center font-medium ring-0 transition-all duration-300 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n size: {\n xs: 'min-h-7 px-3 text-xs max-md:py-1',\n sm: 'min-h-7 px-3 text-xs max-md:py-1',\n md: 'min-h-8 px-6 text-sm max-md:py-2',\n lg: 'min-h-10 px-8 text-lg max-md:py-3',\n xl: 'min-h-11 px-10 text-xl max-md:py-4',\n 'icon-sm': 'p-1.5',\n 'icon-md': 'p-1.5',\n 'icon-lg': 'p-1.5',\n 'icon-xl': 'p-3',\n custom: '',\n },\n color: {\n primary:\n 'hover-primary-500/20 text-primary ring-primary-500/20 *:text-text-light',\n secondary:\n 'hover-secondary-500/20 text-secondary ring-secondary-500/20 *:text-text-light',\n neutral: 'text-neutral ring-neutral-500/5 *:text-text-light',\n card: 'hover-card-500/20 text-card ring-card-500/20 *:text-text-light',\n light: 'hover-white-500/20 text-white ring-white/20 *:text-text-light',\n dark: 'text-neutral-800 ring-text-light/50 *:text-text-light',\n text: 'text-text ring-text/20 *:text-text-opposite',\n foreground:\n 'text-foreground ring-foreground/20 *:text-foreground-opposite',\n current:\n 'hover-current-500/10 text-current ring-current/10 *:text-text-light',\n 'text-inverse': 'text-text-opposite ring-text-opposite/20 *:text-text',\n error: 'hover-error-500/20 text-error ring-error/20 *:text-text-light',\n success:\n 'hover-success-500/20 text-success ring-success/20 *:text-text-light',\n custom: '',\n },\n roundedSize: {\n none: 'rounded-none',\n sm: 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n md: 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n lg: 'rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl',\n xl: 'rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl',\n '2xl':\n 'rounded-4xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[2.5rem]',\n '3xl':\n 'rounded-[2.5rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[3rem]',\n '4xl':\n 'rounded-[3rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[4rem]',\n '5xl':\n 'rounded-[4rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[5rem]',\n full: 'rounded-full',\n },\n variant: {\n default: [\n 'bg-current',\n 'hover:bg-current/90',\n 'hover:ring-5',\n 'aria-selected:ring-5',\n ],\n\n outline: [\n 'rounded-2xl border-[1.3px] border-current bg-current/0 *:text-current!',\n 'hover:bg-current/20 focus-visible:bg-current/20',\n 'hover:ring-5 focus-visible:ring-5',\n 'aria-selected:ring-5',\n ],\n\n none: 'border-none bg-current/0 text-inherit hover:bg-current/0',\n\n link: 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent hover:underline',\n\n 'invisible-link':\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent',\n\n hoverable:\n 'rounded-lg border-none bg-current/0 transition *:text-current! hover:bg-current/10 aria-[current]:bg-current/5',\n\n fade: [\n 'rounded-lg border-none bg-current/10 ring-current/5 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5',\n 'hover:ring-5 focus-visible:ring-5',\n 'aria-selected:ring-5',\n ],\n input: [\n // base styles\n 'text-text',\n 'w-full select-text resize-none rounded-2xl text-base shadow-none outline-none supports-[corner-shape:squircle]:rounded-4xl',\n 'transition-shadow duration-100 md:text-sm',\n 'ring-0', // base ring\n 'disabled:opacity-50',\n\n 'text-text',\n 'bg-neutral-50 dark:bg-neutral-950',\n 'ring-neutral-100 dark:ring-neutral-700',\n\n // Hover ring (similar spirit to your input)\n 'hover:ring-3', // width\n 'aria-selected:ring-4',\n 'focus-visible:ring-3',\n 'disabled:ring-0',\n\n // Focus ring + animation\n 'focus-visible:outline-none',\n\n // Remove any weird box-shadow\n '[box-shadow:none] focus:[box-shadow:none]',\n\n // aria-invalid border color\n 'aria-invalid:border-error',\n ],\n },\n\n textAlign: {\n left: 'justify-start text-left',\n center: 'justify-center text-center',\n right: 'justify-end text-right',\n },\n\n isFullWidth: {\n true: 'w-full',\n false: '',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n color: 'text',\n roundedSize: 'md',\n textAlign: 'center',\n isFullWidth: false,\n },\n }\n);\n\n/**\n * Enhanced Button component props with comprehensive type safety and accessibility features\n */\nexport type ButtonProps = DetailedHTMLProps<\n ButtonHTMLAttributes<HTMLButtonElement>,\n HTMLButtonElement\n> &\n VariantProps<typeof buttonVariants> & {\n /**\n * Accessible label for screen readers and assistive technologies.\n * This is required for accessibility compliance.\n */\n label: string | null;\n\n /**\n * Optional icon to display on the left side of the button\n */\n Icon?: FC | LucideIcon;\n\n /**\n * Optional icon to display on the right side of the button\n */\n IconRight?: FC | LucideIcon;\n\n /**\n * Additional CSS classes for icon styling\n */\n iconClassName?: string;\n\n /**\n * Shows loading spinner and disables button interaction when true\n */\n isLoading?: boolean;\n\n /**\n * Marks the button as active (useful for navigation or toggle states)\n */\n isActive?: boolean;\n\n /**\n * Marks the button as selected\n */\n isSelected?: boolean;\n\n /**\n * Makes the button span the full width of its container\n */\n isFullWidth?: boolean;\n\n /**\n * Additional description for complex buttons (optional)\n */\n 'aria-describedby'?: string;\n\n /**\n * Expanded state for collapsible sections (optional)\n */\n 'aria-expanded'?: boolean;\n\n /**\n * Controls whether the button has popup/menu (optional)\n */\n 'aria-haspopup'?:\n | boolean\n | 'true'\n | 'false'\n | 'menu'\n | 'listbox'\n | 'tree'\n | 'grid'\n | 'dialog';\n\n /**\n * Indicates if button controls are currently pressed (for toggle buttons)\n */\n 'aria-pressed'?: boolean;\n };\n\n/**\n * Button Component - A comprehensive, accessible button component\n *\n * Features:\n * - Full accessibility compliance with ARIA attributes\n * - Multiple variants and sizes for different use cases\n * - Icon support (left and right positioning)\n * - Loading states with spinner\n * - Keyboard navigation support\n * - Focus management with visible indicators\n * - Responsive design adaptations\n *\n * @example\n * ```tsx\n * // Basic button\n * <Button label=\"Click me\">Click me</Button>\n *\n * // Button with icon and loading state\n * <Button\n * label=\"Save document\"\n * Icon={SaveIcon}\n * isLoading={saving}\n * disabled={!hasChanges}\n * >\n * Save\n * </Button>\n *\n * // Error action button\n * <Button\n * variant=\"outline\"\n * color=\"error\"\n * label=\"Delete item permanently\"\n * aria-describedby=\"delete-warning\"\n * >\n * Delete\n * </Button>\n * ```\n */\nexport const Button: FC<ButtonProps> = ({\n variant,\n size,\n color,\n children,\n Icon,\n IconRight,\n iconClassName,\n isLoading = false,\n isActive,\n isSelected,\n isFullWidth,\n roundedSize,\n textAlign,\n disabled,\n label,\n className,\n type = 'button',\n 'aria-describedby': ariaDescribedBy,\n 'aria-expanded': ariaExpanded,\n 'aria-haspopup': ariaHasPopup,\n 'aria-pressed': ariaPressed,\n ...props\n}) => {\n const isLink = variant === 'link' || variant === 'invisible-link';\n const isIconOnly = !children && (Icon || IconRight);\n\n const accessibilityProps = {\n 'aria-label': isIconOnly ? (label ?? undefined) : undefined,\n 'aria-labelledby': !isIconOnly ? undefined : undefined,\n 'aria-describedby': ariaDescribedBy,\n 'aria-expanded': ariaExpanded,\n 'aria-haspopup': ariaHasPopup,\n 'aria-pressed': isActive !== undefined ? isActive : ariaPressed,\n 'aria-busy': isLoading,\n 'aria-current': (isActive ? 'page' : undefined) as 'page' | undefined,\n 'aria-disabled': disabled || isLoading,\n 'aria-selected': isSelected,\n };\n\n const isSquareButton =\n size === 'icon-sm' ||\n size === 'icon-md' ||\n size === 'icon-lg' ||\n size === 'icon-xl';\n\n return (\n <button\n disabled={isLoading || disabled}\n role={isLink ? 'link' : 'button'}\n type={type}\n className={buttonVariants({\n variant,\n size,\n color,\n isFullWidth,\n roundedSize,\n textAlign: textAlign ?? (IconRight ? 'left' : 'center'),\n className,\n })}\n {...accessibilityProps}\n {...props}\n >\n {Icon && !isLoading && (\n <Icon\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'mr-3', iconClassName),\n })}\n aria-hidden=\"true\"\n />\n )}\n\n <div\n className={cn(\n 'flex items-center justify-center transition-[width] duration-300',\n isLoading && size === 'sm' && 'w-3',\n isLoading && size === 'md' && 'w-4',\n isLoading && size === 'lg' && 'w-6',\n isLoading && size === 'xl' && 'w-8'\n )}\n >\n <Loader\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'mr-3', iconClassName),\n })}\n isLoading={isLoading}\n aria-hidden=\"true\"\n data-testid=\"loader\"\n />\n </div>\n\n {children && (\n <span className=\"flex-1 truncate whitespace-nowrap\">{children}</span>\n )}\n\n {!children && isIconOnly && <span className=\"sr-only\">{label}</span>}\n\n {IconRight && (\n <IconRight\n className={buttonIconVariants({\n size,\n className: cn(!isSquareButton && 'ml-3', iconClassName),\n })}\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n};\n"],"mappings":";;;;;;AAqBA,MAAM,qBAAqB,IAAI,sBAAsB;CACnD,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,WAAW;EACX,WAAW;EACX,WAAW;EACX,WAAW;EACX,QAAQ;CACV,EACF;CACA,iBAAiB,EACf,MAAM,KACR;AACF,CAAC;;;;AAyCD,MAAa,iBAAiB,IAC5B,8LACA;CACE,UAAU;EACR,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,WAAW;GACX,WAAW;GACX,WAAW;GACX,WAAW;GACX,QAAQ;EACV;EACA,OAAO;GACL,SACE;GACF,WACE;GACF,SAAS;GACT,MAAM;GACN,OAAO;GACP,MAAM;GACN,MAAM;GACN,YACE;GACF,SACE;GACF,gBAAgB;GAChB,OAAO;GACP,SACE;GACF,QAAQ;EACV;EACA,aAAa;GACX,MAAM;GACN,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,OACE;GACF,OACE;GACF,OACE;GACF,OACE;GACF,MAAM;EACR;EACA,SAAS;GACP,SAAS;IACP;IACA;IACA;IACA;GACF;GAEA,SAAS;IACP;IACA;IACA;IACA;GACF;GAEA,MAAM;GAEN,MAAM;GAEN,kBACE;GAEF,WACE;GAEF,MAAM;IACJ;IACA;IACA;GACF;GACA,OAAO;IAEL;IACA;IACA;IACA;IACA;IAEA;IACA;IACA;IAGA;IACA;IACA;IACA;IAGA;IAGA;IAGA;GACF;EACF;EAEA,WAAW;GACT,MAAM;GACN,QAAQ;GACR,OAAO;EACT;EAEA,aAAa;GACX,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;EACP,aAAa;EACb,WAAW;EACX,aAAa;CACf;AACF,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsHA,MAAa,UAA2B,EACtC,SACA,MACA,OACA,UACA,MACA,WACA,eACA,YAAY,OACZ,UACA,YACA,aACA,aACA,WACA,UACA,OACA,WACA,OAAO,UACP,oBAAoB,iBACpB,iBAAiB,cACjB,iBAAiB,cACjB,gBAAgB,aAChB,GAAG,YACC;CACJ,MAAM,SAAS,YAAY,UAAU,YAAY;CACjD,MAAM,aAAa,CAAC,aAAa,QAAQ;CAEzC,MAAM,qBAAqB;EACzB,cAAc,aAAc,SAAS,SAAa;EAClD,mBAAmB,CAAC,aAAa,SAAY;EAC7C,oBAAoB;EACpB,iBAAiB;EACjB,iBAAiB;EACjB,gBAAgB,aAAa,SAAY,WAAW;EACpD,aAAa;EACb,gBAAiB,WAAW,SAAS;EACrC,iBAAiB,YAAY;EAC7B,iBAAiB;CACnB;CAEA,MAAM,iBACJ,SAAS,aACT,SAAS,aACT,SAAS,aACT,SAAS;CAEX,OACE,qBAAC,UAAD;EACE,UAAU,aAAa;EACvB,MAAM,SAAS,SAAS;EAClB;EACN,WAAW,eAAe;GACxB;GACA;GACA;GACA;GACA;GACA,WAAW,cAAc,YAAY,SAAS;GAC9C;EACF,CAAC;EACD,GAAI;EACJ,GAAI;YAdN;GAgBG,QAAQ,CAAC,aACR,oBAAC,MAAD;IACE,WAAW,mBAAmB;KAC5B;KACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;IACxD,CAAC;IACD,eAAY;GACb;GAGH,oBAAC,OAAD;IACE,WAAW,GACT,oEACA,aAAa,SAAS,QAAQ,OAC9B,aAAa,SAAS,QAAQ,OAC9B,aAAa,SAAS,QAAQ,OAC9B,aAAa,SAAS,QAAQ,KAChC;cAEA,oBAAC,QAAD;KACE,WAAW,mBAAmB;MAC5B;MACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;KACxD,CAAC;KACU;KACX,eAAY;KACZ,eAAY;IACb;GACE;GAEJ,YACC,oBAAC,QAAD;IAAM,WAAU;IAAqC;GAAe;GAGrE,CAAC,YAAY,cAAc,oBAAC,QAAD;IAAM,WAAU;cAAW;GAAY;GAElE,aACC,oBAAC,WAAD;IACE,WAAW,mBAAmB;KAC5B;KACA,WAAW,GAAG,CAAC,kBAAkB,QAAQ,aAAa;IACxD,CAAC;IACD,eAAY;GACb;EAEG;;AAEZ"}
@@ -62,7 +62,7 @@ const CarouselIndicators = ({ className, disableKeyboardShortcuts = false, ...pr
62
62
  identifier: "carousel-prev",
63
63
  children: [/* @__PURE__ */ jsx(Button, {
64
64
  variant: "hoverable",
65
- color: "neutral",
65
+ color: "foreground",
66
66
  label: previousSlide.value,
67
67
  roundedSize: "full",
68
68
  onClick: (e) => {
@@ -77,7 +77,7 @@ const CarouselIndicators = ({ className, disableKeyboardShortcuts = false, ...pr
77
77
  children: /* @__PURE__ */ jsxs("div", {
78
78
  className: "flex items-center gap-2 p-2",
79
79
  children: [/* @__PURE__ */ jsx("span", {
80
- className: "whitespace-nowrap text-neutral text-xs",
80
+ className: "whitespace-nowrap text-muted-foreground text-xs",
81
81
  children: previousSlide.value
82
82
  }), /* @__PURE__ */ jsx(KeyboardShortcut, {
83
83
  shortcut: "ArrowLeft",
@@ -97,14 +97,14 @@ const CarouselIndicators = ({ className, disableKeyboardShortcuts = false, ...pr
97
97
  setSelectedIndex(index);
98
98
  },
99
99
  "aria-label": goToSlide({ index: index + 1 }).value,
100
- className: cn("h-2.5 w-2.5 rounded-full transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2", isActive ? "scale-110 bg-text" : "bg-text/20 hover:bg-text/40")
100
+ className: cn("size-2.5 rounded-full transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2", isActive ? "size-3.5 bg-text" : "bg-text/20 hover:bg-text/40")
101
101
  }, index);
102
102
  }),
103
103
  /* @__PURE__ */ jsxs(Popover, {
104
104
  identifier: "carousel-next",
105
105
  children: [/* @__PURE__ */ jsx(Button, {
106
106
  variant: "hoverable",
107
- color: "neutral",
107
+ color: "foreground",
108
108
  roundedSize: "full",
109
109
  label: nextSlide.value,
110
110
  onClick: (e) => {
@@ -119,7 +119,7 @@ const CarouselIndicators = ({ className, disableKeyboardShortcuts = false, ...pr
119
119
  children: /* @__PURE__ */ jsxs("div", {
120
120
  className: "flex items-center gap-2 p-2",
121
121
  children: [/* @__PURE__ */ jsx("span", {
122
- className: "whitespace-nowrap text-neutral text-xs",
122
+ className: "whitespace-nowrap text-muted-foreground text-xs",
123
123
  children: nextSlide.value
124
124
  }), /* @__PURE__ */ jsx(KeyboardShortcut, {
125
125
  shortcut: "ArrowRight",