@blueprint-ts/core 3.0.0 → 4.0.0-beta.1

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 (206) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +25 -1
  3. package/docs/.vitepress/config.ts +80 -23
  4. package/docs/index.md +6 -63
  5. package/docs/{services/laravel → laravel}/pagination.md +19 -6
  6. package/docs/{services/laravel → laravel}/requests.md +2 -2
  7. package/docs/services/pagination/index.md +46 -0
  8. package/docs/services/pagination/infinite-scroller.md +19 -0
  9. package/docs/services/pagination/page-aware.md +46 -0
  10. package/docs/services/pagination/state-pagination.md +77 -0
  11. package/docs/services/pagination/updating-rows.md +36 -0
  12. package/docs/services/persistence/index.md +46 -0
  13. package/docs/services/requests/abort-requests.md +25 -0
  14. package/docs/services/requests/bulk-requests.md +70 -0
  15. package/docs/services/requests/drivers.md +50 -0
  16. package/docs/services/requests/error-handling.md +137 -0
  17. package/docs/services/requests/events.md +31 -0
  18. package/docs/services/requests/getting-started.md +201 -0
  19. package/docs/services/requests/headers.md +40 -0
  20. package/docs/services/requests/loading.md +63 -0
  21. package/docs/services/requests/request-bodies.md +59 -0
  22. package/docs/services/requests/responses.md +34 -0
  23. package/docs/services/support/deferred-promise.md +63 -0
  24. package/docs/services/support/helpers.md +77 -0
  25. package/docs/services/support/index.md +6 -0
  26. package/docs/upgrading/v1-to-v2.md +64 -0
  27. package/docs/upgrading/v2-to-v3.md +52 -0
  28. package/docs/upgrading/v3-to-v4.md +171 -0
  29. package/docs/upgrading.md +0 -0
  30. package/docs/vue/composables/use-confirm-dialog.md +96 -0
  31. package/docs/vue/composables/use-global-checkbox.md +73 -0
  32. package/docs/vue/composables/use-is-empty.md +26 -0
  33. package/docs/vue/composables/use-is-open-from-var.md +32 -0
  34. package/docs/vue/composables/use-is-open.md +28 -0
  35. package/docs/vue/composables/use-model-wrapper.md +29 -0
  36. package/docs/vue/composables/use-on-open.md +26 -0
  37. package/docs/vue/forms/arrays.md +45 -0
  38. package/docs/vue/forms/errors.md +52 -0
  39. package/docs/vue/forms/index.md +99 -0
  40. package/docs/vue/forms/payloads.md +99 -0
  41. package/docs/vue/forms/persistence.md +19 -0
  42. package/docs/vue/forms/state-and-properties.md +26 -0
  43. package/docs/vue/forms/utilities.md +27 -0
  44. package/docs/vue/forms/validation.md +189 -0
  45. package/docs/vue/requests/loading.md +51 -0
  46. package/docs/vue/{requests → router}/route-resource-binding.md +33 -27
  47. package/docs/vue/state.md +27 -11
  48. package/package.json +9 -10
  49. package/release-tool.json +22 -3
  50. package/src/{service/bulkRequests → bulkRequests}/BulkRequestSender.ts +29 -17
  51. package/src/{service/bulkRequests → bulkRequests}/BulkRequestWrapper.ts +5 -5
  52. package/src/laravel/pagination/dataDrivers/RequestDriver.ts +30 -0
  53. package/src/laravel/pagination/index.ts +6 -0
  54. package/src/{service/pagination → pagination}/BasePaginator.ts +35 -0
  55. package/src/{service/pagination → pagination}/InfiniteScroller.ts +1 -0
  56. package/src/{service/pagination → pagination}/PageAwarePaginator.ts +19 -21
  57. package/src/{service/pagination → pagination}/StatePaginator.ts +2 -8
  58. package/src/{service/pagination → pagination}/index.ts +1 -1
  59. package/src/{service/requests → requests}/BaseRequest.ts +2 -2
  60. package/src/requests/ErrorHandler.ts +144 -0
  61. package/src/requests/RequestErrorRouter.ts +89 -0
  62. package/src/{service/requests → requests}/bodies/FormDataBody.ts +10 -6
  63. package/src/requests/exceptions/BadGatewayException.ts +3 -0
  64. package/src/requests/exceptions/BadRequestException.ts +3 -0
  65. package/src/requests/exceptions/ConflictException.ts +3 -0
  66. package/src/requests/exceptions/ForbiddenException.ts +3 -0
  67. package/src/requests/exceptions/GatewayTimeoutException.ts +3 -0
  68. package/src/requests/exceptions/GoneException.ts +3 -0
  69. package/src/requests/exceptions/InvalidJsonException.ts +15 -0
  70. package/src/requests/exceptions/LockedException.ts +3 -0
  71. package/src/requests/exceptions/MethodNotAllowedException.ts +3 -0
  72. package/src/requests/exceptions/NotImplementedException.ts +3 -0
  73. package/src/requests/exceptions/PayloadTooLargeException.ts +3 -0
  74. package/src/requests/exceptions/PreconditionFailedException.ts +3 -0
  75. package/src/requests/exceptions/RequestTimeoutException.ts +3 -0
  76. package/src/requests/exceptions/ServiceUnavailableException.ts +3 -0
  77. package/src/requests/exceptions/TooManyRequestsException.ts +3 -0
  78. package/src/requests/exceptions/UnsupportedMediaTypeException.ts +3 -0
  79. package/src/requests/exceptions/index.ts +51 -0
  80. package/src/requests/factories/FormDataFactory.ts +14 -0
  81. package/src/{service/requests → requests}/index.ts +2 -2
  82. package/src/{service/support → support}/DeferredPromise.ts +1 -1
  83. package/src/support/index.ts +4 -0
  84. package/src/vue/composables/useConfirmDialog.ts +5 -1
  85. package/src/vue/composables/useModelWrapper.ts +3 -0
  86. package/src/vue/forms/BaseForm.ts +491 -393
  87. package/src/vue/forms/PropertyAwareArray.ts +2 -2
  88. package/src/vue/forms/index.ts +4 -4
  89. package/src/vue/forms/validation/index.ts +5 -2
  90. package/src/vue/forms/validation/rules/ConfirmedRule.ts +3 -3
  91. package/src/vue/forms/validation/rules/EmailRule.ts +23 -0
  92. package/src/vue/forms/validation/rules/JsonRule.ts +28 -0
  93. package/src/vue/forms/validation/types/BidirectionalRule.ts +2 -2
  94. package/src/vue/forms/validation/types/ValidationRules.ts +15 -0
  95. package/src/vue/index.ts +3 -3
  96. package/src/vue/requests/factories/VueRequestLoaderFactory.ts +3 -2
  97. package/src/vue/requests/loaders/VueRequestBatchLoader.ts +6 -1
  98. package/src/vue/requests/loaders/VueRequestLoader.ts +1 -1
  99. package/src/vue/router/routeResourceBinding/types.ts +3 -3
  100. package/src/vue/state/State.ts +38 -50
  101. package/tests/service/helpers/mergeDeep.test.ts +1 -1
  102. package/tests/service/laravel/pagination/dataDrivers/RequestDriver.test.ts +3 -3
  103. package/tests/service/laravel/requests/JsonBaseRequest.test.ts +4 -4
  104. package/tests/service/laravel/requests/PaginationJsonBaseRequest.test.ts +3 -3
  105. package/tests/service/laravel/requests/responses/JsonResponse.test.ts +2 -2
  106. package/tests/service/laravel/requests/responses/PaginationResponse.test.ts +2 -2
  107. package/tests/service/pagination/dtos/PaginationDataDto.test.ts +1 -1
  108. package/tests/service/pagination/factories/VuePaginationDriverFactory.test.ts +2 -2
  109. package/tests/service/pagination/frontendDrivers/VuePaginationDriver.test.ts +1 -1
  110. package/tests/service/requests/ErrorHandler.test.ts +61 -58
  111. package/tests/service/requests/FormDataBody.test.ts +1 -1
  112. package/tests/vue/forms/BaseForm.behavior.test.ts +98 -0
  113. package/docs/.vitepress/theme/Layout.vue +0 -14
  114. package/docs/.vitepress/theme/components/VersionSelector.vue +0 -64
  115. package/docs/.vitepress/theme/index.js +0 -13
  116. package/docs/services/requests/index.md +0 -74
  117. package/docs/vue/forms.md +0 -477
  118. package/examples/files/7z2404-x64.exe +0 -0
  119. package/examples/index.html +0 -14
  120. package/examples/js/app.js +0 -8
  121. package/examples/js/router.js +0 -22
  122. package/examples/js/view/App.vue +0 -49
  123. package/examples/js/view/layout/DemoPage.vue +0 -28
  124. package/examples/js/view/pagination/Pagination.vue +0 -28
  125. package/examples/js/view/pagination/components/errorPagination/ErrorPagination.vue +0 -71
  126. package/examples/js/view/pagination/components/errorPagination/GetProductsRequest.ts +0 -54
  127. package/examples/js/view/pagination/components/infiniteScrolling/GetProductsRequest.ts +0 -50
  128. package/examples/js/view/pagination/components/infiniteScrolling/InfiniteScrolling.vue +0 -57
  129. package/examples/js/view/pagination/components/tablePagination/GetProductsRequest.ts +0 -50
  130. package/examples/js/view/pagination/components/tablePagination/TablePagination.vue +0 -63
  131. package/examples/js/view/requests/Requests.vue +0 -34
  132. package/examples/js/view/requests/components/abortableRequest/AbortableRequest.vue +0 -36
  133. package/examples/js/view/requests/components/abortableRequest/GetProductsRequest.ts +0 -25
  134. package/examples/js/view/requests/components/fileDownloadRequest/DownloadFileRequest.ts +0 -15
  135. package/examples/js/view/requests/components/fileDownloadRequest/FileDownloadRequest.vue +0 -44
  136. package/examples/js/view/requests/components/getRequestWithDynamicParams/GetProductsRequest.ts +0 -34
  137. package/examples/js/view/requests/components/getRequestWithDynamicParams/GetRequestWithDynamicParams.vue +0 -59
  138. package/examples/js/view/requests/components/serverErrorRequest/ServerErrorRequest.ts +0 -21
  139. package/examples/js/view/requests/components/serverErrorRequest/ServerErrorRequest.vue +0 -53
  140. package/src/service/laravel/pagination/contracts/PaginationParamsContract.ts +0 -4
  141. package/src/service/laravel/pagination/dataDrivers/RequestDriver.ts +0 -32
  142. package/src/service/laravel/pagination/index.ts +0 -7
  143. package/src/service/requests/ErrorHandler.ts +0 -64
  144. package/src/service/requests/exceptions/index.ts +0 -19
  145. package/src/service/requests/factories/FormDataFactory.ts +0 -9
  146. package/src/service/support/index.ts +0 -3
  147. /package/src/{service/bulkRequests → bulkRequests}/BulkRequestEvent.enum.ts +0 -0
  148. /package/src/{service/bulkRequests → bulkRequests}/index.ts +0 -0
  149. /package/src/{service/laravel → laravel}/pagination/contracts/PaginationResponseBodyContract.ts +0 -0
  150. /package/src/{service/laravel → laravel}/requests/JsonBaseRequest.ts +0 -0
  151. /package/src/{service/laravel → laravel}/requests/PaginationJsonBaseRequest.ts +0 -0
  152. /package/src/{service/laravel → laravel}/requests/index.ts +0 -0
  153. /package/src/{service/laravel → laravel}/requests/responses/JsonResponse.ts +0 -0
  154. /package/src/{service/laravel → laravel}/requests/responses/PaginationResponse.ts +0 -0
  155. /package/src/{service/pagination → pagination}/Paginator.ts +0 -0
  156. /package/src/{service/pagination → pagination}/contracts/BaseViewDriverContract.ts +0 -0
  157. /package/src/{service/pagination → pagination}/contracts/BaseViewDriverFactoryContract.ts +0 -0
  158. /package/src/{service/pagination → pagination}/contracts/PaginateableRequestContract.ts +0 -0
  159. /package/src/{service/pagination → pagination}/contracts/PaginationDataDriverContract.ts +0 -0
  160. /package/src/{service/pagination → pagination}/contracts/PaginationResponseContract.ts +0 -0
  161. /package/src/{service/pagination → pagination}/contracts/PaginatorLoadDataOptions.ts +0 -0
  162. /package/src/{service/pagination → pagination}/contracts/StatePaginationDataDriverContract.ts +0 -0
  163. /package/src/{service/pagination → pagination}/contracts/ViewDriverContract.ts +0 -0
  164. /package/src/{service/pagination → pagination}/contracts/ViewDriverFactoryContract.ts +0 -0
  165. /package/src/{service/pagination → pagination}/dataDrivers/ArrayDriver.ts +0 -0
  166. /package/src/{service/pagination → pagination}/dtos/PaginationDataDto.ts +0 -0
  167. /package/src/{service/pagination → pagination}/dtos/StatePaginationDataDto.ts +0 -0
  168. /package/src/{service/pagination → pagination}/factories/VueBaseViewDriverFactory.ts +0 -0
  169. /package/src/{service/pagination → pagination}/factories/VuePaginationDriverFactory.ts +0 -0
  170. /package/src/{service/pagination → pagination}/frontendDrivers/VueBaseViewDriver.ts +0 -0
  171. /package/src/{service/pagination → pagination}/frontendDrivers/VuePaginationDriver.ts +0 -0
  172. /package/src/{service/persistenceDrivers → persistenceDrivers}/LocalStorageDriver.ts +0 -0
  173. /package/src/{service/persistenceDrivers → persistenceDrivers}/NonPersistentDriver.ts +0 -0
  174. /package/src/{service/persistenceDrivers → persistenceDrivers}/SessionStorageDriver.ts +0 -0
  175. /package/src/{service/persistenceDrivers → persistenceDrivers}/index.ts +0 -0
  176. /package/src/{service/persistenceDrivers → persistenceDrivers}/types/PersistenceDriver.ts +0 -0
  177. /package/src/{service/requests → requests}/RequestEvents.enum.ts +0 -0
  178. /package/src/{service/requests → requests}/RequestMethod.enum.ts +0 -0
  179. /package/src/{service/requests → requests}/bodies/JsonBody.ts +0 -0
  180. /package/src/{service/requests → requests}/contracts/AbortableRequestContract.ts +0 -0
  181. /package/src/{service/requests → requests}/contracts/BaseRequestContract.ts +0 -0
  182. /package/src/{service/requests → requests}/contracts/BodyContract.ts +0 -0
  183. /package/src/{service/requests → requests}/contracts/BodyFactoryContract.ts +0 -0
  184. /package/src/{service/requests → requests}/contracts/DriverConfigContract.ts +0 -0
  185. /package/src/{service/requests → requests}/contracts/HeadersContract.ts +0 -0
  186. /package/src/{service/requests → requests}/contracts/RequestDriverContract.ts +0 -0
  187. /package/src/{service/requests → requests}/contracts/RequestLoaderContract.ts +0 -0
  188. /package/src/{service/requests → requests}/contracts/RequestLoaderFactoryContract.ts +0 -0
  189. /package/src/{service/requests → requests}/contracts/ResponseContract.ts +0 -0
  190. /package/src/{service/requests → requests}/drivers/contracts/ResponseHandlerContract.ts +0 -0
  191. /package/src/{service/requests → requests}/drivers/fetch/FetchDriver.ts +0 -0
  192. /package/src/{service/requests → requests}/drivers/fetch/FetchResponse.ts +0 -0
  193. /package/src/{service/requests → requests}/exceptions/NoResponseReceivedException.ts +0 -0
  194. /package/src/{service/requests → requests}/exceptions/NotFoundException.ts +0 -0
  195. /package/src/{service/requests → requests}/exceptions/PageExpiredException.ts +0 -0
  196. /package/src/{service/requests → requests}/exceptions/ResponseBodyException.ts +0 -0
  197. /package/src/{service/requests → requests}/exceptions/ResponseException.ts +0 -0
  198. /package/src/{service/requests → requests}/exceptions/ServerErrorException.ts +0 -0
  199. /package/src/{service/requests → requests}/exceptions/UnauthorizedException.ts +0 -0
  200. /package/src/{service/requests → requests}/exceptions/ValidationException.ts +0 -0
  201. /package/src/{service/requests → requests}/factories/JsonBodyFactory.ts +0 -0
  202. /package/src/{service/requests → requests}/responses/BaseResponse.ts +0 -0
  203. /package/src/{service/requests → requests}/responses/BlobResponse.ts +0 -0
  204. /package/src/{service/requests → requests}/responses/JsonResponse.ts +0 -0
  205. /package/src/{service/requests → requests}/responses/PlainTextResponse.ts +0 -0
  206. /package/src/{helpers.ts → support/helpers.ts} +0 -0
@@ -0,0 +1,63 @@
1
+ # DeferredPromise
2
+
3
+ `DeferredPromise` exposes a Promise along with `resolve`, `reject`, and a `state` property you can inspect.
4
+
5
+ ```typescript
6
+ import { DeferredPromise } from '@blueprint-ts/core/support'
7
+
8
+ const deferred = new DeferredPromise<string>()
9
+
10
+ deferred.then((value) => {
11
+ console.log(value)
12
+ })
13
+
14
+ deferred.resolve('Done')
15
+ ```
16
+
17
+ State values:
18
+
19
+ - `pending`
20
+ - `fulfilled`
21
+ - `rejected`
22
+
23
+ ## Example: Defer Component Rendering
24
+
25
+ Use a `DeferredPromise` to delay rendering of a Vue component until the slideover is opened and has received the props as non undefined:
26
+
27
+ ```vue
28
+ <template>
29
+ <SlideOver v-model="isOpen">
30
+ <template v-if="resolvedresource">
31
+ <UpdateForm :options="options" :resource="resolvedresource" />
32
+ </template>
33
+ </SlideOver>
34
+ </template>
35
+
36
+ <script setup lang="ts">
37
+ import { ref, onMounted } from 'vue'
38
+ import { DeferredPromise } from '@blueprint-ts/core/support'
39
+
40
+ const props = defineProps<{ resource?: Resource }>()
41
+ const isOpen = defineModel<boolean>('isOpen', { default: false })
42
+
43
+ const readyPromise = new DeferredPromise<Resource>()
44
+ const resolvedresource = ref<Resource>(undefined)
45
+ let options: UpdateFormOptionsContract | undefined
46
+
47
+ readyPromise.then((contract) => {
48
+ options = new UpdateFormOptions(contract)
49
+ })
50
+
51
+ onMounted(() => {
52
+ readyPromise.then((resource: Resource) => {
53
+ resolvedresource.value = resource
54
+ })
55
+ })
56
+
57
+ watch(isOpen, (open) => {
58
+ if (open && props.resource) {
59
+ readyPromise.resolve(props.resource)
60
+ }
61
+ })
62
+ </script>
63
+ ```
@@ -0,0 +1,77 @@
1
+ # Helpers
2
+
3
+ ## isAtBottom
4
+
5
+ Detect whether a scroll container is at the bottom:
6
+
7
+ ```typescript
8
+ import { isAtBottom } from '@blueprint-ts/core/support'
9
+
10
+ const atBottom = isAtBottom(scrollHeight, scrollTop, clientHeight)
11
+ ```
12
+
13
+ Example with a scroll container:
14
+
15
+ ```html
16
+ <nav class="overflow-y-auto" @scroll="handleScroll">
17
+ <!-- content -->
18
+ </nav>
19
+ ```
20
+
21
+ ```typescript
22
+ import { isAtBottom } from '@blueprint-ts/core/support'
23
+
24
+ function handleScroll(event: Event): void {
25
+ if (!(event.target instanceof Element)) {
26
+ return
27
+ }
28
+
29
+ const atBottom = isAtBottom(event.target.scrollHeight, event.target.scrollTop, event.target.clientHeight)
30
+
31
+ if (atBottom) {
32
+ loadMore()
33
+ }
34
+ }
35
+ ```
36
+
37
+ ## getCookie
38
+
39
+ Read a cookie value by name:
40
+
41
+ ```typescript
42
+ import { getCookie } from '@blueprint-ts/core/support'
43
+
44
+ const token = getCookie('XSRF-TOKEN')
45
+ ```
46
+
47
+ ## isObject
48
+
49
+ Check if a value is a non-array object:
50
+
51
+ ```typescript
52
+ import { isObject } from '@blueprint-ts/core/support'
53
+
54
+ if (isObject(value)) {
55
+ // value is an object (not null, not an array)
56
+ }
57
+ ```
58
+
59
+ ## mergeDeep
60
+
61
+ Deep-merge plain objects:
62
+
63
+ ```typescript
64
+ import { mergeDeep } from '@blueprint-ts/core/support'
65
+
66
+ const result = mergeDeep({}, defaults, overrides)
67
+ ```
68
+
69
+ ## getDisplayablePages
70
+
71
+ Generate a window of page numbers for pagination UI:
72
+
73
+ ```typescript
74
+ import { getDisplayablePages } from '@blueprint-ts/core/support'
75
+
76
+ const pages = getDisplayablePages(currentPage, totalPages, displayPages)
77
+ ```
@@ -0,0 +1,6 @@
1
+ # Support
2
+
3
+ The Support utilities include helper functions and small primitives used across the library.
4
+
5
+ - [Helpers](./helpers)
6
+ - [DeferredPromise](./deferred-promise)
@@ -0,0 +1,64 @@
1
+ ## Upgrading from v1 to v2
2
+
3
+ ### Route Resource Binding
4
+
5
+ Commit `db70b77` introduced significant changes to the route resource binding system to support better caching, parent-to-child inheritance, and non-blocking navigation.
6
+
7
+ #### Non-blocking Navigation (Breaking Change)
8
+
9
+ In v1, navigation was blocked until all resources were resolved in the `beforeResolve` guard. In v2, navigation proceeds immediately, and resources are loaded in the background.
10
+
11
+ This means that your components will now render **before** the resources are available (they will be `undefined` initially).
12
+
13
+ **How to upgrade:**
14
+
15
+ 1. **Use `RouteResourceBoundView`**: Replace `<RouterView>` with `<RouteResourceBoundView>`. This component automatically manages loading and error states for you.
16
+ ```vue
17
+ <script setup lang="ts">
18
+ import { RouteResourceBoundView } from '@blueprint-ts/core'
19
+ </script>
20
+
21
+ <template>
22
+ <RouteResourceBoundView />
23
+ </template>
24
+ ```
25
+
26
+ 2. **Define Loading/Error Components**: You can now define these directly in your `defineRoute` call to be used by `RouteResourceBoundView`:
27
+ ```ts
28
+ defineRoute<{ product: ProductResource }>()({
29
+ path: ':productId',
30
+ component: ProductDetailPage,
31
+ loadingComponent: MyLoadingSpinner,
32
+ errorComponent: MyErrorPage,
33
+ inject: {
34
+ product: { from: 'productId', resolve: ... }
35
+ }
36
+ })
37
+ ```
38
+
39
+ 3. **Manual Handling**: If you want your component to handle its own loading state, set `lazy: false` in the route definition and use the updated `useRouteResource` composable.
40
+
41
+ #### `useRouteResource` API Changes (Breaking Change)
42
+
43
+ The `useRouteResource` composable has been updated to support multiple resources and provide reactive state.
44
+
45
+ * **v1**: `const { refresh } = useRouteResource()`
46
+ * **v2**: `const { refresh, isLoading, error } = useRouteResource('resourceName')`
47
+
48
+ The `refresh` function no longer takes the resource name as an argument. It now accepts an optional options object for silent refreshes.
49
+
50
+ ```ts
51
+ // v1
52
+ const { refresh } = useRouteResource()
53
+ await refresh('product')
54
+
55
+ // v2
56
+ const { refresh, isLoading, error } = useRouteResource('product')
57
+ await refresh() // Triggers loading state
58
+ await refresh({ silent: true }) // Background refresh
59
+ ```
60
+
61
+ #### New Features: Caching and Inheritance
62
+
63
+ * **Caching**: Resources are now cached based on the route parameter value. Navigating between child routes that share the same parameter will no longer trigger redundant requests.
64
+ * **Inheritance**: Child routes now automatically inherit resources defined on parent routes. You can define a resource once on a parent route and it will be available to all children through props or `useRouteResource`.
@@ -0,0 +1,52 @@
1
+ ## Upgrading from v2 to v3
2
+
3
+ ### BaseForm
4
+
5
+ #### Omission of fields with `undefined` values (Breaking Change)
6
+
7
+ Commit `26c0d0e` changed how `BaseForm.buildPayload()` handles `undefined` values returned by transformers (getters).
8
+
9
+ In v2, returning `undefined` from a getter would still include the key in the resulting payload object. In v3, the field is **omitted** entirely from the payload.
10
+
11
+ This applies to:
12
+ - **Field Getters**: `getFieldName(value)`
13
+ - **Composite Getters**: `getParentFieldNestedField(value)`
14
+ - **Appended Fields**: Getters for fields defined in the `append` array.
15
+ - **Nested Objects/Arrays**: Recursive transformation of nested data structures.
16
+
17
+ **How to upgrade:**
18
+ If you previously relied on a field being present with an `undefined` value in the JS object, you should now return `null` if you want the field to be included in the request payload.
19
+
20
+ ```ts
21
+ // v2: payload was { name: undefined }
22
+ // v3: payload is {}
23
+ protected getName(value: string) {
24
+ return value ? value : undefined
25
+ }
26
+
27
+ // v3: if you want the field to be included
28
+ protected getName(value: string) {
29
+ return value ? value : null // payload is { name: null }
30
+ }
31
+ ```
32
+
33
+ #### Removal of Suggestions (Breaking Change)
34
+
35
+ The suggestions feature has been removed from `BaseForm` in commit `f21d6ce`.
36
+
37
+ **Changes:**
38
+ - Removed `fillSuggestions()` method from `BaseForm`.
39
+ - Removed `suggestions` property from the `properties` tree (e.g., `form.properties.email.suggestions` is no longer available).
40
+ - Removed `suggestions` from `PropertyAwareField` and `PropertyAware` types.
41
+
42
+ **How to upgrade:**
43
+ If you were using the suggestions feature, you should now manage suggestions independently of the form state, for example, by using a separate reactive object or a dedicated composable.
44
+
45
+ #### File Upload Support and Form Data Handling
46
+
47
+ Commit `d765cae` introduced native support for `File` and `Blob` objects in `BaseForm`.
48
+
49
+ **Changes:**
50
+ - `BaseForm` now detects if the payload contains `File` or `Blob` objects.
51
+ - When sending a request with a `File` or `Blob` in the payload, the library will automatically use `FormData` and set the appropriate `Content-Type` header (multipart/form-data).
52
+ - `File` values are kept intact during the transformation process and are not converted into plain objects.
@@ -0,0 +1,171 @@
1
+ # Upgrading v3 to v4
2
+
3
+ This guide covers breaking changes when upgrading from v3 to v4.
4
+
5
+ ## Bulk Request Sender Is Now Generic
6
+
7
+ `BulkRequestSender` is now a generic class, and `setRequests()` expects wrappers with matching type parameters. If you
8
+ instantiated the sender without generics, TypeScript may report type errors when passing typed request wrappers.
9
+
10
+ ### What Changed
11
+
12
+ - `BulkRequestSender` is now generic over:
13
+ - `RequestLoaderLoadingType`
14
+ - `RequestBodyInterface`
15
+ - `ResponseClass`
16
+ - `RequestParamsInterface`
17
+ - `setRequests()` is now typed to accept wrappers with the same generics.
18
+
19
+ ### How to Fix
20
+
21
+ Add generics when creating the sender so it matches your wrapper types:
22
+
23
+ ```typescript
24
+ const bulkRequestSenderInstance = new BulkRequestSender<
25
+ RequestLoaderLoadingType,
26
+ RequestBodyInterface,
27
+ ResponseClass,
28
+ RequestParamsInterface
29
+ >([], bulkRequestExecutionMode, retryCount)
30
+ ```
31
+
32
+ If you already have typed wrappers, TypeScript will infer the rest once the sender is typed.
33
+
34
+ ## Paginator `init()` Was Removed
35
+
36
+ Pagination now marks a paginator as initialized only after the first successful load. The `init()` method was removed
37
+ in favor of `load()`.
38
+
39
+ ### What Changed
40
+
41
+ - `isInitialized()` now becomes `true` only after a successful data load.
42
+ - `init()` no longer exists.
43
+
44
+ ### How to Fix
45
+
46
+ Replace `init(...)` with `load(...)`:
47
+
48
+ ```typescript
49
+ await paginator.load()
50
+ ```
51
+
52
+ ## Paginator `refresh()` Was Removed
53
+
54
+ `refresh()` was removed in favor of `load()` to make the first load more intuitive.
55
+
56
+ ### How to Fix
57
+
58
+ Replace `refresh(...)` with `load(...)`:
59
+
60
+ ```typescript
61
+ await paginator.load()
62
+ ```
63
+
64
+ ## Pagination Setters No Longer Trigger Loads
65
+
66
+ `setPageNumber()` and `setPageSize()` now only update state. Call `load()` to fetch data:
67
+
68
+ ```typescript
69
+ await paginator.setPageNumber(2).load()
70
+ await paginator.setPageSize(50).load()
71
+ ```
72
+
73
+ `setPage()` was renamed to `setPageNumber()`.
74
+ Page navigation helpers (`toNextPage`, `toPreviousPage`, `toFirstPage`, `toLastPage`) still load the new page in one call.
75
+
76
+ ## PaginationParamsContract Was Removed
77
+
78
+ `PaginationParamsContract` was removed from the Laravel pagination exports because it was unused. Define your own params
79
+ interface in your app instead.
80
+
81
+ ## Core Modules Moved out of `service/`
82
+
83
+ Core (non-framework) modules moved from `@blueprint-ts/core/service/*` to `@blueprint-ts/core/*`.
84
+
85
+ ### How to Fix
86
+
87
+ Replace:
88
+
89
+ ```typescript
90
+ import { BaseRequest } from '@blueprint-ts/core/service/requests'
91
+ import { PageAwarePaginator } from '@blueprint-ts/core/service/pagination'
92
+ import { DeferredPromise } from '@blueprint-ts/core/service/support'
93
+ import { BulkRequestSender } from '@blueprint-ts/core/service/bulkRequests'
94
+ import { LocalStorageDriver } from '@blueprint-ts/core/service/persistenceDrivers'
95
+ ```
96
+
97
+ with:
98
+
99
+ ```typescript
100
+ import { BaseRequest } from '@blueprint-ts/core/requests'
101
+ import { PageAwarePaginator } from '@blueprint-ts/core/pagination'
102
+ import { DeferredPromise } from '@blueprint-ts/core/support'
103
+ import { BulkRequestSender } from '@blueprint-ts/core/bulkRequests'
104
+ import { LocalStorageDriver } from '@blueprint-ts/core/persistenceDrivers'
105
+ ```
106
+
107
+ ## Helpers Moved to Support
108
+
109
+ Helpers are now exported from `@blueprint-ts/core/support`. The `@blueprint-ts/core/helpers` export was removed.
110
+
111
+ ### How to Fix
112
+
113
+ Replace:
114
+
115
+ ```typescript
116
+ import { isAtBottom } from '@blueprint-ts/core/helpers'
117
+ ```
118
+
119
+ with:
120
+
121
+ ```typescript
122
+ import { isAtBottom } from '@blueprint-ts/core/support'
123
+ ```
124
+
125
+ ## ConfirmDialogSeverity Is Now an Enum
126
+
127
+ `ConfirmDialogSeverity` was changed from a string union type to an enum. Update any `getSeverity()` implementations to return the enum value.
128
+
129
+ ### How to Fix
130
+
131
+ Replace:
132
+
133
+ ```typescript
134
+ import { type ConfirmDialogOptions } from '@blueprint-ts/core/vue'
135
+
136
+ export class MyConfirmOptions implements ConfirmDialogOptions {
137
+ getSeverity() {
138
+ return 'warning'
139
+ }
140
+ }
141
+ ```
142
+
143
+ with:
144
+
145
+ ```typescript
146
+ import { type ConfirmDialogOptions, ConfirmDialogSeverity } from '@blueprint-ts/core/vue'
147
+
148
+ export class MyConfirmOptions implements ConfirmDialogOptions {
149
+ getSeverity() {
150
+ return ConfirmDialogSeverity.WARNING
151
+ }
152
+ }
153
+ ```
154
+
155
+ ## Laravel Packages Moved
156
+
157
+ Laravel modules moved from `@blueprint-ts/core/service/laravel/*` to `@blueprint-ts/core/laravel/*`.
158
+
159
+ ### How to Fix
160
+
161
+ Replace:
162
+
163
+ ```typescript
164
+ import { JsonBaseRequest } from '@blueprint-ts/core/service/laravel/requests'
165
+ ```
166
+
167
+ with:
168
+
169
+ ```typescript
170
+ import { JsonBaseRequest } from '@blueprint-ts/core/laravel/requests'
171
+ ```
File without changes
@@ -0,0 +1,96 @@
1
+ # `useConfirmDialog`
2
+
3
+ `useConfirmDialog` mounts a confirmation dialog component on demand and returns an `openConfirmDialog` function that resolves to `true` or `false`.
4
+
5
+ ## Requirements
6
+
7
+ - Call `useConfirmDialog` inside a `setup()` function.
8
+ - The dialog component must expose an `open()` method via `defineExpose`.
9
+ - `open()` must return a `Promise<boolean>`.
10
+ - The component receives an `options` prop that matches `ConfirmDialogOptions`.
11
+
12
+ ## Example
13
+
14
+ ```vue
15
+ <template>
16
+ <button @click.prevent="click">
17
+ <slot />
18
+ </button>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { useConfirmDialog, type ConfirmDialogOptions } from '@blueprint-ts/core/vue'
23
+ import ConfirmDialog from '@/ConfirmDialog.vue'
24
+
25
+ const props = defineProps<{
26
+ confirmation?: ConfirmDialogOptions
27
+ }>()
28
+
29
+ const { openConfirmDialog } = useConfirmDialog(ConfirmDialog)
30
+
31
+ async function click() {
32
+ if (props.confirmation) {
33
+ const confirmed = await openConfirmDialog(props.confirmation)
34
+
35
+ if (confirmed) {
36
+ const request = new Request()
37
+
38
+ request.send()
39
+ }
40
+ }
41
+ }
42
+ </script>
43
+ ```
44
+
45
+ ## ConfirmDialogOptions
46
+
47
+ The options object must implement the following contract:
48
+
49
+ ```ts
50
+ import { ConfirmDialogSeverity } from '@blueprint-ts/core/vue'
51
+
52
+ export interface ConfirmDialogOptions {
53
+ getMessage(): string
54
+ getSeverity(): ConfirmDialogSeverity
55
+ getTitle(): string
56
+ getOkText(): string
57
+ getCancelText(): string
58
+ }
59
+ ```
60
+
61
+ The available severities are:
62
+
63
+ ```ts
64
+ export enum ConfirmDialogSeverity {
65
+ INFO = 'info',
66
+ WARNING = 'warning',
67
+ DANGER = 'danger'
68
+ }
69
+ ```
70
+
71
+ ## Dialog Component Contract
72
+
73
+ The dialog component must expose `open()` using `defineExpose`:
74
+
75
+ ```vue
76
+ <script setup lang="ts">
77
+ import { type ConfirmDialogOptions } from '@blueprint-ts/core/vue'
78
+
79
+ const props = defineProps<{ options: ConfirmDialogOptions }>()
80
+
81
+ async function open(): Promise<boolean> {
82
+ // open dialog and resolve with true or false
83
+ return true
84
+ }
85
+
86
+ defineExpose({ open })
87
+ </script>
88
+ ```
89
+
90
+ ## Mount Target
91
+
92
+ `useConfirmDialog` accepts an optional second parameter to control where the dialog is mounted:
93
+
94
+ ```ts
95
+ const { openConfirmDialog } = useConfirmDialog(ConfirmDialog, '#dialog-root')
96
+ ```
@@ -0,0 +1,73 @@
1
+ # `useGlobalCheckbox`
2
+
3
+ `useGlobalCheckbox` helps you implement “select all” behavior across a paginated data set. It manages:
4
+
5
+ - selected rows
6
+ - indeterminate/checked state
7
+ - a dialog to choose between selecting the current page or all rows
8
+
9
+ ## Requirements
10
+
11
+ - Call `useGlobalCheckbox` inside a `setup()` function.
12
+ - Provide a dialog component that exposes an `open()` method via `defineExpose`.
13
+ - `open()` must return a `Promise<boolean>`.
14
+ - The dialog receives `checked` and `indeterminate` props.
15
+
16
+ ## Example
17
+
18
+ ```ts
19
+ const { selectedRows, indeterminate, checked, handleGlobalCheckboxChange } =
20
+ useGlobalCheckbox<CredentialListResource>(BulkSelectDialog, {
21
+ getAll: async () => {
22
+ isLoadingAllElements.value = true
23
+
24
+ try {
25
+ const response = await request.withParams(buildParams()).send()
26
+ return response.getData()
27
+ } catch (error) {
28
+ console.error(error)
29
+ return []
30
+ } finally {
31
+ isLoadingAllElements.value = false
32
+ }
33
+ },
34
+ getPage: () => paginator.getPageData(),
35
+ totalCount: () => paginator.getTotal()
36
+ })
37
+ ```
38
+
39
+ In the template, bind the handler to your global checkbox:
40
+
41
+ ```vue
42
+ <input
43
+ type="checkbox"
44
+ :checked="checked"
45
+ :indeterminate="indeterminate"
46
+ @change="handleGlobalCheckboxChange"
47
+ />
48
+ ```
49
+
50
+ ## Dialog Component Contract
51
+
52
+ Your dialog component should accept `checked` and `indeterminate` props and expose `open()`:
53
+
54
+ ```vue
55
+ <script setup lang="ts">
56
+ const props = defineProps<{ checked: boolean; indeterminate: boolean }>()
57
+
58
+ async function open(): Promise<boolean> {
59
+ // true = select all entries, false = select current page / discard
60
+ return true
61
+ }
62
+
63
+ defineExpose({ open })
64
+ </script>
65
+ ```
66
+
67
+ ## Mount Target
68
+
69
+ You can pass a third argument to control where the dialog is mounted:
70
+
71
+ ```ts
72
+ const { handleGlobalCheckboxChange } = useGlobalCheckbox(BulkSelectDialog, options, '#dialog-root')
73
+ ```
@@ -0,0 +1,26 @@
1
+ # `useIsEmpty`
2
+
3
+ `useIsEmpty` provides small helpers to check whether a value should be treated as empty.
4
+
5
+ ## What It Considers Empty
6
+
7
+ - `undefined`, `null`, and `''`
8
+ - arrays with `length === 0`
9
+ - plain objects with no keys (checked via `lodash-es` `isEmpty`)
10
+
11
+ Everything else is treated as not empty.
12
+
13
+ ## Example
14
+
15
+ ```ts
16
+ import { useIsEmpty } from '@blueprint-ts/core/vue'
17
+
18
+ const { isEmpty, isNotEmpty } = useIsEmpty()
19
+
20
+ isEmpty(null) // true
21
+ isEmpty('') // true
22
+ isEmpty([]) // true
23
+ isEmpty({}) // true
24
+
25
+ isNotEmpty('Hello') // true
26
+ ```
@@ -0,0 +1,32 @@
1
+ # `useIsOpenFromVar`
2
+
3
+ `useIsOpenFromVar` ties a “selected value” to open/close state. When `fromVar` is set to a non-empty value, `isOpenFromVar` becomes `true`. When the modal closes, `fromVar` resets after a delay so exit animations can finish.
4
+
5
+ ## Example
6
+
7
+ ```ts
8
+ const { fromVar: selectedItem, isOpenFromVar: isDialogOpen, isOpenFromVarKey: dialogKey } =
9
+ useIsOpenFromVar<Resource>()
10
+ ```
11
+
12
+ Use it like this:
13
+
14
+ - set `selectedItem.value = item` to open the modal
15
+ - set `isDialogOpen.value = false` to close and reset `fromVar`
16
+
17
+ In the template:
18
+
19
+ ```vue
20
+ <BulkEditDialog
21
+ :key="dialogKey"
22
+ v-model="isDialogOpen"
23
+ :item="selectedItem"
24
+ />
25
+ ```
26
+
27
+ ## Options
28
+
29
+ `useIsOpenFromVar` accepts two optional parameters:
30
+
31
+ - `defaultValue` (default `undefined`): value assigned to `fromVar` after close
32
+ - `delay` (default `500`): delay in milliseconds before resetting `fromVar` and bumping `isOpenFromVarKey`, so exit animations can finish
@@ -0,0 +1,28 @@
1
+ # `useIsOpen`
2
+
3
+ `useIsOpen` manages a boolean open state and an `isOpenKey` that increments whenever the state is closed. This is useful for forcing re-mounts after a close animation.
4
+
5
+ ## Example
6
+
7
+ ```ts
8
+ import { useIsOpen } from '@blueprint-ts/core/vue'
9
+
10
+ const { isOpen, isOpenKey } = useIsOpen((value) => {
11
+ if (!value) {
12
+ // closed
13
+ }
14
+ })
15
+ ```
16
+
17
+ In a template, bind `isOpenKey` to force a re-mount when closing:
18
+
19
+ ```vue
20
+ <Modal :key="isOpenKey" v-model="isOpen" />
21
+ ```
22
+
23
+ ## Options
24
+
25
+ `useIsOpen` accepts two optional parameters:
26
+
27
+ - `callback` (default `() => {}`): called whenever `isOpen` changes
28
+ - `delay` (default `500`): delay in milliseconds before incrementing `isOpenKey` on close so exit animations can finish