@cozeloop/components 0.0.3 → 0.0.4

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.
Files changed (165) hide show
  1. package/package.json +9 -3
  2. package/.eslintcache +0 -1
  3. package/.rush/temp/shrinkwrap-deps.json +0 -770
  4. package/OWNERS +0 -5
  5. package/config/rush-project.json +0 -8
  6. package/eslint.config.js +0 -7
  7. package/rslib.config.js +0 -7
  8. package/script/publish.js +0 -146
  9. package/src/base-search-select/base-search-form-select.tsx +0 -10
  10. package/src/base-search-select/base-search-select.tsx +0 -200
  11. package/src/base-search-select/index.module.less +0 -16
  12. package/src/base-search-select/index.tsx +0 -3
  13. package/src/base-search-select/types.ts +0 -16
  14. package/src/base-search-select/utils.ts +0 -78
  15. package/src/basic-card/index.tsx +0 -23
  16. package/src/card-pane/index.module.less +0 -14
  17. package/src/card-pane/index.tsx +0 -25
  18. package/src/chip-select/index.module.less +0 -17
  19. package/src/chip-select/index.tsx +0 -7
  20. package/src/code-editor/index.tsx +0 -9
  21. package/src/code-usage/code-item.module.less +0 -32
  22. package/src/code-usage/index.tsx +0 -91
  23. package/src/codemirror-editor/code-editor.tsx +0 -139
  24. package/src/codemirror-editor/index.ts +0 -4
  25. package/src/codemirror-editor/json-editor.tsx +0 -183
  26. package/src/codemirror-editor/raw-text-editor.tsx +0 -68
  27. package/src/codemirror-editor/text-editor.tsx +0 -58
  28. package/src/codemirror-editor/themes/coze-dark.ts +0 -116
  29. package/src/codemirror-editor/themes/coze-light.ts +0 -122
  30. package/src/collapse-card/index.module.less +0 -27
  31. package/src/collapse-card/index.tsx +0 -93
  32. package/src/collapsible-card/index.module.less +0 -63
  33. package/src/collapsible-card/index.tsx +0 -57
  34. package/src/column-manage-storage/index.tsx +0 -64
  35. package/src/columns-select/index.tsx +0 -244
  36. package/src/edit-icon-button/index.tsx +0 -36
  37. package/src/footer-actions/index.tsx +0 -33
  38. package/src/hooks/use-infinite-scroll.ts +0 -183
  39. package/src/hooks/use-mouse-down-offset.ts +0 -50
  40. package/src/hooks/use-unsave-leave-warning.ts +0 -49
  41. package/src/id-render/icon-button-container.tsx +0 -37
  42. package/src/id-render/index.tsx +0 -64
  43. package/src/index-controller/record-navigation.tsx +0 -57
  44. package/src/index-controller/use-item-index-controller.ts +0 -197
  45. package/src/index.ts +0 -208
  46. package/src/infinite-scroll-table/index.tsx +0 -99
  47. package/src/info-tooltip/index.tsx +0 -41
  48. package/src/input-components/radio-button.tsx +0 -63
  49. package/src/input-slider/index.module.less +0 -30
  50. package/src/input-slider/index.tsx +0 -161
  51. package/src/input-with-count/index.tsx +0 -31
  52. package/src/jump-button/jump-icon-button.tsx +0 -12
  53. package/src/large-txt-render/index.tsx +0 -46
  54. package/src/layout/content.tsx +0 -28
  55. package/src/layout/header.tsx +0 -15
  56. package/src/layout/index.module.less +0 -28
  57. package/src/layout/index.tsx +0 -9
  58. package/src/layout/tabs.tsx +0 -11
  59. package/src/lazy-load-component/index.tsx +0 -55
  60. package/src/logic-editor/index.ts +0 -3
  61. package/src/logic-editor/logic-editor.module.less +0 -13
  62. package/src/logic-editor/logic-editor.tsx +0 -200
  63. package/src/logic-editor/logic-left-render.tsx +0 -100
  64. package/src/logic-editor/logic-operator-render.tsx +0 -54
  65. package/src/logic-editor/logic-right-render.tsx +0 -51
  66. package/src/logic-editor/logic-types.tsx +0 -238
  67. package/src/logic-editor/utils.ts +0 -22
  68. package/src/logic-expr/assets/select.svg +0 -1
  69. package/src/logic-expr/consts.ts +0 -6
  70. package/src/logic-expr/expr-group-render.tsx +0 -238
  71. package/src/logic-expr/expr-render.tsx +0 -226
  72. package/src/logic-expr/index.module.less +0 -252
  73. package/src/logic-expr/index.ts +0 -13
  74. package/src/logic-expr/logic-expr.tsx +0 -261
  75. package/src/logic-expr/logic-not.tsx +0 -46
  76. package/src/logic-expr/logic-toggle.tsx +0 -96
  77. package/src/logic-expr/types.ts +0 -95
  78. package/src/loop-radio-group/index.tsx +0 -16
  79. package/src/multi-part-editor/components/image-item-renderer.tsx +0 -134
  80. package/src/multi-part-editor/components/index.module.less +0 -21
  81. package/src/multi-part-editor/components/multipart-item-renderer.tsx +0 -74
  82. package/src/multi-part-editor/components/url-input-modal.tsx +0 -317
  83. package/src/multi-part-editor/components/video-item-renderer.tsx +0 -145
  84. package/src/multi-part-editor/index.module.less +0 -8
  85. package/src/multi-part-editor/index.tsx +0 -571
  86. package/src/multi-part-editor/multi-part-render.tsx +0 -87
  87. package/src/multi-part-editor/type.tsx +0 -103
  88. package/src/multi-part-editor/upload-button.tsx +0 -256
  89. package/src/multi-part-editor/utils.ts +0 -64
  90. package/src/open-detail-button/index.tsx +0 -30
  91. package/src/page-content/index.tsx +0 -99
  92. package/src/primary-page/index.tsx +0 -1
  93. package/src/primary-page/primary-header.tsx +0 -64
  94. package/src/primary-title/index.module.less +0 -14
  95. package/src/primary-title/index.tsx +0 -18
  96. package/src/provider/index.tsx +0 -89
  97. package/src/resizable-side-sheet/index.tsx +0 -69
  98. package/src/resize-sidesheet/index.module.less +0 -14
  99. package/src/resize-sidesheet/index.tsx +0 -68
  100. package/src/resize-sidesheet/use-drag.ts +0 -43
  101. package/src/schema-editor/index.tsx +0 -52
  102. package/src/search-form/index.tsx +0 -134
  103. package/src/semi-schema-form/components/tmpls/array-field-item.tsx +0 -97
  104. package/src/semi-schema-form/components/tmpls/array-field.tsx +0 -127
  105. package/src/semi-schema-form/components/tmpls/base-input.tsx +0 -126
  106. package/src/semi-schema-form/components/tmpls/description-field.tsx +0 -23
  107. package/src/semi-schema-form/components/tmpls/error-list.tsx +0 -44
  108. package/src/semi-schema-form/components/tmpls/field-error.tsx +0 -33
  109. package/src/semi-schema-form/components/tmpls/field.tsx +0 -54
  110. package/src/semi-schema-form/components/tmpls/icon-button.tsx +0 -112
  111. package/src/semi-schema-form/components/tmpls/index.ts +0 -39
  112. package/src/semi-schema-form/components/tmpls/object-field.tsx +0 -173
  113. package/src/semi-schema-form/components/tmpls/submit.tsx +0 -31
  114. package/src/semi-schema-form/components/tmpls/title-field.tsx +0 -30
  115. package/src/semi-schema-form/components/widgets/checkbox.tsx +0 -67
  116. package/src/semi-schema-form/components/widgets/checkboxs.tsx +0 -100
  117. package/src/semi-schema-form/components/widgets/index.ts +0 -17
  118. package/src/semi-schema-form/components/widgets/radio.tsx +0 -105
  119. package/src/semi-schema-form/components/widgets/range.tsx +0 -73
  120. package/src/semi-schema-form/components/widgets/select.tsx +0 -108
  121. package/src/semi-schema-form/components/widgets/textarea.tsx +0 -63
  122. package/src/semi-schema-form/index.tsx +0 -14
  123. package/src/sentinel-form/enum.ts +0 -16
  124. package/src/sentinel-form/index.tsx +0 -382
  125. package/src/step-nav/index.module.less +0 -45
  126. package/src/step-nav/index.tsx +0 -53
  127. package/src/table/index.module.less +0 -144
  128. package/src/table/index.tsx +0 -18
  129. package/src/table/sort-icon.tsx +0 -73
  130. package/src/table/table-with-pagination.tsx +0 -150
  131. package/src/table/table-without-pagniation.tsx +0 -66
  132. package/src/table-batch-operate/table-batch-operation.tsx +0 -47
  133. package/src/table-batch-operate/use-batch-operate.ts +0 -111
  134. package/src/table-col-actions/index.module.less +0 -8
  135. package/src/table-col-actions/index.tsx +0 -149
  136. package/src/table-cols-config/index.module.less +0 -34
  137. package/src/table-cols-config/index.tsx +0 -171
  138. package/src/table-cols-config/type.ts +0 -12
  139. package/src/table-cols-config/use-hidden-col-keys.ts +0 -53
  140. package/src/table-cols-config/util.ts +0 -56
  141. package/src/table-empty/index.tsx +0 -23
  142. package/src/table-header/index.module.less +0 -7
  143. package/src/table-header/index.tsx +0 -70
  144. package/src/tabs/index.module.less +0 -48
  145. package/src/tabs/index.tsx +0 -9
  146. package/src/text-area-pro/index.module.less +0 -5
  147. package/src/text-area-pro/index.tsx +0 -49
  148. package/src/text-with-copy/index.tsx +0 -95
  149. package/src/title-with-sub/index.tsx +0 -27
  150. package/src/tooltip-when-disabled/index.tsx +0 -23
  151. package/src/tooltip-with-disabled/index.tsx +0 -17
  152. package/src/types.d.ts +0 -24
  153. package/src/upload/index.ts +0 -39
  154. package/src/user-profile/index.tsx +0 -49
  155. package/src/utils/basic.ts +0 -29
  156. package/src/version-list/index.module.less +0 -16
  157. package/src/version-list/version-descriptions.tsx +0 -80
  158. package/src/version-list/version-item.tsx +0 -30
  159. package/src/version-list/version-list.tsx +0 -59
  160. package/src/version-list/version-switch-panel.tsx +0 -31
  161. package/tailwind.config.ts +0 -6
  162. package/tsconfig.build.json +0 -44
  163. package/tsconfig.json +0 -17
  164. package/tsconfig.misc.json +0 -28
  165. package/vitest.config.mts +0 -7
@@ -1,33 +0,0 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
2
- import { Button, type ButtonProps } from '@coze-arch/coze-design';
3
-
4
- import { useI18n } from '../provider';
5
-
6
- interface Props {
7
- confirmBtnProps?: ButtonProps & {
8
- 'data-btm'?: string;
9
- 'data-btm-title'?: string;
10
- text?: string;
11
- };
12
- cancelBtnProps?: ButtonProps & {
13
- 'data-btm'?: string;
14
- 'data-btm-title'?: string;
15
- text?: string;
16
- };
17
- }
18
- export function FooterActions({
19
- confirmBtnProps: { text: confirmBtnText, ...confirmBtnProps } = {},
20
- cancelBtnProps: { text: cancelBtnText, ...cancelBtnProps } = {},
21
- }: Props) {
22
- const I18n = useI18n();
23
- return (
24
- <div className="flex justify-end">
25
- <Button color="primary" {...cancelBtnProps}>
26
- {cancelBtnText ?? I18n.t('global_btn_cancel')}
27
- </Button>
28
- <Button className="ml-2" {...confirmBtnProps}>
29
- {confirmBtnText ?? I18n.t('global_btn_confirm')}
30
- </Button>
31
- </div>
32
- );
33
- }
@@ -1,183 +0,0 @@
1
- /* eslint-disable @coze-arch/max-line-per-function */
2
- /* eslint-disable max-lines-per-function */
3
- import { useMemo, useState } from 'react';
4
-
5
- import { getTargetElement } from 'ahooks/lib/utils/domTarget';
6
- import {
7
- type InfiniteScrollOptions,
8
- type Data,
9
- type Service,
10
- } from 'ahooks/lib/useInfiniteScroll/types';
11
- import {
12
- useEventListener,
13
- useMemoizedFn,
14
- useRequest,
15
- useUpdateEffect,
16
- } from 'ahooks';
17
- import {
18
- getClientHeight,
19
- getScrollHeight,
20
- getScrollTop,
21
- } from '@cozeloop/toolkit';
22
-
23
- /**
24
- * ahook的实现,在刷新列表时会出现以下两个问题
25
- * 1. 发送存量数据对应的列表请求
26
- * 2. 列表请求重复
27
- * 因此fork ahook 实现,并将reload含义定义为刷新列表,并回到第一页
28
- * @param service
29
- * @param options
30
- * @returns
31
- */
32
- export const useInfiniteScroll = <TData extends Data>(
33
- service: Service<TData>,
34
- options: InfiniteScrollOptions<TData> = {},
35
- ) => {
36
- const {
37
- target,
38
- isNoMore,
39
- threshold = 100,
40
- reloadDeps = [],
41
- manual,
42
- onBefore,
43
- onSuccess,
44
- onError,
45
- onFinally,
46
- } = options;
47
-
48
- const [finalData, setFinalData] = useState<TData>();
49
- const [loadingMore, setLoadingMore] = useState(false);
50
-
51
- const noMore = useMemo(() => {
52
- if (!isNoMore) {
53
- return false;
54
- }
55
- return isNoMore(finalData);
56
- }, [finalData]);
57
-
58
- const { loading, error, run, runAsync, cancel } = useRequest(
59
- async (lastData?: TData) => {
60
- const currentData = await service(lastData);
61
- if (!lastData) {
62
- setFinalData({
63
- ...currentData,
64
- list: [...(currentData.list ?? [])],
65
- });
66
- } else {
67
- setFinalData({
68
- ...currentData,
69
- list: [...(lastData.list ?? []), ...currentData.list],
70
- });
71
- }
72
- return currentData;
73
- },
74
- {
75
- manual,
76
- onFinally: (_, d, e) => {
77
- setLoadingMore(false);
78
- onFinally?.(d, e);
79
- },
80
- onBefore: () => onBefore?.(),
81
- onSuccess: d => {
82
- // setTimeout(() => {
83
- // scrollMethod();
84
- // });
85
- onSuccess?.(d);
86
- setTimeout(() => {
87
- checkFirstScreen();
88
- });
89
- },
90
- onError,
91
- },
92
- );
93
-
94
- const loadMore = useMemoizedFn(() => {
95
- if (noMore) {
96
- return;
97
- }
98
- setLoadingMore(true);
99
- run(finalData);
100
- });
101
-
102
- const loadMoreAsync = useMemoizedFn(() => {
103
- if (noMore) {
104
- // eslint-disable-next-line prefer-promise-reject-errors
105
- return Promise.reject();
106
- }
107
- setLoadingMore(true);
108
- return runAsync(finalData);
109
- });
110
-
111
- const reload = () => {
112
- setLoadingMore(false);
113
- setFinalData(undefined);
114
- return run();
115
- };
116
-
117
- const reloadAsync = () => {
118
- setLoadingMore(false);
119
- return runAsync();
120
- };
121
-
122
- const checkFirstScreen = () => {
123
- let el = getTargetElement(target);
124
- if (!el) {
125
- return;
126
- }
127
-
128
- el = el === document ? document.documentElement : el;
129
- const scrollHeight = getScrollHeight(el);
130
- const clientHeight = getClientHeight(el);
131
- if (scrollHeight <= clientHeight) {
132
- // 首屏没满,自动 loadMore
133
- loadMore();
134
- }
135
- };
136
-
137
- const scrollMethod = () => {
138
- let el = getTargetElement(target);
139
- if (!el) {
140
- return;
141
- }
142
-
143
- el = el === document ? document.documentElement : el;
144
-
145
- const scrollTop = getScrollTop(el);
146
- const scrollHeight = getScrollHeight(el);
147
- const clientHeight = getClientHeight(el);
148
-
149
- if (scrollHeight - scrollTop <= clientHeight + threshold) {
150
- loadMore();
151
- }
152
- };
153
-
154
- useEventListener(
155
- 'scroll',
156
- () => {
157
- if (loading || loadingMore) {
158
- return;
159
- }
160
- scrollMethod();
161
- },
162
- { target },
163
- );
164
-
165
- useUpdateEffect(() => {
166
- setFinalData(undefined);
167
- run();
168
- }, [...reloadDeps]);
169
-
170
- return {
171
- data: finalData,
172
- loading: !loadingMore && loading,
173
- error,
174
- loadingMore,
175
- noMore,
176
- loadMore,
177
- loadMoreAsync,
178
- reload: useMemoizedFn(reload),
179
- reloadAsync: useMemoizedFn(reloadAsync),
180
- mutate: setFinalData,
181
- cancel,
182
- };
183
- };
@@ -1,50 +0,0 @@
1
- import { useState, useRef, useCallback } from 'react';
2
-
3
- import { useLatest, useMemoizedFn } from 'ahooks';
4
-
5
- interface Offset {
6
- offsetX: number;
7
- offsetY: number;
8
- }
9
-
10
- export const useMouseDownOffset = (callback: (offset: Offset) => void) => {
11
- const [isActive, setIsActive] = useState(false);
12
- const targetRef = useRef<HTMLElement | null>(null);
13
- const [startPosition, setStartPosition] = useState<Offset | null>(null);
14
- const callbackRef = useLatest(callback);
15
-
16
- const ref = useCallback((node: HTMLElement | null) => {
17
- if (targetRef.current) {
18
- targetRef.current.removeEventListener('mousedown', handleMouseDown);
19
- }
20
- targetRef.current = node; // 更新 ref
21
- if (targetRef.current) {
22
- targetRef.current.addEventListener('mousedown', handleMouseDown);
23
- }
24
- }, []);
25
-
26
- const handleMouseDown = useMemoizedFn((event: MouseEvent) => {
27
- setIsActive(true);
28
- setStartPosition({ offsetX: event.clientX, offsetY: event.clientY });
29
- window.addEventListener('mousemove', handleMouseMove);
30
- window.addEventListener('mouseup', handleMouseUp);
31
- });
32
-
33
- const handleMouseMove = useMemoizedFn((event: MouseEvent) => {
34
- if (startPosition) {
35
- callbackRef.current({
36
- offsetX: event.clientX - startPosition.offsetX,
37
- offsetY: event.clientY - startPosition.offsetY,
38
- });
39
- }
40
- });
41
-
42
- const handleMouseUp = useMemoizedFn(() => {
43
- setIsActive(false);
44
- setStartPosition(null);
45
- window.removeEventListener('mousemove', handleMouseMove);
46
- window.removeEventListener('mouseup', handleMouseUp);
47
- });
48
-
49
- return { isActive, ref };
50
- };
@@ -1,49 +0,0 @@
1
- import { useBlocker } from 'react-router-dom';
2
- import { useEffect } from 'react';
3
-
4
- import { useI18n } from '../provider';
5
-
6
- interface UnsaveLeaveWarningProps {
7
- /** 是否阻塞 */
8
- block: boolean;
9
- /** 自定义提示消息*/
10
- message?: string;
11
- }
12
-
13
- /**
14
- * 离开页面时的警告
15
- * 触发时机:关闭浏览器、关闭浏览器标签页、刷新页面、导航回退离开等
16
- */
17
- export const useUnsaveLeaveWarning = ({
18
- block,
19
- message,
20
- }: UnsaveLeaveWarningProps) => {
21
- const i18n = useI18n();
22
- const warnMessage =
23
- message || i18n.t('unsave_leave_confirm_warning') || 'Close confirm';
24
-
25
- useEffect(() => {
26
- const handleBeforeUnload = (event: BeforeUnloadEvent) => {
27
- if (block) {
28
- event.preventDefault();
29
- event.returnValue = warnMessage; // 显示自定义消息(部分浏览器可能不支持)
30
- }
31
- };
32
-
33
- // 监听浏览器关闭标签页或刷新事件
34
- window.addEventListener('beforeunload', handleBeforeUnload);
35
-
36
- return () => {
37
- window.removeEventListener('beforeunload', handleBeforeUnload);
38
- };
39
- }, [block, warnMessage]);
40
-
41
- // 处理 React Router 导航离开
42
- useBlocker(() => {
43
- if (block) {
44
- // 弹出确认框
45
- return !window.confirm(warnMessage);
46
- }
47
- return true;
48
- });
49
- };
@@ -1,37 +0,0 @@
1
- import { type DOMAttributes } from 'react';
2
-
3
- import cls from 'classnames';
4
-
5
- export function IconButtonContainer({
6
- icon,
7
- className,
8
- style,
9
- onClick,
10
- active,
11
- disabled,
12
- ...rest
13
- }: {
14
- icon: React.ReactNode;
15
- className?: string;
16
- style?: React.CSSProperties;
17
- disabled?: boolean;
18
- active?: boolean;
19
- } & DOMAttributes<HTMLDivElement>) {
20
- return (
21
- <div
22
- {...rest}
23
- onClick={onClick}
24
- style={style}
25
- className={cls(
26
- 'inline-flex items-center justify-center shrink-0 w-5 h-5 rounded-[4px] text-sm text-[var(--coz-fg-secondary)] ',
27
- active && !disabled ? 'bg-[var(--coz-mg-plus)]' : '',
28
- disabled
29
- ? 'cursor-not-allowed'
30
- : 'cursor-pointer hover:text-[var(--coz-fg-primary)] hover:bg-[var(--coz-mg-plus)]',
31
- className,
32
- )}
33
- >
34
- {icon}
35
- </div>
36
- );
37
- }
@@ -1,64 +0,0 @@
1
- import classNames from 'classnames';
2
- import { IconCozCopy } from '@coze-arch/coze-design/icons';
3
- import { Toast, Tooltip } from '@coze-arch/coze-design';
4
-
5
- import { IconButtonContainer } from './icon-button-container';
6
-
7
- export function IDRender({
8
- id,
9
- showSuffixLength = 5,
10
- enableCopy = true,
11
- useTag = false,
12
- defaultShowCopyBtn,
13
- }: {
14
- id: Int64;
15
- showSuffixLength?: number;
16
- enableCopy?: boolean;
17
- useTag?: boolean;
18
- defaultShowCopyBtn?: boolean;
19
- }) {
20
- const idString = id?.toString() ?? '';
21
- const suffix = idString.slice(
22
- Math.max(idString.length - showSuffixLength, 0),
23
- idString.length,
24
- );
25
- return (
26
- <div
27
- className="group flex items-center gap-1"
28
- onClick={e => e.stopPropagation()}
29
- >
30
- <Tooltip content={idString} theme="dark">
31
- {useTag ? (
32
- <div className="shrink-0 h-5 flex items-center px-2 rounded-[3px] border border-solid border-[var(--coz-stroke-plus)] font-medium text-[var(--coz-fg-primary)]">
33
- #{suffix || '-'}
34
- </div>
35
- ) : (
36
- <span className="shrink-0">#{suffix || '-'}</span>
37
- )}
38
- </Tooltip>
39
- {enableCopy ? (
40
- <Tooltip content="复制 ID" theme="dark">
41
- <div>
42
- <IconButtonContainer
43
- className={classNames(
44
- 'id-render-copy-action-button shrink-0 text-sm',
45
- defaultShowCopyBtn ? '' : ' hidden group-hover:flex',
46
- )}
47
- icon={<IconCozCopy />}
48
- onClick={async e => {
49
- e.stopPropagation();
50
- try {
51
- await navigator.clipboard.writeText(idString);
52
- Toast.success('复制成功');
53
- } catch (error) {
54
- console.error(error);
55
- Toast.error('复制失败');
56
- }
57
- }}
58
- />
59
- </div>
60
- </Tooltip>
61
- ) : null}
62
- </div>
63
- );
64
- }
@@ -1,57 +0,0 @@
1
- import {
2
- IconCozArrowLeft,
3
- IconCozArrowRight,
4
- } from '@coze-arch/coze-design/icons';
5
- import { Space, Button, Tooltip } from '@coze-arch/coze-design';
6
-
7
- import { type IndexControllerStore } from './use-item-index-controller';
8
-
9
- /** 控制上一个下一个记录的控制器组件 */
10
- export function IndexControllerView({
11
- indexControllerStore,
12
- className,
13
- }: {
14
- indexControllerStore: IndexControllerStore;
15
- className?: string;
16
- }) {
17
- const {
18
- hasPrevious,
19
- hasNext,
20
- currentIndex,
21
- total,
22
- loading,
23
- goToPrevious,
24
- goToNext,
25
- } = indexControllerStore ?? {};
26
- return (
27
- <Space className={className} spacing={4}>
28
- <Tooltip content="上一条" theme="dark">
29
- <div className="flex items-center">
30
- <Button
31
- icon={<IconCozArrowLeft />}
32
- size="mini"
33
- color="primary"
34
- style={{ height: 20 }}
35
- disabled={!hasPrevious || loading}
36
- onClick={goToPrevious}
37
- />
38
- </div>
39
- </Tooltip>
40
- <span className="text-sm text-gray-500 min-w-[64px] text-center">
41
- {currentIndex + 1} / {total}
42
- </span>
43
- <Tooltip content="下一条" theme="dark">
44
- <div className="flex items-center">
45
- <Button
46
- icon={<IconCozArrowRight />}
47
- size="mini"
48
- color="primary"
49
- style={{ height: 20 }}
50
- disabled={!hasNext || loading}
51
- onClick={goToNext}
52
- />
53
- </div>
54
- </Tooltip>
55
- </Space>
56
- );
57
- }
@@ -1,197 +0,0 @@
1
- /* eslint-disable @coze-arch/no-destructuring-use-request */
2
- import { useState, useCallback, useEffect, useRef } from 'react';
3
-
4
- import { useLatest, useRequest } from 'ahooks';
5
-
6
- type FetchRecordItemsFn<RecordItem> = (params: {
7
- pageSize: number;
8
- pageIndex: number;
9
- }) => Promise<{ list: RecordItem[]; total: number }>;
10
-
11
- /**
12
- * 索引控制器参数
13
- */
14
- export interface IndexControllerOptions<RecordItem = Record<string, unknown>> {
15
- /** 默认选中的记录索引 */
16
- defaultIndex: number;
17
- /** 刷新记录列表 */
18
- fetchRecordItems: FetchRecordItemsFn<RecordItem>;
19
- /** 批量获取记录数据的个数 */
20
- batchSize?: number;
21
- /** 获取记录详情 */
22
- fetchRecordItem: (record: RecordItem) => Promise<RecordItem | undefined>;
23
- /** 切换记录的回调 */
24
- onRecordChange?: (record: RecordItem) => void;
25
- }
26
-
27
- /**
28
- * 索引控制器状态&API
29
- */
30
- export interface IndexControllerStore {
31
- /** 是否有上一条 */
32
- hasPrevious: boolean;
33
- /** 是否有下一条 */
34
- hasNext: boolean;
35
- /** 当前记录在列表中的位置信息 */
36
- currentIndex: number;
37
- /** 总记录数 */
38
- total: number;
39
- /** 切换到上一条 */
40
- goToPrevious: () => void;
41
- /** 切换到下一条 */
42
- goToNext: () => void;
43
- /** 加载状态 */
44
- loading: boolean;
45
- }
46
-
47
- async function batchFetchRecordItems<RecordItem>({
48
- currentIndex = 0,
49
- batchSize,
50
- total,
51
- fetchRecordItems,
52
- }: {
53
- currentIndex: number;
54
- total?: number;
55
- batchSize: number;
56
- fetchRecordItems: FetchRecordItemsFn<RecordItem>;
57
- }) {
58
- const pageIndex = Math.floor(currentIndex / batchSize) + 1;
59
- // index所在区间的前一个区间
60
- const prevPromise =
61
- pageIndex > 1
62
- ? fetchRecordItems({
63
- pageSize: batchSize,
64
- pageIndex: pageIndex - 1,
65
- })
66
- : Promise.resolve({ list: [], total: 0 });
67
-
68
- const nextPromise =
69
- typeof total !== 'number' || currentIndex + batchSize < total
70
- ? fetchRecordItems({
71
- pageSize: batchSize,
72
- pageIndex: pageIndex + 1,
73
- })
74
- : Promise.resolve({ list: [], total: 0 });
75
- const res = await Promise.all([
76
- // index所在区间的前一个区间
77
- prevPromise,
78
- // index所在区间
79
- fetchRecordItems({
80
- pageSize: batchSize,
81
- pageIndex,
82
- }),
83
- // index所在区间的后一个区间
84
- nextPromise,
85
- ]);
86
- const allList = res.flatMap(item => item.list || []);
87
- // 计算当前index所在区间的前一个区间的起始index
88
- const prevStartIndex = Math.max(
89
- 0,
90
- currentIndex - (currentIndex % batchSize) - batchSize,
91
- );
92
- const allItems: (RecordItem | undefined)[] = [];
93
- const list = allList || [];
94
- for (let i = 0; i < list.length; i++) {
95
- allItems[prevStartIndex + i] = list[i];
96
- }
97
- return { list: allItems, total: res?.[1]?.total || 0 };
98
- }
99
-
100
- /**
101
- * 索引控制器
102
- * 用于在详情抽屉中实现上一条、下一条功能
103
- */
104
- export function useItemIndexController<RecordItem = Record<string, unknown>>({
105
- defaultIndex,
106
- onRecordChange,
107
- fetchRecordItem,
108
- fetchRecordItems,
109
- batchSize = 50,
110
- }: IndexControllerOptions<RecordItem>): IndexControllerStore {
111
- const [allRecords, setAllRecords] = useState<(RecordItem | undefined)[]>([]);
112
- const [currentIndex, setCurrentIndex] = useState(defaultIndex);
113
- const [total, setTotalCount] = useState(0);
114
- const totalRef = useLatest(total);
115
- // 上一次触发请求的index
116
- const lastCurrentIndexRef = useRef<number>(-1);
117
-
118
- // 列表请求,根据 currentIndex 所在位置的前后各自获取batchSize个数量
119
- const { loading: listLoading, run: updateList } = useRequest(async () => {
120
- const res = await batchFetchRecordItems({
121
- currentIndex,
122
- total: total || undefined,
123
- batchSize,
124
- fetchRecordItems,
125
- });
126
- setTotalCount(res.total);
127
- setAllRecords(res.list);
128
- lastCurrentIndexRef.current = currentIndex;
129
- });
130
-
131
- // 详情请求
132
- const { runAsync: fetchRecordItemAsync, loading: itemLoading } = useRequest(
133
- async (record: RecordItem) => {
134
- const res = await fetchRecordItem(record);
135
- return res;
136
- },
137
- { manual: true },
138
- );
139
-
140
- // 切换到上一条记录
141
- const goToPrevious = useCallback(async () => {
142
- try {
143
- const record = allRecords[currentIndex - 1];
144
- if (currentIndex > 0 && record) {
145
- const previousRecord = await fetchRecordItemAsync(record);
146
- if (previousRecord) {
147
- onRecordChange?.(previousRecord);
148
- setCurrentIndex(currentIndex - 1);
149
- }
150
- }
151
- } catch (e) {
152
- console.warn(e);
153
- }
154
- }, [currentIndex, fetchRecordItemAsync, allRecords, onRecordChange]);
155
-
156
- // 切换到下一条记录
157
- const goToNext = useCallback(async () => {
158
- try {
159
- const record = allRecords[currentIndex + 1];
160
- if (currentIndex < totalRef.current - 1 && record) {
161
- const nextRecord = await fetchRecordItemAsync(record);
162
- if (nextRecord) {
163
- onRecordChange?.(nextRecord);
164
- setCurrentIndex(currentIndex + 1);
165
- }
166
- }
167
- } catch (e) {
168
- console.warn(e);
169
- }
170
- }, [
171
- currentIndex,
172
- totalRef,
173
- fetchRecordItemAsync,
174
- allRecords,
175
- onRecordChange,
176
- ]);
177
-
178
- useEffect(() => {
179
- // fetchRecordItems 会根据 currentIndex 是否超过上次触发请求的list范围来判断是否需要触发请求
180
- if (
181
- lastCurrentIndexRef.current !== -1 &&
182
- Math.abs(currentIndex - lastCurrentIndexRef.current) >= batchSize
183
- ) {
184
- updateList();
185
- }
186
- }, [currentIndex]);
187
-
188
- return {
189
- hasPrevious: currentIndex > 0,
190
- hasNext: currentIndex < total - 1,
191
- currentIndex,
192
- total,
193
- goToPrevious,
194
- goToNext,
195
- loading: listLoading || itemLoading,
196
- };
197
- }