@quandis/qbo4.ui 4.0.1-CI-20240425-194927 → 4.0.1-CI-20240429-172951
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/package.json +1 -1
- package/readme.md +106 -0
- package/src/qbo/IApiService.d.ts +9 -1
- package/src/qbo/IApiService.js +0 -4
- package/src/qbo/IApiService.ts +10 -3
- package/src/qbo/Program.d.ts +3 -0
- package/src/qbo/Program.js +26 -0
- package/src/qbo/Program.ts +30 -0
- package/src/qbo/RestApiService.d.ts +1 -0
- package/src/qbo/RestApiService.js +2 -1
- package/src/qbo/RestApiService.ts +2 -1
- package/src/qbo/qbo-api.d.ts +1 -1
- package/src/qbo/qbo-api.js +7 -7
- package/src/qbo/qbo-api.ts +1 -1
- package/src/qbo/qbo-contact.d.ts +2 -3
- package/src/qbo/qbo-contact.js +46 -58
- package/src/qbo/qbo-contact.ts +42 -56
- package/src/qbo/qbo-select.d.ts +0 -2
- package/src/qbo/qbo-select.js +4 -15
- package/src/qbo/qbo-select.ts +4 -11
- package/wwwroot/js/qbo4.ui.js +106 -107
- package/wwwroot/js/qbo4.ui.min.js +79 -94
- package/wwwroot/js/qbo4.ui.min.js.map +1 -1
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -34,6 +34,38 @@ const address = getApiService('geoApi').fetch('', { value: '1600 Pennsylvania Av
|
|
|
34
34
|
> - The `json` object can be a string, or an object. If it is an object, it will be stringified.
|
|
35
35
|
> - Additional headers can be specified in the `qbo-api` component.
|
|
36
36
|
|
|
37
|
+
## Default API Endpoint
|
|
38
|
+
|
|
39
|
+
The `qbo4-ui` package will automatically register a default API endpoint using the window.location.
|
|
40
|
+
So, if your endpoints are on the same page as your web page, you can use the default API endpoint.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// This will point to the same website you are on.
|
|
44
|
+
const defaultApi = getApiService('default');
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Reusing the same API with different paths
|
|
48
|
+
|
|
49
|
+
You may register an API, and then reuse it with different paths:
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<qbo-api name="qms" method="POST" apiEndpoint="https://services.quandis.io/api/military/">
|
|
53
|
+
<header name="Accept">application/json</header>
|
|
54
|
+
</qbo-api>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then, in typescript:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { getApiService } from '@quandis/qbo4.ui';
|
|
61
|
+
const qmsSearch = getApiService('api://qms/scra/instant/{clientID}');
|
|
62
|
+
const qmsHealth = getApiService('api://qms/scra/healthcheck');
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
In this example, the `qms` is cloned in `getApiService`, and the `relativePath` is substituted into the `apiEndpoint`.
|
|
66
|
+
|
|
67
|
+
## Custom API Services
|
|
68
|
+
|
|
37
69
|
You can write your own `IApiService` class, and register it to qbo's DI container:
|
|
38
70
|
|
|
39
71
|
```typescript
|
|
@@ -52,6 +84,80 @@ export class MyApi implements IApiService {
|
|
|
52
84
|
services.container.registerInstance<IApiService>('myApi', new MyApi());
|
|
53
85
|
```
|
|
54
86
|
|
|
87
|
+
# QboFormElement
|
|
88
|
+
|
|
89
|
+
The `QboFormElement` web component is used to wrap form elements,
|
|
90
|
+
and present them to a <code>form</code> element upon submission.
|
|
91
|
+
|
|
92
|
+
For example, a credit card components might look like this:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
export class QboPayment extends QboFormElement {
|
|
96
|
+
render() {
|
|
97
|
+
return html`<slot>
|
|
98
|
+
<input type="text" name="Number" placeholder="Card Number" required />
|
|
99
|
+
<input type="text" name="Expiry" placeholder="MM/YY" required />
|
|
100
|
+
<input type="text" name="CVC" placeholder="CVC" required />
|
|
101
|
+
<input type="text" name="Name" placeholder="Name on Card" required />
|
|
102
|
+
</slot>`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> This is a simple example without any credit card validation logic.
|
|
108
|
+
|
|
109
|
+
This can be used within a form multiple times:
|
|
110
|
+
|
|
111
|
+
```html
|
|
112
|
+
<form id='fact'>
|
|
113
|
+
<qbo-payment name="primary"></qbo-payment>
|
|
114
|
+
<qbo-payment name="backup"></qbo-payment>
|
|
115
|
+
</form>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Upon submission, the form will contain the following fields:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"primaryNumber": "...",
|
|
123
|
+
"primaryExpiry": "...",
|
|
124
|
+
"primaryCVC": "...",
|
|
125
|
+
"primaryName": "..."
|
|
126
|
+
"backupNumber": "...",
|
|
127
|
+
"backupExpiry": "...",
|
|
128
|
+
"backupCVC": "...",
|
|
129
|
+
"backupName": "..."
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
> The `name` attribute is used to prefix the field names for any fields in the `ShadowDOM`.
|
|
134
|
+
|
|
135
|
+
If you render elements as children of a `QboFormElement`, they will be directly visible to the form:
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<form id='fact'>
|
|
139
|
+
<qbo-payment name="primary">
|
|
140
|
+
Number: <input type="text" name="myNumber" placeholder="Card Number" required value="1234567890">
|
|
141
|
+
</qbo-payment>
|
|
142
|
+
<qbo-payment name="backup"></qbo-payment>
|
|
143
|
+
</form>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Upon submission, the form will contain the following fields:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"myNumber": "...",
|
|
151
|
+
"backupNumber": "...",
|
|
152
|
+
"backupExpiry": "...",
|
|
153
|
+
"backupCVC": "...",
|
|
154
|
+
"backupName": "..."
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
> The `myNumber` field is slotted into the `ShadowDOM` from the normal markup, and is visible to the form.
|
|
159
|
+
Thus, `myNumber` is not prefixed with `primary`.
|
|
160
|
+
|
|
55
161
|
# Form Validation
|
|
56
162
|
|
|
57
163
|
Modern browers support a very rich set of [form valiation functionality](https://www.w3.org/TR/2009/WD-html5-20090825/forms.html#attr-input-pattern); use it!
|
package/src/qbo/IApiService.d.ts
CHANGED
|
@@ -3,10 +3,18 @@ import { InjectionToken } from "tsyringe";
|
|
|
3
3
|
* Defines a contract for API calls.
|
|
4
4
|
*/
|
|
5
5
|
export interface IApiService {
|
|
6
|
+
/**
|
|
7
|
+
* The relative path to the API endpoint.
|
|
8
|
+
*/
|
|
9
|
+
relativePath: string;
|
|
10
|
+
/**
|
|
11
|
+
* Fetch data from the API.
|
|
12
|
+
* @param relativePath The relative path to the API endpoint.
|
|
13
|
+
* @param payload The payload to send to the API.
|
|
14
|
+
*/
|
|
6
15
|
fetch(relativePath: string | null, payload: Record<string, string> | null): Promise<any>;
|
|
7
16
|
}
|
|
8
17
|
/**
|
|
9
18
|
* Define a token for the IApiService interface
|
|
10
19
|
*/
|
|
11
20
|
export declare const IApiServiceToken: InjectionToken<IApiService>;
|
|
12
|
-
export declare function getApiService(name: string): IApiService;
|
package/src/qbo/IApiService.js
CHANGED
package/src/qbo/IApiService.ts
CHANGED
|
@@ -5,6 +5,16 @@ import { InjectionToken } from "tsyringe";
|
|
|
5
5
|
* Defines a contract for API calls.
|
|
6
6
|
*/
|
|
7
7
|
export interface IApiService {
|
|
8
|
+
/**
|
|
9
|
+
* The relative path to the API endpoint.
|
|
10
|
+
*/
|
|
11
|
+
relativePath: string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fetch data from the API.
|
|
15
|
+
* @param relativePath The relative path to the API endpoint.
|
|
16
|
+
* @param payload The payload to send to the API.
|
|
17
|
+
*/
|
|
8
18
|
fetch(relativePath: string | null, payload: Record<string, string> | null): Promise<any>;
|
|
9
19
|
}
|
|
10
20
|
|
|
@@ -13,6 +23,3 @@ export interface IApiService {
|
|
|
13
23
|
*/
|
|
14
24
|
export const IApiServiceToken: InjectionToken<IApiService> = 'ApiServiceToken';
|
|
15
25
|
|
|
16
|
-
export function getApiService(name: string) {
|
|
17
|
-
return services.container.resolve<IApiService>(name);
|
|
18
|
-
}
|
package/src/qbo/Program.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
+
import { IApiService } from './IApiService.js';
|
|
2
3
|
export { services } from '@quandis/qbo4.configuration';
|
|
3
4
|
export * from './IApiService.js';
|
|
4
5
|
export * from './IValidate.js';
|
|
@@ -27,3 +28,5 @@ export declare function elementSelectorAll(element: Element, selector: string):
|
|
|
27
28
|
export declare function elementResetValue(element: Element): void;
|
|
28
29
|
export declare function elementClearValue(element: Element): void;
|
|
29
30
|
export declare function matches(source: any, match: any, wildCard?: string, ignoreCase?: boolean): boolean;
|
|
31
|
+
export declare function elementData(element: HTMLElement): Record<string, string>;
|
|
32
|
+
export declare function getApiService(url: string): IApiService;
|
package/src/qbo/Program.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
+
import { services } from '@quandis/qbo4.configuration';
|
|
3
|
+
import { RestApiService } from './RestApiService.js';
|
|
2
4
|
export { services } from '@quandis/qbo4.configuration';
|
|
3
5
|
export * from './IApiService.js';
|
|
4
6
|
export * from './IValidate.js';
|
|
@@ -96,3 +98,27 @@ export function matches(source, match, wildCard = "*", ignoreCase = true) {
|
|
|
96
98
|
let regex = new RegExp("^" + match + "$", ignoreCase ? "i" : "");
|
|
97
99
|
return regex.test(source);
|
|
98
100
|
}
|
|
101
|
+
export function elementData(element) {
|
|
102
|
+
const data = element.dataset;
|
|
103
|
+
const record = {};
|
|
104
|
+
for (const key in data) {
|
|
105
|
+
if (data.hasOwnProperty(key) && data[key] !== undefined) {
|
|
106
|
+
record[key] = data[key];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return record;
|
|
110
|
+
}
|
|
111
|
+
export function getApiService(url) {
|
|
112
|
+
const parts = url.match(/api:\/\/([^/]+)/);
|
|
113
|
+
const name = parts ? parts[1] : url;
|
|
114
|
+
const relativePath = parts ? url.substring(`api://${name}`.length) : '';
|
|
115
|
+
const service = services.container.isRegistered(name)
|
|
116
|
+
? services.container.resolve(name)
|
|
117
|
+
: new RestApiService(url);
|
|
118
|
+
if (relativePath) {
|
|
119
|
+
const clone = structuredClone(service);
|
|
120
|
+
clone.relativePath = relativePath;
|
|
121
|
+
return clone;
|
|
122
|
+
}
|
|
123
|
+
return service;
|
|
124
|
+
}
|
package/src/qbo/Program.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
+
import { IApiService } from './IApiService.js';
|
|
3
|
+
import { services } from '@quandis/qbo4.configuration';
|
|
4
|
+
import { RestApiService } from './RestApiService.js';
|
|
2
5
|
|
|
3
6
|
export { services } from '@quandis/qbo4.configuration';
|
|
4
7
|
export * from './IApiService.js';
|
|
@@ -101,3 +104,30 @@ export function matches(source, match, wildCard = "*", ignoreCase = true) {
|
|
|
101
104
|
return regex.test(source);
|
|
102
105
|
}
|
|
103
106
|
|
|
107
|
+
export function elementData(element: HTMLElement) {
|
|
108
|
+
const data = element.dataset;
|
|
109
|
+
const record: Record<string, string> = {};
|
|
110
|
+
for (const key in data) {
|
|
111
|
+
if (data.hasOwnProperty(key) && data[key] !== undefined) {
|
|
112
|
+
record[key] = data[key] as string;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return record;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
export function getApiService(url: string) {
|
|
120
|
+
const parts = url.match(/api:\/\/([^/]+)/);
|
|
121
|
+
const name = parts ? parts[1] : url;
|
|
122
|
+
const relativePath = parts ? url.substring(`api://${name}`.length) : '';
|
|
123
|
+
const service: IApiService = services.container.isRegistered(name)
|
|
124
|
+
? services.container.resolve<IApiService>(name)
|
|
125
|
+
: new RestApiService(url);
|
|
126
|
+
|
|
127
|
+
if (relativePath) {
|
|
128
|
+
const clone = structuredClone(service);
|
|
129
|
+
clone.relativePath = relativePath;
|
|
130
|
+
return clone;
|
|
131
|
+
}
|
|
132
|
+
return service;
|
|
133
|
+
}
|
|
@@ -3,6 +3,7 @@ export declare class RestApiService implements IApiService {
|
|
|
3
3
|
private headers;
|
|
4
4
|
private apiEndpoint;
|
|
5
5
|
private method;
|
|
6
|
+
relativePath: string;
|
|
6
7
|
constructor(apiEndpoint: string, headers?: HeadersInit, method?: string | null);
|
|
7
8
|
fetch(relativePath: string | null, payload?: Record<string, string> | null): Promise<any>;
|
|
8
9
|
}
|
|
@@ -2,12 +2,13 @@ import { services } from "@quandis/qbo4.logging";
|
|
|
2
2
|
import { substitute } from "./qbo-json.js";
|
|
3
3
|
export class RestApiService {
|
|
4
4
|
constructor(apiEndpoint, headers = { 'Content-Type': 'application/json' }, method = null) {
|
|
5
|
+
this.relativePath = '';
|
|
5
6
|
this.apiEndpoint = apiEndpoint;
|
|
6
7
|
this.headers = headers;
|
|
7
8
|
this.method = method;
|
|
8
9
|
}
|
|
9
10
|
async fetch(relativePath, payload = null) {
|
|
10
|
-
const endpoint = substitute(new URL(relativePath ??
|
|
11
|
+
const endpoint = substitute(new URL(relativePath ?? this.relativePath, this.apiEndpoint).href, payload);
|
|
11
12
|
const method = this.method ?? (payload !== null ? 'POST' : 'GET');
|
|
12
13
|
const headers = new Headers(this.headers || {});
|
|
13
14
|
if (payload !== null && !headers.has('Content-Type')) {
|
|
@@ -7,6 +7,7 @@ export class RestApiService implements IApiService {
|
|
|
7
7
|
private headers: HeadersInit;
|
|
8
8
|
private apiEndpoint: string;
|
|
9
9
|
private method: string | null;
|
|
10
|
+
public relativePath: string = '';
|
|
10
11
|
|
|
11
12
|
constructor(apiEndpoint: string, headers: HeadersInit = { 'Content-Type': 'application/json' }, method: string | null = null) {
|
|
12
13
|
this.apiEndpoint = apiEndpoint;
|
|
@@ -15,7 +16,7 @@ export class RestApiService implements IApiService {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
async fetch(relativePath: string | null, payload: Record<string, string> | null = null): Promise<any> {
|
|
18
|
-
const endpoint = substitute(new URL(relativePath ??
|
|
19
|
+
const endpoint = substitute(new URL(relativePath ?? this.relativePath, this.apiEndpoint).href, payload);
|
|
19
20
|
const method = this.method ?? (payload !== null ? 'POST' : 'GET');
|
|
20
21
|
const headers = new Headers(this.headers || {});
|
|
21
22
|
if (payload !== null && !headers.has('Content-Type')) {
|
package/src/qbo/qbo-api.d.ts
CHANGED
package/src/qbo/qbo-api.js
CHANGED
|
@@ -10,7 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
import { LitElement, html } from 'lit';
|
|
11
11
|
import { customElement, property } from 'lit/decorators.js';
|
|
12
12
|
import { registerRestApi } from './RestApiService.js';
|
|
13
|
-
let
|
|
13
|
+
let QboApiElement = class QboApiElement extends LitElement {
|
|
14
14
|
constructor() {
|
|
15
15
|
super(...arguments);
|
|
16
16
|
this.name = null;
|
|
@@ -51,16 +51,16 @@ let ApiServiceComponent = class ApiServiceComponent extends LitElement {
|
|
|
51
51
|
__decorate([
|
|
52
52
|
property({ type: String }),
|
|
53
53
|
__metadata("design:type", Object)
|
|
54
|
-
],
|
|
54
|
+
], QboApiElement.prototype, "name", void 0);
|
|
55
55
|
__decorate([
|
|
56
56
|
property({ type: String }),
|
|
57
57
|
__metadata("design:type", Object)
|
|
58
|
-
],
|
|
58
|
+
], QboApiElement.prototype, "apiEndpoint", void 0);
|
|
59
59
|
__decorate([
|
|
60
60
|
property({ type: String }),
|
|
61
61
|
__metadata("design:type", String)
|
|
62
|
-
],
|
|
63
|
-
|
|
62
|
+
], QboApiElement.prototype, "method", void 0);
|
|
63
|
+
QboApiElement = __decorate([
|
|
64
64
|
customElement('qbo-api')
|
|
65
|
-
],
|
|
66
|
-
export {
|
|
65
|
+
], QboApiElement);
|
|
66
|
+
export { QboApiElement };
|
package/src/qbo/qbo-api.ts
CHANGED
package/src/qbo/qbo-contact.d.ts
CHANGED
|
@@ -8,14 +8,13 @@ export declare class QboContact extends LitElement {
|
|
|
8
8
|
contactId: String | null;
|
|
9
9
|
contactLabel: String | null;
|
|
10
10
|
contactName: String | null;
|
|
11
|
+
disabled: boolean;
|
|
11
12
|
divSectionHeaderClass: String | null;
|
|
12
|
-
emptyOptionText: String | null;
|
|
13
|
-
emptyOptionValue: String | null;
|
|
14
13
|
formControlSmallClass: String | null;
|
|
15
14
|
formLabelSmallClass: String | null;
|
|
16
15
|
formSelectSmallClass: String | null;
|
|
17
16
|
renderInHost: boolean;
|
|
18
|
-
jsonData: any;
|
|
17
|
+
jsonData: any | null;
|
|
19
18
|
createRenderRoot(): HTMLElement | DocumentFragment;
|
|
20
19
|
connectedCallback(): Promise<void>;
|
|
21
20
|
render(): import("lit-html").TemplateResult<1>;
|