@insforge/react 1.0.3 → 1.0.5-dev.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.
@@ -232,7 +232,7 @@ var require_react_is_development = __commonJS({
232
232
  var ContextProvider = REACT_PROVIDER_TYPE;
233
233
  var Element = REACT_ELEMENT_TYPE;
234
234
  var ForwardRef = REACT_FORWARD_REF_TYPE;
235
- var Fragment9 = REACT_FRAGMENT_TYPE;
235
+ var Fragment10 = REACT_FRAGMENT_TYPE;
236
236
  var Lazy = REACT_LAZY_TYPE;
237
237
  var Memo = REACT_MEMO_TYPE;
238
238
  var Portal = REACT_PORTAL_TYPE;
@@ -291,7 +291,7 @@ var require_react_is_development = __commonJS({
291
291
  exports$1.ContextProvider = ContextProvider;
292
292
  exports$1.Element = Element;
293
293
  exports$1.ForwardRef = ForwardRef;
294
- exports$1.Fragment = Fragment9;
294
+ exports$1.Fragment = Fragment10;
295
295
  exports$1.Lazy = Lazy;
296
296
  exports$1.Memo = Memo;
297
297
  exports$1.Portal = Portal;
@@ -2148,6 +2148,8 @@ var theme = {
2148
2148
  sm: "0.875rem",
2149
2149
  // 14px
2150
2150
  base: "1rem",
2151
+ // 16px
2152
+ lg: "1.125rem",
2151
2153
  // 20px
2152
2154
  "2xl": "1.5rem"
2153
2155
  // 24px
@@ -2179,6 +2181,10 @@ var theme = {
2179
2181
  base: "200ms cubic-bezier(0.4, 0, 0.2, 1)"
2180
2182
  },
2181
2183
  sizes: {
2184
+ input: {
2185
+ height: "2.5rem"
2186
+ // 40px
2187
+ },
2182
2188
  button: {
2183
2189
  height: "2.5rem",
2184
2190
  // 40px for submit
@@ -4534,11 +4540,7 @@ function VerifyEmail({ token: token2, onSuccess, onError, ...uiProps }) {
4534
4540
  }
4535
4541
  setStatus("success");
4536
4542
  if (onSuccess) {
4537
- onSuccess({
4538
- accessToken: result.accessToken,
4539
- user: result.user,
4540
- redirectTo: result.redirectTo
4541
- });
4543
+ onSuccess(result);
4542
4544
  }
4543
4545
  } catch (err) {
4544
4546
  const errorMessage = err instanceof Error ? err.message : "Email verification failed";
@@ -4698,9 +4700,382 @@ var UserButtonMenuItemIcon = styled.div`
4698
4700
  height: 100%;
4699
4701
  }
4700
4702
  `;
4701
- function UserButton({ afterSignOutUrl = "/", mode = "simple" }) {
4703
+ var ProfileModalOverlay = styled.div`
4704
+ position: fixed;
4705
+ inset: 0;
4706
+ background-color: rgba(0, 0, 0, 0.5);
4707
+ display: flex;
4708
+ align-items: center;
4709
+ justify-content: center;
4710
+ z-index: 100;
4711
+ padding: ${theme.spacing[4]};
4712
+ `;
4713
+ var ProfileModalContainer = styled.div`
4714
+ background-color: ${theme.colors.bgWhite};
4715
+ border-radius: ${theme.radius.xl};
4716
+ box-shadow: ${theme.shadow.lg};
4717
+ width: 100%;
4718
+ max-width: 400px;
4719
+ max-height: 90vh;
4720
+ overflow: hidden;
4721
+ display: flex;
4722
+ flex-direction: column;
4723
+ font-family: ${theme.fontFamily.base};
4724
+ `;
4725
+ var ProfileModalHeader = styled.div`
4726
+ display: flex;
4727
+ align-items: center;
4728
+ justify-content: space-between;
4729
+ padding: ${theme.spacing[4]} ${theme.spacing[6]};
4730
+ border-bottom: 1px solid ${theme.colors.border};
4731
+ `;
4732
+ var ProfileModalTitle = styled.h2`
4733
+ font-size: ${theme.fontSize.lg};
4734
+ font-weight: ${theme.fontWeight.semibold};
4735
+ color: ${theme.colors.text};
4736
+ margin: 0;
4737
+ `;
4738
+ var ProfileModalCloseButton = styled.button`
4739
+ display: flex;
4740
+ align-items: center;
4741
+ justify-content: center;
4742
+ width: 2rem;
4743
+ height: 2rem;
4744
+ border-radius: ${theme.radius.md};
4745
+ background: none;
4746
+ border: none;
4747
+ cursor: pointer;
4748
+ color: ${theme.colors.textSecondary};
4749
+ transition: all ${theme.transition.fast};
4750
+
4751
+ &:hover {
4752
+ background-color: ${theme.colors.bgLight};
4753
+ color: ${theme.colors.text};
4754
+ }
4755
+
4756
+ svg {
4757
+ width: 1.25rem;
4758
+ height: 1.25rem;
4759
+ }
4760
+ `;
4761
+ var ProfileModalBody = styled.div`
4762
+ padding: ${theme.spacing[6]};
4763
+ overflow-y: auto;
4764
+ flex: 1;
4765
+ `;
4766
+ var ProfileAvatarSection = styled.div`
4767
+ display: flex;
4768
+ flex-direction: column;
4769
+ align-items: center;
4770
+ margin-bottom: ${theme.spacing[6]};
4771
+ `;
4772
+ var ProfileAvatar = styled.div`
4773
+ width: 5rem;
4774
+ height: 5rem;
4775
+ border-radius: ${theme.radius.full};
4776
+ background-color: ${theme.colors.primary};
4777
+ color: ${theme.colors.bgWhite};
4778
+ display: flex;
4779
+ align-items: center;
4780
+ justify-content: center;
4781
+ font-weight: ${theme.fontWeight.semibold};
4782
+ font-size: ${theme.fontSize["2xl"]};
4783
+ overflow: hidden;
4784
+ `;
4785
+ var ProfileAvatarImage = styled.img`
4786
+ width: 100%;
4787
+ height: 100%;
4788
+ object-fit: cover;
4789
+ `;
4790
+ var ProfileFieldsContainer = styled.div`
4791
+ display: flex;
4792
+ flex-direction: column;
4793
+ gap: ${theme.spacing[4]};
4794
+ `;
4795
+ var ProfileField = styled.div`
4796
+ display: flex;
4797
+ flex-direction: column;
4798
+ gap: ${theme.spacing[1]};
4799
+ `;
4800
+ var ProfileFieldLabel = styled.label`
4801
+ font-size: ${theme.fontSize.sm};
4802
+ font-weight: ${theme.fontWeight.medium};
4803
+ color: ${theme.colors.textSecondary};
4804
+ text-transform: capitalize;
4805
+ `;
4806
+ var ProfileFieldValue = styled.div`
4807
+ font-size: ${theme.fontSize.base};
4808
+ color: ${theme.colors.text};
4809
+ padding: ${theme.spacing[2]} 0;
4810
+ word-break: break-word;
4811
+ `;
4812
+ var ProfileFieldInput = styled.input`
4813
+ width: 100%;
4814
+ height: ${theme.sizes.input.height};
4815
+ padding: 0 ${theme.spacing[3]};
4816
+ border: 1px solid ${theme.colors.border};
4817
+ border-radius: ${theme.radius.md};
4818
+ font-size: ${theme.fontSize.base};
4819
+ font-family: ${theme.fontFamily.base};
4820
+ color: ${theme.colors.text};
4821
+ background-color: ${theme.colors.bgWhite};
4822
+ transition: border-color ${theme.transition.fast};
4823
+
4824
+ &:focus {
4825
+ outline: none;
4826
+ border-color: ${theme.colors.borderFocus};
4827
+ }
4828
+
4829
+ &:disabled {
4830
+ background-color: ${theme.colors.bgLight};
4831
+ color: ${theme.colors.textSecondary};
4832
+ cursor: not-allowed;
4833
+ }
4834
+ `;
4835
+ var ProfileModalFooter = styled.div`
4836
+ display: flex;
4837
+ align-items: center;
4838
+ justify-content: flex-end;
4839
+ gap: ${theme.spacing[3]};
4840
+ padding: ${theme.spacing[4]} ${theme.spacing[6]};
4841
+ border-top: 1px solid ${theme.colors.border};
4842
+ `;
4843
+ var ProfileButton = styled.button`
4844
+ display: flex;
4845
+ align-items: center;
4846
+ justify-content: center;
4847
+ gap: ${theme.spacing[2]};
4848
+ height: ${theme.sizes.button.height};
4849
+ padding: 0 ${theme.spacing[4]};
4850
+ border-radius: ${theme.radius.md};
4851
+ font-size: ${theme.fontSize.sm};
4852
+ font-weight: ${theme.fontWeight.medium};
4853
+ font-family: ${theme.fontFamily.base};
4854
+ cursor: pointer;
4855
+ transition: all ${theme.transition.fast};
4856
+
4857
+ ${(props) => props.$primary ? `
4858
+ background-color: ${theme.colors.primary};
4859
+ color: ${theme.colors.bgWhite};
4860
+ border: none;
4861
+
4862
+ &:hover:not(:disabled) {
4863
+ background-color: ${theme.colors.primaryHover};
4864
+ }
4865
+
4866
+ &:disabled {
4867
+ opacity: 0.5;
4868
+ cursor: not-allowed;
4869
+ }
4870
+ ` : `
4871
+ background-color: ${theme.colors.bgWhite};
4872
+ color: ${theme.colors.text};
4873
+ border: 1px solid ${theme.colors.border};
4874
+
4875
+ &:hover:not(:disabled) {
4876
+ background-color: ${theme.colors.bgLight};
4877
+ }
4878
+
4879
+ &:disabled {
4880
+ opacity: 0.5;
4881
+ cursor: not-allowed;
4882
+ }
4883
+ `}
4884
+ `;
4885
+ var ProfileSpinner = styled.div`
4886
+ width: 1rem;
4887
+ height: 1rem;
4888
+ border: 2px solid transparent;
4889
+ border-top-color: currentColor;
4890
+ border-radius: ${theme.radius.full};
4891
+ animation: spin 0.6s linear infinite;
4892
+
4893
+ @keyframes spin {
4894
+ to {
4895
+ transform: rotate(360deg);
4896
+ }
4897
+ }
4898
+ `;
4899
+ var READ_ONLY_FIELDS = ["id", "email", "avatar_url", "created_at", "updated_at"];
4900
+ var HIDDEN_FIELDS = ["id"];
4901
+ function formatFieldLabel(key) {
4902
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase();
4903
+ }
4904
+ function UserProfileModal({ onClose, onError }) {
4905
+ const { user, updateUser, isLoaded } = useInsforge();
4906
+ const [isEditing, setIsEditing] = React2.useState(false);
4907
+ const [isSaving, setIsSaving] = React2.useState(false);
4908
+ const [imageError, setImageError] = React2.useState(false);
4909
+ const [formData, setFormData] = React2.useState({});
4910
+ React2.useEffect(() => {
4911
+ if (user) {
4912
+ const initialData = {
4913
+ name: user.name || ""
4914
+ };
4915
+ if (user.profile) {
4916
+ Object.entries(user.profile).forEach(([key, value]) => {
4917
+ if (!HIDDEN_FIELDS.includes(key) && typeof value === "string") {
4918
+ initialData[key] = value;
4919
+ }
4920
+ });
4921
+ }
4922
+ setFormData(initialData);
4923
+ }
4924
+ }, [user]);
4925
+ React2.useEffect(() => {
4926
+ setImageError(false);
4927
+ const avatarUrl = user?.avatarUrl;
4928
+ if (!avatarUrl) {
4929
+ return;
4930
+ }
4931
+ const checkImageUrl = async () => {
4932
+ try {
4933
+ const response = await fetch(avatarUrl, {
4934
+ method: "HEAD",
4935
+ cache: "no-cache"
4936
+ });
4937
+ if (!response.ok) {
4938
+ setImageError(true);
4939
+ }
4940
+ } catch {
4941
+ setImageError(true);
4942
+ }
4943
+ };
4944
+ void checkImageUrl();
4945
+ }, [user?.avatarUrl]);
4946
+ const handleFieldChange = React2.useCallback((key, value) => {
4947
+ setFormData((prev2) => ({
4948
+ ...prev2,
4949
+ [key]: value
4950
+ }));
4951
+ }, []);
4952
+ const handleSave = React2.useCallback(async () => {
4953
+ if (!user) return;
4954
+ setIsSaving(true);
4955
+ try {
4956
+ const { name, ...dynamicFields } = formData;
4957
+ const updateData = {
4958
+ name
4959
+ };
4960
+ if (Object.keys(dynamicFields).length > 0) {
4961
+ updateData.profile = dynamicFields;
4962
+ }
4963
+ const result = await updateUser(updateData);
4964
+ if (result?.error) {
4965
+ onError?.(result.error);
4966
+ } else {
4967
+ setIsEditing(false);
4968
+ }
4969
+ } catch (error) {
4970
+ onError?.(error instanceof Error ? error.message : "Failed to update profile");
4971
+ } finally {
4972
+ setIsSaving(false);
4973
+ }
4974
+ }, [user, formData, updateUser, onError]);
4975
+ const handleCancel = React2.useCallback(() => {
4976
+ if (user) {
4977
+ const resetData = {
4978
+ name: user.name || ""
4979
+ };
4980
+ if (user.profile) {
4981
+ Object.entries(user.profile).forEach(([key, value]) => {
4982
+ if (!HIDDEN_FIELDS.includes(key) && typeof value === "string") {
4983
+ resetData[key] = value;
4984
+ }
4985
+ });
4986
+ }
4987
+ setFormData(resetData);
4988
+ }
4989
+ setIsEditing(false);
4990
+ }, [user]);
4991
+ const handleOverlayClick = React2.useCallback(
4992
+ (e) => {
4993
+ if (e.target === e.currentTarget) {
4994
+ onClose();
4995
+ }
4996
+ },
4997
+ [onClose]
4998
+ );
4999
+ React2.useEffect(() => {
5000
+ const handleEscape = (e) => {
5001
+ if (e.key === "Escape") {
5002
+ if (isEditing) {
5003
+ handleCancel();
5004
+ } else {
5005
+ onClose();
5006
+ }
5007
+ }
5008
+ };
5009
+ document.addEventListener("keydown", handleEscape);
5010
+ return () => document.removeEventListener("keydown", handleEscape);
5011
+ }, [isEditing, handleCancel, onClose]);
5012
+ if (!isLoaded || !user) {
5013
+ return null;
5014
+ }
5015
+ const initials = user.name ? user.name.charAt(0).toUpperCase() : user.email.split("@")[0].slice(0, 2).toUpperCase();
5016
+ const fields = [];
5017
+ fields.push({ key: "email", value: user.email, readOnly: true });
5018
+ fields.push({
5019
+ key: "name",
5020
+ value: isEditing ? formData.name || "" : user.name || "",
5021
+ readOnly: false
5022
+ });
5023
+ if (user.profile) {
5024
+ Object.entries(user.profile).forEach(([key, value]) => {
5025
+ if (!HIDDEN_FIELDS.includes(key) && typeof value === "string") {
5026
+ fields.push({
5027
+ key,
5028
+ value: isEditing ? formData[key] ?? value : value,
5029
+ readOnly: READ_ONLY_FIELDS.includes(key)
5030
+ });
5031
+ }
5032
+ });
5033
+ }
5034
+ return /* @__PURE__ */ jsxRuntime.jsx(ProfileModalOverlay, { onClick: handleOverlayClick, children: /* @__PURE__ */ jsxRuntime.jsxs(ProfileModalContainer, { onClick: (e) => e.stopPropagation(), children: [
5035
+ /* @__PURE__ */ jsxRuntime.jsxs(ProfileModalHeader, { children: [
5036
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileModalTitle, { children: "Profile" }),
5037
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileModalCloseButton, { onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, {}) })
5038
+ ] }),
5039
+ /* @__PURE__ */ jsxRuntime.jsxs(ProfileModalBody, { children: [
5040
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileAvatarSection, { children: /* @__PURE__ */ jsxRuntime.jsx(ProfileAvatar, { children: user.avatarUrl && !imageError ? /* @__PURE__ */ jsxRuntime.jsx(
5041
+ ProfileAvatarImage,
5042
+ {
5043
+ src: user.avatarUrl,
5044
+ alt: user.name || user.email,
5045
+ onError: () => setImageError(true)
5046
+ }
5047
+ ) : initials }) }),
5048
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileFieldsContainer, { children: fields.map(({ key, value, readOnly }) => /* @__PURE__ */ jsxRuntime.jsxs(ProfileField, { children: [
5049
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileFieldLabel, { children: formatFieldLabel(key) }),
5050
+ isEditing && !readOnly ? /* @__PURE__ */ jsxRuntime.jsx(
5051
+ ProfileFieldInput,
5052
+ {
5053
+ type: "text",
5054
+ value: formData[key] ?? value,
5055
+ onChange: (e) => handleFieldChange(key, e.target.value),
5056
+ disabled: isSaving
5057
+ }
5058
+ ) : /* @__PURE__ */ jsxRuntime.jsx(ProfileFieldValue, { children: value || "-" })
5059
+ ] }, key)) })
5060
+ ] }),
5061
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileModalFooter, { children: isEditing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5062
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileButton, { onClick: handleCancel, disabled: isSaving, children: "Cancel" }),
5063
+ /* @__PURE__ */ jsxRuntime.jsxs(ProfileButton, { $primary: true, onClick: handleSave, disabled: isSaving, children: [
5064
+ isSaving && /* @__PURE__ */ jsxRuntime.jsx(ProfileSpinner, {}),
5065
+ isSaving ? "Saving..." : "Save"
5066
+ ] })
5067
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ProfileButton, { $primary: true, onClick: () => setIsEditing(true), children: "Edit Profile" }) })
5068
+ ] }) });
5069
+ }
5070
+ function UserButton({
5071
+ afterSignOutUrl = "/",
5072
+ mode = "simple",
5073
+ showProfile = true,
5074
+ onProfileError
5075
+ }) {
4702
5076
  const { user } = useInsforge();
4703
5077
  const [isOpen, setIsOpen] = React2.useState(false);
5078
+ const [showProfileModal, setShowProfileModal] = React2.useState(false);
4704
5079
  const [imageError, setImageError] = React2.useState(false);
4705
5080
  const [openUpward, setOpenUpward] = React2.useState(false);
4706
5081
  const [horizontalOffset, setHorizontalOffset] = React2.useState(0);
@@ -4798,10 +5173,32 @@ function UserButton({ afterSignOutUrl = "/", mode = "simple" }) {
4798
5173
  ]
4799
5174
  }
4800
5175
  ),
4801
- isOpen && /* @__PURE__ */ jsxRuntime.jsx(UserButtonMenu, { ref: menuRef, $openUpward: openUpward, $horizontalOffset: horizontalOffset, children: /* @__PURE__ */ jsxRuntime.jsx(SignOutButton, { afterSignOutUrl, children: /* @__PURE__ */ jsxRuntime.jsxs(UserButtonMenuItem, { $signout: true, onClick: () => setIsOpen(false), children: [
4802
- /* @__PURE__ */ jsxRuntime.jsx(UserButtonMenuItemIcon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, {}) }),
4803
- "Sign out"
4804
- ] }) }) })
5176
+ isOpen && /* @__PURE__ */ jsxRuntime.jsxs(UserButtonMenu, { ref: menuRef, $openUpward: openUpward, $horizontalOffset: horizontalOffset, children: [
5177
+ showProfile && /* @__PURE__ */ jsxRuntime.jsxs(
5178
+ UserButtonMenuItem,
5179
+ {
5180
+ onClick: () => {
5181
+ setShowProfileModal(true);
5182
+ setIsOpen(false);
5183
+ },
5184
+ children: [
5185
+ /* @__PURE__ */ jsxRuntime.jsx(UserButtonMenuItemIcon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, {}) }),
5186
+ "Profile"
5187
+ ]
5188
+ }
5189
+ ),
5190
+ /* @__PURE__ */ jsxRuntime.jsx(SignOutButton, { afterSignOutUrl, children: /* @__PURE__ */ jsxRuntime.jsxs(UserButtonMenuItem, { $signout: true, onClick: () => setIsOpen(false), children: [
5191
+ /* @__PURE__ */ jsxRuntime.jsx(UserButtonMenuItemIcon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, {}) }),
5192
+ "Sign out"
5193
+ ] }) })
5194
+ ] }),
5195
+ showProfileModal && /* @__PURE__ */ jsxRuntime.jsx(
5196
+ UserProfileModal,
5197
+ {
5198
+ onClose: () => setShowProfileModal(false),
5199
+ onError: onProfileError
5200
+ }
5201
+ )
4805
5202
  ] });
4806
5203
  }
4807
5204
  function Protect({
@@ -4950,6 +5347,7 @@ exports.SignUpForm = SignUpForm;
4950
5347
  exports.SignedIn = SignedIn;
4951
5348
  exports.SignedOut = SignedOut;
4952
5349
  exports.UserButton = UserButton;
5350
+ exports.UserProfileModal = UserProfileModal;
4953
5351
  exports.VerifyEmail = VerifyEmail;
4954
5352
  exports.VerifyEmailStatus = VerifyEmailStatus;
4955
5353
  //# sourceMappingURL=components.cjs.map