@serhii.olishevskyi/docs 1.1.5 → 1.1.7

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,6 +1,6 @@
1
1
  import * as React$1 from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
- import { TextFieldProps, IconButtonProps, TooltipProps, SxProps, Theme } from '@mui/material';
3
+ import { TextFieldProps, SelectProps, SelectChangeEvent, IconButtonProps, TooltipProps, SxProps, Theme } from '@mui/material';
4
4
  import * as _supabase_supabase_js from '@supabase/supabase-js';
5
5
  import { SupabaseClient, User, Session } from '@supabase/supabase-js';
6
6
 
@@ -70,6 +70,39 @@ type EmailInputComponentProps = Omit<TextFieldComponentProps, 'type'> & {
70
70
  onValidationChange?: (isValid: boolean) => void;
71
71
  };
72
72
 
73
+ interface SearchComponentProps extends Omit<TextFieldProps, 'placeholder'> {
74
+ overrideNamespace?: string;
75
+ label?: ReactNode;
76
+ placeholderKey?: string;
77
+ clearTooltip?: string;
78
+ onClear?: () => void;
79
+ debounceDelay?: number;
80
+ onDebounceChange?: (value: string) => void;
81
+ }
82
+
83
+ interface SelectOption<T = string> {
84
+ value: T;
85
+ label: React__default.ReactNode;
86
+ isItalic?: boolean;
87
+ }
88
+ interface SelectComponentProps<T = string> extends Omit<SelectProps<T | T[]>, 'onChange' | 'value'> {
89
+ overrideNamespace?: string;
90
+ label: React__default.ReactNode;
91
+ value: T | T[];
92
+ options: SelectOption<T>[];
93
+ onChange?: (event: SelectChangeEvent<T | T[]>, child: React__default.ReactNode) => void;
94
+ id?: string;
95
+ fullWidth?: boolean;
96
+ size?: 'small' | 'medium';
97
+ multiple?: boolean;
98
+ withCheckboxes?: boolean;
99
+ clearable?: boolean;
100
+ maxSelect?: number;
101
+ maxDisplayedTags?: number;
102
+ helperText?: ReactNode;
103
+ defaultValueOnClear?: T;
104
+ }
105
+
73
106
  interface CopyClipboardButtonComponentProps extends Omit<IconButtonProps, 'onClick'> {
74
107
  overrideNamespace?: string;
75
108
  /** Текст, який буде скопійовано в буфер обміну */
@@ -196,6 +229,8 @@ declare const UI: {
196
229
  TextField: React$1.FC<TextFieldComponentProps>;
197
230
  NumberField: React$1.FC<NumberInputComponentProps>;
198
231
  EmailField: React$1.FC<EmailInputComponentProps>;
232
+ Search: React$1.FC<SearchComponentProps>;
233
+ Select: <T extends string | number>({ overrideNamespace, label, value, options, onChange, id, fullWidth, size, multiple, withCheckboxes, clearable, maxSelect, disabled, maxDisplayedTags, helperText, defaultValueOnClear, sx, ...restProps }: SelectComponentProps<T>) => React$1.JSX.Element;
199
234
  Url: typeof UrlComponent;
200
235
  };
201
236
  Buttons: {
@@ -528,4 +563,10 @@ declare const Services: {
528
563
  };
529
564
  };
530
565
 
531
- 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, Services, 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 UserGroupedSessions, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, type UserSessionItem, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
566
+ declare function useDebounce<T>(value: T, delay?: number): T;
567
+
568
+ declare const Hooks: {
569
+ readonly useDebounce: typeof useDebounce;
570
+ };
571
+
572
+ export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps, Hooks, type Language, LocalizationProvider, type MyAvatarComponentProps, type NumberInputComponentProps, Providers, type SearchComponentProps, type SelectComponentProps, Services, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, 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 UserRoleChipComponentProps, type UserSessionItem, UserSettingsProvider, type UserStatusChipComponentProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React$1 from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
- import { TextFieldProps, IconButtonProps, TooltipProps, SxProps, Theme } from '@mui/material';
3
+ import { TextFieldProps, SelectProps, SelectChangeEvent, IconButtonProps, TooltipProps, SxProps, Theme } from '@mui/material';
4
4
  import * as _supabase_supabase_js from '@supabase/supabase-js';
5
5
  import { SupabaseClient, User, Session } from '@supabase/supabase-js';
6
6
 
@@ -70,6 +70,39 @@ type EmailInputComponentProps = Omit<TextFieldComponentProps, 'type'> & {
70
70
  onValidationChange?: (isValid: boolean) => void;
71
71
  };
72
72
 
73
+ interface SearchComponentProps extends Omit<TextFieldProps, 'placeholder'> {
74
+ overrideNamespace?: string;
75
+ label?: ReactNode;
76
+ placeholderKey?: string;
77
+ clearTooltip?: string;
78
+ onClear?: () => void;
79
+ debounceDelay?: number;
80
+ onDebounceChange?: (value: string) => void;
81
+ }
82
+
83
+ interface SelectOption<T = string> {
84
+ value: T;
85
+ label: React__default.ReactNode;
86
+ isItalic?: boolean;
87
+ }
88
+ interface SelectComponentProps<T = string> extends Omit<SelectProps<T | T[]>, 'onChange' | 'value'> {
89
+ overrideNamespace?: string;
90
+ label: React__default.ReactNode;
91
+ value: T | T[];
92
+ options: SelectOption<T>[];
93
+ onChange?: (event: SelectChangeEvent<T | T[]>, child: React__default.ReactNode) => void;
94
+ id?: string;
95
+ fullWidth?: boolean;
96
+ size?: 'small' | 'medium';
97
+ multiple?: boolean;
98
+ withCheckboxes?: boolean;
99
+ clearable?: boolean;
100
+ maxSelect?: number;
101
+ maxDisplayedTags?: number;
102
+ helperText?: ReactNode;
103
+ defaultValueOnClear?: T;
104
+ }
105
+
73
106
  interface CopyClipboardButtonComponentProps extends Omit<IconButtonProps, 'onClick'> {
74
107
  overrideNamespace?: string;
75
108
  /** Текст, який буде скопійовано в буфер обміну */
@@ -196,6 +229,8 @@ declare const UI: {
196
229
  TextField: React$1.FC<TextFieldComponentProps>;
197
230
  NumberField: React$1.FC<NumberInputComponentProps>;
198
231
  EmailField: React$1.FC<EmailInputComponentProps>;
232
+ Search: React$1.FC<SearchComponentProps>;
233
+ Select: <T extends string | number>({ overrideNamespace, label, value, options, onChange, id, fullWidth, size, multiple, withCheckboxes, clearable, maxSelect, disabled, maxDisplayedTags, helperText, defaultValueOnClear, sx, ...restProps }: SelectComponentProps<T>) => React$1.JSX.Element;
199
234
  Url: typeof UrlComponent;
200
235
  };
201
236
  Buttons: {
@@ -528,4 +563,10 @@ declare const Services: {
528
563
  };
529
564
  };
530
565
 
531
- 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, Services, 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 UserGroupedSessions, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, type UserSessionItem, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
566
+ declare function useDebounce<T>(value: T, delay?: number): T;
567
+
568
+ declare const Hooks: {
569
+ readonly useDebounce: typeof useDebounce;
570
+ };
571
+
572
+ export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps, Hooks, type Language, LocalizationProvider, type MyAvatarComponentProps, type NumberInputComponentProps, Providers, type SearchComponentProps, type SelectComponentProps, Services, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, 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 UserRoleChipComponentProps, type UserSessionItem, UserSettingsProvider, type UserStatusChipComponentProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
package/dist/index.js CHANGED
@@ -2748,6 +2748,7 @@ __export(index_exports, {
2748
2748
  AuthProvider: () => AuthProvider,
2749
2749
  BreadcrumbProvider: () => BreadcrumbProvider,
2750
2750
  ConfirmationProvider: () => ConfirmationProvider,
2751
+ Hooks: () => Hooks,
2751
2752
  LocalizationProvider: () => LocalizationProvider,
2752
2753
  Providers: () => Providers,
2753
2754
  Services: () => Services,
@@ -3978,12 +3979,346 @@ var PublicLanguageSelector = () => {
3978
3979
  ] });
3979
3980
  };
3980
3981
 
3982
+ // src/UI/components/inputs/SearchComponent.tsx
3983
+ var import_react26 = require("react");
3984
+ var import_react_i18next6 = require("react-i18next");
3985
+ var import_material18 = require("@mui/material");
3986
+ var import_Translate4 = __toESM(require("@mui/icons-material/Translate"));
3987
+ var import_Clear2 = __toESM(require("@mui/icons-material/Clear"));
3988
+ init_context();
3989
+ init_translation_service();
3990
+ init_TranslationDialog();
3991
+ init_TranslatableTooltipComponent();
3992
+
3993
+ // src/hooks/useDebounce.ts
3994
+ var import_react25 = require("react");
3995
+ function useDebounce(value, delay = 500) {
3996
+ const [debouncedValue, setDebouncedValue] = (0, import_react25.useState)(value);
3997
+ (0, import_react25.useEffect)(() => {
3998
+ const timer = setTimeout(() => {
3999
+ setDebouncedValue(value);
4000
+ }, delay);
4001
+ return () => {
4002
+ clearTimeout(timer);
4003
+ };
4004
+ }, [value, delay]);
4005
+ return debouncedValue;
4006
+ }
4007
+
4008
+ // src/UI/components/inputs/SearchComponent.tsx
4009
+ var import_jsx_runtime28 = require("react/jsx-runtime");
4010
+ var SearchComponent = ({
4011
+ overrideNamespace,
4012
+ label = "",
4013
+ value = "",
4014
+ onChange,
4015
+ onClear,
4016
+ placeholderKey,
4017
+ clearTooltip = "Clear",
4018
+ debounceDelay = 500,
4019
+ onDebounceChange,
4020
+ slotProps,
4021
+ ...restProps
4022
+ }) => {
4023
+ const supabase = useSupabase();
4024
+ const namespace = useTranslationNamespace();
4025
+ const { t, i18n: i18n2 } = (0, import_react_i18next6.useTranslation)(namespace);
4026
+ const { isTranslationMode } = useTranslationEdit();
4027
+ const [isDialogOpen, setIsDialogOpen] = (0, import_react26.useState)(false);
4028
+ const [localValue, setLocalValue] = (0, import_react26.useState)(String(value ?? ""));
4029
+ const debouncedValue = useDebounce(localValue, debounceDelay);
4030
+ (0, import_react26.useEffect)(() => {
4031
+ setLocalValue(String(value ?? ""));
4032
+ }, [value]);
4033
+ (0, import_react26.useEffect)(() => {
4034
+ if (onDebounceChange) {
4035
+ onDebounceChange(debouncedValue);
4036
+ }
4037
+ if (onChange && debouncedValue !== String(value ?? "")) {
4038
+ const syntheticEvent = {
4039
+ target: { value: debouncedValue },
4040
+ currentTarget: { value: debouncedValue }
4041
+ };
4042
+ onChange(syntheticEvent);
4043
+ }
4044
+ }, [debouncedValue]);
4045
+ const hasValue = localValue.length > 0;
4046
+ const handleChange = (e) => {
4047
+ setLocalValue(e.target.value);
4048
+ };
4049
+ const handleClear = (e) => {
4050
+ e.stopPropagation();
4051
+ setLocalValue("");
4052
+ if (onClear) {
4053
+ onClear();
4054
+ }
4055
+ if (onChange) {
4056
+ const syntheticEvent = {
4057
+ ...e,
4058
+ target: { ...e.target, value: "" },
4059
+ currentTarget: { ...e.currentTarget, value: "" }
4060
+ };
4061
+ onChange(syntheticEvent);
4062
+ }
4063
+ };
4064
+ const translatedPlaceholder = placeholderKey ? t(placeholderKey) : void 0;
4065
+ const handleSaveTranslations = async (newValues, contextHint) => {
4066
+ if (!placeholderKey) return;
4067
+ const upsertData = Object.entries(newValues).map(([langCode, text]) => ({
4068
+ lang_code: langCode,
4069
+ namespace,
4070
+ key: placeholderKey,
4071
+ value: text,
4072
+ context_hint: contextHint
4073
+ }));
4074
+ const { error } = await supabase.from("translations").upsert(upsertData, { onConflict: "lang_code,namespace,key" });
4075
+ if (error) {
4076
+ alert("\u041F\u043E\u043C\u0438\u043B\u043A\u0430 \u043F\u0456\u0434 \u0447\u0430\u0441 \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043D\u043D\u044F \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043D\u0438\u0445");
4077
+ throw error;
4078
+ }
4079
+ await translationService.incrementVersion(supabase, namespace, Object.keys(newValues));
4080
+ Object.entries(newValues).forEach(([langCode, text]) => {
4081
+ i18n2.addResource(langCode, namespace, placeholderKey, text);
4082
+ });
4083
+ };
4084
+ const renderEndAdornment = () => {
4085
+ const rawInputProps = slotProps?.input;
4086
+ const isInputObject = typeof rawInputProps === "object" && rawInputProps !== null;
4087
+ const existingEndAdornment = isInputObject && "endAdornment" in rawInputProps ? rawInputProps.endAdornment : void 0;
4088
+ if (!hasValue && (!isTranslationMode || !placeholderKey)) {
4089
+ return existingEndAdornment;
4090
+ }
4091
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
4092
+ existingEndAdornment,
4093
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_material18.InputAdornment, { position: "end", children: [
4094
+ hasValue && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TranslatableTooltipComponent, { title: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TranslateText, { overrideNamespace, children: clearTooltip }), children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
4095
+ import_material18.IconButton,
4096
+ {
4097
+ size: "small",
4098
+ onClick: handleClear,
4099
+ edge: "end",
4100
+ "aria-label": "clear input",
4101
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_Clear2.default, { fontSize: "small" })
4102
+ }
4103
+ ) }),
4104
+ isTranslationMode && placeholderKey && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material18.Tooltip, { title: `Translate placeholder: "${placeholderKey}"`, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
4105
+ import_material18.IconButton,
4106
+ {
4107
+ size: "small",
4108
+ onClick: (e) => {
4109
+ e.preventDefault();
4110
+ e.stopPropagation();
4111
+ setIsDialogOpen(true);
4112
+ },
4113
+ sx: {
4114
+ color: "#d97706",
4115
+ backgroundColor: "rgba(254, 243, 199, 0.6)",
4116
+ border: "1px dashed #f59e0b",
4117
+ p: "3px",
4118
+ ml: 0.5,
4119
+ "&:hover": {
4120
+ backgroundColor: "#fef08a"
4121
+ }
4122
+ },
4123
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_Translate4.default, { fontSize: "small" })
4124
+ }
4125
+ ) })
4126
+ ] })
4127
+ ] });
4128
+ };
4129
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
4130
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
4131
+ import_material18.TextField,
4132
+ {
4133
+ ...restProps,
4134
+ label: typeof label === "string" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TranslateText, { overrideNamespace, children: label }) : label,
4135
+ value: localValue,
4136
+ onChange: handleChange,
4137
+ placeholder: translatedPlaceholder,
4138
+ slotProps: {
4139
+ ...slotProps,
4140
+ input: {
4141
+ ...slotProps?.input,
4142
+ endAdornment: renderEndAdornment()
4143
+ }
4144
+ }
4145
+ }
4146
+ ),
4147
+ placeholderKey && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
4148
+ TranslationDialog,
4149
+ {
4150
+ open: isDialogOpen,
4151
+ onClose: () => setIsDialogOpen(false),
4152
+ translationKey: placeholderKey,
4153
+ namespace,
4154
+ onSave: handleSaveTranslations
4155
+ }
4156
+ )
4157
+ ] });
4158
+ };
4159
+
4160
+ // src/UI/components/inputs/SelectComponent.tsx
4161
+ var import_material19 = require("@mui/material");
4162
+ var import_Close = __toESM(require("@mui/icons-material/Close"));
4163
+ init_context();
4164
+ var import_jsx_runtime29 = require("react/jsx-runtime");
4165
+ var SelectComponent = ({
4166
+ overrideNamespace,
4167
+ label,
4168
+ value,
4169
+ options,
4170
+ onChange,
4171
+ id = "translated-select",
4172
+ fullWidth,
4173
+ size = "medium",
4174
+ multiple = false,
4175
+ withCheckboxes = false,
4176
+ clearable = true,
4177
+ maxSelect,
4178
+ disabled,
4179
+ maxDisplayedTags = 2,
4180
+ helperText,
4181
+ defaultValueOnClear,
4182
+ sx,
4183
+ ...restProps
4184
+ }) => {
4185
+ const labelId = `${id}-label`;
4186
+ const safeValue = multiple ? Array.isArray(value) ? value : [] : value ?? "";
4187
+ const selectedArray = multiple ? safeValue : [];
4188
+ const isLimitReached = Boolean(multiple && maxSelect && selectedArray.length >= maxSelect);
4189
+ const hasValue = multiple ? selectedArray.length > 0 : safeValue !== "" && safeValue !== null && safeValue !== void 0;
4190
+ const handleChange = (event, child) => {
4191
+ if (multiple && maxSelect) {
4192
+ const newValue = event.target.value;
4193
+ if (newValue.length > maxSelect) {
4194
+ return;
4195
+ }
4196
+ }
4197
+ onChange?.(event, child);
4198
+ };
4199
+ const handleClear = (e) => {
4200
+ e.stopPropagation();
4201
+ if (!onChange) return;
4202
+ const emptyValue = multiple ? [] : "";
4203
+ const targetValue = defaultValueOnClear !== void 0 ? defaultValueOnClear : emptyValue;
4204
+ const syntheticEvent = {
4205
+ target: {
4206
+ value: targetValue,
4207
+ name: restProps.name
4208
+ }
4209
+ };
4210
+ onChange(syntheticEvent, null);
4211
+ };
4212
+ const renderValueHandler = (selected) => {
4213
+ if (!multiple) return void 0;
4214
+ const currentSelected = selected;
4215
+ if (currentSelected.length === 0) return null;
4216
+ const selectedOptions = options.filter((opt) => currentSelected.includes(opt.value));
4217
+ const visibleLabels = selectedOptions.slice(0, maxDisplayedTags).map((opt) => typeof opt.label === "string" ? opt.label : String(opt.value));
4218
+ const hiddenCount = selectedOptions.length - maxDisplayedTags;
4219
+ if (hiddenCount > 0) {
4220
+ return `${visibleLabels.join(", ")} (+${hiddenCount})`;
4221
+ }
4222
+ return visibleLabels.join(", ");
4223
+ };
4224
+ const isSmall = size === "small";
4225
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4226
+ import_material19.Tooltip,
4227
+ {
4228
+ title: multiple ? Array.isArray(value) ? options.filter((opt) => value.includes(opt.value)).map((opt) => typeof opt.label === "string" ? opt.label : String(opt.value)).join(", ") : "" : options.find((opt) => opt.value === value)?.label ?? String(value ?? ""),
4229
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_material19.FormControl, { fullWidth, size, sx: { mt: 1 }, children: [
4230
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material19.InputLabel, { id: labelId, size: isSmall ? "small" : "medium", children: typeof label === "string" ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TranslateText, { overrideNamespace, children: label }) : label }),
4231
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4232
+ import_material19.Select,
4233
+ {
4234
+ labelId,
4235
+ id,
4236
+ value: safeValue,
4237
+ label: typeof label === "string" ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TranslateText, { overrideNamespace, children: label }) : label,
4238
+ size,
4239
+ multiple,
4240
+ disabled,
4241
+ onChange: handleChange,
4242
+ renderValue: multiple ? renderValueHandler : void 0,
4243
+ ...restProps,
4244
+ endAdornment: clearable && hasValue && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material19.InputAdornment, { position: "end", sx: { mr: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4245
+ import_material19.IconButton,
4246
+ {
4247
+ size: "small",
4248
+ onClick: handleClear,
4249
+ "aria-label": "clear selection",
4250
+ edge: "end",
4251
+ sx: { p: 0.5 },
4252
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_Close.default, { fontSize: "small" })
4253
+ }
4254
+ ) }) : null,
4255
+ sx: {
4256
+ // 1. Задаем фиксированную высоту для всего контейнера инпута
4257
+ height: isSmall ? "40px" : "56px",
4258
+ minHeight: isSmall ? "40px" : "56px",
4259
+ // 2. Выравниваем внутренний класс текстового поля
4260
+ "& .MuiSelect-select": {
4261
+ display: "flex",
4262
+ alignItems: "center",
4263
+ boxSizing: "border-box",
4264
+ height: "100%",
4265
+ minHeight: "100% !important",
4266
+ py: 0,
4267
+ lineHeight: "normal",
4268
+ overflow: "hidden",
4269
+ textOverflow: "ellipsis",
4270
+ whiteSpace: "nowrap",
4271
+ paddingRight: clearable && hasValue ? "64px !important" : "32px !important"
4272
+ },
4273
+ // 3. Корректируем иконку стрелки выпадающего списка
4274
+ "& .MuiSelect-icon": {
4275
+ top: "calc(50% - 12px)"
4276
+ },
4277
+ // 4. Корректируем расположение иконки очистки
4278
+ "& .MuiInputAdornment-root": {
4279
+ maxHeight: "100%",
4280
+ height: "100%",
4281
+ alignItems: "center"
4282
+ },
4283
+ ...sx
4284
+ },
4285
+ children: options.map((option) => {
4286
+ const isSelected = multiple && selectedArray.includes(option.value);
4287
+ const isDisabled = Boolean(isLimitReached && !isSelected);
4288
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
4289
+ import_material19.MenuItem,
4290
+ {
4291
+ value: option.value,
4292
+ disabled: isDisabled,
4293
+ children: [
4294
+ multiple && withCheckboxes && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material19.Checkbox, { checked: isSelected, size, disabled: isDisabled }),
4295
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4296
+ import_material19.ListItemText,
4297
+ {
4298
+ primary: option.isItalic ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("em", { children: option.label }) : option.label
4299
+ }
4300
+ )
4301
+ ]
4302
+ },
4303
+ String(option.value)
4304
+ );
4305
+ })
4306
+ }
4307
+ ),
4308
+ helperText && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_material19.FormHelperText, { children: helperText })
4309
+ ] })
4310
+ }
4311
+ );
4312
+ };
4313
+
3981
4314
  // src/UI/index.ts
3982
4315
  var UI = {
3983
4316
  Inputs: {
3984
4317
  TextField: TextFieldComponent,
3985
4318
  NumberField: NumberInputComponent,
3986
4319
  EmailField: EmailInputComponent,
4320
+ Search: SearchComponent,
4321
+ Select: SelectComponent,
3987
4322
  Url: UrlComponent
3988
4323
  },
3989
4324
  Buttons: {
@@ -4024,6 +4359,11 @@ var Services = {
4024
4359
  getUsersLastSeenMap
4025
4360
  }
4026
4361
  };
4362
+
4363
+ // src/hooks/index.ts
4364
+ var Hooks = {
4365
+ useDebounce
4366
+ };
4027
4367
  // Annotate the CommonJS export names for ESM import in node:
4028
4368
  0 && (module.exports = {
4029
4369
  AIProvider,
@@ -4031,6 +4371,7 @@ var Services = {
4031
4371
  AuthProvider,
4032
4372
  BreadcrumbProvider,
4033
4373
  ConfirmationProvider,
4374
+ Hooks,
4034
4375
  LocalizationProvider,
4035
4376
  Providers,
4036
4377
  Services,
package/dist/index.mjs CHANGED
@@ -1247,12 +1247,352 @@ var PublicLanguageSelector = () => {
1247
1247
  ] });
1248
1248
  };
1249
1249
 
1250
+ // src/UI/components/inputs/SearchComponent.tsx
1251
+ import { useState as useState9, useEffect as useEffect5 } from "react";
1252
+ import { useTranslation as useTranslation3 } from "react-i18next";
1253
+ import { TextField as TextField3, InputAdornment as InputAdornment4, IconButton as IconButton5, Tooltip as Tooltip5 } from "@mui/material";
1254
+ import TranslateIcon2 from "@mui/icons-material/Translate";
1255
+ import ClearIcon2 from "@mui/icons-material/Clear";
1256
+
1257
+ // src/hooks/useDebounce.ts
1258
+ import { useEffect as useEffect4, useState as useState8 } from "react";
1259
+ function useDebounce(value, delay = 500) {
1260
+ const [debouncedValue, setDebouncedValue] = useState8(value);
1261
+ useEffect4(() => {
1262
+ const timer = setTimeout(() => {
1263
+ setDebouncedValue(value);
1264
+ }, delay);
1265
+ return () => {
1266
+ clearTimeout(timer);
1267
+ };
1268
+ }, [value, delay]);
1269
+ return debouncedValue;
1270
+ }
1271
+
1272
+ // src/UI/components/inputs/SearchComponent.tsx
1273
+ import { Fragment as Fragment7, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1274
+ var SearchComponent = ({
1275
+ overrideNamespace,
1276
+ label = "",
1277
+ value = "",
1278
+ onChange,
1279
+ onClear,
1280
+ placeholderKey,
1281
+ clearTooltip = "Clear",
1282
+ debounceDelay = 500,
1283
+ onDebounceChange,
1284
+ slotProps,
1285
+ ...restProps
1286
+ }) => {
1287
+ const supabase = useSupabase();
1288
+ const namespace = useTranslationNamespace();
1289
+ const { t, i18n } = useTranslation3(namespace);
1290
+ const { isTranslationMode } = useTranslationEdit();
1291
+ const [isDialogOpen, setIsDialogOpen] = useState9(false);
1292
+ const [localValue, setLocalValue] = useState9(String(value ?? ""));
1293
+ const debouncedValue = useDebounce(localValue, debounceDelay);
1294
+ useEffect5(() => {
1295
+ setLocalValue(String(value ?? ""));
1296
+ }, [value]);
1297
+ useEffect5(() => {
1298
+ if (onDebounceChange) {
1299
+ onDebounceChange(debouncedValue);
1300
+ }
1301
+ if (onChange && debouncedValue !== String(value ?? "")) {
1302
+ const syntheticEvent = {
1303
+ target: { value: debouncedValue },
1304
+ currentTarget: { value: debouncedValue }
1305
+ };
1306
+ onChange(syntheticEvent);
1307
+ }
1308
+ }, [debouncedValue]);
1309
+ const hasValue = localValue.length > 0;
1310
+ const handleChange = (e) => {
1311
+ setLocalValue(e.target.value);
1312
+ };
1313
+ const handleClear = (e) => {
1314
+ e.stopPropagation();
1315
+ setLocalValue("");
1316
+ if (onClear) {
1317
+ onClear();
1318
+ }
1319
+ if (onChange) {
1320
+ const syntheticEvent = {
1321
+ ...e,
1322
+ target: { ...e.target, value: "" },
1323
+ currentTarget: { ...e.currentTarget, value: "" }
1324
+ };
1325
+ onChange(syntheticEvent);
1326
+ }
1327
+ };
1328
+ const translatedPlaceholder = placeholderKey ? t(placeholderKey) : void 0;
1329
+ const handleSaveTranslations = async (newValues, contextHint) => {
1330
+ if (!placeholderKey) return;
1331
+ const upsertData = Object.entries(newValues).map(([langCode, text]) => ({
1332
+ lang_code: langCode,
1333
+ namespace,
1334
+ key: placeholderKey,
1335
+ value: text,
1336
+ context_hint: contextHint
1337
+ }));
1338
+ const { error } = await supabase.from("translations").upsert(upsertData, { onConflict: "lang_code,namespace,key" });
1339
+ if (error) {
1340
+ alert("\u041F\u043E\u043C\u0438\u043B\u043A\u0430 \u043F\u0456\u0434 \u0447\u0430\u0441 \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043D\u043D\u044F \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043D\u0438\u0445");
1341
+ throw error;
1342
+ }
1343
+ await translationService.incrementVersion(supabase, namespace, Object.keys(newValues));
1344
+ Object.entries(newValues).forEach(([langCode, text]) => {
1345
+ i18n.addResource(langCode, namespace, placeholderKey, text);
1346
+ });
1347
+ };
1348
+ const renderEndAdornment = () => {
1349
+ const rawInputProps = slotProps?.input;
1350
+ const isInputObject = typeof rawInputProps === "object" && rawInputProps !== null;
1351
+ const existingEndAdornment = isInputObject && "endAdornment" in rawInputProps ? rawInputProps.endAdornment : void 0;
1352
+ if (!hasValue && (!isTranslationMode || !placeholderKey)) {
1353
+ return existingEndAdornment;
1354
+ }
1355
+ return /* @__PURE__ */ jsxs7(Fragment7, { children: [
1356
+ existingEndAdornment,
1357
+ /* @__PURE__ */ jsxs7(InputAdornment4, { position: "end", children: [
1358
+ hasValue && /* @__PURE__ */ jsx8(TranslatableTooltipComponent, { title: /* @__PURE__ */ jsx8(TranslateText, { overrideNamespace, children: clearTooltip }), children: /* @__PURE__ */ jsx8(
1359
+ IconButton5,
1360
+ {
1361
+ size: "small",
1362
+ onClick: handleClear,
1363
+ edge: "end",
1364
+ "aria-label": "clear input",
1365
+ children: /* @__PURE__ */ jsx8(ClearIcon2, { fontSize: "small" })
1366
+ }
1367
+ ) }),
1368
+ isTranslationMode && placeholderKey && /* @__PURE__ */ jsx8(Tooltip5, { title: `Translate placeholder: "${placeholderKey}"`, children: /* @__PURE__ */ jsx8(
1369
+ IconButton5,
1370
+ {
1371
+ size: "small",
1372
+ onClick: (e) => {
1373
+ e.preventDefault();
1374
+ e.stopPropagation();
1375
+ setIsDialogOpen(true);
1376
+ },
1377
+ sx: {
1378
+ color: "#d97706",
1379
+ backgroundColor: "rgba(254, 243, 199, 0.6)",
1380
+ border: "1px dashed #f59e0b",
1381
+ p: "3px",
1382
+ ml: 0.5,
1383
+ "&:hover": {
1384
+ backgroundColor: "#fef08a"
1385
+ }
1386
+ },
1387
+ children: /* @__PURE__ */ jsx8(TranslateIcon2, { fontSize: "small" })
1388
+ }
1389
+ ) })
1390
+ ] })
1391
+ ] });
1392
+ };
1393
+ return /* @__PURE__ */ jsxs7(Fragment7, { children: [
1394
+ /* @__PURE__ */ jsx8(
1395
+ TextField3,
1396
+ {
1397
+ ...restProps,
1398
+ label: typeof label === "string" ? /* @__PURE__ */ jsx8(TranslateText, { overrideNamespace, children: label }) : label,
1399
+ value: localValue,
1400
+ onChange: handleChange,
1401
+ placeholder: translatedPlaceholder,
1402
+ slotProps: {
1403
+ ...slotProps,
1404
+ input: {
1405
+ ...slotProps?.input,
1406
+ endAdornment: renderEndAdornment()
1407
+ }
1408
+ }
1409
+ }
1410
+ ),
1411
+ placeholderKey && /* @__PURE__ */ jsx8(
1412
+ TranslationDialog,
1413
+ {
1414
+ open: isDialogOpen,
1415
+ onClose: () => setIsDialogOpen(false),
1416
+ translationKey: placeholderKey,
1417
+ namespace,
1418
+ onSave: handleSaveTranslations
1419
+ }
1420
+ )
1421
+ ] });
1422
+ };
1423
+
1424
+ // src/UI/components/inputs/SelectComponent.tsx
1425
+ import {
1426
+ FormControl,
1427
+ InputLabel,
1428
+ Select,
1429
+ MenuItem as MenuItem2,
1430
+ Checkbox,
1431
+ ListItemText,
1432
+ IconButton as IconButton6,
1433
+ InputAdornment as InputAdornment5,
1434
+ Tooltip as Tooltip6,
1435
+ FormHelperText
1436
+ } from "@mui/material";
1437
+ import CloseIcon from "@mui/icons-material/Close";
1438
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1439
+ var SelectComponent = ({
1440
+ overrideNamespace,
1441
+ label,
1442
+ value,
1443
+ options,
1444
+ onChange,
1445
+ id = "translated-select",
1446
+ fullWidth,
1447
+ size = "medium",
1448
+ multiple = false,
1449
+ withCheckboxes = false,
1450
+ clearable = true,
1451
+ maxSelect,
1452
+ disabled,
1453
+ maxDisplayedTags = 2,
1454
+ helperText,
1455
+ defaultValueOnClear,
1456
+ sx,
1457
+ ...restProps
1458
+ }) => {
1459
+ const labelId = `${id}-label`;
1460
+ const safeValue = multiple ? Array.isArray(value) ? value : [] : value ?? "";
1461
+ const selectedArray = multiple ? safeValue : [];
1462
+ const isLimitReached = Boolean(multiple && maxSelect && selectedArray.length >= maxSelect);
1463
+ const hasValue = multiple ? selectedArray.length > 0 : safeValue !== "" && safeValue !== null && safeValue !== void 0;
1464
+ const handleChange = (event, child) => {
1465
+ if (multiple && maxSelect) {
1466
+ const newValue = event.target.value;
1467
+ if (newValue.length > maxSelect) {
1468
+ return;
1469
+ }
1470
+ }
1471
+ onChange?.(event, child);
1472
+ };
1473
+ const handleClear = (e) => {
1474
+ e.stopPropagation();
1475
+ if (!onChange) return;
1476
+ const emptyValue = multiple ? [] : "";
1477
+ const targetValue = defaultValueOnClear !== void 0 ? defaultValueOnClear : emptyValue;
1478
+ const syntheticEvent = {
1479
+ target: {
1480
+ value: targetValue,
1481
+ name: restProps.name
1482
+ }
1483
+ };
1484
+ onChange(syntheticEvent, null);
1485
+ };
1486
+ const renderValueHandler = (selected) => {
1487
+ if (!multiple) return void 0;
1488
+ const currentSelected = selected;
1489
+ if (currentSelected.length === 0) return null;
1490
+ const selectedOptions = options.filter((opt) => currentSelected.includes(opt.value));
1491
+ const visibleLabels = selectedOptions.slice(0, maxDisplayedTags).map((opt) => typeof opt.label === "string" ? opt.label : String(opt.value));
1492
+ const hiddenCount = selectedOptions.length - maxDisplayedTags;
1493
+ if (hiddenCount > 0) {
1494
+ return `${visibleLabels.join(", ")} (+${hiddenCount})`;
1495
+ }
1496
+ return visibleLabels.join(", ");
1497
+ };
1498
+ const isSmall = size === "small";
1499
+ return /* @__PURE__ */ jsx9(
1500
+ Tooltip6,
1501
+ {
1502
+ title: multiple ? Array.isArray(value) ? options.filter((opt) => value.includes(opt.value)).map((opt) => typeof opt.label === "string" ? opt.label : String(opt.value)).join(", ") : "" : options.find((opt) => opt.value === value)?.label ?? String(value ?? ""),
1503
+ children: /* @__PURE__ */ jsxs8(FormControl, { fullWidth, size, sx: { mt: 1 }, children: [
1504
+ /* @__PURE__ */ jsx9(InputLabel, { id: labelId, size: isSmall ? "small" : "medium", children: typeof label === "string" ? /* @__PURE__ */ jsx9(TranslateText, { overrideNamespace, children: label }) : label }),
1505
+ /* @__PURE__ */ jsx9(
1506
+ Select,
1507
+ {
1508
+ labelId,
1509
+ id,
1510
+ value: safeValue,
1511
+ label: typeof label === "string" ? /* @__PURE__ */ jsx9(TranslateText, { overrideNamespace, children: label }) : label,
1512
+ size,
1513
+ multiple,
1514
+ disabled,
1515
+ onChange: handleChange,
1516
+ renderValue: multiple ? renderValueHandler : void 0,
1517
+ ...restProps,
1518
+ endAdornment: clearable && hasValue && !disabled ? /* @__PURE__ */ jsx9(InputAdornment5, { position: "end", sx: { mr: 1 }, children: /* @__PURE__ */ jsx9(
1519
+ IconButton6,
1520
+ {
1521
+ size: "small",
1522
+ onClick: handleClear,
1523
+ "aria-label": "clear selection",
1524
+ edge: "end",
1525
+ sx: { p: 0.5 },
1526
+ children: /* @__PURE__ */ jsx9(CloseIcon, { fontSize: "small" })
1527
+ }
1528
+ ) }) : null,
1529
+ sx: {
1530
+ // 1. Задаем фиксированную высоту для всего контейнера инпута
1531
+ height: isSmall ? "40px" : "56px",
1532
+ minHeight: isSmall ? "40px" : "56px",
1533
+ // 2. Выравниваем внутренний класс текстового поля
1534
+ "& .MuiSelect-select": {
1535
+ display: "flex",
1536
+ alignItems: "center",
1537
+ boxSizing: "border-box",
1538
+ height: "100%",
1539
+ minHeight: "100% !important",
1540
+ py: 0,
1541
+ lineHeight: "normal",
1542
+ overflow: "hidden",
1543
+ textOverflow: "ellipsis",
1544
+ whiteSpace: "nowrap",
1545
+ paddingRight: clearable && hasValue ? "64px !important" : "32px !important"
1546
+ },
1547
+ // 3. Корректируем иконку стрелки выпадающего списка
1548
+ "& .MuiSelect-icon": {
1549
+ top: "calc(50% - 12px)"
1550
+ },
1551
+ // 4. Корректируем расположение иконки очистки
1552
+ "& .MuiInputAdornment-root": {
1553
+ maxHeight: "100%",
1554
+ height: "100%",
1555
+ alignItems: "center"
1556
+ },
1557
+ ...sx
1558
+ },
1559
+ children: options.map((option) => {
1560
+ const isSelected = multiple && selectedArray.includes(option.value);
1561
+ const isDisabled = Boolean(isLimitReached && !isSelected);
1562
+ return /* @__PURE__ */ jsxs8(
1563
+ MenuItem2,
1564
+ {
1565
+ value: option.value,
1566
+ disabled: isDisabled,
1567
+ children: [
1568
+ multiple && withCheckboxes && /* @__PURE__ */ jsx9(Checkbox, { checked: isSelected, size, disabled: isDisabled }),
1569
+ /* @__PURE__ */ jsx9(
1570
+ ListItemText,
1571
+ {
1572
+ primary: option.isItalic ? /* @__PURE__ */ jsx9("em", { children: option.label }) : option.label
1573
+ }
1574
+ )
1575
+ ]
1576
+ },
1577
+ String(option.value)
1578
+ );
1579
+ })
1580
+ }
1581
+ ),
1582
+ helperText && /* @__PURE__ */ jsx9(FormHelperText, { children: helperText })
1583
+ ] })
1584
+ }
1585
+ );
1586
+ };
1587
+
1250
1588
  // src/UI/index.ts
1251
1589
  var UI = {
1252
1590
  Inputs: {
1253
1591
  TextField: TextFieldComponent,
1254
1592
  NumberField: NumberInputComponent,
1255
1593
  EmailField: EmailInputComponent,
1594
+ Search: SearchComponent,
1595
+ Select: SelectComponent,
1256
1596
  Url: UrlComponent
1257
1597
  },
1258
1598
  Buttons: {
@@ -1288,12 +1628,18 @@ var Services = {
1288
1628
  getUsersLastSeenMap
1289
1629
  }
1290
1630
  };
1631
+
1632
+ // src/hooks/index.ts
1633
+ var Hooks = {
1634
+ useDebounce
1635
+ };
1291
1636
  export {
1292
1637
  AIProvider,
1293
1638
  AppSettingsProvider,
1294
1639
  AuthProvider,
1295
1640
  BreadcrumbProvider,
1296
1641
  ConfirmationProvider,
1642
+ Hooks,
1297
1643
  LocalizationProvider,
1298
1644
  Providers,
1299
1645
  Services,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serhii.olishevskyi/docs",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "DOCS.UA react packages",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",