@serhii.olishevskyi/docs 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -182,6 +182,14 @@ interface UserStatusChipComponentProps {
182
182
  };
183
183
  }
184
184
 
185
+ interface MyAvatarComponentProps {
186
+ size?: number;
187
+ disableBorder?: boolean;
188
+ disableShadow?: boolean;
189
+ disablePopover?: boolean;
190
+ sx?: SxProps<Theme>;
191
+ }
192
+
185
193
  declare const UI: {
186
194
  Inputs: {
187
195
  TextField: React$1.FC<TextFieldComponentProps>;
@@ -198,6 +206,11 @@ declare const UI: {
198
206
  UserPopover: React$1.FC<UserPopoverComponentProps>;
199
207
  UserRoleChip: React$1.FC<UserRoleChipComponentProps>;
200
208
  UserStatusChip: React$1.FC<UserStatusChipComponentProps>;
209
+ MyAvatar: React$1.FC<MyAvatarComponentProps>;
210
+ };
211
+ Widgets: {
212
+ ProtectedLanguageSelector: React$1.FC<{}>;
213
+ PublicLanguageSelector: React$1.FC<{}>;
201
214
  };
202
215
  };
203
216
 
@@ -475,4 +488,4 @@ declare const Providers: {
475
488
  readonly AIProvider: React$1.FC<AIProviderProps>;
476
489
  };
477
490
 
478
- export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps as CopyClipboardButtonProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps as EmailInputProps, type Language, LocalizationProvider, type NumberInputComponentProps as NumberInputProps, Providers, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type TextFieldComponentProps as TextFieldProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps as TranslatableTooltipProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps as UserAvatarProps, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
491
+ export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps as CopyClipboardButtonProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps as EmailInputProps, type Language, LocalizationProvider, type MyAvatarComponentProps as MyAvatarProps, type NumberInputComponentProps as NumberInputProps, Providers, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type TextFieldComponentProps as TextFieldProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps as TranslatableTooltipProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps as UserAvatarProps, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
package/dist/index.d.ts CHANGED
@@ -182,6 +182,14 @@ interface UserStatusChipComponentProps {
182
182
  };
183
183
  }
184
184
 
185
+ interface MyAvatarComponentProps {
186
+ size?: number;
187
+ disableBorder?: boolean;
188
+ disableShadow?: boolean;
189
+ disablePopover?: boolean;
190
+ sx?: SxProps<Theme>;
191
+ }
192
+
185
193
  declare const UI: {
186
194
  Inputs: {
187
195
  TextField: React$1.FC<TextFieldComponentProps>;
@@ -198,6 +206,11 @@ declare const UI: {
198
206
  UserPopover: React$1.FC<UserPopoverComponentProps>;
199
207
  UserRoleChip: React$1.FC<UserRoleChipComponentProps>;
200
208
  UserStatusChip: React$1.FC<UserStatusChipComponentProps>;
209
+ MyAvatar: React$1.FC<MyAvatarComponentProps>;
210
+ };
211
+ Widgets: {
212
+ ProtectedLanguageSelector: React$1.FC<{}>;
213
+ PublicLanguageSelector: React$1.FC<{}>;
201
214
  };
202
215
  };
203
216
 
@@ -475,4 +488,4 @@ declare const Providers: {
475
488
  readonly AIProvider: React$1.FC<AIProviderProps>;
476
489
  };
477
490
 
478
- export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps as CopyClipboardButtonProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps as EmailInputProps, type Language, LocalizationProvider, type NumberInputComponentProps as NumberInputProps, Providers, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type TextFieldComponentProps as TextFieldProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps as TranslatableTooltipProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps as UserAvatarProps, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
491
+ export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps as CopyClipboardButtonProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps as EmailInputProps, type Language, LocalizationProvider, type MyAvatarComponentProps as MyAvatarProps, type NumberInputComponentProps as NumberInputProps, Providers, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type TextFieldComponentProps as TextFieldProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps as TranslatableTooltipProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps as UserAvatarProps, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
package/dist/index.js CHANGED
@@ -3452,6 +3452,399 @@ init_CopyClipboardButtonComponent();
3452
3452
  init_UserRoleChipComponent();
3453
3453
  init_UserStatusChipComponent();
3454
3454
  init_UserPopoverComponent();
3455
+
3456
+ // src/UI/components/data.display/MyAvatarComponent.tsx
3457
+ var import_react23 = require("react");
3458
+ var import_material16 = require("@mui/material");
3459
+ init_context();
3460
+ var import_jsx_runtime26 = require("react/jsx-runtime");
3461
+ var getInitials2 = (name) => {
3462
+ if (!name) return "?";
3463
+ const parts = name.trim().split(" ");
3464
+ if (parts.length >= 2) {
3465
+ return `${parts[0][0]}${parts[1][0]}`.toUpperCase();
3466
+ }
3467
+ return name[0].toUpperCase();
3468
+ };
3469
+ var MyAvatarComponent = ({
3470
+ size = 40,
3471
+ disableBorder = false,
3472
+ disableShadow = false,
3473
+ disablePopover = false,
3474
+ sx = {}
3475
+ }) => {
3476
+ const { profile: userProfile, loading: isLoading } = useAuth();
3477
+ const [anchorEl, setAnchorEl] = (0, import_react23.useState)(null);
3478
+ const handlePopoverOpen = (event) => {
3479
+ if (!disablePopover) {
3480
+ setAnchorEl(event.currentTarget);
3481
+ }
3482
+ };
3483
+ const handlePopoverClose = () => {
3484
+ setAnchorEl(null);
3485
+ };
3486
+ const isPopoverOpen = Boolean(anchorEl);
3487
+ if (isLoading) {
3488
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3489
+ import_material16.Skeleton,
3490
+ {
3491
+ variant: "circular",
3492
+ width: size,
3493
+ height: size,
3494
+ sx: { display: "inline-block", flexShrink: 0, ...sx }
3495
+ }
3496
+ );
3497
+ }
3498
+ const initials = getInitials2(userProfile?.display_name || "");
3499
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
3500
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3501
+ import_material16.Avatar,
3502
+ {
3503
+ src: userProfile?.avatar_url || void 0,
3504
+ alt: userProfile?.display_name || "User Avatar",
3505
+ onMouseEnter: handlePopoverOpen,
3506
+ onMouseLeave: handlePopoverClose,
3507
+ sx: {
3508
+ width: size,
3509
+ height: size,
3510
+ fontSize: size * 0.4,
3511
+ fontWeight: 600,
3512
+ bgcolor: "primary.main",
3513
+ color: "primary.contrastText",
3514
+ cursor: disablePopover ? void 0 : "pointer",
3515
+ transition: "transform 0.2s ease, box-shadow 0.2s ease",
3516
+ border: disableBorder ? "none" : "2px solid",
3517
+ borderColor: "background.paper",
3518
+ boxShadow: disableShadow ? "none" : 2,
3519
+ "&:hover": {
3520
+ transform: disablePopover ? "none" : "scale(1.05)"
3521
+ },
3522
+ ...sx
3523
+ },
3524
+ children: !userProfile?.avatar_url && initials
3525
+ }
3526
+ ),
3527
+ !disablePopover && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3528
+ import_material16.Popover,
3529
+ {
3530
+ id: `avatar-popover-${userProfile?.id}`,
3531
+ open: isPopoverOpen,
3532
+ anchorEl,
3533
+ anchorOrigin: {
3534
+ vertical: "bottom",
3535
+ horizontal: "center"
3536
+ },
3537
+ transformOrigin: {
3538
+ vertical: "top",
3539
+ horizontal: "center"
3540
+ },
3541
+ onClose: handlePopoverClose,
3542
+ disableRestoreFocus: true,
3543
+ disableAutoFocus: true,
3544
+ disableEnforceFocus: true,
3545
+ sx: {
3546
+ pointerEvents: "none",
3547
+ mt: 1
3548
+ },
3549
+ slotProps: {
3550
+ paper: {
3551
+ sx: {
3552
+ p: 1.5,
3553
+ borderRadius: 2,
3554
+ boxShadow: 3
3555
+ }
3556
+ }
3557
+ },
3558
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_material16.Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 0.5 }, children: [
3559
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_material16.Typography, { variant: "subtitle2", sx: { fontWeight: 600 }, children: userProfile?.display_name }),
3560
+ userProfile?.email && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_material16.Typography, { variant: "caption", color: "text.secondary", children: userProfile.email })
3561
+ ] })
3562
+ }
3563
+ )
3564
+ ] });
3565
+ };
3566
+
3567
+ // src/i18n/LanguageSelector.tsx
3568
+ var import_react24 = require("react");
3569
+ var import_react_i18next5 = require("react-i18next");
3570
+ var import_material17 = require("@mui/material");
3571
+ init_LocalizationProvider();
3572
+ init_UserSettingsProvider();
3573
+ init_SupabaseProvider();
3574
+ var import_jsx_runtime27 = require("react/jsx-runtime");
3575
+ var LanguageSelector = () => {
3576
+ const supabase = useSupabase();
3577
+ const { i18n: i18n2 } = (0, import_react_i18next5.useTranslation)();
3578
+ const [languages, setLanguages] = (0, import_react24.useState)([]);
3579
+ const [loading, setLoading] = (0, import_react24.useState)(true);
3580
+ const { changeLanguage } = useLocalization();
3581
+ const { settings: userSettings, setLanguage } = useUserSettings();
3582
+ const [anchorEl, setAnchorEl] = (0, import_react24.useState)(null);
3583
+ const open = Boolean(anchorEl);
3584
+ (0, import_react24.useEffect)(() => {
3585
+ const fetchLanguages = async () => {
3586
+ try {
3587
+ const { data, error } = await supabase.from("languages").select("code, name, svg_flag").eq("enabled", true);
3588
+ if (error) throw error;
3589
+ setLanguages(data || []);
3590
+ if (userSettings.language) changeLanguage(userSettings.language);
3591
+ } catch (err) {
3592
+ console.error("\u041F\u043E\u043C\u0438\u043B\u043A\u0430 \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u043C\u043E\u0432:", err);
3593
+ } finally {
3594
+ setLoading(false);
3595
+ }
3596
+ };
3597
+ fetchLanguages();
3598
+ }, []);
3599
+ const handleClick = (event) => {
3600
+ setAnchorEl(event.currentTarget);
3601
+ };
3602
+ const handleClose = () => {
3603
+ setAnchorEl(null);
3604
+ };
3605
+ const handleSelectLanguage = (langCode) => {
3606
+ changeLanguage(langCode);
3607
+ setLanguage(langCode);
3608
+ handleClose();
3609
+ };
3610
+ if (loading) {
3611
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_material17.CircularProgress, { size: 20, color: "inherit", sx: { mx: 1 } });
3612
+ }
3613
+ const rawCurrentLang = i18n2.language || "uk";
3614
+ const currentLangCode = rawCurrentLang.split("-")[0];
3615
+ const currentLang = languages.find((l) => l.code === currentLangCode) || languages[0];
3616
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3617
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_material17.Tooltip, { title: currentLang ? currentLang.name : "\u041C\u043E\u0432\u0430", arrow: true, placement: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3618
+ import_material17.IconButton,
3619
+ {
3620
+ onClick: handleClick,
3621
+ size: "small",
3622
+ "aria-controls": open ? "language-menu" : void 0,
3623
+ "aria-haspopup": "true",
3624
+ "aria-expanded": open ? "true" : void 0,
3625
+ sx: {
3626
+ p: 1,
3627
+ borderRadius: 2,
3628
+ transition: "background-color 0.2s",
3629
+ "&:hover": {
3630
+ backgroundColor: "action.hover"
3631
+ }
3632
+ },
3633
+ children: currentLang?.svg_flag && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3634
+ import_material17.Box,
3635
+ {
3636
+ sx: {
3637
+ width: 24,
3638
+ height: 16,
3639
+ borderRadius: "3px",
3640
+ overflow: "hidden",
3641
+ flexShrink: 0,
3642
+ boxShadow: "0 0 2px rgba(0,0,0,0.3)",
3643
+ display: "flex",
3644
+ alignItems: "center",
3645
+ justifyContent: "center",
3646
+ "& svg": { width: "100%", height: "100%", display: "block", objectFit: "cover" }
3647
+ },
3648
+ dangerouslySetInnerHTML: { __html: currentLang.svg_flag }
3649
+ }
3650
+ )
3651
+ }
3652
+ ) }),
3653
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3654
+ import_material17.Menu,
3655
+ {
3656
+ id: "language-menu",
3657
+ anchorEl,
3658
+ open,
3659
+ onClose: handleClose,
3660
+ slotProps: {
3661
+ paper: {
3662
+ elevation: 3,
3663
+ sx: {
3664
+ mt: 1,
3665
+ minWidth: 150,
3666
+ borderRadius: 2
3667
+ }
3668
+ }
3669
+ },
3670
+ transformOrigin: { horizontal: "right", vertical: "top" },
3671
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
3672
+ children: languages.map((lang) => {
3673
+ const isSelected = lang.code === currentLangCode;
3674
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3675
+ import_material17.MenuItem,
3676
+ {
3677
+ selected: isSelected,
3678
+ onClick: () => handleSelectLanguage(lang.code),
3679
+ sx: {
3680
+ display: "flex",
3681
+ alignItems: "center",
3682
+ gap: 1.5,
3683
+ py: 1,
3684
+ px: 2
3685
+ },
3686
+ children: [
3687
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3688
+ import_material17.Box,
3689
+ {
3690
+ sx: {
3691
+ width: 22,
3692
+ height: 15,
3693
+ borderRadius: "2px",
3694
+ overflow: "hidden",
3695
+ flexShrink: 0,
3696
+ boxShadow: "0 0 1px rgba(0,0,0,0.4)",
3697
+ "& svg": { width: "100%", height: "100%", display: "block" }
3698
+ },
3699
+ dangerouslySetInnerHTML: { __html: lang.svg_flag }
3700
+ }
3701
+ ),
3702
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_material17.Typography, { variant: "body2", sx: { fontWeight: isSelected ? 600 : 400 }, children: lang.name })
3703
+ ]
3704
+ },
3705
+ lang.code
3706
+ );
3707
+ })
3708
+ }
3709
+ )
3710
+ ] });
3711
+ };
3712
+ var PublicLanguageSelector = () => {
3713
+ const supabase = useSupabase();
3714
+ const { i18n: i18n2 } = (0, import_react_i18next5.useTranslation)();
3715
+ const { changeLanguage } = useLocalization();
3716
+ const [languages, setLanguages] = (0, import_react24.useState)([]);
3717
+ const [loading, setLoading] = (0, import_react24.useState)(true);
3718
+ const [anchorEl, setAnchorEl] = (0, import_react24.useState)(null);
3719
+ const open = Boolean(anchorEl);
3720
+ (0, import_react24.useEffect)(() => {
3721
+ const fetchLanguages = async () => {
3722
+ try {
3723
+ const { data, error } = await supabase.from("languages").select("code, name, svg_flag").eq("enabled", true);
3724
+ if (error) throw error;
3725
+ setLanguages(data || []);
3726
+ } catch (err) {
3727
+ console.error("\u041F\u043E\u043C\u0438\u043B\u043A\u0430 \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u043C\u043E\u0432:", err);
3728
+ } finally {
3729
+ setLoading(false);
3730
+ }
3731
+ };
3732
+ fetchLanguages();
3733
+ }, []);
3734
+ const handleClick = (event) => {
3735
+ setAnchorEl(event.currentTarget);
3736
+ };
3737
+ const handleClose = () => {
3738
+ setAnchorEl(null);
3739
+ };
3740
+ const handleSelectLanguage = (langCode) => {
3741
+ changeLanguage(langCode);
3742
+ handleClose();
3743
+ };
3744
+ if (loading) {
3745
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_material17.CircularProgress, { size: 20, color: "inherit", sx: { mx: 1 } });
3746
+ }
3747
+ const rawCurrentLang = i18n2.language || "uk";
3748
+ const currentLangCode = rawCurrentLang.split("-")[0];
3749
+ const currentLang = languages.find((l) => l.code === currentLangCode) || languages[0];
3750
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3751
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_material17.Tooltip, { title: currentLang ? currentLang.name : "\u041C\u043E\u0432\u0430", arrow: true, placement: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3752
+ import_material17.IconButton,
3753
+ {
3754
+ onClick: handleClick,
3755
+ size: "small",
3756
+ "aria-controls": open ? "language-menu" : void 0,
3757
+ "aria-haspopup": "true",
3758
+ "aria-expanded": open ? "true" : void 0,
3759
+ sx: {
3760
+ p: 1,
3761
+ borderRadius: 2,
3762
+ transition: "background-color 0.2s",
3763
+ "&:hover": {
3764
+ backgroundColor: "action.hover"
3765
+ }
3766
+ },
3767
+ children: currentLang?.svg_flag && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3768
+ import_material17.Box,
3769
+ {
3770
+ sx: {
3771
+ width: 24,
3772
+ height: 16,
3773
+ borderRadius: "3px",
3774
+ overflow: "hidden",
3775
+ flexShrink: 0,
3776
+ boxShadow: "0 0 2px rgba(0,0,0,0.3)",
3777
+ display: "flex",
3778
+ alignItems: "center",
3779
+ justifyContent: "center",
3780
+ "& svg": { width: "100%", height: "100%", display: "block", objectFit: "cover" }
3781
+ },
3782
+ dangerouslySetInnerHTML: { __html: currentLang.svg_flag }
3783
+ }
3784
+ )
3785
+ }
3786
+ ) }),
3787
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3788
+ import_material17.Menu,
3789
+ {
3790
+ id: "language-menu",
3791
+ anchorEl,
3792
+ open,
3793
+ onClose: handleClose,
3794
+ slotProps: {
3795
+ paper: {
3796
+ elevation: 3,
3797
+ sx: {
3798
+ mt: 1,
3799
+ minWidth: 150,
3800
+ borderRadius: 2
3801
+ }
3802
+ }
3803
+ },
3804
+ transformOrigin: { horizontal: "right", vertical: "top" },
3805
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
3806
+ children: languages.map((lang) => {
3807
+ const isSelected = lang.code === currentLangCode;
3808
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3809
+ import_material17.MenuItem,
3810
+ {
3811
+ selected: isSelected,
3812
+ onClick: () => handleSelectLanguage(lang.code),
3813
+ sx: {
3814
+ display: "flex",
3815
+ alignItems: "center",
3816
+ gap: 1.5,
3817
+ py: 1,
3818
+ px: 2
3819
+ },
3820
+ children: [
3821
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3822
+ import_material17.Box,
3823
+ {
3824
+ sx: {
3825
+ width: 22,
3826
+ height: 15,
3827
+ borderRadius: "2px",
3828
+ overflow: "hidden",
3829
+ flexShrink: 0,
3830
+ boxShadow: "0 0 1px rgba(0,0,0,0.4)",
3831
+ "& svg": { width: "100%", height: "100%", display: "block" }
3832
+ },
3833
+ dangerouslySetInnerHTML: { __html: lang.svg_flag }
3834
+ }
3835
+ ),
3836
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_material17.Typography, { variant: "body2", sx: { fontWeight: isSelected ? 600 : 400 }, children: lang.name })
3837
+ ]
3838
+ },
3839
+ lang.code
3840
+ );
3841
+ })
3842
+ }
3843
+ )
3844
+ ] });
3845
+ };
3846
+
3847
+ // src/UI/index.ts
3455
3848
  var UI = {
3456
3849
  Inputs: {
3457
3850
  TextField: TextFieldComponent,
@@ -3467,11 +3860,16 @@ var UI = {
3467
3860
  UserAvatar: UserAvatarComponent,
3468
3861
  UserPopover: UserPopoverComponent_default,
3469
3862
  UserRoleChip: UserRoleChipComponent,
3470
- UserStatusChip: UserStatusChipComponent
3471
- }
3863
+ UserStatusChip: UserStatusChipComponent,
3864
+ MyAvatar: MyAvatarComponent
3865
+ },
3472
3866
  /*Layout: {
3473
3867
  Row: FormRow,
3474
3868
  },*/
3869
+ Widgets: {
3870
+ ProtectedLanguageSelector: LanguageSelector,
3871
+ PublicLanguageSelector
3872
+ }
3475
3873
  };
3476
3874
 
3477
3875
  // src/index.ts
package/dist/index.mjs CHANGED
@@ -844,6 +844,401 @@ var UserAvatarComponent = ({
844
844
  ] });
845
845
  };
846
846
 
847
+ // src/UI/components/data.display/MyAvatarComponent.tsx
848
+ import { useState as useState6 } from "react";
849
+ import { Avatar as Avatar2, Box as Box4, Popover as Popover2, Typography as Typography3, Skeleton } from "@mui/material";
850
+ import { Fragment as Fragment5, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
851
+ var getInitials2 = (name) => {
852
+ if (!name) return "?";
853
+ const parts = name.trim().split(" ");
854
+ if (parts.length >= 2) {
855
+ return `${parts[0][0]}${parts[1][0]}`.toUpperCase();
856
+ }
857
+ return name[0].toUpperCase();
858
+ };
859
+ var MyAvatarComponent = ({
860
+ size = 40,
861
+ disableBorder = false,
862
+ disableShadow = false,
863
+ disablePopover = false,
864
+ sx = {}
865
+ }) => {
866
+ const { profile: userProfile, loading: isLoading } = useAuth();
867
+ const [anchorEl, setAnchorEl] = useState6(null);
868
+ const handlePopoverOpen = (event) => {
869
+ if (!disablePopover) {
870
+ setAnchorEl(event.currentTarget);
871
+ }
872
+ };
873
+ const handlePopoverClose = () => {
874
+ setAnchorEl(null);
875
+ };
876
+ const isPopoverOpen = Boolean(anchorEl);
877
+ if (isLoading) {
878
+ return /* @__PURE__ */ jsx6(
879
+ Skeleton,
880
+ {
881
+ variant: "circular",
882
+ width: size,
883
+ height: size,
884
+ sx: { display: "inline-block", flexShrink: 0, ...sx }
885
+ }
886
+ );
887
+ }
888
+ const initials = getInitials2(userProfile?.display_name || "");
889
+ return /* @__PURE__ */ jsxs5(Fragment5, { children: [
890
+ /* @__PURE__ */ jsx6(
891
+ Avatar2,
892
+ {
893
+ src: userProfile?.avatar_url || void 0,
894
+ alt: userProfile?.display_name || "User Avatar",
895
+ onMouseEnter: handlePopoverOpen,
896
+ onMouseLeave: handlePopoverClose,
897
+ sx: {
898
+ width: size,
899
+ height: size,
900
+ fontSize: size * 0.4,
901
+ fontWeight: 600,
902
+ bgcolor: "primary.main",
903
+ color: "primary.contrastText",
904
+ cursor: disablePopover ? void 0 : "pointer",
905
+ transition: "transform 0.2s ease, box-shadow 0.2s ease",
906
+ border: disableBorder ? "none" : "2px solid",
907
+ borderColor: "background.paper",
908
+ boxShadow: disableShadow ? "none" : 2,
909
+ "&:hover": {
910
+ transform: disablePopover ? "none" : "scale(1.05)"
911
+ },
912
+ ...sx
913
+ },
914
+ children: !userProfile?.avatar_url && initials
915
+ }
916
+ ),
917
+ !disablePopover && /* @__PURE__ */ jsx6(
918
+ Popover2,
919
+ {
920
+ id: `avatar-popover-${userProfile?.id}`,
921
+ open: isPopoverOpen,
922
+ anchorEl,
923
+ anchorOrigin: {
924
+ vertical: "bottom",
925
+ horizontal: "center"
926
+ },
927
+ transformOrigin: {
928
+ vertical: "top",
929
+ horizontal: "center"
930
+ },
931
+ onClose: handlePopoverClose,
932
+ disableRestoreFocus: true,
933
+ disableAutoFocus: true,
934
+ disableEnforceFocus: true,
935
+ sx: {
936
+ pointerEvents: "none",
937
+ mt: 1
938
+ },
939
+ slotProps: {
940
+ paper: {
941
+ sx: {
942
+ p: 1.5,
943
+ borderRadius: 2,
944
+ boxShadow: 3
945
+ }
946
+ }
947
+ },
948
+ children: /* @__PURE__ */ jsxs5(Box4, { sx: { display: "flex", flexDirection: "column", alignItems: "center", gap: 0.5 }, children: [
949
+ /* @__PURE__ */ jsx6(Typography3, { variant: "subtitle2", sx: { fontWeight: 600 }, children: userProfile?.display_name }),
950
+ userProfile?.email && /* @__PURE__ */ jsx6(Typography3, { variant: "caption", color: "text.secondary", children: userProfile.email })
951
+ ] })
952
+ }
953
+ )
954
+ ] });
955
+ };
956
+
957
+ // src/i18n/LanguageSelector.tsx
958
+ import { useEffect as useEffect3, useState as useState7 } from "react";
959
+ import { useTranslation as useTranslation2 } from "react-i18next";
960
+ import {
961
+ Box as Box5,
962
+ Typography as Typography4,
963
+ CircularProgress,
964
+ IconButton as IconButton4,
965
+ Menu,
966
+ MenuItem,
967
+ Tooltip as Tooltip4
968
+ } from "@mui/material";
969
+ import { Fragment as Fragment6, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
970
+ var LanguageSelector = () => {
971
+ const supabase = useSupabase();
972
+ const { i18n } = useTranslation2();
973
+ const [languages, setLanguages] = useState7([]);
974
+ const [loading, setLoading] = useState7(true);
975
+ const { changeLanguage } = useLocalization();
976
+ const { settings: userSettings, setLanguage } = useUserSettings();
977
+ const [anchorEl, setAnchorEl] = useState7(null);
978
+ const open = Boolean(anchorEl);
979
+ useEffect3(() => {
980
+ const fetchLanguages = async () => {
981
+ try {
982
+ const { data, error } = await supabase.from("languages").select("code, name, svg_flag").eq("enabled", true);
983
+ if (error) throw error;
984
+ setLanguages(data || []);
985
+ if (userSettings.language) changeLanguage(userSettings.language);
986
+ } catch (err) {
987
+ console.error("\u041F\u043E\u043C\u0438\u043B\u043A\u0430 \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u043C\u043E\u0432:", err);
988
+ } finally {
989
+ setLoading(false);
990
+ }
991
+ };
992
+ fetchLanguages();
993
+ }, []);
994
+ const handleClick = (event) => {
995
+ setAnchorEl(event.currentTarget);
996
+ };
997
+ const handleClose = () => {
998
+ setAnchorEl(null);
999
+ };
1000
+ const handleSelectLanguage = (langCode) => {
1001
+ changeLanguage(langCode);
1002
+ setLanguage(langCode);
1003
+ handleClose();
1004
+ };
1005
+ if (loading) {
1006
+ return /* @__PURE__ */ jsx7(CircularProgress, { size: 20, color: "inherit", sx: { mx: 1 } });
1007
+ }
1008
+ const rawCurrentLang = i18n.language || "uk";
1009
+ const currentLangCode = rawCurrentLang.split("-")[0];
1010
+ const currentLang = languages.find((l) => l.code === currentLangCode) || languages[0];
1011
+ return /* @__PURE__ */ jsxs6(Fragment6, { children: [
1012
+ /* @__PURE__ */ jsx7(Tooltip4, { title: currentLang ? currentLang.name : "\u041C\u043E\u0432\u0430", arrow: true, placement: "bottom", children: /* @__PURE__ */ jsx7(
1013
+ IconButton4,
1014
+ {
1015
+ onClick: handleClick,
1016
+ size: "small",
1017
+ "aria-controls": open ? "language-menu" : void 0,
1018
+ "aria-haspopup": "true",
1019
+ "aria-expanded": open ? "true" : void 0,
1020
+ sx: {
1021
+ p: 1,
1022
+ borderRadius: 2,
1023
+ transition: "background-color 0.2s",
1024
+ "&:hover": {
1025
+ backgroundColor: "action.hover"
1026
+ }
1027
+ },
1028
+ children: currentLang?.svg_flag && /* @__PURE__ */ jsx7(
1029
+ Box5,
1030
+ {
1031
+ sx: {
1032
+ width: 24,
1033
+ height: 16,
1034
+ borderRadius: "3px",
1035
+ overflow: "hidden",
1036
+ flexShrink: 0,
1037
+ boxShadow: "0 0 2px rgba(0,0,0,0.3)",
1038
+ display: "flex",
1039
+ alignItems: "center",
1040
+ justifyContent: "center",
1041
+ "& svg": { width: "100%", height: "100%", display: "block", objectFit: "cover" }
1042
+ },
1043
+ dangerouslySetInnerHTML: { __html: currentLang.svg_flag }
1044
+ }
1045
+ )
1046
+ }
1047
+ ) }),
1048
+ /* @__PURE__ */ jsx7(
1049
+ Menu,
1050
+ {
1051
+ id: "language-menu",
1052
+ anchorEl,
1053
+ open,
1054
+ onClose: handleClose,
1055
+ slotProps: {
1056
+ paper: {
1057
+ elevation: 3,
1058
+ sx: {
1059
+ mt: 1,
1060
+ minWidth: 150,
1061
+ borderRadius: 2
1062
+ }
1063
+ }
1064
+ },
1065
+ transformOrigin: { horizontal: "right", vertical: "top" },
1066
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
1067
+ children: languages.map((lang) => {
1068
+ const isSelected = lang.code === currentLangCode;
1069
+ return /* @__PURE__ */ jsxs6(
1070
+ MenuItem,
1071
+ {
1072
+ selected: isSelected,
1073
+ onClick: () => handleSelectLanguage(lang.code),
1074
+ sx: {
1075
+ display: "flex",
1076
+ alignItems: "center",
1077
+ gap: 1.5,
1078
+ py: 1,
1079
+ px: 2
1080
+ },
1081
+ children: [
1082
+ /* @__PURE__ */ jsx7(
1083
+ Box5,
1084
+ {
1085
+ sx: {
1086
+ width: 22,
1087
+ height: 15,
1088
+ borderRadius: "2px",
1089
+ overflow: "hidden",
1090
+ flexShrink: 0,
1091
+ boxShadow: "0 0 1px rgba(0,0,0,0.4)",
1092
+ "& svg": { width: "100%", height: "100%", display: "block" }
1093
+ },
1094
+ dangerouslySetInnerHTML: { __html: lang.svg_flag }
1095
+ }
1096
+ ),
1097
+ /* @__PURE__ */ jsx7(Typography4, { variant: "body2", sx: { fontWeight: isSelected ? 600 : 400 }, children: lang.name })
1098
+ ]
1099
+ },
1100
+ lang.code
1101
+ );
1102
+ })
1103
+ }
1104
+ )
1105
+ ] });
1106
+ };
1107
+ var PublicLanguageSelector = () => {
1108
+ const supabase = useSupabase();
1109
+ const { i18n } = useTranslation2();
1110
+ const { changeLanguage } = useLocalization();
1111
+ const [languages, setLanguages] = useState7([]);
1112
+ const [loading, setLoading] = useState7(true);
1113
+ const [anchorEl, setAnchorEl] = useState7(null);
1114
+ const open = Boolean(anchorEl);
1115
+ useEffect3(() => {
1116
+ const fetchLanguages = async () => {
1117
+ try {
1118
+ const { data, error } = await supabase.from("languages").select("code, name, svg_flag").eq("enabled", true);
1119
+ if (error) throw error;
1120
+ setLanguages(data || []);
1121
+ } catch (err) {
1122
+ console.error("\u041F\u043E\u043C\u0438\u043B\u043A\u0430 \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u043C\u043E\u0432:", err);
1123
+ } finally {
1124
+ setLoading(false);
1125
+ }
1126
+ };
1127
+ fetchLanguages();
1128
+ }, []);
1129
+ const handleClick = (event) => {
1130
+ setAnchorEl(event.currentTarget);
1131
+ };
1132
+ const handleClose = () => {
1133
+ setAnchorEl(null);
1134
+ };
1135
+ const handleSelectLanguage = (langCode) => {
1136
+ changeLanguage(langCode);
1137
+ handleClose();
1138
+ };
1139
+ if (loading) {
1140
+ return /* @__PURE__ */ jsx7(CircularProgress, { size: 20, color: "inherit", sx: { mx: 1 } });
1141
+ }
1142
+ const rawCurrentLang = i18n.language || "uk";
1143
+ const currentLangCode = rawCurrentLang.split("-")[0];
1144
+ const currentLang = languages.find((l) => l.code === currentLangCode) || languages[0];
1145
+ return /* @__PURE__ */ jsxs6(Fragment6, { children: [
1146
+ /* @__PURE__ */ jsx7(Tooltip4, { title: currentLang ? currentLang.name : "\u041C\u043E\u0432\u0430", arrow: true, placement: "bottom", children: /* @__PURE__ */ jsx7(
1147
+ IconButton4,
1148
+ {
1149
+ onClick: handleClick,
1150
+ size: "small",
1151
+ "aria-controls": open ? "language-menu" : void 0,
1152
+ "aria-haspopup": "true",
1153
+ "aria-expanded": open ? "true" : void 0,
1154
+ sx: {
1155
+ p: 1,
1156
+ borderRadius: 2,
1157
+ transition: "background-color 0.2s",
1158
+ "&:hover": {
1159
+ backgroundColor: "action.hover"
1160
+ }
1161
+ },
1162
+ children: currentLang?.svg_flag && /* @__PURE__ */ jsx7(
1163
+ Box5,
1164
+ {
1165
+ sx: {
1166
+ width: 24,
1167
+ height: 16,
1168
+ borderRadius: "3px",
1169
+ overflow: "hidden",
1170
+ flexShrink: 0,
1171
+ boxShadow: "0 0 2px rgba(0,0,0,0.3)",
1172
+ display: "flex",
1173
+ alignItems: "center",
1174
+ justifyContent: "center",
1175
+ "& svg": { width: "100%", height: "100%", display: "block", objectFit: "cover" }
1176
+ },
1177
+ dangerouslySetInnerHTML: { __html: currentLang.svg_flag }
1178
+ }
1179
+ )
1180
+ }
1181
+ ) }),
1182
+ /* @__PURE__ */ jsx7(
1183
+ Menu,
1184
+ {
1185
+ id: "language-menu",
1186
+ anchorEl,
1187
+ open,
1188
+ onClose: handleClose,
1189
+ slotProps: {
1190
+ paper: {
1191
+ elevation: 3,
1192
+ sx: {
1193
+ mt: 1,
1194
+ minWidth: 150,
1195
+ borderRadius: 2
1196
+ }
1197
+ }
1198
+ },
1199
+ transformOrigin: { horizontal: "right", vertical: "top" },
1200
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
1201
+ children: languages.map((lang) => {
1202
+ const isSelected = lang.code === currentLangCode;
1203
+ return /* @__PURE__ */ jsxs6(
1204
+ MenuItem,
1205
+ {
1206
+ selected: isSelected,
1207
+ onClick: () => handleSelectLanguage(lang.code),
1208
+ sx: {
1209
+ display: "flex",
1210
+ alignItems: "center",
1211
+ gap: 1.5,
1212
+ py: 1,
1213
+ px: 2
1214
+ },
1215
+ children: [
1216
+ /* @__PURE__ */ jsx7(
1217
+ Box5,
1218
+ {
1219
+ sx: {
1220
+ width: 22,
1221
+ height: 15,
1222
+ borderRadius: "2px",
1223
+ overflow: "hidden",
1224
+ flexShrink: 0,
1225
+ boxShadow: "0 0 1px rgba(0,0,0,0.4)",
1226
+ "& svg": { width: "100%", height: "100%", display: "block" }
1227
+ },
1228
+ dangerouslySetInnerHTML: { __html: lang.svg_flag }
1229
+ }
1230
+ ),
1231
+ /* @__PURE__ */ jsx7(Typography4, { variant: "body2", sx: { fontWeight: isSelected ? 600 : 400 }, children: lang.name })
1232
+ ]
1233
+ },
1234
+ lang.code
1235
+ );
1236
+ })
1237
+ }
1238
+ )
1239
+ ] });
1240
+ };
1241
+
847
1242
  // src/UI/index.ts
848
1243
  var UI = {
849
1244
  Inputs: {
@@ -860,11 +1255,16 @@ var UI = {
860
1255
  UserAvatar: UserAvatarComponent,
861
1256
  UserPopover: UserPopoverComponent_default,
862
1257
  UserRoleChip: UserRoleChipComponent,
863
- UserStatusChip: UserStatusChipComponent
864
- }
1258
+ UserStatusChip: UserStatusChipComponent,
1259
+ MyAvatar: MyAvatarComponent
1260
+ },
865
1261
  /*Layout: {
866
1262
  Row: FormRow,
867
1263
  },*/
1264
+ Widgets: {
1265
+ ProtectedLanguageSelector: LanguageSelector,
1266
+ PublicLanguageSelector
1267
+ }
868
1268
  };
869
1269
  export {
870
1270
  AIProvider,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serhii.olishevskyi/docs",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "DOCS.UA react packages",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",