@dofe/sso-hooks 0.1.5

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.
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Hotkey definition
3
+ */
4
+ export interface HotkeyConfig {
5
+ /** Key combination (e.g., 'Space', 'Ctrl+S', 'Meta+Shift+P') */
6
+ key: string;
7
+ /** Callback when hotkey is pressed */
8
+ handler: (event: KeyboardEvent) => void;
9
+ /** Whether to prevent default behavior (default: true) */
10
+ preventDefault?: boolean;
11
+ /** Whether to stop propagation (default: false) */
12
+ stopPropagation?: boolean;
13
+ /** Only trigger when these elements are NOT focused */
14
+ ignoreInputs?: boolean;
15
+ /** Description for accessibility */
16
+ description?: string;
17
+ /** Whether the hotkey is enabled (default: true) */
18
+ enabled?: boolean;
19
+ }
20
+ /**
21
+ * Custom hook for keyboard shortcuts
22
+ *
23
+ * Features:
24
+ * - Support for key combinations (Ctrl, Alt, Shift, Meta)
25
+ * - Option to ignore when input elements are focused
26
+ * - Enable/disable hotkeys dynamically
27
+ * - Prevent default and stop propagation options
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * useHotkeys([
32
+ * { key: 'Space', handler: toggleRecording, description: 'Toggle recording' },
33
+ * { key: 'Escape', handler: cancelRecording, description: 'Cancel recording' },
34
+ * { key: 'Ctrl+S', handler: saveRecording, description: 'Save recording' },
35
+ * ]);
36
+ * ```
37
+ */
38
+ export declare function useHotkeys(hotkeys: HotkeyConfig[], deps?: React.DependencyList): void;
39
+ /**
40
+ * Hook for a single hotkey
41
+ */
42
+ export declare function useHotkey(key: string, handler: (event: KeyboardEvent) => void, options?: Omit<HotkeyConfig, 'key' | 'handler'>, deps?: React.DependencyList): void;
43
+ /**
44
+ * Recording-specific hotkeys preset
45
+ */
46
+ export interface RecordingHotkeysConfig {
47
+ onToggleRecording?: () => void;
48
+ onPauseResume?: () => void;
49
+ onStop?: () => void;
50
+ onCancel?: () => void;
51
+ enabled?: boolean;
52
+ }
53
+ /**
54
+ * Hook for recording-specific keyboard shortcuts
55
+ *
56
+ * Default shortcuts:
57
+ * - Space: Pause/Resume
58
+ * - Enter: Stop recording
59
+ * - Escape: Cancel recording
60
+ * - R: Start recording (when idle)
61
+ *
62
+ * @example
63
+ * ```tsx
64
+ * useRecordingHotkeys({
65
+ * onToggleRecording: handleToggle,
66
+ * onPauseResume: handlePauseResume,
67
+ * onStop: handleStop,
68
+ * onCancel: handleCancel,
69
+ * enabled: isRecordingActive,
70
+ * });
71
+ * ```
72
+ */
73
+ export declare function useRecordingHotkeys(config: RecordingHotkeysConfig): void;
74
+ export default useHotkeys;
75
+ //# sourceMappingURL=useHotkeys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHotkeys.d.ts","sourceRoot":"","sources":["../src/useHotkeys.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC,0DAA0D;IAC1D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mDAAmD;IACnD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uDAAuD;IACvD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAgDD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EAAE,EACvB,IAAI,GAAE,KAAK,CAAC,cAAmB,QAuDhC;AAED;;GAEG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,EACvC,OAAO,GAAE,IAAI,CAAC,YAAY,EAAE,KAAK,GAAG,SAAS,CAAM,EACnD,IAAI,GAAE,KAAK,CAAC,cAAmB,QAGhC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,sBAAsB,QA0DjE;AAED,eAAe,UAAU,CAAC"}
@@ -0,0 +1,172 @@
1
+ 'use client';
2
+ import { useEffect, useCallback, useRef } from 'react';
3
+ /**
4
+ * Parse key combination string into parts
5
+ */
6
+ function parseKey(key) {
7
+ const parts = key
8
+ .toLowerCase()
9
+ .split('+')
10
+ .map((p) => p.trim());
11
+ const mainKey = parts[parts.length - 1] ?? '';
12
+ return {
13
+ key: mainKey === 'space' ? ' ' : mainKey,
14
+ ctrl: parts.includes('ctrl') || parts.includes('control'),
15
+ alt: parts.includes('alt') || parts.includes('option'),
16
+ shift: parts.includes('shift'),
17
+ meta: parts.includes('meta') ||
18
+ parts.includes('cmd') ||
19
+ parts.includes('command'),
20
+ };
21
+ }
22
+ /**
23
+ * Check if an element is an input element
24
+ */
25
+ function isInputElement(element) {
26
+ if (!element)
27
+ return false;
28
+ const tagName = element.tagName.toLowerCase();
29
+ if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') {
30
+ return true;
31
+ }
32
+ if (element.getAttribute('contenteditable') === 'true') {
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+ /**
38
+ * Custom hook for keyboard shortcuts
39
+ *
40
+ * Features:
41
+ * - Support for key combinations (Ctrl, Alt, Shift, Meta)
42
+ * - Option to ignore when input elements are focused
43
+ * - Enable/disable hotkeys dynamically
44
+ * - Prevent default and stop propagation options
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * useHotkeys([
49
+ * { key: 'Space', handler: toggleRecording, description: 'Toggle recording' },
50
+ * { key: 'Escape', handler: cancelRecording, description: 'Cancel recording' },
51
+ * { key: 'Ctrl+S', handler: saveRecording, description: 'Save recording' },
52
+ * ]);
53
+ * ```
54
+ */
55
+ export function useHotkeys(hotkeys, deps = []) {
56
+ const hotkeysRef = useRef(hotkeys);
57
+ // Update ref when hotkeys change
58
+ useEffect(() => {
59
+ hotkeysRef.current = hotkeys;
60
+ }, [hotkeys]);
61
+ const handleKeyDown = useCallback((event) => {
62
+ const activeElement = document.activeElement;
63
+ for (const hotkey of hotkeysRef.current) {
64
+ // Skip if disabled
65
+ if (hotkey.enabled === false)
66
+ continue;
67
+ // Skip if focused on input and ignoreInputs is true
68
+ if (hotkey.ignoreInputs !== false && isInputElement(activeElement)) {
69
+ continue;
70
+ }
71
+ const parsed = parseKey(hotkey.key);
72
+ // Check if key matches
73
+ const keyMatches = event.key.toLowerCase() === parsed.key ||
74
+ event.code.toLowerCase() === parsed.key ||
75
+ event.code.toLowerCase() === `key${parsed.key}`;
76
+ // Check modifiers
77
+ const modifiersMatch = event.ctrlKey === parsed.ctrl &&
78
+ event.altKey === parsed.alt &&
79
+ event.shiftKey === parsed.shift &&
80
+ event.metaKey === parsed.meta;
81
+ if (keyMatches && modifiersMatch) {
82
+ if (hotkey.preventDefault !== false) {
83
+ event.preventDefault();
84
+ }
85
+ if (hotkey.stopPropagation) {
86
+ event.stopPropagation();
87
+ }
88
+ hotkey.handler(event);
89
+ return;
90
+ }
91
+ }
92
+ }, []);
93
+ useEffect(() => {
94
+ window.addEventListener('keydown', handleKeyDown);
95
+ return () => window.removeEventListener('keydown', handleKeyDown);
96
+ }, [handleKeyDown, ...deps]);
97
+ }
98
+ /**
99
+ * Hook for a single hotkey
100
+ */
101
+ export function useHotkey(key, handler, options = {}, deps = []) {
102
+ useHotkeys([{ key, handler, ...options }], deps);
103
+ }
104
+ /**
105
+ * Hook for recording-specific keyboard shortcuts
106
+ *
107
+ * Default shortcuts:
108
+ * - Space: Pause/Resume
109
+ * - Enter: Stop recording
110
+ * - Escape: Cancel recording
111
+ * - R: Start recording (when idle)
112
+ *
113
+ * @example
114
+ * ```tsx
115
+ * useRecordingHotkeys({
116
+ * onToggleRecording: handleToggle,
117
+ * onPauseResume: handlePauseResume,
118
+ * onStop: handleStop,
119
+ * onCancel: handleCancel,
120
+ * enabled: isRecordingActive,
121
+ * });
122
+ * ```
123
+ */
124
+ export function useRecordingHotkeys(config) {
125
+ const { onToggleRecording, onPauseResume, onStop, onCancel, enabled = true, } = config;
126
+ const hotkeys = [];
127
+ if (onPauseResume) {
128
+ hotkeys.push({
129
+ key: 'Space',
130
+ handler: onPauseResume,
131
+ description: 'Pause/Resume recording',
132
+ enabled,
133
+ ignoreInputs: true,
134
+ });
135
+ }
136
+ if (onStop) {
137
+ hotkeys.push({
138
+ key: 'Enter',
139
+ handler: onStop,
140
+ description: 'Stop recording',
141
+ enabled,
142
+ ignoreInputs: true,
143
+ });
144
+ }
145
+ if (onCancel) {
146
+ hotkeys.push({
147
+ key: 'Escape',
148
+ handler: onCancel,
149
+ description: 'Cancel recording',
150
+ enabled,
151
+ ignoreInputs: true,
152
+ });
153
+ }
154
+ if (onToggleRecording) {
155
+ hotkeys.push({
156
+ key: 'r',
157
+ handler: onToggleRecording,
158
+ description: 'Toggle recording',
159
+ enabled,
160
+ ignoreInputs: true,
161
+ });
162
+ }
163
+ useHotkeys(hotkeys, [
164
+ enabled,
165
+ onToggleRecording,
166
+ onPauseResume,
167
+ onStop,
168
+ onCancel,
169
+ ]);
170
+ }
171
+ export default useHotkeys;
172
+ //# sourceMappingURL=useHotkeys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHotkeys.js","sourceRoot":"","sources":["../src/useHotkeys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import { z } from 'zod';
2
+ type FieldKey = 'username' | 'email' | 'password' | 'confirmPassword' | 'currentPassword' | 'newPassword' | 'phone' | 'name' | 'title' | 'content' | 'description' | 'code' | 'roleName' | 'departmentName' | 'link';
3
+ /**
4
+ * 国际化表单验证 Hook
5
+ *
6
+ * 使用示例:
7
+ * ```tsx
8
+ * const v = useI18nValidation();
9
+ *
10
+ * // 使用预定义验证器
11
+ * const schema = z.object({
12
+ * email: v.email(),
13
+ * phone: v.phone(),
14
+ * password: v.password({ min: 6 }),
15
+ * username: v.required('username'),
16
+ * bio: v.string('description', { max: 200 }),
17
+ * });
18
+ *
19
+ * // 自定义验证消息
20
+ * const customSchema = z.object({
21
+ * title: z.string().min(1, { message: v.getMessage('required', { field: 'title' }) }),
22
+ * });
23
+ * ```
24
+ */
25
+ export declare function useI18nValidation(): {
26
+ getFieldName: (fieldKey: FieldKey | string) => string;
27
+ getMessage: (messageKey: string, params?: {
28
+ field?: FieldKey | string;
29
+ min?: number;
30
+ max?: number;
31
+ }) => string;
32
+ required: (fieldKey: FieldKey | string) => z.ZodString;
33
+ string: (fieldKey: FieldKey | string, options?: {
34
+ min?: number;
35
+ max?: number;
36
+ }) => z.ZodString;
37
+ email: () => z.ZodString;
38
+ phone: () => z.ZodString;
39
+ url: () => z.ZodString;
40
+ password: (options?: {
41
+ min?: number;
42
+ requireStrong?: boolean;
43
+ }) => z.ZodString;
44
+ number: (fieldKey: FieldKey | string, options?: {
45
+ min?: number;
46
+ max?: number;
47
+ }) => z.ZodNumber;
48
+ passwordMismatchMessage: string;
49
+ };
50
+ /**
51
+ * 创建带有密码确认的表单 schema 辅助函数
52
+ *
53
+ * 使用示例:
54
+ * ```tsx
55
+ * const v = useI18nValidation();
56
+ * const schema = createPasswordConfirmSchema(v, {
57
+ * passwordField: 'password',
58
+ * confirmField: 'confirmPassword',
59
+ * minLength: 8,
60
+ * });
61
+ * ```
62
+ */
63
+ export declare function createPasswordConfirmSchema(v: ReturnType<typeof useI18nValidation>, options?: {
64
+ passwordField?: string;
65
+ confirmField?: string;
66
+ minLength?: number;
67
+ requireStrong?: boolean;
68
+ }): z.ZodEffects<z.ZodObject<{
69
+ [x: string]: z.ZodString;
70
+ }, "strip", z.ZodTypeAny, {
71
+ [x: string]: string;
72
+ }, {
73
+ [x: string]: string;
74
+ }>, {
75
+ [x: string]: string;
76
+ }, {
77
+ [x: string]: string;
78
+ }>;
79
+ export {};
80
+ //# sourceMappingURL=useI18nValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useI18nValidation.d.ts","sourceRoot":"","sources":["../src/useI18nValidation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,KAAK,QAAQ,GACT,UAAU,GACV,OAAO,GACP,UAAU,GACV,iBAAiB,GACjB,iBAAiB,GACjB,aAAa,GACb,OAAO,GACP,MAAM,GACN,OAAO,GACP,SAAS,GACT,aAAa,GACb,MAAM,GACN,UAAU,GACV,gBAAgB,GAChB,MAAM,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB;6BAOlB,QAAQ,GAAG,MAAM,KAAG,MAAM;6BAgBvB,MAAM,WACT;QAAE,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,KACjE,MAAM;yBAmBE,QAAQ,GAAG,MAAM;uBAYjB,QAAQ,GAAG,MAAM,YAAY;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;;;;yBAqD3D;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE;uBAsBzC,QAAQ,GAAG,MAAM,YAAY;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;;EA6CzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,CAAC,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,EACvC,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACpB;;;;;;;;;;GAkBP"}
@@ -0,0 +1,186 @@
1
+ 'use client';
2
+ import { useCallback, useMemo } from 'react';
3
+ import { z } from 'zod';
4
+ import { useTranslations } from 'next-intl';
5
+ /**
6
+ * 国际化表单验证 Hook
7
+ *
8
+ * 使用示例:
9
+ * ```tsx
10
+ * const v = useI18nValidation();
11
+ *
12
+ * // 使用预定义验证器
13
+ * const schema = z.object({
14
+ * email: v.email(),
15
+ * phone: v.phone(),
16
+ * password: v.password({ min: 6 }),
17
+ * username: v.required('username'),
18
+ * bio: v.string('description', { max: 200 }),
19
+ * });
20
+ *
21
+ * // 自定义验证消息
22
+ * const customSchema = z.object({
23
+ * title: z.string().min(1, { message: v.getMessage('required', { field: 'title' }) }),
24
+ * });
25
+ * ```
26
+ */
27
+ export function useI18nValidation() {
28
+ const t = useTranslations('validation');
29
+ /**
30
+ * 获取字段显示名称
31
+ */
32
+ const getFieldName = useCallback((fieldKey) => {
33
+ try {
34
+ return t(`fields.${fieldKey}`);
35
+ }
36
+ catch {
37
+ // 如果没有翻译,返回原始 key
38
+ return fieldKey;
39
+ }
40
+ }, [t]);
41
+ /**
42
+ * 获取验证消息
43
+ */
44
+ const getMessage = useCallback((messageKey, params) => {
45
+ const fieldName = params?.field ? getFieldName(params.field) : '';
46
+ try {
47
+ return t(messageKey, {
48
+ field: fieldName,
49
+ min: params?.min ?? 0,
50
+ max: params?.max ?? 0,
51
+ });
52
+ }
53
+ catch {
54
+ return messageKey;
55
+ }
56
+ }, [t, getFieldName]);
57
+ /**
58
+ * 必填字符串
59
+ */
60
+ const required = useCallback((fieldKey) => {
61
+ return z
62
+ .string()
63
+ .min(1, { message: getMessage('required', { field: fieldKey }) });
64
+ }, [getMessage]);
65
+ /**
66
+ * 字符串(可选长度限制)
67
+ */
68
+ const string = useCallback((fieldKey, options) => {
69
+ let schema = z.string();
70
+ if (options?.min !== undefined) {
71
+ schema = schema.min(options.min, {
72
+ message: getMessage('minLength', {
73
+ field: fieldKey,
74
+ min: options.min,
75
+ }),
76
+ });
77
+ }
78
+ if (options?.max !== undefined) {
79
+ schema = schema.max(options.max, {
80
+ message: getMessage('maxLength', {
81
+ field: fieldKey,
82
+ max: options.max,
83
+ }),
84
+ });
85
+ }
86
+ return schema;
87
+ }, [getMessage]);
88
+ /**
89
+ * 邮箱验证
90
+ */
91
+ const email = useCallback(() => {
92
+ return z.string().email({ message: getMessage('invalidEmail') });
93
+ }, [getMessage]);
94
+ /**
95
+ * 手机号验证(中国大陆格式)
96
+ */
97
+ const phone = useCallback(() => {
98
+ return z
99
+ .string()
100
+ .regex(/^1[3-9]\d{9}$/, { message: getMessage('invalidPhone') });
101
+ }, [getMessage]);
102
+ /**
103
+ * URL 验证
104
+ */
105
+ const url = useCallback(() => {
106
+ return z.string().url({ message: getMessage('invalidUrl') });
107
+ }, [getMessage]);
108
+ /**
109
+ * 密码验证
110
+ */
111
+ const password = useCallback((options) => {
112
+ const minLength = options?.min ?? 6;
113
+ let schema = z.string().min(minLength, {
114
+ message: getMessage('passwordTooShort', { min: minLength }),
115
+ });
116
+ if (options?.requireStrong) {
117
+ // 要求包含字母和数字
118
+ schema = schema.regex(/^(?=.*[A-Za-z])(?=.*\d)/, {
119
+ message: getMessage('passwordTooWeak'),
120
+ });
121
+ }
122
+ return schema;
123
+ }, [getMessage]);
124
+ /**
125
+ * 数字验证(可选范围限制)
126
+ */
127
+ const number = useCallback((fieldKey, options) => {
128
+ let schema = z.number();
129
+ if (options?.min !== undefined) {
130
+ schema = schema.min(options.min, {
131
+ message: getMessage('min', { field: fieldKey, min: options.min }),
132
+ });
133
+ }
134
+ if (options?.max !== undefined) {
135
+ schema = schema.max(options.max, {
136
+ message: getMessage('max', { field: fieldKey, max: options.max }),
137
+ });
138
+ }
139
+ return schema;
140
+ }, [getMessage]);
141
+ /**
142
+ * 确认密码验证(需要在 refine 中使用)
143
+ */
144
+ const passwordMismatchMessage = useMemo(() => getMessage('passwordMismatch'), [getMessage]);
145
+ return {
146
+ // 获取方法
147
+ getFieldName,
148
+ getMessage,
149
+ // 验证器
150
+ required,
151
+ string,
152
+ email,
153
+ phone,
154
+ url,
155
+ password,
156
+ number,
157
+ // 预定义消息
158
+ passwordMismatchMessage,
159
+ };
160
+ }
161
+ /**
162
+ * 创建带有密码确认的表单 schema 辅助函数
163
+ *
164
+ * 使用示例:
165
+ * ```tsx
166
+ * const v = useI18nValidation();
167
+ * const schema = createPasswordConfirmSchema(v, {
168
+ * passwordField: 'password',
169
+ * confirmField: 'confirmPassword',
170
+ * minLength: 8,
171
+ * });
172
+ * ```
173
+ */
174
+ export function createPasswordConfirmSchema(v, options = {}) {
175
+ const { passwordField = 'password', confirmField = 'confirmPassword', minLength = 6, requireStrong = false, } = options;
176
+ return z
177
+ .object({
178
+ [passwordField]: v.password({ min: minLength, requireStrong }),
179
+ [confirmField]: v.required('confirmPassword'),
180
+ })
181
+ .refine((data) => data[passwordField] === data[confirmField], {
182
+ message: v.passwordMismatchMessage,
183
+ path: [confirmField],
184
+ });
185
+ }
186
+ //# sourceMappingURL=useI18nValidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useI18nValidation.js","sourceRoot":"","sources":["../src/useI18nValidation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ interface ToastOptions {
2
+ duration?: number;
3
+ position?: 'top-left' | 'top-right' | 'top-center' | 'bottom-left' | 'bottom-right' | 'bottom-center';
4
+ }
5
+ /**
6
+ * 统一的操作反馈 Hook
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { success, error, warning, info } = useOperationFeedback();
11
+ *
12
+ * success('操作成功');
13
+ * error('操作失败');
14
+ * ```
15
+ */
16
+ export declare function useOperationFeedback(): {
17
+ success: (message: string, options?: ToastOptions) => void;
18
+ error: (message: string, options?: ToastOptions) => void;
19
+ warning: (message: string, options?: ToastOptions) => void;
20
+ info: (message: string, options?: ToastOptions) => void;
21
+ loading: (message: string, options?: ToastOptions) => string | number;
22
+ promise: <T>(promise: Promise<T>, messages: {
23
+ loading: string;
24
+ success: string | ((data: T) => string);
25
+ error: string | ((error: unknown) => string);
26
+ }, options?: ToastOptions) => (string & {
27
+ unwrap: () => Promise<T>;
28
+ }) | (number & {
29
+ unwrap: () => Promise<T>;
30
+ }) | {
31
+ unwrap: () => Promise<T>;
32
+ };
33
+ };
34
+ export {};
35
+ //# sourceMappingURL=useOperationFeedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOperationFeedback.d.ts","sourceRoot":"","sources":["../src/useOperationFeedback.ts"],"names":[],"mappings":"AAWA,UAAU,YAAY;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EACL,UAAU,GACV,WAAW,GACX,YAAY,GACZ,aAAa,GACb,cAAc,GACd,eAAe,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB;uBAQI,MAAM,YAAY,YAAY;qBAQhC,MAAM,YAAY,YAAY;uBAQ5B,MAAM,YAAY,YAAY;oBAQjC,MAAM,YAAY,YAAY;uBAQ3B,MAAM,YAAY,YAAY;cAQjE,CAAC,WACS,OAAO,CAAC,CAAC,CAAC,YACT;QACR,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC;QACxC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC;KAC9C,YACS,YAAY;;;;;;;EAqB3B"}
@@ -0,0 +1,78 @@
1
+ 'use client';
2
+ /**
3
+ * useOperationFeedback - 统一的操作反馈 Hook
4
+ * 提供一致的成功/错误/警告提示
5
+ */
6
+ import { useCallback } from 'react';
7
+ import { toast } from 'sonner';
8
+ import { useTranslations } from 'next-intl';
9
+ /**
10
+ * 统一的操作反馈 Hook
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const { success, error, warning, info } = useOperationFeedback();
15
+ *
16
+ * success('操作成功');
17
+ * error('操作失败');
18
+ * ```
19
+ */
20
+ export function useOperationFeedback() {
21
+ const t = useTranslations('common');
22
+ const defaultOptions = {
23
+ duration: 3000,
24
+ position: 'top-right',
25
+ };
26
+ const success = useCallback((message, options) => {
27
+ toast.success(message, {
28
+ ...defaultOptions,
29
+ ...options,
30
+ duration: options?.duration ?? 2000, // 成功提示稍短
31
+ });
32
+ }, []);
33
+ const error = useCallback((message, options) => {
34
+ toast.error(message, {
35
+ ...defaultOptions,
36
+ ...options,
37
+ duration: options?.duration ?? 4000, // 错误提示稍长
38
+ });
39
+ }, []);
40
+ const warning = useCallback((message, options) => {
41
+ toast.warning(message, {
42
+ ...defaultOptions,
43
+ ...options,
44
+ duration: options?.duration ?? 3000,
45
+ });
46
+ }, []);
47
+ const info = useCallback((message, options) => {
48
+ toast.info(message, {
49
+ ...defaultOptions,
50
+ ...options,
51
+ duration: options?.duration ?? 3000,
52
+ });
53
+ }, []);
54
+ const loading = useCallback((message, options) => {
55
+ return toast.loading(message, {
56
+ ...defaultOptions,
57
+ ...options,
58
+ });
59
+ }, []);
60
+ const promise = useCallback((promise, messages, options) => {
61
+ return toast.promise(promise, {
62
+ loading: messages.loading,
63
+ success: messages.success,
64
+ error: messages.error,
65
+ ...defaultOptions,
66
+ ...options,
67
+ });
68
+ }, []);
69
+ return {
70
+ success,
71
+ error,
72
+ warning,
73
+ info,
74
+ loading,
75
+ promise,
76
+ };
77
+ }
78
+ //# sourceMappingURL=useOperationFeedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOperationFeedback.js","sourceRoot":"","sources":["../src/useOperationFeedback.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAa5C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAEpC,MAAM,cAAc,GAAiB;QACnC,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,WAAW;KACtB,CAAC;IAEF,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,OAAe,EAAE,OAAsB,EAAE,EAAE;QACtE,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE;YACrB,GAAG,cAAc;YACjB,GAAG,OAAO;YACV,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,SAAS;SAC/C,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,OAAe,EAAE,OAAsB,EAAE,EAAE;QACpE,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE;YACnB,GAAG,cAAc;YACjB,GAAG,OAAO;YACV,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,SAAS;SAC/C,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,OAAe,EAAE,OAAsB,EAAE,EAAE;QACtE,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE;YACrB,GAAG,cAAc;YACjB,GAAG,OAAO;YACV,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,IAAI;SACpC,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,OAAe,EAAE,OAAsB,EAAE,EAAE;QACnE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE;YAClB,GAAG,cAAc;YACjB,GAAG,OAAO;YACV,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,IAAI;SACpC,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,OAAe,EAAE,OAAsB,EAAE,EAAE;QACtE,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE;YAC5B,GAAG,cAAc;YACjB,GAAG,OAAO;SACX,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,WAAW,CACzB,CACE,OAAmB,EACnB,QAIC,EACD,OAAsB,EACtB,EAAE;QACF,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE;YAC5B,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,GAAG,cAAc;YACjB,GAAG,OAAO;SACX,CAAC,CAAC;IACL,CAAC,EACD,EAAE,CACH,CAAC;IAEF,OAAO;QACL,OAAO;QACP,KAAK;QACL,OAAO;QACP,IAAI;QACJ,OAAO;QACP,OAAO;KACR,CAAC;AACJ,CAAC"}