@letar/forms 1.0.0 → 1.0.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/CHANGELOG.md +34 -0
- package/README.md +79 -4
- package/{chunk-G3HYXHCZ.js → chunk-4V6WBJ76.js} +14 -14
- package/chunk-4V6WBJ76.js.map +1 -0
- package/{chunk-GIBNEYK3.js → chunk-7FEQFDJ7.js} +5 -4
- package/chunk-7FEQFDJ7.js.map +1 -0
- package/i18n.js +1 -1
- package/index.js +454 -290
- package/index.js.map +1 -1
- package/offline.js +1 -1
- package/package.json +7 -2
- package/chunk-G3HYXHCZ.js.map +0 -1
- package/chunk-GIBNEYK3.js.map +0 -1
package/offline.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { FormOfflineIndicator, FormSyncStatus, addToQueue, clearQueue, createSyncQueueStore, getOfflineStatus, getQueueFromStorage, processQueueItem, removeFromQueue, subscribeToStatusChanges, useOfflineForm, useOfflineStatus, useSyncQueue } from './chunk-
|
|
1
|
+
export { FormOfflineIndicator, FormSyncStatus, addToQueue, clearQueue, createSyncQueueStore, getOfflineStatus, getQueueFromStorage, processQueueItem, removeFromQueue, subscribeToStatusChanges, useOfflineForm, useOfflineStatus, useSyncQueue } from './chunk-4V6WBJ76.js';
|
|
2
2
|
//# sourceMappingURL=offline.js.map
|
|
3
3
|
//# sourceMappingURL=offline.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@letar/forms",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Declarative form components for React with 40+ field types, powered by TanStack Form and Chakra UI v3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"**/*.d.ts",
|
|
29
29
|
"**/*.d.ts.map",
|
|
30
30
|
"README.md",
|
|
31
|
+
"README.ru.md",
|
|
31
32
|
"CHANGELOG.md",
|
|
32
33
|
"LICENSE"
|
|
33
34
|
],
|
|
@@ -63,7 +64,8 @@
|
|
|
63
64
|
"react": ">=18.0.0",
|
|
64
65
|
"react-icons": ">=5.0.0",
|
|
65
66
|
"use-mask-input": ">=3.0.0",
|
|
66
|
-
"zod": ">=3.24.0"
|
|
67
|
+
"zod": ">=3.24.0",
|
|
68
|
+
"@uiw/react-json-view": ">=2.0.0"
|
|
67
69
|
},
|
|
68
70
|
"peerDependenciesMeta": {
|
|
69
71
|
"@dnd-kit/core": {
|
|
@@ -81,6 +83,9 @@
|
|
|
81
83
|
"@tiptap/starter-kit": {
|
|
82
84
|
"optional": true
|
|
83
85
|
},
|
|
86
|
+
"@uiw/react-json-view": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
84
89
|
"next-intl": {
|
|
85
90
|
"optional": true
|
|
86
91
|
},
|
package/chunk-G3HYXHCZ.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/offline/offline-service.ts","../src/lib/offline/use-offline-status.ts","../src/lib/offline/use-sync-queue.ts","../src/lib/offline/use-offline-form.ts","../src/lib/offline/form-offline-indicator.tsx","../src/lib/offline/form-sync-status.tsx"],"names":["listeners","notifyListeners","isOffline","useSyncExternalStore","useState","useCallback","useEffect","jsxs","HStack","jsx","Icon","Badge"],"mappings":"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","file":"chunk-G3HYXHCZ.js","sourcesContent":["/**\n * Сервис оффлайн-функциональности\n *\n * Бизнес-логика:\n * - Определение статуса онлайн/оффлайн\n * - Очередь синхронизации действий в IndexedDB\n */\n\nimport type { ProcessQueueResult, SyncAction, SyncActionHandler, SyncQueueItem, SyncQueueStore } from './types'\n\n// ============================================\n// ЛЕНИВЫЙ ИМПОРТ IDB-KEYVAL\n// ============================================\n\n/**\n * Проверка, что мы в браузере с поддержкой IndexedDB\n */\nfunction canUseIDB(): boolean {\n return typeof window !== 'undefined' && typeof indexedDB !== 'undefined'\n}\n\n/**\n * Ленивый импорт idb-keyval для избежания SSR проблем\n * Кэшируем промис для предотвращения повторных импортов\n */\nlet idbModule: typeof import('idb-keyval') | null = null\nasync function getIDB(): Promise<typeof import('idb-keyval') | null> {\n if (!canUseIDB()) {\n return null\n }\n if (!idbModule) {\n idbModule = await import('idb-keyval')\n }\n return idbModule\n}\n\n// ============================================\n// КОНСТАНТЫ\n// ============================================\n\nconst DEFAULT_SYNC_QUEUE_STORAGE_KEY = 'lena-form-sync-queue'\n\n// ============================================\n// СТАТУС СОЕДИНЕНИЯ\n// ============================================\n\n/**\n * Получить текущий статус оффлайн\n * @returns true если оффлайн, false если онлайн\n */\nexport function getOfflineStatus(): boolean {\n if (typeof navigator === 'undefined') {\n return false\n }\n return !navigator.onLine\n}\n\n/**\n * Подписаться на изменения статуса соединения\n * @param callback - функция, вызываемая при изменении статуса\n * @returns функция отписки\n */\nexport function subscribeToStatusChanges(callback: (isOffline: boolean) => void): () => void {\n if (typeof window === 'undefined') {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {}\n }\n\n const handleOnline = () => callback(false)\n const handleOffline = () => callback(true)\n\n window.addEventListener('online', handleOnline)\n window.addEventListener('offline', handleOffline)\n\n return () => {\n window.removeEventListener('online', handleOnline)\n window.removeEventListener('offline', handleOffline)\n }\n}\n\n// ============================================\n// ОЧЕРЕДЬ СИНХРОНИЗАЦИИ\n// ============================================\n\n/**\n * Генерация уникального ID\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n/**\n * Получить очередь из IndexedDB\n */\nexport async function getQueueFromStorage(storageKey?: string): Promise<SyncQueueItem[]> {\n try {\n const idb = await getIDB()\n if (!idb) {\n return []\n }\n const key = storageKey ?? DEFAULT_SYNC_QUEUE_STORAGE_KEY\n const stored = await idb.get<SyncQueueItem[]>(key)\n return stored ?? []\n } catch (error) {\n console.error('[OfflineService] Ошибка загрузки очереди из IndexedDB:', error)\n return []\n }\n}\n\n/**\n * Сохранить очередь в IndexedDB\n */\nasync function saveQueueToStorage(queue: SyncQueueItem[], storageKey?: string): Promise<void> {\n try {\n const idb = await getIDB()\n if (!idb) {\n return\n }\n const key = storageKey ?? DEFAULT_SYNC_QUEUE_STORAGE_KEY\n await idb.set(key, queue)\n } catch (error) {\n console.error('[OfflineService] Ошибка сохранения очереди в IndexedDB:', error)\n }\n}\n\n/**\n * Добавить действие в очередь\n */\nexport async function addToQueue(action: SyncAction, storageKey?: string): Promise<SyncQueueItem> {\n const queue = await getQueueFromStorage(storageKey)\n\n const item: SyncQueueItem = {\n id: generateId(),\n action,\n createdAt: Date.now(),\n attempts: 0,\n maxAttempts: 3,\n status: 'PENDING',\n }\n\n queue.push(item)\n await saveQueueToStorage(queue, storageKey)\n\n return item\n}\n\n/**\n * Удалить элемент из очереди\n */\nexport async function removeFromQueue(id: string, storageKey?: string): Promise<boolean> {\n const queue = await getQueueFromStorage(storageKey)\n const index = queue.findIndex((item) => item.id === id)\n\n if (index === -1) {\n return false\n }\n\n queue.splice(index, 1)\n await saveQueueToStorage(queue, storageKey)\n\n return true\n}\n\n/**\n * Обработать один элемент очереди\n */\nexport async function processQueueItem(item: SyncQueueItem, handler: SyncActionHandler): Promise<ProcessQueueResult> {\n try {\n const result = await handler(item.action)\n\n if (result.success) {\n return {\n success: true,\n item: { ...item, status: 'SYNCED' as const },\n }\n }\n\n // Неуспешный результат без исключения\n const updatedItem: SyncQueueItem = {\n ...item,\n attempts: item.attempts + 1,\n status: item.attempts + 1 >= item.maxAttempts ? 'FAILED' : 'PENDING',\n error: result.error,\n }\n\n return {\n success: false,\n item: updatedItem,\n error: result.error,\n }\n } catch (error) {\n // Исключение при выполнении\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n const updatedItem: SyncQueueItem = {\n ...item,\n attempts: item.attempts + 1,\n status: item.attempts + 1 >= item.maxAttempts ? 'FAILED' : 'PENDING',\n error: errorMessage,\n }\n\n return {\n success: false,\n item: updatedItem,\n error: errorMessage,\n }\n }\n}\n\n/**\n * Очистить очередь\n */\nexport async function clearQueue(storageKey?: string): Promise<void> {\n try {\n const idb = await getIDB()\n if (!idb) {\n return\n }\n const key = storageKey ?? DEFAULT_SYNC_QUEUE_STORAGE_KEY\n await idb.del(key)\n } catch (error) {\n console.error('[OfflineService] Ошибка очистки очереди из IndexedDB:', error)\n }\n}\n\n/**\n * Создать store для очереди синхронизации\n */\nexport function createSyncQueueStore(storageKey?: string): SyncQueueStore {\n let queue: SyncQueueItem[] = []\n const listeners = new Set<() => void>()\n const key = storageKey ?? DEFAULT_SYNC_QUEUE_STORAGE_KEY\n\n const notifyListeners = () => {\n listeners.forEach((listener) => listener())\n }\n\n return {\n getQueue: () => queue,\n\n getQueueLength: () => queue.length,\n\n subscribe: (listener: () => void) => {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n initialize: async () => {\n queue = await getQueueFromStorage(key)\n notifyListeners()\n },\n\n add: async (action: SyncAction) => {\n const item = await addToQueue(action, key)\n queue.push(item)\n notifyListeners()\n return item\n },\n\n remove: async (id: string) => {\n const result = await removeFromQueue(id, key)\n if (result) {\n queue = queue.filter((item) => item.id !== id)\n notifyListeners()\n }\n return result\n },\n\n processAll: async (handler: SyncActionHandler) => {\n const results: ProcessQueueResult[] = []\n\n for (const item of queue) {\n if (item.status === 'PENDING') {\n const result = await processQueueItem(item, handler)\n results.push(result)\n\n // Обновляем очередь после обработки\n if (result.success && result.item) {\n await removeFromQueue(item.id, key)\n queue = queue.filter((q) => q.id !== item.id)\n } else if (result.item) {\n // Обновляем элемент в очереди\n const index = queue.findIndex((q) => q.id === item.id)\n if (index !== -1) {\n queue[index] = result.item\n }\n await saveQueueToStorage(queue, key)\n }\n }\n }\n\n notifyListeners()\n return results\n },\n }\n}\n","'use client'\n\nimport { useSyncExternalStore } from 'react'\n\nimport { getOfflineStatus, subscribeToStatusChanges } from './offline-service'\n\n// Глобальное состояние для синхронизации между вкладками\nlet isOffline = false\n\nconst listeners = new Set<() => void>()\n\nconst notifyListeners = () => {\n listeners.forEach((listener) => listener())\n}\n\n// Инициализация при первой загрузке\nif (typeof window !== 'undefined') {\n isOffline = getOfflineStatus()\n\n subscribeToStatusChanges((offline) => {\n isOffline = offline\n notifyListeners()\n })\n}\n\n/**\n * Хук для определения статуса оффлайн\n *\n * @returns true если браузер оффлайн\n *\n * @example\n * ```tsx\n * import { useOfflineStatus } from '@lena/form-components/offline'\n *\n * function MyComponent() {\n * const isOffline = useOfflineStatus()\n *\n * if (isOffline) {\n * return <OfflineBanner />\n * }\n *\n * return <OnlineContent />\n * }\n * ```\n */\nexport function useOfflineStatus(): boolean {\n return useSyncExternalStore(\n (callback) => {\n listeners.add(callback)\n return () => {\n listeners.delete(callback)\n }\n },\n () => isOffline,\n () => false // SSR fallback — считаем что онлайн\n )\n}\n","'use client'\n\nimport { useCallback, useEffect, useState, useSyncExternalStore } from 'react'\n\nimport { createSyncQueueStore } from './offline-service'\nimport type { ProcessQueueResult, SyncAction, SyncActionHandler, SyncQueueItem, UseSyncQueueResult } from './types'\nimport { useOfflineStatus } from './use-offline-status'\n\n// Глобальный store для очереди синхронизации (по умолчанию)\nconst defaultSyncQueueStore = createSyncQueueStore()\nconst EMPTY_QUEUE: SyncQueueItem[] = []\n\n// Флаг инициализации\nlet initialized = false\n\nconst initialize = async () => {\n if (!initialized && typeof window !== 'undefined') {\n initialized = true\n await defaultSyncQueueStore.initialize()\n }\n}\n\n/**\n * Хук для работы с очередью синхронизации\n *\n * Позволяет добавлять действия в очередь при оффлайн режиме\n * и синхронизировать их при восстановлении соединения.\n *\n * @example\n * ```tsx\n * import { useSyncQueue } from '@lena/form-components/offline'\n *\n * function MyComponent() {\n * const { queue, queueLength, addAction, processQueue, isProcessing } = useSyncQueue()\n *\n * // Добавление действия в очередь (работает и оффлайн)\n * const handleBookLesson = async (slotId: string) => {\n * if (isOffline) {\n * await addAction({ type: 'BOOK_LESSON', payload: { slotId } })\n * toast({ title: 'Действие добавлено в очередь синхронизации' })\n * } else {\n * await api.bookLesson(slotId)\n * }\n * }\n *\n * // Обработка очереди при восстановлении соединения\n * useEffect(() => {\n * if (!isOffline && queueLength > 0) {\n * processQueue(async (action) => {\n * switch (action.type) {\n * case 'BOOK_LESSON':\n * return api.bookLesson(action.payload.slotId)\n * // ... другие типы действий\n * }\n * })\n * }\n * }, [isOffline, queueLength, processQueue])\n * }\n * ```\n */\nexport function useSyncQueue(): UseSyncQueueResult {\n const [isLoading, setIsLoading] = useState(true)\n const [isProcessing, setIsProcessing] = useState(false)\n const isOffline = useOfflineStatus()\n\n // Подписка на изменения очереди\n const queue = useSyncExternalStore(\n (callback) => defaultSyncQueueStore.subscribe(callback),\n () => defaultSyncQueueStore.getQueue(),\n () => EMPTY_QUEUE // SSR fallback\n )\n\n // Инициализация при монтировании\n useEffect(() => {\n initialize().then(() => {\n setIsLoading(false)\n })\n }, [])\n\n // Добавление действия в очередь\n const addAction = useCallback(async (action: SyncAction): Promise<SyncQueueItem> => {\n return defaultSyncQueueStore.add(action)\n }, [])\n\n // Удаление действия из очереди\n const removeAction = useCallback(async (id: string): Promise<boolean> => {\n return defaultSyncQueueStore.remove(id)\n }, [])\n\n // Обработка всей очереди\n const processQueue = useCallback(\n async (handler: SyncActionHandler): Promise<ProcessQueueResult[]> => {\n if (isOffline) {\n console.warn('[SyncQueue] Невозможно обработать очередь в оффлайн режиме')\n return []\n }\n\n setIsProcessing(true)\n try {\n return await defaultSyncQueueStore.processAll(handler)\n } finally {\n setIsProcessing(false)\n }\n },\n [isOffline]\n )\n\n return {\n queue,\n queueLength: queue.length,\n pendingCount: queue.filter((item) => item.status === 'PENDING').length,\n isLoading,\n isProcessing,\n addAction,\n removeAction,\n processQueue,\n }\n}\n","'use client'\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { OfflineSubmitResult, SyncAction, UseOfflineFormOptions, UseOfflineFormResult } from './types'\nimport { useOfflineStatus } from './use-offline-status'\nimport { useSyncQueue } from './use-sync-queue'\n\n/**\n * Хук для оффлайн-поддержки форм с TanStack Form\n *\n * Автоматически определяет статус соединения и:\n * - Онлайн: отправляет данные напрямую\n * - Оффлайн: сохраняет в очередь IndexedDB для синхронизации\n *\n * @example\n * ```tsx\n * import { useOfflineForm } from '@lena/form-components/offline'\n *\n * function ProfileForm({ initialData }) {\n * const { submit, isOffline, pendingCount, isProcessing } = useOfflineForm({\n * actionType: 'UPDATE_PROFILE',\n * onlineSubmit: async (value) => {\n * const result = await updateProfileAction(value)\n * return { success: result.success, error: result.error?.formErrors?.[0] }\n * },\n * onSuccess: () => toaster.success({ title: 'Сохранено' }),\n * onQueued: () => toaster.info({ title: 'Сохранено локально' }),\n * onError: (error) => toaster.error({ title: 'Ошибка', description: error }),\n * })\n *\n * const form = useAppForm({\n * defaultValues: initialData,\n * onSubmit: async ({ value }) => {\n * await submit(value)\n * },\n * })\n *\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); form.handleSubmit() }}>\n * {isOffline && <Badge colorPalette=\"orange\">Оффлайн режим</Badge>}\n * {pendingCount > 0 && (\n * <Badge colorPalette=\"blue\">\n * {isProcessing ? 'Синхронизация...' : `Ожидает: ${pendingCount}`}\n * </Badge>\n * )}\n * <form.AppField name=\"name\" children={(field) => <field.TextField label=\"Имя\" />} />\n * <Button type=\"submit\">{isOffline ? 'Сохранить локально' : 'Сохранить'}</Button>\n * </form>\n * )\n * }\n * ```\n */\nexport function useOfflineForm<T extends object>({\n actionType,\n onlineSubmit,\n onSuccess,\n onQueued,\n onError,\n}: UseOfflineFormOptions<T>): UseOfflineFormResult<T> {\n const isOffline = useOfflineStatus()\n const { addAction, processQueue, pendingCount, isProcessing, queueLength } = useSyncQueue()\n const [lastSyncAttempt, setLastSyncAttempt] = useState<number | null>(null)\n\n // Ref для предотвращения повторной обработки очереди\n const processingRef = useRef(false)\n\n /**\n * Отправка формы с поддержкой оффлайн\n */\n const submit = useCallback(\n async (value: T): Promise<OfflineSubmitResult> => {\n // Оффлайн — добавляем в очередь\n if (isOffline) {\n try {\n const queueItem = await addAction({\n type: actionType,\n payload: value as Record<string, unknown>,\n })\n\n onQueued?.()\n\n return {\n success: true,\n queued: true,\n queueItemId: queueItem.id,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Ошибка сохранения в очередь'\n onError?.(errorMessage)\n return {\n success: false,\n error: errorMessage,\n }\n }\n }\n\n // Онлайн — отправляем напрямую\n try {\n const result = await onlineSubmit(value)\n\n if (result.success) {\n onSuccess?.()\n } else if (result.error) {\n onError?.(result.error)\n }\n\n return {\n success: result.success,\n error: result.error,\n queued: false,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Ошибка отправки'\n onError?.(errorMessage)\n return {\n success: false,\n error: errorMessage,\n queued: false,\n }\n }\n },\n [isOffline, actionType, addAction, onlineSubmit, onSuccess, onQueued, onError]\n )\n\n /**\n * Обработчик действий из очереди\n * Вызывается при восстановлении соединения\n */\n const handleQueuedAction = useCallback(\n async (action: SyncAction): Promise<{ success: boolean; error?: string }> => {\n // Обрабатываем только наш тип действия\n if (action.type !== actionType) {\n return { success: true } // Пропускаем чужие действия\n }\n\n return onlineSubmit(action.payload as T)\n },\n [actionType, onlineSubmit]\n )\n\n // Автоматическая синхронизация при восстановлении соединения\n useEffect(() => {\n // Если онлайн и есть элементы в очереди — пробуем синхронизировать\n if (!isOffline && pendingCount > 0 && !processingRef.current) {\n processingRef.current = true\n setLastSyncAttempt(Date.now())\n\n processQueue(handleQueuedAction)\n .then((results) => {\n const failed = results.filter((r) => !r.success)\n if (failed.length > 0) {\n console.warn(`[OfflineForm] ${failed.length} действий не удалось синхронизировать`)\n }\n })\n .finally(() => {\n processingRef.current = false\n })\n }\n }, [isOffline, pendingCount, processQueue, handleQueuedAction])\n\n return {\n submit,\n isOffline,\n pendingCount,\n queueLength,\n isProcessing,\n lastSyncAttempt,\n }\n}\n","'use client'\n\nimport { Badge, type BadgeProps, HStack, Icon } from '@chakra-ui/react'\nimport { LuWifiOff } from 'react-icons/lu'\n\nimport type { OfflineIndicatorProps } from './types'\nimport { useOfflineStatus } from './use-offline-status'\n\n/**\n * Индикатор оффлайн режима\n *\n * Автоматически отображается когда браузер оффлайн.\n * Скрывается при восстановлении соединения.\n *\n * @example\n * ```tsx\n * import { Form } from '@lena/form-components'\n *\n * <Form initialValue={data} onSubmit={handleSubmit}>\n * <Form.OfflineIndicator />\n * <Form.Field.String name=\"title\" />\n * <Form.Button.Submit />\n * </Form>\n * ```\n *\n * @example С настройками\n * ```tsx\n * <Form.OfflineIndicator\n * label=\"Нет связи\"\n * colorPalette=\"red\"\n * variant=\"solid\"\n * />\n * ```\n */\nexport function FormOfflineIndicator({\n label = 'Оффлайн режим',\n colorPalette = 'orange',\n variant = 'subtle',\n ...rest\n}: OfflineIndicatorProps & Omit<BadgeProps, 'children'>) {\n const isOffline = useOfflineStatus()\n\n if (!isOffline) {\n return null\n }\n\n return (\n <Badge colorPalette={colorPalette} variant={variant} data-testid=\"offline-indicator\" {...rest}>\n <HStack gap={1}>\n <Icon asChild boxSize={3}>\n <LuWifiOff />\n </Icon>\n <span>{label}</span>\n </HStack>\n </Badge>\n )\n}\n","'use client'\n\nimport { Badge, type BadgeProps, HStack, Icon, Spinner } from '@chakra-ui/react'\nimport { LuCheck, LuClock } from 'react-icons/lu'\n\nimport type { SyncStatusProps } from './types'\nimport { useOfflineStatus } from './use-offline-status'\nimport { useSyncQueue } from './use-sync-queue'\n\n/**\n * Индикатор статуса синхронизации очереди\n *\n * Показывает:\n * - Количество ожидающих действий\n * - Spinner при синхронизации\n * - \"Синхронизировано\" когда очередь пуста\n *\n * Работает глобально, не требует Form контекста.\n *\n * @example\n * ```tsx\n * import { FormSyncStatus } from '@lena/form-components/offline'\n *\n * // В layout или header\n * <FormSyncStatus />\n * ```\n *\n * @example С настройками\n * ```tsx\n * <FormSyncStatus\n * showWhenEmpty={false}\n * syncingLabel=\"Синхронизация...\"\n * pendingLabel={(count) => `Ожидает: ${count}`}\n * syncedLabel=\"Всё синхронизировано\"\n * />\n * ```\n */\nexport function FormSyncStatus({\n showWhenEmpty = false,\n syncingLabel = 'Синхронизация...',\n pendingLabel = (count: number) => `Ожидает: ${count}`,\n syncedLabel = 'Синхронизировано',\n colorPalette = 'blue',\n ...rest\n}: SyncStatusProps & Omit<BadgeProps, 'children'>) {\n const isOffline = useOfflineStatus()\n const { pendingCount, isProcessing } = useSyncQueue()\n\n // Скрываем если онлайн, очередь пуста и showWhenEmpty = false\n if (!isOffline && pendingCount === 0 && !isProcessing && !showWhenEmpty) {\n return null\n }\n\n // Определяем что показывать\n const renderContent = () => {\n // Синхронизация в процессе\n if (isProcessing) {\n return (\n <HStack gap={1}>\n <Spinner size=\"xs\" />\n <span>{syncingLabel}</span>\n </HStack>\n )\n }\n\n // Есть ожидающие элементы\n if (pendingCount > 0) {\n const label = typeof pendingLabel === 'function' ? pendingLabel(pendingCount) : pendingLabel\n return (\n <HStack gap={1}>\n <Icon asChild boxSize={3}>\n <LuClock />\n </Icon>\n <span>{label}</span>\n </HStack>\n )\n }\n\n // Всё синхронизировано\n return (\n <HStack gap={1}>\n <Icon asChild boxSize={3}>\n <LuCheck />\n </Icon>\n <span>{syncedLabel}</span>\n </HStack>\n )\n }\n\n // Цвет зависит от состояния\n const effectiveColorPalette = pendingCount > 0 ? 'orange' : isProcessing ? colorPalette : 'green'\n\n return (\n <Badge\n colorPalette={effectiveColorPalette}\n variant=\"subtle\"\n data-testid=\"sync-status\"\n data-pending-count={pendingCount}\n data-processing={isProcessing}\n {...rest}\n >\n {renderContent()}\n </Badge>\n )\n}\n"]}
|
package/chunk-GIBNEYK3.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/i18n/create-form-error-map.ts","../src/lib/i18n/form-i18n-provider.tsx","../src/lib/i18n/use-localized-options.ts"],"names":[],"mappings":"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","file":"chunk-GIBNEYK3.js","sourcesContent":["import type { TranslateFunction, TranslateParams } from './form-i18n-provider'\n\n/**\n * Zod v4 issue type (упрощённый тип для работы с error map)\n * Полный тип: z.core.$ZodIssue (discriminated union)\n */\ninterface ZodIssue {\n readonly code: string\n readonly path: PropertyKey[]\n readonly message: string\n readonly input?: unknown\n readonly expected?: string\n readonly minimum?: number\n readonly maximum?: number\n readonly inclusive?: boolean\n readonly origin?: string\n readonly format?: string\n readonly options?: unknown[]\n readonly keys?: string[]\n readonly multipleOf?: number\n}\n\n/**\n * Конфигурация для создания error map\n */\nexport interface FormErrorMapConfig {\n /** Функция перевода (совместима с next-intl) */\n t: TranslateFunction\n /** Префикс для ключей валидации (по умолчанию 'validation') */\n prefix?: string\n}\n\n/**\n * Извлекает параметры из Zod issue для интерполяции в сообщение\n */\nfunction extractParams(issue: ZodIssue): TranslateParams {\n const params: TranslateParams = {}\n\n // Общие параметры\n if (issue.input !== undefined) {\n params.received = typeof issue.input === 'object' ? 'object' : String(issue.input)\n }\n\n // too_small / too_big\n if (issue.minimum !== undefined) {\n params.minimum = issue.minimum\n }\n if (issue.maximum !== undefined) {\n params.maximum = issue.maximum\n }\n if (issue.inclusive !== undefined) {\n params.inclusive = issue.inclusive\n }\n\n // invalid_type\n if (issue.expected !== undefined) {\n params.expected = issue.expected\n }\n\n // invalid_value (объединяет invalid_enum_value + invalid_literal в Zod v4)\n if (issue.options && Array.isArray(issue.options)) {\n params.options = issue.options.join(', ')\n }\n\n // unrecognized_keys\n if (issue.keys && Array.isArray(issue.keys)) {\n params.keys = issue.keys.join(', ')\n }\n\n // not_multiple_of\n if (issue.multipleOf !== undefined) {\n params.multipleOf = issue.multipleOf\n }\n\n // custom — сообщение из .refine()\n if (issue.message) {\n params.message = issue.message\n }\n\n return params\n}\n\n/**\n * Определяет origin (тип данных) для issue\n * Используется для построения ключа: validation.too_small.string\n */\nfunction getIssueOrigin(issue: ZodIssue): string | undefined {\n // Для too_small / too_big определяем origin по типу input или expected\n if (issue.code === 'too_small' || issue.code === 'too_big') {\n if (issue.origin) {\n return issue.origin\n }\n // Fallback по типу input\n const input = issue.input\n if (typeof input === 'string') {\n return 'string'\n }\n if (typeof input === 'number') {\n return 'number'\n }\n if (Array.isArray(input)) {\n return 'array'\n }\n if (input instanceof Date) {\n return 'date'\n }\n }\n\n // Для invalid_format (Zod v4, ранее invalid_string) определяем format\n if (issue.code === 'invalid_format') {\n if (issue.format) {\n return issue.format\n }\n }\n\n return undefined\n}\n\n/**\n * Пытается получить перевод по ключу\n * @returns переведённая строка или undefined если перевод не найден\n */\nfunction tryTranslate(t: TranslateFunction, key: string, params: TranslateParams): string | undefined {\n try {\n const result = t(key, params)\n // next-intl возвращает ключ при отсутствии перевода\n if (!result || result === key || result.startsWith(key)) {\n return undefined\n }\n return result\n } catch {\n return undefined\n }\n}\n\n/**\n * Создаёт Zod error map с поддержкой i18n\n *\n * Error map преобразует Zod ошибки в переведённые сообщения.\n * Ключи строятся по формату: `{prefix}.{code}.{origin?}`\n *\n * @example\n * ```tsx\n * import { createFormErrorMap } from '@lena/form-components'\n * import { z } from 'zod/v4'\n * import { useTranslations } from 'next-intl'\n *\n * // В провайдере приложения\n * const t = useTranslations('formSchemas')\n * z.config({ customError: createFormErrorMap({ t }) })\n *\n * // Или через FormI18nProvider\n * <FormI18nProvider t={t} locale={locale} setupZodErrorMap>\n * {children}\n * </FormI18nProvider>\n * ```\n *\n * @example Структура ключей в JSON\n * ```json\n * {\n * \"validation\": {\n * \"required\": \"Обязательное поле\",\n * \"too_small\": {\n * \"string\": \"Минимум {minimum} символов\",\n * \"number\": \"Минимум {minimum}\"\n * },\n * \"invalid_format\": {\n * \"email\": \"Некорректный email\"\n * }\n * }\n * }\n * ```\n */\nexport function createFormErrorMap(config: FormErrorMapConfig) {\n const { t, prefix = 'validation' } = config\n\n return (issue: ZodIssue): string | undefined => {\n const params = extractParams(issue)\n const origin = getIssueOrigin(issue)\n\n // Строим ключи для поиска перевода\n const baseKey = `${prefix}.${issue.code}`\n const originKey = origin ? `${baseKey}.${origin}` : null\n\n // Пробуем с origin, затем без\n let translated: string | undefined\n\n if (originKey) {\n translated = tryTranslate(t, originKey, params)\n }\n\n if (!translated) {\n translated = tryTranslate(t, baseKey, params)\n }\n\n // Возвращаем undefined чтобы Zod использовал дефолтное сообщение\n return translated\n }\n}\n\n/**\n * Список всех кодов ошибок Zod v4 для генерации ключей\n */\nexport const ZOD_ERROR_CODES = [\n 'invalid_type',\n 'too_small',\n 'too_big',\n 'invalid_format', // Zod v4: ранее invalid_string\n 'not_multiple_of',\n 'unrecognized_keys',\n 'invalid_value', // Zod v4: объединяет invalid_enum_value + invalid_literal\n 'invalid_union',\n 'invalid_key',\n 'invalid_element',\n 'custom',\n] as const\n\nexport type ZodErrorCode = (typeof ZOD_ERROR_CODES)[number]\n\n/**\n * Origins для ошибок too_small / too_big\n */\nexport const SIZE_ORIGINS = ['string', 'number', 'array', 'date', 'set', 'file'] as const\n\n/**\n * Formats для ошибок invalid_format (Zod v4, ранее invalid_string)\n */\nexport const STRING_FORMATS = [\n 'email',\n 'url',\n 'uuid',\n 'cuid',\n 'cuid2',\n 'ulid',\n 'regex',\n 'datetime',\n 'date',\n 'time',\n 'duration',\n 'ip',\n 'cidr',\n 'base64',\n 'base64url',\n 'json_string',\n 'e164',\n 'jwt',\n 'emoji',\n 'nanoid',\n 'guid',\n 'lowercase',\n 'uppercase',\n] as const\n","'use client'\n\nimport { createContext, useContext, useEffect, type ReactNode } from 'react'\nimport { z } from 'zod/v4'\nimport { createFormErrorMap } from './create-form-error-map'\n\n/**\n * Параметры для интерполяции в сообщениях об ошибках\n * @example { minimum: 5, maximum: 100 }\n */\nexport type TranslateParams = Record<string, string | number | boolean | undefined>\n\n/**\n * Функция перевода (совместима с next-intl useTranslations)\n * @param key - Ключ перевода в формате \"ModelName.fieldName.property\"\n * @param params - Опциональные параметры для интерполяции\n * @returns Переведённая строка или fallback\n */\nexport type TranslateFunction = (key: string, params?: TranslateParams) => string\n\n/**\n * Контекст для i18n в формах\n */\ninterface FormI18nContextValue {\n /** Функция перевода */\n t: TranslateFunction\n /** Текущая локаль */\n locale: string\n /** Включен ли i18n */\n enabled: boolean\n}\n\nconst FormI18nContext = createContext<FormI18nContextValue | null>(null)\n\n/**\n * Props для FormI18nProvider\n */\ninterface FormI18nProviderProps {\n /**\n * Функция перевода из next-intl или другой i18n библиотеки\n * @example useTranslations('formSchemas')\n */\n t: TranslateFunction\n /** Текущая локаль */\n locale: string\n children: ReactNode\n /**\n * Автоматически настроить глобальный Zod error map для i18n\n *\n * При включении, Zod ошибки будут переводиться через функцию t()\n * с ключами вида `validation.{code}.{origin?}`\n *\n * @example\n * ```json\n * {\n * \"validation\": {\n * \"too_small\": { \"string\": \"Минимум {minimum} символов\" },\n * \"invalid_string\": { \"email\": \"Некорректный email\" }\n * }\n * }\n * ```\n *\n * @default false\n */\n setupZodErrorMap?: boolean\n}\n\n/**\n * Провайдер i18n для форм\n *\n * Оборачивает формы и предоставляет доступ к переводам через useFormI18n.\n * При наличии провайдера, компоненты форм будут использовать переводы\n * по ключам из i18nKey в схемах.\n *\n * @example Базовое использование\n * ```tsx\n * import { FormI18nProvider } from '@lena/form-components'\n * import { useTranslations, useLocale } from 'next-intl'\n *\n * function App({ children }) {\n * const t = useTranslations('formSchemas')\n * const locale = useLocale()\n *\n * return (\n * <FormI18nProvider t={t} locale={locale}>\n * {children}\n * </FormI18nProvider>\n * )\n * }\n * ```\n *\n * @example С автоматической настройкой Zod error map\n * ```tsx\n * <FormI18nProvider t={t} locale={locale} setupZodErrorMap>\n * {children}\n * </FormI18nProvider>\n * ```\n */\nexport function FormI18nProvider({ t, locale, children, setupZodErrorMap = false }: FormI18nProviderProps) {\n // Настраиваем глобальный Zod error map при включённом флаге\n useEffect(() => {\n if (setupZodErrorMap) {\n const errorMap = createFormErrorMap({ t })\n // Type assertion: наш error map совместим с Zod v4 API,\n // но TypeScript не может вывести это автоматически\n z.config({ customError: errorMap as z.core.$ZodErrorMap<z.core.$ZodIssue> })\n }\n }, [setupZodErrorMap, t])\n\n return <FormI18nContext.Provider value={{ t, locale, enabled: true }}>{children}</FormI18nContext.Provider>\n}\n\n/**\n * Хук для доступа к i18n в формах\n *\n * Возвращает функцию перевода и информацию о локали.\n * Если FormI18nProvider не найден, возвращает null.\n *\n * @example\n * ```tsx\n * function MyField() {\n * const i18n = useFormI18n()\n *\n * if (i18n) {\n * const label = i18n.t('Product.name.title')\n * }\n * }\n * ```\n */\nexport function useFormI18n(): FormI18nContextValue | null {\n return useContext(FormI18nContext)\n}\n\n/**\n * Получить переведённое значение с fallback\n *\n * Пытается получить перевод по ключу. Если перевод пустой или\n * i18n не настроен, возвращает fallback значение.\n *\n * @param i18n - Контекст i18n (может быть null)\n * @param i18nKey - Базовый ключ (например, \"Product.name\")\n * @param property - Свойство (например, \"title\", \"placeholder\")\n * @param fallback - Значение по умолчанию\n * @returns Переведённая строка или fallback\n */\nexport function getLocalizedValue(\n i18n: FormI18nContextValue | null,\n i18nKey: string | undefined,\n property: 'title' | 'placeholder' | 'description' | 'label',\n fallback: string | undefined\n): string | undefined {\n if (!i18n || !i18nKey) {\n return fallback\n }\n\n try {\n const fullKey = `${i18nKey}.${property}`\n const translated = i18n.t(fullKey)\n\n // Если перевод пустой или равен ключу (next-intl возвращает ключ при отсутствии перевода)\n if (!translated || translated === fullKey) {\n return fallback\n }\n\n return translated\n } catch {\n // При ошибке возвращаем fallback\n return fallback\n }\n}\n","'use client'\n\nimport { useMemo } from 'react'\nimport { useFormI18n } from './form-i18n-provider'\n\n/**\n * Опция для select/radio/checkbox с поддержкой i18n\n */\nexport interface LocalizableOption {\n value: string | number\n label: string\n /** Опция отключена */\n disabled?: boolean\n /** Ключ i18n для перевода label (например, \"RecipeType.SWEET\") */\n i18nKey?: string\n}\n\n/**\n * Хук для локализации опций select/radio/checkbox\n *\n * Принимает массив опций с i18nKey и возвращает массив с переведёнными label.\n * Если i18n не настроен или перевод отсутствует, используется оригинальный label.\n *\n * @param options - Массив опций с возможными i18nKey\n * @returns Массив опций с переведёнными label\n *\n * @example\n * ```tsx\n * // Опции из Zod схемы\n * const rawOptions = [\n * { value: 'SWEET', label: 'Сладкое', i18nKey: 'RecipeType.SWEET' },\n * { value: 'SALTY', label: 'Солёное', i18nKey: 'RecipeType.SALTY' },\n * ]\n *\n * function MySelect() {\n * const options = useLocalizedOptions(rawOptions)\n * // options[0].label будет переведён если есть FormI18nProvider\n * }\n * ```\n */\nexport function useLocalizedOptions(\n options: LocalizableOption[] | undefined\n): { value: string | number; label: string; disabled?: boolean }[] {\n const i18n = useFormI18n()\n\n return useMemo(() => {\n if (!options) {\n return []\n }\n\n if (!i18n) {\n // i18n не настроен — возвращаем оригинальные label\n return options.map(({ value, label, disabled }) => ({ value, label, disabled }))\n }\n\n return options.map(({ value, label, disabled, i18nKey }) => {\n if (!i18nKey) {\n return { value, label, disabled }\n }\n\n try {\n // Ключ для enum option: \"EnumName.VALUE.label\"\n const fullKey = `${i18nKey}.label`\n const translated = i18n.t(fullKey)\n\n // Если перевод пустой или равен ключу — используем fallback\n if (!translated || translated === fullKey) {\n return { value, label, disabled }\n }\n\n return { value, label: translated, disabled }\n } catch {\n return { value, label, disabled }\n }\n })\n }, [options, i18n])\n}\n"]}
|