UNPKG

@abgov/ui-components-common

Version:

Government of Alberta - UI Web components

437 lines (436 loc) 13 kB
var S = Object.defineProperty; var g = (r) => { throw TypeError(r); }; var M = (r, t, e) => t in r ? S(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e; var l = (r, t, e) => M(r, typeof t != "symbol" ? t + "" : t, e), w = (r, t, e) => t.has(r) || g("Cannot " + e); var y = (r, t, e) => t.has(r) ? g("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(r) : t.set(r, e); var _ = (r, t, e) => (w(r, t, "access private method"), e); class x { constructor(t) { l(this, "validators"); this.validators = t || {}; } add(t, ...e) { this.validators[t] = e; } validate(t) { const e = {}; return Object.entries(this.validators).forEach(([n, i]) => { const a = i.map((s) => s(t[n])).find((s) => !!s); a && (e[n] = a); }), e; } } function I() { return [ c("Day is required"), h({ min: 1, max: 31, minMsg: "Day must be between 1 and 31", maxMsg: "Day must be between 1 and 31" }) ]; } function L() { return [ c("Month is required"), h({ min: 0, max: 11, minMsg: "Month must be between Jan and Dec", maxMsg: "Month must be between Jan and Dec" }) ]; } function U() { const r = (/* @__PURE__ */ new Date()).getFullYear(); return [ c("Year is required"), h({ min: 1900, max: r, minMsg: "Year must be greater than 1900", maxMsg: `Year must be less than ${r}` }) ]; } function c(r) { return (t) => (r = r || "Required", typeof t == "number" && !isNaN(t) || t ? "" : r); } function T(r) { const t = new RegExp(/^\+?[\d-() ]{10,18}$/); return m(t, r || "Invalid phone number"); } function q(r) { const t = new RegExp( /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ ); return m(t, r || "Invalid email address"); } function Y() { return (r) => { if (!r) return ""; const t = "121212121".split("").map((i) => parseInt(i)), e = r.replace(/\D/g, ""); return e.length !== 9 ? "SIN must contain 9 numbers" : e.split("").map((i) => parseInt(i)).map((i, a) => { const s = i * t[a]; return s < 10 ? s : `${s}`.split("").map((u) => parseInt(u)).reduce((u, f) => u + f, 0); }).reduce((i, a) => i + a, 0) % 10 === 0 ? "" : "Invalid SIN"; }; } function J() { return m( /^[ABCEGHJ-NPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][ -]?\d[ABCEGHJ-NPRSTV-Z]\d$/i, "Invalid postal code" ); } function m(r, t) { return (e) => !e || e.match(r) ? "" : t; } function O({ invalidMsg: r, minMsg: t, maxMsg: e, min: n, max: i } = {}) { return (a) => { let s = /* @__PURE__ */ new Date(-1); return `${a || ""}`.length === 0 ? "" : (typeof a == "string" && (s = new Date(a)), typeof a == "number" && (s = new Date(a)), a.toDateString && (s = a), s.getDate() === -1 ? r || "Invalid date" : n && s < n ? t || `Must be after ${n}` : i && s > i ? e || `Must be before ${i}` : ""); }; } function h({ invalidTypeMsg: r, minMsg: t, maxMsg: e, min: n = -Number.MAX_VALUE, max: i = Number.MAX_VALUE } = {}) { return (a) => { let s = Number.MAX_VALUE; return `${a ?? ""}`.length === 0 ? "" : (typeof a == "string" && (s = parseFloat(a)), typeof a == "number" && (s = a), isNaN(s) ? r || "Must be a numeric value" : s > i ? e || `Must be less than or equal to ${i}` : s < n ? t || `Must be greater than or equal to ${n}` : ""); }; } function X({ invalidTypeMsg: r, minMsg: t, maxMsg: e, min: n = -Number.MAX_VALUE, max: i = Number.MAX_VALUE }) { return (a) => `${a || ""}`.length === 0 ? "" : typeof a != "string" ? r || "Invalid type" : a.length > i ? e || `Must be less than ${i} characters` : a.length < n ? t || `Must be greater than ${n} characters` : ""; } function v(r, t, e, n) { if (!r) { console.error("dispatch element is null"); return; } r.dispatchEvent( new CustomEvent(t, { composed: !0, bubbles: n == null ? void 0 : n.bubbles, detail: e }) ); } function o(r, t, e, n) { if (!r) { console.error("dispatch element is null"); return; } r.dispatchEvent( new CustomEvent("msg", { composed: !0, bubbles: n == null ? void 0 : n.bubbles, detail: { action: t, data: e } }) ); } var d, E, A; class j { constructor(t) { y(this, d); l(this, "state"); l(this, "_formData"); l(this, "_formRef"); l(this, "_isCompleting", !1); this.type = t; } // Obtain reference to the form element init(t) { if (this._formRef) { console.warn("init: form element has already been set"); return; } this._formRef = t.detail.el, this.state = { uuid: crypto.randomUUID(), form: {}, history: [], editting: "", status: "not-started" }; } initList(t) { this._formRef = t.detail.el, this.state = []; } // Public method to allow for the initialization of the state initState(t, e) { o(this._formRef, "external::init:state", t), typeof t == "string" ? this.state = JSON.parse(t) : Array.isArray(t) || (this.state = t), e && setTimeout(e, 200); } updateListState(t) { const e = t.detail; Array.isArray(e.data) && (this.state = e.data); } updateObjectState(t) { var n, i; if (Array.isArray(this.state)) return; const e = t.detail; e.type === "list" ? this.state = { ...this.state, form: { ...((n = this.state) == null ? void 0 : n.form) || {}, [e.id]: e.data } } : this.state = { ...this.state, ...e.data, form: { ...((i = this.state) == null ? void 0 : i.form) || {}, ...e.data.form }, history: e.data.history }; } getStateList() { return this.state ? Array.isArray(this.state) ? this.state.length === 0 ? [] : this.state.map((t) => Object.values(t.form).filter((e) => { var n; return ((n = e == null ? void 0 : e.data) == null ? void 0 : n.type) === "details"; }).map((e) => { var n; return e.data.type === "details" && ((n = e.data) == null ? void 0 : n.fieldsets) || {}; }).reduce( (e, n) => { for (const [i, a] of Object.entries(n)) e[i] = a.value; return e; }, {} )) : (console.warn( "Utils:getStateList: unable to update the state of a non-multi form type", this.state ), []) : []; } // getStateItems(group: string): Record<string, FieldsetItemState>[] { // if (Array.isArray(this.state)) { // console.error( // "Utils:getStateItems: unable to update the state of a multi form type", // ); // return []; // } // if (!this.state) { // console.error("Utils:getStateItems: state has not yet been set"); // return []; // } // // const data = this.state.form[group].data; // if (data.type !== "list") { // return []; // } // // return data.items.; // } // Public method to allow for the retrieval of the state value getStateValue(t, e) { if (Array.isArray(this.state)) return console.error("getStateValue: unable to update the state of a multi form type"), ""; if (!this.state) return console.error("getStateValue: state has not yet been set"), ""; const n = this.state.form[t].data; return n.type !== "details" ? "" : n.fieldsets[e].value; } // Public method to allow for the continuing to the next page continueTo(t) { if (!t) { console.error("continueTo [name] is undefined"); return; } o(this._formRef, "external::continue", { next: t }); } // Public method to perform validation and send the appropriate messages to the form elements validate(t, e, n, i) { var b; const { el: a, state: s, cancelled: u } = t.detail, f = (b = s == null ? void 0 : s[e]) == null ? void 0 : b.value; if (window.scrollTo({ top: 0, behavior: "smooth" }), u) return [!0, f]; for (const V of n) { const p = V(f); if (_(this, d, A).call(this, a, e, p, i), p) return [!1, ""]; } return [!0, f]; } /** * Validates a group of fields ensuring that at least `minPassCount` of the items within the group * passes. This is useful in the scenario when n number fields are required out of n+m number of fields. * * @param {string[]} fields - An array of field names to be validated. * @param {Event} e - The event object associated with the validation trigger. * @param {FieldValidator[]} validators - An array of validator functions to apply to the fields. * @return {[number, Record<string, boolean>]} - Returns back the number of fields that passed and a record of the fields and their pass status. */ validateGroup(t, e, n) { let i = 0; const a = {}; for (const s of e) { const [u] = this.validate(t, s, n, { grouped: !0 }); u && (a[s] = !0, i++); } return [i, a]; } edit(t) { o(this._formRef, "external::alter:state", { index: t, operation: "edit" }); } remove(t) { o(this._formRef, "external::alter:state", { index: t, operation: "remove" }); } /** * Completes the form and triggers the onComplete callback. * This method should be used when you want to complete a form without navigating to a summary page. * * @important Developers must validate the form before calling this method. * * @example * // Validate first, then complete * const [isValid] = this.validate(e, "firstName", [ * requiredValidator("First name is required.") * ]); * if (isValid) { * this.complete(); * } * @returns void */ complete() { if (!this._formRef) { console.error("complete: form ref is not set"); return; } if (this._isCompleting) { console.warn("complete: completion already in progress"); return; } this._isCompleting = !0, o(this._formRef, "fieldset::submit", null, { bubbles: !0 }), this._isCompleting = !1; } /** * Completes a subform and returns control to the parent form. * This method should be used when working with subforms that need to complete without a summary page. * * @important Developers must validate the subform before calling this method. * * @example * // Validate first, then complete the subform * const [isValid] = this._childFormController.validate(e, "fullName", [ * requiredValidator("Please enter the dependent's full name.") * ]); * if (isValid) { * this._childFormController.completeSubform(); * } * @returns void */ completeSubform() { if (!this._formRef) { console.error("completeSubform: form ref is not set"); return; } if (this._isCompleting) { console.warn("completeSubform: completion already in progress"); return; } const t = this._formRef; this._isCompleting = !0; const e = (n) => { t.removeEventListener("_stateChange", e), v(t, "_complete", {}, { bubbles: !0 }), this._isCompleting = !1; }; t.addEventListener("_stateChange", e), v(t, "_continue", null, { bubbles: !0 }); } // removes any data collected that doesn't correspond with the final history path clean(t) { return t.history.reduce((e, n) => (e[n] = t.form[n], e), {}); } } d = new WeakSet(), E = function(t) { var e; Array.isArray(t.data) && (Array.isArray(this.state) || (this.state = { ...this.state, form: { ...((e = this.state) == null ? void 0 : e.form) || {}, [t.id]: t.data } })); }, // Private method to dispatch the error message to the form element A = function(t, e, n, i) { t.dispatchEvent( new CustomEvent("msg", { composed: !0, detail: { action: "external::set:error", data: { name: e, msg: n, grouped: i == null ? void 0 : i.grouped } } }) ); }; const R = ["basic", "success", "failure"]; function C(r, t) { const e = crypto.randomUUID(); return t = { uuid: e, type: "basic", ...t || {} }, !t.duration && t.type && R.includes(t.type) && (t.duration = "short"), o( document.body, "goa:temp-notification", { message: r, ...t }, { bubbles: !0 } ), e; } function N(r) { o( document.body, "goa:temp-notification:dismiss", r, { bubbles: !0 } ); } function D(r, t) { o( document.body, "goa:temp-notification:progress", { uuid: r, progress: t }, { bubbles: !0 } ); } const G = { show: C, dismiss: N, setProgress: D }; export { x as FormValidator, j as PublicFormController, Y as SINValidator, G as TemporaryNotification, I as birthDayValidator, L as birthMonthValidator, U as birthYearValidator, O as dateValidator, v as dispatch, q as emailValidator, X as lengthValidator, h as numericValidator, T as phoneNumberValidator, J as postalCodeValidator, m as regexValidator, o as relay, c as requiredValidator };