You're Invited: Meet the Socket team at BSidesSF and RSAC - April 27 - May 1.RSVP
Socket
Sign inDemoInstall
Socket

@api-components/api-form-mixin

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@api-components/api-form-mixin - npm Package Compare versions

Comparing version

to
3.0.0-preview.3

karma.conf.js

549

api-form-mixin.js

@@ -14,4 +14,2 @@ /**

*/
import {dedupingMixin} from '../../@polymer/polymer/lib/utils/mixin.js';
/**

@@ -25,272 +23,321 @@ * A behavior to be implemented to elements that processes AMF data via form

*
* @polymer
* @mixinFunction
* @memberof ArcBehaviors
* @param {Class} base
* @return {Class}
*/
export const ApiFormMixin = dedupingMixin((base) => {
/**
* @polymer
* @mixinClass
*/
class AFmixin extends base {
static get properties() {
return {
/**
* View model to use to render the form.
*/
model: {
type: Array,
notify: true
},
/**
* Set to true to show optional parameters (not required by the API).
*/
optionalOpened: {
type: Boolean,
reflectToAttribute: true
},
/**
* Computed value from `allowHideOptional` and view model.
* `true` if current model has any optional property.
*/
hasOptional: {
type: Boolean,
computed: '_computeHasOptionalParameters(allowHideOptional, model.*)',
notify: true
},
/**
* If set it computes `hasOptional` property and shows checkbox in the
* form to show / hide optional properties.
*/
allowHideOptional: Boolean,
/**
* Computed flag to determine if optional checkbox can be rendered
*/
renderOptionalCheckbox: {
type: Boolean,
computed: '_computeRenderCheckbox(allowHideOptional, hasOptional)'
},
/**
* If set, enable / disable param checkbox is rendered next to each
* form item.
*/
allowDisableParams: Boolean,
/**
* When set, renders "add custom" item button.
* If the element is to be used withouth AMF model this should always
* be enabled. Otherwise users won't be able to add a parameter.
*/
allowCustom: Boolean,
/**
* Renders items in "narrow" view
*/
narrow: Boolean,
/**
* Computed value. The form renders empty message (if supported by
* the form element). It occurs when model is not set and allowCustom
* is not set
*/
renderEmptyMessage: {
type: Boolean,
value: true,
computed: '_computeRenderEmptyMessage(allowCustom, model)'
}
};
export const ApiFormMixin = (base) => class extends base {
static get properties() {
return {
/**
* View model to use to render the form.
*/
model: { type: Array },
/**
* Set to true to show optional parameters (not required by the API).
*/
optionalOpened: {
type: Boolean,
reflect: true
},
/**
* Computed value from `allowHideOptional` and view model.
* `true` if current model has any optional property.
*/
hasOptional: { type: Boolean },
/**
* If set it computes `hasOptional` property and shows checkbox in the
* form to show / hide optional properties.
*/
allowHideOptional: { type: Boolean },
/**
* Computed flag to determine if optional checkbox can be rendered
*/
renderOptionalCheckbox: { type: Boolean },
/**
* If set, enable / disable param checkbox is rendered next to each
* form item.
*/
allowDisableParams: { type: Boolean },
/**
* When set, renders "add custom" item button.
* If the element is to be used withouth AMF model this should always
* be enabled. Otherwise users won't be able to add a parameter.
*/
allowCustom: { type: Boolean },
/**
* Renders items in "narrow" view
*/
narrow: { type: Boolean },
/**
* Computed value. The form renders empty message (if supported by
* the form element). It occurs when model is not set and allowCustom
* is not set
*/
renderEmptyMessage: { type: Boolean }
};
}
get model() {
return this._model;
}
set model(value) {
if (this._sop('model', value)) {
this._notifyChanged('model', value);
this.renderEmptyMessage = this._computeRenderEmptyMessage(this.allowCustom, value);
this.hasOptional = this._computeHasOptionalParameters(this.allowHideOptional, value);
}
/**
* Computes class name for each form item depending on the item state.
*
* @param {Object} item Model item
* @param {Boolean} allowHideOptional
* @param {Boolean} optionalOpened True if optional parameters are rendered.
* @param {Boolean} allowDisableParams
* @return {String}
*/
computeFormRowClass(item, allowHideOptional, optionalOpened, allowDisableParams) {
let clazz = 'param-value';
if (item && item.required) {
clazz += ' required';
} else if (allowHideOptional) {
clazz += ' optional';
}
if (optionalOpened) {
clazz += ' with-optional';
}
if (allowDisableParams) {
clazz += ' has-enable-button';
}
return clazz;
}
get allowCustom() {
return this._allowCustom;
}
set allowCustom(value) {
if (this._sop('allowCustom', value)) {
this.renderEmptyMessage = this._computeRenderEmptyMessage(value, this.model);
}
/**
* Toggles visibility of optional parameters.
*/
toggleOptionalParams() {
if (!this.allowHideOptional) {
return;
}
this.optionalOpened = !this.optionalOpened;
}
get allowHideOptional() {
return this._allowHideOptional;
}
set allowHideOptional(value) {
if (this._sop('allowHideOptional', value)) {
this.hasOptional = this._computeHasOptionalParameters(value, this.model);
this.renderOptionalCheckbox = this._computeRenderCheckbox(value, this.hasOptional);
}
}
/**
* Returns a reference to the form element, if the DOM is ready.
* This only works with `iron-form` that is in the DOM.
*
* @return {IronForm} Iron form element. It may be `undefined` if local
* DOM is not yet initialized.
*/
_getForm() {
if (!this.$.form && this.shadowRoot) {
this.$.form = this.shadowRoot.querySelector('iron-form');
}
return this.$.form;
get hasOptional() {
return this._hasOptional;
}
set hasOptional(value) {
if (this._sop('hasOptional', value)) {
this._notifyChanged('hasOptional', value);
this.renderOptionalCheckbox = this._computeRenderCheckbox(this.allowHideOptional, value);
}
/**
* Validates the form. It uses `iron-form`'s `validate()` function to
* perform the validation.
* @return {Boolean} Validation result or `true` if DOM is not yet ready.
*/
_getValidity() {
const form = this._getForm();
if (!form) {
return true;
}
return form.validate();
}
constructor() {
super();
this.renderEmptyMessage = true;
}
_sop(prop, value) {
const key = `_${prop}`;
const old = this[key];
if (old === value) {
return false;
}
/**
* Link to the form's serialize function.
* @return {Object} Serialized form values or `undefined` if DOM is not ready.
* Note, `undefined` is returned **only** if DOM is not yet ready.
*/
serializeForm() {
const form = this._getForm();
if (!form) {
return;
this[key] = value;
this.requestUpdate(prop, old);
return true;
}
_notifyChanged(prop, value) {
this.dispatchEvent(new CustomEvent(prop + '-changed', {
composed: true,
detail: {
value
}
return form.serializeForm();
}));
}
/**
* Computes class name for each form item depending on the item state.
*
* @param {Object} item Model item
* @param {Boolean} allowHideOptional
* @param {Boolean} optionalOpened True if optional parameters are rendered.
* @param {Boolean} allowDisableParams
* @return {String}
*/
computeFormRowClass(item, allowHideOptional, optionalOpened, allowDisableParams) {
let clazz = 'param-value';
if (item && item.required) {
clazz += ' required';
} else if (allowHideOptional) {
clazz += ' optional';
}
/**
* Computes if any of the parameters are required.
* It iterates over the model to find any first element that has `required`
* propeerty set to `false`.
*
* @param {Boolean} allowHideOptional State of `allowHideOptional` property.
* If `false` this function always returns `false`.
* @param {Object} record Change record. Note, it does not checks for
* optional parameters each time the model changes. It rescans the model
* when splices changed.
* @return {Boolean} `true` if model has at leas one alement that is not required.
*/
_computeHasOptionalParameters(allowHideOptional, record) {
if (!allowHideOptional || !record.base || !record.path) {
return false;
}
if (record.path !== 'model' && record.path !== 'model.splices') {
return this.hasOptional;
}
return record.base.some((item) => item.required === false);
if (optionalOpened) {
clazz += ' with-optional';
}
/**
* Computes value for `renderOptionalCheckbox` property.
*
* @param {Boolean} render Value of `allowHideOptional` property
* @param {Boolean} has Value of `hasOptional` property.
* @return {Boolean} True if both values are `true`.
*/
_computeRenderCheckbox(render, has) {
return render && has;
if (allowDisableParams) {
clazz += ' has-enable-button';
}
/**
* Computes if given model item is a custom property (not generated by
* AMF model transformation).
* @param {Object} model Model item.
* @return {Boolean} `true` if `isCustom` property is set on model's schema
* property.
*/
_computeIsCustom(model) {
if (!model || !model.schema || !model.schema.isCustom) {
return false;
}
return clazz;
}
/**
* Toggles visibility of optional parameters.
*/
toggleOptionalParams() {
if (!this.allowHideOptional) {
return;
}
this.optionalOpened = !this.optionalOpened;
}
/**
* Returns a reference to the form element, if the DOM is ready.
* This only works with `iron-form` that is in the DOM.
*
* @return {IronForm} Iron form element. It may be `undefined` if local
* DOM is not yet initialized.
*/
_getForm() {
if (!this.__form && this.shadowRoot) {
this.__form = this.shadowRoot.querySelector('iron-form');
}
return this.__form;
}
/**
* Validates the form. It uses `iron-form`'s `validate()` function to
* perform the validation.
* @return {Boolean} Validation result or `true` if DOM is not yet ready.
*/
_getValidity() {
const form = this._getForm();
if (!form) {
return true;
}
/**
* Adds empty custom property to the list.
*
* It dispatches `api-property-model-build` custom event that is handled by
* `api-view-model-transformer` to build model item.
* This assumem that the transformer element is already in the DOM.
*
* After the transformation the element pushes or sets the data to the
* `model`.
*
* @param {String} binding Value if the `binding` property.
* @param {Object} opts Additional options:
* - inputLabel {String} - Forces a label of the input
*/
addCustom(binding, opts) {
if (!opts) {
opts = {};
}
const e = new CustomEvent('api-property-model-build', {
bubbles: true,
composed: true,
cancelable: true,
detail: {
name: opts.name || '',
value: opts.value || '',
binding: binding,
schema: {
enabled: true,
isCustom: true,
inputLabel: opts.inputLabel || undefined
}
return form.validate();
}
/**
* Link to the form's serialize function.
* @return {Object} Serialized form values or `undefined` if DOM is not ready.
* Note, `undefined` is returned **only** if DOM is not yet ready.
*/
serializeForm() {
const form = this._getForm();
if (!form) {
return;
}
return form.serializeForm();
}
/**
* Computes if any of the parameters are required.
* It iterates over the model to find any first element that has `required`
* propeerty set to `false`.
*
* @param {Boolean} allowHideOptional State of `allowHideOptional` property.
* If `false` this function always returns `false`.
* @param {Object} model Current model
* @return {Boolean} `true` if model has at leas one alement that is not required.
*/
_computeHasOptionalParameters(allowHideOptional, model) {
if (!allowHideOptional || !model) {
return false;
}
return model.some((item) => item.required === false);
}
/**
* Computes value for `renderOptionalCheckbox` property.
*
* @param {Boolean} render Value of `allowHideOptional` property
* @param {Boolean} has Value of `hasOptional` property.
* @return {Boolean} True if both values are `true`.
*/
_computeRenderCheckbox(render, has) {
return render && has;
}
/**
* Computes if given model item is a custom property (not generated by
* AMF model transformation).
* @param {Object} model Model item.
* @return {Boolean} `true` if `isCustom` property is set on model's schema
* property.
*/
_computeIsCustom(model) {
if (!model || !model.schema || !model.schema.isCustom) {
return false;
}
return true;
}
/**
* Adds empty custom property to the list.
*
* It dispatches `api-property-model-build` custom event that is handled by
* `api-view-model-transformer` to build model item.
* This assumem that the transformer element is already in the DOM.
*
* After the transformation the element pushes or sets the data to the
* `model`.
*
* @param {String} binding Value if the `binding` property.
* @param {Object} opts Additional options:
* - inputLabel {String} - Forces a label of the input
*/
addCustom(binding, opts) {
if (!opts) {
opts = {};
}
const e = new CustomEvent('api-property-model-build', {
bubbles: true,
composed: true,
cancelable: true,
detail: {
name: opts.name || '',
value: opts.value || '',
binding: binding,
schema: {
enabled: true,
isCustom: true,
inputLabel: opts.inputLabel || undefined
}
});
this.dispatchEvent(e);
if (this.model) {
this.push('model', e.detail);
} else {
this.set('model', [e.detail]);
}
this.optionalOpened = true;
});
this.dispatchEvent(e);
const model = this.model || [];
this.model = [...model, e.detail];
this.optionalOpened = true;
}
/**
* Removes custom item from the UI.
* This can only be called from `dom-repeat` element so it contain's
* `model` property.
*
* @param {CustomEvent} e
*/
_removeCustom(e) {
const index = Number(e.target.dataset.index);
if (index !== index) {
return;
}
/**
* Removes custom item from the UI.
* This can only be called from `dom-repeat` element so it contain's
* `model` property.
*
* @param {CustomEvent} e
*/
_removeCustom(e) {
this.splice('model', e.model.get('index'), 1);
const model = this.model;
if (!model || !model.length) {
return;
}
/**
* Computes if model item is optional.
* The items is always optional if is not required and when `hasOptional`
* is set to `true`.
*
* @param {Boolean} hasOptional [description]
* @param {Object} model Model item.
* @return {Boolean} `true` if the model item is optional in the form.
*/
computeIsOptional(hasOptional, model) {
if (!hasOptional) {
return false;
}
if (!model || !model.required) {
return true;
}
model.splice(index, 1);
this.model = Array.from(model);
}
/**
* Computes if model item is optional.
* The items is always optional if is not required and when `hasOptional`
* is set to `true`.
*
* @param {Boolean} hasOptional [description]
* @param {Object} model Model item.
* @return {Boolean} `true` if the model item is optional in the form.
*/
computeIsOptional(hasOptional, model) {
if (!hasOptional) {
return false;
}
/**
* Computes value for `renderEmptyMessage`.
*
* @param {Boolean} allowCustom True if the form allows to add custom values.
* @param {?Array} model Current model
* @return {Boolean} `true` when allowCustom is falsy set and model is empty
*/
_computeRenderEmptyMessage(allowCustom, model) {
return !allowCustom && !model;
if (!model || !model.required) {
return true;
}
return false;
}
return AFmixin;
});
/**
* Computes value for `renderEmptyMessage`.
*
* @param {Boolean} allowCustom True if the form allows to add custom values.
* @param {?Array} model Current model
* @return {Boolean} `true` when allowCustom is falsy set and model is empty
*/
_computeRenderEmptyMessage(allowCustom, model) {
return !allowCustom && !model;
}
};

@@ -1,3 +0,2 @@

import '../../@polymer/polymer/polymer-element.js';
const $documentContainer = document.createElement('template');
import { css } from 'lit-element';

@@ -9,18 +8,8 @@ /**

----------------|-------------|----------
`--api-form-row` | Mixin applied to API form rows. Each row already applies `--layout-horizontal` and `--layout-start` | `{}`
`--api-form-row-narrow` | Mixin applied to API form rows when `narrow` property is set | `{}`
`--api-form-row-optional` | Mixin applied to optional row of the form (not required). By default this form row is hidden from the view | `{}`
`--api-form-row-optional-visible` | Mixin applied to optional row of the form when it becomes visible | `{}`
`--api-form-action-button-color` | Color of the action button in the form. Action buttons should perform form's primary actions like "submit" or "add new". Use `--api-form-action-icon-*` for icons related styling | `--secondary-button-color` or `--accent-color`
`--api-form-action-button-background-color` | Similar to `--api-form-action-button-color` but it's background color | `--secondary-button-background`
`--secondary-button` | Mixin applied to the action button. This is more general theme element. This values can be overriten by `--api-form-action-button` | `{}`
`--api-form-action-button` | Mixin applied to the action button | `{}`
`--api-form-action-button-hover-color` | Color of the action button in the form when hovering. | `--secondary-button-color` or `--accent-color`
`--api-form-action-button-hover-background-color` | Similar to `--api-form-action-button-hover-color` but it's background color | `--secondary-button-background`
`--secondary-button-hover` | Mixin applied to the action button when hovered. This is more general theme element. This values can be overriten by `--api-form-action-button` | `{}`
`--api-form-action-button-hover` | Mixin applied to the action button when hovered. | `{}`
`--hint-trigger-color` | Color of the form action icon button to dispay documentation for the item. | `rgba(0, 0, 0, 0.74)`
`--icon-button` | Mixin applied to the icon button to dispay documentation for the item | `{}`
`--hint-trigger-hover-color` | Color of the form action icon button to dispay documentation for the item when hovered | `rgba(0, 0, 0, 0.74)`
`--icon-button-hover` | Mixin applied to the icon button to dispay documentation for the item when hovered | `{}`
`--api-form-action-icon-color` | Color of any other than documentation icon button in form row | `--icon-button-color` or `rgba(0, 0, 0, 0.74)`

@@ -33,101 +22,88 @@ `--api-form-action-icon-hover-color` | Color of any other than documentation icon button in form row when hovering | `--accent-color` or `rgba(0, 0, 0, 0.88)`

$documentContainer.innerHTML = `<dom-module id="api-form-styles">
<template>
<style>
.form-item {
@apply --layout-horizontal;
@apply --layout-start;
@apply --api-form-row;
}
export default css`
.form-item {
display: flex;
flex-direction: row;
align-items: flex-start;
}
:host([narrow]) .form-item,
.narrow .form-item {
display: block;
@apply --api-form-row-narrow;
}
:host([narrow]) .form-item,
.narrow .form-item {
display: block;
}
.form-item[data-optional] {
display: none;
@apply --api-form-row-optional;
}
.form-item[data-optional] {
display: none;
}
:host([optional-opened]) [data-optional] {
@apply --layout-horizontal;
@apply --api-form-row-optional-visible;
}
/* General action button like "add property" etc */
.action-button {
transition: color 0.25s ease-in-out, background-color 0.25s ease-in-out;
margin: var(--api-form-action-button-margin-top, 0);
color: var(--api-form-action-button-color, var(--secondary-button-color, var(--accent-color)));
background-color: var(--api-form-action-button-background-color, var(--secondary-button-background));
@apply --secondary-button;
@apply --api-form-action-button;
}
:host([optional-opened]) [data-optional] {
display: flex;
flex-direction: row;
}
/* General action button like "add property" etc */
.action-button {
transition: color 0.25s ease-in-out, background-color 0.25s ease-in-out;
margin: var(--api-form-action-button-margin-top, 0);
color: var(--api-form-action-button-color, var(--secondary-button-color, var(--accent-color)));
background-color: var(--api-form-action-button-background-color, var(--secondary-button-background));
}
.action-button:hover {
color: var(--api-form-action-button-hover-color, var(--secondary-button-color, var(--accent-color)));
background-color: var(--api-form-action-button-hover-background-color, var(--secondary-button-background));
@apply --secondary-button-hover;
@apply --api-form-action-button-hover;
}
/* Any icon element inside the action button should inherit the same styles */
.action-button .action-icon {
margin-right: 12px;
color: var(--api-form-action-button-color, var(--secondary-button-color, var(--accent-color)));
}
.action-button:hover {
color: var(--api-form-action-button-hover-color, var(--secondary-button-color, var(--accent-color)));
background-color: var(--api-form-action-button-hover-background-color, var(--secondary-button-background));
}
/* Any icon element inside the action button should inherit the same styles */
.action-button .action-icon {
margin-right: 12px;
color: var(--api-form-action-button-color, var(--secondary-button-color, var(--accent-color)));
}
.action-button:hover .action-icon {
color: var(--api-form-action-button-hover-color, var(--secondary-button-color, var(--accent-color)));
}
.action-button:hover .action-icon {
color: var(--api-form-action-button-hover-color, var(--secondary-button-color, var(--accent-color)));
}
/* Icons that provide additional documentation to the form item */
.hint-icon {
color: var(--hint-trigger-color, rgba(0, 0, 0, 0.74));
transition: color 0.25s ease-in-out;
@apply --icon-button;
}
/* Icons that provide additional documentation to the form item */
.hint-icon {
color: var(--hint-trigger-color, rgba(0, 0, 0, 0.74));
transition: color 0.25s ease-in-out;
}
.hint-icon:hover {
color: var(--hint-trigger-hover-color, var(--accent-color, rgba(0, 0, 0, 0.88)));
@apply --icon-button-hover;
}
/* An icon in the form row that performs some action other than documentation. */
.action-icon {
color: var(--api-form-action-icon-color, var(--icon-button-color, rgba(0, 0, 0, 0.74)));
transition: color 0.2s ease-in-out;
}
.hint-icon:hover {
color: var(--hint-trigger-hover-color, var(--accent-color, rgba(0, 0, 0, 0.88)));
}
/* An icon in the form row that performs some action other than documentation. */
.action-icon {
color: var(--api-form-action-icon-color, var(--icon-button-color, rgba(0, 0, 0, 0.74)));
transition: color 0.2s ease-in-out;
}
.action-icon:hover {
color: var(--api-form-action-icon-hover-color, var(--accent-color, rgba(0, 0, 0, 0.88)));
}
/* styling form inline markdown */
marked-element {
background-color: var(--inline-documentation-background-color, #FFF3E0);
padding: 4px;
}
/* wrapped for \`marked-element\` */
.docs {
@apply --arc-font-body1;
color: var(--inline-documentation-color, rgba(0, 0, 0, 0.87));
margin-right: 40px;
}
.action-icon:hover {
color: var(--api-form-action-icon-hover-color, var(--accent-color, rgba(0, 0, 0, 0.88)));
}
/* styling form inline markdown */
arc-marked {
background-color: var(--inline-documentation-background-color, #FFF3E0);
padding: 4px;
}
/* wrapped for \`arc-marked\` */
.docs {
font-size: var(--arc-font-body1-font-size);
font-weight: var(--arc-font-body1-font-weight);
line-height: var(--arc-font-body1-line-height);
color: var(--inline-documentation-color, rgba(0, 0, 0, 0.87));
margin-right: 40px;
}
.markdown-body * {
font-size: var(--inline-documentation-font-size, 13px) !important;
}
.markdown-body * {
font-size: var(--inline-documentation-font-size, 13px) !important;
}
.markdown-body p:first-child {
margin-top: 0;
padding-top: 0;
}
.markdown-body p:first-child {
margin-top: 0;
padding-top: 0;
}
.markdown-body p:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
</style>
</template>
</dom-module>`;
document.head.appendChild($documentContainer.content);
.markdown-body p:last-child {
margin-bottom: 0;
padding-bottom: 0;
}`;

@@ -15,14 +15,13 @@ {

"name": "@api-components/api-form-mixin",
"version": "3.0.0-preview.2",
"version": "3.0.0-preview.3",
"license": "Apache-2.0",
"devDependencies": {
"@polymer/gen-typescript-declarations": "^1.6.0",
"@polymer/iron-demo-helpers": "^3.0.0",
"@webcomponents/webcomponentsjs": "^2.0.0",
"@polymer/iron-component-page": "^4.0.0",
"@advanced-rest-client/arc-demo-helper": "^1.0.3",
"@advanced-rest-client/testing-karma-sl": "^1.0.2",
"@open-wc/testing": "^0.11.1",
"@open-wc/testing-karma": "^2.0.6",
"@polymer/gen-typescript-declarations": "^1.6.2",
"@polymer/iron-form": "^3.0.0",
"@polymer/test-fixture": "^4.0.2",
"chai": "^4.2.0",
"mocha": "^5.2.0",
"wct-mocha": "^1.0.0"
"@webcomponents/webcomponentsjs": "^2.2.10",
"lit-element": "^2.0.1"
},

@@ -35,5 +34,7 @@ "main": "api-form-mixin.js",

"scripts": {
"lint": "polymer lint api-form-mixin.html",
"test-sauce": "polymer test --plugin sauce --job-name \"api-form-mixin:local-test\"",
"test": "polymer test --plugin local",
"test": "karma start --coverage",
"test:watch": "karma start --auto-watch=true --single-run=false",
"test:legacy": "karma start --legacy --coverage",
"test:legacy:watch": "karma start --legacy --auto-watch=true --single-run=false",
"test:sl": "karma start karma.sl.config.js --legacy --coverage",
"update-types": "gen-typescript-declarations --deleteExisting --outDir ."

@@ -50,4 +51,4 @@ },

"dependencies": {
"@polymer/polymer": "^3.0.0"
"lit-element": "^2.0.1"
}
}

@@ -7,11 +7,85 @@ [![Build Status](https://travis-ci.org/advanced-rest-client/api-form-mixin.svg?branch=stage)](https://travis-ci.org/advanced-rest-client/api-form-mixin)

A behavior to be implemented to elements that processes AMF data via form data model and displays forms from the model.
A mixin to be used with elements that processes AMF data via form data model and displays forms from the model.
It contains common methods used in forms.
Use `api-form-styles` from this package to include common styles.
### API components
This components is a part of [API components ecosystem](https://elements.advancedrestclient.com/)
### API components
## Usage
This components is a part of API components ecosystem: https://elements.advancedrestclient.com/
### Installation
```
npm install --save @api-components/api-form-mixin
```
### In a LitElement
```js
import { LitElement, html, css } from 'lit-element';
import { ApiFormMixin } from '@api-components/api-form-mixin/api-form-mixin.js';
import styles from '@api-components/api-form-mixin/api-form-styles.js';
import '@polymer/iron-form/iron-form.js';
class SampleElement extends PolymerElement {
static get styles() {
return [
styles,
css`:host {
display: block;
}`
];
}
render() {
const { model: items, allowHideOptional, optionalOpened, allowDisableParams } = this;
return html`
<h1>Form</h1>
<iron-form>
<form enctype="application/json">
${items ? items.map((item, index) => html`<div class="form-item">
<div class="${this.computeFormRowClass(item, allowHideOptional, optionalOpened, allowDisableParams)}">
<input
data-index="${index}"
type="text"
name="${item.name}"
?required="${item.required}"
.value="${item.value}"
@change="${this._modelValueChanged}">
</div>
</div>`) : undefined}
</form>
</iron-form>`;
}
_modelValueChanged(e) {
const index = Number(e.target.dataset.index);
if (index !== index) {
return;
}
this.model[index].value = e.target.value;
this._requestRender();
}
}
customElements.define('sample-element', SampleElement);
```
### Installation
```sh
git clone https://github.com/advanced-rest-client/api-form-mixin
cd api-form-mixin
npm install
```
### Running the demo locally
```sh
npm start
```
### Running the tests
```sh
npm test
```