@blueprint-ts/core 2.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 +30 -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/requests/bodies/FormDataBody.ts +81 -0
- 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 +509 -405
- package/src/vue/forms/PropertyAwareArray.ts +3 -4
- 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 +63 -0
- package/tests/vue/forms/BaseForm.behavior.test.ts +98 -0
- package/tests/vue/forms/BaseForm.transformers.test.ts +109 -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 -326
- 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/bodies/FormDataBody.ts +0 -41
- 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
package/docs/vue/forms.md
DELETED
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
# BaseForm Documentation
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
`BaseForm` is a powerful and flexible TypeScript class for handling form state, validation, and submission in Vue
|
|
6
|
-
applications. It provides a comprehensive solution for managing form data with features like:
|
|
7
|
-
|
|
8
|
-
- Type-safe form state management
|
|
9
|
-
- Dirty state tracking for individual fields
|
|
10
|
-
- Error handling and field validation
|
|
11
|
-
- Form persistence between page reloads
|
|
12
|
-
- Support for complex nested objects and arrays
|
|
13
|
-
- Automatic transformation of form values to API payloads
|
|
14
|
-
|
|
15
|
-
## Key Features
|
|
16
|
-
|
|
17
|
-
### 1. Type-Safe Form Management
|
|
18
|
-
|
|
19
|
-
The `BaseForm` is a generic class that takes two type parameters:
|
|
20
|
-
|
|
21
|
-
- `RequestBody`: The shape of the data that will be sent to the server
|
|
22
|
-
- `FormBody`: The shape of the form's internal state, which can differ from the request payload
|
|
23
|
-
|
|
24
|
-
````typescript
|
|
25
|
-
class MyForm extends BaseForm<MyRequestPayload, MyFormState> {
|
|
26
|
-
// ...
|
|
27
|
-
}
|
|
28
|
-
````
|
|
29
|
-
|
|
30
|
-
### 2. Form State Persistence
|
|
31
|
-
|
|
32
|
-
Forms can automatically save their state to browser storage (session, local, etc.), allowing users to navigate away and
|
|
33
|
-
return without losing their input.
|
|
34
|
-
|
|
35
|
-
````typescript
|
|
36
|
-
protected override getPersistenceDriver(): PersistenceDriver
|
|
37
|
-
{
|
|
38
|
-
return new SessionStorageDriver() // Or LocalStorageDriver, etc.
|
|
39
|
-
}
|
|
40
|
-
````
|
|
41
|
-
|
|
42
|
-
### 3. Transformations and Getters
|
|
43
|
-
|
|
44
|
-
Transform form values before they are sent to the server using getter methods:
|
|
45
|
-
|
|
46
|
-
````typescript
|
|
47
|
-
protected getStartedAt(): string {
|
|
48
|
-
return DateTime.fromFormat(`${this.state.start_date} ${this.state.start_time}`, 'dd.MM.yyyy HH:mm').toISO()
|
|
49
|
-
}
|
|
50
|
-
````
|
|
51
|
-
|
|
52
|
-
### 4. Error Handling and Validation
|
|
53
|
-
|
|
54
|
-
Map server-side validation errors to specific form fields, with support for nested fields:
|
|
55
|
-
|
|
56
|
-
````typescript
|
|
57
|
-
protected override errorMap: { [serverKey: string]: string | string[] } = {
|
|
58
|
-
started_at: ['start_date', 'start_time'],
|
|
59
|
-
ended_at: ['end_date', 'end_time']
|
|
60
|
-
}
|
|
61
|
-
````
|
|
62
|
-
|
|
63
|
-
### 5. Array Management
|
|
64
|
-
|
|
65
|
-
Special support for arrays with the class `PropertyAwareArray`, enabling reactive updates to array items:
|
|
66
|
-
|
|
67
|
-
````typescript
|
|
68
|
-
public addPosition(): void {
|
|
69
|
-
this.addToArrayProperty('positions', {
|
|
70
|
-
index: this.properties.positions.length + 1,
|
|
71
|
-
gross_amount: null,
|
|
72
|
-
vat_rate: VatRateEnum.VAT_RATE_19,
|
|
73
|
-
booking_account_category_id: null
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
````
|
|
77
|
-
|
|
78
|
-
## Core Concepts
|
|
79
|
-
|
|
80
|
-
### State and Dirty Tracking
|
|
81
|
-
|
|
82
|
-
`BaseForm` tracks the original state and current state of each form field, automatically computing "dirty" status for
|
|
83
|
-
fields that have been changed.
|
|
84
|
-
|
|
85
|
-
````typescript
|
|
86
|
-
// Check if any field in the form has been modified
|
|
87
|
-
form.isDirty()
|
|
88
|
-
````
|
|
89
|
-
|
|
90
|
-
### The Properties Object
|
|
91
|
-
|
|
92
|
-
The `properties` getter provides access to each form field with its model, errors, and dirty status:
|
|
93
|
-
|
|
94
|
-
````html
|
|
95
|
-
|
|
96
|
-
<template>
|
|
97
|
-
<input v-model="form.properties.email.model.value" />
|
|
98
|
-
<div v-if="form.properties.email.dirty">This field has been changed</div>
|
|
99
|
-
<div v-if="form.properties.email.errors.length">{{ form.properties.email.errors[0] }}</div>
|
|
100
|
-
</template>
|
|
101
|
-
````
|
|
102
|
-
|
|
103
|
-
### Form Submission
|
|
104
|
-
|
|
105
|
-
Build a payload for API submission with:
|
|
106
|
-
|
|
107
|
-
````typescript
|
|
108
|
-
const payload = form.buildPayload()
|
|
109
|
-
````
|
|
110
|
-
|
|
111
|
-
## How to Use
|
|
112
|
-
|
|
113
|
-
### 1. Create a Form Class
|
|
114
|
-
|
|
115
|
-
````typescript
|
|
116
|
-
import { BaseForm, type PersistenceDriver, SessionStorageDriver } from '@hank-it/ui/vue/forms'
|
|
117
|
-
|
|
118
|
-
interface MyFormState {
|
|
119
|
-
name: string
|
|
120
|
-
email: string
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
interface MyRequestPayload {
|
|
124
|
-
name: string
|
|
125
|
-
email: string
|
|
126
|
-
timestamp: string // Added field not in the form
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
class MyForm extends BaseForm<MyRequestPayload, MyFormState> {
|
|
130
|
-
// Fields to add to the final payload that aren't in the form state
|
|
131
|
-
protected override append: string[] = ['timestamp']
|
|
132
|
-
|
|
133
|
-
// Fields to exclude from the final payload
|
|
134
|
-
protected override ignore: string[] = []
|
|
135
|
-
|
|
136
|
-
// Map server error keys to form field names
|
|
137
|
-
protected override errorMap: { [serverKey: string]: string | string[] } = {}
|
|
138
|
-
|
|
139
|
-
public constructor() {
|
|
140
|
-
super({
|
|
141
|
-
name: '',
|
|
142
|
-
email: ''
|
|
143
|
-
}, { persist: true, persistSuffix: 'optional-suffix' })
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Use session storage for persistence
|
|
147
|
-
protected override getPersistenceDriver(): PersistenceDriver {
|
|
148
|
-
return new SessionStorageDriver()
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Generate a timestamp for the request
|
|
152
|
-
protected getTimestamp(): string {
|
|
153
|
-
return new Date().toISOString()
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
````
|
|
157
|
-
|
|
158
|
-
### 2. Use in Components
|
|
159
|
-
|
|
160
|
-
````vue
|
|
161
|
-
<template>
|
|
162
|
-
<form @submit.prevent="submitForm">
|
|
163
|
-
<div>
|
|
164
|
-
<label>Name</label>
|
|
165
|
-
<input v-model="form.properties.name.model.value" />
|
|
166
|
-
<div v-if="form.properties.name.errors.length" class="error">
|
|
167
|
-
{{ form.properties.name.errors[0] }}
|
|
168
|
-
</div>
|
|
169
|
-
</div>
|
|
170
|
-
|
|
171
|
-
<div>
|
|
172
|
-
<label>Email</label>
|
|
173
|
-
<input v-model="form.properties.email.model.value" />
|
|
174
|
-
<div v-if="form.properties.email.errors.length" class="error">
|
|
175
|
-
{{ form.properties.email.errors[0] }}
|
|
176
|
-
</div>
|
|
177
|
-
</div>
|
|
178
|
-
|
|
179
|
-
<button type="submit" :disabled="!form.isDirty()">Submit</button>
|
|
180
|
-
<button type="button" @click="form.reset()">Reset</button>
|
|
181
|
-
</form>
|
|
182
|
-
</template>
|
|
183
|
-
|
|
184
|
-
<script setup>
|
|
185
|
-
import { MyForm } from './MyForm'
|
|
186
|
-
|
|
187
|
-
const form = new MyForm({
|
|
188
|
-
name: '',
|
|
189
|
-
email: ''
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
async function submitForm() {
|
|
193
|
-
try {
|
|
194
|
-
const payload = form.buildPayload()
|
|
195
|
-
await api.submitForm(payload)
|
|
196
|
-
// Success handling
|
|
197
|
-
} catch (error) {
|
|
198
|
-
if (error.response?.data?.errors) {
|
|
199
|
-
form.fillErrors(error.response.data.errors)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
</script>
|
|
204
|
-
````
|
|
205
|
-
|
|
206
|
-
## Working with Arrays
|
|
207
|
-
The `PropertyAwareArray` class enables special handling for array items. Each value of objects in the PropertyAwareArray will receive a v-model, errors, etc.:
|
|
208
|
-
|
|
209
|
-
````typescript
|
|
210
|
-
import { BaseForm, PropertyAwareArray } from '@hank-it/ui/vue/forms'
|
|
211
|
-
|
|
212
|
-
export interface FormWithPositions {
|
|
213
|
-
// ...other fields
|
|
214
|
-
positions: PositionItem[]
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export class MyComplexForm extends BaseForm<RequestType, FormWithPositions> {
|
|
218
|
-
constructor() {
|
|
219
|
-
super({
|
|
220
|
-
// ...other defaults
|
|
221
|
-
positions: new PropertyAwareArray([
|
|
222
|
-
{ id: 1, value: '' }
|
|
223
|
-
])
|
|
224
|
-
})
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Add a new position to the array
|
|
228
|
-
public addPosition(): void {
|
|
229
|
-
this.addToArrayProperty('positions', {
|
|
230
|
-
id: this.properties.positions.length + 1,
|
|
231
|
-
value: ''
|
|
232
|
-
})
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Remove a position by id
|
|
236
|
-
public removePosition(id: number): void {
|
|
237
|
-
this.state.positions = new PropertyAwareArray(
|
|
238
|
-
this.state.positions.filter(position => position.id !== id)
|
|
239
|
-
)
|
|
240
|
-
this.resetPositionIds()
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Reset the sequential IDs after removing items
|
|
244
|
-
protected resetPositionIds(): void {
|
|
245
|
-
let count = 1
|
|
246
|
-
this.state.positions.forEach(position => {
|
|
247
|
-
position.id = count
|
|
248
|
-
count++
|
|
249
|
-
})
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const form = new MyComplexForm()
|
|
254
|
-
const id = form.properties.positions[0].id.model.value
|
|
255
|
-
````
|
|
256
|
-
|
|
257
|
-
## Advanced Features
|
|
258
|
-
### 1. Form Reset
|
|
259
|
-
Revert all changes to the original state:
|
|
260
|
-
|
|
261
|
-
````typescript
|
|
262
|
-
form.reset()
|
|
263
|
-
````
|
|
264
|
-
|
|
265
|
-
### 2. Error Handling
|
|
266
|
-
Fill form with validation errors from a server response:
|
|
267
|
-
|
|
268
|
-
````typescript
|
|
269
|
-
try {
|
|
270
|
-
await submitForm(form.buildPayload())
|
|
271
|
-
} catch (error) {
|
|
272
|
-
form.fillErrors(error.response.data.errors)
|
|
273
|
-
}
|
|
274
|
-
````
|
|
275
|
-
|
|
276
|
-
The `fileErrors` method currently only supports the Laravel style dot notation errors.
|
|
277
|
-
|
|
278
|
-
### 3. Filling Form State
|
|
279
|
-
Update multiple form fields at once, preserving the dirty state:
|
|
280
|
-
|
|
281
|
-
````typescript
|
|
282
|
-
form.fillState({
|
|
283
|
-
name: 'John Doe',
|
|
284
|
-
email: 'john@example.com'
|
|
285
|
-
})
|
|
286
|
-
````
|
|
287
|
-
|
|
288
|
-
### 4. Synchronizing Values Without Marking Dirty
|
|
289
|
-
Update both the current and original state, keeping the field "clean":
|
|
290
|
-
|
|
291
|
-
````typescript
|
|
292
|
-
form.syncValue('email', 'new@example.com')
|
|
293
|
-
````
|
|
294
|
-
|
|
295
|
-
## Real-World Examples
|
|
296
|
-
### 1. Date/Time Handling
|
|
297
|
-
|
|
298
|
-
````typescript
|
|
299
|
-
export class TimeTrackingEntryCreateUpdateForm extends BaseForm<RequestPayload, FormState> {
|
|
300
|
-
protected override append: string[] = ['started_at', 'ended_at']
|
|
301
|
-
protected override ignore: string[] = ['start_date', 'start_time', 'end_date', 'end_time']
|
|
302
|
-
|
|
303
|
-
protected getStartedAt(): string {
|
|
304
|
-
return DateTime.fromFormat(`${this.state.start_date} ${this.state.start_time}`, 'dd.MM.yyyy HH:mm').toISO()
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
protected getEndedAt(): string {
|
|
308
|
-
return DateTime.fromFormat(`${this.state.end_date} ${this.state.end_time}`, 'dd.MM.yyyy HH:mm').toISO()
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
````
|
|
312
|
-
|
|
313
|
-
### 2. Complex Object Handling
|
|
314
|
-
|
|
315
|
-
````typescript
|
|
316
|
-
export class IncomingVoucherCreateUpdateForm extends BaseForm<RequestPayload, FormState> {
|
|
317
|
-
// Extract IDs from related objects
|
|
318
|
-
protected getBusinessAssociateId(value: BusinessAssociateResource): string | null {
|
|
319
|
-
return value?.id
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
protected getFileId(value: FileResource): string | null {
|
|
323
|
-
return value?.id
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
````
|
|
Binary file
|
package/examples/index.html
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
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>
|
package/examples/js/app.js
DELETED
package/examples/js/router.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
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
|
-
})
|
package/examples/js/view/App.vue
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
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>
|
|
@@ -1,28 +0,0 @@
|
|
|
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>
|
|
@@ -1,28 +0,0 @@
|
|
|
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>
|
|
@@ -1,71 +0,0 @@
|
|
|
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>
|
|
@@ -1,54 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
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/products'
|
|
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
|
-
protected getResponse() {
|
|
48
|
-
return new GetProductsRequestResponse
|
|
49
|
-
}
|
|
50
|
-
}
|