@apvee/spfx-react-toolkit 1.0.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 +21 -0
- package/README.md +2012 -0
- package/lib/core/atoms.internal.d.ts +53 -0
- package/lib/core/atoms.internal.d.ts.map +1 -0
- package/lib/core/atoms.internal.js +35 -0
- package/lib/core/atoms.internal.js.map +1 -0
- package/lib/core/context.internal.d.ts +23 -0
- package/lib/core/context.internal.d.ts.map +1 -0
- package/lib/core/context.internal.js +34 -0
- package/lib/core/context.internal.js.map +1 -0
- package/lib/core/index.d.ts +6 -0
- package/lib/core/index.d.ts.map +1 -0
- package/lib/core/index.js +6 -0
- package/lib/core/index.js.map +1 -0
- package/lib/core/provider-application-customizer.d.ts +57 -0
- package/lib/core/provider-application-customizer.d.ts.map +1 -0
- package/lib/core/provider-application-customizer.js +45 -0
- package/lib/core/provider-application-customizer.js.map +1 -0
- package/lib/core/provider-base.internal.d.ts +20 -0
- package/lib/core/provider-base.internal.d.ts.map +1 -0
- package/lib/core/provider-base.internal.js +126 -0
- package/lib/core/provider-base.internal.js.map +1 -0
- package/lib/core/provider-field-customizer.d.ts +58 -0
- package/lib/core/provider-field-customizer.d.ts.map +1 -0
- package/lib/core/provider-field-customizer.js +46 -0
- package/lib/core/provider-field-customizer.js.map +1 -0
- package/lib/core/provider-listview-commandset.d.ts +60 -0
- package/lib/core/provider-listview-commandset.d.ts.map +1 -0
- package/lib/core/provider-listview-commandset.js +48 -0
- package/lib/core/provider-listview-commandset.js.map +1 -0
- package/lib/core/provider-webpart.d.ts +48 -0
- package/lib/core/provider-webpart.d.ts.map +1 -0
- package/lib/core/provider-webpart.js +36 -0
- package/lib/core/provider-webpart.js.map +1 -0
- package/lib/core/types.d.ts +84 -0
- package/lib/core/types.d.ts.map +1 -0
- package/lib/core/types.js +4 -0
- package/lib/core/types.js.map +1 -0
- package/lib/hooks/index.d.ts +34 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/index.js +34 -0
- package/lib/hooks/index.js.map +1 -0
- package/lib/hooks/useSPFxAadHttpClient.d.ts +231 -0
- package/lib/hooks/useSPFxAadHttpClient.d.ts.map +1 -0
- package/lib/hooks/useSPFxAadHttpClient.js +299 -0
- package/lib/hooks/useSPFxAadHttpClient.js.map +1 -0
- package/lib/hooks/useSPFxContainerInfo.d.ts +41 -0
- package/lib/hooks/useSPFxContainerInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxContainerInfo.js +47 -0
- package/lib/hooks/useSPFxContainerInfo.js.map +1 -0
- package/lib/hooks/useSPFxContainerSize.d.ts +119 -0
- package/lib/hooks/useSPFxContainerSize.d.ts.map +1 -0
- package/lib/hooks/useSPFxContainerSize.js +150 -0
- package/lib/hooks/useSPFxContainerSize.js.map +1 -0
- package/lib/hooks/useSPFxContext.d.ts +14 -0
- package/lib/hooks/useSPFxContext.d.ts.map +1 -0
- package/lib/hooks/useSPFxContext.js +16 -0
- package/lib/hooks/useSPFxContext.js.map +1 -0
- package/lib/hooks/useSPFxCorrelationInfo.d.ts +51 -0
- package/lib/hooks/useSPFxCorrelationInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxCorrelationInfo.js +58 -0
- package/lib/hooks/useSPFxCorrelationInfo.js.map +1 -0
- package/lib/hooks/useSPFxCrossSitePermissions.d.ts +81 -0
- package/lib/hooks/useSPFxCrossSitePermissions.d.ts.map +1 -0
- package/lib/hooks/useSPFxCrossSitePermissions.js +132 -0
- package/lib/hooks/useSPFxCrossSitePermissions.js.map +1 -0
- package/lib/hooks/useSPFxDisplayMode.d.ts +61 -0
- package/lib/hooks/useSPFxDisplayMode.d.ts.map +1 -0
- package/lib/hooks/useSPFxDisplayMode.js +69 -0
- package/lib/hooks/useSPFxDisplayMode.js.map +1 -0
- package/lib/hooks/useSPFxEnvironmentInfo.d.ts +63 -0
- package/lib/hooks/useSPFxEnvironmentInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxEnvironmentInfo.js +91 -0
- package/lib/hooks/useSPFxEnvironmentInfo.js.map +1 -0
- package/lib/hooks/useSPFxFluent9ThemeInfo.d.ts +105 -0
- package/lib/hooks/useSPFxFluent9ThemeInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxFluent9ThemeInfo.js +136 -0
- package/lib/hooks/useSPFxFluent9ThemeInfo.js.map +1 -0
- package/lib/hooks/useSPFxHubSiteInfo.d.ts +80 -0
- package/lib/hooks/useSPFxHubSiteInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxHubSiteInfo.js +127 -0
- package/lib/hooks/useSPFxHubSiteInfo.js.map +1 -0
- package/lib/hooks/useSPFxInstanceInfo.d.ts +41 -0
- package/lib/hooks/useSPFxInstanceInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxInstanceInfo.js +40 -0
- package/lib/hooks/useSPFxInstanceInfo.js.map +1 -0
- package/lib/hooks/useSPFxListInfo.d.ts +64 -0
- package/lib/hooks/useSPFxListInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxListInfo.js +70 -0
- package/lib/hooks/useSPFxListInfo.js.map +1 -0
- package/lib/hooks/useSPFxLocaleInfo.d.ts +123 -0
- package/lib/hooks/useSPFxLocaleInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxLocaleInfo.js +109 -0
- package/lib/hooks/useSPFxLocaleInfo.js.map +1 -0
- package/lib/hooks/useSPFxLogger.d.ts +108 -0
- package/lib/hooks/useSPFxLogger.d.ts.map +1 -0
- package/lib/hooks/useSPFxLogger.js +117 -0
- package/lib/hooks/useSPFxLogger.js.map +1 -0
- package/lib/hooks/useSPFxMSGraphClient.d.ts +200 -0
- package/lib/hooks/useSPFxMSGraphClient.d.ts.map +1 -0
- package/lib/hooks/useSPFxMSGraphClient.js +264 -0
- package/lib/hooks/useSPFxMSGraphClient.js.map +1 -0
- package/lib/hooks/useSPFxOneDriveAppData.d.ts +264 -0
- package/lib/hooks/useSPFxOneDriveAppData.d.ts.map +1 -0
- package/lib/hooks/useSPFxOneDriveAppData.js +395 -0
- package/lib/hooks/useSPFxOneDriveAppData.js.map +1 -0
- package/lib/hooks/useSPFxPageContext.d.ts +37 -0
- package/lib/hooks/useSPFxPageContext.d.ts.map +1 -0
- package/lib/hooks/useSPFxPageContext.js +49 -0
- package/lib/hooks/useSPFxPageContext.js.map +1 -0
- package/lib/hooks/useSPFxPageType.d.ts +82 -0
- package/lib/hooks/useSPFxPageType.d.ts.map +1 -0
- package/lib/hooks/useSPFxPageType.js +137 -0
- package/lib/hooks/useSPFxPageType.js.map +1 -0
- package/lib/hooks/useSPFxPerformance.d.ts +72 -0
- package/lib/hooks/useSPFxPerformance.d.ts.map +1 -0
- package/lib/hooks/useSPFxPerformance.js +167 -0
- package/lib/hooks/useSPFxPerformance.js.map +1 -0
- package/lib/hooks/useSPFxPermissions.d.ts +61 -0
- package/lib/hooks/useSPFxPermissions.d.ts.map +1 -0
- package/lib/hooks/useSPFxPermissions.js +73 -0
- package/lib/hooks/useSPFxPermissions.js.map +1 -0
- package/lib/hooks/useSPFxPnP.d.ts +539 -0
- package/lib/hooks/useSPFxPnP.d.ts.map +1 -0
- package/lib/hooks/useSPFxPnP.js +533 -0
- package/lib/hooks/useSPFxPnP.js.map +1 -0
- package/lib/hooks/useSPFxPnPContext.d.ts +290 -0
- package/lib/hooks/useSPFxPnPContext.d.ts.map +1 -0
- package/lib/hooks/useSPFxPnPContext.js +340 -0
- package/lib/hooks/useSPFxPnPContext.js.map +1 -0
- package/lib/hooks/useSPFxPnPList.d.ts +545 -0
- package/lib/hooks/useSPFxPnPList.d.ts.map +1 -0
- package/lib/hooks/useSPFxPnPList.js +906 -0
- package/lib/hooks/useSPFxPnPList.js.map +1 -0
- package/lib/hooks/useSPFxPnPSearch.d.ts +540 -0
- package/lib/hooks/useSPFxPnPSearch.d.ts.map +1 -0
- package/lib/hooks/useSPFxPnPSearch.js +672 -0
- package/lib/hooks/useSPFxPnPSearch.js.map +1 -0
- package/lib/hooks/useSPFxProperties.d.ts +80 -0
- package/lib/hooks/useSPFxProperties.d.ts.map +1 -0
- package/lib/hooks/useSPFxProperties.js +95 -0
- package/lib/hooks/useSPFxProperties.js.map +1 -0
- package/lib/hooks/useSPFxSPHttpClient.d.ts +218 -0
- package/lib/hooks/useSPFxSPHttpClient.d.ts.map +1 -0
- package/lib/hooks/useSPFxSPHttpClient.js +287 -0
- package/lib/hooks/useSPFxSPHttpClient.js.map +1 -0
- package/lib/hooks/useSPFxServiceScope.d.ts +107 -0
- package/lib/hooks/useSPFxServiceScope.d.ts.map +1 -0
- package/lib/hooks/useSPFxServiceScope.js +105 -0
- package/lib/hooks/useSPFxServiceScope.js.map +1 -0
- package/lib/hooks/useSPFxSiteInfo.d.ts +116 -0
- package/lib/hooks/useSPFxSiteInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxSiteInfo.js +109 -0
- package/lib/hooks/useSPFxSiteInfo.js.map +1 -0
- package/lib/hooks/useSPFxStorage.d.ts +81 -0
- package/lib/hooks/useSPFxStorage.d.ts.map +1 -0
- package/lib/hooks/useSPFxStorage.js +140 -0
- package/lib/hooks/useSPFxStorage.js.map +1 -0
- package/lib/hooks/useSPFxTeams.d.ts +63 -0
- package/lib/hooks/useSPFxTeams.d.ts.map +1 -0
- package/lib/hooks/useSPFxTeams.js +198 -0
- package/lib/hooks/useSPFxTeams.js.map +1 -0
- package/lib/hooks/useSPFxTenantProperty.d.ts +389 -0
- package/lib/hooks/useSPFxTenantProperty.d.ts.map +1 -0
- package/lib/hooks/useSPFxTenantProperty.js +683 -0
- package/lib/hooks/useSPFxTenantProperty.js.map +1 -0
- package/lib/hooks/useSPFxThemeInfo.d.ts +27 -0
- package/lib/hooks/useSPFxThemeInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxThemeInfo.js +33 -0
- package/lib/hooks/useSPFxThemeInfo.js.map +1 -0
- package/lib/hooks/useSPFxUserInfo.d.ts +47 -0
- package/lib/hooks/useSPFxUserInfo.d.ts.map +1 -0
- package/lib/hooks/useSPFxUserInfo.js +47 -0
- package/lib/hooks/useSPFxUserInfo.js.map +1 -0
- package/lib/hooks/useSPFxUserPhoto.d.ts +270 -0
- package/lib/hooks/useSPFxUserPhoto.d.ts.map +1 -0
- package/lib/hooks/useSPFxUserPhoto.js +346 -0
- package/lib/hooks/useSPFxUserPhoto.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +3 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/resize-observer.internal.d.ts +10 -0
- package/lib/utils/resize-observer.internal.d.ts.map +1 -0
- package/lib/utils/resize-observer.internal.js +34 -0
- package/lib/utils/resize-observer.internal.js.map +1 -0
- package/lib/utils/theme-subscription.internal.d.ts +11 -0
- package/lib/utils/theme-subscription.internal.d.ts.map +1 -0
- package/lib/utils/theme-subscription.internal.js +58 -0
- package/lib/utils/theme-subscription.internal.js.map +1 -0
- package/lib/utils/type-guards.internal.d.ts +35 -0
- package/lib/utils/type-guards.internal.d.ts.map +1 -0
- package/lib/utils/type-guards.internal.js +88 -0
- package/lib/utils/type-guards.internal.js.map +1 -0
- package/lib/webparts/spFxReactToolkitTest/SpFxReactToolkitTestWebPart.d.ts +13 -0
- package/lib/webparts/spFxReactToolkitTest/SpFxReactToolkitTestWebPart.d.ts.map +1 -0
- package/lib/webparts/spFxReactToolkitTest/SpFxReactToolkitTestWebPart.js +67 -0
- package/lib/webparts/spFxReactToolkitTest/SpFxReactToolkitTestWebPart.js.map +1 -0
- package/lib/webparts/spFxReactToolkitTest/SpFxReactToolkitTestWebPart.manifest.json +21 -0
- package/lib/webparts/spFxReactToolkitTest/assets/welcome-dark.png +0 -0
- package/lib/webparts/spFxReactToolkitTest/assets/welcome-light.png +0 -0
- package/lib/webparts/spFxReactToolkitTest/components/ISpFxReactToolkitTestProps.d.ts +8 -0
- package/lib/webparts/spFxReactToolkitTest/components/ISpFxReactToolkitTestProps.d.ts.map +1 -0
- package/lib/webparts/spFxReactToolkitTest/components/ISpFxReactToolkitTestProps.js +2 -0
- package/lib/webparts/spFxReactToolkitTest/components/ISpFxReactToolkitTestProps.js.map +1 -0
- package/lib/webparts/spFxReactToolkitTest/components/SpFxReactToolkitTest.d.ts +8 -0
- package/lib/webparts/spFxReactToolkitTest/components/SpFxReactToolkitTest.d.ts.map +1 -0
- package/lib/webparts/spFxReactToolkitTest/components/SpFxReactToolkitTest.js +1351 -0
- package/lib/webparts/spFxReactToolkitTest/components/SpFxReactToolkitTest.js.map +1 -0
- package/lib/webparts/spFxReactToolkitTest/components/SpFxReactToolkitTest.module.css +2 -0
- package/lib/webparts/spFxReactToolkitTest/components/SpFxReactToolkitTest.module.scss.d.ts +18 -0
- package/lib/webparts/spFxReactToolkitTest/components/SpFxReactToolkitTest.module.scss.d.ts.map +1 -0
- package/lib/webparts/spFxReactToolkitTest/components/SpFxReactToolkitTest.module.scss.js +19 -0
- package/lib/webparts/spFxReactToolkitTest/components/SpFxReactToolkitTest.module.scss.js.map +1 -0
- package/lib/webparts/spFxReactToolkitTest/loc/en-us.js +16 -0
- package/package.json +95 -0
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
38
|
+
import { useSPFxPnPContext } from './useSPFxPnPContext';
|
|
39
|
+
/**
|
|
40
|
+
* Hook for working with SharePoint lists using PnPjs fluent API.
|
|
41
|
+
* Provides query execution with automatic .top() detection, CRUD operations, pagination, and state management.
|
|
42
|
+
*
|
|
43
|
+
* **Key Features**:
|
|
44
|
+
* - ✅ Native PnPjs fluent API - full type-safe query building
|
|
45
|
+
* - ✅ Smart .top() detection - no conflicts between user .top() and pageSize
|
|
46
|
+
* - ✅ CRUD operations (create, read, update, delete)
|
|
47
|
+
* - ✅ Batch operations for bulk updates
|
|
48
|
+
* - ✅ Pagination with `loadMore()` and `hasMore`
|
|
49
|
+
* - ✅ Automatic refetch after CRUD operations
|
|
50
|
+
* - ✅ Local state management per component instance
|
|
51
|
+
* - ✅ Cross-site support via PnPContextInfo
|
|
52
|
+
* - ✅ ES5 compatibility (IE11 support)
|
|
53
|
+
*
|
|
54
|
+
* **How .top() Detection Works**:
|
|
55
|
+
* The hook uses a recursive Proxy to detect if `.top()` is called in your queryBuilder:
|
|
56
|
+
* - If `.top()` is specified → uses that value
|
|
57
|
+
* - If no `.top()` but `pageSize` option → adds `.top(pageSize)` automatically
|
|
58
|
+
* - If neither → no limit (SharePoint default ~100-5000)
|
|
59
|
+
* - If both → `.top()` wins, warning logged
|
|
60
|
+
*
|
|
61
|
+
* @template T - The type of the list item (default: unknown)
|
|
62
|
+
* @param listTitle - The title of the SharePoint list
|
|
63
|
+
* @param options - Optional configuration (pageSize for pagination)
|
|
64
|
+
* @param pnpContext - Optional PnP context for cross-site scenarios
|
|
65
|
+
* @returns Object containing query method, items, loading states, error, and CRUD operations
|
|
66
|
+
*
|
|
67
|
+
* @example Basic usage with pageSize
|
|
68
|
+
* ```tsx
|
|
69
|
+
* import { useSPFxPnPList } from '@apvee/spfx-react-toolkit';
|
|
70
|
+
*
|
|
71
|
+
* function TaskList() {
|
|
72
|
+
* const { query, items, loading, error } = useSPFxPnPList('Tasks', { pageSize: 50 });
|
|
73
|
+
*
|
|
74
|
+
* useEffect(() => {
|
|
75
|
+
* query(q =>
|
|
76
|
+
* q.select('Id', 'Title', 'Status', 'Priority')
|
|
77
|
+
* .filter("Status eq 'Active'")
|
|
78
|
+
* .orderBy('Priority', false)
|
|
79
|
+
* );
|
|
80
|
+
* }, [query]);
|
|
81
|
+
*
|
|
82
|
+
* if (loading) return <Spinner />;
|
|
83
|
+
* if (error) return <MessageBar>Error: {error.message}</MessageBar>;
|
|
84
|
+
*
|
|
85
|
+
* return (
|
|
86
|
+
* <ul>
|
|
87
|
+
* {items.map(task => (
|
|
88
|
+
* <li key={task.Id}>{task.Title} - {task.Status}</li>
|
|
89
|
+
* ))}
|
|
90
|
+
* </ul>
|
|
91
|
+
* );
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* @example Type-safe fluent filter
|
|
96
|
+
* ```tsx
|
|
97
|
+
* interface Task {
|
|
98
|
+
* Id: number;
|
|
99
|
+
* Title: string;
|
|
100
|
+
* Status: string;
|
|
101
|
+
* Priority: number;
|
|
102
|
+
* DueDate: string;
|
|
103
|
+
* }
|
|
104
|
+
*
|
|
105
|
+
* function ActiveHighPriorityTasks() {
|
|
106
|
+
* const { query, items, loading } = useSPFxPnPList<Task>('Tasks', { pageSize: 50 });
|
|
107
|
+
*
|
|
108
|
+
* useEffect(() => {
|
|
109
|
+
* query(q =>
|
|
110
|
+
* q.select('Id', 'Title', 'Priority', 'DueDate')
|
|
111
|
+
* .filter(f => f.text("Status").equals("Active")
|
|
112
|
+
* .and()
|
|
113
|
+
* .number("Priority").greaterThan(3))
|
|
114
|
+
* .orderBy('DueDate', true)
|
|
115
|
+
* );
|
|
116
|
+
* }, [query]);
|
|
117
|
+
*
|
|
118
|
+
* return (
|
|
119
|
+
* <div>
|
|
120
|
+
* {items.map(task => (
|
|
121
|
+
* <TaskCard key={task.Id} task={task} />
|
|
122
|
+
* ))}
|
|
123
|
+
* </div>
|
|
124
|
+
* );
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @example Query with explicit .top()
|
|
129
|
+
* ```tsx
|
|
130
|
+
* const { query, items } = useSPFxPnPList<Task>('Tasks');
|
|
131
|
+
*
|
|
132
|
+
* useEffect(() => {
|
|
133
|
+
* // User specifies .top() explicitly - takes precedence
|
|
134
|
+
* query(q =>
|
|
135
|
+
* q.select('Id', 'Title')
|
|
136
|
+
* .top(100) // Hook detects this and uses it
|
|
137
|
+
* .orderBy('Created', false)
|
|
138
|
+
* );
|
|
139
|
+
* }, [query]);
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @example CRUD operations
|
|
143
|
+
* ```tsx
|
|
144
|
+
* function TaskManager() {
|
|
145
|
+
* const { items, create, update, remove, loading } = useSPFxPnPList<Task>('Tasks');
|
|
146
|
+
*
|
|
147
|
+
* const handleCreate = async () => {
|
|
148
|
+
* try {
|
|
149
|
+
* const newId = await create({
|
|
150
|
+
* Title: 'New Task',
|
|
151
|
+
* Status: 'Active',
|
|
152
|
+
* Priority: 3
|
|
153
|
+
* });
|
|
154
|
+
* console.log('Created task with ID:', newId);
|
|
155
|
+
* // List automatically refetches
|
|
156
|
+
* } catch (error) {
|
|
157
|
+
* console.error('Create failed:', error);
|
|
158
|
+
* }
|
|
159
|
+
* };
|
|
160
|
+
*
|
|
161
|
+
* const handleUpdate = async (id: number) => {
|
|
162
|
+
* await update(id, { Status: 'Completed' });
|
|
163
|
+
* // List automatically refetches
|
|
164
|
+
* };
|
|
165
|
+
*
|
|
166
|
+
* const handleDelete = async (id: number) => {
|
|
167
|
+
* await remove(id);
|
|
168
|
+
* // List automatically refetches
|
|
169
|
+
* };
|
|
170
|
+
*
|
|
171
|
+
* return (
|
|
172
|
+
* <div>
|
|
173
|
+
* <button onClick={handleCreate}>Create Task</button>
|
|
174
|
+
* {items.map(task => (
|
|
175
|
+
* <div key={task.Id}>
|
|
176
|
+
* <span>{task.Title}</span>
|
|
177
|
+
* <button onClick={() => handleUpdate(task.Id)}>Complete</button>
|
|
178
|
+
* <button onClick={() => handleDelete(task.Id)}>Delete</button>
|
|
179
|
+
* </div>
|
|
180
|
+
* ))}
|
|
181
|
+
* </div>
|
|
182
|
+
* );
|
|
183
|
+
* }
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* @example Batch operations for bulk updates
|
|
187
|
+
* ```tsx
|
|
188
|
+
* function BulkTaskManager() {
|
|
189
|
+
* const { createBatch, updateBatch, removeBatch } = useSPFxPnPList<Task>('Tasks');
|
|
190
|
+
*
|
|
191
|
+
* const handleBulkCreate = async () => {
|
|
192
|
+
* const ids = await createBatch([
|
|
193
|
+
* { Title: 'Task 1', Status: 'Active', Priority: 1 },
|
|
194
|
+
* { Title: 'Task 2', Status: 'Active', Priority: 2 },
|
|
195
|
+
* { Title: 'Task 3', Status: 'Active', Priority: 3 }
|
|
196
|
+
* ]);
|
|
197
|
+
* console.log('Created task IDs:', ids);
|
|
198
|
+
* };
|
|
199
|
+
*
|
|
200
|
+
* const handleBulkUpdate = async (taskIds: number[]) => {
|
|
201
|
+
* await updateBatch([
|
|
202
|
+
* { id: taskIds[0], item: { Status: 'Completed' } },
|
|
203
|
+
* { id: taskIds[1], item: { Priority: 5 } },
|
|
204
|
+
* { id: taskIds[2], item: { Status: 'In Progress', Priority: 4 } }
|
|
205
|
+
* ]);
|
|
206
|
+
* };
|
|
207
|
+
*
|
|
208
|
+
* const handleBulkDelete = async (taskIds: number[]) => {
|
|
209
|
+
* await removeBatch(taskIds);
|
|
210
|
+
* };
|
|
211
|
+
*
|
|
212
|
+
* return (
|
|
213
|
+
* <div>
|
|
214
|
+
* <button onClick={handleBulkCreate}>Create 3 Tasks</button>
|
|
215
|
+
* </div>
|
|
216
|
+
* );
|
|
217
|
+
* }
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* @example Pagination with loadMore
|
|
221
|
+
* ```tsx
|
|
222
|
+
* function InfiniteTaskList() {
|
|
223
|
+
* const {
|
|
224
|
+
* query,
|
|
225
|
+
* items,
|
|
226
|
+
* loading,
|
|
227
|
+
* loadingMore,
|
|
228
|
+
* hasMore,
|
|
229
|
+
* loadMore,
|
|
230
|
+
* isEmpty
|
|
231
|
+
* } = useSPFxPnPList<Task>('Tasks', { pageSize: 50 });
|
|
232
|
+
*
|
|
233
|
+
* useEffect(() => {
|
|
234
|
+
* query(q =>
|
|
235
|
+
* q.select('Id', 'Title', 'Status')
|
|
236
|
+
* .orderBy('Created', false)
|
|
237
|
+
* );
|
|
238
|
+
* }, [query]);
|
|
239
|
+
*
|
|
240
|
+
* if (loading) return <Spinner label="Loading tasks..." />;
|
|
241
|
+
* if (isEmpty) return <MessageBar>No tasks found</MessageBar>;
|
|
242
|
+
*
|
|
243
|
+
* return (
|
|
244
|
+
* <div>
|
|
245
|
+
* {items.map(task => (
|
|
246
|
+
* <TaskCard key={task.Id} task={task} />
|
|
247
|
+
* ))}
|
|
248
|
+
*
|
|
249
|
+
* {hasMore && (
|
|
250
|
+
* <PrimaryButton
|
|
251
|
+
* text={loadingMore ? 'Loading...' : 'Load More'}
|
|
252
|
+
* onClick={loadMore}
|
|
253
|
+
* disabled={loadingMore}
|
|
254
|
+
* />
|
|
255
|
+
* )}
|
|
256
|
+
* </div>
|
|
257
|
+
* );
|
|
258
|
+
* }
|
|
259
|
+
* ```
|
|
260
|
+
*
|
|
261
|
+
* @example Cross-site usage
|
|
262
|
+
* ```tsx
|
|
263
|
+
* function EmployeeList() {
|
|
264
|
+
* // Create context for HR site
|
|
265
|
+
* const hrContext = useSPFxPnPContext({
|
|
266
|
+
* siteUrl: '/sites/hr'
|
|
267
|
+
* });
|
|
268
|
+
*
|
|
269
|
+
* // Query Employees list from HR site
|
|
270
|
+
* const { query, items, loading } = useSPFxPnPList<Employee>(
|
|
271
|
+
* 'Employees',
|
|
272
|
+
* { pageSize: 100 },
|
|
273
|
+
* hrContext // Pass context for cross-site query
|
|
274
|
+
* );
|
|
275
|
+
*
|
|
276
|
+
* useEffect(() => {
|
|
277
|
+
* query(q =>
|
|
278
|
+
* q.select('Id', 'Name', 'Department', 'Email')
|
|
279
|
+
* .filter("Department eq 'Engineering'")
|
|
280
|
+
* .orderBy('Name', true)
|
|
281
|
+
* );
|
|
282
|
+
* }, [query]);
|
|
283
|
+
*
|
|
284
|
+
* return (
|
|
285
|
+
* <div>
|
|
286
|
+
* {items.map(emp => (
|
|
287
|
+
* <PersonaCard key={emp.Id} employee={emp} />
|
|
288
|
+
* ))}
|
|
289
|
+
* </div>
|
|
290
|
+
* );
|
|
291
|
+
* }
|
|
292
|
+
* ```
|
|
293
|
+
*
|
|
294
|
+
* @example Manual refetch with error handling
|
|
295
|
+
* ```tsx
|
|
296
|
+
* function TaskListWithRefresh() {
|
|
297
|
+
* const { query, items, loading, error, refetch, clearError } = useSPFxPnPList<Task>('Tasks', { pageSize: 50 });
|
|
298
|
+
*
|
|
299
|
+
* useEffect(() => {
|
|
300
|
+
* query(q => q.select('Id', 'Title', 'Status').orderBy('Created', false));
|
|
301
|
+
* }, [query]);
|
|
302
|
+
*
|
|
303
|
+
* return (
|
|
304
|
+
* <div>
|
|
305
|
+
* <CommandBar
|
|
306
|
+
* items={[
|
|
307
|
+
* {
|
|
308
|
+
* key: 'refresh',
|
|
309
|
+
* text: 'Refresh',
|
|
310
|
+
* iconProps: { iconName: 'Refresh' },
|
|
311
|
+
* onClick: () => refetch()
|
|
312
|
+
* }
|
|
313
|
+
* ]}
|
|
314
|
+
* />
|
|
315
|
+
*
|
|
316
|
+
* {error && (
|
|
317
|
+
* <MessageBar
|
|
318
|
+
* messageBarType={MessageBarType.error}
|
|
319
|
+
* onDismiss={clearError}
|
|
320
|
+
* >
|
|
321
|
+
* Error loading tasks: {error.message}
|
|
322
|
+
* </MessageBar>
|
|
323
|
+
* )}
|
|
324
|
+
*
|
|
325
|
+
* {loading ? (
|
|
326
|
+
* <Spinner />
|
|
327
|
+
* ) : (
|
|
328
|
+
* <DetailsList items={items} />
|
|
329
|
+
* )}
|
|
330
|
+
* </div>
|
|
331
|
+
* );
|
|
332
|
+
* }
|
|
333
|
+
* ```
|
|
334
|
+
*
|
|
335
|
+
* @example Conditional loading (autoLoad: false)
|
|
336
|
+
* ```tsx
|
|
337
|
+
* function ConditionalTaskList() {
|
|
338
|
+
* const [showCompleted, setShowCompleted] = useState(false);
|
|
339
|
+
* const { items, loading, refetch } = useSPFxPnPList<Task>(
|
|
340
|
+
* 'Tasks',
|
|
341
|
+
* {
|
|
342
|
+
* filter: showCompleted ? "Status eq 'Completed'" : "Status eq 'Active'",
|
|
343
|
+
* autoLoad: false // Don't load on mount
|
|
344
|
+
* }
|
|
345
|
+
* );
|
|
346
|
+
*
|
|
347
|
+
* useEffect(() => {
|
|
348
|
+
* if (showCompleted) {
|
|
349
|
+
* refetch(); // Manually trigger load
|
|
350
|
+
* }
|
|
351
|
+
* }, [showCompleted, refetch]);
|
|
352
|
+
*
|
|
353
|
+
* return (
|
|
354
|
+
* <div>
|
|
355
|
+
* <Toggle
|
|
356
|
+
* label="Show Completed"
|
|
357
|
+
* checked={showCompleted}
|
|
358
|
+
* onChange={(_, checked) => setShowCompleted(checked || false)}
|
|
359
|
+
* />
|
|
360
|
+
* {items.map(task => <TaskCard key={task.Id} task={task} />)}
|
|
361
|
+
* </div>
|
|
362
|
+
* );
|
|
363
|
+
* }
|
|
364
|
+
* ```
|
|
365
|
+
*
|
|
366
|
+
* @remarks
|
|
367
|
+
* **PnPjs Installation**: This hook requires `@pnp/sp` to be installed:
|
|
368
|
+
* ```bash
|
|
369
|
+
* npm install @pnp/sp @pnp/core @pnp/queryable
|
|
370
|
+
* ```
|
|
371
|
+
*
|
|
372
|
+
* **SharePoint List View Threshold**: Be aware that querying lists with more than 5000 items
|
|
373
|
+
* may cause throttling unless:
|
|
374
|
+
* - Filters use indexed columns
|
|
375
|
+
* - Query results are under 5000 items
|
|
376
|
+
* - Proper pagination is used (top + skip)
|
|
377
|
+
*
|
|
378
|
+
* **Fluent Filter Requirements**: To use type-safe fluent filters, import types:
|
|
379
|
+
* ```typescript
|
|
380
|
+
* import '@pnp/sp/items'; // Enables fluent filter on items
|
|
381
|
+
* ```
|
|
382
|
+
*
|
|
383
|
+
* **State Management**: Each hook instance maintains its own local state (items, loading, error).
|
|
384
|
+
* State is not shared between components - this follows the standard React hooks pattern.
|
|
385
|
+
*
|
|
386
|
+
* **Debounced Refetch**: CRUD operations trigger a debounced refetch (100ms delay) to prevent
|
|
387
|
+
* race conditions when multiple operations occur in quick succession.
|
|
388
|
+
*
|
|
389
|
+
* @see {@link useSPFxPnPContext} for creating PnP contexts
|
|
390
|
+
* @see {@link PNPContextInfo} for context information
|
|
391
|
+
*/
|
|
392
|
+
export function useSPFxPnPList(listTitle, options, pnpContext) {
|
|
393
|
+
var _this = this;
|
|
394
|
+
// Get PnP context (use provided context or create default)
|
|
395
|
+
var defaultContext = useSPFxPnPContext();
|
|
396
|
+
var context = pnpContext || defaultContext;
|
|
397
|
+
// Use the native SPFI instance from context
|
|
398
|
+
var sp = context === null || context === void 0 ? void 0 : context.sp;
|
|
399
|
+
// Default pageSize from hook options
|
|
400
|
+
var defaultPageSize = options === null || options === void 0 ? void 0 : options.pageSize;
|
|
401
|
+
// Local state management
|
|
402
|
+
var _a = useState([]), items = _a[0], setItems = _a[1];
|
|
403
|
+
var _b = useState(false), loading = _b[0], setLoading = _b[1];
|
|
404
|
+
var _c = useState(false), loadingMore = _c[0], setLoadingMore = _c[1];
|
|
405
|
+
var _d = useState(), error = _d[0], setError = _d[1];
|
|
406
|
+
var _e = useState(false), hasMore = _e[0], setHasMore = _e[1];
|
|
407
|
+
// State for tracking last query (needed for refetch and loadMore)
|
|
408
|
+
var _f = useState(undefined), lastQueryBuilder = _f[0], setLastQueryBuilder = _f[1];
|
|
409
|
+
var _g = useState(undefined), lastEffectivePageSize = _g[0], setLastEffectivePageSize = _g[1];
|
|
410
|
+
var _h = useState(0), currentSkip = _h[0], setCurrentSkip = _h[1];
|
|
411
|
+
// Refs
|
|
412
|
+
var refetchTimeoutRef = useRef(undefined);
|
|
413
|
+
var mountedRef = useRef(true);
|
|
414
|
+
// Clear error handler
|
|
415
|
+
var clearError = useCallback(function () {
|
|
416
|
+
setError(undefined);
|
|
417
|
+
}, []);
|
|
418
|
+
/**
|
|
419
|
+
* Helper: Creates a recursive Proxy to track .top() calls in queryBuilder.
|
|
420
|
+
* This allows automatic detection of user-specified page size.
|
|
421
|
+
*/
|
|
422
|
+
var createMonitoredQuery = useCallback(function (target, tracker) {
|
|
423
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
424
|
+
return new Proxy(target, {
|
|
425
|
+
get: function (t, prop) {
|
|
426
|
+
if (prop === 'top') {
|
|
427
|
+
return function (n) {
|
|
428
|
+
tracker.top = n;
|
|
429
|
+
var result = t.top.call(t, n);
|
|
430
|
+
return createMonitoredQuery(result, tracker);
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
var value = t[prop];
|
|
434
|
+
if (typeof value === 'function') {
|
|
435
|
+
return function () {
|
|
436
|
+
var args = [];
|
|
437
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
438
|
+
args[_i] = arguments[_i];
|
|
439
|
+
}
|
|
440
|
+
var result = value.apply(t, args);
|
|
441
|
+
if (result && typeof result === 'object' && typeof result.select === 'function') {
|
|
442
|
+
return createMonitoredQuery(result, tracker);
|
|
443
|
+
}
|
|
444
|
+
return result;
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
return value;
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
451
|
+
}, []);
|
|
452
|
+
/**
|
|
453
|
+
* Executes a query with automatic .top() detection.
|
|
454
|
+
*/
|
|
455
|
+
var query = useCallback(function (queryBuilder, queryOptions) { return __awaiter(_this, void 0, void 0, function () {
|
|
456
|
+
var err, pageSize, baseQuery, tracker, monitored, userQuery, finalQuery, effectivePageSize, result, err_1;
|
|
457
|
+
var _a;
|
|
458
|
+
return __generator(this, function (_b) {
|
|
459
|
+
switch (_b.label) {
|
|
460
|
+
case 0:
|
|
461
|
+
if (!sp || !(context === null || context === void 0 ? void 0 : context.isInitialized)) {
|
|
462
|
+
err = new Error('[useSPFxPnPList] PnP context not initialized. Ensure @pnp/sp is installed.');
|
|
463
|
+
setError(err);
|
|
464
|
+
throw err;
|
|
465
|
+
}
|
|
466
|
+
setLoading(true);
|
|
467
|
+
setError(undefined);
|
|
468
|
+
_b.label = 1;
|
|
469
|
+
case 1:
|
|
470
|
+
_b.trys.push([1, 3, , 4]);
|
|
471
|
+
pageSize = (_a = queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.pageSize) !== null && _a !== void 0 ? _a : defaultPageSize;
|
|
472
|
+
baseQuery = sp.web.lists.getByTitle(listTitle).items;
|
|
473
|
+
tracker = { top: undefined };
|
|
474
|
+
monitored = createMonitoredQuery(baseQuery, tracker);
|
|
475
|
+
userQuery = queryBuilder ? queryBuilder(monitored) : monitored;
|
|
476
|
+
finalQuery = void 0;
|
|
477
|
+
effectivePageSize = void 0;
|
|
478
|
+
// Warning if both specified
|
|
479
|
+
if (tracker.top !== undefined && pageSize !== undefined) {
|
|
480
|
+
console.warn("[useSPFxPnPList] Both .top(".concat(tracker.top, ") and pageSize(").concat(pageSize, ") specified. ") +
|
|
481
|
+
"Using .top(".concat(tracker.top, ")."));
|
|
482
|
+
}
|
|
483
|
+
if (tracker.top !== undefined) {
|
|
484
|
+
// User specified .top() explicitly
|
|
485
|
+
finalQuery = userQuery;
|
|
486
|
+
effectivePageSize = tracker.top;
|
|
487
|
+
}
|
|
488
|
+
else if (pageSize !== undefined) {
|
|
489
|
+
// Use pageSize option
|
|
490
|
+
finalQuery = userQuery.top(pageSize);
|
|
491
|
+
effectivePageSize = pageSize;
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
// No pagination
|
|
495
|
+
finalQuery = userQuery;
|
|
496
|
+
effectivePageSize = undefined;
|
|
497
|
+
}
|
|
498
|
+
return [4 /*yield*/, finalQuery()];
|
|
499
|
+
case 2:
|
|
500
|
+
result = _b.sent();
|
|
501
|
+
if (!mountedRef.current)
|
|
502
|
+
return [2 /*return*/, result];
|
|
503
|
+
// Update state
|
|
504
|
+
setItems(result);
|
|
505
|
+
setLastQueryBuilder(function () { return queryBuilder; });
|
|
506
|
+
setLastEffectivePageSize(effectivePageSize);
|
|
507
|
+
setCurrentSkip(result.length);
|
|
508
|
+
// hasMore only meaningful with pagination
|
|
509
|
+
if (effectivePageSize !== undefined) {
|
|
510
|
+
setHasMore(result.length === effectivePageSize);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
setHasMore(false);
|
|
514
|
+
}
|
|
515
|
+
setLoading(false);
|
|
516
|
+
return [2 /*return*/, result];
|
|
517
|
+
case 3:
|
|
518
|
+
err_1 = _b.sent();
|
|
519
|
+
if (mountedRef.current) {
|
|
520
|
+
setError(err_1);
|
|
521
|
+
setLoading(false);
|
|
522
|
+
}
|
|
523
|
+
throw err_1;
|
|
524
|
+
case 4: return [2 /*return*/];
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
}); }, [sp, context === null || context === void 0 ? void 0 : context.isInitialized, listTitle, defaultPageSize, createMonitoredQuery]);
|
|
528
|
+
/**
|
|
529
|
+
* Re-executes the last query (resets pagination).
|
|
530
|
+
*/
|
|
531
|
+
var refetch = useCallback(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
532
|
+
return __generator(this, function (_a) {
|
|
533
|
+
switch (_a.label) {
|
|
534
|
+
case 0:
|
|
535
|
+
if (!lastQueryBuilder) {
|
|
536
|
+
throw new Error('[useSPFxPnPList] No previous query to refetch. Call query() first.');
|
|
537
|
+
}
|
|
538
|
+
setCurrentSkip(0);
|
|
539
|
+
return [4 /*yield*/, query(lastQueryBuilder, { pageSize: lastEffectivePageSize })];
|
|
540
|
+
case 1:
|
|
541
|
+
_a.sent();
|
|
542
|
+
return [2 /*return*/];
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
}); }, [lastQueryBuilder, lastEffectivePageSize, query]);
|
|
546
|
+
/**
|
|
547
|
+
* Debounced refetch to prevent race conditions during rapid CRUD operations.
|
|
548
|
+
*/
|
|
549
|
+
var debouncedRefetch = useCallback(function () {
|
|
550
|
+
if (refetchTimeoutRef.current) {
|
|
551
|
+
clearTimeout(refetchTimeoutRef.current);
|
|
552
|
+
}
|
|
553
|
+
refetchTimeoutRef.current = setTimeout(function () {
|
|
554
|
+
refetch().catch(function (err) {
|
|
555
|
+
var error = err;
|
|
556
|
+
console.error('[useSPFxPnPList] Debounced refetch error:', error);
|
|
557
|
+
setError(error);
|
|
558
|
+
});
|
|
559
|
+
}, 100);
|
|
560
|
+
}, [refetch]);
|
|
561
|
+
/**
|
|
562
|
+
* Loads more items (pagination with last query).
|
|
563
|
+
*/
|
|
564
|
+
var loadMore = useCallback(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
565
|
+
var baseQuery, tracker, monitored, userQuery, finalQuery, result_1, err_2;
|
|
566
|
+
return __generator(this, function (_a) {
|
|
567
|
+
switch (_a.label) {
|
|
568
|
+
case 0:
|
|
569
|
+
if (!lastQueryBuilder) {
|
|
570
|
+
throw new Error('[useSPFxPnPList] No previous query. Call query() first.');
|
|
571
|
+
}
|
|
572
|
+
if (lastEffectivePageSize === undefined) {
|
|
573
|
+
throw new Error('[useSPFxPnPList] Cannot loadMore without pageSize. Specify .top() or pageSize option in query().');
|
|
574
|
+
}
|
|
575
|
+
if (loadingMore || loading) {
|
|
576
|
+
return [2 /*return*/, []];
|
|
577
|
+
}
|
|
578
|
+
setLoadingMore(true);
|
|
579
|
+
_a.label = 1;
|
|
580
|
+
case 1:
|
|
581
|
+
_a.trys.push([1, 3, , 4]);
|
|
582
|
+
if (!sp || !(context === null || context === void 0 ? void 0 : context.isInitialized)) {
|
|
583
|
+
throw new Error('[useSPFxPnPList] PnP context not initialized');
|
|
584
|
+
}
|
|
585
|
+
baseQuery = sp.web.lists.getByTitle(listTitle).items;
|
|
586
|
+
tracker = { top: undefined };
|
|
587
|
+
monitored = createMonitoredQuery(baseQuery, tracker);
|
|
588
|
+
userQuery = lastQueryBuilder(monitored);
|
|
589
|
+
finalQuery = userQuery.skip(currentSkip).top(lastEffectivePageSize);
|
|
590
|
+
return [4 /*yield*/, finalQuery()];
|
|
591
|
+
case 2:
|
|
592
|
+
result_1 = _a.sent();
|
|
593
|
+
if (!mountedRef.current)
|
|
594
|
+
return [2 /*return*/, result_1];
|
|
595
|
+
setItems(function (prevItems) {
|
|
596
|
+
return prevItems.concat(result_1);
|
|
597
|
+
});
|
|
598
|
+
setCurrentSkip(function (prev) {
|
|
599
|
+
return prev + result_1.length;
|
|
600
|
+
});
|
|
601
|
+
setHasMore(result_1.length === lastEffectivePageSize);
|
|
602
|
+
setLoadingMore(false);
|
|
603
|
+
return [2 /*return*/, result_1];
|
|
604
|
+
case 3:
|
|
605
|
+
err_2 = _a.sent();
|
|
606
|
+
if (mountedRef.current) {
|
|
607
|
+
setError(err_2);
|
|
608
|
+
setLoadingMore(false);
|
|
609
|
+
}
|
|
610
|
+
throw err_2;
|
|
611
|
+
case 4: return [2 /*return*/];
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}); }, [lastQueryBuilder, lastEffectivePageSize, loadingMore, loading, sp, context === null || context === void 0 ? void 0 : context.isInitialized, listTitle, currentSkip, createMonitoredQuery]);
|
|
615
|
+
/**
|
|
616
|
+
* Gets a single item by ID.
|
|
617
|
+
*/
|
|
618
|
+
var getById = useCallback(function (id) { return __awaiter(_this, void 0, void 0, function () {
|
|
619
|
+
var item, err_3, error_1;
|
|
620
|
+
return __generator(this, function (_a) {
|
|
621
|
+
switch (_a.label) {
|
|
622
|
+
case 0:
|
|
623
|
+
if (!sp || !(context === null || context === void 0 ? void 0 : context.isInitialized)) {
|
|
624
|
+
throw new Error('[useSPFxPnPList] PnP context not initialized');
|
|
625
|
+
}
|
|
626
|
+
_a.label = 1;
|
|
627
|
+
case 1:
|
|
628
|
+
_a.trys.push([1, 3, , 4]);
|
|
629
|
+
return [4 /*yield*/, sp.web.lists.getByTitle(listTitle).items.getById(id)()];
|
|
630
|
+
case 2:
|
|
631
|
+
item = _a.sent();
|
|
632
|
+
return [2 /*return*/, item];
|
|
633
|
+
case 3:
|
|
634
|
+
err_3 = _a.sent();
|
|
635
|
+
error_1 = err_3;
|
|
636
|
+
console.error('[useSPFxPnPList] getById error:', error_1);
|
|
637
|
+
setError(error_1);
|
|
638
|
+
return [2 /*return*/, undefined];
|
|
639
|
+
case 4: return [2 /*return*/];
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}); }, [sp, context === null || context === void 0 ? void 0 : context.isInitialized, listTitle]);
|
|
643
|
+
/**
|
|
644
|
+
* Creates a new list item.
|
|
645
|
+
*/
|
|
646
|
+
var create = useCallback(function (item) { return __awaiter(_this, void 0, void 0, function () {
|
|
647
|
+
var result, err_4;
|
|
648
|
+
return __generator(this, function (_a) {
|
|
649
|
+
switch (_a.label) {
|
|
650
|
+
case 0:
|
|
651
|
+
if (!sp || !(context === null || context === void 0 ? void 0 : context.isInitialized)) {
|
|
652
|
+
throw new Error('PnP context not initialized');
|
|
653
|
+
}
|
|
654
|
+
_a.label = 1;
|
|
655
|
+
case 1:
|
|
656
|
+
_a.trys.push([1, 3, , 4]);
|
|
657
|
+
return [4 /*yield*/, sp.web.lists.getByTitle(listTitle).items.add(item)];
|
|
658
|
+
case 2:
|
|
659
|
+
result = _a.sent();
|
|
660
|
+
debouncedRefetch();
|
|
661
|
+
return [2 /*return*/, result.data.Id];
|
|
662
|
+
case 3:
|
|
663
|
+
err_4 = _a.sent();
|
|
664
|
+
setError(err_4);
|
|
665
|
+
throw err_4;
|
|
666
|
+
case 4: return [2 /*return*/];
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
}); }, [sp, context === null || context === void 0 ? void 0 : context.isInitialized, listTitle, debouncedRefetch]);
|
|
670
|
+
/**
|
|
671
|
+
* Updates an existing list item.
|
|
672
|
+
*/
|
|
673
|
+
var update = useCallback(function (id, item) { return __awaiter(_this, void 0, void 0, function () {
|
|
674
|
+
var err_5;
|
|
675
|
+
return __generator(this, function (_a) {
|
|
676
|
+
switch (_a.label) {
|
|
677
|
+
case 0:
|
|
678
|
+
if (!sp || !(context === null || context === void 0 ? void 0 : context.isInitialized)) {
|
|
679
|
+
throw new Error('PnP context not initialized');
|
|
680
|
+
}
|
|
681
|
+
_a.label = 1;
|
|
682
|
+
case 1:
|
|
683
|
+
_a.trys.push([1, 3, , 4]);
|
|
684
|
+
return [4 /*yield*/, sp.web.lists.getByTitle(listTitle).items.getById(id).update(item)];
|
|
685
|
+
case 2:
|
|
686
|
+
_a.sent();
|
|
687
|
+
debouncedRefetch();
|
|
688
|
+
return [3 /*break*/, 4];
|
|
689
|
+
case 3:
|
|
690
|
+
err_5 = _a.sent();
|
|
691
|
+
setError(err_5);
|
|
692
|
+
throw err_5;
|
|
693
|
+
case 4: return [2 /*return*/];
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
}); }, [sp, context === null || context === void 0 ? void 0 : context.isInitialized, listTitle, debouncedRefetch]);
|
|
697
|
+
/**
|
|
698
|
+
* Deletes a list item.
|
|
699
|
+
*/
|
|
700
|
+
var remove = useCallback(function (id) { return __awaiter(_this, void 0, void 0, function () {
|
|
701
|
+
var err_6;
|
|
702
|
+
return __generator(this, function (_a) {
|
|
703
|
+
switch (_a.label) {
|
|
704
|
+
case 0:
|
|
705
|
+
if (!sp || !(context === null || context === void 0 ? void 0 : context.isInitialized)) {
|
|
706
|
+
throw new Error('PnP context not initialized');
|
|
707
|
+
}
|
|
708
|
+
_a.label = 1;
|
|
709
|
+
case 1:
|
|
710
|
+
_a.trys.push([1, 3, , 4]);
|
|
711
|
+
return [4 /*yield*/, sp.web.lists.getByTitle(listTitle).items.getById(id).delete()];
|
|
712
|
+
case 2:
|
|
713
|
+
_a.sent();
|
|
714
|
+
debouncedRefetch();
|
|
715
|
+
return [3 /*break*/, 4];
|
|
716
|
+
case 3:
|
|
717
|
+
err_6 = _a.sent();
|
|
718
|
+
setError(err_6);
|
|
719
|
+
throw err_6;
|
|
720
|
+
case 4: return [2 /*return*/];
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
}); }, [sp, context === null || context === void 0 ? void 0 : context.isInitialized, listTitle, debouncedRefetch]);
|
|
724
|
+
/**
|
|
725
|
+
* Creates multiple items in a batch.
|
|
726
|
+
*/
|
|
727
|
+
var createBatch = useCallback(function (itemsToCreate) { return __awaiter(_this, void 0, void 0, function () {
|
|
728
|
+
var ids_1, errors_1, batchResult, batchedSP, execute, list, i, batchError, err_7, error_2;
|
|
729
|
+
return __generator(this, function (_a) {
|
|
730
|
+
switch (_a.label) {
|
|
731
|
+
case 0:
|
|
732
|
+
if (!sp || !(context === null || context === void 0 ? void 0 : context.isInitialized)) {
|
|
733
|
+
throw new Error('PnP context not initialized');
|
|
734
|
+
}
|
|
735
|
+
_a.label = 1;
|
|
736
|
+
case 1:
|
|
737
|
+
_a.trys.push([1, 3, , 4]);
|
|
738
|
+
ids_1 = [];
|
|
739
|
+
errors_1 = [];
|
|
740
|
+
batchResult = sp.batched();
|
|
741
|
+
batchedSP = batchResult[0];
|
|
742
|
+
execute = batchResult[1];
|
|
743
|
+
list = batchedSP.web.lists.getByTitle(listTitle);
|
|
744
|
+
// Queue all creates
|
|
745
|
+
for (i = 0; i < itemsToCreate.length; i++) {
|
|
746
|
+
list.items.add(itemsToCreate[i]).then(function (result) {
|
|
747
|
+
ids_1.push(result.data.Id);
|
|
748
|
+
}).catch(function (error) {
|
|
749
|
+
console.error('Batch create error:', error);
|
|
750
|
+
errors_1.push(error);
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
// Execute batch
|
|
754
|
+
return [4 /*yield*/, execute()];
|
|
755
|
+
case 2:
|
|
756
|
+
// Execute batch
|
|
757
|
+
_a.sent();
|
|
758
|
+
// If there were errors in individual operations, set error state
|
|
759
|
+
if (errors_1.length > 0) {
|
|
760
|
+
batchError = new Error("Batch create failed: ".concat(errors_1.length, " of ").concat(itemsToCreate.length, " items failed"));
|
|
761
|
+
setError(batchError);
|
|
762
|
+
console.error('Batch create summary:', errors_1);
|
|
763
|
+
}
|
|
764
|
+
debouncedRefetch();
|
|
765
|
+
return [2 /*return*/, ids_1];
|
|
766
|
+
case 3:
|
|
767
|
+
err_7 = _a.sent();
|
|
768
|
+
error_2 = err_7;
|
|
769
|
+
setError(error_2);
|
|
770
|
+
throw error_2;
|
|
771
|
+
case 4: return [2 /*return*/];
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
}); }, [sp, context === null || context === void 0 ? void 0 : context.isInitialized, listTitle, debouncedRefetch]);
|
|
775
|
+
/**
|
|
776
|
+
* Updates multiple items in a batch.
|
|
777
|
+
*/
|
|
778
|
+
var updateBatch = useCallback(function (updates) { return __awaiter(_this, void 0, void 0, function () {
|
|
779
|
+
var errors_2, batchResult, batchedSP, execute, list, i, updateItem, batchError, err_8, error_3;
|
|
780
|
+
return __generator(this, function (_a) {
|
|
781
|
+
switch (_a.label) {
|
|
782
|
+
case 0:
|
|
783
|
+
if (!sp || !(context === null || context === void 0 ? void 0 : context.isInitialized)) {
|
|
784
|
+
throw new Error('PnP context not initialized');
|
|
785
|
+
}
|
|
786
|
+
_a.label = 1;
|
|
787
|
+
case 1:
|
|
788
|
+
_a.trys.push([1, 3, , 4]);
|
|
789
|
+
errors_2 = [];
|
|
790
|
+
batchResult = sp.batched();
|
|
791
|
+
batchedSP = batchResult[0];
|
|
792
|
+
execute = batchResult[1];
|
|
793
|
+
list = batchedSP.web.lists.getByTitle(listTitle);
|
|
794
|
+
// Queue all updates
|
|
795
|
+
for (i = 0; i < updates.length; i++) {
|
|
796
|
+
updateItem = updates[i];
|
|
797
|
+
list.items.getById(updateItem.id).update(updateItem.item).catch(function (error) {
|
|
798
|
+
console.error('Batch update error:', error);
|
|
799
|
+
errors_2.push(error);
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
// Execute batch
|
|
803
|
+
return [4 /*yield*/, execute()];
|
|
804
|
+
case 2:
|
|
805
|
+
// Execute batch
|
|
806
|
+
_a.sent();
|
|
807
|
+
// If there were errors in individual operations, set error state
|
|
808
|
+
if (errors_2.length > 0) {
|
|
809
|
+
batchError = new Error("Batch update failed: ".concat(errors_2.length, " of ").concat(updates.length, " items failed"));
|
|
810
|
+
setError(batchError);
|
|
811
|
+
console.error('Batch update summary:', errors_2);
|
|
812
|
+
}
|
|
813
|
+
debouncedRefetch();
|
|
814
|
+
return [3 /*break*/, 4];
|
|
815
|
+
case 3:
|
|
816
|
+
err_8 = _a.sent();
|
|
817
|
+
error_3 = err_8;
|
|
818
|
+
setError(error_3);
|
|
819
|
+
throw error_3;
|
|
820
|
+
case 4: return [2 /*return*/];
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
}); }, [sp, context === null || context === void 0 ? void 0 : context.isInitialized, listTitle, debouncedRefetch]);
|
|
824
|
+
/**
|
|
825
|
+
* Deletes multiple items in a batch.
|
|
826
|
+
*/
|
|
827
|
+
var removeBatch = useCallback(function (ids) { return __awaiter(_this, void 0, void 0, function () {
|
|
828
|
+
var errors_3, batchResult, batchedSP, execute, list, i, batchError, err_9, error_4;
|
|
829
|
+
return __generator(this, function (_a) {
|
|
830
|
+
switch (_a.label) {
|
|
831
|
+
case 0:
|
|
832
|
+
if (!sp || !(context === null || context === void 0 ? void 0 : context.isInitialized)) {
|
|
833
|
+
throw new Error('PnP context not initialized');
|
|
834
|
+
}
|
|
835
|
+
_a.label = 1;
|
|
836
|
+
case 1:
|
|
837
|
+
_a.trys.push([1, 3, , 4]);
|
|
838
|
+
errors_3 = [];
|
|
839
|
+
batchResult = sp.batched();
|
|
840
|
+
batchedSP = batchResult[0];
|
|
841
|
+
execute = batchResult[1];
|
|
842
|
+
list = batchedSP.web.lists.getByTitle(listTitle);
|
|
843
|
+
// Queue all deletes
|
|
844
|
+
for (i = 0; i < ids.length; i++) {
|
|
845
|
+
list.items.getById(ids[i]).delete().catch(function (error) {
|
|
846
|
+
console.error('Batch delete error:', error);
|
|
847
|
+
errors_3.push(error);
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
// Execute batch
|
|
851
|
+
return [4 /*yield*/, execute()];
|
|
852
|
+
case 2:
|
|
853
|
+
// Execute batch
|
|
854
|
+
_a.sent();
|
|
855
|
+
// If there were errors in individual operations, set error state
|
|
856
|
+
if (errors_3.length > 0) {
|
|
857
|
+
batchError = new Error("Batch delete failed: ".concat(errors_3.length, " of ").concat(ids.length, " items failed"));
|
|
858
|
+
setError(batchError);
|
|
859
|
+
console.error('Batch delete summary:', errors_3);
|
|
860
|
+
}
|
|
861
|
+
debouncedRefetch();
|
|
862
|
+
return [3 /*break*/, 4];
|
|
863
|
+
case 3:
|
|
864
|
+
err_9 = _a.sent();
|
|
865
|
+
error_4 = err_9;
|
|
866
|
+
setError(error_4);
|
|
867
|
+
throw error_4;
|
|
868
|
+
case 4: return [2 /*return*/];
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
}); }, [sp, context === null || context === void 0 ? void 0 : context.isInitialized, listTitle, debouncedRefetch]);
|
|
872
|
+
/**
|
|
873
|
+
* Cleanup on unmount.
|
|
874
|
+
*/
|
|
875
|
+
useEffect(function () {
|
|
876
|
+
mountedRef.current = true;
|
|
877
|
+
return function () {
|
|
878
|
+
mountedRef.current = false;
|
|
879
|
+
if (refetchTimeoutRef.current) {
|
|
880
|
+
clearTimeout(refetchTimeoutRef.current);
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
}, []);
|
|
884
|
+
// Derived state
|
|
885
|
+
var isEmpty = items.length === 0 && !loading && !error;
|
|
886
|
+
return {
|
|
887
|
+
query: query,
|
|
888
|
+
items: items,
|
|
889
|
+
loading: loading,
|
|
890
|
+
loadingMore: loadingMore,
|
|
891
|
+
error: error,
|
|
892
|
+
isEmpty: isEmpty,
|
|
893
|
+
hasMore: hasMore,
|
|
894
|
+
refetch: refetch,
|
|
895
|
+
loadMore: loadMore,
|
|
896
|
+
clearError: clearError,
|
|
897
|
+
getById: getById,
|
|
898
|
+
create: create,
|
|
899
|
+
update: update,
|
|
900
|
+
remove: remove,
|
|
901
|
+
createBatch: createBatch,
|
|
902
|
+
updateBatch: updateBatch,
|
|
903
|
+
removeBatch: removeBatch,
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
//# sourceMappingURL=useSPFxPnPList.js.map
|