@rc-component/listy 1.1.0
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/LICENSE +20 -0
- package/README.md +110 -0
- package/assets/index.css +36 -0
- package/assets/index.less +44 -0
- package/es/GroupHeader.d.ts +15 -0
- package/es/GroupHeader.js +32 -0
- package/es/List.d.ts +49 -0
- package/es/List.js +33 -0
- package/es/RawList/index.d.ts +5 -0
- package/es/RawList/index.js +90 -0
- package/es/RawList/useRawListScroll.d.ts +3 -0
- package/es/RawList/useRawListScroll.js +71 -0
- package/es/VirtualList/index.d.ts +5 -0
- package/es/VirtualList/index.js +151 -0
- package/es/VirtualList/useFlattenRows.d.ts +21 -0
- package/es/VirtualList/useFlattenRows.js +62 -0
- package/es/VirtualList/useStickyGroupHeader.d.ts +14 -0
- package/es/VirtualList/useStickyGroupHeader.js +82 -0
- package/es/hooks/useGroupSegments.d.ts +16 -0
- package/es/hooks/useGroupSegments.js +39 -0
- package/es/index.d.ts +3 -0
- package/es/index.js +2 -0
- package/lib/GroupHeader.d.ts +15 -0
- package/lib/GroupHeader.js +40 -0
- package/lib/List.d.ts +49 -0
- package/lib/List.js +41 -0
- package/lib/RawList/index.d.ts +5 -0
- package/lib/RawList/index.js +98 -0
- package/lib/RawList/useRawListScroll.d.ts +3 -0
- package/lib/RawList/useRawListScroll.js +79 -0
- package/lib/VirtualList/index.d.ts +5 -0
- package/lib/VirtualList/index.js +159 -0
- package/lib/VirtualList/useFlattenRows.d.ts +21 -0
- package/lib/VirtualList/useFlattenRows.js +69 -0
- package/lib/VirtualList/useStickyGroupHeader.d.ts +14 -0
- package/lib/VirtualList/useStickyGroupHeader.js +90 -0
- package/lib/hooks/useGroupSegments.d.ts +16 -0
- package/lib/hooks/useGroupSegments.js +46 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +9 -0
- package/package.json +76 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { Group, GroupSegmentItem } from '../hooks/useGroupSegments';
|
|
3
|
+
export type Row<T, K extends React.Key = React.Key> = {
|
|
4
|
+
type: 'header';
|
|
5
|
+
groupKey: K;
|
|
6
|
+
} | {
|
|
7
|
+
type: 'item';
|
|
8
|
+
item: T;
|
|
9
|
+
index: number;
|
|
10
|
+
};
|
|
11
|
+
export interface FlattenRowsResult<T, K extends React.Key = React.Key> {
|
|
12
|
+
rows: Row<T, K>[];
|
|
13
|
+
groupKeys: K[];
|
|
14
|
+
groupKeyToItems: Map<K, T[]>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Flatten grouped data into header and item rows.
|
|
18
|
+
* When grouping is enabled, items follow the insertion order of the group map
|
|
19
|
+
* while preserving their original indexes.
|
|
20
|
+
*/
|
|
21
|
+
export default function useFlattenRows<T, K extends React.Key = React.Key>(data: T[], groupData: Map<K, GroupSegmentItem<T>[]>, group?: Group<T, K>): FlattenRowsResult<T, K>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
// ============================== Types ===============================
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Flatten grouped data into header and item rows.
|
|
7
|
+
* When grouping is enabled, items follow the insertion order of the group map
|
|
8
|
+
* while preserving their original indexes.
|
|
9
|
+
*/
|
|
10
|
+
export default function useFlattenRows(data, groupData, group) {
|
|
11
|
+
return React.useMemo(() => {
|
|
12
|
+
// ============================== Init ================================
|
|
13
|
+
const flatRows = [];
|
|
14
|
+
const groupKeys = [];
|
|
15
|
+
const groupKeyToItems = new Map();
|
|
16
|
+
|
|
17
|
+
// ============================ No Group ==============================
|
|
18
|
+
if (!group) {
|
|
19
|
+
data.forEach((item, index) => {
|
|
20
|
+
flatRows.push({
|
|
21
|
+
type: 'item',
|
|
22
|
+
item,
|
|
23
|
+
index
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
rows: flatRows,
|
|
28
|
+
groupKeys,
|
|
29
|
+
groupKeyToItems
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ============================= Flatten ==============================
|
|
34
|
+
groupData.forEach((groupItems, groupKey) => {
|
|
35
|
+
groupKeyToItems.set(groupKey, groupItems.map(({
|
|
36
|
+
item
|
|
37
|
+
}) => item));
|
|
38
|
+
groupKeys.push(groupKey);
|
|
39
|
+
flatRows.push({
|
|
40
|
+
type: 'header',
|
|
41
|
+
groupKey
|
|
42
|
+
});
|
|
43
|
+
groupItems.forEach(({
|
|
44
|
+
item,
|
|
45
|
+
index
|
|
46
|
+
}) => {
|
|
47
|
+
flatRows.push({
|
|
48
|
+
type: 'item',
|
|
49
|
+
item,
|
|
50
|
+
index
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// ============================== Return ==============================
|
|
56
|
+
return {
|
|
57
|
+
rows: flatRows,
|
|
58
|
+
groupKeys,
|
|
59
|
+
groupKeyToItems
|
|
60
|
+
};
|
|
61
|
+
}, [data, group, groupData]);
|
|
62
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ListProps as VirtualListProps, ListRef as RcVirtualListRef } from '@rc-component/virtual-list';
|
|
3
|
+
import type { Group } from '../hooks/useGroupSegments';
|
|
4
|
+
type ExtraRenderInfo = Parameters<NonNullable<VirtualListProps<unknown>['extraRender']>>[0];
|
|
5
|
+
export interface StickyHeaderParams<T, K extends React.Key = React.Key> {
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
group: Group<T, K> | undefined;
|
|
8
|
+
groupKeys: K[];
|
|
9
|
+
groupKeyToItems: Map<K, T[]>;
|
|
10
|
+
prefixCls: string;
|
|
11
|
+
listRef: React.RefObject<RcVirtualListRef | null>;
|
|
12
|
+
}
|
|
13
|
+
export default function useStickyGroupHeader<T, K extends React.Key = React.Key>(params: StickyHeaderParams<T, K>): (info: ExtraRenderInfo) => React.JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import Portal from '@rc-component/portal';
|
|
3
|
+
import GroupHeader from "../GroupHeader";
|
|
4
|
+
|
|
5
|
+
// ============================== Types ===============================
|
|
6
|
+
|
|
7
|
+
// ============================== Utils ===============================
|
|
8
|
+
const HEADER_TOP_TOLERANCE = 1;
|
|
9
|
+
function findActiveHeaderIndex(groupKeys, getHeaderTop, scrollTop) {
|
|
10
|
+
let left = 0;
|
|
11
|
+
let right = groupKeys.length - 1;
|
|
12
|
+
let activeIndex = 0;
|
|
13
|
+
while (left <= right) {
|
|
14
|
+
const mid = Math.floor((left + right) / 2);
|
|
15
|
+
if (getHeaderTop(groupKeys[mid]) <= scrollTop + HEADER_TOP_TOLERANCE) {
|
|
16
|
+
activeIndex = mid;
|
|
17
|
+
left = mid + 1;
|
|
18
|
+
} else {
|
|
19
|
+
right = mid - 1;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return activeIndex;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ============================== Params ==============================
|
|
26
|
+
|
|
27
|
+
export default function useStickyGroupHeader(params) {
|
|
28
|
+
// ============================== Props ==============================
|
|
29
|
+
const {
|
|
30
|
+
enabled,
|
|
31
|
+
group,
|
|
32
|
+
groupKeys,
|
|
33
|
+
groupKeyToItems,
|
|
34
|
+
prefixCls,
|
|
35
|
+
listRef
|
|
36
|
+
} = params;
|
|
37
|
+
|
|
38
|
+
// ============================ Extra Render ==========================
|
|
39
|
+
const extraRender = React.useCallback(info => {
|
|
40
|
+
const {
|
|
41
|
+
getSize,
|
|
42
|
+
scrollTop,
|
|
43
|
+
virtual
|
|
44
|
+
} = info;
|
|
45
|
+
if (!enabled || !group || !groupKeys.length || !virtual) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const container = listRef.current?.nativeElement;
|
|
49
|
+
if (!container) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// The sticky header is the group whose section the viewport top sits in.
|
|
54
|
+
const activeHeaderIdx = findActiveHeaderIndex(groupKeys, groupKey => getSize(groupKey).top, scrollTop);
|
|
55
|
+
const currGroupKey = groupKeys[activeHeaderIdx];
|
|
56
|
+
const groupItems = groupKeyToItems.get(currGroupKey) || [];
|
|
57
|
+
const currentSize = getSize(currGroupKey);
|
|
58
|
+
const headerHeight = currentSize.bottom - currentSize.top;
|
|
59
|
+
const nextGroupKey = groupKeys[activeHeaderIdx + 1];
|
|
60
|
+
const top = nextGroupKey ? Math.min(0, getSize(nextGroupKey).top - headerHeight - scrollTop) : 0;
|
|
61
|
+
|
|
62
|
+
// Render a cloned header pinned over the virtual list.
|
|
63
|
+
return /*#__PURE__*/React.createElement(Portal, {
|
|
64
|
+
open: true,
|
|
65
|
+
getContainer: () => container
|
|
66
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
67
|
+
className: `${prefixCls}-group-header-holder`
|
|
68
|
+
}, /*#__PURE__*/React.createElement(GroupHeader, {
|
|
69
|
+
fixed: true,
|
|
70
|
+
group: group,
|
|
71
|
+
groupKey: currGroupKey,
|
|
72
|
+
groupItems: groupItems,
|
|
73
|
+
prefixCls: prefixCls,
|
|
74
|
+
style: {
|
|
75
|
+
top
|
|
76
|
+
}
|
|
77
|
+
})));
|
|
78
|
+
}, [enabled, group, groupKeys, groupKeyToItems, prefixCls, listRef]);
|
|
79
|
+
|
|
80
|
+
// ============================== Return ==============================
|
|
81
|
+
return extraRender;
|
|
82
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface Group<T, K extends React.Key = React.Key> {
|
|
3
|
+
key: (item: T) => K;
|
|
4
|
+
title: (groupKey: K, items: T[]) => React.ReactNode;
|
|
5
|
+
}
|
|
6
|
+
export interface GroupSegmentItem<T> {
|
|
7
|
+
item: T;
|
|
8
|
+
index: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Build a lookup map from group key to all matching data items and their
|
|
12
|
+
* original indexes.
|
|
13
|
+
* This groups by key across the full data set and does not require items with
|
|
14
|
+
* the same key to be contiguous.
|
|
15
|
+
*/
|
|
16
|
+
export default function useGroupSegments<T, K extends React.Key = React.Key>(data: T[], group?: Group<T, K>): Map<K, GroupSegmentItem<T>[]>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
// ============================== Types ===============================
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Build a lookup map from group key to all matching data items and their
|
|
7
|
+
* original indexes.
|
|
8
|
+
* This groups by key across the full data set and does not require items with
|
|
9
|
+
* the same key to be contiguous.
|
|
10
|
+
*/
|
|
11
|
+
export default function useGroupSegments(data, group) {
|
|
12
|
+
return React.useMemo(() => {
|
|
13
|
+
// ============================== Init ================================
|
|
14
|
+
const map = new Map();
|
|
15
|
+
|
|
16
|
+
// ============================ No Group ==============================
|
|
17
|
+
if (!group) {
|
|
18
|
+
return map;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ============================= Collect ==============================
|
|
22
|
+
data.forEach((item, index) => {
|
|
23
|
+
const groupKey = group.key(item);
|
|
24
|
+
const groupItems = map.get(groupKey);
|
|
25
|
+
const groupSegmentItem = {
|
|
26
|
+
item,
|
|
27
|
+
index
|
|
28
|
+
};
|
|
29
|
+
if (groupItems) {
|
|
30
|
+
groupItems.push(groupSegmentItem);
|
|
31
|
+
} else {
|
|
32
|
+
map.set(groupKey, [groupSegmentItem]);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// ============================== Return ==============================
|
|
37
|
+
return map;
|
|
38
|
+
}, [data, group]);
|
|
39
|
+
}
|
package/es/index.d.ts
ADDED
package/es/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { Group } from './hooks/useGroupSegments';
|
|
3
|
+
export interface GroupHeaderProps<T, K extends React.Key = React.Key> {
|
|
4
|
+
group: Group<T, K>;
|
|
5
|
+
groupKey: K;
|
|
6
|
+
groupItems: T[];
|
|
7
|
+
prefixCls: string;
|
|
8
|
+
fixed?: boolean;
|
|
9
|
+
sticky?: boolean;
|
|
10
|
+
style?: React.CSSProperties;
|
|
11
|
+
}
|
|
12
|
+
declare const GroupHeaderWithRef: <T, K extends React.Key = React.Key>(props: GroupHeaderProps<T, K> & {
|
|
13
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
14
|
+
}) => React.ReactElement;
|
|
15
|
+
export default GroupHeaderWithRef;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var React = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _clsx = _interopRequireDefault(require("clsx"));
|
|
9
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
11
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
12
|
+
// ============================== Types ===============================
|
|
13
|
+
|
|
14
|
+
function GroupHeader(props, ref) {
|
|
15
|
+
// ============================== Props ==============================
|
|
16
|
+
const {
|
|
17
|
+
group,
|
|
18
|
+
groupKey,
|
|
19
|
+
groupItems,
|
|
20
|
+
prefixCls,
|
|
21
|
+
fixed,
|
|
22
|
+
sticky,
|
|
23
|
+
style
|
|
24
|
+
} = props;
|
|
25
|
+
|
|
26
|
+
// ============================= Classes =============================
|
|
27
|
+
const className = (0, _clsx.default)(`${prefixCls}-group-header`, {
|
|
28
|
+
[`${prefixCls}-group-header-sticky`]: sticky,
|
|
29
|
+
[`${prefixCls}-group-header-fixed`]: fixed
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ============================== Render ==============================
|
|
33
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
34
|
+
ref: ref,
|
|
35
|
+
className: className,
|
|
36
|
+
style: style
|
|
37
|
+
}, group.title(groupKey, groupItems));
|
|
38
|
+
}
|
|
39
|
+
const GroupHeaderWithRef = /*#__PURE__*/React.forwardRef(GroupHeader);
|
|
40
|
+
var _default = exports.default = GroupHeaderWithRef;
|
package/lib/List.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { Group } from './hooks/useGroupSegments';
|
|
3
|
+
export type RowKey<T> = keyof T | ((item: T) => React.Key);
|
|
4
|
+
export type ScrollAlign = 'top' | 'bottom' | 'auto';
|
|
5
|
+
export interface GroupScrollToConfig {
|
|
6
|
+
groupKey: React.Key;
|
|
7
|
+
align?: ScrollAlign;
|
|
8
|
+
offset?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface KeyScrollToConfig {
|
|
11
|
+
key: React.Key;
|
|
12
|
+
align?: ScrollAlign;
|
|
13
|
+
offset?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface PositionScrollToConfig {
|
|
16
|
+
left?: number;
|
|
17
|
+
top?: number;
|
|
18
|
+
}
|
|
19
|
+
export type ListyScrollToConfig = number | null | KeyScrollToConfig | PositionScrollToConfig | GroupScrollToConfig;
|
|
20
|
+
export interface ListyRef {
|
|
21
|
+
scrollTo: (config?: ListyScrollToConfig) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface ListyProps<T, K extends React.Key = React.Key> {
|
|
24
|
+
items?: T[];
|
|
25
|
+
sticky?: boolean;
|
|
26
|
+
itemHeight?: number;
|
|
27
|
+
height?: number;
|
|
28
|
+
group?: Group<T, K>;
|
|
29
|
+
virtual?: boolean;
|
|
30
|
+
prefixCls?: string;
|
|
31
|
+
rowKey: RowKey<T>;
|
|
32
|
+
onScroll?: React.UIEventHandler<HTMLElement>;
|
|
33
|
+
itemRender: (item: T, index: number) => React.ReactNode;
|
|
34
|
+
}
|
|
35
|
+
export interface ListComponentProps<T, K extends React.Key = React.Key> {
|
|
36
|
+
data: T[];
|
|
37
|
+
sticky?: boolean;
|
|
38
|
+
itemHeight?: number;
|
|
39
|
+
height?: number;
|
|
40
|
+
group?: Group<T, K>;
|
|
41
|
+
prefixCls: string;
|
|
42
|
+
rowKey: RowKey<T>;
|
|
43
|
+
onScroll?: React.UIEventHandler<HTMLElement>;
|
|
44
|
+
itemRender: (item: T, index: number) => React.ReactNode;
|
|
45
|
+
}
|
|
46
|
+
declare const ListyWithForwardRef: <T, K extends React.Key = React.Key>(props: ListyProps<T, K> & {
|
|
47
|
+
ref?: React.Ref<ListyRef>;
|
|
48
|
+
}) => React.ReactElement;
|
|
49
|
+
export default ListyWithForwardRef;
|
package/lib/List.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var React = _react;
|
|
9
|
+
var _RawList = _interopRequireDefault(require("./RawList"));
|
|
10
|
+
var _VirtualList = _interopRequireDefault(require("./VirtualList"));
|
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
13
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
14
|
+
// ============================== Types ===============================
|
|
15
|
+
|
|
16
|
+
function Listy(props, ref) {
|
|
17
|
+
// ============================== Props ==============================
|
|
18
|
+
const {
|
|
19
|
+
items,
|
|
20
|
+
virtual = true,
|
|
21
|
+
prefixCls = 'rc-listy',
|
|
22
|
+
...restProps
|
|
23
|
+
} = props;
|
|
24
|
+
|
|
25
|
+
// =============================== Data ===============================
|
|
26
|
+
const data = React.useMemo(() => items || [], [items]);
|
|
27
|
+
|
|
28
|
+
// ============================== Render ===============================
|
|
29
|
+
const sharedListProps = {
|
|
30
|
+
...restProps,
|
|
31
|
+
data,
|
|
32
|
+
prefixCls,
|
|
33
|
+
ref
|
|
34
|
+
};
|
|
35
|
+
const listNode = virtual ? /*#__PURE__*/React.createElement(_VirtualList.default, sharedListProps) : /*#__PURE__*/React.createElement(_RawList.default, sharedListProps);
|
|
36
|
+
return listNode;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Const to support generic with forwardRef
|
|
40
|
+
const ListyWithForwardRef = /*#__PURE__*/(0, _react.forwardRef)(Listy);
|
|
41
|
+
var _default = exports.default = ListyWithForwardRef;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var React = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _util = require("@rc-component/util");
|
|
9
|
+
var _GroupHeader = _interopRequireDefault(require("../GroupHeader"));
|
|
10
|
+
var _useGroupSegments = _interopRequireDefault(require("../hooks/useGroupSegments"));
|
|
11
|
+
var _useRawListScroll = _interopRequireDefault(require("./useRawListScroll"));
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
14
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
15
|
+
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
16
|
+
// ============================== Types ===============================
|
|
17
|
+
|
|
18
|
+
function RawList(props, ref) {
|
|
19
|
+
// ============================== Props ==============================
|
|
20
|
+
const {
|
|
21
|
+
data,
|
|
22
|
+
group,
|
|
23
|
+
height,
|
|
24
|
+
itemRender,
|
|
25
|
+
onScroll,
|
|
26
|
+
prefixCls,
|
|
27
|
+
rowKey,
|
|
28
|
+
sticky
|
|
29
|
+
} = props;
|
|
30
|
+
|
|
31
|
+
// =============================== Refs ===============================
|
|
32
|
+
const holderRef = (0, _useRawListScroll.default)(ref, prefixCls, !!(sticky && group));
|
|
33
|
+
|
|
34
|
+
// =============================== Data ===============================
|
|
35
|
+
const groupData = (0, _useGroupSegments.default)(data, group);
|
|
36
|
+
|
|
37
|
+
// ============================== Utils ===============================
|
|
38
|
+
const getItemKey = (0, _util.useEvent)(item => {
|
|
39
|
+
if (typeof rowKey === 'function') {
|
|
40
|
+
return rowKey(item);
|
|
41
|
+
}
|
|
42
|
+
return item[rowKey];
|
|
43
|
+
});
|
|
44
|
+
const getScrollTargetProps = React.useCallback(key => ({
|
|
45
|
+
'data-key': String(key)
|
|
46
|
+
}), []);
|
|
47
|
+
|
|
48
|
+
// ============================ Render Item ===========================
|
|
49
|
+
const renderItem = React.useCallback((item, index, groupKey) => {
|
|
50
|
+
const key = getItemKey(item);
|
|
51
|
+
const scrollTargetProps = getScrollTargetProps(key);
|
|
52
|
+
return /*#__PURE__*/React.createElement("div", _extends({
|
|
53
|
+
key: key,
|
|
54
|
+
className: `${prefixCls}-item`,
|
|
55
|
+
style: sticky && groupKey !== undefined ? {
|
|
56
|
+
scrollMarginTop: `var(--${prefixCls}-item-scroll-margin-top, 0px)`
|
|
57
|
+
} : undefined
|
|
58
|
+
}, scrollTargetProps), itemRender(item, index));
|
|
59
|
+
}, [getItemKey, getScrollTargetProps, itemRender, prefixCls, sticky]);
|
|
60
|
+
|
|
61
|
+
// ============================= Content ==============================
|
|
62
|
+
const rawContent = group ? Array.from(groupData, ([groupKey, groupItems]) => {
|
|
63
|
+
const currentGroupItems = groupItems.map(({
|
|
64
|
+
item
|
|
65
|
+
}) => item);
|
|
66
|
+
return /*#__PURE__*/React.createElement("div", _extends({
|
|
67
|
+
key: groupKey,
|
|
68
|
+
className: `${prefixCls}-group-section`
|
|
69
|
+
}, getScrollTargetProps(groupKey)), /*#__PURE__*/React.createElement(_GroupHeader.default, {
|
|
70
|
+
group: group,
|
|
71
|
+
groupKey: groupKey,
|
|
72
|
+
groupItems: currentGroupItems,
|
|
73
|
+
prefixCls: prefixCls,
|
|
74
|
+
sticky: sticky
|
|
75
|
+
}), groupItems.map(({
|
|
76
|
+
item,
|
|
77
|
+
index
|
|
78
|
+
}) => {
|
|
79
|
+
return renderItem(item, index, groupKey);
|
|
80
|
+
}));
|
|
81
|
+
}) : data.map((item, index) => {
|
|
82
|
+
return renderItem(item, index);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ============================== Render ==============================
|
|
86
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
87
|
+
ref: holderRef,
|
|
88
|
+
className: prefixCls,
|
|
89
|
+
style: {
|
|
90
|
+
maxHeight: height,
|
|
91
|
+
overflowY: height === undefined ? undefined : 'auto',
|
|
92
|
+
overflowAnchor: 'none'
|
|
93
|
+
},
|
|
94
|
+
onScroll: onScroll
|
|
95
|
+
}, rawContent);
|
|
96
|
+
}
|
|
97
|
+
const RawListWithRef = /*#__PURE__*/React.forwardRef(RawList);
|
|
98
|
+
var _default = exports.default = RawListWithRef;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = useRawListScroll;
|
|
7
|
+
var React = _interopRequireWildcard(require("react"));
|
|
8
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
9
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
10
|
+
function useRawListScroll(ref, prefixCls, stickyGroup) {
|
|
11
|
+
// =============================== Refs ===============================
|
|
12
|
+
const holderRef = React.useRef(null);
|
|
13
|
+
|
|
14
|
+
// ============================== Utils ===============================
|
|
15
|
+
const getStickyHeaderHeight = React.useCallback(targetElement => {
|
|
16
|
+
if (!stickyGroup) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
const groupSection = targetElement.closest(`.${CSS.escape(`${prefixCls}-group-section`)}`);
|
|
20
|
+
const groupHeader = groupSection?.querySelector(`.${CSS.escape(`${prefixCls}-group-header`)}`);
|
|
21
|
+
if (!groupHeader) {
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
const rect = groupHeader.getBoundingClientRect();
|
|
25
|
+
const height = rect.height || rect.bottom - rect.top || groupHeader.offsetHeight;
|
|
26
|
+
return Number.isFinite(height) ? height : 0;
|
|
27
|
+
}, [prefixCls, stickyGroup]);
|
|
28
|
+
const setTargetScrollMargin = React.useCallback((targetElement, align) => {
|
|
29
|
+
const marginTop = align === 'top' ? getStickyHeaderHeight(targetElement) : 0;
|
|
30
|
+
targetElement.style.setProperty(`--${prefixCls}-item-scroll-margin-top`, `${marginTop}px`);
|
|
31
|
+
}, [getStickyHeaderHeight, prefixCls]);
|
|
32
|
+
|
|
33
|
+
// ============================== Scroll ==============================
|
|
34
|
+
const scrollTo = React.useCallback(config => {
|
|
35
|
+
const holder = holderRef.current;
|
|
36
|
+
if (!holder || config == null) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (typeof config === 'number') {
|
|
40
|
+
holder.scrollTop = config;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if ('key' in config || 'groupKey' in config) {
|
|
44
|
+
const {
|
|
45
|
+
align = 'top'
|
|
46
|
+
} = config;
|
|
47
|
+
const targetKey = 'groupKey' in config ? config.groupKey : config.key;
|
|
48
|
+
const targetElement = holder.querySelector(`[data-key="${CSS.escape(String(targetKey))}"]`);
|
|
49
|
+
if (targetElement) {
|
|
50
|
+
if ('key' in config) {
|
|
51
|
+
setTargetScrollMargin(targetElement, align);
|
|
52
|
+
}
|
|
53
|
+
targetElement.scrollIntoView({
|
|
54
|
+
block: align === 'bottom' ? 'end' : align === 'auto' ? 'nearest' : 'start',
|
|
55
|
+
inline: 'nearest'
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const {
|
|
61
|
+
left,
|
|
62
|
+
top
|
|
63
|
+
} = config;
|
|
64
|
+
if (left !== undefined) {
|
|
65
|
+
holder.scrollLeft = left;
|
|
66
|
+
}
|
|
67
|
+
if (top !== undefined) {
|
|
68
|
+
holder.scrollTop = top;
|
|
69
|
+
}
|
|
70
|
+
}, [setTargetScrollMargin]);
|
|
71
|
+
|
|
72
|
+
// ============================ Imperative ============================
|
|
73
|
+
React.useImperativeHandle(ref, () => ({
|
|
74
|
+
scrollTo
|
|
75
|
+
}), [scrollTo]);
|
|
76
|
+
|
|
77
|
+
// ============================== Return ==============================
|
|
78
|
+
return holderRef;
|
|
79
|
+
}
|