1; (function (root, factory) { 2 const ns = root.FieldKit = root.FieldKit || {}; 3 ns.icons = ns.icons || {}; 4 5 factory(ns); 6})(window, function (ns) { 7 class FieldGroup { 8 /** @type {HTMLElement} */ 9 host; 10 11 /** 12 * Lookup by id (label anchors, JS that has the element). 13 * 14 * @type {Record<string, Field>} 15 */ 16 idMap = {}; 17 18 /** 19 * Lookup by name (server payloads, FormData, etc.). 20 * 21 * @type {Record<string, Field>} 22 */ 23 nameMap = {}; 24 25 /** 26 * @param {HTMLElement} host 27 * @private 28 */ 29 constructor(host) { 30 this.host = host; 31 } 32 33 /** 34 * Register a new {@link Field} so it can be looked up by either 35 * its **id** or **name** attribute. 36 * 37 * @param {Field} field 38 * @returns {void} 39 */ 40 addField(field) { 41 // always index by id 42 if (field.id) { 43 this.idMap[field.id] = field; 44 } 45 46 // if the element has a `name`, index by that as well 47 const name = field.element.name; 48 if (name) { 49 this.nameMap[name] = field; 50 } 51 } 52 53 /** 54 * Bulk-apply or clear errors on every registered field. 55 * 56 * @param {Object} object 57 * @returns {void} 58 */ 59 setErrors(object = {}) { 60 // clear previous errors 61 Object.values(this.idMap).forEach(field => field.clearError()); 62 63 // apply fresh ones 64 Object.entries(object).forEach(([key, error]) => { 65 const field = this.idMap[key] || this.nameMap[key] 66 if (!field) { 67 return; 68 } 69 70 field.setError(error, key); 71 }); 72 } 73 } 74 75 /** 76 * Singleton map 77 * @type {WeakMap<HTMLElement, FieldGroup>} 78 */ 79 FieldGroup._map = new WeakMap(); 80 81 /** 82 * Get or create the {@link FieldGroup} for a given host element. 83 * 84 * @param {HTMLElement} host 85 * @returns {FieldGroup} 86 */ 87 FieldGroup.get = function (host) { 88 if (!FieldGroup._map.has(host)) { 89 FieldGroup._map.set(host, new FieldGroup(host)); 90 } 91 return FieldGroup._map.get(host); 92 }; 93 94 /** 95 * Find the {@link Field} that matches a given **id**â¯/â¯**name**. 96 * 97 * If **scope** is provided, the search is limited to the group hosted by that 98 * element (or the element matched by the selector). 99 * Without a scope it scans every registered form / `[data-fieldâgroup]` 100 * on the page, falling back toâ¯`<body>` as the default group. 101 * 102 * @param {string} key The field's *id* or *name*. 103 * @param {HTMLElement|string|null} [scope] Host element or CSS selector to restrict the lookup. 104 * @returns {Field|null} The matching {@link Field} instance, or `null` if none is found. 105 */ 106 FieldGroup.getField = function (key, scope = null) { 107 // 1. scoped lookup 108 if (scope) { 109 const host = typeof scope === 'string' ? document.querySelector(scope) : scope; 110 const group = FieldGroup.get(host); 111 return group.idMap[key] || group.nameMap[key] || null; 112 } 113 114 // 2. global lookup across every registered group 115 const groups = document.querySelectorAll('form,[data-field-group],body'); 116 for (const host of groups) { 117 const group = FieldGroup.get(host); 118 const field = group.idMap[key] || group.nameMap[key]; 119 if (field) { 120 return field; 121 } 122 } 123 return null; 124 } 125 126 // Namespace export 127 ns.FieldGroup = FieldGroup; 128});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.