@blueprint-ts/core 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.
Files changed (158) hide show
  1. package/.editorconfig +508 -0
  2. package/.eslintrc.cjs +15 -0
  3. package/.prettierrc.json +8 -0
  4. package/LICENSE +21 -0
  5. package/README.md +1 -0
  6. package/docker-compose.yaml +8 -0
  7. package/docs/.vitepress/config.ts +68 -0
  8. package/docs/.vitepress/theme/Layout.vue +14 -0
  9. package/docs/.vitepress/theme/components/VersionSelector.vue +64 -0
  10. package/docs/.vitepress/theme/index.js +13 -0
  11. package/docs/index.md +70 -0
  12. package/docs/services/laravel/pagination.md +54 -0
  13. package/docs/services/laravel/requests.md +62 -0
  14. package/docs/services/requests/index.md +74 -0
  15. package/docs/vue/forms.md +326 -0
  16. package/docs/vue/requests/route-model-binding.md +66 -0
  17. package/docs/vue/state.md +293 -0
  18. package/env.d.ts +1 -0
  19. package/eslint.config.js +15 -0
  20. package/examples/files/7z2404-x64.exe +0 -0
  21. package/examples/index.html +14 -0
  22. package/examples/js/app.js +8 -0
  23. package/examples/js/router.js +22 -0
  24. package/examples/js/view/App.vue +49 -0
  25. package/examples/js/view/layout/DemoPage.vue +28 -0
  26. package/examples/js/view/pagination/Pagination.vue +28 -0
  27. package/examples/js/view/pagination/components/errorPagination/ErrorPagination.vue +71 -0
  28. package/examples/js/view/pagination/components/errorPagination/GetProductsRequest.ts +54 -0
  29. package/examples/js/view/pagination/components/infiniteScrolling/GetProductsRequest.ts +50 -0
  30. package/examples/js/view/pagination/components/infiniteScrolling/InfiniteScrolling.vue +57 -0
  31. package/examples/js/view/pagination/components/tablePagination/GetProductsRequest.ts +50 -0
  32. package/examples/js/view/pagination/components/tablePagination/TablePagination.vue +63 -0
  33. package/examples/js/view/requests/Requests.vue +34 -0
  34. package/examples/js/view/requests/components/abortableRequest/AbortableRequest.vue +36 -0
  35. package/examples/js/view/requests/components/abortableRequest/GetProductsRequest.ts +25 -0
  36. package/examples/js/view/requests/components/fileDownloadRequest/DownloadFileRequest.ts +15 -0
  37. package/examples/js/view/requests/components/fileDownloadRequest/FileDownloadRequest.vue +44 -0
  38. package/examples/js/view/requests/components/getRequestWithDynamicParams/GetProductsRequest.ts +34 -0
  39. package/examples/js/view/requests/components/getRequestWithDynamicParams/GetRequestWithDynamicParams.vue +59 -0
  40. package/examples/js/view/requests/components/serverErrorRequest/ServerErrorRequest.ts +21 -0
  41. package/examples/js/view/requests/components/serverErrorRequest/ServerErrorRequest.vue +53 -0
  42. package/package.json +81 -0
  43. package/release-tool.json +7 -0
  44. package/src/helpers.ts +78 -0
  45. package/src/service/bulkRequests/BulkRequestEvent.enum.ts +4 -0
  46. package/src/service/bulkRequests/BulkRequestSender.ts +184 -0
  47. package/src/service/bulkRequests/BulkRequestWrapper.ts +49 -0
  48. package/src/service/bulkRequests/index.ts +6 -0
  49. package/src/service/laravel/pagination/contracts/PaginationParamsContract.ts +4 -0
  50. package/src/service/laravel/pagination/contracts/PaginationResponseBodyContract.ts +6 -0
  51. package/src/service/laravel/pagination/dataDrivers/RequestDriver.ts +32 -0
  52. package/src/service/laravel/pagination/index.ts +7 -0
  53. package/src/service/laravel/requests/JsonBaseRequest.ts +35 -0
  54. package/src/service/laravel/requests/PaginationJsonBaseRequest.ts +29 -0
  55. package/src/service/laravel/requests/index.ts +9 -0
  56. package/src/service/laravel/requests/responses/JsonResponse.ts +8 -0
  57. package/src/service/laravel/requests/responses/PaginationResponse.ts +16 -0
  58. package/src/service/pagination/InfiniteScroller.ts +21 -0
  59. package/src/service/pagination/Paginator.ts +149 -0
  60. package/src/service/pagination/contracts/PaginateableRequestContract.ts +13 -0
  61. package/src/service/pagination/contracts/PaginationDataDriverContract.ts +5 -0
  62. package/src/service/pagination/contracts/PaginationResponseContract.ts +7 -0
  63. package/src/service/pagination/contracts/PaginatorLoadDataOptions.ts +4 -0
  64. package/src/service/pagination/contracts/ViewDriverContract.ts +12 -0
  65. package/src/service/pagination/contracts/ViewDriverFactoryContract.ts +5 -0
  66. package/src/service/pagination/dataDrivers/ArrayDriver.ts +28 -0
  67. package/src/service/pagination/dtos/PaginationDataDto.ts +14 -0
  68. package/src/service/pagination/factories/VuePaginationDriverFactory.ts +9 -0
  69. package/src/service/pagination/frontendDrivers/VuePaginationDriver.ts +61 -0
  70. package/src/service/pagination/index.ts +16 -0
  71. package/src/service/persistenceDrivers/LocalStorageDriver.ts +22 -0
  72. package/src/service/persistenceDrivers/NonPersistentDriver.ts +12 -0
  73. package/src/service/persistenceDrivers/SessionStorageDriver.ts +22 -0
  74. package/src/service/persistenceDrivers/index.ts +8 -0
  75. package/src/service/persistenceDrivers/types/PersistenceDriver.ts +5 -0
  76. package/src/service/requests/BaseRequest.ts +197 -0
  77. package/src/service/requests/ErrorHandler.ts +64 -0
  78. package/src/service/requests/RequestEvents.enum.ts +3 -0
  79. package/src/service/requests/RequestMethod.enum.ts +8 -0
  80. package/src/service/requests/bodies/FormDataBody.ts +41 -0
  81. package/src/service/requests/bodies/JsonBody.ts +16 -0
  82. package/src/service/requests/contracts/AbortableRequestContract.ts +3 -0
  83. package/src/service/requests/contracts/BaseRequestContract.ts +36 -0
  84. package/src/service/requests/contracts/BodyContract.ts +7 -0
  85. package/src/service/requests/contracts/BodyFactoryContract.ts +5 -0
  86. package/src/service/requests/contracts/DriverConfigContract.ts +7 -0
  87. package/src/service/requests/contracts/HeadersContract.ts +5 -0
  88. package/src/service/requests/contracts/RequestDriverContract.ts +15 -0
  89. package/src/service/requests/contracts/RequestLoaderContract.ts +5 -0
  90. package/src/service/requests/contracts/RequestLoaderFactoryContract.ts +5 -0
  91. package/src/service/requests/contracts/ResponseContract.ts +7 -0
  92. package/src/service/requests/drivers/contracts/ResponseHandlerContract.ts +10 -0
  93. package/src/service/requests/drivers/fetch/FetchDriver.ts +115 -0
  94. package/src/service/requests/drivers/fetch/FetchResponse.ts +30 -0
  95. package/src/service/requests/exceptions/NoResponseReceivedException.ts +3 -0
  96. package/src/service/requests/exceptions/NotFoundException.ts +3 -0
  97. package/src/service/requests/exceptions/PageExpiredException.ts +3 -0
  98. package/src/service/requests/exceptions/ResponseBodyException.ts +15 -0
  99. package/src/service/requests/exceptions/ResponseException.ts +11 -0
  100. package/src/service/requests/exceptions/ServerErrorException.ts +3 -0
  101. package/src/service/requests/exceptions/UnauthorizedException.ts +3 -0
  102. package/src/service/requests/exceptions/ValidationException.ts +3 -0
  103. package/src/service/requests/exceptions/index.ts +19 -0
  104. package/src/service/requests/factories/FormDataFactory.ts +9 -0
  105. package/src/service/requests/factories/JsonBodyFactory.ts +9 -0
  106. package/src/service/requests/index.ts +50 -0
  107. package/src/service/requests/responses/BaseResponse.ts +41 -0
  108. package/src/service/requests/responses/BlobResponse.ts +19 -0
  109. package/src/service/requests/responses/JsonResponse.ts +15 -0
  110. package/src/service/requests/responses/PlainTextResponse.ts +15 -0
  111. package/src/service/support/DeferredPromise.ts +67 -0
  112. package/src/service/support/index.ts +3 -0
  113. package/src/vue/composables/useConfirmDialog.ts +59 -0
  114. package/src/vue/composables/useGlobalCheckbox.ts +145 -0
  115. package/src/vue/composables/useIsEmpty.ts +34 -0
  116. package/src/vue/composables/useIsOpen.ts +37 -0
  117. package/src/vue/composables/useIsOpenFromVar.ts +61 -0
  118. package/src/vue/composables/useModelWrapper.ts +24 -0
  119. package/src/vue/composables/useOnOpen.ts +34 -0
  120. package/src/vue/contracts/ModelValueOptions.ts +3 -0
  121. package/src/vue/contracts/ModelValueProps.ts +3 -0
  122. package/src/vue/forms/BaseForm.ts +1074 -0
  123. package/src/vue/forms/PropertyAwareArray.ts +78 -0
  124. package/src/vue/forms/index.ts +11 -0
  125. package/src/vue/forms/types/PersistedForm.ts +6 -0
  126. package/src/vue/forms/validation/ValidationMode.enum.ts +14 -0
  127. package/src/vue/forms/validation/index.ts +12 -0
  128. package/src/vue/forms/validation/rules/BaseRule.ts +7 -0
  129. package/src/vue/forms/validation/rules/ConfirmedRule.ts +39 -0
  130. package/src/vue/forms/validation/rules/MinRule.ts +61 -0
  131. package/src/vue/forms/validation/rules/RequiredRule.ts +19 -0
  132. package/src/vue/forms/validation/rules/UrlRule.ts +24 -0
  133. package/src/vue/forms/validation/types/BidirectionalRule.ts +11 -0
  134. package/src/vue/index.ts +14 -0
  135. package/src/vue/requests/factories/VueRequestLoaderFactory.ts +9 -0
  136. package/src/vue/requests/index.ts +5 -0
  137. package/src/vue/requests/loaders/VueRequestBatchLoader.ts +30 -0
  138. package/src/vue/requests/loaders/VueRequestLoader.ts +18 -0
  139. package/src/vue/router/routeModelBinding/RouteModelRequestResolver.ts +11 -0
  140. package/src/vue/router/routeModelBinding/defineRoute.ts +31 -0
  141. package/src/vue/router/routeModelBinding/index.ts +8 -0
  142. package/src/vue/router/routeModelBinding/installRouteInjection.ts +73 -0
  143. package/src/vue/router/routeModelBinding/types.ts +46 -0
  144. package/src/vue/state/State.ts +391 -0
  145. package/src/vue/state/index.ts +3 -0
  146. package/tests/service/helpers/mergeDeep.test.ts +53 -0
  147. package/tests/service/laravel/pagination/dataDrivers/RequestDriver.test.ts +84 -0
  148. package/tests/service/laravel/requests/JsonBaseRequest.test.ts +43 -0
  149. package/tests/service/laravel/requests/PaginationJsonBaseRequest.test.ts +58 -0
  150. package/tests/service/laravel/requests/responses/JsonResponse.test.ts +59 -0
  151. package/tests/service/laravel/requests/responses/PaginationResponse.test.ts +127 -0
  152. package/tests/service/pagination/dtos/PaginationDataDto.test.ts +35 -0
  153. package/tests/service/pagination/factories/VuePaginationDriverFactory.test.ts +32 -0
  154. package/tests/service/pagination/frontendDrivers/VuePaginationDriver.test.ts +66 -0
  155. package/tests/service/requests/ErrorHandler.test.ts +141 -0
  156. package/tsconfig.json +114 -0
  157. package/vite.config.ts +34 -0
  158. package/vitest.config.ts +14 -0
package/src/helpers.ts ADDED
@@ -0,0 +1,78 @@
1
+ export const getCookie = (cname: string) => {
2
+ const name = cname + '='
3
+ const decodedCookie = decodeURIComponent(document.cookie)
4
+ const ca = decodedCookie.split(';')
5
+ for (let i = 0; i < ca.length; i++) {
6
+ let c = ca[i]
7
+
8
+ if (c === undefined) {
9
+ return ''
10
+ }
11
+
12
+ while (c.charAt(0) == ' ') {
13
+ c = c.substring(1)
14
+ }
15
+ if (c.indexOf(name) == 0) {
16
+ return c.substring(name.length, c.length)
17
+ }
18
+ }
19
+ return ''
20
+ }
21
+
22
+ export const isObject = (item: unknown): boolean => {
23
+ if (!item) {
24
+ return false
25
+ }
26
+
27
+ if (Array.isArray(item)) {
28
+ return false
29
+ }
30
+
31
+ return typeof item === 'object'
32
+ }
33
+
34
+ export const mergeDeep = (target: object, ...sources: object[]): object => {
35
+ if (!sources.length) return target
36
+ const source = sources.shift()
37
+
38
+ if (isObject(target) && isObject(source)) {
39
+ for (const key in source) {
40
+ // @ts-expect-error unknown structure
41
+ if (isObject(source[key])) {
42
+ // @ts-expect-error unknown structure
43
+ if (!target[key]) Object.assign(target, { [key]: {} })
44
+ // @ts-expect-error unknown structure
45
+ mergeDeep(target[key], source[key])
46
+ } else {
47
+ // @ts-expect-error unknown structure
48
+ Object.assign(target, { [key]: source[key] })
49
+ }
50
+ }
51
+ }
52
+
53
+ return mergeDeep(target, ...sources)
54
+ }
55
+
56
+ export const getDisplayablePages = (totalPages: number, pageNumber: number, displayPages: number = 4) => {
57
+ if (totalPages < displayPages) {
58
+ displayPages = totalPages
59
+ }
60
+
61
+ let offset = 0
62
+
63
+ const middlePage = Math.ceil(displayPages / 2)
64
+
65
+ if (pageNumber <= middlePage) {
66
+ offset = 0
67
+ } else if (pageNumber > totalPages - middlePage) {
68
+ offset = totalPages - displayPages
69
+ } else {
70
+ offset = pageNumber - middlePage
71
+ }
72
+
73
+ return [...Array(displayPages).keys()].map((i) => i + 1 + offset)
74
+ }
75
+
76
+ export const isAtBottom = (scrollHeight: number, scrollTop: number, clientHeight: number): boolean => {
77
+ return Math.abs(scrollHeight - scrollTop - clientHeight) <= 3.0
78
+ }
@@ -0,0 +1,4 @@
1
+ export enum BulkRequestEventEnum {
2
+ REQUEST_SUCCESSFUL = 'REQUEST_SUCCESSFUL',
3
+ REQUEST_FAILED = 'REQUEST_FAILED'
4
+ }
@@ -0,0 +1,184 @@
1
+ import { BulkRequestWrapper } from './BulkRequestWrapper'
2
+ import { BulkRequestEventEnum } from './BulkRequestEvent.enum'
3
+ import { BaseRequest } from '../requests'
4
+
5
+ export enum BulkRequestExecutionMode {
6
+ PARALLEL = 'parallel',
7
+ SEQUENTIAL = 'sequential'
8
+ }
9
+
10
+ export class BulkRequestSender {
11
+ // @ts-expect-error
12
+ protected events: Map<BulkRequestEventEnum, ((req: BulkRequestWrapper<BaseRequest>) => void)[]> = new Map()
13
+ protected abortController: AbortController | undefined = undefined
14
+
15
+ public constructor(
16
+ // @ts-expect-error
17
+ protected requests: BulkRequestWrapper<BaseRequest>[] = [],
18
+ protected executionMode: BulkRequestExecutionMode = BulkRequestExecutionMode.PARALLEL,
19
+ protected retryCount: number = 0
20
+ ) {}
21
+
22
+ // @ts-expect-error
23
+ public setRequests(requests: BulkRequestWrapper<BaseRequest>[] = []) {
24
+ this.requests = requests
25
+
26
+ return this
27
+ }
28
+
29
+ public setExecutionMode(mode: BulkRequestExecutionMode): this {
30
+ this.executionMode = mode
31
+
32
+ return this
33
+ }
34
+
35
+ public setRetryCount(count: number): this {
36
+ this.retryCount = count
37
+
38
+ return this
39
+ }
40
+
41
+ public get isLoading(): boolean {
42
+ return this.requests.some((req) => req.isLoading())
43
+ }
44
+
45
+ // @ts-expect-error
46
+ public on(event: BulkRequestEventEnum, callback: (req: BulkRequestWrapper<BaseRequest>) => void): this {
47
+ if (!this.events.has(event)) {
48
+ this.events.set(event, [])
49
+ }
50
+
51
+ this.events.get(event)!.push(callback)
52
+
53
+ return this
54
+ }
55
+
56
+ public off(event: BulkRequestEventEnum): this {
57
+ this.events.delete(event)
58
+
59
+ return this
60
+ }
61
+
62
+ // @ts-expect-error
63
+ protected emit(event: BulkRequestEventEnum, req: BulkRequestWrapper<BaseRequest>): void {
64
+ const callbacks = this.events.get(event) || []
65
+
66
+ callbacks.forEach((callback) => callback(req))
67
+ }
68
+
69
+ public get signal(): AbortSignal | undefined {
70
+ return this.abortController?.signal
71
+ }
72
+
73
+ public abort(): void {
74
+ this.abortController?.abort()
75
+ }
76
+
77
+ public async send() {
78
+ this.abortController = new AbortController()
79
+
80
+ try {
81
+ if (this.executionMode === BulkRequestExecutionMode.PARALLEL) {
82
+ await this.sendParallel()
83
+ } else {
84
+ await this.sendSequential()
85
+ }
86
+ } catch (error) {
87
+ // If an abort occurs, the underlying fetch (or request mechanism) should throw an AbortError.
88
+ console.error('Bulk operation aborted or encountered an error:', error)
89
+ }
90
+
91
+ return {
92
+ getSuccessCount: () => this.requests.filter((r) => !r.getError()).length,
93
+ getErrorCount: () => this.requests.filter((r) => r.getError()).length,
94
+ getSuccessfulResponses: () => this.requests.filter((r) => !r.getError()).map((r) => r.getResponse()),
95
+ getFailedResponses: () => this.requests.filter((r) => r.getError()).map((r) => r.getError())
96
+ }
97
+ }
98
+
99
+ protected async sendParallel() {
100
+ // First attempt for all requests
101
+ await Promise.all(
102
+ this.requests.map((req) =>
103
+ req.send(this.abortController?.signal).then(() => {
104
+ if (!req.hasError()) {
105
+ this.emit(BulkRequestEventEnum.REQUEST_SUCCESSFUL, req)
106
+ }
107
+ })
108
+ )
109
+ )
110
+
111
+ // Retry logic for failed requests
112
+ let retriesLeft = this.retryCount
113
+ while (retriesLeft > 0) {
114
+ const failedRequests = this.requests.filter((req) => req.hasError())
115
+
116
+ if (failedRequests.length === 0) {
117
+ break // No failed requests to retry
118
+ }
119
+
120
+ console.log(`Retrying ${failedRequests.length} failed requests. Attempts left: ${retriesLeft}`)
121
+
122
+ await Promise.all(
123
+ failedRequests.map((req) =>
124
+ req.send(this.abortController?.signal).then(() => {
125
+ if (!req.hasError()) {
126
+ // Success after retry
127
+ this.emit(BulkRequestEventEnum.REQUEST_SUCCESSFUL, req)
128
+ }
129
+ })
130
+ )
131
+ )
132
+
133
+ retriesLeft--
134
+ }
135
+
136
+ // Emit failed events for any requests that still have errors after all retries
137
+ this.requests
138
+ .filter((req) => req.hasError())
139
+ .forEach((req) => {
140
+ this.emit(BulkRequestEventEnum.REQUEST_FAILED, req)
141
+ })
142
+ }
143
+
144
+ protected async sendSequential() {
145
+ // First attempt for all requests
146
+ for (const req of this.requests) {
147
+ await req.send(this.abortController?.signal)
148
+
149
+ if (!req.hasError()) {
150
+ this.emit(BulkRequestEventEnum.REQUEST_SUCCESSFUL, req)
151
+ }
152
+ }
153
+
154
+ // Retry logic for failed requests
155
+ let retriesLeft = this.retryCount
156
+ while (retriesLeft > 0) {
157
+ const failedRequests = this.requests.filter((req) => req.hasError())
158
+
159
+ if (failedRequests.length === 0) {
160
+ break // No failed requests to retry
161
+ }
162
+
163
+ console.log(`Retrying ${failedRequests.length} failed requests sequentially. Attempts left: ${retriesLeft}`)
164
+
165
+ for (const req of failedRequests) {
166
+ await req.send(this.abortController?.signal)
167
+
168
+ if (!req.hasError()) {
169
+ // Success after retry
170
+ this.emit(BulkRequestEventEnum.REQUEST_SUCCESSFUL, req)
171
+ }
172
+ }
173
+
174
+ retriesLeft--
175
+ }
176
+
177
+ // Emit failed events for any requests that still have errors after all retries
178
+ this.requests
179
+ .filter((req) => req.hasError())
180
+ .forEach((req) => {
181
+ this.emit(BulkRequestEventEnum.REQUEST_FAILED, req)
182
+ })
183
+ }
184
+ }
@@ -0,0 +1,49 @@
1
+ import { type BaseRequestContract } from '../requests'
2
+
3
+ export class BulkRequestWrapper<RequestLoaderLoadingType, RequestBodyInterface, ResponseClass, RequestParamsInterface extends object> {
4
+ protected response: any = null
5
+ protected error: any = null
6
+ protected sent: boolean = false
7
+
8
+ public constructor(protected request: BaseRequestContract<RequestLoaderLoadingType, RequestBodyInterface, ResponseClass, RequestParamsInterface>) {}
9
+
10
+ public async send(signal?: AbortSignal) {
11
+ try {
12
+ if (signal !== undefined) {
13
+ this.request.setAbortSignal(signal)
14
+ }
15
+
16
+ this.response = await this.request.send()
17
+ } catch (err) {
18
+ console.error(err)
19
+
20
+ this.error = err
21
+ }
22
+
23
+ this.sent = true
24
+ }
25
+
26
+ public isLoading(): RequestLoaderLoadingType {
27
+ return this.request.isLoading()
28
+ }
29
+
30
+ public getResponse() {
31
+ return this.response
32
+ }
33
+
34
+ public getError() {
35
+ return this.error
36
+ }
37
+
38
+ public getRequest(): BaseRequestContract<RequestLoaderLoadingType, RequestBodyInterface, ResponseClass, RequestParamsInterface> {
39
+ return this.request
40
+ }
41
+
42
+ public hasError(): boolean {
43
+ return Boolean(this.getError())
44
+ }
45
+
46
+ public wasSent(): boolean {
47
+ return this.sent
48
+ }
49
+ }
@@ -0,0 +1,6 @@
1
+ import { BulkRequestWrapper } from './BulkRequestWrapper'
2
+ import { BulkRequestSender } from './BulkRequestSender'
3
+ import { BulkRequestEventEnum } from './BulkRequestEvent.enum'
4
+ import { BulkRequestExecutionMode } from './BulkRequestSender'
5
+
6
+ export { BulkRequestEventEnum, BulkRequestWrapper, BulkRequestSender, BulkRequestExecutionMode }
@@ -0,0 +1,4 @@
1
+ export interface PaginationParamsContract {
2
+ page_size: number
3
+ page_number: number
4
+ }
@@ -0,0 +1,6 @@
1
+ export interface PaginationResponseBodyContract<ResourceInterface> {
2
+ data: ResourceInterface
3
+ meta: {
4
+ total: number
5
+ }
6
+ }
@@ -0,0 +1,32 @@
1
+ import { PaginationDataDto } from '../../../pagination/dtos/PaginationDataDto'
2
+ import { PaginationJsonBaseRequest } from '../../requests/PaginationJsonBaseRequest'
3
+ import { PaginationResponse } from '../../requests/responses/PaginationResponse'
4
+ import { type PaginateableRequestContract } from '../../../pagination/contracts/PaginateableRequestContract'
5
+ import { type PaginationDataDriverContract } from '../../../pagination/contracts/PaginationDataDriverContract'
6
+
7
+ type ExtractRequestTypes<T> =
8
+ T extends PaginationJsonBaseRequest<any, undefined, infer Resource, object>
9
+ ? {
10
+ Resource: Resource
11
+ }
12
+ : never
13
+
14
+ export class RequestDriver<
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ TReq extends PaginateableRequestContract<any, any, any, any>
17
+ > implements PaginationDataDriverContract<ExtractRequestTypes<TReq>['Resource']> {
18
+ public constructor(protected request: TReq) {}
19
+
20
+ public get(pageNumber: number, pageSize: number): Promise<PaginationDataDto<ExtractRequestTypes<TReq>['Resource']>> {
21
+ return this.request
22
+ .setPaginationParams(pageNumber, pageSize)
23
+ .send()
24
+ .then((response: PaginationResponse<ExtractRequestTypes<TReq>['Resource']>) => {
25
+ return new PaginationDataDto<ExtractRequestTypes<TReq>['Resource']>(response.getData(), response.getTotal())
26
+ })
27
+ }
28
+
29
+ public getRequest(): TReq {
30
+ return this.request
31
+ }
32
+ }
@@ -0,0 +1,7 @@
1
+ import { type PaginationParamsContract } from './contracts/PaginationParamsContract'
2
+ import { type PaginationResponseBodyContract } from './contracts/PaginationResponseBodyContract'
3
+ import { RequestDriver } from '../pagination/dataDrivers/RequestDriver'
4
+
5
+ export type { PaginationParamsContract, PaginationResponseBodyContract }
6
+
7
+ export { RequestDriver }
@@ -0,0 +1,35 @@
1
+ import { BaseRequest } from '../../requests/BaseRequest'
2
+ import { JsonResponse } from './responses/JsonResponse'
3
+ import { JsonBodyFactory } from '../../requests/factories/JsonBodyFactory'
4
+ import { type BodyFactoryContract } from '../../requests/contracts/BodyFactoryContract'
5
+
6
+ export interface JsonResponseInterface<ResourceInterface> {
7
+ data: ResourceInterface
8
+ }
9
+
10
+ /**
11
+ * This requests expects a response in the JSON format
12
+ * and automatically sets the required headers.
13
+ */
14
+ export abstract class JsonBaseRequest<
15
+ RequestLoaderLoadingType,
16
+ ResponseErrorBody,
17
+ ResourceInterface,
18
+ RequestBodyInterface = undefined,
19
+ RequestParamsInterface extends object = object
20
+ > extends BaseRequest<
21
+ RequestLoaderLoadingType,
22
+ ResponseErrorBody,
23
+ JsonResponseInterface<ResourceInterface>,
24
+ JsonResponse<ResourceInterface>,
25
+ RequestBodyInterface,
26
+ RequestParamsInterface
27
+ > {
28
+ public getResponse(): JsonResponse<ResourceInterface> {
29
+ return new JsonResponse<ResourceInterface>()
30
+ }
31
+
32
+ public override getRequestBodyFactory(): BodyFactoryContract<RequestBodyInterface | undefined> {
33
+ return new JsonBodyFactory<RequestBodyInterface>()
34
+ }
35
+ }
@@ -0,0 +1,29 @@
1
+ import { BaseRequest } from '../../requests/BaseRequest'
2
+ import { type PaginationResponseBodyContract } from '../pagination/contracts/PaginationResponseBodyContract'
3
+ import { type PaginationResponseContract } from '../../pagination/contracts/PaginationResponseContract'
4
+ import { PaginationResponse } from './responses/PaginationResponse'
5
+
6
+ export abstract class PaginationJsonBaseRequest<
7
+ RequestLoaderLoadingType,
8
+ ResponseErrorBodyInterface,
9
+ ResourceInterface,
10
+ RequestParamsInterface extends object
11
+ > extends BaseRequest<
12
+ RequestLoaderLoadingType,
13
+ ResponseErrorBodyInterface,
14
+ PaginationResponseBodyContract<ResourceInterface>,
15
+ PaginationResponseContract<PaginationResponseBodyContract<ResourceInterface>, ResourceInterface>,
16
+ undefined,
17
+ RequestParamsInterface
18
+ > {
19
+ public getResponse(): PaginationResponse<ResourceInterface> {
20
+ return new PaginationResponse()
21
+ }
22
+
23
+ public setPaginationParams(page: number, size: number): this {
24
+ return this.withParams({
25
+ page_number: page,
26
+ page_size: size
27
+ } as RequestParamsInterface)
28
+ }
29
+ }
@@ -0,0 +1,9 @@
1
+ import { JsonResponse } from './responses/JsonResponse'
2
+ import { PaginationResponse } from './responses/PaginationResponse'
3
+ import { JsonBaseRequest } from './JsonBaseRequest'
4
+ import { PaginationJsonBaseRequest } from './PaginationJsonBaseRequest'
5
+ import { type JsonResponseInterface } from './JsonBaseRequest'
6
+
7
+ export { JsonResponse, PaginationResponse, JsonBaseRequest, PaginationJsonBaseRequest }
8
+
9
+ export type { JsonResponseInterface }
@@ -0,0 +1,8 @@
1
+ import { JsonResponse as ParentJsonResponse } from '../../../requests/responses/JsonResponse'
2
+ import { type JsonResponseInterface } from '../JsonBaseRequest'
3
+
4
+ export class JsonResponse<ResourceInterface> extends ParentJsonResponse<JsonResponseInterface<ResourceInterface>> {
5
+ public getData(): ResourceInterface {
6
+ return this.getBody().data
7
+ }
8
+ }
@@ -0,0 +1,16 @@
1
+ import { JsonResponse } from '../../../requests/responses/JsonResponse'
2
+ import { type PaginationResponseBodyContract } from '../../pagination/contracts/PaginationResponseBodyContract'
3
+ import { type PaginationResponseContract } from '../../../pagination/contracts/PaginationResponseContract'
4
+
5
+ export class PaginationResponse<ResourceInterface>
6
+ extends JsonResponse<PaginationResponseBodyContract<ResourceInterface>>
7
+ implements PaginationResponseContract<PaginationResponseBodyContract<ResourceInterface>, ResourceInterface>
8
+ {
9
+ public getTotal(): number {
10
+ return this.getBody().meta.total
11
+ }
12
+
13
+ public getData(): ResourceInterface {
14
+ return this.getBody().data
15
+ }
16
+ }
@@ -0,0 +1,21 @@
1
+ import { Paginator } from './Paginator'
2
+ import { PaginationDataDto } from './dtos/PaginationDataDto'
3
+ import { type PaginatorLoadDataOptions } from './contracts/PaginatorLoadDataOptions'
4
+
5
+ export class InfiniteScroller<ResourceInterface> extends Paginator<ResourceInterface> {
6
+ protected override passDataToViewDriver(dto: PaginationDataDto<ResourceInterface[]>, options: PaginatorLoadDataOptions = {}) {
7
+ const { flush = false, replace = false } = options
8
+
9
+ if (flush) {
10
+ this.flush()
11
+ }
12
+
13
+ if (replace) {
14
+ this.viewDriver.setData(dto.getData())
15
+ } else {
16
+ this.viewDriver.setData(this.viewDriver.getData().concat(dto.getData()))
17
+ }
18
+
19
+ this.viewDriver.setTotal(dto.getTotal())
20
+ }
21
+ }
@@ -0,0 +1,149 @@
1
+ import { PaginationDataDto } from './dtos/PaginationDataDto'
2
+ import { type ViewDriverContract } from './contracts/ViewDriverContract'
3
+ import { type ViewDriverFactoryContract } from './contracts/ViewDriverFactoryContract'
4
+ import { type PaginatorLoadDataOptions } from './contracts/PaginatorLoadDataOptions'
5
+ import { type PaginationDataDriverContract } from './contracts/PaginationDataDriverContract'
6
+
7
+ export interface PaginatorOptions {
8
+ viewDriverFactory?: ViewDriverFactoryContract
9
+ }
10
+
11
+ export class Paginator<ResourceInterface> {
12
+ protected initialized: boolean = false
13
+
14
+ protected static viewDriverFactory: ViewDriverFactoryContract
15
+
16
+ protected viewDriver: ViewDriverContract<ResourceInterface[]>
17
+
18
+ public static setViewDriverFactory(value: ViewDriverFactoryContract): void {
19
+ Paginator.viewDriverFactory = value
20
+ }
21
+
22
+ public constructor(
23
+ protected dataDriver: PaginationDataDriverContract<ResourceInterface[]>,
24
+ pageNumber: number = 1,
25
+ pageSize: number = 10,
26
+ options?: PaginatorOptions
27
+ ) {
28
+ this.viewDriver = options?.viewDriverFactory
29
+ ? options.viewDriverFactory.make<ResourceInterface>(pageNumber, pageSize)
30
+ : Paginator.viewDriverFactory.make<ResourceInterface>(pageNumber, pageSize)
31
+ }
32
+
33
+ public setDataDriver(dataDriver: PaginationDataDriverContract<ResourceInterface[]>): this {
34
+ this.dataDriver = dataDriver
35
+
36
+ return this
37
+ }
38
+
39
+ public getDataDriver(): PaginationDataDriverContract<ResourceInterface[]> {
40
+ return this.dataDriver
41
+ }
42
+
43
+ public init(pageNumber: number, pageSize: number): Promise<PaginationDataDto<ResourceInterface[]>> {
44
+ this.initialized = true
45
+
46
+ if (pageNumber && pageSize) {
47
+ return this.loadData(pageNumber, pageSize)
48
+ }
49
+
50
+ return this.loadData(this.getCurrentPage(), this.getPageSize())
51
+ }
52
+
53
+ public refresh(pageNumber?: number, options?: PaginatorLoadDataOptions): Promise<PaginationDataDto<ResourceInterface[]>> {
54
+ if (pageNumber !== undefined) {
55
+ return this.setPage(pageNumber, options)
56
+ }
57
+
58
+ return this.loadData(this.getCurrentPage(), this.getPageSize(), options)
59
+ }
60
+
61
+ public flush(): void {
62
+ this.viewDriver.setData([])
63
+ }
64
+
65
+ public setPage(pageNumber: number, options?: PaginatorLoadDataOptions): Promise<PaginationDataDto<ResourceInterface[]>> {
66
+ this.viewDriver.setPage(pageNumber)
67
+
68
+ return this.loadData(this.viewDriver.getCurrentPage(), this.viewDriver.getPageSize(), options)
69
+ }
70
+
71
+ public isInitialized(): boolean {
72
+ return this.initialized
73
+ }
74
+
75
+ public getLastPage(): number {
76
+ return this.viewDriver.getLastPage()
77
+ }
78
+
79
+ public toNextPage(): Promise<PaginationDataDto<ResourceInterface[]>> {
80
+ return this.setPage(this.getCurrentPage() + 1)
81
+ }
82
+
83
+ public toFirstPage(): Promise<PaginationDataDto<ResourceInterface[]>> {
84
+ return this.setPage(1)
85
+ }
86
+
87
+ public toLastPage(): Promise<PaginationDataDto<ResourceInterface[]>> {
88
+ return this.setPage(this.viewDriver.getLastPage())
89
+ }
90
+
91
+ public toPreviousPage(): Promise<PaginationDataDto<ResourceInterface[]>> {
92
+ return this.setPage(this.getCurrentPage() - 1)
93
+ }
94
+
95
+ public getPageData(): ResourceInterface[] {
96
+ return this.viewDriver.getData()
97
+ }
98
+
99
+ public getCurrentPage(): number {
100
+ return this.viewDriver.getCurrentPage()
101
+ }
102
+
103
+ public getFromItemNumber(): number {
104
+ return (this.getCurrentPage() - 1) * this.getPageSize() + 1
105
+ }
106
+
107
+ public getToItemNumber(): number {
108
+ return this.getCurrentPage() * this.getPageSize()
109
+ }
110
+
111
+ public getTotal(): number {
112
+ return this.viewDriver.getTotal()
113
+ }
114
+
115
+ public getPageSize(): number {
116
+ return this.viewDriver.getPageSize()
117
+ }
118
+
119
+ public setPageSize(pageSize: number): Promise<PaginationDataDto<ResourceInterface[]>> {
120
+ this.viewDriver.setPageSize(pageSize)
121
+
122
+ if (this.getCurrentPage() * pageSize > this.getTotal()) {
123
+ return this.setPage(1)
124
+ }
125
+
126
+ return this.loadData(this.viewDriver.getCurrentPage(), this.viewDriver.getPageSize())
127
+ }
128
+
129
+ public getPages(): number[] {
130
+ return this.viewDriver.getPages()
131
+ }
132
+
133
+ protected loadData(pageNumber: number, pageSize: number, options?: PaginatorLoadDataOptions): Promise<PaginationDataDto<ResourceInterface[]>> {
134
+ return this.dataDriver.get(pageNumber, pageSize).then((value: PaginationDataDto<ResourceInterface[]>) => {
135
+ this.passDataToViewDriver(value, options)
136
+
137
+ return value
138
+ })
139
+ }
140
+
141
+ protected passDataToViewDriver(dto: PaginationDataDto<ResourceInterface[]>, options?: PaginatorLoadDataOptions): void {
142
+ if (options?.flush) {
143
+ this.flush()
144
+ }
145
+
146
+ this.viewDriver.setData(dto.getData())
147
+ this.viewDriver.setTotal(dto.getTotal())
148
+ }
149
+ }
@@ -0,0 +1,13 @@
1
+ import { type BaseRequestContract } from '../../requests/contracts/BaseRequestContract'
2
+
3
+ export interface PaginateableRequestContract<
4
+ RequestLoaderLoadingType,
5
+ ResponseClass,
6
+ RequestBodyInterface,
7
+ RequestParamsInterface extends object
8
+ > extends BaseRequestContract<RequestLoaderLoadingType, ResponseClass, RequestBodyInterface, RequestParamsInterface> {
9
+ setPaginationParams(
10
+ page: number,
11
+ size: number
12
+ ): BaseRequestContract<RequestLoaderLoadingType, ResponseClass, RequestBodyInterface, RequestParamsInterface>
13
+ }
@@ -0,0 +1,5 @@
1
+ import { PaginationDataDto } from '../dtos/PaginationDataDto'
2
+
3
+ export interface PaginationDataDriverContract<ResourceInterface> {
4
+ get(pageNumber: number, pageSize: number): Promise<PaginationDataDto<ResourceInterface>>
5
+ }
@@ -0,0 +1,7 @@
1
+ import { type ResponseContract } from '../../requests/contracts/ResponseContract'
2
+
3
+ export interface PaginationResponseContract<ResponseBodyInterface, ResourceInterface> extends ResponseContract<ResponseBodyInterface> {
4
+ getData(): ResourceInterface
5
+
6
+ getTotal(): number
7
+ }