@intlayer/design-system 8.10.1 → 8.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/api/hooks/ai.mjs +66 -0
- package/dist/esm/api/hooks/ai.mjs.map +1 -0
- package/dist/esm/api/hooks/audit.mjs +34 -0
- package/dist/esm/api/hooks/audit.mjs.map +1 -0
- package/dist/esm/api/hooks/auth.mjs +217 -0
- package/dist/esm/api/hooks/auth.mjs.map +1 -0
- package/dist/esm/api/hooks/bitbucket.mjs +39 -0
- package/dist/esm/api/hooks/bitbucket.mjs.map +1 -0
- package/dist/esm/api/hooks/dictionary.mjs +105 -0
- package/dist/esm/api/hooks/dictionary.mjs.map +1 -0
- package/dist/esm/api/hooks/discussions.mjs +37 -0
- package/dist/esm/api/hooks/discussions.mjs.map +1 -0
- package/dist/esm/api/hooks/editor.mjs +24 -0
- package/dist/esm/api/hooks/editor.mjs.map +1 -0
- package/dist/esm/api/hooks/github.mjs +54 -0
- package/dist/esm/api/hooks/github.mjs.map +1 -0
- package/dist/esm/api/hooks/gitlab.mjs +43 -0
- package/dist/esm/api/hooks/gitlab.mjs.map +1 -0
- package/dist/esm/api/hooks/newsletter.mjs +31 -0
- package/dist/esm/api/hooks/newsletter.mjs.map +1 -0
- package/dist/esm/api/hooks/organization.mjs +123 -0
- package/dist/esm/api/hooks/organization.mjs.map +1 -0
- package/dist/esm/api/hooks/project.mjs +187 -0
- package/dist/esm/api/hooks/project.mjs.map +1 -0
- package/dist/esm/api/hooks/reviewer.mjs +288 -0
- package/dist/esm/api/hooks/reviewer.mjs.map +1 -0
- package/dist/esm/api/hooks/search.mjs +18 -0
- package/dist/esm/api/hooks/search.mjs.map +1 -0
- package/dist/esm/api/hooks/showcaseProject.mjs +95 -0
- package/dist/esm/api/hooks/showcaseProject.mjs.map +1 -0
- package/dist/esm/api/hooks/stripe.mjs +261 -0
- package/dist/esm/api/hooks/stripe.mjs.map +1 -0
- package/dist/esm/api/hooks/tag.mjs +45 -0
- package/dist/esm/api/hooks/tag.mjs.map +1 -0
- package/dist/esm/api/hooks/translate.mjs +47 -0
- package/dist/esm/api/hooks/translate.mjs.map +1 -0
- package/dist/esm/api/hooks/user.mjs +58 -0
- package/dist/esm/api/hooks/user.mjs.map +1 -0
- package/dist/esm/api/hooks/utils.mjs +30 -0
- package/dist/esm/api/hooks/utils.mjs.map +1 -0
- package/dist/esm/api/index.mjs +26 -0
- package/dist/esm/{hooks → api}/useAuth/useAuth.mjs +1 -1
- package/dist/esm/api/useAuth/useAuth.mjs.map +1 -0
- package/dist/esm/{hooks → api}/useAuth/useOAuth2.mjs +3 -3
- package/dist/esm/api/useAuth/useOAuth2.mjs.map +1 -0
- package/dist/esm/{hooks → api}/useAuth/useSession.mjs +3 -4
- package/dist/esm/api/useAuth/useSession.mjs.map +1 -0
- package/dist/esm/api/useIntlayerAPI.mjs +123 -0
- package/dist/esm/api/useIntlayerAPI.mjs.map +1 -0
- package/dist/esm/{hooks → api}/useUser/index.mjs +2 -2
- package/dist/esm/api/useUser/index.mjs.map +1 -0
- package/dist/esm/components/Badge/index.mjs +1 -3
- package/dist/esm/components/Badge/index.mjs.map +1 -1
- package/dist/esm/components/Breadcrumb/index.mjs +0 -1
- package/dist/esm/components/Breadcrumb/index.mjs.map +1 -1
- package/dist/esm/components/Button/Button.mjs +3 -5
- package/dist/esm/components/Button/Button.mjs.map +1 -1
- package/dist/esm/components/Carousel/index.mjs +3 -3
- package/dist/esm/components/Carousel/index.mjs.map +1 -1
- package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +1 -1
- package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +1 -1
- package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +3 -2
- package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +4 -2
- package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +3 -2
- package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs.map +1 -1
- package/dist/esm/components/ExpandCollapse/ExpandCollapse.mjs +2 -2
- package/dist/esm/components/ExpandCollapse/ExpandCollapse.mjs.map +1 -1
- package/dist/esm/components/IDE/FileList.mjs +1 -1
- package/dist/esm/components/IDE/FileList.mjs.map +1 -1
- package/dist/esm/components/IDE/IDE.mjs +1 -1
- package/dist/esm/components/IDE/IDE.mjs.map +1 -1
- package/dist/esm/components/Input/Checkbox.mjs +1 -3
- package/dist/esm/components/Input/Checkbox.mjs.map +1 -1
- package/dist/esm/components/Label/index.mjs +2 -2
- package/dist/esm/components/Label/index.mjs.map +1 -1
- package/dist/esm/components/LanguageBackground/LanguageSection.mjs +8 -6
- package/dist/esm/components/LanguageBackground/LanguageSection.mjs.map +1 -1
- package/dist/esm/components/LanguageBackground/index.mjs +13 -5
- package/dist/esm/components/LanguageBackground/index.mjs.map +1 -1
- package/dist/esm/components/Link/Link.mjs +0 -7
- package/dist/esm/components/Link/Link.mjs.map +1 -1
- package/dist/esm/components/Loader/spinner.mjs +1 -1
- package/dist/esm/components/Loader/spinner.mjs.map +1 -1
- package/dist/esm/components/Pagination/Pagination.mjs +1 -2
- package/dist/esm/components/Pagination/Pagination.mjs.map +1 -1
- package/dist/esm/components/Popover/dynamic.mjs +2 -2
- package/dist/esm/components/Popover/dynamic.mjs.map +1 -1
- package/dist/esm/components/SwitchSelector/SwitchSelector.mjs +3 -5
- package/dist/esm/components/SwitchSelector/SwitchSelector.mjs.map +1 -1
- package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs +3 -3
- package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs.map +1 -1
- package/dist/esm/components/TabSelector/TabSelector.mjs +3 -3
- package/dist/esm/components/TabSelector/TabSelector.mjs.map +1 -1
- package/dist/esm/components/Tag/index.mjs +1 -1
- package/dist/esm/components/Tag/index.mjs.map +1 -1
- package/dist/esm/components/Terminal/Terminal.mjs +1 -1
- package/dist/esm/components/Terminal/Terminal.mjs.map +1 -1
- package/dist/esm/components/TextArea/AutoSizeTextArea.mjs +2 -2
- package/dist/esm/components/TextArea/AutoSizeTextArea.mjs.map +1 -1
- package/dist/esm/components/TextArea/ContentEditableTextArea.mjs +3 -3
- package/dist/esm/components/TextArea/ContentEditableTextArea.mjs.map +1 -1
- package/dist/esm/components/Toaster/Toast.mjs +3 -3
- package/dist/esm/components/Toaster/Toast.mjs.map +1 -1
- package/dist/esm/components/WithResizer/index.mjs +7 -2
- package/dist/esm/components/WithResizer/index.mjs.map +1 -1
- package/dist/esm/hooks/index.mjs +1 -7
- package/dist/esm/libs/auth.mjs +1 -1
- package/dist/esm/routes.mjs +23 -1
- package/dist/esm/routes.mjs.map +1 -1
- package/dist/types/api/hooks/ai.d.ts +12 -0
- package/dist/types/api/hooks/ai.d.ts.map +1 -0
- package/dist/types/api/hooks/audit.d.ts +10 -0
- package/dist/types/api/hooks/audit.d.ts.map +1 -0
- package/dist/types/api/hooks/auth.d.ts +29 -0
- package/dist/types/api/hooks/auth.d.ts.map +1 -0
- package/dist/types/api/hooks/bitbucket.d.ts +8 -0
- package/dist/types/api/hooks/bitbucket.d.ts.map +1 -0
- package/dist/types/api/hooks/dictionary.d.ts +15 -0
- package/dist/types/api/hooks/dictionary.d.ts.map +1 -0
- package/dist/types/api/hooks/discussions.d.ts +8 -0
- package/dist/types/api/hooks/discussions.d.ts.map +1 -0
- package/dist/types/api/hooks/editor.d.ts +6 -0
- package/dist/types/api/hooks/editor.d.ts.map +1 -0
- package/dist/types/api/hooks/github.d.ts +12 -0
- package/dist/types/api/hooks/github.d.ts.map +1 -0
- package/dist/types/api/hooks/gitlab.d.ts +8 -0
- package/dist/types/api/hooks/gitlab.d.ts.map +1 -0
- package/dist/types/api/hooks/newsletter.d.ts +7 -0
- package/dist/types/api/hooks/newsletter.d.ts.map +1 -0
- package/dist/types/api/hooks/organization.d.ts +16 -0
- package/dist/types/api/hooks/organization.d.ts.map +1 -0
- package/dist/types/api/hooks/project.d.ts +23 -0
- package/dist/types/api/hooks/project.d.ts.map +1 -0
- package/dist/types/api/hooks/reviewer.d.ts +39 -0
- package/dist/types/api/hooks/reviewer.d.ts.map +1 -0
- package/dist/types/api/hooks/search.d.ts +7 -0
- package/dist/types/api/hooks/search.d.ts.map +1 -0
- package/dist/types/api/hooks/showcaseProject.d.ts +16 -0
- package/dist/types/api/hooks/showcaseProject.d.ts.map +1 -0
- package/dist/types/api/hooks/stripe.d.ts +33 -0
- package/dist/types/api/hooks/stripe.d.ts.map +1 -0
- package/dist/types/api/hooks/tag.d.ts +11 -0
- package/dist/types/api/hooks/tag.d.ts.map +1 -0
- package/dist/types/api/hooks/translate.d.ts +8 -0
- package/dist/types/api/hooks/translate.d.ts.map +1 -0
- package/dist/types/api/hooks/user.d.ts +13 -0
- package/dist/types/api/hooks/user.d.ts.map +1 -0
- package/dist/types/api/hooks/utils.d.ts +23 -0
- package/dist/types/api/hooks/utils.d.ts.map +1 -0
- package/dist/types/api/index.d.ts +25 -0
- package/dist/types/{hooks → api}/useAuth/useAuth.d.ts +1 -1
- package/dist/types/api/useAuth/useAuth.d.ts.map +1 -0
- package/dist/types/{hooks → api}/useAuth/useOAuth2.d.ts +1 -1
- package/dist/types/api/useAuth/useOAuth2.d.ts.map +1 -0
- package/dist/types/{hooks → api}/useAuth/useSession.d.ts +1 -1
- package/dist/types/api/useAuth/useSession.d.ts.map +1 -0
- package/dist/types/api/useIntlayerAPI.d.ts +297 -0
- package/dist/types/api/useIntlayerAPI.d.ts.map +1 -0
- package/dist/types/{hooks → api}/useUser/index.d.ts +1 -1
- package/dist/types/api/useUser/index.d.ts.map +1 -0
- package/dist/types/components/Badge/index.d.ts +3 -4
- package/dist/types/components/Badge/index.d.ts.map +1 -1
- package/dist/types/components/Breadcrumb/index.d.ts.map +1 -1
- package/dist/types/components/Button/Button.d.ts +5 -6
- package/dist/types/components/Button/Button.d.ts.map +1 -1
- package/dist/types/components/Carousel/index.d.ts.map +1 -1
- package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts +1 -1
- package/dist/types/components/Container/index.d.ts +4 -4
- package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts.map +1 -1
- package/dist/types/components/ExpandCollapse/ExpandCollapse.d.ts.map +1 -1
- package/dist/types/components/Input/Checkbox.d.ts +1 -2
- package/dist/types/components/Input/Checkbox.d.ts.map +1 -1
- package/dist/types/components/LanguageBackground/index.d.ts.map +1 -1
- package/dist/types/components/Link/Link.d.ts +2 -3
- package/dist/types/components/Link/Link.d.ts.map +1 -1
- package/dist/types/components/Loader/spinner.d.ts +1 -1
- package/dist/types/components/Pagination/Pagination.d.ts +1 -1
- package/dist/types/components/Pagination/Pagination.d.ts.map +1 -1
- package/dist/types/components/SwitchSelector/SwitchSelector.d.ts +2 -3
- package/dist/types/components/SwitchSelector/SwitchSelector.d.ts.map +1 -1
- package/dist/types/components/SwitchSelector/VerticalSwitchSelector.d.ts +1 -1
- package/dist/types/components/SwitchSelector/VerticalSwitchSelector.d.ts.map +1 -1
- package/dist/types/components/TabSelector/TabSelector.d.ts +2 -2
- package/dist/types/components/TabSelector/TabSelector.d.ts.map +1 -1
- package/dist/types/components/Tag/index.d.ts +2 -2
- package/dist/types/components/TextArea/ContentEditableTextArea.d.ts.map +1 -1
- package/dist/types/components/Toaster/Toast.d.ts +1 -1
- package/dist/types/hooks/index.d.ts +1 -7
- package/dist/types/providers/ReactQueryProvider.d.ts +1 -1
- package/dist/types/routes.d.ts +23 -1
- package/dist/types/routes.d.ts.map +1 -1
- package/package.json +35 -30
- package/tailwind.css +0 -19
- package/dist/esm/hooks/reactQuery.mjs +0 -1187
- package/dist/esm/hooks/reactQuery.mjs.map +0 -1
- package/dist/esm/hooks/useAuth/useAuth.mjs.map +0 -1
- package/dist/esm/hooks/useAuth/useOAuth2.mjs.map +0 -1
- package/dist/esm/hooks/useAuth/useSession.mjs.map +0 -1
- package/dist/esm/hooks/useIntlayerAPI.mjs +0 -22
- package/dist/esm/hooks/useIntlayerAPI.mjs.map +0 -1
- package/dist/esm/hooks/useUser/index.mjs.map +0 -1
- package/dist/types/hooks/reactQuery.d.ts +0 -188
- package/dist/types/hooks/reactQuery.d.ts.map +0 -1
- package/dist/types/hooks/useAuth/useAuth.d.ts.map +0 -1
- package/dist/types/hooks/useAuth/useOAuth2.d.ts.map +0 -1
- package/dist/types/hooks/useAuth/useSession.d.ts.map +0 -1
- package/dist/types/hooks/useIntlayerAPI.d.ts +0 -14
- package/dist/types/hooks/useIntlayerAPI.d.ts.map +0 -1
- package/dist/types/hooks/useUser/index.d.ts.map +0 -1
- /package/dist/esm/{hooks → api}/useAuth/index.mjs +0 -0
- /package/dist/types/{hooks → api}/useAuth/index.d.ts +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { getAuthAPI } from "../libs/auth.mjs";
|
|
4
|
+
import { useAuth } from "./useAuth/useAuth.mjs";
|
|
5
|
+
import { getIntlayerAPI } from "@intlayer/api";
|
|
6
|
+
import { getAiAPI } from "@intlayer/api/ai";
|
|
7
|
+
import { getAuditAPI } from "@intlayer/api/audit";
|
|
8
|
+
import { getBitbucketAPI } from "@intlayer/api/bitbucket";
|
|
9
|
+
import { getDictionaryAPI } from "@intlayer/api/dictionary";
|
|
10
|
+
import { getEditorAPI } from "@intlayer/api/editor";
|
|
11
|
+
import { getGithubAPI } from "@intlayer/api/github";
|
|
12
|
+
import { getGitlabAPI } from "@intlayer/api/gitlab";
|
|
13
|
+
import { getNewsletterAPI } from "@intlayer/api/newsletter";
|
|
14
|
+
import { getOAuthAPI as getOAuthAPI$1 } from "@intlayer/api/oAuth";
|
|
15
|
+
import { getOrganizationAPI } from "@intlayer/api/organization";
|
|
16
|
+
import { getProjectAPI } from "@intlayer/api/project";
|
|
17
|
+
import { getReviewerAPI } from "@intlayer/api/reviewer";
|
|
18
|
+
import { getSearchAPI } from "@intlayer/api/search";
|
|
19
|
+
import { getShowcaseProjectAPI } from "@intlayer/api/showcaseProject";
|
|
20
|
+
import { getStripeAPI } from "@intlayer/api/stripe";
|
|
21
|
+
import { getTagAPI } from "@intlayer/api/tag";
|
|
22
|
+
import { getTranslateAPI } from "@intlayer/api/translate";
|
|
23
|
+
import { getUserAPI } from "@intlayer/api/user";
|
|
24
|
+
import { useConfiguration } from "@intlayer/editor-react";
|
|
25
|
+
|
|
26
|
+
//#region src/api/useIntlayerAPI.ts
|
|
27
|
+
const useIntlayerOAuthOptions = (props) => {
|
|
28
|
+
const configuration = useConfiguration();
|
|
29
|
+
const { oAuth2AccessToken } = useAuth();
|
|
30
|
+
const options = {
|
|
31
|
+
...oAuth2AccessToken && { headers: { Authorization: `Bearer ${oAuth2AccessToken.accessToken}` } },
|
|
32
|
+
...props?.options ?? {}
|
|
33
|
+
};
|
|
34
|
+
const rawConfig = props?.intlayerConfiguration ?? configuration;
|
|
35
|
+
return {
|
|
36
|
+
options,
|
|
37
|
+
resolvedConfig: rawConfig?.editor ? rawConfig : void 0
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const useIntlayerOAuth = (props) => {
|
|
41
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
42
|
+
return getIntlayerAPI(options, resolvedConfig);
|
|
43
|
+
};
|
|
44
|
+
const useIntlayerAuth = (props) => {
|
|
45
|
+
const configuration = useConfiguration();
|
|
46
|
+
return getAuthAPI(props?.intlayerConfiguration ?? configuration);
|
|
47
|
+
};
|
|
48
|
+
const useOrganizationAPI = (props) => {
|
|
49
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
50
|
+
return getOrganizationAPI(options, resolvedConfig);
|
|
51
|
+
};
|
|
52
|
+
const useProjectAPI = (props) => {
|
|
53
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
54
|
+
return getProjectAPI(options, resolvedConfig);
|
|
55
|
+
};
|
|
56
|
+
const useUserAPI = (props) => {
|
|
57
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
58
|
+
return getUserAPI(options, resolvedConfig);
|
|
59
|
+
};
|
|
60
|
+
const useOAuthAPI = (props) => {
|
|
61
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
62
|
+
return getOAuthAPI$1(options, resolvedConfig);
|
|
63
|
+
};
|
|
64
|
+
const useDictionaryAPI = (props) => {
|
|
65
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
66
|
+
return getDictionaryAPI(options, resolvedConfig);
|
|
67
|
+
};
|
|
68
|
+
const useStripeAPI = (props) => {
|
|
69
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
70
|
+
return getStripeAPI(options, resolvedConfig);
|
|
71
|
+
};
|
|
72
|
+
const useAiAPI = (props) => {
|
|
73
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
74
|
+
return getAiAPI(options, resolvedConfig);
|
|
75
|
+
};
|
|
76
|
+
const useAuditAPI = (props) => {
|
|
77
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
78
|
+
return getAuditAPI(options, resolvedConfig);
|
|
79
|
+
};
|
|
80
|
+
const useTagAPI = (props) => {
|
|
81
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
82
|
+
return getTagAPI(options, resolvedConfig);
|
|
83
|
+
};
|
|
84
|
+
const useSearchAPI = (props) => {
|
|
85
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
86
|
+
return getSearchAPI(options, resolvedConfig);
|
|
87
|
+
};
|
|
88
|
+
const useEditorAPI = (props) => {
|
|
89
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
90
|
+
return getEditorAPI(options, resolvedConfig);
|
|
91
|
+
};
|
|
92
|
+
const useNewsletterAPI = (props) => {
|
|
93
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
94
|
+
return getNewsletterAPI(options, resolvedConfig);
|
|
95
|
+
};
|
|
96
|
+
const useGithubAPI = (props) => {
|
|
97
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
98
|
+
return getGithubAPI(options, resolvedConfig);
|
|
99
|
+
};
|
|
100
|
+
const useGitlabAPI = (props) => {
|
|
101
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
102
|
+
return getGitlabAPI(options, resolvedConfig);
|
|
103
|
+
};
|
|
104
|
+
const useBitbucketAPI = (props) => {
|
|
105
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
106
|
+
return getBitbucketAPI(options, resolvedConfig);
|
|
107
|
+
};
|
|
108
|
+
const useShowcaseProjectAPI = (props) => {
|
|
109
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
110
|
+
return getShowcaseProjectAPI(options, resolvedConfig);
|
|
111
|
+
};
|
|
112
|
+
const useTranslateAPI = (props) => {
|
|
113
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
114
|
+
return getTranslateAPI(options, resolvedConfig);
|
|
115
|
+
};
|
|
116
|
+
const useReviewerAPI = (props) => {
|
|
117
|
+
const { options, resolvedConfig } = useIntlayerOAuthOptions(props);
|
|
118
|
+
return getReviewerAPI(options, resolvedConfig);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
//#endregion
|
|
122
|
+
export { useAiAPI, useAuditAPI, useBitbucketAPI, useDictionaryAPI, useEditorAPI, useGithubAPI, useGitlabAPI, useIntlayerAuth, useIntlayerOAuth, useIntlayerOAuthOptions, useNewsletterAPI, useOAuthAPI, useOrganizationAPI, useProjectAPI, useReviewerAPI, useSearchAPI, useShowcaseProjectAPI, useStripeAPI, useTagAPI, useTranslateAPI, useUserAPI };
|
|
123
|
+
//# sourceMappingURL=useIntlayerAPI.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIntlayerAPI.mjs","names":["getOAuthAPI"],"sources":["../../../src/api/useIntlayerAPI.ts"],"sourcesContent":["'use client';\n\nimport {\n type FetcherOptions,\n getIntlayerAPI,\n type IntlayerAPI,\n} from '@intlayer/api';\nimport { getAiAPI } from '@intlayer/api/ai';\nimport { getAuditAPI } from '@intlayer/api/audit';\nimport { getBitbucketAPI } from '@intlayer/api/bitbucket';\nimport { getDictionaryAPI } from '@intlayer/api/dictionary';\nimport { getEditorAPI } from '@intlayer/api/editor';\nimport { getGithubAPI } from '@intlayer/api/github';\nimport { getGitlabAPI } from '@intlayer/api/gitlab';\nimport { getNewsletterAPI } from '@intlayer/api/newsletter';\nimport { getOAuthAPI } from '@intlayer/api/oAuth';\nimport { getOrganizationAPI } from '@intlayer/api/organization';\nimport { getProjectAPI } from '@intlayer/api/project';\nimport { getReviewerAPI } from '@intlayer/api/reviewer';\nimport { getSearchAPI } from '@intlayer/api/search';\nimport { getShowcaseProjectAPI } from '@intlayer/api/showcaseProject';\nimport { getStripeAPI } from '@intlayer/api/stripe';\nimport { getTagAPI } from '@intlayer/api/tag';\nimport { getTranslateAPI } from '@intlayer/api/translate';\nimport { getUserAPI } from '@intlayer/api/user';\nimport { useConfiguration } from '@intlayer/editor-react';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { type AuthAPI, getAuthAPI } from '../libs/auth';\nimport { useAuth } from './useAuth';\n\nexport type UseIntlayerAuthProps = {\n options?: FetcherOptions;\n intlayerConfiguration?: IntlayerConfig;\n};\n\nexport const useIntlayerOAuthOptions = (props?: UseIntlayerAuthProps) => {\n const configuration = useConfiguration();\n const { oAuth2AccessToken } = useAuth();\n\n const options = {\n ...(oAuth2AccessToken && {\n headers: {\n Authorization: `Bearer ${oAuth2AccessToken.accessToken}`,\n },\n }),\n ...(props?.options ?? {}),\n };\n\n const rawConfig = props?.intlayerConfiguration ?? configuration;\n // Only use the config if it's fully populated; an empty object (e.g. while\n // the session is still loading) has no `editor` key and would crash API\n // getters that do `intlayerConfig.editor.backendURL`.\n const resolvedConfig = rawConfig?.editor ? rawConfig : undefined;\n\n return { options, resolvedConfig };\n};\n\nexport const useIntlayerOAuth = (props?: UseIntlayerAuthProps): IntlayerAPI => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getIntlayerAPI(options, resolvedConfig);\n};\n\nexport const useIntlayerAuth = (props?: UseIntlayerAuthProps): AuthAPI => {\n const configuration = useConfiguration();\n\n return getAuthAPI(props?.intlayerConfiguration ?? configuration);\n};\n\nexport const useOrganizationAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getOrganizationAPI(options, resolvedConfig);\n};\n\nexport const useProjectAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getProjectAPI(options, resolvedConfig);\n};\n\nexport const useUserAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getUserAPI(options, resolvedConfig);\n};\n\nexport const useOAuthAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getOAuthAPI(options, resolvedConfig);\n};\n\nexport const useDictionaryAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getDictionaryAPI(options, resolvedConfig);\n};\n\nexport const useStripeAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getStripeAPI(options, resolvedConfig);\n};\n\nexport const useAiAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getAiAPI(options, resolvedConfig);\n};\n\nexport const useAuditAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getAuditAPI(options, resolvedConfig);\n};\n\nexport const useTagAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getTagAPI(options, resolvedConfig);\n};\n\nexport const useSearchAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getSearchAPI(options, resolvedConfig);\n};\n\nexport const useEditorAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getEditorAPI(options, resolvedConfig);\n};\n\nexport const useNewsletterAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getNewsletterAPI(options, resolvedConfig);\n};\n\nexport const useGithubAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getGithubAPI(options, resolvedConfig);\n};\n\nexport const useGitlabAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getGitlabAPI(options, resolvedConfig);\n};\n\nexport const useBitbucketAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getBitbucketAPI(options, resolvedConfig);\n};\n\nexport const useShowcaseProjectAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getShowcaseProjectAPI(options, resolvedConfig);\n};\n\nexport const useTranslateAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getTranslateAPI(options, resolvedConfig);\n};\n\nexport const useReviewerAPI = (props?: UseIntlayerAuthProps) => {\n const { options, resolvedConfig } = useIntlayerOAuthOptions(props);\n return getReviewerAPI(options, resolvedConfig);\n};\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useAuth } from "../useAuth/useAuth.mjs";
|
|
4
|
-
import { useLogout } from "../
|
|
4
|
+
import { useLogout } from "../hooks/auth.mjs";
|
|
5
5
|
|
|
6
|
-
//#region src/
|
|
6
|
+
//#region src/api/useUser/index.ts
|
|
7
7
|
const useUser = () => {
|
|
8
8
|
const { session, revalidateSession, setSession } = useAuth();
|
|
9
9
|
const { mutate: logout } = useLogout();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/api/useUser/index.ts"],"sourcesContent":["'use client';\n\nimport { useLogout } from '../hooks/auth';\nimport { useAuth } from '../useAuth';\n\nexport const useUser = () => {\n const { session, revalidateSession, setSession } = useAuth();\n const { mutate: logout } = useLogout();\n\n const status = session?.user ? 'authenticated' : 'unauthenticated';\n\n const isAuthenticated = status === 'authenticated';\n const isUnauthenticated = status === 'unauthenticated';\n\n const user = session?.user;\n\n const logoutHandle = async () => {\n setSession(null);\n logout();\n };\n\n return {\n isAuthenticated,\n isUnauthenticated,\n user,\n revalidateSession,\n logout: logoutHandle,\n };\n};\n"],"mappings":";;;;;;AAKA,MAAa,gBAAgB;CAC3B,MAAM,EAAE,SAAS,mBAAmB,eAAe,QAAQ;CAC3D,MAAM,EAAE,QAAQ,WAAW,UAAU;CAErC,MAAM,SAAS,SAAS,OAAO,kBAAkB;CAEjD,MAAM,kBAAkB,WAAW;CACnC,MAAM,oBAAoB,WAAW;CAErC,MAAM,OAAO,SAAS;CAEtB,MAAM,eAAe,YAAY;EAC/B,WAAW,IAAI;EACf,OAAO;CACT;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA,QAAQ;CACV;AACF"}
|
|
@@ -10,7 +10,6 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
let BadgeColor = /* @__PURE__ */ function(BadgeColor) {
|
|
11
11
|
BadgeColor["PRIMARY"] = "primary";
|
|
12
12
|
BadgeColor["SECONDARY"] = "secondary";
|
|
13
|
-
BadgeColor["DESTRUCTIVE"] = "destructive";
|
|
14
13
|
BadgeColor["SUCCESS"] = "success";
|
|
15
14
|
BadgeColor["ERROR"] = "error";
|
|
16
15
|
BadgeColor["NEUTRAL"] = "neutral";
|
|
@@ -50,7 +49,6 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
|
|
|
50
49
|
color: {
|
|
51
50
|
[`primary`]: "border-primary bg-primary text-primary hover:bg-primary-500",
|
|
52
51
|
[`secondary`]: "border-secondary bg-secondary text-secondary hover:bg-secondary-300",
|
|
53
|
-
[`destructive`]: "border-destructive bg-destructive text-destructive hover:bg-destructive-500",
|
|
54
52
|
[`success`]: "border-success bg-success text-success hover:bg-success-500",
|
|
55
53
|
[`error`]: "border-error bg-error text-error hover:bg-error-500",
|
|
56
54
|
[`neutral`]: "border-neutral bg-neutral text-neutral hover:bg-neutral-600",
|
|
@@ -89,7 +87,7 @@ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-
|
|
|
89
87
|
* <Badge>New</Badge>
|
|
90
88
|
*
|
|
91
89
|
* // Colored badge
|
|
92
|
-
* <Badge color={BadgeColor.
|
|
90
|
+
* <Badge color={BadgeColor.ERROR}>Error</Badge>
|
|
93
91
|
*
|
|
94
92
|
* // Clickable badge
|
|
95
93
|
* <Badge clickable onClick={() => console.log('clicked')}>
|
|
@@ -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 enum BadgeColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n
|
|
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 enum BadgeColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n SUCCESS = 'success',\n ERROR = 'error',\n NEUTRAL = 'neutral',\n LIGHT = 'light',\n DARK = 'dark',\n TEXT = 'text',\n CUSTOM = 'custom',\n}\n\n/**\n * Badge visual variants enum\n * @description Defines the available visual styles for the badge component\n */\nexport enum BadgeVariant {\n DEFAULT = 'default',\n NONE = 'none',\n OUTLINE = 'outline',\n HOVERABLE = 'hoverable',\n}\n\n/**\n * Badge size variants enum\n * @description Defines the available sizes for the badge component\n */\nexport enum BadgeSize {\n SMALL = 'sm',\n MEDIUM = 'md',\n LARGE = 'lg',\n}\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 [`${BadgeColor.PRIMARY}`]:\n 'border-primary bg-primary text-primary hover:bg-primary-500',\n [`${BadgeColor.SECONDARY}`]:\n 'border-secondary bg-secondary text-secondary hover:bg-secondary-300',\n [`${BadgeColor.SUCCESS}`]:\n 'border-success bg-success text-success hover:bg-success-500',\n [`${BadgeColor.ERROR}`]:\n 'border-error bg-error text-error hover:bg-error-500',\n [`${BadgeColor.NEUTRAL}`]:\n 'border-neutral bg-neutral text-neutral hover:bg-neutral-600',\n [`${BadgeColor.LIGHT}`]:\n 'border-white bg-white text-white hover:bg-neutral-500',\n [`${BadgeColor.DARK}`]:\n 'border-neutral-800 bg-neutral-800 text-neutral-800 hover:bg-neutral-900',\n [`${BadgeColor.TEXT}`]:\n 'border-text bg-text text-text hover:opacity-80',\n [`${BadgeColor.CUSTOM}`]: '',\n },\n variant: {\n [`${BadgeVariant.DEFAULT}`]: 'rounded-lg text-text-opposite',\n [`${BadgeVariant.NONE}`]:\n 'border-none bg-opacity-0 text-inherit hover:bg-opacity-0',\n [`${BadgeVariant.OUTLINE}`]:\n 'rounded-lg border-[1.3px] bg-opacity-0 hover:bg-opacity-30',\n [`${BadgeVariant.HOVERABLE}`]:\n 'rounded-lg border-none bg-opacity-0 transition hover:bg-opacity-10',\n },\n size: {\n [`${BadgeSize.SMALL}`]: 'px-2 py-0.5 text-xs',\n [`${BadgeSize.MEDIUM}`]: 'px-2.5 py-0.5 text-xs',\n [`${BadgeSize.LARGE}`]: 'px-3 py-1 text-sm',\n },\n },\n defaultVariants: {\n variant: BadgeVariant.DEFAULT,\n color: BadgeColor.PRIMARY,\n size: BadgeSize.MEDIUM,\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={BadgeColor.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 = BadgeVariant.DEFAULT,\n color = BadgeColor.PRIMARY,\n size = BadgeSize.MEDIUM,\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":";;;;;;;;;AAQA,IAAY,aAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;;AAMA,IAAY,eAAL;CACL;CACA;CACA;CACA;;AACF;;;;;AAMA,IAAY,YAAL;CACL;CACA;CACA;;AACF;;;;;AAMA,MAAa,gBAAgB,IAC3B,0KACA;CACE,UAAU;EACR,OAAO;IACJ,YACC;IACD,cACC;IACD,YACC;IACD,UACC;IACD,YACC;IACD,UACC;IACD,SACC;IACD,SACC;IACD,WAAyB;EAC5B;EACA,SAAS;IACN,YAA4B;IAC5B,SACC;IACD,YACC;IACD,cACC;EACJ;EACA,MAAM;IACH,OAAuB;IACvB,OAAwB;IACxB,OAAuB;EAC1B;CACF;CACA,iBAAiB;EACf;EACA;EACA;CACF;AACF,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,MAAa,SAA+B,EAC1C,WACA,qBACA,mBACA,aACA,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 +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, ButtonVariant } from '../Button';\nimport { Link, 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 [LinkColor.PRIMARY]: 'text-primary',\n [LinkColor.SECONDARY]: 'text-secondary',\n [LinkColor.DESTRUCTIVE]: 'text-destructive',\n [LinkColor.NEUTRAL]: 'text-neutral',\n [LinkColor.LIGHT]: 'text-white',\n [LinkColor.DARK]: 'text-neutral-800',\n [LinkColor.TEXT]: 'text-text',\n [LinkColor.TEXT_INVERSE]: 'text-text-opposite',\n [LinkColor.ERROR]: 'text-error',\n [LinkColor.SUCCESS]: 'text-success',\n [LinkColor.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 text-sm', {\n variants: {\n size: {\n small: 'gap-1 text-xs',\n medium: 'gap-2 text-sm',\n large: 'gap-3 text-base',\n },\n spacing: {\n compact: 'gap-1',\n normal: 'gap-2',\n loose: 'gap-4',\n },\n },\n defaultVariants: {\n size: 'medium',\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=\"sm\"\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={ButtonVariant.LINK}\n label={`${linkLabel} ${text}`}\n color={color}\n itemProp=\"item\"\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 LinkColor.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=\"medium\"\n * ariaLabel=\"Product navigation\"\n * />\n * ```\n */\nexport const Breadcrumb: FC<BreadcrumbProps> = ({\n links,\n className,\n color = LinkColor.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, ButtonVariant } from '../Button';\nimport { Link, 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 [LinkColor.PRIMARY]: 'text-primary',\n [LinkColor.SECONDARY]: 'text-secondary',\n [LinkColor.NEUTRAL]: 'text-neutral',\n [LinkColor.LIGHT]: 'text-white',\n [LinkColor.DARK]: 'text-neutral-800',\n [LinkColor.TEXT]: 'text-text',\n [LinkColor.TEXT_INVERSE]: 'text-text-opposite',\n [LinkColor.ERROR]: 'text-error',\n [LinkColor.SUCCESS]: 'text-success',\n [LinkColor.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 text-sm', {\n variants: {\n size: {\n small: 'gap-1 text-xs',\n medium: 'gap-2 text-sm',\n large: 'gap-3 text-base',\n },\n spacing: {\n compact: 'gap-1',\n normal: 'gap-2',\n loose: 'gap-4',\n },\n },\n defaultVariants: {\n size: 'medium',\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=\"sm\"\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={ButtonVariant.LINK}\n label={`${linkLabel} ${text}`}\n color={color}\n itemProp=\"item\"\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 LinkColor.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=\"medium\"\n * ariaLabel=\"Product navigation\"\n * />\n * ```\n */\nexport const Breadcrumb: FC<BreadcrumbProps> = ({\n links,\n className,\n color = LinkColor.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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"}
|
|
@@ -54,7 +54,6 @@ let ButtonVariant = /* @__PURE__ */ function(ButtonVariant) {
|
|
|
54
54
|
let ButtonColor = /* @__PURE__ */ function(ButtonColor) {
|
|
55
55
|
ButtonColor["PRIMARY"] = "primary";
|
|
56
56
|
ButtonColor["SECONDARY"] = "secondary";
|
|
57
|
-
ButtonColor["DESTRUCTIVE"] = "destructive";
|
|
58
57
|
ButtonColor["NEUTRAL"] = "neutral";
|
|
59
58
|
ButtonColor["LIGHT"] = "light";
|
|
60
59
|
ButtonColor["DARK"] = "dark";
|
|
@@ -95,7 +94,6 @@ const buttonVariants = cva("relative inline-flex cursor-pointer items-center jus
|
|
|
95
94
|
color: {
|
|
96
95
|
[`primary`]: "hover-primary-500/20 text-primary ring-primary-500/20 *:text-text-light",
|
|
97
96
|
[`secondary`]: "hover-secondary-500/20 text-secondary ring-secondary-500/20 *:text-text-light",
|
|
98
|
-
[`destructive`]: "hover-destructive-500/20 text-destructive ring-destructive-500/20 *:text-text-light",
|
|
99
97
|
[`neutral`]: "text-neutral ring-neutral-500/5 *:text-text-light",
|
|
100
98
|
[`card`]: "hover-card-500/20 text-card ring-card-500/20 *:text-text-light",
|
|
101
99
|
[`light`]: "hover-white-500/20 text-white ring-white/20 *:text-text-light",
|
|
@@ -172,7 +170,7 @@ const buttonVariants = cva("relative inline-flex cursor-pointer items-center jus
|
|
|
172
170
|
defaultVariants: {
|
|
173
171
|
variant: `default`,
|
|
174
172
|
size: `md`,
|
|
175
|
-
color: `
|
|
173
|
+
color: `text`,
|
|
176
174
|
roundedSize: `${"md"}`,
|
|
177
175
|
textAlign: `center`,
|
|
178
176
|
isFullWidth: false
|
|
@@ -205,10 +203,10 @@ const buttonVariants = cva("relative inline-flex cursor-pointer items-center jus
|
|
|
205
203
|
* Save
|
|
206
204
|
* </Button>
|
|
207
205
|
*
|
|
208
|
-
* //
|
|
206
|
+
* // Error action button
|
|
209
207
|
* <Button
|
|
210
208
|
* variant={`${ButtonVariant.OUTLINE}`}
|
|
211
|
-
* color={ButtonColor.
|
|
209
|
+
* color={ButtonColor.ERROR}
|
|
212
210
|
* label="Delete item permanently"
|
|
213
211
|
* aria-describedby="delete-warning"
|
|
214
212
|
* >
|
|
@@ -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 { ContainerRoundedSize as ButtonRoundedSize } from '../Container';\nimport { Loader } from '../Loader';\n\n/**\n * Button size variants for different use cases\n */\nexport enum ButtonSize {\n XS = 'xs',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n ICON_SM = 'icon-sm',\n ICON_MD = 'icon-md',\n ICON_LG = 'icon-lg',\n ICON_XL = 'icon-xl',\n}\n\nconst buttonIconVariants = cva('flex-none shrink-0', {\n variants: {\n size: {\n [`${ButtonSize.XS}`]: 'size-2',\n [`${ButtonSize.SM}`]: 'size-3',\n [`${ButtonSize.MD}`]: 'size-4',\n [`${ButtonSize.LG}`]: 'size-5',\n [`${ButtonSize.XL}`]: 'size-6',\n [`${ButtonSize.ICON_SM}`]: 'size-3',\n [`${ButtonSize.ICON_MD}`]: 'size-4',\n [`${ButtonSize.ICON_LG}`]: 'size-5',\n [`${ButtonSize.ICON_XL}`]: 'size-6',\n },\n },\n defaultVariants: {\n size: ButtonSize.MD,\n },\n});\n\n/**\n * Button visual style variants\n */\nexport enum ButtonVariant {\n DEFAULT = 'default',\n NONE = 'none',\n OUTLINE = 'outline',\n LINK = 'link',\n INVISIBLE_LINK = 'invisible-link',\n HOVERABLE = 'hoverable',\n FADE = 'fade',\n INPUT = 'input',\n}\n\n/**\n * Button color themes that work with the design system\n */\nexport enum ButtonColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n DESTRUCTIVE = 'destructive',\n NEUTRAL = 'neutral',\n LIGHT = 'light',\n DARK = 'dark',\n TEXT = 'text',\n CARD = 'card',\n TEXT_INVERSE = 'text-inverse',\n CURRENT = 'current',\n ERROR = 'error',\n SUCCESS = 'success',\n CUSTOM = 'custom',\n}\n\n/**\n * Text alignment options for button content\n */\nexport enum ButtonTextAlign {\n LEFT = 'left',\n CENTER = 'center',\n RIGHT = 'right',\n}\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 [`${ButtonSize.XS}`]: 'min-h-7 px-3 text-xs max-md:py-1',\n [`${ButtonSize.SM}`]: 'min-h-7 px-3 text-xs max-md:py-1',\n [`${ButtonSize.MD}`]: 'min-h-8 px-6 text-sm max-md:py-2',\n [`${ButtonSize.LG}`]: 'min-h-10 px-8 text-lg max-md:py-3',\n [`${ButtonSize.XL}`]: 'min-h-11 px-10 text-xl max-md:py-4',\n [`${ButtonSize.ICON_SM}`]: 'p-1.5',\n [`${ButtonSize.ICON_MD}`]: 'p-1.5',\n [`${ButtonSize.ICON_LG}`]: 'p-1.5',\n [`${ButtonSize.ICON_XL}`]: 'p-3',\n },\n color: {\n [`${ButtonColor.PRIMARY}`]:\n 'hover-primary-500/20 text-primary ring-primary-500/20 *:text-text-light',\n [`${ButtonColor.SECONDARY}`]:\n 'hover-secondary-500/20 text-secondary ring-secondary-500/20 *:text-text-light',\n [`${ButtonColor.DESTRUCTIVE}`]:\n 'hover-destructive-500/20 text-destructive ring-destructive-500/20 *:text-text-light',\n [`${ButtonColor.NEUTRAL}`]:\n 'text-neutral ring-neutral-500/5 *:text-text-light',\n [`${ButtonColor.CARD}`]:\n 'hover-card-500/20 text-card ring-card-500/20 *:text-text-light',\n [`${ButtonColor.LIGHT}`]:\n 'hover-white-500/20 text-white ring-white/20 *:text-text-light',\n [`${ButtonColor.DARK}`]:\n 'text-neutral-800 ring-text-light/50 *:text-text-light',\n [`${ButtonColor.TEXT}`]: 'text-text ring-text/20 *:text-text-opposite',\n [`${ButtonColor.CURRENT}`]:\n 'hover-current-500/10 text-current ring-current/10 *:text-text-light',\n [`${ButtonColor.TEXT_INVERSE}`]:\n 'text-text-opposite ring-text-opposite/20 *:text-text',\n [`${ButtonColor.ERROR}`]:\n 'hover-error-500/20 text-error ring-error/20 *:text-text-light',\n [`${ButtonColor.SUCCESS}`]:\n 'hover-success-500/20 text-success ring-success/20 *:text-text-light',\n [`${ButtonColor.CUSTOM}`]: '',\n },\n roundedSize: {\n [`${ButtonRoundedSize.NONE}`]: 'rounded-none',\n [`${ButtonRoundedSize.SM}`]:\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n [`${ButtonRoundedSize.MD}`]:\n 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n [`${ButtonRoundedSize.LG}`]:\n 'rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl',\n [`${ButtonRoundedSize.XL}`]:\n 'rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl',\n [`${ButtonRoundedSize['2xl']}`]:\n 'rounded-4xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[2.5rem]',\n [`${ButtonRoundedSize['3xl']}`]:\n 'rounded-[2.5rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[3rem]',\n [`${ButtonRoundedSize['4xl']}`]:\n 'rounded-[3rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[4rem]',\n [`${ButtonRoundedSize['5xl']}`]:\n 'rounded-[4rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[5rem]',\n [`${ButtonRoundedSize.FULL}`]: 'rounded-full',\n },\n variant: {\n [`${ButtonVariant.DEFAULT}`]: [\n 'bg-current',\n 'hover:bg-current/90',\n 'hover:ring-5',\n 'aria-selected:ring-5',\n ],\n\n [`${ButtonVariant.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 [`${ButtonVariant.NONE}`]:\n 'border-none bg-current/0 text-inherit hover:bg-current/0',\n\n [`${ButtonVariant.LINK}`]:\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent hover:underline',\n\n [`${ButtonVariant.INVISIBLE_LINK}`]:\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent',\n\n [`${ButtonVariant.HOVERABLE}`]:\n 'rounded-lg border-none bg-current/0 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5',\n\n [`${ButtonVariant.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 [`${ButtonVariant.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 [`${ButtonTextAlign.LEFT}`]: 'justify-start text-left',\n [`${ButtonTextAlign.CENTER}`]: 'justify-center text-center',\n [`${ButtonTextAlign.RIGHT}`]: 'justify-end text-right',\n },\n\n isFullWidth: {\n true: 'w-full',\n false: '',\n },\n },\n defaultVariants: {\n variant: `${ButtonVariant.DEFAULT}`,\n size: `${ButtonSize.MD}`,\n color: `${ButtonColor.CUSTOM}`,\n roundedSize: `${ButtonRoundedSize.MD}`,\n textAlign: `${ButtonTextAlign.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 * // Destructive action button\n * <Button\n * variant={`${ButtonVariant.OUTLINE}`}\n * color={ButtonColor.DESTRUCTIVE}\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 =\n variant === `${ButtonVariant.LINK}` ||\n variant === `${ButtonVariant.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 === ButtonSize.ICON_SM ||\n size === ButtonSize.ICON_MD ||\n size === ButtonSize.ICON_LG ||\n size === ButtonSize.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:\n textAlign ??\n (IconRight ? ButtonTextAlign.LEFT : ButtonTextAlign.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 === ButtonSize.SM && 'w-3',\n isLoading && size === ButtonSize.MD && 'w-4',\n isLoading && size === ButtonSize.LG && 'w-6',\n isLoading && size === ButtonSize.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":";;;;;;;;;;AAUA,IAAY,aAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;AAEA,MAAM,qBAAqB,IAAI,sBAAsB;CACnD,UAAU,EACR,MAAM;GACH,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,YAA0B;GAC1B,YAA0B;GAC1B,YAA0B;GAC1B,YAA0B;CAC7B,EACF;CACA,iBAAiB,EACf,WACF;AACF,CAAC;;;;AAKD,IAAY,gBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;AAKA,IAAY,cAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;AAKA,IAAY,kBAAL;CACL;CACA;CACA;;AACF;;;;AAKA,MAAa,iBAAiB,IAC5B,8LACA;CACE,UAAU;EACR,MAAM;IACH,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,YAA0B;IAC1B,YAA0B;IAC1B,YAA0B;IAC1B,YAA0B;EAC7B;EACA,OAAO;IACJ,YACC;IACD,cACC;IACD,gBACC;IACD,YACC;IACD,SACC;IACD,UACC;IACD,SACC;IACD,SAAwB;IACxB,YACC;IACD,iBACC;IACD,UACC;IACD,YACC;IACD,WAA0B;EAC7B;EACA,aAAa;IACV,cAA8B;IAC9B,YACC;IACD,YACC;IACD,YACC;IACD,YACC;IACD,aACC;IACD,aACC;IACD,aACC;IACD,aACC;IACD,cAA8B;EACjC;EACA,SAAS;IACN,YAA6B;IAC5B;IACA;IACA;IACA;GACF;IAEC,YAA6B;IAC5B;IACA;IACA;IACA;GACF;IAEC,SACC;IAED,SACC;IAED,mBACC;IAED,cACC;IAED,SAA0B;IACzB;IACA;IACA;GACF;IACC,UAA2B;IAE1B;IACA;IACA;IACA;IACA;IAEA;IACA;IACA;IAGA;IACA;IACA;IACA;IAGA;IAGA;IAGA;GACF;EACF;EAEA,WAAW;IACR,SAA4B;IAC5B,WAA8B;IAC9B,UAA6B;EAChC;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,SACJ,YAAY,UACZ,YAAY;CACd,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,sBACA,sBACA,sBACA;CAEF,OACE,qBAAC,UAAD;EACE,UAAU,aAAa;EACvB,MAAM,SAAS,SAAS;EAClB;EACN,WAAW,eAAe;GACxB;GACA;GACA;GACA;GACA;GACA,WACE,cACC;GACH;EACF,CAAC;EACD,GAAI;EACJ,GAAI;YAhBN;GAkBG,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,iBAA0B,OACvC,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,KACzC;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 { ContainerRoundedSize as ButtonRoundedSize } from '../Container';\nimport { Loader } from '../Loader';\n\n/**\n * Button size variants for different use cases\n */\nexport enum ButtonSize {\n XS = 'xs',\n SM = 'sm',\n MD = 'md',\n LG = 'lg',\n XL = 'xl',\n ICON_SM = 'icon-sm',\n ICON_MD = 'icon-md',\n ICON_LG = 'icon-lg',\n ICON_XL = 'icon-xl',\n}\n\nconst buttonIconVariants = cva('flex-none shrink-0', {\n variants: {\n size: {\n [`${ButtonSize.XS}`]: 'size-2',\n [`${ButtonSize.SM}`]: 'size-3',\n [`${ButtonSize.MD}`]: 'size-4',\n [`${ButtonSize.LG}`]: 'size-5',\n [`${ButtonSize.XL}`]: 'size-6',\n [`${ButtonSize.ICON_SM}`]: 'size-3',\n [`${ButtonSize.ICON_MD}`]: 'size-4',\n [`${ButtonSize.ICON_LG}`]: 'size-5',\n [`${ButtonSize.ICON_XL}`]: 'size-6',\n },\n },\n defaultVariants: {\n size: ButtonSize.MD,\n },\n});\n\n/**\n * Button visual style variants\n */\nexport enum ButtonVariant {\n DEFAULT = 'default',\n NONE = 'none',\n OUTLINE = 'outline',\n LINK = 'link',\n INVISIBLE_LINK = 'invisible-link',\n HOVERABLE = 'hoverable',\n FADE = 'fade',\n INPUT = 'input',\n}\n\n/**\n * Button color themes that work with the design system\n */\nexport enum ButtonColor {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n NEUTRAL = 'neutral',\n LIGHT = 'light',\n DARK = 'dark',\n TEXT = 'text',\n CARD = 'card',\n TEXT_INVERSE = 'text-inverse',\n CURRENT = 'current',\n ERROR = 'error',\n SUCCESS = 'success',\n CUSTOM = 'custom',\n}\n\n/**\n * Text alignment options for button content\n */\nexport enum ButtonTextAlign {\n LEFT = 'left',\n CENTER = 'center',\n RIGHT = 'right',\n}\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 [`${ButtonSize.XS}`]: 'min-h-7 px-3 text-xs max-md:py-1',\n [`${ButtonSize.SM}`]: 'min-h-7 px-3 text-xs max-md:py-1',\n [`${ButtonSize.MD}`]: 'min-h-8 px-6 text-sm max-md:py-2',\n [`${ButtonSize.LG}`]: 'min-h-10 px-8 text-lg max-md:py-3',\n [`${ButtonSize.XL}`]: 'min-h-11 px-10 text-xl max-md:py-4',\n [`${ButtonSize.ICON_SM}`]: 'p-1.5',\n [`${ButtonSize.ICON_MD}`]: 'p-1.5',\n [`${ButtonSize.ICON_LG}`]: 'p-1.5',\n [`${ButtonSize.ICON_XL}`]: 'p-3',\n },\n color: {\n [`${ButtonColor.PRIMARY}`]:\n 'hover-primary-500/20 text-primary ring-primary-500/20 *:text-text-light',\n [`${ButtonColor.SECONDARY}`]:\n 'hover-secondary-500/20 text-secondary ring-secondary-500/20 *:text-text-light',\n [`${ButtonColor.NEUTRAL}`]:\n 'text-neutral ring-neutral-500/5 *:text-text-light',\n [`${ButtonColor.CARD}`]:\n 'hover-card-500/20 text-card ring-card-500/20 *:text-text-light',\n [`${ButtonColor.LIGHT}`]:\n 'hover-white-500/20 text-white ring-white/20 *:text-text-light',\n [`${ButtonColor.DARK}`]:\n 'text-neutral-800 ring-text-light/50 *:text-text-light',\n [`${ButtonColor.TEXT}`]: 'text-text ring-text/20 *:text-text-opposite',\n [`${ButtonColor.CURRENT}`]:\n 'hover-current-500/10 text-current ring-current/10 *:text-text-light',\n [`${ButtonColor.TEXT_INVERSE}`]:\n 'text-text-opposite ring-text-opposite/20 *:text-text',\n [`${ButtonColor.ERROR}`]:\n 'hover-error-500/20 text-error ring-error/20 *:text-text-light',\n [`${ButtonColor.SUCCESS}`]:\n 'hover-success-500/20 text-success ring-success/20 *:text-text-light',\n [`${ButtonColor.CUSTOM}`]: '',\n },\n roundedSize: {\n [`${ButtonRoundedSize.NONE}`]: 'rounded-none',\n [`${ButtonRoundedSize.SM}`]:\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n [`${ButtonRoundedSize.MD}`]:\n 'rounded-xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-2xl',\n [`${ButtonRoundedSize.LG}`]:\n 'rounded-2xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-3xl',\n [`${ButtonRoundedSize.XL}`]:\n 'rounded-3xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-4xl',\n [`${ButtonRoundedSize['2xl']}`]:\n 'rounded-4xl [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[2.5rem]',\n [`${ButtonRoundedSize['3xl']}`]:\n 'rounded-[2.5rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[3rem]',\n [`${ButtonRoundedSize['4xl']}`]:\n 'rounded-[3rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[4rem]',\n [`${ButtonRoundedSize['5xl']}`]:\n 'rounded-[4rem] [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-[5rem]',\n [`${ButtonRoundedSize.FULL}`]: 'rounded-full',\n },\n variant: {\n [`${ButtonVariant.DEFAULT}`]: [\n 'bg-current',\n 'hover:bg-current/90',\n 'hover:ring-5',\n 'aria-selected:ring-5',\n ],\n\n [`${ButtonVariant.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 [`${ButtonVariant.NONE}`]:\n 'border-none bg-current/0 text-inherit hover:bg-current/0',\n\n [`${ButtonVariant.LINK}`]:\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent hover:underline',\n\n [`${ButtonVariant.INVISIBLE_LINK}`]:\n 'h-auto justify-start border-inherit bg-transparent px-1 underline-offset-4 *:text-current! hover:bg-transparent',\n\n [`${ButtonVariant.HOVERABLE}`]:\n 'rounded-lg border-none bg-current/0 transition *:text-current! hover:bg-current/20 aria-[current]:bg-current/5',\n\n [`${ButtonVariant.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 [`${ButtonVariant.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 [`${ButtonTextAlign.LEFT}`]: 'justify-start text-left',\n [`${ButtonTextAlign.CENTER}`]: 'justify-center text-center',\n [`${ButtonTextAlign.RIGHT}`]: 'justify-end text-right',\n },\n\n isFullWidth: {\n true: 'w-full',\n false: '',\n },\n },\n defaultVariants: {\n variant: `${ButtonVariant.DEFAULT}`,\n size: `${ButtonSize.MD}`,\n color: `${ButtonColor.TEXT}`,\n roundedSize: `${ButtonRoundedSize.MD}`,\n textAlign: `${ButtonTextAlign.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={`${ButtonVariant.OUTLINE}`}\n * color={ButtonColor.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 =\n variant === `${ButtonVariant.LINK}` ||\n variant === `${ButtonVariant.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 === ButtonSize.ICON_SM ||\n size === ButtonSize.ICON_MD ||\n size === ButtonSize.ICON_LG ||\n size === ButtonSize.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:\n textAlign ??\n (IconRight ? ButtonTextAlign.LEFT : ButtonTextAlign.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 === ButtonSize.SM && 'w-3',\n isLoading && size === ButtonSize.MD && 'w-4',\n isLoading && size === ButtonSize.LG && 'w-6',\n isLoading && size === ButtonSize.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":";;;;;;;;;;AAUA,IAAY,aAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;AAEA,MAAM,qBAAqB,IAAI,sBAAsB;CACnD,UAAU,EACR,MAAM;GACH,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,OAAqB;GACrB,YAA0B;GAC1B,YAA0B;GAC1B,YAA0B;GAC1B,YAA0B;CAC7B,EACF;CACA,iBAAiB,EACf,WACF;AACF,CAAC;;;;AAKD,IAAY,gBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;AAKA,IAAY,cAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF;;;;AAKA,IAAY,kBAAL;CACL;CACA;CACA;;AACF;;;;AAKA,MAAa,iBAAiB,IAC5B,8LACA;CACE,UAAU;EACR,MAAM;IACH,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,OAAqB;IACrB,YAA0B;IAC1B,YAA0B;IAC1B,YAA0B;IAC1B,YAA0B;EAC7B;EACA,OAAO;IACJ,YACC;IACD,cACC;IACD,YACC;IACD,SACC;IACD,UACC;IACD,SACC;IACD,SAAwB;IACxB,YACC;IACD,iBACC;IACD,UACC;IACD,YACC;IACD,WAA0B;EAC7B;EACA,aAAa;IACV,cAA8B;IAC9B,YACC;IACD,YACC;IACD,YACC;IACD,YACC;IACD,aACC;IACD,aACC;IACD,aACC;IACD,aACC;IACD,cAA8B;EACjC;EACA,SAAS;IACN,YAA6B;IAC5B;IACA;IACA;IACA;GACF;IAEC,YAA6B;IAC5B;IACA;IACA;IACA;GACF;IAEC,SACC;IAED,SACC;IAED,mBACC;IAED,cACC;IAED,SAA0B;IACzB;IACA;IACA;GACF;IACC,UAA2B;IAE1B;IACA;IACA;IACA;IACA;IAEA;IACA;IACA;IAGA;IACA;IACA;IACA;IAGA;IAGA;IAGA;GACF;EACF;EAEA,WAAW;IACR,SAA4B;IAC5B,WAA8B;IAC9B,UAA6B;EAChC;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,SACJ,YAAY,UACZ,YAAY;CACd,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,sBACA,sBACA,sBACA;CAEF,OACE,qBAAC,UAAD;EACE,UAAU,aAAa;EACvB,MAAM,SAAS,SAAS;EAClB;EACN,WAAW,eAAe;GACxB;GACA;GACA;GACA;GACA;GACA,WACE,cACC;GACH;EACF,CAAC;EACD,GAAI;EACJ,GAAI;YAhBN;GAkBG,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,iBAA0B,OACvC,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,OACvC,aAAa,iBAA0B,KACzC;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"}
|
|
@@ -4,7 +4,7 @@ import { cn } from "../../utils/cn.mjs";
|
|
|
4
4
|
import { Button, ButtonColor, ButtonSize, ButtonVariant } from "../Button/Button.mjs";
|
|
5
5
|
import { KeyboardShortcut } from "../KeyboardShortcut/KeyboardShortcut.mjs";
|
|
6
6
|
import { Popover } from "../Popover/dynamic.mjs";
|
|
7
|
-
import { Children, createContext, isValidElement, useContext, useEffect, useRef, useState } from "react";
|
|
7
|
+
import { Children, createContext, isValidElement, useContext, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
8
8
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
9
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { useIntlayer } from "react-intlayer";
|
|
@@ -143,7 +143,7 @@ const CarouselRoot = ({ children, className, initialIndex = 0, onIndexChange, ..
|
|
|
143
143
|
const [displayedIndex, setDisplayedIndex] = useState(initialIndex);
|
|
144
144
|
const [containerHeight, setContainerHeight] = useState(0);
|
|
145
145
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
146
|
-
|
|
146
|
+
useLayoutEffect(() => {
|
|
147
147
|
const calculateDimensions = () => {
|
|
148
148
|
if (!containerRef.current) return;
|
|
149
149
|
const width = containerRef.current.clientWidth;
|
|
@@ -216,7 +216,7 @@ const CarouselRoot = ({ children, className, initialIndex = 0, onIndexChange, ..
|
|
|
216
216
|
}, TRANSITION_DELAY_MS);
|
|
217
217
|
return () => clearInterval(interval);
|
|
218
218
|
}, [selectedIndex, displayedIndex]);
|
|
219
|
-
|
|
219
|
+
useLayoutEffect(() => {
|
|
220
220
|
const calculateMaxHeight = () => {
|
|
221
221
|
const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);
|
|
222
222
|
const maxHeight = Math.max(0, ...heights);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Carousel/index.tsx"],"sourcesContent":["'use client';\n\nimport {\n Button,\n ButtonColor,\n ButtonSize,\n ButtonVariant,\n} from '@components/Button';\nimport { KeyboardShortcut } from '@components/KeyboardShortcut';\nimport { Popover } from '@components/Popover';\nimport { cn } from '@utils/cn';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport {\n Children,\n createContext,\n type FC,\n type HTMLAttributes,\n isValidElement,\n type MouseEventHandler,\n type ReactElement,\n type ReactNode,\n type TouchEventHandler,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\n\n// ------------------------------------------------------------------\n// Configuration\n// ------------------------------------------------------------------\nconst SWIPE_THRESHOLD_DIVISOR = 5;\nconst TRANSITION_DELAY_MS = 50;\n\n// ------------------------------------------------------------------\n// Context Definition\n// ------------------------------------------------------------------\ntype CarouselContextValue = {\n selectedIndex: number;\n setSelectedIndex: (index: number) => void;\n totalItems: number;\n handlePrev: () => void;\n handleNext: () => void;\n};\n\nconst CarouselContext = createContext<CarouselContextValue | null>(null);\n\nconst useCarousel = () => {\n const context = useContext(CarouselContext);\n if (!context) {\n throw new Error('useCarousel must be used within a Carousel');\n }\n return context;\n};\n\n// ------------------------------------------------------------------\n// Helper Functions\n// ------------------------------------------------------------------\nconst getCardStyle = (index: number, displayedIndex: number) => {\n const diff = Math.abs(index - displayedIndex);\n switch (diff) {\n case 0:\n return 'opacity-100 z-40';\n case 1:\n return 'opacity-75 z-30 cursor-pointer';\n case 2:\n return 'opacity-50 z-20 pointer-events-none';\n default:\n return 'opacity-0 z-10 pointer-events-none';\n }\n};\n\nconst getCardScale = (index: number, displayedIndex: number) => {\n const diff = Math.abs(index - displayedIndex);\n switch (diff) {\n case 0:\n return 1;\n case 1:\n return 0.9;\n case 2:\n return 0.8;\n default:\n return 0.7;\n }\n};\n\n// This allows the calculation to work on SSR without hydration mismatch.\n// Your original logic: (3 * screenWidth) / 10 === 30% of viewport width\nconst getCardPositionX = (\n index: number,\n displayedIndex: number,\n containerWidth: number\n) => {\n const diff = index - displayedIndex;\n const gapPercentage = containerWidth < 600 ? 0.15 : 0.3; // Dropped to 15% for a tighter cluster\n const step = Math.min(containerWidth * gapPercentage, 300);\n\n // The 'px' here is mandatory\n return `${diff * step}px`;\n};\n\n// ------------------------------------------------------------------\n// Sub-Component: Item\n// ------------------------------------------------------------------\ntype CarouselItemProps = HTMLAttributes<HTMLDivElement> & {\n children: ReactNode;\n};\n\nconst CarouselItem: FC<CarouselItemProps> = ({\n children,\n className,\n ...props\n}) => {\n return (\n <div className={cn('h-full w-full', className)} {...props}>\n {children}\n </div>\n );\n};\n\n// ------------------------------------------------------------------\n// Sub-Component: Indicators (Controller)\n// ------------------------------------------------------------------\ntype CarouselIndicatorsProps = HTMLAttributes<HTMLDivElement> & {\n disableKeyboardShortcuts?: boolean;\n};\n\nconst CarouselIndicators: FC<CarouselIndicatorsProps> = ({\n className,\n disableKeyboardShortcuts = false,\n ...props\n}) => {\n const {\n selectedIndex,\n setSelectedIndex,\n totalItems,\n handlePrev,\n handleNext,\n } = useCarousel();\n const { goToSlide, previousSlide, nextSlide } = useIntlayer('carousel');\n\n if (totalItems <= 1) return null;\n\n return (\n <div\n className={cn(\n 'absolute bottom-4 left-1/2 z-50 flex -translate-x-1/2 flex-row items-center gap-2',\n className\n )}\n {...props}\n >\n <Popover identifier=\"carousel-prev\">\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n label={previousSlide.value}\n roundedSize=\"full\"\n onClick={(e) => {\n e.stopPropagation();\n handlePrev();\n }}\n Icon={ChevronLeft}\n size={ButtonSize.ICON_MD}\n disabled={selectedIndex === 0}\n />\n\n <Popover.Detail identifier=\"carousel-prev\">\n <div className=\"flex items-center gap-2 p-2\">\n <span className=\"whitespace-nowrap text-neutral text-xs\">\n {previousSlide.value}\n </span>\n <KeyboardShortcut\n shortcut=\"ArrowLeft\"\n disabled={disableKeyboardShortcuts}\n size=\"sm\"\n onTriggered={handlePrev}\n />\n </div>\n </Popover.Detail>\n </Popover>\n\n {Array.from({ length: totalItems }).map((_, index) => {\n const isActive = index === selectedIndex;\n return (\n <button\n key={index}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setSelectedIndex(index);\n }}\n aria-label={goToSlide({ index: index + 1 }).value}\n className={cn(\n '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',\n isActive ? 'scale-110 bg-text' : 'bg-text/20 hover:bg-text/40'\n )}\n />\n );\n })}\n\n <Popover identifier=\"carousel-next\">\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n roundedSize=\"full\"\n label={nextSlide.value}\n onClick={(e) => {\n e.stopPropagation();\n handleNext();\n }}\n Icon={ChevronRight}\n size={ButtonSize.ICON_MD}\n disabled={selectedIndex === totalItems - 1}\n />\n\n <Popover.Detail identifier=\"carousel-next\">\n <div className=\"flex items-center gap-2 p-2\">\n <span className=\"whitespace-nowrap text-neutral text-xs\">\n {nextSlide.value}\n </span>\n <KeyboardShortcut\n shortcut=\"ArrowRight\"\n size=\"sm\"\n onTriggered={handleNext}\n disabled={disableKeyboardShortcuts}\n />\n </div>\n </Popover.Detail>\n </Popover>\n </div>\n );\n};\n\n// ------------------------------------------------------------------\n// Main Component: Carousel Root\n// ------------------------------------------------------------------\ntype CarouselProps = HTMLAttributes<HTMLDivElement> & {\n children: ReactNode;\n initialIndex?: number;\n onIndexChange?: (index: number) => void;\n};\n\nconst partitionCarouselChildren = (\n children: ReactNode[]\n): [ReactElement[], ReactNode[]] => {\n const slides: ReactElement[] = [];\n const others: ReactNode[] = [];\n\n children.forEach((child) => {\n if (isValidElement(child) && child.type === CarouselItem) {\n slides.push(child);\n } else {\n others.push(child);\n }\n });\n\n return [slides, others];\n};\n\nconst CarouselRoot: FC<CarouselProps> = ({\n children,\n className,\n initialIndex = 0,\n onIndexChange,\n ...props\n}) => {\n const allChildren = Children.toArray(children);\n const [slides, others] = partitionCarouselChildren(allChildren);\n const totalItems = slides.length;\n\n // State Management\n const [selectedIndex, setSelectedIndex] = useState<number>(initialIndex);\n const [displayedIndex, setDisplayedIndex] = useState<number>(initialIndex);\n const [containerHeight, setContainerHeight] = useState<number>(0);\n const [containerWidth, setContainerWidth] = useState<number>(0);\n\n useEffect(() => {\n const calculateDimensions = () => {\n if (!containerRef.current) return;\n\n // Track Width\n const width = containerRef.current.clientWidth;\n setContainerWidth(width);\n\n // Track Height (existing logic)\n const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);\n const maxHeight = Math.max(0, ...heights);\n if (maxHeight > 0) {\n setContainerHeight(maxHeight + 40);\n }\n };\n\n calculateDimensions();\n\n const observer = new ResizeObserver(() => {\n calculateDimensions();\n });\n\n if (containerRef.current) observer.observe(containerRef.current);\n itemsRef.current.forEach((item) => {\n if (item) observer.observe(item);\n });\n\n return () => observer.disconnect();\n }, [totalItems]);\n\n // Drag State\n const [startX, setStartX] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n // Refs\n const containerRef = useRef<HTMLDivElement>(null);\n const itemsRef = useRef<(HTMLDivElement | null)[]>([]);\n\n // Navigation Logic\n const handleSwitchItem = (diff: number) => {\n if (containerWidth === 0) return;\n\n // Use container width for the threshold\n const swipeStep = containerWidth / SWIPE_THRESHOLD_DIVISOR;\n const numSwipe = Math.round(diff / swipeStep);\n\n if (Math.abs(numSwipe) >= 1) {\n const newIndex = displayedIndex - numSwipe;\n const clampedIndex = Math.max(0, Math.min(newIndex, totalItems - 1));\n\n if (clampedIndex !== selectedIndex) {\n setSelectedIndex(clampedIndex);\n setStartX((prev) => prev + diff);\n }\n }\n };\n\n const handleNext = () => {\n setSelectedIndex((prev) => Math.min(prev + 1, totalItems - 1));\n };\n\n const handlePrev = () => {\n setSelectedIndex((prev) => Math.max(prev - 1, 0));\n };\n\n // Input Handlers\n const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {\n setIsDragging(true);\n setStartX(e.clientX);\n };\n const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {\n if (isDragging) handleSwitchItem(e.clientX - startX);\n };\n const handleDragEnd = () => setIsDragging(false);\n const handleTouchStart: TouchEventHandler<HTMLDivElement> = (e) => {\n setIsDragging(true);\n setStartX(e.touches[0].clientX);\n };\n const handleTouchMove: TouchEventHandler<HTMLDivElement> = (e) => {\n if (isDragging) handleSwitchItem(e.touches[0].clientX - startX);\n };\n\n // Effects\n useEffect(() => {\n if (selectedIndex) onIndexChange?.(selectedIndex);\n }, [selectedIndex, onIndexChange]);\n\n useEffect(() => {\n let interval: NodeJS.Timeout;\n\n if (selectedIndex !== displayedIndex) {\n interval = setInterval(() => {\n setDisplayedIndex((prev) => {\n if (prev === selectedIndex) {\n clearInterval(interval);\n return prev;\n }\n return prev < selectedIndex ? prev + 1 : prev - 1;\n });\n }, TRANSITION_DELAY_MS);\n }\n return () => clearInterval(interval);\n }, [selectedIndex, displayedIndex]);\n\n // Calculate height based on the MAX height of ALL items\n useEffect(() => {\n const calculateMaxHeight = () => {\n const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);\n const maxHeight = Math.max(0, ...heights);\n\n if (maxHeight > 0) {\n setContainerHeight(maxHeight + 40);\n }\n };\n\n calculateMaxHeight();\n\n const observer = new ResizeObserver(() => {\n calculateMaxHeight();\n });\n\n itemsRef.current.forEach((item) => {\n if (item) observer.observe(item);\n });\n\n return () => observer.disconnect();\n }, [totalItems]); // Removed isMounted dependency\n\n return (\n <CarouselContext.Provider\n value={{\n selectedIndex,\n setSelectedIndex,\n totalItems,\n handlePrev,\n handleNext,\n }}\n >\n <div\n ref={containerRef}\n className={cn(\n 'relative flex w-full cursor-grab select-none items-center overflow-hidden outline-none transition-[height] duration-300 ease-in-out focus:outline-none focus:outline-none focus:ring-0 active:cursor-grabbing',\n 'max-w-[1400px]',\n className\n )}\n style={{\n height: containerHeight > 0 ? containerHeight : 'auto',\n minHeight: '400px',\n }}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleDragEnd}\n onMouseLeave={handleDragEnd}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleDragEnd}\n role=\"region\"\n aria-label=\"Carousel\"\n {...props}\n >\n {slides.map((child, index) => {\n return (\n <div\n key={index}\n role=\"button\"\n tabIndex={0}\n ref={(el) => {\n itemsRef.current[index] = el;\n }}\n // FIX 2: Removed isMounted checks and invisible classes.\n // CSS units allow correct SSR rendering.\n className={cn(\n 'absolute left-1/2 -translate-x-1/2 transition-all duration-300 ease-in-out',\n 'outline-none ring-0 focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0',\n getCardStyle(index, displayedIndex)\n )}\n onClick={(e) => {\n e.stopPropagation();\n setSelectedIndex(index);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') setSelectedIndex(index);\n }}\n style={{\n transform: `\n translateX(${getCardPositionX(\n index,\n displayedIndex,\n containerWidth\n )})\n scale(${getCardScale(index, displayedIndex)})\n `,\n }}\n >\n {child}\n </div>\n );\n })}\n\n {others}\n </div>\n </CarouselContext.Provider>\n );\n};\n\nexport const Carousel = Object.assign(CarouselRoot, {\n Item: CarouselItem,\n Indicators: CarouselIndicators,\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/components/Carousel/index.tsx"],"sourcesContent":["'use client';\n\nimport {\n Button,\n ButtonColor,\n ButtonSize,\n ButtonVariant,\n} from '@components/Button';\nimport { KeyboardShortcut } from '@components/KeyboardShortcut';\nimport { Popover } from '@components/Popover';\nimport { cn } from '@utils/cn';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport {\n Children,\n createContext,\n type FC,\n type HTMLAttributes,\n isValidElement,\n type MouseEventHandler,\n type ReactElement,\n type ReactNode,\n type TouchEventHandler,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { useIntlayer } from 'react-intlayer';\n\n// ------------------------------------------------------------------\n// Configuration\n// ------------------------------------------------------------------\nconst SWIPE_THRESHOLD_DIVISOR = 5;\nconst TRANSITION_DELAY_MS = 50;\n\n// ------------------------------------------------------------------\n// Context Definition\n// ------------------------------------------------------------------\ntype CarouselContextValue = {\n selectedIndex: number;\n setSelectedIndex: (index: number) => void;\n totalItems: number;\n handlePrev: () => void;\n handleNext: () => void;\n};\n\nconst CarouselContext = createContext<CarouselContextValue | null>(null);\n\nconst useCarousel = () => {\n const context = useContext(CarouselContext);\n if (!context) {\n throw new Error('useCarousel must be used within a Carousel');\n }\n return context;\n};\n\n// ------------------------------------------------------------------\n// Helper Functions\n// ------------------------------------------------------------------\nconst getCardStyle = (index: number, displayedIndex: number) => {\n const diff = Math.abs(index - displayedIndex);\n switch (diff) {\n case 0:\n return 'opacity-100 z-40';\n case 1:\n return 'opacity-75 z-30 cursor-pointer';\n case 2:\n return 'opacity-50 z-20 pointer-events-none';\n default:\n return 'opacity-0 z-10 pointer-events-none';\n }\n};\n\nconst getCardScale = (index: number, displayedIndex: number) => {\n const diff = Math.abs(index - displayedIndex);\n switch (diff) {\n case 0:\n return 1;\n case 1:\n return 0.9;\n case 2:\n return 0.8;\n default:\n return 0.7;\n }\n};\n\n// This allows the calculation to work on SSR without hydration mismatch.\n// Your original logic: (3 * screenWidth) / 10 === 30% of viewport width\nconst getCardPositionX = (\n index: number,\n displayedIndex: number,\n containerWidth: number\n) => {\n const diff = index - displayedIndex;\n const gapPercentage = containerWidth < 600 ? 0.15 : 0.3; // Dropped to 15% for a tighter cluster\n const step = Math.min(containerWidth * gapPercentage, 300);\n\n // The 'px' here is mandatory\n return `${diff * step}px`;\n};\n\n// ------------------------------------------------------------------\n// Sub-Component: Item\n// ------------------------------------------------------------------\ntype CarouselItemProps = HTMLAttributes<HTMLDivElement> & {\n children: ReactNode;\n};\n\nconst CarouselItem: FC<CarouselItemProps> = ({\n children,\n className,\n ...props\n}) => {\n return (\n <div className={cn('h-full w-full', className)} {...props}>\n {children}\n </div>\n );\n};\n\n// ------------------------------------------------------------------\n// Sub-Component: Indicators (Controller)\n// ------------------------------------------------------------------\ntype CarouselIndicatorsProps = HTMLAttributes<HTMLDivElement> & {\n disableKeyboardShortcuts?: boolean;\n};\n\nconst CarouselIndicators: FC<CarouselIndicatorsProps> = ({\n className,\n disableKeyboardShortcuts = false,\n ...props\n}) => {\n const {\n selectedIndex,\n setSelectedIndex,\n totalItems,\n handlePrev,\n handleNext,\n } = useCarousel();\n const { goToSlide, previousSlide, nextSlide } = useIntlayer('carousel');\n\n if (totalItems <= 1) return null;\n\n return (\n <div\n className={cn(\n 'absolute bottom-4 left-1/2 z-50 flex -translate-x-1/2 flex-row items-center gap-2',\n className\n )}\n {...props}\n >\n <Popover identifier=\"carousel-prev\">\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n label={previousSlide.value}\n roundedSize=\"full\"\n onClick={(e) => {\n e.stopPropagation();\n handlePrev();\n }}\n Icon={ChevronLeft}\n size={ButtonSize.ICON_MD}\n disabled={selectedIndex === 0}\n />\n\n <Popover.Detail identifier=\"carousel-prev\">\n <div className=\"flex items-center gap-2 p-2\">\n <span className=\"whitespace-nowrap text-neutral text-xs\">\n {previousSlide.value}\n </span>\n <KeyboardShortcut\n shortcut=\"ArrowLeft\"\n disabled={disableKeyboardShortcuts}\n size=\"sm\"\n onTriggered={handlePrev}\n />\n </div>\n </Popover.Detail>\n </Popover>\n\n {Array.from({ length: totalItems }).map((_, index) => {\n const isActive = index === selectedIndex;\n return (\n <button\n key={index}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setSelectedIndex(index);\n }}\n aria-label={goToSlide({ index: index + 1 }).value}\n className={cn(\n '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',\n isActive ? 'scale-110 bg-text' : 'bg-text/20 hover:bg-text/40'\n )}\n />\n );\n })}\n\n <Popover identifier=\"carousel-next\">\n <Button\n variant={ButtonVariant.HOVERABLE}\n color={ButtonColor.NEUTRAL}\n roundedSize=\"full\"\n label={nextSlide.value}\n onClick={(e) => {\n e.stopPropagation();\n handleNext();\n }}\n Icon={ChevronRight}\n size={ButtonSize.ICON_MD}\n disabled={selectedIndex === totalItems - 1}\n />\n\n <Popover.Detail identifier=\"carousel-next\">\n <div className=\"flex items-center gap-2 p-2\">\n <span className=\"whitespace-nowrap text-neutral text-xs\">\n {nextSlide.value}\n </span>\n <KeyboardShortcut\n shortcut=\"ArrowRight\"\n size=\"sm\"\n onTriggered={handleNext}\n disabled={disableKeyboardShortcuts}\n />\n </div>\n </Popover.Detail>\n </Popover>\n </div>\n );\n};\n\n// ------------------------------------------------------------------\n// Main Component: Carousel Root\n// ------------------------------------------------------------------\ntype CarouselProps = HTMLAttributes<HTMLDivElement> & {\n children: ReactNode;\n initialIndex?: number;\n onIndexChange?: (index: number) => void;\n};\n\nconst partitionCarouselChildren = (\n children: ReactNode[]\n): [ReactElement[], ReactNode[]] => {\n const slides: ReactElement[] = [];\n const others: ReactNode[] = [];\n\n children.forEach((child) => {\n if (isValidElement(child) && child.type === CarouselItem) {\n slides.push(child);\n } else {\n others.push(child);\n }\n });\n\n return [slides, others];\n};\n\nconst CarouselRoot: FC<CarouselProps> = ({\n children,\n className,\n initialIndex = 0,\n onIndexChange,\n ...props\n}) => {\n const allChildren = Children.toArray(children);\n const [slides, others] = partitionCarouselChildren(allChildren);\n const totalItems = slides.length;\n\n // State Management\n const [selectedIndex, setSelectedIndex] = useState<number>(initialIndex);\n const [displayedIndex, setDisplayedIndex] = useState<number>(initialIndex);\n const [containerHeight, setContainerHeight] = useState<number>(0);\n const [containerWidth, setContainerWidth] = useState<number>(0);\n\n useLayoutEffect(() => {\n const calculateDimensions = () => {\n if (!containerRef.current) return;\n\n // Track Width\n const width = containerRef.current.clientWidth;\n setContainerWidth(width);\n\n // Track Height (existing logic)\n const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);\n const maxHeight = Math.max(0, ...heights);\n if (maxHeight > 0) {\n setContainerHeight(maxHeight + 40);\n }\n };\n\n calculateDimensions();\n\n const observer = new ResizeObserver(() => {\n calculateDimensions();\n });\n\n if (containerRef.current) observer.observe(containerRef.current);\n itemsRef.current.forEach((item) => {\n if (item) observer.observe(item);\n });\n\n return () => observer.disconnect();\n }, [totalItems]);\n\n // Drag State\n const [startX, setStartX] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n // Refs\n const containerRef = useRef<HTMLDivElement>(null);\n const itemsRef = useRef<(HTMLDivElement | null)[]>([]);\n\n // Navigation Logic\n const handleSwitchItem = (diff: number) => {\n if (containerWidth === 0) return;\n\n // Use container width for the threshold\n const swipeStep = containerWidth / SWIPE_THRESHOLD_DIVISOR;\n const numSwipe = Math.round(diff / swipeStep);\n\n if (Math.abs(numSwipe) >= 1) {\n const newIndex = displayedIndex - numSwipe;\n const clampedIndex = Math.max(0, Math.min(newIndex, totalItems - 1));\n\n if (clampedIndex !== selectedIndex) {\n setSelectedIndex(clampedIndex);\n setStartX((prev) => prev + diff);\n }\n }\n };\n\n const handleNext = () => {\n setSelectedIndex((prev) => Math.min(prev + 1, totalItems - 1));\n };\n\n const handlePrev = () => {\n setSelectedIndex((prev) => Math.max(prev - 1, 0));\n };\n\n // Input Handlers\n const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {\n setIsDragging(true);\n setStartX(e.clientX);\n };\n const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {\n if (isDragging) handleSwitchItem(e.clientX - startX);\n };\n const handleDragEnd = () => setIsDragging(false);\n const handleTouchStart: TouchEventHandler<HTMLDivElement> = (e) => {\n setIsDragging(true);\n setStartX(e.touches[0].clientX);\n };\n const handleTouchMove: TouchEventHandler<HTMLDivElement> = (e) => {\n if (isDragging) handleSwitchItem(e.touches[0].clientX - startX);\n };\n\n // Effects\n useEffect(() => {\n if (selectedIndex) onIndexChange?.(selectedIndex);\n }, [selectedIndex, onIndexChange]);\n\n useEffect(() => {\n let interval: NodeJS.Timeout;\n\n if (selectedIndex !== displayedIndex) {\n interval = setInterval(() => {\n setDisplayedIndex((prev) => {\n if (prev === selectedIndex) {\n clearInterval(interval);\n return prev;\n }\n return prev < selectedIndex ? prev + 1 : prev - 1;\n });\n }, TRANSITION_DELAY_MS);\n }\n return () => clearInterval(interval);\n }, [selectedIndex, displayedIndex]);\n\n // Calculate height based on the MAX height of ALL items\n useLayoutEffect(() => {\n const calculateMaxHeight = () => {\n const heights = itemsRef.current.map((item) => item?.offsetHeight || 0);\n const maxHeight = Math.max(0, ...heights);\n\n if (maxHeight > 0) {\n setContainerHeight(maxHeight + 40);\n }\n };\n\n calculateMaxHeight();\n\n const observer = new ResizeObserver(() => {\n calculateMaxHeight();\n });\n\n itemsRef.current.forEach((item) => {\n if (item) observer.observe(item);\n });\n\n return () => observer.disconnect();\n }, [totalItems]); // Removed isMounted dependency\n\n return (\n <CarouselContext.Provider\n value={{\n selectedIndex,\n setSelectedIndex,\n totalItems,\n handlePrev,\n handleNext,\n }}\n >\n <div\n ref={containerRef}\n className={cn(\n 'relative flex w-full cursor-grab select-none items-center overflow-hidden outline-none transition-[height] duration-300 ease-in-out focus:outline-none focus:outline-none focus:ring-0 active:cursor-grabbing',\n 'max-w-[1400px]',\n className\n )}\n style={{\n height: containerHeight > 0 ? containerHeight : 'auto',\n minHeight: '400px',\n }}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleDragEnd}\n onMouseLeave={handleDragEnd}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleDragEnd}\n role=\"region\"\n aria-label=\"Carousel\"\n {...props}\n >\n {slides.map((child, index) => {\n return (\n <div\n key={index}\n role=\"button\"\n tabIndex={0}\n ref={(el) => {\n itemsRef.current[index] = el;\n }}\n // FIX 2: Removed isMounted checks and invisible classes.\n // CSS units allow correct SSR rendering.\n className={cn(\n 'absolute left-1/2 -translate-x-1/2 transition-all duration-300 ease-in-out',\n 'outline-none ring-0 focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0',\n getCardStyle(index, displayedIndex)\n )}\n onClick={(e) => {\n e.stopPropagation();\n setSelectedIndex(index);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') setSelectedIndex(index);\n }}\n style={{\n transform: `\n translateX(${getCardPositionX(\n index,\n displayedIndex,\n containerWidth\n )})\n scale(${getCardScale(index, displayedIndex)})\n `,\n }}\n >\n {child}\n </div>\n );\n })}\n\n {others}\n </div>\n </CarouselContext.Provider>\n );\n};\n\nexport const Carousel = Object.assign(CarouselRoot, {\n Item: CarouselItem,\n Indicators: CarouselIndicators,\n});\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useUser } from "../../
|
|
3
|
+
import { useUser } from "../../api/useUser/index.mjs";
|
|
4
4
|
import { Button, ButtonColor, ButtonSize, ButtonVariant } from "../Button/Button.mjs";
|
|
5
5
|
import { AutoCompleteTextarea } from "../TextArea/AutocompleteTextArea.mjs";
|
|
6
6
|
import { useEffect, useState } from "react";
|