@exmg/exm-form 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -0
- package/index.d.ts +4 -0
- package/index.js +5 -0
- package/package.json +47 -0
- package/src/exm-form-base.d.ts +48 -0
- package/src/exm-form-base.js +239 -0
- package/src/exm-form.d.ts +10 -0
- package/src/exm-form.js +15 -0
- package/src/styles/exm-form-base-css.d.ts +2 -0
- package/src/styles/exm-form-base-css.js +4 -0
- package/src/styles/exm-form-base.scss +79 -0
- package/src/styles/exm-form-css.d.ts +2 -0
- package/src/styles/exm-form-css.js +4 -0
- package/src/styles/exm-form.scss +108 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# `<exm-button>` [](https://www.npmjs.com/package/@exmg/exm-button)
|
|
2
|
+
|
|
3
|
+
# exm-form
|
|
4
|
+
|
|
5
|
+
The Exmg Form enhances form design by offering a more intuitive layout visualization, streamlining the user experience with several key features. These include:
|
|
6
|
+
|
|
7
|
+
- Automated form validity checks, ensuring data integrity.
|
|
8
|
+
- Dynamic submit button control, which enables or disables the button based on form completeness.
|
|
9
|
+
- JSON output capability, allowing for easy data serialization and integration.
|
|
10
|
+
- A loading spinner, providing a visual indicator for asynchronous form submissions.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @exmg/exm-form
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Example Usage
|
|
19
|
+
|
|
20
|
+
### Text
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<exm-form class="has-aside" @dialog-submit="${doFormAction}">
|
|
24
|
+
<div slot="toolbar" class="toolbar"><div class="title">Create contact</div></div>
|
|
25
|
+
<form>
|
|
26
|
+
<div class="row">
|
|
27
|
+
<md-filled-text-field name="firstname" dialogFocus label="First Name" required></md-filled-text-field>
|
|
28
|
+
<md-filled-text-field name="lastname" label="Last Name" required></md-filled-text-field>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="row">
|
|
31
|
+
<md-filled-text-field name="company" label="Company"></md-filled-text-field>
|
|
32
|
+
<md-filled-text-field name="amount" label="Amount" type="number" min="0" max="10"></md-filled-text-field>
|
|
33
|
+
</div>
|
|
34
|
+
<md-filled-text-field
|
|
35
|
+
name="email"
|
|
36
|
+
label="Email"
|
|
37
|
+
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,4}$"
|
|
38
|
+
required
|
|
39
|
+
></md-filled-text-field>
|
|
40
|
+
<md-filled-text-field name="phone" label="Phone" required></md-filled-text-field>
|
|
41
|
+
</form>
|
|
42
|
+
<div slot="aside">
|
|
43
|
+
Ex Machina will send notices about the Data <a href="#">Processing Terms</a> and EU General Data Protection
|
|
44
|
+
Regulation to your primary contact. If your organization has a data protection officer or an EU representative, add
|
|
45
|
+
their contact information.
|
|
46
|
+
</div>
|
|
47
|
+
</exm-form>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
async function doFormAction(e: CustomEvent<unknown>) {
|
|
52
|
+
const formDialog = e.target as ExmgForm;
|
|
53
|
+
try {
|
|
54
|
+
formDialog.submitting = true;
|
|
55
|
+
// Do server call
|
|
56
|
+
// Show success message
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error(error instanceof Error ? error.message : 'Unknown error');
|
|
59
|
+
} finally {
|
|
60
|
+
formDialog.submitting = false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API
|
|
66
|
+
|
|
67
|
+
### Properties/Attributes
|
|
68
|
+
|
|
69
|
+
| Name | Type | Default | Description |
|
|
70
|
+
| ----------------- | --------- | -------- | --------------------------------------------------------- |
|
|
71
|
+
| `submitBtn` | `string` | `Save` | Submit button copy |
|
|
72
|
+
| `cancelBtn` | `string` | `Cancel` | Cancel button copy |
|
|
73
|
+
| `submitting` | `boolean` | false | Indicated form submit in progress and disables the button |
|
|
74
|
+
| `hasAsideContent` | `boolean` | false | Indicated if form has a side help section |
|
|
75
|
+
|
|
76
|
+
For other options see the m3 docs:
|
|
77
|
+
|
|
78
|
+
- [Material 3 Button Docs](https://github.com/material-components/material-web/blob/main/docs/components/button.md)
|
|
79
|
+
|
|
80
|
+
## Additional references
|
|
81
|
+
|
|
82
|
+
- [Demo / Docs](https://exmg.github.io/exmachina-web-components/demo/?el=exm-button)
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ExmgForm } from './src/exm-form.js';
|
|
2
|
+
export { ExmgFormBase, serializeForm } from './src/exm-form-base.js';
|
|
3
|
+
export { style as exmgFormStyles } from './src/styles/exm-form-css.js';
|
|
4
|
+
export { style as exmgFormBaseStyles } from './src/styles/exm-form-base-css.js';
|
package/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ExmgForm } from './src/exm-form.js';
|
|
2
|
+
export { ExmgFormBase, serializeForm } from './src/exm-form-base.js';
|
|
3
|
+
export { style as exmgFormStyles } from './src/styles/exm-form-css.js';
|
|
4
|
+
export { style as exmgFormBaseStyles } from './src/styles/exm-form-base-css.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@exmg/exm-form",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"module": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./exm-form.js": "./src/exm-form.js"
|
|
10
|
+
},
|
|
11
|
+
"description": "Form element",
|
|
12
|
+
"contributors": [
|
|
13
|
+
"Ex Machina"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"web-components",
|
|
17
|
+
"lit",
|
|
18
|
+
"form"
|
|
19
|
+
],
|
|
20
|
+
"files": [
|
|
21
|
+
"**/*.scss",
|
|
22
|
+
"**/*.js",
|
|
23
|
+
"**/*.d.ts"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git@github.com:exmg/exm-web-components.git",
|
|
28
|
+
"directory": "packages/exm-form"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@exmg/exm-button": "^1.0.0",
|
|
33
|
+
"@material/typography": "^14.0.0",
|
|
34
|
+
"lit": "^3.0.0",
|
|
35
|
+
"tslib": "^2.6.2"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@exmg/lit-cli": "1.1.13"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build:styles": "exmg-lit-cli sass -f \"./**/*.scss\""
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"gitHead": "0907b55c89325d59902b98a64c352bf6e1fc81ff"
|
|
47
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import '@exmg/exm-button/exm-text-button.js';
|
|
2
|
+
import '@material/web/button/text-button.js';
|
|
3
|
+
import '@material/web/divider/divider.js';
|
|
4
|
+
import { ExmgElement } from '@exmg/lit-base';
|
|
5
|
+
export declare const CLOSE_ACTION = "close";
|
|
6
|
+
export declare const serializeForm: (form: any) => {};
|
|
7
|
+
export declare class ExmgFormBase extends ExmgElement {
|
|
8
|
+
/**
|
|
9
|
+
* Submit button copy
|
|
10
|
+
*/
|
|
11
|
+
submitBtn: string;
|
|
12
|
+
/**
|
|
13
|
+
* Cancel button copy
|
|
14
|
+
*/
|
|
15
|
+
cancelBtn: string;
|
|
16
|
+
/**
|
|
17
|
+
* Internall used to show button spinner.
|
|
18
|
+
*/
|
|
19
|
+
submitting: boolean;
|
|
20
|
+
formValid: boolean;
|
|
21
|
+
boundHandleBlur?: (e: Event) => void;
|
|
22
|
+
boundHandleKeyup?: (e: Event) => void;
|
|
23
|
+
hasAsideContent: boolean;
|
|
24
|
+
errorMessage?: string | null;
|
|
25
|
+
protected getForm(): HTMLFormElement | null;
|
|
26
|
+
reset(): void;
|
|
27
|
+
submit(): void;
|
|
28
|
+
protected _handleKeyup(): void;
|
|
29
|
+
protected _handleBlur(e: Event): void;
|
|
30
|
+
protected firstUpdated(): Promise<void>;
|
|
31
|
+
disconnectedCallback(): void;
|
|
32
|
+
protected _checkFormValidity(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Action method that needs to be implemented
|
|
35
|
+
* @param {CustomEvent} e
|
|
36
|
+
*/
|
|
37
|
+
doAction?(formData: unknown): Promise<void> | void;
|
|
38
|
+
protected handleSubmit(): Promise<void>;
|
|
39
|
+
showError(message: string): void;
|
|
40
|
+
protected renderToolbar(): import("lit-html").TemplateResult<1>;
|
|
41
|
+
protected renderFormContent(): import("lit-html").TemplateResult<1>;
|
|
42
|
+
protected renderAside(): import("lit-html").TemplateResult<1>;
|
|
43
|
+
handleAsideSlotChange(e: CustomEvent): void;
|
|
44
|
+
protected renderError(): import("lit-html").TemplateResult<1>;
|
|
45
|
+
protected renderActions(): import("lit-html").TemplateResult<1>;
|
|
46
|
+
protected renderContainer(): import("lit-html").TemplateResult<1>;
|
|
47
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
48
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html, nothing } from 'lit';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
import '@exmg/exm-button/exm-text-button.js';
|
|
5
|
+
import '@material/web/button/text-button.js';
|
|
6
|
+
import '@material/web/divider/divider.js';
|
|
7
|
+
import { ExmgElement } from '@exmg/lit-base';
|
|
8
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
9
|
+
export const CLOSE_ACTION = 'close';
|
|
10
|
+
export const serializeForm = (form) => {
|
|
11
|
+
const obj = {};
|
|
12
|
+
const formElements = form === null || form === void 0 ? void 0 : form.elements;
|
|
13
|
+
const formElementsArray = Array.from(formElements);
|
|
14
|
+
/**
|
|
15
|
+
* Create list of checkbox elements. If no value is set the return value of the checkbox for 'on' will be a boolean
|
|
16
|
+
*/
|
|
17
|
+
const checkboxNames = formElementsArray
|
|
18
|
+
.filter((input) => {
|
|
19
|
+
return input.value === 'on' && (input.type === 'checkbox' || input.tagName.toLowerCase().includes('checkbox'));
|
|
20
|
+
})
|
|
21
|
+
.map((input) => input.name);
|
|
22
|
+
/* Same for the radio items */
|
|
23
|
+
const radioNames = formElementsArray
|
|
24
|
+
.filter((input) => {
|
|
25
|
+
return input.type === 'radio' || input.tagName.toLowerCase().includes('radio');
|
|
26
|
+
})
|
|
27
|
+
.map((input) => input.name);
|
|
28
|
+
const numberNames = formElementsArray
|
|
29
|
+
.filter((input) => {
|
|
30
|
+
return input.type === 'number';
|
|
31
|
+
})
|
|
32
|
+
.map((input) => input.name);
|
|
33
|
+
const formData = new FormData(form);
|
|
34
|
+
for (const pair of formData.entries()) {
|
|
35
|
+
const key = pair[0];
|
|
36
|
+
const val = pair[1];
|
|
37
|
+
if (Object.hasOwnProperty.call(obj, key)) {
|
|
38
|
+
if (!Array.isArray(obj[key])) {
|
|
39
|
+
obj[key] = [obj[key]];
|
|
40
|
+
}
|
|
41
|
+
obj[key].push(val);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (numberNames.includes(key)) {
|
|
45
|
+
obj[key] = val ? parseFloat(`${val}`) : undefined;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
// When set to on convert to boolean return value
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
if (checkboxNames.includes(key)) {
|
|
51
|
+
obj[key] = val === 'on';
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
// Check for a default value of on for radio items to set to true, if not, set to the value
|
|
55
|
+
if (radioNames.includes(key)) {
|
|
56
|
+
if (val === 'on') {
|
|
57
|
+
obj[key] = val === 'on';
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
obj[key] = val;
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
obj[key] = formData.get(key);
|
|
65
|
+
}
|
|
66
|
+
// All checkboxes that are not checked will not be included in the form data and need to return false
|
|
67
|
+
for (const name of checkboxNames) {
|
|
68
|
+
// check for
|
|
69
|
+
if (!Object.hasOwnProperty.call(obj, name)) {
|
|
70
|
+
obj[name] = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return obj;
|
|
74
|
+
};
|
|
75
|
+
export class ExmgFormBase extends ExmgElement {
|
|
76
|
+
constructor() {
|
|
77
|
+
super(...arguments);
|
|
78
|
+
/**
|
|
79
|
+
* Submit button copy
|
|
80
|
+
*/
|
|
81
|
+
this.submitBtn = 'Save';
|
|
82
|
+
/**
|
|
83
|
+
* Cancel button copy
|
|
84
|
+
*/
|
|
85
|
+
this.cancelBtn = 'Cancel';
|
|
86
|
+
/**
|
|
87
|
+
* Internall used to show button spinner.
|
|
88
|
+
*/
|
|
89
|
+
this.submitting = false;
|
|
90
|
+
this.formValid = false;
|
|
91
|
+
this.hasAsideContent = false;
|
|
92
|
+
}
|
|
93
|
+
getForm() {
|
|
94
|
+
return this.shadowRoot.querySelector('form');
|
|
95
|
+
}
|
|
96
|
+
reset() {
|
|
97
|
+
const form = this.getForm();
|
|
98
|
+
form === null || form === void 0 ? void 0 : form.reset();
|
|
99
|
+
}
|
|
100
|
+
submit() {
|
|
101
|
+
this.handleSubmit();
|
|
102
|
+
}
|
|
103
|
+
_handleKeyup() {
|
|
104
|
+
this._checkFormValidity();
|
|
105
|
+
}
|
|
106
|
+
_handleBlur(e) {
|
|
107
|
+
// @ts-ignore
|
|
108
|
+
typeof e.target.reportValidity === 'function' && e.target.reportValidity();
|
|
109
|
+
this._checkFormValidity();
|
|
110
|
+
}
|
|
111
|
+
async firstUpdated() {
|
|
112
|
+
const form = this.getForm();
|
|
113
|
+
this.boundHandleBlur = this._handleBlur.bind(this);
|
|
114
|
+
form.addEventListener('blur', this.boundHandleBlur, true);
|
|
115
|
+
this.boundHandleKeyup = this._handleKeyup.bind(this);
|
|
116
|
+
form.addEventListener('keyup', this.boundHandleKeyup, true);
|
|
117
|
+
await this.updateComplete;
|
|
118
|
+
this._checkFormValidity();
|
|
119
|
+
}
|
|
120
|
+
disconnectedCallback() {
|
|
121
|
+
const form = this.getForm();
|
|
122
|
+
this.boundHandleBlur && form.removeEventListener('blur', this.boundHandleBlur, true);
|
|
123
|
+
this.boundHandleKeyup && form.removeEventListener('keyup', this.boundHandleKeyup, true);
|
|
124
|
+
super.disconnectedCallback();
|
|
125
|
+
}
|
|
126
|
+
_checkFormValidity() {
|
|
127
|
+
const form = this.getForm();
|
|
128
|
+
const formElements = form === null || form === void 0 ? void 0 : form.elements;
|
|
129
|
+
let allValid = true;
|
|
130
|
+
for (const el of formElements || []) {
|
|
131
|
+
// @ts-ignore
|
|
132
|
+
if (typeof el.reportValidity !== 'function')
|
|
133
|
+
continue;
|
|
134
|
+
// @ts-ignore
|
|
135
|
+
const isValid = el.checkValidity();
|
|
136
|
+
if (!isValid) {
|
|
137
|
+
allValid = false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
this.formValid = allValid;
|
|
141
|
+
}
|
|
142
|
+
async handleSubmit() {
|
|
143
|
+
this.errorMessage = null;
|
|
144
|
+
const form = this.getForm();
|
|
145
|
+
// Check form validity
|
|
146
|
+
this._checkFormValidity();
|
|
147
|
+
// Return when there are invalid fields
|
|
148
|
+
if (!this.formValid) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// Serialize form data
|
|
152
|
+
const data = serializeForm(form);
|
|
153
|
+
if (this.doAction) {
|
|
154
|
+
try {
|
|
155
|
+
this.submitting = true;
|
|
156
|
+
await this.doAction(data);
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
this.showError(error instanceof Error ? error.message : 'Unknown error');
|
|
160
|
+
this.fire('form-error', { message: error instanceof Error ? error.message : 'Unknown error' }, true);
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
this.submitting = false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this.fire('form-submit', data, true);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
showError(message) {
|
|
171
|
+
this.errorMessage = message;
|
|
172
|
+
}
|
|
173
|
+
renderToolbar() {
|
|
174
|
+
return html `<div class="toolbar-container"><slot name="toolbar"></slot></div>`;
|
|
175
|
+
}
|
|
176
|
+
renderFormContent() {
|
|
177
|
+
return html `<slot></slot>`;
|
|
178
|
+
}
|
|
179
|
+
renderAside() {
|
|
180
|
+
return html `<slot name="aside" @slotchange="${this.handleAsideSlotChange}"></slot>`;
|
|
181
|
+
}
|
|
182
|
+
handleAsideSlotChange(e) {
|
|
183
|
+
const slot = e.target;
|
|
184
|
+
// @ts-ignore
|
|
185
|
+
const nodes = slot.assignedNodes({ flatten: true });
|
|
186
|
+
this.hasAsideContent = nodes.length > 0;
|
|
187
|
+
}
|
|
188
|
+
renderError() {
|
|
189
|
+
return html `<div class="form-error"><div>${this.errorMessage}</div></div>`;
|
|
190
|
+
}
|
|
191
|
+
renderActions() {
|
|
192
|
+
return html `
|
|
193
|
+
<div class="actions">
|
|
194
|
+
<md-text-button slot="footer" dialogFocus @click=${() => this.fire('form-cancel')}
|
|
195
|
+
>${this.cancelBtn}</md-text-button
|
|
196
|
+
>
|
|
197
|
+
<exm-text-button
|
|
198
|
+
slot="footer"
|
|
199
|
+
@click=${this.handleSubmit}
|
|
200
|
+
?disabled=${this.submitting || !this.formValid}
|
|
201
|
+
?loading=${this.submitting}
|
|
202
|
+
>${this.submitBtn}</exm-text-button
|
|
203
|
+
>
|
|
204
|
+
</div>
|
|
205
|
+
`;
|
|
206
|
+
}
|
|
207
|
+
renderContainer() {
|
|
208
|
+
return html ` <div class="container">
|
|
209
|
+
<div class="content">${this.renderFormContent()}</div>
|
|
210
|
+
<div class="aside ${classMap({ empty: !this.hasAsideContent })}">${this.renderAside()}</div>
|
|
211
|
+
</div>`;
|
|
212
|
+
}
|
|
213
|
+
render() {
|
|
214
|
+
return html `
|
|
215
|
+
${this.renderToolbar()} ${this.errorMessage ? this.renderError() : nothing} ${this.renderContainer()}
|
|
216
|
+
<div class="divider"><md-divider></md-divider></div>
|
|
217
|
+
${this.renderActions()}
|
|
218
|
+
`;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
__decorate([
|
|
222
|
+
property({ type: String })
|
|
223
|
+
], ExmgFormBase.prototype, "submitBtn", void 0);
|
|
224
|
+
__decorate([
|
|
225
|
+
property({ type: String })
|
|
226
|
+
], ExmgFormBase.prototype, "cancelBtn", void 0);
|
|
227
|
+
__decorate([
|
|
228
|
+
property({ type: Boolean })
|
|
229
|
+
], ExmgFormBase.prototype, "submitting", void 0);
|
|
230
|
+
__decorate([
|
|
231
|
+
property({ type: Boolean })
|
|
232
|
+
], ExmgFormBase.prototype, "formValid", void 0);
|
|
233
|
+
__decorate([
|
|
234
|
+
property({ type: Boolean })
|
|
235
|
+
], ExmgFormBase.prototype, "hasAsideContent", void 0);
|
|
236
|
+
__decorate([
|
|
237
|
+
property({ type: String })
|
|
238
|
+
], ExmgFormBase.prototype, "errorMessage", void 0);
|
|
239
|
+
//# sourceMappingURL=exm-form-base.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ExmgFormBase } from './exm-form-base.js';
|
|
2
|
+
export declare class ExmgForm extends ExmgFormBase {
|
|
3
|
+
static styles: import("lit").CSSResult[];
|
|
4
|
+
getForm(): HTMLFormElement | null;
|
|
5
|
+
}
|
|
6
|
+
declare global {
|
|
7
|
+
interface HTMLElementTagNameMap {
|
|
8
|
+
'exm-form': ExmgForm;
|
|
9
|
+
}
|
|
10
|
+
}
|
package/src/exm-form.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { customElement } from 'lit/decorators.js';
|
|
3
|
+
import { style } from './styles/exm-form-base-css.js';
|
|
4
|
+
import { ExmgFormBase } from './exm-form-base.js';
|
|
5
|
+
let ExmgForm = class ExmgForm extends ExmgFormBase {
|
|
6
|
+
getForm() {
|
|
7
|
+
return this.querySelector('form');
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
ExmgForm.styles = [style];
|
|
11
|
+
ExmgForm = __decorate([
|
|
12
|
+
customElement('exm-form')
|
|
13
|
+
], ExmgForm);
|
|
14
|
+
export { ExmgForm };
|
|
15
|
+
//# sourceMappingURL=exm-form.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const style = css `:host{display:block;--_exm-form-content-margin-left: var(--exm-form-content-margin-left, 0);--_exm-form-container-margin-top: var(--exm-form-container-margin-top, 1rem);--_exm-form-container-margin-bottom: var(--exm-form-container-margin-bottom, 1rem);--_exm-form-divider-margin-top: var(--exm-form-divider-margin-top, 2rem);--_exm-form-aside-font-size: var(--exm-form-aside-font-size, 0.875rem);--_exm-form-aside-line-height: var(--exm-form-aside-line-height, 1.25rem);--_exm-form-aside-letter-spacing: var(--exm-form-aside-letter-spacing, 0.0142857143em);--_exm-form-aside-font-weight: var(--exm-form-aside-font-weight, 400);--_exm-form-aside-margin-left: var(--exm-form-aside-margin-left, 1rem);--_exm-form-aside-margin-right: var(--exm-form-aside-margin-right, 5rem);--_exm-form-aside-min-width: var(--exm-form-aside-min-width, 180px)}.aside{line-height:var(--_exm-form-aside-line-height);font-size:var(--_exm-form-aside-font-size);letter-spacing:var(--_exm-form-aside-letter-spacing);font-weight:var(--_exm-form-aside-font-weight);color:var(--_exm-form-aside-color, var(--md-sys-color-on-surface));border-left:1px solid var(--md-sys-color-outline-variant);box-sizing:border-box;margin-left:var(--_exm-form-aside-margin-left);margin-right:var(--_exm-form-aside-margin-right);min-height:2rem;padding-left:1rem;vertical-align:top;height:100%;min-width:var(--_exm-form-aside-min-width);flex:0}.aside.empty{display:none}.content,.container{display:flex}.content{flex:1}.container{margin-top:var(--_exm-form-container-margin-top);margin-bottom:var(--_exm-form-container-margin-bottom)}.actions{display:flex;padding:8px 4px;justify-content:flex-end}.actions>*{margin-right:8px}.divider{margin-top:var(--_exm-form-divider-margin-top)}.content{margin-left:var(--_exm-form-content-margin-left)}.form-error{background-color:var(--_exm-form-error-background, var(--md-sys-color-error-container, red));color:var(--_exm-form-error-color, var(--md-sys-color-on-error-container, white))}.form-error>div{padding:1rem}`;
|
|
3
|
+
export default style;
|
|
4
|
+
//# sourceMappingURL=exm-form-base-css.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
|
|
4
|
+
--_exm-form-content-margin-left: var(--exm-form-content-margin-left, 0);
|
|
5
|
+
--_exm-form-container-margin-top: var(--exm-form-container-margin-top, 1rem);
|
|
6
|
+
--_exm-form-container-margin-bottom: var(--exm-form-container-margin-bottom, 1rem);
|
|
7
|
+
--_exm-form-divider-margin-top: var(--exm-form-divider-margin-top, 2rem);
|
|
8
|
+
|
|
9
|
+
// Aside
|
|
10
|
+
--_exm-form-aside-font-size: var(--exm-form-aside-font-size, 0.875rem);
|
|
11
|
+
--_exm-form-aside-line-height: var(--exm-form-aside-line-height, 1.25rem);
|
|
12
|
+
--_exm-form-aside-letter-spacing: var(--exm-form-aside-letter-spacing, 0.0142857143em);
|
|
13
|
+
--_exm-form-aside-font-weight: var(--exm-form-aside-font-weight, 400);
|
|
14
|
+
--_exm-form-aside-margin-left: var(--exm-form-aside-margin-left, 1rem);
|
|
15
|
+
--_exm-form-aside-margin-right: var(--exm-form-aside-margin-right, 5rem);
|
|
16
|
+
--_exm-form-aside-min-width: var(--exm-form-aside-min-width, 180px);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.aside {
|
|
20
|
+
line-height: var(--_exm-form-aside-line-height);
|
|
21
|
+
font-size: var(--_exm-form-aside-font-size);
|
|
22
|
+
letter-spacing: var(--_exm-form-aside-letter-spacing);
|
|
23
|
+
font-weight: var(--_exm-form-aside-font-weight);
|
|
24
|
+
color: var(--_exm-form-aside-color, var(--md-sys-color-on-surface));
|
|
25
|
+
border-left: 1px solid var(--md-sys-color-outline-variant);
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
margin-left: var(--_exm-form-aside-margin-left);
|
|
28
|
+
margin-right: var(--_exm-form-aside-margin-right);
|
|
29
|
+
min-height: 2rem;
|
|
30
|
+
padding-left: 1rem;
|
|
31
|
+
vertical-align: top;
|
|
32
|
+
height: 100%;
|
|
33
|
+
min-width: var(--_exm-form-aside-min-width);
|
|
34
|
+
flex: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.aside.empty {
|
|
38
|
+
display: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.content,
|
|
42
|
+
.container {
|
|
43
|
+
display: flex;
|
|
44
|
+
}
|
|
45
|
+
.content {
|
|
46
|
+
flex: 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.container {
|
|
50
|
+
margin-top: var(--_exm-form-container-margin-top);
|
|
51
|
+
margin-bottom: var(--_exm-form-container-margin-bottom);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.actions {
|
|
55
|
+
display: flex;
|
|
56
|
+
padding: 8px 4px;
|
|
57
|
+
justify-content: flex-end;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.actions > * {
|
|
61
|
+
margin-right: 8px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.divider {
|
|
65
|
+
margin-top: var(--_exm-form-divider-margin-top);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.content {
|
|
69
|
+
margin-left: var(--_exm-form-content-margin-left);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.form-error {
|
|
73
|
+
background-color: var(--_exm-form-error-background, var(--md-sys-color-error-container, red));
|
|
74
|
+
color: var(--_exm-form-error-color, var(--md-sys-color-on-error-container, white));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.form-error > div {
|
|
78
|
+
padding: 1rem;
|
|
79
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const style = css `.section-title{line-height:1.5rem;font-size:1rem;letter-spacing:.00625em;font-weight:500;color:var(--_exm-form-title-color, var(--md-sys-color-on-surface));padding-bottom:8px}.aside{--_exm-form-aside-font-size: var(--exm-form-aside-font-size, 0.875rem);--_exm-form-aside-line-height: var(--exm-form-aside-line-height, 1.25rem);--_exm-form-aside-letter-spacing: var(--exm-form-aside-letter-spacing, 0.0142857143em);--_exm-form-aside-font-weight: var(--exm-form-aside-font-weight, 400);--_exm-form-aside-margin-left: var(--exm-form-aside-margin-left, 1rem);--_exm-form-aside-margin-right: var(--exm-form-aside-margin-right, 5rem);--_exm-form-aside-min-width: var(--exm-form-aside-min-width, 180px);line-height:var(--_exm-form-aside-line-height);font-size:var(--_exm-form-aside-font-size);letter-spacing:var(--_exm-form-aside-letter-spacing);font-weight:var(--_exm-form-aside-font-weight);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);box-sizing:border-box;margin-left:var(--_exm-form-aside-margin-left);margin-right:var(--_exm-form-aside-margin-right);min-height:2rem;padding-left:1rem;vertical-align:top;height:100%;min-width:var(--_exm-form-aside-min-width);flex:0}form{margin:0 1rem;width:100%}form .row{display:flex;gap:6px;height:76px;align-items:flex-start}form .row>*{flex:1;height:fit-content}form{display:flex;flex-direction:column;gap:12px;margin-top:.5rem}form>*{display:contents}.actions>*{margin-right:8px}.no-flex{display:block}.extra-margin{margin-top:2rem;display:block}label.checkbox{display:flex;justify-content:flex-start;align-items:center;font-size:1rem;font-weight:500;margin:.5rem 1rem}label.checkbox md-checkbox{margin-right:1rem}.toolbar{display:flex;align-items:center;padding:8px 4px 8px 16px;height:56px;box-sizing:border-box}.toolbar .title{line-height:1.5rem;font-size:1rem;letter-spacing:.00625em;font-weight:500;color:var(--md-sys-color-on-surface);box-sizing:border-box;overflow-wrap:break-word}a{color:var(--md-sys-color-primary)}`;
|
|
3
|
+
export default style;
|
|
4
|
+
//# sourceMappingURL=exm-form-css.js.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
.section-title {
|
|
2
|
+
line-height: 1.5rem;
|
|
3
|
+
font-size: 1rem;
|
|
4
|
+
letter-spacing: 0.00625em;
|
|
5
|
+
font-weight: 500;
|
|
6
|
+
color: var(--_exm-form-title-color, var(--md-sys-color-on-surface));
|
|
7
|
+
padding-bottom: 8px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.aside {
|
|
11
|
+
--_exm-form-aside-font-size: var(--exm-form-aside-font-size, 0.875rem);
|
|
12
|
+
--_exm-form-aside-line-height: var(--exm-form-aside-line-height, 1.25rem);
|
|
13
|
+
--_exm-form-aside-letter-spacing: var(--exm-form-aside-letter-spacing, 0.0142857143em);
|
|
14
|
+
--_exm-form-aside-font-weight: var(--exm-form-aside-font-weight, 400);
|
|
15
|
+
--_exm-form-aside-margin-left: var(--exm-form-aside-margin-left, 1rem);
|
|
16
|
+
--_exm-form-aside-margin-right: var(--exm-form-aside-margin-right, 5rem);
|
|
17
|
+
--_exm-form-aside-min-width: var(--exm-form-aside-min-width, 180px);
|
|
18
|
+
line-height: var(--_exm-form-aside-line-height);
|
|
19
|
+
font-size: var(--_exm-form-aside-font-size);
|
|
20
|
+
letter-spacing: var(--_exm-form-aside-letter-spacing);
|
|
21
|
+
font-weight: var(--_exm-form-aside-font-weight);
|
|
22
|
+
color: var(--md-sys-color-on-surface);
|
|
23
|
+
border-left: 1px solid var(--md-sys-color-outline-variant);
|
|
24
|
+
box-sizing: border-box;
|
|
25
|
+
margin-left: var(--_exm-form-aside-margin-left);
|
|
26
|
+
margin-right: var(--_exm-form-aside-margin-right);
|
|
27
|
+
min-height: 2rem;
|
|
28
|
+
padding-left: 1rem;
|
|
29
|
+
vertical-align: top;
|
|
30
|
+
height: 100%;
|
|
31
|
+
min-width: var(--_exm-form-aside-min-width);
|
|
32
|
+
flex: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
form {
|
|
36
|
+
margin: 0 1rem;
|
|
37
|
+
width: 100%;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
form .row {
|
|
41
|
+
display: flex;
|
|
42
|
+
gap: 6px;
|
|
43
|
+
height: 76px;
|
|
44
|
+
align-items: flex-start;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
form .row > * {
|
|
48
|
+
flex: 1;
|
|
49
|
+
height: fit-content;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
form {
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
gap: 12px;
|
|
56
|
+
margin-top: 0.5rem;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
form > * {
|
|
60
|
+
display: contents;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.actions > * {
|
|
64
|
+
margin-right: 8px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.no-flex {
|
|
68
|
+
display: block;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.extra-margin {
|
|
72
|
+
margin-top: 2rem;
|
|
73
|
+
display: block;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
label.checkbox {
|
|
77
|
+
display: flex;
|
|
78
|
+
justify-content: flex-start;
|
|
79
|
+
align-items: center;
|
|
80
|
+
font-size: 1rem;
|
|
81
|
+
font-weight: 500;
|
|
82
|
+
margin: 0.5rem 1rem;
|
|
83
|
+
md-checkbox {
|
|
84
|
+
margin-right: 1rem;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.toolbar {
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
padding: 8px 4px 8px 16px;
|
|
92
|
+
height: 56px;
|
|
93
|
+
box-sizing: border-box;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.toolbar .title {
|
|
97
|
+
line-height: 1.5rem;
|
|
98
|
+
font-size: 1rem;
|
|
99
|
+
letter-spacing: 0.00625em;
|
|
100
|
+
font-weight: 500;
|
|
101
|
+
color: var(--md-sys-color-on-surface);
|
|
102
|
+
box-sizing: border-box;
|
|
103
|
+
overflow-wrap: break-word;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
a {
|
|
107
|
+
color: var(--md-sys-color-primary);
|
|
108
|
+
}
|