@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,545 @@
|
|
|
1
|
+
import type { PnPContextInfo } from './useSPFxPnPContext';
|
|
2
|
+
import type { IItems } from '@pnp/sp/items';
|
|
3
|
+
import type { InitialFieldQuery, ComparisonResult } from '@pnp/sp/spqueryable';
|
|
4
|
+
/**
|
|
5
|
+
* Type representing a fluent filter function for type-safe query building.
|
|
6
|
+
* Uses native PnPjs v4 fluent filter API types.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of the list item
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* interface Task {
|
|
12
|
+
* Status: string;
|
|
13
|
+
* Priority: number;
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* const filterFn: ListFilterFunction<Task> = (f) =>
|
|
17
|
+
* f.text("Status").equals("Active")
|
|
18
|
+
* .and()
|
|
19
|
+
* .number("Priority").greaterThan(3);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export type ListFilterFunction<T> = (f: InitialFieldQuery<T>) => ComparisonResult<T>;
|
|
23
|
+
/**
|
|
24
|
+
* Options for configuring the useSPFxPnPList hook.
|
|
25
|
+
*/
|
|
26
|
+
export interface UseSPFxPnPListOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Page size for pagination (number of items per page).
|
|
29
|
+
* Used by `loadMore()` for automatic pagination.
|
|
30
|
+
*
|
|
31
|
+
* @default 100
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* const { query, loadMore } = useSPFxPnPList('Tasks', { pageSize: 50 });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
pageSize?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Return type for the useSPFxPnPList hook.
|
|
41
|
+
*
|
|
42
|
+
* @template T - The type of the list item
|
|
43
|
+
*/
|
|
44
|
+
export interface SPFxPnPListInfo<T = unknown> {
|
|
45
|
+
/**
|
|
46
|
+
* Executes a query against the SharePoint list using PnPjs fluent API.
|
|
47
|
+
*
|
|
48
|
+
* The hook automatically detects if `.top()` is called in the queryBuilder:
|
|
49
|
+
* - If `.top()` is specified → uses that value
|
|
50
|
+
* - If no `.top()` → uses `pageSize` option (if provided)
|
|
51
|
+
* - If neither → no limit (SharePoint default ~100-5000)
|
|
52
|
+
*
|
|
53
|
+
* **Note**: If both `.top()` and `pageSize` are specified, `.top()` takes precedence
|
|
54
|
+
* and a warning is logged to the console.
|
|
55
|
+
*
|
|
56
|
+
* @param queryBuilder - Function to build the query using PnPjs fluent API
|
|
57
|
+
* @param options - Query options (pageSize for pagination)
|
|
58
|
+
* @returns Promise resolving to the array of items
|
|
59
|
+
*
|
|
60
|
+
* @example Basic query with pageSize
|
|
61
|
+
* ```tsx
|
|
62
|
+
* const { query, items } = useSPFxPnPList('Tasks', { pageSize: 50 });
|
|
63
|
+
*
|
|
64
|
+
* await query(q =>
|
|
65
|
+
* q.select('Id', 'Title', 'Status')
|
|
66
|
+
* .filter("Status eq 'Active'")
|
|
67
|
+
* .orderBy('Created', false)
|
|
68
|
+
* );
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @example Query with explicit .top()
|
|
72
|
+
* ```tsx
|
|
73
|
+
* await query(q =>
|
|
74
|
+
* q.select('Id', 'Title')
|
|
75
|
+
* .top(100) // Explicit top takes precedence
|
|
76
|
+
* .orderBy('Priority', false)
|
|
77
|
+
* );
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @example Query without pagination
|
|
81
|
+
* ```tsx
|
|
82
|
+
* await query(q => q.select('Id', 'Title'));
|
|
83
|
+
* // Uses SharePoint default limit
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
query: (queryBuilder?: (items: IItems) => IItems, options?: {
|
|
87
|
+
pageSize?: number;
|
|
88
|
+
}) => Promise<T[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Array of list items returned from the last query.
|
|
91
|
+
*/
|
|
92
|
+
items: T[];
|
|
93
|
+
/**
|
|
94
|
+
* Indicates if a query is in progress.
|
|
95
|
+
*/
|
|
96
|
+
loading: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Indicates if additional items are being loaded via loadMore().
|
|
99
|
+
*/
|
|
100
|
+
loadingMore: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Error object if an error occurred during any operation.
|
|
103
|
+
*/
|
|
104
|
+
error: Error | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* Indicates if the items array is empty.
|
|
107
|
+
*/
|
|
108
|
+
isEmpty: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Indicates if there are more items available to load.
|
|
111
|
+
* Only meaningful when using pagination (pageSize or .top()).
|
|
112
|
+
*/
|
|
113
|
+
hasMore: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Re-executes the last query with the same parameters.
|
|
116
|
+
* Resets pagination state.
|
|
117
|
+
*
|
|
118
|
+
* @returns Promise resolving when refetch is complete
|
|
119
|
+
* @throws Error if no previous query was executed
|
|
120
|
+
*/
|
|
121
|
+
refetch: () => Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Loads the next page of items using the last query.
|
|
124
|
+
* Automatically appends new items to the existing items array.
|
|
125
|
+
*
|
|
126
|
+
* @returns Promise resolving to the newly loaded items
|
|
127
|
+
* @throws Error if no previous query was executed
|
|
128
|
+
* @throws Error if no pageSize was specified in the previous query
|
|
129
|
+
*/
|
|
130
|
+
loadMore: () => Promise<T[]>;
|
|
131
|
+
/**
|
|
132
|
+
* Clears the current error state.
|
|
133
|
+
*/
|
|
134
|
+
clearError: () => void;
|
|
135
|
+
/**
|
|
136
|
+
* Retrieves a single list item by ID.
|
|
137
|
+
*
|
|
138
|
+
* @param id - The ID of the list item to retrieve
|
|
139
|
+
* @returns Promise resolving to the item or undefined if not found
|
|
140
|
+
*/
|
|
141
|
+
getById: (id: number) => Promise<T | undefined>;
|
|
142
|
+
/**
|
|
143
|
+
* Creates a new list item.
|
|
144
|
+
* Automatically triggers a refetch after successful creation.
|
|
145
|
+
*
|
|
146
|
+
* @param item - Partial object containing the fields to set
|
|
147
|
+
* @returns Promise resolving to the ID of the created item
|
|
148
|
+
*/
|
|
149
|
+
create: (item: Partial<T>) => Promise<number>;
|
|
150
|
+
/**
|
|
151
|
+
* Updates an existing list item by ID.
|
|
152
|
+
* Automatically triggers a refetch after successful update.
|
|
153
|
+
*
|
|
154
|
+
* @param id - The ID of the list item to update
|
|
155
|
+
* @param item - Partial object containing the fields to update
|
|
156
|
+
*/
|
|
157
|
+
update: (id: number, item: Partial<T>) => Promise<void>;
|
|
158
|
+
/**
|
|
159
|
+
* Deletes a list item by ID.
|
|
160
|
+
* Automatically triggers a refetch after successful deletion.
|
|
161
|
+
*
|
|
162
|
+
* @param id - The ID of the list item to delete
|
|
163
|
+
*/
|
|
164
|
+
remove: (id: number) => Promise<void>;
|
|
165
|
+
/**
|
|
166
|
+
* Creates multiple list items in a single batched request.
|
|
167
|
+
* Automatically triggers a refetch after successful batch creation.
|
|
168
|
+
*
|
|
169
|
+
* @param items - Array of partial objects to create
|
|
170
|
+
* @returns Promise resolving to an array of created item IDs
|
|
171
|
+
*/
|
|
172
|
+
createBatch: (items: Partial<T>[]) => Promise<number[]>;
|
|
173
|
+
/**
|
|
174
|
+
* Updates multiple list items in a single batched request.
|
|
175
|
+
* Automatically triggers a refetch after successful batch update.
|
|
176
|
+
*
|
|
177
|
+
* @param updates - Array of objects containing id and item fields to update
|
|
178
|
+
*/
|
|
179
|
+
updateBatch: (updates: Array<{
|
|
180
|
+
id: number;
|
|
181
|
+
item: Partial<T>;
|
|
182
|
+
}>) => Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Deletes multiple list items in a single batched request.
|
|
185
|
+
* Automatically triggers a refetch after successful batch deletion.
|
|
186
|
+
*
|
|
187
|
+
* @param ids - Array of item IDs to delete
|
|
188
|
+
*/
|
|
189
|
+
removeBatch: (ids: number[]) => Promise<void>;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Hook for working with SharePoint lists using PnPjs fluent API.
|
|
193
|
+
* Provides query execution with automatic .top() detection, CRUD operations, pagination, and state management.
|
|
194
|
+
*
|
|
195
|
+
* **Key Features**:
|
|
196
|
+
* - ✅ Native PnPjs fluent API - full type-safe query building
|
|
197
|
+
* - ✅ Smart .top() detection - no conflicts between user .top() and pageSize
|
|
198
|
+
* - ✅ CRUD operations (create, read, update, delete)
|
|
199
|
+
* - ✅ Batch operations for bulk updates
|
|
200
|
+
* - ✅ Pagination with `loadMore()` and `hasMore`
|
|
201
|
+
* - ✅ Automatic refetch after CRUD operations
|
|
202
|
+
* - ✅ Local state management per component instance
|
|
203
|
+
* - ✅ Cross-site support via PnPContextInfo
|
|
204
|
+
* - ✅ ES5 compatibility (IE11 support)
|
|
205
|
+
*
|
|
206
|
+
* **How .top() Detection Works**:
|
|
207
|
+
* The hook uses a recursive Proxy to detect if `.top()` is called in your queryBuilder:
|
|
208
|
+
* - If `.top()` is specified → uses that value
|
|
209
|
+
* - If no `.top()` but `pageSize` option → adds `.top(pageSize)` automatically
|
|
210
|
+
* - If neither → no limit (SharePoint default ~100-5000)
|
|
211
|
+
* - If both → `.top()` wins, warning logged
|
|
212
|
+
*
|
|
213
|
+
* @template T - The type of the list item (default: unknown)
|
|
214
|
+
* @param listTitle - The title of the SharePoint list
|
|
215
|
+
* @param options - Optional configuration (pageSize for pagination)
|
|
216
|
+
* @param pnpContext - Optional PnP context for cross-site scenarios
|
|
217
|
+
* @returns Object containing query method, items, loading states, error, and CRUD operations
|
|
218
|
+
*
|
|
219
|
+
* @example Basic usage with pageSize
|
|
220
|
+
* ```tsx
|
|
221
|
+
* import { useSPFxPnPList } from '@apvee/spfx-react-toolkit';
|
|
222
|
+
*
|
|
223
|
+
* function TaskList() {
|
|
224
|
+
* const { query, items, loading, error } = useSPFxPnPList('Tasks', { pageSize: 50 });
|
|
225
|
+
*
|
|
226
|
+
* useEffect(() => {
|
|
227
|
+
* query(q =>
|
|
228
|
+
* q.select('Id', 'Title', 'Status', 'Priority')
|
|
229
|
+
* .filter("Status eq 'Active'")
|
|
230
|
+
* .orderBy('Priority', false)
|
|
231
|
+
* );
|
|
232
|
+
* }, [query]);
|
|
233
|
+
*
|
|
234
|
+
* if (loading) return <Spinner />;
|
|
235
|
+
* if (error) return <MessageBar>Error: {error.message}</MessageBar>;
|
|
236
|
+
*
|
|
237
|
+
* return (
|
|
238
|
+
* <ul>
|
|
239
|
+
* {items.map(task => (
|
|
240
|
+
* <li key={task.Id}>{task.Title} - {task.Status}</li>
|
|
241
|
+
* ))}
|
|
242
|
+
* </ul>
|
|
243
|
+
* );
|
|
244
|
+
* }
|
|
245
|
+
* ```
|
|
246
|
+
*
|
|
247
|
+
* @example Type-safe fluent filter
|
|
248
|
+
* ```tsx
|
|
249
|
+
* interface Task {
|
|
250
|
+
* Id: number;
|
|
251
|
+
* Title: string;
|
|
252
|
+
* Status: string;
|
|
253
|
+
* Priority: number;
|
|
254
|
+
* DueDate: string;
|
|
255
|
+
* }
|
|
256
|
+
*
|
|
257
|
+
* function ActiveHighPriorityTasks() {
|
|
258
|
+
* const { query, items, loading } = useSPFxPnPList<Task>('Tasks', { pageSize: 50 });
|
|
259
|
+
*
|
|
260
|
+
* useEffect(() => {
|
|
261
|
+
* query(q =>
|
|
262
|
+
* q.select('Id', 'Title', 'Priority', 'DueDate')
|
|
263
|
+
* .filter(f => f.text("Status").equals("Active")
|
|
264
|
+
* .and()
|
|
265
|
+
* .number("Priority").greaterThan(3))
|
|
266
|
+
* .orderBy('DueDate', true)
|
|
267
|
+
* );
|
|
268
|
+
* }, [query]);
|
|
269
|
+
*
|
|
270
|
+
* return (
|
|
271
|
+
* <div>
|
|
272
|
+
* {items.map(task => (
|
|
273
|
+
* <TaskCard key={task.Id} task={task} />
|
|
274
|
+
* ))}
|
|
275
|
+
* </div>
|
|
276
|
+
* );
|
|
277
|
+
* }
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* @example Query with explicit .top()
|
|
281
|
+
* ```tsx
|
|
282
|
+
* const { query, items } = useSPFxPnPList<Task>('Tasks');
|
|
283
|
+
*
|
|
284
|
+
* useEffect(() => {
|
|
285
|
+
* // User specifies .top() explicitly - takes precedence
|
|
286
|
+
* query(q =>
|
|
287
|
+
* q.select('Id', 'Title')
|
|
288
|
+
* .top(100) // Hook detects this and uses it
|
|
289
|
+
* .orderBy('Created', false)
|
|
290
|
+
* );
|
|
291
|
+
* }, [query]);
|
|
292
|
+
* ```
|
|
293
|
+
*
|
|
294
|
+
* @example CRUD operations
|
|
295
|
+
* ```tsx
|
|
296
|
+
* function TaskManager() {
|
|
297
|
+
* const { items, create, update, remove, loading } = useSPFxPnPList<Task>('Tasks');
|
|
298
|
+
*
|
|
299
|
+
* const handleCreate = async () => {
|
|
300
|
+
* try {
|
|
301
|
+
* const newId = await create({
|
|
302
|
+
* Title: 'New Task',
|
|
303
|
+
* Status: 'Active',
|
|
304
|
+
* Priority: 3
|
|
305
|
+
* });
|
|
306
|
+
* console.log('Created task with ID:', newId);
|
|
307
|
+
* // List automatically refetches
|
|
308
|
+
* } catch (error) {
|
|
309
|
+
* console.error('Create failed:', error);
|
|
310
|
+
* }
|
|
311
|
+
* };
|
|
312
|
+
*
|
|
313
|
+
* const handleUpdate = async (id: number) => {
|
|
314
|
+
* await update(id, { Status: 'Completed' });
|
|
315
|
+
* // List automatically refetches
|
|
316
|
+
* };
|
|
317
|
+
*
|
|
318
|
+
* const handleDelete = async (id: number) => {
|
|
319
|
+
* await remove(id);
|
|
320
|
+
* // List automatically refetches
|
|
321
|
+
* };
|
|
322
|
+
*
|
|
323
|
+
* return (
|
|
324
|
+
* <div>
|
|
325
|
+
* <button onClick={handleCreate}>Create Task</button>
|
|
326
|
+
* {items.map(task => (
|
|
327
|
+
* <div key={task.Id}>
|
|
328
|
+
* <span>{task.Title}</span>
|
|
329
|
+
* <button onClick={() => handleUpdate(task.Id)}>Complete</button>
|
|
330
|
+
* <button onClick={() => handleDelete(task.Id)}>Delete</button>
|
|
331
|
+
* </div>
|
|
332
|
+
* ))}
|
|
333
|
+
* </div>
|
|
334
|
+
* );
|
|
335
|
+
* }
|
|
336
|
+
* ```
|
|
337
|
+
*
|
|
338
|
+
* @example Batch operations for bulk updates
|
|
339
|
+
* ```tsx
|
|
340
|
+
* function BulkTaskManager() {
|
|
341
|
+
* const { createBatch, updateBatch, removeBatch } = useSPFxPnPList<Task>('Tasks');
|
|
342
|
+
*
|
|
343
|
+
* const handleBulkCreate = async () => {
|
|
344
|
+
* const ids = await createBatch([
|
|
345
|
+
* { Title: 'Task 1', Status: 'Active', Priority: 1 },
|
|
346
|
+
* { Title: 'Task 2', Status: 'Active', Priority: 2 },
|
|
347
|
+
* { Title: 'Task 3', Status: 'Active', Priority: 3 }
|
|
348
|
+
* ]);
|
|
349
|
+
* console.log('Created task IDs:', ids);
|
|
350
|
+
* };
|
|
351
|
+
*
|
|
352
|
+
* const handleBulkUpdate = async (taskIds: number[]) => {
|
|
353
|
+
* await updateBatch([
|
|
354
|
+
* { id: taskIds[0], item: { Status: 'Completed' } },
|
|
355
|
+
* { id: taskIds[1], item: { Priority: 5 } },
|
|
356
|
+
* { id: taskIds[2], item: { Status: 'In Progress', Priority: 4 } }
|
|
357
|
+
* ]);
|
|
358
|
+
* };
|
|
359
|
+
*
|
|
360
|
+
* const handleBulkDelete = async (taskIds: number[]) => {
|
|
361
|
+
* await removeBatch(taskIds);
|
|
362
|
+
* };
|
|
363
|
+
*
|
|
364
|
+
* return (
|
|
365
|
+
* <div>
|
|
366
|
+
* <button onClick={handleBulkCreate}>Create 3 Tasks</button>
|
|
367
|
+
* </div>
|
|
368
|
+
* );
|
|
369
|
+
* }
|
|
370
|
+
* ```
|
|
371
|
+
*
|
|
372
|
+
* @example Pagination with loadMore
|
|
373
|
+
* ```tsx
|
|
374
|
+
* function InfiniteTaskList() {
|
|
375
|
+
* const {
|
|
376
|
+
* query,
|
|
377
|
+
* items,
|
|
378
|
+
* loading,
|
|
379
|
+
* loadingMore,
|
|
380
|
+
* hasMore,
|
|
381
|
+
* loadMore,
|
|
382
|
+
* isEmpty
|
|
383
|
+
* } = useSPFxPnPList<Task>('Tasks', { pageSize: 50 });
|
|
384
|
+
*
|
|
385
|
+
* useEffect(() => {
|
|
386
|
+
* query(q =>
|
|
387
|
+
* q.select('Id', 'Title', 'Status')
|
|
388
|
+
* .orderBy('Created', false)
|
|
389
|
+
* );
|
|
390
|
+
* }, [query]);
|
|
391
|
+
*
|
|
392
|
+
* if (loading) return <Spinner label="Loading tasks..." />;
|
|
393
|
+
* if (isEmpty) return <MessageBar>No tasks found</MessageBar>;
|
|
394
|
+
*
|
|
395
|
+
* return (
|
|
396
|
+
* <div>
|
|
397
|
+
* {items.map(task => (
|
|
398
|
+
* <TaskCard key={task.Id} task={task} />
|
|
399
|
+
* ))}
|
|
400
|
+
*
|
|
401
|
+
* {hasMore && (
|
|
402
|
+
* <PrimaryButton
|
|
403
|
+
* text={loadingMore ? 'Loading...' : 'Load More'}
|
|
404
|
+
* onClick={loadMore}
|
|
405
|
+
* disabled={loadingMore}
|
|
406
|
+
* />
|
|
407
|
+
* )}
|
|
408
|
+
* </div>
|
|
409
|
+
* );
|
|
410
|
+
* }
|
|
411
|
+
* ```
|
|
412
|
+
*
|
|
413
|
+
* @example Cross-site usage
|
|
414
|
+
* ```tsx
|
|
415
|
+
* function EmployeeList() {
|
|
416
|
+
* // Create context for HR site
|
|
417
|
+
* const hrContext = useSPFxPnPContext({
|
|
418
|
+
* siteUrl: '/sites/hr'
|
|
419
|
+
* });
|
|
420
|
+
*
|
|
421
|
+
* // Query Employees list from HR site
|
|
422
|
+
* const { query, items, loading } = useSPFxPnPList<Employee>(
|
|
423
|
+
* 'Employees',
|
|
424
|
+
* { pageSize: 100 },
|
|
425
|
+
* hrContext // Pass context for cross-site query
|
|
426
|
+
* );
|
|
427
|
+
*
|
|
428
|
+
* useEffect(() => {
|
|
429
|
+
* query(q =>
|
|
430
|
+
* q.select('Id', 'Name', 'Department', 'Email')
|
|
431
|
+
* .filter("Department eq 'Engineering'")
|
|
432
|
+
* .orderBy('Name', true)
|
|
433
|
+
* );
|
|
434
|
+
* }, [query]);
|
|
435
|
+
*
|
|
436
|
+
* return (
|
|
437
|
+
* <div>
|
|
438
|
+
* {items.map(emp => (
|
|
439
|
+
* <PersonaCard key={emp.Id} employee={emp} />
|
|
440
|
+
* ))}
|
|
441
|
+
* </div>
|
|
442
|
+
* );
|
|
443
|
+
* }
|
|
444
|
+
* ```
|
|
445
|
+
*
|
|
446
|
+
* @example Manual refetch with error handling
|
|
447
|
+
* ```tsx
|
|
448
|
+
* function TaskListWithRefresh() {
|
|
449
|
+
* const { query, items, loading, error, refetch, clearError } = useSPFxPnPList<Task>('Tasks', { pageSize: 50 });
|
|
450
|
+
*
|
|
451
|
+
* useEffect(() => {
|
|
452
|
+
* query(q => q.select('Id', 'Title', 'Status').orderBy('Created', false));
|
|
453
|
+
* }, [query]);
|
|
454
|
+
*
|
|
455
|
+
* return (
|
|
456
|
+
* <div>
|
|
457
|
+
* <CommandBar
|
|
458
|
+
* items={[
|
|
459
|
+
* {
|
|
460
|
+
* key: 'refresh',
|
|
461
|
+
* text: 'Refresh',
|
|
462
|
+
* iconProps: { iconName: 'Refresh' },
|
|
463
|
+
* onClick: () => refetch()
|
|
464
|
+
* }
|
|
465
|
+
* ]}
|
|
466
|
+
* />
|
|
467
|
+
*
|
|
468
|
+
* {error && (
|
|
469
|
+
* <MessageBar
|
|
470
|
+
* messageBarType={MessageBarType.error}
|
|
471
|
+
* onDismiss={clearError}
|
|
472
|
+
* >
|
|
473
|
+
* Error loading tasks: {error.message}
|
|
474
|
+
* </MessageBar>
|
|
475
|
+
* )}
|
|
476
|
+
*
|
|
477
|
+
* {loading ? (
|
|
478
|
+
* <Spinner />
|
|
479
|
+
* ) : (
|
|
480
|
+
* <DetailsList items={items} />
|
|
481
|
+
* )}
|
|
482
|
+
* </div>
|
|
483
|
+
* );
|
|
484
|
+
* }
|
|
485
|
+
* ```
|
|
486
|
+
*
|
|
487
|
+
* @example Conditional loading (autoLoad: false)
|
|
488
|
+
* ```tsx
|
|
489
|
+
* function ConditionalTaskList() {
|
|
490
|
+
* const [showCompleted, setShowCompleted] = useState(false);
|
|
491
|
+
* const { items, loading, refetch } = useSPFxPnPList<Task>(
|
|
492
|
+
* 'Tasks',
|
|
493
|
+
* {
|
|
494
|
+
* filter: showCompleted ? "Status eq 'Completed'" : "Status eq 'Active'",
|
|
495
|
+
* autoLoad: false // Don't load on mount
|
|
496
|
+
* }
|
|
497
|
+
* );
|
|
498
|
+
*
|
|
499
|
+
* useEffect(() => {
|
|
500
|
+
* if (showCompleted) {
|
|
501
|
+
* refetch(); // Manually trigger load
|
|
502
|
+
* }
|
|
503
|
+
* }, [showCompleted, refetch]);
|
|
504
|
+
*
|
|
505
|
+
* return (
|
|
506
|
+
* <div>
|
|
507
|
+
* <Toggle
|
|
508
|
+
* label="Show Completed"
|
|
509
|
+
* checked={showCompleted}
|
|
510
|
+
* onChange={(_, checked) => setShowCompleted(checked || false)}
|
|
511
|
+
* />
|
|
512
|
+
* {items.map(task => <TaskCard key={task.Id} task={task} />)}
|
|
513
|
+
* </div>
|
|
514
|
+
* );
|
|
515
|
+
* }
|
|
516
|
+
* ```
|
|
517
|
+
*
|
|
518
|
+
* @remarks
|
|
519
|
+
* **PnPjs Installation**: This hook requires `@pnp/sp` to be installed:
|
|
520
|
+
* ```bash
|
|
521
|
+
* npm install @pnp/sp @pnp/core @pnp/queryable
|
|
522
|
+
* ```
|
|
523
|
+
*
|
|
524
|
+
* **SharePoint List View Threshold**: Be aware that querying lists with more than 5000 items
|
|
525
|
+
* may cause throttling unless:
|
|
526
|
+
* - Filters use indexed columns
|
|
527
|
+
* - Query results are under 5000 items
|
|
528
|
+
* - Proper pagination is used (top + skip)
|
|
529
|
+
*
|
|
530
|
+
* **Fluent Filter Requirements**: To use type-safe fluent filters, import types:
|
|
531
|
+
* ```typescript
|
|
532
|
+
* import '@pnp/sp/items'; // Enables fluent filter on items
|
|
533
|
+
* ```
|
|
534
|
+
*
|
|
535
|
+
* **State Management**: Each hook instance maintains its own local state (items, loading, error).
|
|
536
|
+
* State is not shared between components - this follows the standard React hooks pattern.
|
|
537
|
+
*
|
|
538
|
+
* **Debounced Refetch**: CRUD operations trigger a debounced refetch (100ms delay) to prevent
|
|
539
|
+
* race conditions when multiple operations occur in quick succession.
|
|
540
|
+
*
|
|
541
|
+
* @see {@link useSPFxPnPContext} for creating PnP contexts
|
|
542
|
+
* @see {@link PNPContextInfo} for context information
|
|
543
|
+
*/
|
|
544
|
+
export declare function useSPFxPnPList<T = unknown>(listTitle: string, options?: UseSPFxPnPListOptions, pnpContext?: PnPContextInfo): SPFxPnPListInfo<T>;
|
|
545
|
+
//# sourceMappingURL=useSPFxPnPList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSPFxPnPList.d.ts","sourceRoot":"","sources":["../../src/hooks/useSPFxPnPList.ts"],"names":[],"mappings":"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"}
|