@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.
- package/CHANGELOG.md +20 -0
- package/README.md +25 -1
- package/docs/.vitepress/config.ts +80 -23
- package/docs/index.md +6 -63
- package/docs/{services/laravel → laravel}/pagination.md +19 -6
- package/docs/{services/laravel → laravel}/requests.md +2 -2
- package/docs/services/pagination/index.md +46 -0
- package/docs/services/pagination/infinite-scroller.md +19 -0
- package/docs/services/pagination/page-aware.md +46 -0
- package/docs/services/pagination/state-pagination.md +77 -0
- package/docs/services/pagination/updating-rows.md +36 -0
- package/docs/services/persistence/index.md +46 -0
- package/docs/services/requests/abort-requests.md +25 -0
- package/docs/services/requests/bulk-requests.md +70 -0
- package/docs/services/requests/drivers.md +50 -0
- package/docs/services/requests/error-handling.md +137 -0
- package/docs/services/requests/events.md +31 -0
- package/docs/services/requests/getting-started.md +201 -0
- package/docs/services/requests/headers.md +40 -0
- package/docs/services/requests/loading.md +63 -0
- package/docs/services/requests/request-bodies.md +59 -0
- package/docs/services/requests/responses.md +34 -0
- package/docs/services/support/deferred-promise.md +63 -0
- package/docs/services/support/helpers.md +77 -0
- package/docs/services/support/index.md +6 -0
- package/docs/upgrading/v1-to-v2.md +64 -0
- package/docs/upgrading/v2-to-v3.md +52 -0
- package/docs/upgrading/v3-to-v4.md +171 -0
- package/docs/upgrading.md +0 -0
- package/docs/vue/composables/use-confirm-dialog.md +96 -0
- package/docs/vue/composables/use-global-checkbox.md +73 -0
- package/docs/vue/composables/use-is-empty.md +26 -0
- package/docs/vue/composables/use-is-open-from-var.md +32 -0
- package/docs/vue/composables/use-is-open.md +28 -0
- package/docs/vue/composables/use-model-wrapper.md +29 -0
- package/docs/vue/composables/use-on-open.md +26 -0
- package/docs/vue/forms/arrays.md +45 -0
- package/docs/vue/forms/errors.md +52 -0
- package/docs/vue/forms/index.md +99 -0
- package/docs/vue/forms/payloads.md +99 -0
- package/docs/vue/forms/persistence.md +19 -0
- package/docs/vue/forms/state-and-properties.md +26 -0
- package/docs/vue/forms/utilities.md +27 -0
- package/docs/vue/forms/validation.md +189 -0
- package/docs/vue/requests/loading.md +51 -0
- package/docs/vue/{requests → router}/route-resource-binding.md +33 -27
- package/docs/vue/state.md +27 -11
- package/package.json +9 -10
- package/release-tool.json +22 -3
- package/src/{service/bulkRequests → bulkRequests}/BulkRequestSender.ts +29 -17
- package/src/{service/bulkRequests → bulkRequests}/BulkRequestWrapper.ts +5 -5
- package/src/laravel/pagination/dataDrivers/RequestDriver.ts +30 -0
- package/src/laravel/pagination/index.ts +6 -0
- package/src/{service/pagination → pagination}/BasePaginator.ts +35 -0
- package/src/{service/pagination → pagination}/InfiniteScroller.ts +1 -0
- package/src/{service/pagination → pagination}/PageAwarePaginator.ts +19 -21
- package/src/{service/pagination → pagination}/StatePaginator.ts +2 -8
- package/src/{service/pagination → pagination}/index.ts +1 -1
- package/src/{service/requests → requests}/BaseRequest.ts +2 -2
- package/src/requests/ErrorHandler.ts +144 -0
- package/src/requests/RequestErrorRouter.ts +89 -0
- package/src/{service/requests → requests}/bodies/FormDataBody.ts +10 -6
- package/src/requests/exceptions/BadGatewayException.ts +3 -0
- package/src/requests/exceptions/BadRequestException.ts +3 -0
- package/src/requests/exceptions/ConflictException.ts +3 -0
- package/src/requests/exceptions/ForbiddenException.ts +3 -0
- package/src/requests/exceptions/GatewayTimeoutException.ts +3 -0
- package/src/requests/exceptions/GoneException.ts +3 -0
- package/src/requests/exceptions/InvalidJsonException.ts +15 -0
- package/src/requests/exceptions/LockedException.ts +3 -0
- package/src/requests/exceptions/MethodNotAllowedException.ts +3 -0
- package/src/requests/exceptions/NotImplementedException.ts +3 -0
- package/src/requests/exceptions/PayloadTooLargeException.ts +3 -0
- package/src/requests/exceptions/PreconditionFailedException.ts +3 -0
- package/src/requests/exceptions/RequestTimeoutException.ts +3 -0
- package/src/requests/exceptions/ServiceUnavailableException.ts +3 -0
- package/src/requests/exceptions/TooManyRequestsException.ts +3 -0
- package/src/requests/exceptions/UnsupportedMediaTypeException.ts +3 -0
- package/src/requests/exceptions/index.ts +51 -0
- package/src/requests/factories/FormDataFactory.ts +14 -0
- package/src/{service/requests → requests}/index.ts +2 -2
- package/src/{service/support → support}/DeferredPromise.ts +1 -1
- package/src/support/index.ts +4 -0
- package/src/vue/composables/useConfirmDialog.ts +5 -1
- package/src/vue/composables/useModelWrapper.ts +3 -0
- package/src/vue/forms/BaseForm.ts +491 -393
- package/src/vue/forms/PropertyAwareArray.ts +2 -2
- package/src/vue/forms/index.ts +4 -4
- package/src/vue/forms/validation/index.ts +5 -2
- package/src/vue/forms/validation/rules/ConfirmedRule.ts +3 -3
- package/src/vue/forms/validation/rules/EmailRule.ts +23 -0
- package/src/vue/forms/validation/rules/JsonRule.ts +28 -0
- package/src/vue/forms/validation/types/BidirectionalRule.ts +2 -2
- package/src/vue/forms/validation/types/ValidationRules.ts +15 -0
- package/src/vue/index.ts +3 -3
- package/src/vue/requests/factories/VueRequestLoaderFactory.ts +3 -2
- package/src/vue/requests/loaders/VueRequestBatchLoader.ts +6 -1
- package/src/vue/requests/loaders/VueRequestLoader.ts +1 -1
- package/src/vue/router/routeResourceBinding/types.ts +3 -3
- package/src/vue/state/State.ts +38 -50
- package/tests/service/helpers/mergeDeep.test.ts +1 -1
- package/tests/service/laravel/pagination/dataDrivers/RequestDriver.test.ts +3 -3
- package/tests/service/laravel/requests/JsonBaseRequest.test.ts +4 -4
- package/tests/service/laravel/requests/PaginationJsonBaseRequest.test.ts +3 -3
- package/tests/service/laravel/requests/responses/JsonResponse.test.ts +2 -2
- package/tests/service/laravel/requests/responses/PaginationResponse.test.ts +2 -2
- package/tests/service/pagination/dtos/PaginationDataDto.test.ts +1 -1
- package/tests/service/pagination/factories/VuePaginationDriverFactory.test.ts +2 -2
- package/tests/service/pagination/frontendDrivers/VuePaginationDriver.test.ts +1 -1
- package/tests/service/requests/ErrorHandler.test.ts +61 -58
- package/tests/service/requests/FormDataBody.test.ts +1 -1
- package/tests/vue/forms/BaseForm.behavior.test.ts +98 -0
- package/docs/.vitepress/theme/Layout.vue +0 -14
- package/docs/.vitepress/theme/components/VersionSelector.vue +0 -64
- package/docs/.vitepress/theme/index.js +0 -13
- package/docs/services/requests/index.md +0 -74
- package/docs/vue/forms.md +0 -477
- package/examples/files/7z2404-x64.exe +0 -0
- package/examples/index.html +0 -14
- package/examples/js/app.js +0 -8
- package/examples/js/router.js +0 -22
- package/examples/js/view/App.vue +0 -49
- package/examples/js/view/layout/DemoPage.vue +0 -28
- package/examples/js/view/pagination/Pagination.vue +0 -28
- package/examples/js/view/pagination/components/errorPagination/ErrorPagination.vue +0 -71
- package/examples/js/view/pagination/components/errorPagination/GetProductsRequest.ts +0 -54
- package/examples/js/view/pagination/components/infiniteScrolling/GetProductsRequest.ts +0 -50
- package/examples/js/view/pagination/components/infiniteScrolling/InfiniteScrolling.vue +0 -57
- package/examples/js/view/pagination/components/tablePagination/GetProductsRequest.ts +0 -50
- package/examples/js/view/pagination/components/tablePagination/TablePagination.vue +0 -63
- package/examples/js/view/requests/Requests.vue +0 -34
- package/examples/js/view/requests/components/abortableRequest/AbortableRequest.vue +0 -36
- package/examples/js/view/requests/components/abortableRequest/GetProductsRequest.ts +0 -25
- package/examples/js/view/requests/components/fileDownloadRequest/DownloadFileRequest.ts +0 -15
- package/examples/js/view/requests/components/fileDownloadRequest/FileDownloadRequest.vue +0 -44
- package/examples/js/view/requests/components/getRequestWithDynamicParams/GetProductsRequest.ts +0 -34
- package/examples/js/view/requests/components/getRequestWithDynamicParams/GetRequestWithDynamicParams.vue +0 -59
- package/examples/js/view/requests/components/serverErrorRequest/ServerErrorRequest.ts +0 -21
- package/examples/js/view/requests/components/serverErrorRequest/ServerErrorRequest.vue +0 -53
- package/src/service/laravel/pagination/contracts/PaginationParamsContract.ts +0 -4
- package/src/service/laravel/pagination/dataDrivers/RequestDriver.ts +0 -32
- package/src/service/laravel/pagination/index.ts +0 -7
- package/src/service/requests/ErrorHandler.ts +0 -64
- package/src/service/requests/exceptions/index.ts +0 -19
- package/src/service/requests/factories/FormDataFactory.ts +0 -9
- package/src/service/support/index.ts +0 -3
- /package/src/{service/bulkRequests → bulkRequests}/BulkRequestEvent.enum.ts +0 -0
- /package/src/{service/bulkRequests → bulkRequests}/index.ts +0 -0
- /package/src/{service/laravel → laravel}/pagination/contracts/PaginationResponseBodyContract.ts +0 -0
- /package/src/{service/laravel → laravel}/requests/JsonBaseRequest.ts +0 -0
- /package/src/{service/laravel → laravel}/requests/PaginationJsonBaseRequest.ts +0 -0
- /package/src/{service/laravel → laravel}/requests/index.ts +0 -0
- /package/src/{service/laravel → laravel}/requests/responses/JsonResponse.ts +0 -0
- /package/src/{service/laravel → laravel}/requests/responses/PaginationResponse.ts +0 -0
- /package/src/{service/pagination → pagination}/Paginator.ts +0 -0
- /package/src/{service/pagination → pagination}/contracts/BaseViewDriverContract.ts +0 -0
- /package/src/{service/pagination → pagination}/contracts/BaseViewDriverFactoryContract.ts +0 -0
- /package/src/{service/pagination → pagination}/contracts/PaginateableRequestContract.ts +0 -0
- /package/src/{service/pagination → pagination}/contracts/PaginationDataDriverContract.ts +0 -0
- /package/src/{service/pagination → pagination}/contracts/PaginationResponseContract.ts +0 -0
- /package/src/{service/pagination → pagination}/contracts/PaginatorLoadDataOptions.ts +0 -0
- /package/src/{service/pagination → pagination}/contracts/StatePaginationDataDriverContract.ts +0 -0
- /package/src/{service/pagination → pagination}/contracts/ViewDriverContract.ts +0 -0
- /package/src/{service/pagination → pagination}/contracts/ViewDriverFactoryContract.ts +0 -0
- /package/src/{service/pagination → pagination}/dataDrivers/ArrayDriver.ts +0 -0
- /package/src/{service/pagination → pagination}/dtos/PaginationDataDto.ts +0 -0
- /package/src/{service/pagination → pagination}/dtos/StatePaginationDataDto.ts +0 -0
- /package/src/{service/pagination → pagination}/factories/VueBaseViewDriverFactory.ts +0 -0
- /package/src/{service/pagination → pagination}/factories/VuePaginationDriverFactory.ts +0 -0
- /package/src/{service/pagination → pagination}/frontendDrivers/VueBaseViewDriver.ts +0 -0
- /package/src/{service/pagination → pagination}/frontendDrivers/VuePaginationDriver.ts +0 -0
- /package/src/{service/persistenceDrivers → persistenceDrivers}/LocalStorageDriver.ts +0 -0
- /package/src/{service/persistenceDrivers → persistenceDrivers}/NonPersistentDriver.ts +0 -0
- /package/src/{service/persistenceDrivers → persistenceDrivers}/SessionStorageDriver.ts +0 -0
- /package/src/{service/persistenceDrivers → persistenceDrivers}/index.ts +0 -0
- /package/src/{service/persistenceDrivers → persistenceDrivers}/types/PersistenceDriver.ts +0 -0
- /package/src/{service/requests → requests}/RequestEvents.enum.ts +0 -0
- /package/src/{service/requests → requests}/RequestMethod.enum.ts +0 -0
- /package/src/{service/requests → requests}/bodies/JsonBody.ts +0 -0
- /package/src/{service/requests → requests}/contracts/AbortableRequestContract.ts +0 -0
- /package/src/{service/requests → requests}/contracts/BaseRequestContract.ts +0 -0
- /package/src/{service/requests → requests}/contracts/BodyContract.ts +0 -0
- /package/src/{service/requests → requests}/contracts/BodyFactoryContract.ts +0 -0
- /package/src/{service/requests → requests}/contracts/DriverConfigContract.ts +0 -0
- /package/src/{service/requests → requests}/contracts/HeadersContract.ts +0 -0
- /package/src/{service/requests → requests}/contracts/RequestDriverContract.ts +0 -0
- /package/src/{service/requests → requests}/contracts/RequestLoaderContract.ts +0 -0
- /package/src/{service/requests → requests}/contracts/RequestLoaderFactoryContract.ts +0 -0
- /package/src/{service/requests → requests}/contracts/ResponseContract.ts +0 -0
- /package/src/{service/requests → requests}/drivers/contracts/ResponseHandlerContract.ts +0 -0
- /package/src/{service/requests → requests}/drivers/fetch/FetchDriver.ts +0 -0
- /package/src/{service/requests → requests}/drivers/fetch/FetchResponse.ts +0 -0
- /package/src/{service/requests → requests}/exceptions/NoResponseReceivedException.ts +0 -0
- /package/src/{service/requests → requests}/exceptions/NotFoundException.ts +0 -0
- /package/src/{service/requests → requests}/exceptions/PageExpiredException.ts +0 -0
- /package/src/{service/requests → requests}/exceptions/ResponseBodyException.ts +0 -0
- /package/src/{service/requests → requests}/exceptions/ResponseException.ts +0 -0
- /package/src/{service/requests → requests}/exceptions/ServerErrorException.ts +0 -0
- /package/src/{service/requests → requests}/exceptions/UnauthorizedException.ts +0 -0
- /package/src/{service/requests → requests}/exceptions/ValidationException.ts +0 -0
- /package/src/{service/requests → requests}/factories/JsonBodyFactory.ts +0 -0
- /package/src/{service/requests → requests}/responses/BaseResponse.ts +0 -0
- /package/src/{service/requests → requests}/responses/BlobResponse.ts +0 -0
- /package/src/{service/requests → requests}/responses/JsonResponse.ts +0 -0
- /package/src/{service/requests → requests}/responses/PlainTextResponse.ts +0 -0
- /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,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
|