@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
@@ -0,0 +1,66 @@
1
+ # Route Model Binding
2
+
3
+ When using `vue-router`, you can automatically bind route parameters to resources, similar to how Laravel's route model binding works.
4
+
5
+ ## Setup
6
+
7
+ To enable the router to load resources automatically, install the route injection plugin when initializing your router:
8
+
9
+ ```ts
10
+ installRouteInjection(router)
11
+ ```
12
+
13
+ ### Defining Routes
14
+
15
+ Use the `defineRoute` helper to define your routes and specify which parameters should be resolved into resources:
16
+
17
+ ```ts
18
+ defineRoute<{
19
+ product: ProductResource
20
+ }>()({
21
+ path: ':productId',
22
+ name: 'products.show',
23
+ component: ProductDetailPage,
24
+ meta: {
25
+ inject: {
26
+ product: {
27
+ from: 'productId',
28
+ resolve: (productId: string) => {
29
+ return new RouteModelRequestResolver(
30
+ new ProductShowRequest(productId)
31
+ )
32
+ }
33
+ }
34
+ }
35
+ }
36
+ })
37
+ ```
38
+
39
+ The `beforeResolve` navigation guard will automatically fetch the `ProductResource` using the `ProductShowRequest` and the `productId` from the route.
40
+
41
+ ## Usage in Components
42
+
43
+ Your component can then directly access the resolved resource via props:
44
+
45
+ ```vue
46
+ <script setup lang="ts">
47
+ const props = defineProps<{
48
+ product: ProductResource
49
+ }>()
50
+ </script>
51
+ ```
52
+
53
+ ## Handling Loading States
54
+
55
+ You can handle the loading state of the request by using the event system of the request class:
56
+
57
+ ```ts
58
+ resolve: (productId: string) => {
59
+ return new RouteModelRequestResolver(
60
+ new ProductShowRequest(productId).on<boolean>(RequestEvents.LOADING, (loading: boolean) => {
61
+ const loadingStore = useLoadingStore()
62
+ loadingStore.setLoading(loading)
63
+ })
64
+ )
65
+ }
66
+ ```
@@ -0,0 +1,293 @@
1
+ # State Management
2
+
3
+ ## Overview
4
+ The `State` class provides a reactive, type-safe state management system with rich features like change detection, nested property watching, persistence, and more. It's designed to make state management in Vue applications clean and flexible.
5
+
6
+ ## Core Features
7
+ - **Type-safe state**: Fully typed with TypeScript generics
8
+ - **Reactive properties**: Seamless integration with Vue's reactivity system
9
+ - **Nested property watching**: Track changes to deeply nested properties
10
+ - **Persistence options**: Optional state persistence between sessions
11
+ - **Change subscriptions**: React to state changes with optional debouncing
12
+ - **Deep object watching**: Special handling for nested objects
13
+ - **Efficient equality checking**: Smart comparison of state values
14
+ - **State export/import**: Easily serialize and deserialize state
15
+ - **Reset capability**: Revert to initial state when needed
16
+
17
+ ## Usage Guide
18
+ ### Creating a State Class
19
+ Create a new state by extending the base class and defining your interface:
20
+
21
+ ```typescript
22
+ // Define your state interface
23
+ interface UserStateInterface {
24
+ name: string;
25
+ email: string;
26
+ preferences: {
27
+ darkMode: boolean;
28
+ notifications: boolean;
29
+ };
30
+ }
31
+
32
+ // Create your state class
33
+ class UserState extends State<UserStateInterface> {
34
+ constructor() {
35
+ super(
36
+ {
37
+ name: '',
38
+ email: '',
39
+ preferences: {
40
+ darkMode: false,
41
+ notifications: true
42
+ }
43
+ },
44
+ {
45
+ persist: true // Enable persistence
46
+ }
47
+ );
48
+ }
49
+
50
+ // Optional: Override persistence method
51
+ protected override getPersistenceDriver(): PersistenceDriver {
52
+ return new LocalStorageDriver();
53
+ }
54
+ }
55
+
56
+ // Create an instance
57
+ export const userState = new UserState();
58
+ ```
59
+
60
+ ### Accessing State
61
+
62
+ ````typescript
63
+ // Read
64
+ const userName = userState.state.name;
65
+
66
+ // Write
67
+ userState.state.name = 'John Doe';
68
+ ````
69
+
70
+ ### Subscribing to Changes
71
+ Listen for changes to specific properties using the method: `subscribe`
72
+
73
+ #### Basic subscription to a single property
74
+ ````typescript
75
+ // Subscribe to changes on a top-level property
76
+ userState.subscribe(
77
+ 'name',
78
+ (newValue, oldValue) => {
79
+ console.log(`Name changed from ${oldValue} to ${newValue}`);
80
+ }
81
+ );
82
+ ````
83
+
84
+ #### Subscribing to nested properties
85
+
86
+ ````typescript
87
+ // Subscribe to a nested property using dot notation
88
+ userState.subscribe(
89
+ 'preferences.darkMode',
90
+ (newValue, oldValue) => {
91
+ console.log(`Dark mode changed from ${oldValue} to ${newValue}`);
92
+ // Update UI theme
93
+ }
94
+ );
95
+ ````
96
+
97
+ #### Debounced subscriptions
98
+
99
+ ````typescript
100
+ // Debounce the handler to avoid frequent updates
101
+ userState.subscribe(
102
+ 'email',
103
+ (newValue, oldValue) => {
104
+ // This will only execute after 300ms of stability
105
+ validateEmail(newValue);
106
+ },
107
+ { debounce: 300 }
108
+ );
109
+ ````
110
+
111
+ #### Watching multiple properties
112
+ ````typescript
113
+ // Subscribe to multiple properties at once
114
+ userState.subscribe(
115
+ ['name', 'email'],
116
+ (changedPath, state) => {
117
+ console.log(`Property ${changedPath} changed`);
118
+ console.log('Current state:', state);
119
+ }
120
+ );
121
+ ````
122
+
123
+ ### State Persistence
124
+ Enable state persistence by passing `persist: true` in the constructor options. Override the method `getPersistenceDriver` to use different storage mechanisms:
125
+
126
+ ````typescript
127
+ // Examples of different persistence drivers:
128
+ protected override getPersistenceDriver(): PersistenceDriver {
129
+ // Session storage (lasts until browser tab is closed)
130
+ return new SessionStorageDriver();
131
+
132
+ // Local storage (persists between sessions)
133
+ // return new LocalStorageDriver();
134
+
135
+ // Non-persistent (for testing or when persistence not needed)
136
+ // return new NonPersistentDriver();
137
+ }
138
+ ````
139
+
140
+ ### Importing and Exporting State
141
+
142
+ Export the current state to a plain object or import values:
143
+
144
+ ````typescript
145
+ // Export current state as a plain object
146
+ const stateSnapshot = userState.export();
147
+ console.log(stateSnapshot);
148
+
149
+ // Import partial state
150
+ userState.import({
151
+ name: 'Jane Doe',
152
+ preferences: {
153
+ darkMode: true
154
+ }
155
+ });
156
+
157
+ // Import with hook suppression (doesn't trigger change handlers)
158
+ userState.import(
159
+ {
160
+ name: 'Jane Doe'
161
+ },
162
+ true // suppress hooks
163
+ );
164
+ ````
165
+
166
+ ### Resetting State
167
+ Reset the state to its initial values:
168
+
169
+ ````typescript
170
+ // Reset to initial values
171
+ userState.reset();
172
+ ````
173
+
174
+ ````
175
+ userState.subscribe(
176
+ 'name',
177
+ (newValue, oldValue) => {
178
+ console.log(`Name reset from ${oldValue} to ${newValue}`);
179
+ },
180
+ { executeOnReset: true }
181
+ );
182
+ ````
183
+
184
+ ## Advanced Features
185
+ ### Deep Object Watching
186
+ When you modify nested properties, the system automatically detects these changes and triggers appropriate callbacks:
187
+
188
+ ````typescript
189
+ // Both of these will trigger the 'preferences.darkMode' subscription
190
+ userState.state.preferences.darkMode = true;
191
+ userState.state.preferences = { darkMode: true, notifications: false };
192
+ ````
193
+
194
+ ### Type-Safe Path Access
195
+ The state system enforces type safety for property paths:
196
+
197
+ ````typescript
198
+ // TypeScript will error on invalid paths
199
+ userState.subscribe(
200
+ 'preferences.invalidProperty', // Type error!
201
+ (newValue, oldValue) => {
202
+ // ...
203
+ }
204
+ );
205
+ ````
206
+
207
+ ### Smart Comparison
208
+ The system performs deep equality checks before triggering change handlers, ensuring callbacks are only called when values actually change:
209
+
210
+ ````typescript
211
+ // This won't trigger change handlers if the values are deeply equal
212
+ userState.state.preferences = { darkMode: false, notifications: true };
213
+ ````
214
+
215
+ ## Example: Complete Workflow
216
+
217
+ ````typescript
218
+ // 1. Define state interface
219
+ interface FormState {
220
+ username: string;
221
+ password: string;
222
+ validation: {
223
+ usernameValid: boolean;
224
+ passwordValid: boolean;
225
+ };
226
+ }
227
+
228
+ // 2. Create state class
229
+ class FormStateManager extends State<FormState> {
230
+ constructor() {
231
+ super({
232
+ username: '',
233
+ password: '',
234
+ validation: {
235
+ usernameValid: false,
236
+ passwordValid: false
237
+ }
238
+ });
239
+ }
240
+
241
+ // Helper methods can be added to state classes
242
+ public validateForm(): boolean {
243
+ return this.state.validation.usernameValid &&
244
+ this.state.validation.passwordValid;
245
+ }
246
+ }
247
+
248
+ // 3. Create instance
249
+ const formState = new FormStateManager();
250
+
251
+ // 4. Set up subscriptions
252
+ formState.subscribe(
253
+ 'username',
254
+ (username) => {
255
+ // Validate username
256
+ const valid = username.length >= 3;
257
+ formState.state.validation.usernameValid = valid;
258
+ },
259
+ { debounce: 300 }
260
+ );
261
+
262
+ formState.subscribe(
263
+ 'password',
264
+ (password) => {
265
+ // Validate password
266
+ const valid = password.length >= 8;
267
+ formState.state.validation.passwordValid = valid;
268
+ },
269
+ { debounce: 300 }
270
+ );
271
+
272
+ // 5. Watch form validity
273
+ formState.subscribe(
274
+ ['validation.usernameValid', 'validation.passwordValid'],
275
+ (_, state) => {
276
+ const submitButton = document.getElementById('submit');
277
+ if (formState.validateForm()) {
278
+ submitButton.disabled = false;
279
+ } else {
280
+ submitButton.disabled = true;
281
+ }
282
+ }
283
+ );
284
+
285
+ // 6. Use the state
286
+ formState.state.username = 'john_doe';
287
+ formState.state.password = 'securePassword123';
288
+
289
+ // 7. Reset when needed
290
+ document.getElementById('reset').addEventListener('click', () => {
291
+ formState.reset();
292
+ });
293
+ ````
package/env.d.ts ADDED
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,15 @@
1
+ import eslint from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+ import pluginVue from 'eslint-plugin-vue';
4
+
5
+ import {
6
+ defineConfigWithVueTs,
7
+ vueTsConfigs,
8
+ } from '@vue/eslint-config-typescript'
9
+
10
+ export default defineConfigWithVueTs(
11
+ eslint.configs.recommended,
12
+ ...tseslint.configs.recommended,
13
+ ...pluginVue.configs['flat/recommended'],
14
+ vueTsConfigs.recommended,
15
+ )
Binary file
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="h-full">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Hank-IT UI</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ </head>
8
+ <body class="h-full">
9
+
10
+ <div id="app" class="w-full h-full"></div>
11
+
12
+ <script type="module" src="/js/app.js"></script>
13
+ </body>
14
+ </html>
@@ -0,0 +1,8 @@
1
+ import { createApp } from 'vue'
2
+ import App from './view/App.vue'
3
+ import Router from './router'
4
+
5
+ const app = createApp(App)
6
+ .use(Router)
7
+
8
+ app.mount('#app')
@@ -0,0 +1,22 @@
1
+ import { createWebHistory, createRouter } from 'vue-router'
2
+
3
+ const routes = [
4
+ {
5
+ path: '/pagination/:component?',
6
+ component: () => import('@view/pagination/Pagination.vue'),
7
+ name: 'pagination',
8
+ props: true,
9
+ },
10
+ {
11
+ path: '/requests/:component?',
12
+ component: () => import('@view/requests/Requests.vue'),
13
+ name: 'requests',
14
+ props: true,
15
+ }
16
+ ]
17
+
18
+
19
+ export default createRouter({
20
+ history: createWebHistory(),
21
+ routes, // short for `routes: routes`,
22
+ })
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <div class="container">
3
+ <div class="sidebar">
4
+ <ul>
5
+ <li><RouterLink :to="{ name: 'requests' }">Requests</RouterLink></li>
6
+ <li><RouterLink :to="{ name: 'pagination' }">Pagination</RouterLink></li>
7
+ </ul>
8
+ </div>
9
+ <div class="content">
10
+ <RouterView />
11
+ </div>
12
+ </div>
13
+ </template>
14
+
15
+ <script setup>
16
+
17
+ </script>
18
+
19
+ <style>
20
+ body {
21
+ margin: 0;
22
+ padding:0;
23
+ }
24
+
25
+ .container {
26
+ display: flex;
27
+ flex-direction: row;
28
+ }
29
+
30
+ .sidebar,
31
+ .content {
32
+ display: flex;
33
+ flex-direction: column;
34
+ color: black;
35
+ min-height: 500px;
36
+ border-radius: 4px;
37
+ margin: 10px;
38
+ }
39
+
40
+ .sidebar {
41
+ flex-grow: 1;
42
+ min-width: 300px;
43
+ }
44
+
45
+ .content {
46
+ flex-grow: 5;
47
+ min-width: 630px;
48
+ }
49
+ </style>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <select v-model="selectedComponentKey">
3
+ <RouterLink
4
+ v-for="(component, key) in components"
5
+ :key="key"
6
+ :to="component.route"
7
+ custom
8
+ v-slot="{ isActive, href, navigate }"
9
+ >
10
+ <option @click="navigate" :value="key">
11
+ {{ component.name }}
12
+ </option>
13
+ </RouterLink>
14
+ </select>
15
+
16
+ <component v-if="selectedComponentKey" :is="components[selectedComponentKey].component" />
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import {ref} from 'vue'
21
+
22
+ const props = defineProps({
23
+ components: {},
24
+ component: String,
25
+ })
26
+
27
+ const selectedComponentKey = ref(props.component)
28
+ </script>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <DemoPage :components="components" />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import TablePagination from './components/tablePagination/TablePagination.vue'
7
+ import ErrorPagination from './components/errorPagination/ErrorPagination.vue'
8
+ import InfiniteScrolling from './components/infiniteScrolling/InfiniteScrolling.vue'
9
+ import DemoPage from '../layout/DemoPage.vue'
10
+
11
+ const components = {
12
+ 'table-pagination': {
13
+ name: 'Paginated table',
14
+ component: TablePagination,
15
+ route: { name: 'pagination', params: { component: 'table-pagination' } },
16
+ },
17
+ 'error-pagination': {
18
+ name: 'Pagination with error',
19
+ component: ErrorPagination,
20
+ route: { name: 'pagination', params: { component: 'error-pagination' } },
21
+ },
22
+ 'infinite-scrolling': {
23
+ name: 'Infinite scrolling',
24
+ component: InfiniteScrolling,
25
+ route: { name: 'pagination', params: { component: 'infinite-scrolling' } },
26
+ },
27
+ }
28
+ </script>
@@ -0,0 +1,71 @@
1
+ <template>
2
+ <table>
3
+ <tr>
4
+ <th>ID</th>
5
+ <th>Title</th>
6
+ <th>Description</th>
7
+ </tr>
8
+ <tr v-for="row in paginator.getPageData()">
9
+ <td>{{ row.id }}</td>
10
+ <td>{{ row.title }}</td>
11
+ <td>{{ row.description }}</td>
12
+ </tr>
13
+ </table>
14
+
15
+ Current page: {{ paginator.getCurrentPage() }}
16
+ <br>
17
+ Pages: {{ paginator.getPages() }}
18
+ <br>
19
+ Page Size: <input v-model="pageSize" />
20
+ <br>
21
+ Back Page <button @click="paginator.toPreviousPage()">Back</button>
22
+ <br>
23
+ Next Page <button @click="paginator.toNextPage()">Next</button>
24
+ <br>
25
+ Showing {{ paginator.getFromItemNumber() }} to {{ paginator.getToItemNumber() }} of {{ paginator.getTotal() }} items.
26
+
27
+ {{ displayablePages }}
28
+ </template>
29
+
30
+ <script setup lang="ts">
31
+ import {BaseRequest, FetchDriver, VueLoaderDriverFactory} from "@hank-it/ui/service/requests"
32
+ import {Paginator, RequestDriver, VuePaginationDriverFactory} from "@hank-it/ui/service/pagination";
33
+ import {getDisplayablePages} from '@hank-it/ui/service/helpers'
34
+ import {GetProductsRequest} from "./GetProductsRequest";
35
+ import {computed} from 'vue'
36
+
37
+ /* Booting */
38
+ BaseRequest.setRequestDriver(new FetchDriver)
39
+ BaseRequest.setLoaderStateFactory(new VueLoaderDriverFactory)
40
+ Paginator.setViewDriverFactory(new VuePaginationDriverFactory())
41
+
42
+ /* component */
43
+ const getProductsRequest = new GetProductsRequest
44
+
45
+ const paginator = new Paginator(new RequestDriver(getProductsRequest))
46
+
47
+ paginator.init(1, 10).catch(paginationErrorHandler)
48
+
49
+ function paginationErrorHandler(response) {
50
+ console.log(response.getError())
51
+ }
52
+
53
+ const pageSize = computed({
54
+ set(value) {
55
+ paginator.setPageSize(value)
56
+ },
57
+ get() {
58
+ return paginator.getPageSize()
59
+ }
60
+ })
61
+
62
+ const displayablePages = computed(() => {
63
+ console.log(paginator.getPages())
64
+
65
+ return getDisplayablePages(paginator.getPages().length, paginator.getCurrentPage())
66
+ })
67
+ </script>
68
+
69
+ <style scoped>
70
+
71
+ </style>
@@ -0,0 +1,54 @@
1
+ import {BaseRequest, type Paginatable, JsonResponse } from '@hank-it/ui/service/requests'
2
+ import type {PaginationResponseContract} from '@hank-it/ui/service/pagination'
3
+
4
+ export interface ProductResource {
5
+ id: number
6
+ title: string
7
+ description: string
8
+ }
9
+
10
+ export interface ProductPaginationResource {
11
+ products: ProductResource[]
12
+ total: number
13
+ limit: number
14
+ skip: number
15
+ }
16
+
17
+ export class GetProductsRequestResponse extends JsonResponse implements PaginationResponseContract {
18
+ public getData() {
19
+ return this.data
20
+ }
21
+
22
+ public getTotal(): number {
23
+ return this.body.total
24
+ }
25
+
26
+ public dataHandler(data) {
27
+ return data.products
28
+ }
29
+ }
30
+
31
+ export class GetProductsRequest extends BaseRequest implements Paginatable {
32
+ method() {
33
+ return 'GET'
34
+ }
35
+
36
+ url() {
37
+ return 'https://dummyjson.com/http/500'
38
+ }
39
+
40
+ public setPaginationParams(page: number, size: number): BaseRequest {
41
+ return this.withParams({
42
+ skip: (page - 1) * size,
43
+ limit: size,
44
+ })
45
+ }
46
+
47
+ public getCorsWithCredentials(): boolean {
48
+ return false
49
+ }
50
+
51
+ protected getResponse() {
52
+ return new GetProductsRequestResponse
53
+ }
54
+ }