@serhii.olishevskyi/docs 1.2.21 → 1.2.23

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
@@ -1,5 +1,5 @@
1
1
  import * as React$1 from 'react';
2
- import React__default, { ReactNode } from 'react';
2
+ import React__default, { ReactNode, PropsWithChildren } from 'react';
3
3
  import { TextFieldProps, SelectProps, SelectChangeEvent, FormControlLabelProps, SwitchProps, CheckboxProps, ButtonProps, IconButtonProps, TooltipProps, SxProps, Theme, TypographyProps } from '@mui/material';
4
4
  import { SystemProps } from '@mui/system';
5
5
  import * as _supabase_supabase_js from '@supabase/supabase-js';
@@ -385,6 +385,48 @@ interface ExpandableNoteComponentProps extends Omit<TypographyProps, 'children'>
385
385
  label?: React__default.ReactNode;
386
386
  }
387
387
 
388
+ type TextTruncateMode = 'truncate' | 'clamp-2' | 'wrap';
389
+ interface HeaderActionItem {
390
+ id: string;
391
+ label: string;
392
+ icon?: React__default.ReactNode;
393
+ onClick: () => void;
394
+ color?: 'inherit' | 'primary' | 'secondary' | 'error' | 'warning' | 'info' | 'success';
395
+ disabled?: boolean;
396
+ divider?: boolean;
397
+ }
398
+ interface PrimaryActionProps {
399
+ label: string;
400
+ onClick: () => void;
401
+ icon?: React__default.ReactNode;
402
+ variant?: 'contained' | 'outlined' | 'text';
403
+ color?: 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning';
404
+ disabled?: boolean;
405
+ }
406
+ interface RefreshActionProps {
407
+ onClick: () => void;
408
+ disabled?: boolean;
409
+ }
410
+ interface PageHeaderProps {
411
+ overrideNamespace?: string;
412
+ icon?: React__default.ReactNode;
413
+ title: string;
414
+ subtitle?: string;
415
+ tag?: React__default.ReactNode;
416
+ titleTruncateMode?: TextTruncateMode;
417
+ subtitleTruncateMode?: TextTruncateMode;
418
+ customFragment?: React__default.JSX.Element;
419
+ primaryAction?: PrimaryActionProps;
420
+ secondaryActions?: HeaderActionItem[];
421
+ refreshAction?: RefreshActionProps;
422
+ mobileBreakpoint?: 'xs' | 'sm' | 'md';
423
+ forceMobileView?: boolean;
424
+ sx?: SxProps<Theme>;
425
+ }
426
+ type PageComponentProps = {
427
+ header?: PageHeaderProps;
428
+ };
429
+
388
430
  interface PageLink {
389
431
  text: ReactNode;
390
432
  href?: string;
@@ -415,6 +457,22 @@ type CollapsedWidgetContainerProps = {
415
457
  onExpanded?: (expanded: boolean) => void;
416
458
  };
417
459
 
460
+ type FilterWidth = '25%' | '33%' | '50%' | '66%' | '75%' | '100%';
461
+ interface FilterSectionProps extends PropsWithChildren {
462
+ /** Ширина самой секции на десктопе (по умолчанию '100%') */
463
+ width?: FilterWidth;
464
+ /** Ширина самой секции на мобильном (по умолчанию '100%') */
465
+ mobileWidth?: FilterWidth;
466
+ /** Ширина каждого элемента ВНУТРИ секции на десктопе (по умолчанию '50%') */
467
+ itemWidth?: FilterWidth;
468
+ /** Ширина каждого элемента ВНУТРИ секции на мобильном (по умолчанию '100%') */
469
+ itemMobileWidth?: FilterWidth;
470
+ }
471
+ interface FilterContainerWidgetProps extends PropsWithChildren {
472
+ sections?: ReactNode[];
473
+ sx?: SxProps<Theme>;
474
+ }
475
+
418
476
  interface ErrorAlertWidgetProps {
419
477
  overrideNamespace?: string;
420
478
  title?: ReactNode;
@@ -449,6 +507,9 @@ declare const UI: {
449
507
  MyAvatar: React$1.FC<MyAvatarComponentProps>;
450
508
  ExpandableNote: React$1.FC<ExpandableNoteComponentProps>;
451
509
  };
510
+ Layout: {
511
+ Page: React$1.FC<React$1.PropsWithChildren<PageComponentProps>>;
512
+ };
452
513
  Widgets: {
453
514
  Locales: {
454
515
  ProtectedLanguageSelector: React$1.FC<{}>;
@@ -457,6 +518,8 @@ declare const UI: {
457
518
  Pages: {
458
519
  PageLinkWidget: React$1.FC<PageLinkWidgetProps>;
459
520
  CollapsedWidgetContainer: (props: React$1.PropsWithChildren<CollapsedWidgetContainerProps>) => React$1.JSX.Element;
521
+ FilterContainerWidget: React$1.FC<FilterContainerWidgetProps>;
522
+ FilterSection: React$1.FC<FilterSectionProps>;
460
523
  };
461
524
  Misc: {
462
525
  ErrorAlertWidget: React$1.FC<ErrorAlertWidgetProps>;
@@ -814,4 +877,4 @@ declare const Utils: {
814
877
  };
815
878
  };
816
879
 
817
- export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ButtonComponentProps, type CheckboxComponentProps, type CollapsedWidgetContainerProps, type CompanyMember, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps, type ErrorAlertWidgetProps, type ExpandableNoteComponentProps, type FullCompanyMemberItem, Hooks, type Language, LocalizationProvider, type Membership, type MembershipRole, type MyAvatarComponentProps, type NumberInputComponentProps, type PageLink, type PageLinkWidgetProps, Providers, type RefreshButtonComponentProps, type SaveCompanyUserPayload, type SearchComponentProps, type SelectComponentProps, type SelectOption, Services, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type SwitchComponentProps, type TextFieldComponentProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps, type UserGroupedSessions, type UserPopoverComponentProps, type UserProfile, type UserRoleChipComponentProps, type UserSessionItem, type UserSettings, UserSettingsProvider, type UserStatusChipComponentProps, type UserStatusType, Utils, defaultUserSettings, initI18n, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
880
+ export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ButtonComponentProps, type CheckboxComponentProps, type CollapsedWidgetContainerProps, type CompanyMember, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps, type ErrorAlertWidgetProps, type ExpandableNoteComponentProps, type FilterContainerWidgetProps, type FilterSectionProps, type FilterWidth, type FullCompanyMemberItem, type HeaderActionItem, Hooks, type Language, LocalizationProvider, type Membership, type MembershipRole, type MyAvatarComponentProps, type NumberInputComponentProps, type PageComponentProps, type PageHeaderProps, type PageLink, type PageLinkWidgetProps, type PrimaryActionProps, Providers, type RefreshActionProps, type RefreshButtonComponentProps, type SaveCompanyUserPayload, type SearchComponentProps, type SelectComponentProps, type SelectOption, Services, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type SwitchComponentProps, type TextFieldComponentProps, type TextTruncateMode, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps, type UserGroupedSessions, type UserPopoverComponentProps, type UserProfile, type UserRoleChipComponentProps, type UserSessionItem, type UserSettings, UserSettingsProvider, type UserStatusChipComponentProps, type UserStatusType, Utils, defaultUserSettings, initI18n, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React$1 from 'react';
2
- import React__default, { ReactNode } from 'react';
2
+ import React__default, { ReactNode, PropsWithChildren } from 'react';
3
3
  import { TextFieldProps, SelectProps, SelectChangeEvent, FormControlLabelProps, SwitchProps, CheckboxProps, ButtonProps, IconButtonProps, TooltipProps, SxProps, Theme, TypographyProps } from '@mui/material';
4
4
  import { SystemProps } from '@mui/system';
5
5
  import * as _supabase_supabase_js from '@supabase/supabase-js';
@@ -385,6 +385,48 @@ interface ExpandableNoteComponentProps extends Omit<TypographyProps, 'children'>
385
385
  label?: React__default.ReactNode;
386
386
  }
387
387
 
388
+ type TextTruncateMode = 'truncate' | 'clamp-2' | 'wrap';
389
+ interface HeaderActionItem {
390
+ id: string;
391
+ label: string;
392
+ icon?: React__default.ReactNode;
393
+ onClick: () => void;
394
+ color?: 'inherit' | 'primary' | 'secondary' | 'error' | 'warning' | 'info' | 'success';
395
+ disabled?: boolean;
396
+ divider?: boolean;
397
+ }
398
+ interface PrimaryActionProps {
399
+ label: string;
400
+ onClick: () => void;
401
+ icon?: React__default.ReactNode;
402
+ variant?: 'contained' | 'outlined' | 'text';
403
+ color?: 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning';
404
+ disabled?: boolean;
405
+ }
406
+ interface RefreshActionProps {
407
+ onClick: () => void;
408
+ disabled?: boolean;
409
+ }
410
+ interface PageHeaderProps {
411
+ overrideNamespace?: string;
412
+ icon?: React__default.ReactNode;
413
+ title: string;
414
+ subtitle?: string;
415
+ tag?: React__default.ReactNode;
416
+ titleTruncateMode?: TextTruncateMode;
417
+ subtitleTruncateMode?: TextTruncateMode;
418
+ customFragment?: React__default.JSX.Element;
419
+ primaryAction?: PrimaryActionProps;
420
+ secondaryActions?: HeaderActionItem[];
421
+ refreshAction?: RefreshActionProps;
422
+ mobileBreakpoint?: 'xs' | 'sm' | 'md';
423
+ forceMobileView?: boolean;
424
+ sx?: SxProps<Theme>;
425
+ }
426
+ type PageComponentProps = {
427
+ header?: PageHeaderProps;
428
+ };
429
+
388
430
  interface PageLink {
389
431
  text: ReactNode;
390
432
  href?: string;
@@ -415,6 +457,22 @@ type CollapsedWidgetContainerProps = {
415
457
  onExpanded?: (expanded: boolean) => void;
416
458
  };
417
459
 
460
+ type FilterWidth = '25%' | '33%' | '50%' | '66%' | '75%' | '100%';
461
+ interface FilterSectionProps extends PropsWithChildren {
462
+ /** Ширина самой секции на десктопе (по умолчанию '100%') */
463
+ width?: FilterWidth;
464
+ /** Ширина самой секции на мобильном (по умолчанию '100%') */
465
+ mobileWidth?: FilterWidth;
466
+ /** Ширина каждого элемента ВНУТРИ секции на десктопе (по умолчанию '50%') */
467
+ itemWidth?: FilterWidth;
468
+ /** Ширина каждого элемента ВНУТРИ секции на мобильном (по умолчанию '100%') */
469
+ itemMobileWidth?: FilterWidth;
470
+ }
471
+ interface FilterContainerWidgetProps extends PropsWithChildren {
472
+ sections?: ReactNode[];
473
+ sx?: SxProps<Theme>;
474
+ }
475
+
418
476
  interface ErrorAlertWidgetProps {
419
477
  overrideNamespace?: string;
420
478
  title?: ReactNode;
@@ -449,6 +507,9 @@ declare const UI: {
449
507
  MyAvatar: React$1.FC<MyAvatarComponentProps>;
450
508
  ExpandableNote: React$1.FC<ExpandableNoteComponentProps>;
451
509
  };
510
+ Layout: {
511
+ Page: React$1.FC<React$1.PropsWithChildren<PageComponentProps>>;
512
+ };
452
513
  Widgets: {
453
514
  Locales: {
454
515
  ProtectedLanguageSelector: React$1.FC<{}>;
@@ -457,6 +518,8 @@ declare const UI: {
457
518
  Pages: {
458
519
  PageLinkWidget: React$1.FC<PageLinkWidgetProps>;
459
520
  CollapsedWidgetContainer: (props: React$1.PropsWithChildren<CollapsedWidgetContainerProps>) => React$1.JSX.Element;
521
+ FilterContainerWidget: React$1.FC<FilterContainerWidgetProps>;
522
+ FilterSection: React$1.FC<FilterSectionProps>;
460
523
  };
461
524
  Misc: {
462
525
  ErrorAlertWidget: React$1.FC<ErrorAlertWidgetProps>;
@@ -814,4 +877,4 @@ declare const Utils: {
814
877
  };
815
878
  };
816
879
 
817
- export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ButtonComponentProps, type CheckboxComponentProps, type CollapsedWidgetContainerProps, type CompanyMember, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps, type ErrorAlertWidgetProps, type ExpandableNoteComponentProps, type FullCompanyMemberItem, Hooks, type Language, LocalizationProvider, type Membership, type MembershipRole, type MyAvatarComponentProps, type NumberInputComponentProps, type PageLink, type PageLinkWidgetProps, Providers, type RefreshButtonComponentProps, type SaveCompanyUserPayload, type SearchComponentProps, type SelectComponentProps, type SelectOption, Services, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type SwitchComponentProps, type TextFieldComponentProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps, type UserGroupedSessions, type UserPopoverComponentProps, type UserProfile, type UserRoleChipComponentProps, type UserSessionItem, type UserSettings, UserSettingsProvider, type UserStatusChipComponentProps, type UserStatusType, Utils, defaultUserSettings, initI18n, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
880
+ export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ButtonComponentProps, type CheckboxComponentProps, type CollapsedWidgetContainerProps, type CompanyMember, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps, type ErrorAlertWidgetProps, type ExpandableNoteComponentProps, type FilterContainerWidgetProps, type FilterSectionProps, type FilterWidth, type FullCompanyMemberItem, type HeaderActionItem, Hooks, type Language, LocalizationProvider, type Membership, type MembershipRole, type MyAvatarComponentProps, type NumberInputComponentProps, type PageComponentProps, type PageHeaderProps, type PageLink, type PageLinkWidgetProps, type PrimaryActionProps, Providers, type RefreshActionProps, type RefreshButtonComponentProps, type SaveCompanyUserPayload, type SearchComponentProps, type SelectComponentProps, type SelectOption, Services, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type SwitchComponentProps, type TextFieldComponentProps, type TextTruncateMode, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps, type UserGroupedSessions, type UserPopoverComponentProps, type UserProfile, type UserRoleChipComponentProps, type UserSessionItem, type UserSettings, UserSettingsProvider, type UserStatusChipComponentProps, type UserStatusType, Utils, defaultUserSettings, initI18n, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
package/dist/index.js CHANGED
@@ -5131,6 +5131,380 @@ var ButtonComponent = (0, import_react31.forwardRef)(
5131
5131
  );
5132
5132
  ButtonComponent.displayName = "ButtonComponent";
5133
5133
 
5134
+ // src/UI/components/layout/PageComponent.tsx
5135
+ var import_react32 = __toESM(require("react"));
5136
+ var import_material28 = require("@mui/material");
5137
+ var import_MoreVert = __toESM(require("@mui/icons-material/MoreVert"));
5138
+ var import_KeyboardArrowDown = __toESM(require("@mui/icons-material/KeyboardArrowDown"));
5139
+ var import_icons_material5 = require("@mui/icons-material");
5140
+ init_context();
5141
+ init_TranslatableTooltipComponent();
5142
+ var import_jsx_runtime38 = require("react/jsx-runtime");
5143
+ var getTruncateStyles = (mode = "truncate") => {
5144
+ switch (mode) {
5145
+ case "truncate":
5146
+ return { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", display: "block" };
5147
+ case "clamp-2":
5148
+ return { display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "normal" };
5149
+ case "wrap":
5150
+ default:
5151
+ return { whiteSpace: "normal", wordBreak: "break-word" };
5152
+ }
5153
+ };
5154
+ var PageHeader = ({
5155
+ overrideNamespace,
5156
+ icon,
5157
+ title,
5158
+ subtitle,
5159
+ tag,
5160
+ titleTruncateMode = "truncate",
5161
+ subtitleTruncateMode = "truncate",
5162
+ customFragment,
5163
+ primaryAction,
5164
+ secondaryActions = [],
5165
+ refreshAction,
5166
+ mobileBreakpoint = "sm",
5167
+ forceMobileView,
5168
+ sx = []
5169
+ }) => {
5170
+ const theme = (0, import_material28.useTheme)();
5171
+ const mediaIsMobile = (0, import_material28.useMediaQuery)(theme.breakpoints.down(mobileBreakpoint));
5172
+ const isMobile = forceMobileView ?? mediaIsMobile;
5173
+ const [anchorEl, setAnchorEl] = (0, import_react32.useState)(null);
5174
+ const isMenuOpen = Boolean(anchorEl);
5175
+ const handleOpenMenu = (e) => setAnchorEl(e.currentTarget);
5176
+ const handleCloseMenu = () => setAnchorEl(null);
5177
+ const handleMenuAction = (cb) => {
5178
+ handleCloseMenu();
5179
+ cb();
5180
+ };
5181
+ const hasSecondaryActions = secondaryActions.length > 0;
5182
+ const hasActions = Boolean(primaryAction || hasSecondaryActions || refreshAction);
5183
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
5184
+ import_material28.Box,
5185
+ {
5186
+ sx: [
5187
+ {
5188
+ display: "flex",
5189
+ alignItems: "center",
5190
+ justifyContent: "space-between",
5191
+ width: "100%",
5192
+ gap: { xs: 1.5, sm: 2.5 },
5193
+ py: 1.5,
5194
+ px: { xs: 1.5, sm: 2 },
5195
+ borderRadius: 2,
5196
+ backgroundColor: "background.paper",
5197
+ boxShadow: "0 1px 3px rgba(0,0,0,0.05)"
5198
+ },
5199
+ ...Array.isArray(sx) ? sx : [sx]
5200
+ ],
5201
+ children: [
5202
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_material28.Box, { sx: { display: "flex", alignItems: "center", gap: { xs: 1.5, sm: 2 }, minWidth: 0, flex: 1 }, children: [
5203
+ icon && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5204
+ import_material28.Box,
5205
+ {
5206
+ sx: {
5207
+ display: "flex",
5208
+ alignItems: "center",
5209
+ justifyContent: "center",
5210
+ width: { xs: 40, sm: 48 },
5211
+ height: { xs: 40, sm: 48 },
5212
+ borderRadius: 2.5,
5213
+ backgroundColor: "primary.50",
5214
+ color: "primary.main",
5215
+ flexShrink: 0,
5216
+ border: "1px solid",
5217
+ borderColor: "primary.100",
5218
+ "& svg": { fontSize: { xs: 22, sm: 26 } }
5219
+ },
5220
+ children: icon
5221
+ }
5222
+ ),
5223
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_material28.Box, { sx: { minWidth: 0, flex: 1 }, children: [
5224
+ tag && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.Box, { sx: { mb: 0.5, display: "flex", alignItems: "center" }, children: tag }),
5225
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.Tooltip, { title: titleTruncateMode === "truncate" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslateText, { children: title }) : void 0, arrow: true, placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5226
+ import_material28.Typography,
5227
+ {
5228
+ variant: "h5",
5229
+ component: "h1",
5230
+ sx: {
5231
+ fontWeight: 700,
5232
+ color: "text.primary",
5233
+ fontSize: { xs: "1.125rem", sm: "1.35rem" },
5234
+ lineHeight: 1.3,
5235
+ ...getTruncateStyles(titleTruncateMode)
5236
+ },
5237
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslateText, { children: title })
5238
+ }
5239
+ ) }),
5240
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.Tooltip, { title: subtitleTruncateMode === "truncate" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslateText, { children: subtitle }) : void 0, arrow: true, placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5241
+ import_material28.Typography,
5242
+ {
5243
+ variant: "body2",
5244
+ sx: {
5245
+ color: "text.secondary",
5246
+ mt: 0.25,
5247
+ fontSize: { xs: "0.8125rem", sm: "0.875rem" },
5248
+ lineHeight: 1.4,
5249
+ ...getTruncateStyles(subtitleTruncateMode)
5250
+ },
5251
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslateText, { children: subtitle })
5252
+ }
5253
+ ) })
5254
+ ] })
5255
+ ] }),
5256
+ hasActions && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.Box, { sx: { flexShrink: 0, display: "flex", alignItems: "center", gap: 1 }, children: isMobile ? (
5257
+ /* MOBILE: IconButton with MoreVertIcon showing ALL actions inside Menu */
5258
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
5259
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslatableTooltipComponent, { title: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Actions" }), children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5260
+ import_material28.IconButton,
5261
+ {
5262
+ onClick: handleOpenMenu,
5263
+ sx: { border: "1px solid", borderColor: "divider", borderRadius: 2, p: 1 },
5264
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_MoreVert.default, {})
5265
+ }
5266
+ ) }),
5267
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
5268
+ import_material28.Menu,
5269
+ {
5270
+ anchorEl,
5271
+ open: isMenuOpen,
5272
+ onClose: handleCloseMenu,
5273
+ transformOrigin: { horizontal: "right", vertical: "top" },
5274
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
5275
+ slotProps: { paper: { elevation: 4, sx: { minWidth: 200, borderRadius: 2, mt: 1, p: 0.5 } } },
5276
+ children: [
5277
+ primaryAction && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
5278
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
5279
+ import_material28.MenuItem,
5280
+ {
5281
+ onClick: () => handleMenuAction(primaryAction.onClick),
5282
+ disabled: primaryAction.disabled,
5283
+ sx: {
5284
+ borderRadius: 1.5,
5285
+ fontWeight: 700,
5286
+ backgroundColor: "primary.50",
5287
+ color: primaryAction.color ? `${primaryAction.color}.main` : "primary.main",
5288
+ py: 1.2,
5289
+ px: 1.5
5290
+ },
5291
+ children: [
5292
+ primaryAction.icon && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.ListItemIcon, { sx: { color: "inherit", minWidth: 32 }, children: primaryAction.icon }),
5293
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.ListItemText, { primary: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslateText, { children: primaryAction.label }), slotProps: { primary: { sx: { fontWeight: 700, fontSize: "0.9rem" } } } })
5294
+ ]
5295
+ }
5296
+ ),
5297
+ hasSecondaryActions && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.Divider, { sx: { my: 0.75 } })
5298
+ ] }),
5299
+ secondaryActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react32.default.Fragment, { children: [
5300
+ action.divider && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.Divider, { sx: { my: 0.5 } }),
5301
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
5302
+ import_material28.MenuItem,
5303
+ {
5304
+ onClick: () => handleMenuAction(action.onClick),
5305
+ disabled: action.disabled,
5306
+ sx: { borderRadius: 1, py: 1, px: 1.5, color: action.color ? `${action.color}.main` : "text.primary" },
5307
+ children: [
5308
+ action.icon && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.ListItemIcon, { sx: { color: action.color ? `${action.color}.main` : "text.secondary", minWidth: 32 }, children: action.icon }),
5309
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.ListItemText, { primary: action.label, slotProps: { primary: { sx: { fontSize: "0.875rem" } } } })
5310
+ ]
5311
+ }
5312
+ )
5313
+ ] }, action.id)),
5314
+ refreshAction && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
5315
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.Divider, { sx: { my: 0.5 } }),
5316
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
5317
+ import_material28.MenuItem,
5318
+ {
5319
+ onClick: () => handleMenuAction(refreshAction.onClick),
5320
+ disabled: refreshAction.disabled,
5321
+ sx: {
5322
+ borderRadius: 1.5,
5323
+ fontWeight: 700,
5324
+ backgroundColor: "primary.50",
5325
+ py: 1.2,
5326
+ px: 1.5
5327
+ },
5328
+ children: [
5329
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.ListItemIcon, { sx: { color: "inherit", minWidth: 32 }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_icons_material5.Refresh, {}) }),
5330
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.ListItemText, { primary: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Refresh" }), slotProps: { primary: { sx: { fontWeight: 700, fontSize: "0.9rem" } } } })
5331
+ ]
5332
+ }
5333
+ )
5334
+ ] })
5335
+ ]
5336
+ }
5337
+ )
5338
+ ] })
5339
+ ) : (
5340
+ /* DESKTOP: Primary Action Button + Secondary Actions Dropdown Button */
5341
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_material28.Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
5342
+ (primaryAction || refreshAction) && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_material28.Stack, { direction: "row", spacing: 2, children: [
5343
+ primaryAction && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5344
+ import_material28.Button,
5345
+ {
5346
+ variant: primaryAction.variant || "contained",
5347
+ color: primaryAction.color || "primary",
5348
+ onClick: primaryAction.onClick,
5349
+ disabled: primaryAction.disabled,
5350
+ startIcon: primaryAction.icon,
5351
+ sx: { borderRadius: 2, textTransform: "none", fontWeight: 600, px: 2, py: 0.85 },
5352
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslateText, { children: primaryAction.label })
5353
+ }
5354
+ ),
5355
+ refreshAction && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslatableTooltipComponent, { title: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Refresh" }), children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5356
+ import_material28.Fab,
5357
+ {
5358
+ color: "default",
5359
+ onClick: refreshAction.onClick,
5360
+ disabled: refreshAction.disabled,
5361
+ "aria-label": "refresh",
5362
+ size: "small",
5363
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_icons_material5.Refresh, {})
5364
+ }
5365
+ ) })
5366
+ ] }),
5367
+ hasSecondaryActions && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
5368
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5369
+ import_material28.IconButton,
5370
+ {
5371
+ onClick: handleOpenMenu,
5372
+ sx: { border: "1px solid", borderColor: "divider", borderRadius: 2, p: 1 },
5373
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_KeyboardArrowDown.default, {})
5374
+ }
5375
+ ),
5376
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5377
+ import_material28.Menu,
5378
+ {
5379
+ anchorEl,
5380
+ open: isMenuOpen,
5381
+ onClose: handleCloseMenu,
5382
+ transformOrigin: { horizontal: "right", vertical: "top" },
5383
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
5384
+ slotProps: { paper: { elevation: 4, sx: { minWidth: 180, borderRadius: 2, mt: 1, p: 0.5 } } },
5385
+ children: secondaryActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react32.default.Fragment, { children: [
5386
+ action.divider && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.Divider, { sx: { my: 0.5 } }),
5387
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
5388
+ import_material28.MenuItem,
5389
+ {
5390
+ onClick: () => handleMenuAction(action.onClick),
5391
+ disabled: action.disabled,
5392
+ sx: { borderRadius: 1, py: 0.85, color: action.color ? `${action.color}.main` : "text.primary" },
5393
+ children: [
5394
+ action.icon && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.ListItemIcon, { sx: { color: action.color ? `${action.color}.main` : "text.secondary", minWidth: 32 }, children: action.icon }),
5395
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material28.ListItemText, { primary: action.label, slotProps: { primary: { sx: { fontSize: "0.875rem" } } } })
5396
+ ]
5397
+ }
5398
+ )
5399
+ ] }, action.id))
5400
+ }
5401
+ )
5402
+ ] })
5403
+ ] })
5404
+ ) }),
5405
+ customFragment && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_jsx_runtime38.Fragment, { children: customFragment })
5406
+ ]
5407
+ }
5408
+ );
5409
+ };
5410
+ var PageContainer = ({ children }) => {
5411
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5412
+ import_material28.Box,
5413
+ {
5414
+ sx: {
5415
+ width: "100%",
5416
+ boxSizing: "border-box",
5417
+ p: { xs: 1.5, sm: 3 },
5418
+ backgroundColor: "background.paper"
5419
+ },
5420
+ children
5421
+ }
5422
+ );
5423
+ };
5424
+ var PageComponent = ({
5425
+ header,
5426
+ children
5427
+ }) => {
5428
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
5429
+ import_material28.Box,
5430
+ {
5431
+ sx: {
5432
+ //pt: 2,
5433
+ flexGrow: 1,
5434
+ minHeight: 0,
5435
+ overflowY: "auto",
5436
+ boxSizing: "border-box"
5437
+ },
5438
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(PageContainer, { children: [
5439
+ header && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PageHeader, { ...header }),
5440
+ children
5441
+ ] })
5442
+ }
5443
+ );
5444
+ };
5445
+
5446
+ // src/UI/widgets/page/FilterContainerWidget.tsx
5447
+ var import_react33 = __toESM(require("react"));
5448
+ var import_material29 = require("@mui/material");
5449
+ var import_jsx_runtime39 = (
5450
+ /* Автоматическая обертка каждого дочернего элемента в Grid size */
5451
+ require("react/jsx-runtime")
5452
+ );
5453
+ var widthToGridColumns = (width) => {
5454
+ switch (width) {
5455
+ case "25%":
5456
+ return 3;
5457
+ case "33%":
5458
+ return 4;
5459
+ case "50%":
5460
+ return 6;
5461
+ case "66%":
5462
+ return 8;
5463
+ case "75%":
5464
+ return 9;
5465
+ case "100%":
5466
+ default:
5467
+ return 12;
5468
+ }
5469
+ };
5470
+ var createGridSize = (desktopWidth, mobileWidth = "100%") => {
5471
+ return {
5472
+ xs: widthToGridColumns(mobileWidth),
5473
+ md: widthToGridColumns(desktopWidth)
5474
+ };
5475
+ };
5476
+ var FilterSection = ({
5477
+ width = "100%",
5478
+ mobileWidth = "100%",
5479
+ itemWidth = "50%",
5480
+ itemMobileWidth = "100%",
5481
+ children
5482
+ }) => {
5483
+ const sectionGridSize = createGridSize(width, mobileWidth);
5484
+ const itemGridSize = createGridSize(itemWidth, itemMobileWidth);
5485
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material29.Grid, { size: sectionGridSize, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material29.Grid, { container: true, spacing: 2, sx: { alignItems: "center" }, children: import_react33.default.Children.map(children, (child) => {
5486
+ if (!child) return null;
5487
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material29.Grid, { size: itemGridSize, children: child });
5488
+ }) }) });
5489
+ };
5490
+ var FilterContainerWidget = ({
5491
+ sections,
5492
+ children,
5493
+ sx
5494
+ }) => {
5495
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
5496
+ import_material29.Paper,
5497
+ {
5498
+ variant: "outlined",
5499
+ sx: [
5500
+ { p: 2, mb: 3 },
5501
+ ...Array.isArray(sx) ? sx : [sx]
5502
+ ],
5503
+ children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_material29.Grid, { container: true, spacing: 2, sx: { alignItems: "center" }, children: sections ? sections.map((sec, idx) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react33.default.Fragment, { children: sec }, idx)) : children })
5504
+ }
5505
+ );
5506
+ };
5507
+
5134
5508
  // src/UI/index.ts
5135
5509
  var UI = {
5136
5510
  Inputs: {
@@ -5157,9 +5531,9 @@ var UI = {
5157
5531
  MyAvatar: MyAvatarComponent,
5158
5532
  ExpandableNote: ExpandableNoteComponent
5159
5533
  },
5160
- /*Layout: {
5161
- Row: FormRow,
5162
- },*/
5534
+ Layout: {
5535
+ Page: PageComponent
5536
+ },
5163
5537
  Widgets: {
5164
5538
  Locales: {
5165
5539
  ProtectedLanguageSelector: LanguageSelector,
@@ -5167,7 +5541,9 @@ var UI = {
5167
5541
  },
5168
5542
  Pages: {
5169
5543
  PageLinkWidget,
5170
- CollapsedWidgetContainer
5544
+ CollapsedWidgetContainer,
5545
+ FilterContainerWidget,
5546
+ FilterSection
5171
5547
  },
5172
5548
  Misc: {
5173
5549
  ErrorAlertWidget
@@ -5198,16 +5574,16 @@ var Services = {
5198
5574
  };
5199
5575
 
5200
5576
  // src/hooks/useUserStatus.ts
5201
- var import_react32 = require("react");
5577
+ var import_react34 = require("react");
5202
5578
  init_deviceId();
5203
5579
  init_context();
5204
5580
  var ONLINE_THRESHOLD_MINUTES = 2;
5205
5581
  var AWAY_THRESHOLD_MINUTES = 15;
5206
5582
  var useUserStatus2 = (userId, initialLastSeenAt, currentAuthUserId) => {
5207
5583
  const supabase = useSupabase();
5208
- const [status, setStatus] = (0, import_react32.useState)("offline");
5209
- const [lastSeen, setLastSeen] = (0, import_react32.useState)(initialLastSeenAt ?? null);
5210
- const lastSeenRef = (0, import_react32.useRef)(initialLastSeenAt ?? null);
5584
+ const [status, setStatus] = (0, import_react34.useState)("offline");
5585
+ const [lastSeen, setLastSeen] = (0, import_react34.useState)(initialLastSeenAt ?? null);
5586
+ const lastSeenRef = (0, import_react34.useRef)(initialLastSeenAt ?? null);
5211
5587
  lastSeenRef.current = lastSeen;
5212
5588
  const calculateStatus = (isOnline, lastSeenIso) => {
5213
5589
  if (isOnline) return "online";
@@ -5223,7 +5599,7 @@ var useUserStatus2 = (userId, initialLastSeenAt, currentAuthUserId) => {
5223
5599
  }
5224
5600
  return "offline";
5225
5601
  };
5226
- (0, import_react32.useEffect)(() => {
5602
+ (0, import_react34.useEffect)(() => {
5227
5603
  if (!userId) return;
5228
5604
  let isMounted = true;
5229
5605
  const isSelf = currentAuthUserId && userId === currentAuthUserId;
package/dist/index.mjs CHANGED
@@ -2215,6 +2215,393 @@ var ButtonComponent = forwardRef(
2215
2215
  );
2216
2216
  ButtonComponent.displayName = "ButtonComponent";
2217
2217
 
2218
+ // src/UI/components/layout/PageComponent.tsx
2219
+ import React12, { useState as useState13 } from "react";
2220
+ import {
2221
+ Box as Box10,
2222
+ Typography as Typography9,
2223
+ Button as Button6,
2224
+ IconButton as IconButton8,
2225
+ Menu as Menu2,
2226
+ MenuItem as MenuItem3,
2227
+ ListItemIcon as ListItemIcon2,
2228
+ ListItemText as ListItemText3,
2229
+ Divider as Divider3,
2230
+ Tooltip as Tooltip8,
2231
+ useTheme as useTheme2,
2232
+ useMediaQuery,
2233
+ Stack,
2234
+ Fab as Fab2
2235
+ } from "@mui/material";
2236
+ import MoreVertIcon from "@mui/icons-material/MoreVert";
2237
+ import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
2238
+ import { Refresh as RefreshIcon } from "@mui/icons-material";
2239
+ import { Fragment as Fragment10, jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
2240
+ var getTruncateStyles = (mode = "truncate") => {
2241
+ switch (mode) {
2242
+ case "truncate":
2243
+ return { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", display: "block" };
2244
+ case "clamp-2":
2245
+ return { display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "normal" };
2246
+ case "wrap":
2247
+ default:
2248
+ return { whiteSpace: "normal", wordBreak: "break-word" };
2249
+ }
2250
+ };
2251
+ var PageHeader = ({
2252
+ overrideNamespace,
2253
+ icon,
2254
+ title,
2255
+ subtitle,
2256
+ tag,
2257
+ titleTruncateMode = "truncate",
2258
+ subtitleTruncateMode = "truncate",
2259
+ customFragment,
2260
+ primaryAction,
2261
+ secondaryActions = [],
2262
+ refreshAction,
2263
+ mobileBreakpoint = "sm",
2264
+ forceMobileView,
2265
+ sx = []
2266
+ }) => {
2267
+ const theme = useTheme2();
2268
+ const mediaIsMobile = useMediaQuery(theme.breakpoints.down(mobileBreakpoint));
2269
+ const isMobile = forceMobileView ?? mediaIsMobile;
2270
+ const [anchorEl, setAnchorEl] = useState13(null);
2271
+ const isMenuOpen = Boolean(anchorEl);
2272
+ const handleOpenMenu = (e) => setAnchorEl(e.currentTarget);
2273
+ const handleCloseMenu = () => setAnchorEl(null);
2274
+ const handleMenuAction = (cb) => {
2275
+ handleCloseMenu();
2276
+ cb();
2277
+ };
2278
+ const hasSecondaryActions = secondaryActions.length > 0;
2279
+ const hasActions = Boolean(primaryAction || hasSecondaryActions || refreshAction);
2280
+ return /* @__PURE__ */ jsxs13(
2281
+ Box10,
2282
+ {
2283
+ sx: [
2284
+ {
2285
+ display: "flex",
2286
+ alignItems: "center",
2287
+ justifyContent: "space-between",
2288
+ width: "100%",
2289
+ gap: { xs: 1.5, sm: 2.5 },
2290
+ py: 1.5,
2291
+ px: { xs: 1.5, sm: 2 },
2292
+ borderRadius: 2,
2293
+ backgroundColor: "background.paper",
2294
+ boxShadow: "0 1px 3px rgba(0,0,0,0.05)"
2295
+ },
2296
+ ...Array.isArray(sx) ? sx : [sx]
2297
+ ],
2298
+ children: [
2299
+ /* @__PURE__ */ jsxs13(Box10, { sx: { display: "flex", alignItems: "center", gap: { xs: 1.5, sm: 2 }, minWidth: 0, flex: 1 }, children: [
2300
+ icon && /* @__PURE__ */ jsx18(
2301
+ Box10,
2302
+ {
2303
+ sx: {
2304
+ display: "flex",
2305
+ alignItems: "center",
2306
+ justifyContent: "center",
2307
+ width: { xs: 40, sm: 48 },
2308
+ height: { xs: 40, sm: 48 },
2309
+ borderRadius: 2.5,
2310
+ backgroundColor: "primary.50",
2311
+ color: "primary.main",
2312
+ flexShrink: 0,
2313
+ border: "1px solid",
2314
+ borderColor: "primary.100",
2315
+ "& svg": { fontSize: { xs: 22, sm: 26 } }
2316
+ },
2317
+ children: icon
2318
+ }
2319
+ ),
2320
+ /* @__PURE__ */ jsxs13(Box10, { sx: { minWidth: 0, flex: 1 }, children: [
2321
+ tag && /* @__PURE__ */ jsx18(Box10, { sx: { mb: 0.5, display: "flex", alignItems: "center" }, children: tag }),
2322
+ /* @__PURE__ */ jsx18(Tooltip8, { title: titleTruncateMode === "truncate" ? /* @__PURE__ */ jsx18(TranslateText, { children: title }) : void 0, arrow: true, placement: "top", children: /* @__PURE__ */ jsx18(
2323
+ Typography9,
2324
+ {
2325
+ variant: "h5",
2326
+ component: "h1",
2327
+ sx: {
2328
+ fontWeight: 700,
2329
+ color: "text.primary",
2330
+ fontSize: { xs: "1.125rem", sm: "1.35rem" },
2331
+ lineHeight: 1.3,
2332
+ ...getTruncateStyles(titleTruncateMode)
2333
+ },
2334
+ children: /* @__PURE__ */ jsx18(TranslateText, { children: title })
2335
+ }
2336
+ ) }),
2337
+ subtitle && /* @__PURE__ */ jsx18(Tooltip8, { title: subtitleTruncateMode === "truncate" ? /* @__PURE__ */ jsx18(TranslateText, { children: subtitle }) : void 0, arrow: true, placement: "top", children: /* @__PURE__ */ jsx18(
2338
+ Typography9,
2339
+ {
2340
+ variant: "body2",
2341
+ sx: {
2342
+ color: "text.secondary",
2343
+ mt: 0.25,
2344
+ fontSize: { xs: "0.8125rem", sm: "0.875rem" },
2345
+ lineHeight: 1.4,
2346
+ ...getTruncateStyles(subtitleTruncateMode)
2347
+ },
2348
+ children: /* @__PURE__ */ jsx18(TranslateText, { children: subtitle })
2349
+ }
2350
+ ) })
2351
+ ] })
2352
+ ] }),
2353
+ hasActions && /* @__PURE__ */ jsx18(Box10, { sx: { flexShrink: 0, display: "flex", alignItems: "center", gap: 1 }, children: isMobile ? (
2354
+ /* MOBILE: IconButton with MoreVertIcon showing ALL actions inside Menu */
2355
+ /* @__PURE__ */ jsxs13(Fragment10, { children: [
2356
+ /* @__PURE__ */ jsx18(TranslatableTooltipComponent, { title: /* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Actions" }), children: /* @__PURE__ */ jsx18(
2357
+ IconButton8,
2358
+ {
2359
+ onClick: handleOpenMenu,
2360
+ sx: { border: "1px solid", borderColor: "divider", borderRadius: 2, p: 1 },
2361
+ children: /* @__PURE__ */ jsx18(MoreVertIcon, {})
2362
+ }
2363
+ ) }),
2364
+ /* @__PURE__ */ jsxs13(
2365
+ Menu2,
2366
+ {
2367
+ anchorEl,
2368
+ open: isMenuOpen,
2369
+ onClose: handleCloseMenu,
2370
+ transformOrigin: { horizontal: "right", vertical: "top" },
2371
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
2372
+ slotProps: { paper: { elevation: 4, sx: { minWidth: 200, borderRadius: 2, mt: 1, p: 0.5 } } },
2373
+ children: [
2374
+ primaryAction && /* @__PURE__ */ jsxs13(Fragment10, { children: [
2375
+ /* @__PURE__ */ jsxs13(
2376
+ MenuItem3,
2377
+ {
2378
+ onClick: () => handleMenuAction(primaryAction.onClick),
2379
+ disabled: primaryAction.disabled,
2380
+ sx: {
2381
+ borderRadius: 1.5,
2382
+ fontWeight: 700,
2383
+ backgroundColor: "primary.50",
2384
+ color: primaryAction.color ? `${primaryAction.color}.main` : "primary.main",
2385
+ py: 1.2,
2386
+ px: 1.5
2387
+ },
2388
+ children: [
2389
+ primaryAction.icon && /* @__PURE__ */ jsx18(ListItemIcon2, { sx: { color: "inherit", minWidth: 32 }, children: primaryAction.icon }),
2390
+ /* @__PURE__ */ jsx18(ListItemText3, { primary: /* @__PURE__ */ jsx18(TranslateText, { children: primaryAction.label }), slotProps: { primary: { sx: { fontWeight: 700, fontSize: "0.9rem" } } } })
2391
+ ]
2392
+ }
2393
+ ),
2394
+ hasSecondaryActions && /* @__PURE__ */ jsx18(Divider3, { sx: { my: 0.75 } })
2395
+ ] }),
2396
+ secondaryActions.map((action) => /* @__PURE__ */ jsxs13(React12.Fragment, { children: [
2397
+ action.divider && /* @__PURE__ */ jsx18(Divider3, { sx: { my: 0.5 } }),
2398
+ /* @__PURE__ */ jsxs13(
2399
+ MenuItem3,
2400
+ {
2401
+ onClick: () => handleMenuAction(action.onClick),
2402
+ disabled: action.disabled,
2403
+ sx: { borderRadius: 1, py: 1, px: 1.5, color: action.color ? `${action.color}.main` : "text.primary" },
2404
+ children: [
2405
+ action.icon && /* @__PURE__ */ jsx18(ListItemIcon2, { sx: { color: action.color ? `${action.color}.main` : "text.secondary", minWidth: 32 }, children: action.icon }),
2406
+ /* @__PURE__ */ jsx18(ListItemText3, { primary: action.label, slotProps: { primary: { sx: { fontSize: "0.875rem" } } } })
2407
+ ]
2408
+ }
2409
+ )
2410
+ ] }, action.id)),
2411
+ refreshAction && /* @__PURE__ */ jsxs13(Fragment10, { children: [
2412
+ /* @__PURE__ */ jsx18(Divider3, { sx: { my: 0.5 } }),
2413
+ /* @__PURE__ */ jsxs13(
2414
+ MenuItem3,
2415
+ {
2416
+ onClick: () => handleMenuAction(refreshAction.onClick),
2417
+ disabled: refreshAction.disabled,
2418
+ sx: {
2419
+ borderRadius: 1.5,
2420
+ fontWeight: 700,
2421
+ backgroundColor: "primary.50",
2422
+ py: 1.2,
2423
+ px: 1.5
2424
+ },
2425
+ children: [
2426
+ /* @__PURE__ */ jsx18(ListItemIcon2, { sx: { color: "inherit", minWidth: 32 }, children: /* @__PURE__ */ jsx18(RefreshIcon, {}) }),
2427
+ /* @__PURE__ */ jsx18(ListItemText3, { primary: /* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Refresh" }), slotProps: { primary: { sx: { fontWeight: 700, fontSize: "0.9rem" } } } })
2428
+ ]
2429
+ }
2430
+ )
2431
+ ] })
2432
+ ]
2433
+ }
2434
+ )
2435
+ ] })
2436
+ ) : (
2437
+ /* DESKTOP: Primary Action Button + Secondary Actions Dropdown Button */
2438
+ /* @__PURE__ */ jsxs13(Box10, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
2439
+ (primaryAction || refreshAction) && /* @__PURE__ */ jsxs13(Stack, { direction: "row", spacing: 2, children: [
2440
+ primaryAction && /* @__PURE__ */ jsx18(
2441
+ Button6,
2442
+ {
2443
+ variant: primaryAction.variant || "contained",
2444
+ color: primaryAction.color || "primary",
2445
+ onClick: primaryAction.onClick,
2446
+ disabled: primaryAction.disabled,
2447
+ startIcon: primaryAction.icon,
2448
+ sx: { borderRadius: 2, textTransform: "none", fontWeight: 600, px: 2, py: 0.85 },
2449
+ children: /* @__PURE__ */ jsx18(TranslateText, { children: primaryAction.label })
2450
+ }
2451
+ ),
2452
+ refreshAction && /* @__PURE__ */ jsx18(TranslatableTooltipComponent, { title: /* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Refresh" }), children: /* @__PURE__ */ jsx18(
2453
+ Fab2,
2454
+ {
2455
+ color: "default",
2456
+ onClick: refreshAction.onClick,
2457
+ disabled: refreshAction.disabled,
2458
+ "aria-label": "refresh",
2459
+ size: "small",
2460
+ children: /* @__PURE__ */ jsx18(RefreshIcon, {})
2461
+ }
2462
+ ) })
2463
+ ] }),
2464
+ hasSecondaryActions && /* @__PURE__ */ jsxs13(Fragment10, { children: [
2465
+ /* @__PURE__ */ jsx18(
2466
+ IconButton8,
2467
+ {
2468
+ onClick: handleOpenMenu,
2469
+ sx: { border: "1px solid", borderColor: "divider", borderRadius: 2, p: 1 },
2470
+ children: /* @__PURE__ */ jsx18(KeyboardArrowDownIcon, {})
2471
+ }
2472
+ ),
2473
+ /* @__PURE__ */ jsx18(
2474
+ Menu2,
2475
+ {
2476
+ anchorEl,
2477
+ open: isMenuOpen,
2478
+ onClose: handleCloseMenu,
2479
+ transformOrigin: { horizontal: "right", vertical: "top" },
2480
+ anchorOrigin: { horizontal: "right", vertical: "bottom" },
2481
+ slotProps: { paper: { elevation: 4, sx: { minWidth: 180, borderRadius: 2, mt: 1, p: 0.5 } } },
2482
+ children: secondaryActions.map((action) => /* @__PURE__ */ jsxs13(React12.Fragment, { children: [
2483
+ action.divider && /* @__PURE__ */ jsx18(Divider3, { sx: { my: 0.5 } }),
2484
+ /* @__PURE__ */ jsxs13(
2485
+ MenuItem3,
2486
+ {
2487
+ onClick: () => handleMenuAction(action.onClick),
2488
+ disabled: action.disabled,
2489
+ sx: { borderRadius: 1, py: 0.85, color: action.color ? `${action.color}.main` : "text.primary" },
2490
+ children: [
2491
+ action.icon && /* @__PURE__ */ jsx18(ListItemIcon2, { sx: { color: action.color ? `${action.color}.main` : "text.secondary", minWidth: 32 }, children: action.icon }),
2492
+ /* @__PURE__ */ jsx18(ListItemText3, { primary: action.label, slotProps: { primary: { sx: { fontSize: "0.875rem" } } } })
2493
+ ]
2494
+ }
2495
+ )
2496
+ ] }, action.id))
2497
+ }
2498
+ )
2499
+ ] })
2500
+ ] })
2501
+ ) }),
2502
+ customFragment && /* @__PURE__ */ jsx18(Fragment10, { children: customFragment })
2503
+ ]
2504
+ }
2505
+ );
2506
+ };
2507
+ var PageContainer = ({ children }) => {
2508
+ return /* @__PURE__ */ jsx18(
2509
+ Box10,
2510
+ {
2511
+ sx: {
2512
+ width: "100%",
2513
+ boxSizing: "border-box",
2514
+ p: { xs: 1.5, sm: 3 },
2515
+ backgroundColor: "background.paper"
2516
+ },
2517
+ children
2518
+ }
2519
+ );
2520
+ };
2521
+ var PageComponent = ({
2522
+ header,
2523
+ children
2524
+ }) => {
2525
+ return /* @__PURE__ */ jsx18(
2526
+ Box10,
2527
+ {
2528
+ sx: {
2529
+ //pt: 2,
2530
+ flexGrow: 1,
2531
+ minHeight: 0,
2532
+ overflowY: "auto",
2533
+ boxSizing: "border-box"
2534
+ },
2535
+ children: /* @__PURE__ */ jsxs13(PageContainer, { children: [
2536
+ header && /* @__PURE__ */ jsx18(PageHeader, { ...header }),
2537
+ children
2538
+ ] })
2539
+ }
2540
+ );
2541
+ };
2542
+
2543
+ // src/UI/widgets/page/FilterContainerWidget.tsx
2544
+ import React13 from "react";
2545
+ import { Grid, Paper } from "@mui/material";
2546
+ import { jsx as jsx19 } from "react/jsx-runtime";
2547
+ var widthToGridColumns = (width) => {
2548
+ switch (width) {
2549
+ case "25%":
2550
+ return 3;
2551
+ case "33%":
2552
+ return 4;
2553
+ case "50%":
2554
+ return 6;
2555
+ case "66%":
2556
+ return 8;
2557
+ case "75%":
2558
+ return 9;
2559
+ case "100%":
2560
+ default:
2561
+ return 12;
2562
+ }
2563
+ };
2564
+ var createGridSize = (desktopWidth, mobileWidth = "100%") => {
2565
+ return {
2566
+ xs: widthToGridColumns(mobileWidth),
2567
+ md: widthToGridColumns(desktopWidth)
2568
+ };
2569
+ };
2570
+ var FilterSection = ({
2571
+ width = "100%",
2572
+ mobileWidth = "100%",
2573
+ itemWidth = "50%",
2574
+ itemMobileWidth = "100%",
2575
+ children
2576
+ }) => {
2577
+ const sectionGridSize = createGridSize(width, mobileWidth);
2578
+ const itemGridSize = createGridSize(itemWidth, itemMobileWidth);
2579
+ return /* @__PURE__ */ jsx19(Grid, { size: sectionGridSize, children: /* @__PURE__ */ jsx19(Grid, { container: true, spacing: 2, sx: { alignItems: "center" }, children: React13.Children.map(children, (child) => {
2580
+ if (!child) return null;
2581
+ return (
2582
+ /* Автоматическая обертка каждого дочернего элемента в Grid size */
2583
+ /* @__PURE__ */ jsx19(Grid, { size: itemGridSize, children: child })
2584
+ );
2585
+ }) }) });
2586
+ };
2587
+ var FilterContainerWidget = ({
2588
+ sections,
2589
+ children,
2590
+ sx
2591
+ }) => {
2592
+ return /* @__PURE__ */ jsx19(
2593
+ Paper,
2594
+ {
2595
+ variant: "outlined",
2596
+ sx: [
2597
+ { p: 2, mb: 3 },
2598
+ ...Array.isArray(sx) ? sx : [sx]
2599
+ ],
2600
+ children: /* @__PURE__ */ jsx19(Grid, { container: true, spacing: 2, sx: { alignItems: "center" }, children: sections ? sections.map((sec, idx) => /* @__PURE__ */ jsx19(React13.Fragment, { children: sec }, idx)) : children })
2601
+ }
2602
+ );
2603
+ };
2604
+
2218
2605
  // src/UI/index.ts
2219
2606
  var UI = {
2220
2607
  Inputs: {
@@ -2241,9 +2628,9 @@ var UI = {
2241
2628
  MyAvatar: MyAvatarComponent,
2242
2629
  ExpandableNote: ExpandableNoteComponent
2243
2630
  },
2244
- /*Layout: {
2245
- Row: FormRow,
2246
- },*/
2631
+ Layout: {
2632
+ Page: PageComponent
2633
+ },
2247
2634
  Widgets: {
2248
2635
  Locales: {
2249
2636
  ProtectedLanguageSelector: LanguageSelector,
@@ -2251,7 +2638,9 @@ var UI = {
2251
2638
  },
2252
2639
  Pages: {
2253
2640
  PageLinkWidget,
2254
- CollapsedWidgetContainer
2641
+ CollapsedWidgetContainer,
2642
+ FilterContainerWidget,
2643
+ FilterSection
2255
2644
  },
2256
2645
  Misc: {
2257
2646
  ErrorAlertWidget
@@ -2274,13 +2663,13 @@ var Services = {
2274
2663
  };
2275
2664
 
2276
2665
  // src/hooks/useUserStatus.ts
2277
- import { useEffect as useEffect6, useRef, useState as useState13 } from "react";
2666
+ import { useEffect as useEffect6, useRef, useState as useState14 } from "react";
2278
2667
  var ONLINE_THRESHOLD_MINUTES = 2;
2279
2668
  var AWAY_THRESHOLD_MINUTES = 15;
2280
2669
  var useUserStatus = (userId, initialLastSeenAt, currentAuthUserId) => {
2281
2670
  const supabase = useSupabase();
2282
- const [status, setStatus] = useState13("offline");
2283
- const [lastSeen, setLastSeen] = useState13(initialLastSeenAt ?? null);
2671
+ const [status, setStatus] = useState14("offline");
2672
+ const [lastSeen, setLastSeen] = useState14(initialLastSeenAt ?? null);
2284
2673
  const lastSeenRef = useRef(initialLastSeenAt ?? null);
2285
2674
  lastSeenRef.current = lastSeen;
2286
2675
  const calculateStatus = (isOnline, lastSeenIso) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serhii.olishevskyi/docs",
3
- "version": "1.2.21",
3
+ "version": "1.2.23",
4
4
  "description": "DOCS.UA react packages",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",