UNPKG

framer-controller

Version:

Control components and state in Framer X with reusable controllers.

148 lines (147 loc) 5.42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Controller_1 = require("./Controller"); /** * Control forms. * When creating a form, use an object to define the form's `fields`. * For each field, provide a set of optional properties used to * determine the field's `data` entry: * - `defaultValue` - a value for new or reset fields * - `required` - a boolean (or method that takes the Form's state and returns a boolean) * - `validation` - method that takes the field's data value and returns a boolean * - `errorText` - a string (or method that takes the Form's state and returns a string) * - `hidden` - a boolean (or method that takes the Form's state and returns a boolean) * @example``` const controller = new FormController({ name: { defaultValue: "", validation: (v) => v.includes(" "), errorText: "Please provide a first and last name.", required: true, hidden: false } })``` */ class FormController extends Controller_1.Controller { constructor(options) { super({ fields: options, data: Object.keys(options).reduce((a, id) => (Object.assign({}, a, { [id]: { value: options[id].defaultValue, errorText: "", valid: false, hidden: false, required: false, } })), {}), ready: false, }); /** * Set the value of one of the form's data entries. * @param {keyof Form['fields']} id - The data entry's `id`. * @param {*} value - The data entry's new value. */ this.setValue = (id, value) => { const { data } = this.state; const state = this.getComputedState(Object.assign({}, data, { [id]: Object.assign({}, data[id], { value }) })); this.setState(state); }; this.setState(this.getComputedState(this.state.data)); } /** * Return the next state, given a set of incoming entries. * @param incoming */ getComputedState(incoming = {}) { let { fields, data } = this.state; let ready = true; // Merge in new entries data = Object.assign({}, data, incoming); // Set value-computed properties data = Object.keys(data).reduce((a, id) => { const field = this.state.fields[id]; const { value } = data[id]; const { validation, errorText: fieldErrorText } = field; let valid = false; let errorText = ""; // Does the field have value, and does that value pass validation? if (value !== undefined && value !== null) { valid = validation === undefined ? true : validation(value) ? true : false; // If not complete, use error text (as string or function) if provided errorText = value && !valid && fieldErrorText ? typeof fieldErrorText === "string" ? fieldErrorText : fieldErrorText(value) : ""; } return Object.assign({}, a, { [id]: { value, valid, errorText, hidden: false, required: false, } }); }, {}); // Now that the values are all set, set state-computed properties for (let id in data) { const field = this.state.fields[id]; data[id].hidden = field.hidden === undefined ? false : typeof field.hidden === "boolean" ? field.hidden : field.hidden({ fields, data, ready, }) ? true : false; data[id].required = field.required ? typeof field.required === "boolean" ? field.required : field.required({ fields, data, ready, }) : false; } // Finally, loop through to set ready state for (let id in data) { let entry = data[id]; if (entry.required && !entry.hidden && !entry.valid) { ready = false; } } return { fields, data, ready, }; } /** * The form's fields. */ get fields() { return this.state.fields; } /** * The form's data entries. For each field, the * - `value` - The entry's value. * - `valid` - Whether that value is `valid`, according to its `field.validation`. * - `errorText` - Any current `errorText` set on invalid fields. * - `required` - Whether the field is currently required. * - `hidden` - Whether the field is currently hidden. */ get data() { return this.state.data; } /** * Whether all required data entries are valid. */ get ready() { return this.state.ready; } } exports.FormController = FormController;