@uportal/form-builder 1.3.1 → 2.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.
@@ -0,0 +1,699 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2019 Google LLC
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+ const t$2=globalThis,e$3=t$2.ShadowRoot&&(void 0===t$2.ShadyCSS||t$2.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s$2=Symbol(),o$4=new WeakMap;let n$3 = class n{constructor(t,e,o){if(this._$cssResult$=true,o!==s$2)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e;}get styleSheet(){let t=this.o;const s=this.t;if(e$3&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=o$4.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&o$4.set(s,t));}return t}toString(){return this.cssText}};const r$3=t=>new n$3("string"==typeof t?t:t+"",void 0,s$2),i$3=(t,...e)=>{const o=1===t.length?t[0]:e.reduce(((e,s,o)=>e+(t=>{if(true===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+t[o+1]),t[0]);return new n$3(o,t,s$2)},S$1=(s,o)=>{if(e$3)s.adoptedStyleSheets=o.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet));else for(const e of o){const o=document.createElement("style"),n=t$2.litNonce;void 0!==n&&o.setAttribute("nonce",n),o.textContent=e.cssText,s.appendChild(o);}},c$2=e$3?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const s of t.cssRules)e+=s.cssText;return r$3(e)})(t):t;
7
+
8
+ /**
9
+ * @license
10
+ * Copyright 2017 Google LLC
11
+ * SPDX-License-Identifier: BSD-3-Clause
12
+ */const{is:i$2,defineProperty:e$2,getOwnPropertyDescriptor:h$1,getOwnPropertyNames:r$2,getOwnPropertySymbols:o$3,getPrototypeOf:n$2}=Object,a$1=globalThis,c$1=a$1.trustedTypes,l$1=c$1?c$1.emptyScript:"",p$1=a$1.reactiveElementPolyfillSupport,d$1=(t,s)=>t,u$1={toAttribute(t,s){switch(s){case Boolean:t=t?l$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,s){let i=t;switch(s){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t);}catch(t){i=null;}}return i}},f$1=(t,s)=>!i$2(t,s),b={attribute:true,type:String,converter:u$1,reflect:false,useDefault:false,hasChanged:f$1};Symbol.metadata??=Symbol("metadata"),a$1.litPropertyMetadata??=new WeakMap;let y$1 = class y extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t);}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,s=b){if(s.state&&(s.attribute=false),this._$Ei(),this.prototype.hasOwnProperty(t)&&((s=Object.create(s)).wrapped=true),this.elementProperties.set(t,s),!s.noAccessor){const i=Symbol(),h=this.getPropertyDescriptor(t,i,s);void 0!==h&&e$2(this.prototype,t,h);}}static getPropertyDescriptor(t,s,i){const{get:e,set:r}=h$1(this.prototype,t)??{get(){return this[s]},set(t){this[s]=t;}};return {get:e,set(s){const h=e?.call(this);r?.call(this,s),this.requestUpdate(t,h,i);},configurable:true,enumerable:true}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(d$1("elementProperties")))return;const t=n$2(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties);}static finalize(){if(this.hasOwnProperty(d$1("finalized")))return;if(this.finalized=true,this._$Ei(),this.hasOwnProperty(d$1("properties"))){const t=this.properties,s=[...r$2(t),...o$3(t)];for(const i of s)this.createProperty(i,t[i]);}const t=this[Symbol.metadata];if(null!==t){const s=litPropertyMetadata.get(t);if(void 0!==s)for(const[t,i]of s)this.elementProperties.set(t,i);}this._$Eh=new Map;for(const[t,s]of this.elementProperties){const i=this._$Eu(t,s);void 0!==i&&this._$Eh.set(i,t);}this.elementStyles=this.finalizeStyles(this.styles);}static finalizeStyles(s){const i=[];if(Array.isArray(s)){const e=new Set(s.flat(1/0).reverse());for(const s of e)i.unshift(c$2(s));}else void 0!==s&&i.push(c$2(s));return i}static _$Eu(t,s){const i=s.attribute;return false===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=false,this.hasUpdated=false,this._$Em=null,this._$Ev();}_$Ev(){this._$ES=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach((t=>t(this)));}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.();}removeController(t){this._$EO?.delete(t);}_$E_(){const t=new Map,s=this.constructor.elementProperties;for(const i of s.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t);}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return S$1(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(true),this._$EO?.forEach((t=>t.hostConnected?.()));}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach((t=>t.hostDisconnected?.()));}attributeChangedCallback(t,s,i){this._$AK(t,i);}_$ET(t,s){const i=this.constructor.elementProperties.get(t),e=this.constructor._$Eu(t,i);if(void 0!==e&&true===i.reflect){const h=(void 0!==i.converter?.toAttribute?i.converter:u$1).toAttribute(s,i.type);this._$Em=t,null==h?this.removeAttribute(e):this.setAttribute(e,h),this._$Em=null;}}_$AK(t,s){const i=this.constructor,e=i._$Eh.get(t);if(void 0!==e&&this._$Em!==e){const t=i.getPropertyOptions(e),h="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:u$1;this._$Em=e;const r=h.fromAttribute(s,t.type);this[e]=r??this._$Ej?.get(e)??r,this._$Em=null;}}requestUpdate(t,s,i){if(void 0!==t){const e=this.constructor,h=this[t];if(i??=e.getPropertyOptions(t),!((i.hasChanged??f$1)(h,s)||i.useDefault&&i.reflect&&h===this._$Ej?.get(t)&&!this.hasAttribute(e._$Eu(t,i))))return;this.C(t,s,i);} false===this.isUpdatePending&&(this._$ES=this._$EP());}C(t,s,{useDefault:i,reflect:e,wrapped:h},r){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,r??s??this[t]),true!==h||void 0!==r)||(this._$AL.has(t)||(this.hasUpdated||i||(s=void 0),this._$AL.set(t,s)),true===e&&this._$Em!==t&&(this._$Eq??=new Set).add(t));}async _$EP(){this.isUpdatePending=true;try{await this._$ES;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,s]of this._$Ep)this[t]=s;this._$Ep=void 0;}const t=this.constructor.elementProperties;if(t.size>0)for(const[s,i]of t){const{wrapped:t}=i,e=this[s];true!==t||this._$AL.has(s)||void 0===e||this.C(s,void 0,i,e);}}let t=false;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this._$EO?.forEach((t=>t.hostUpdate?.())),this.update(s)):this._$EM();}catch(s){throw t=false,this._$EM(),s}t&&this._$AE(s);}willUpdate(t){}_$AE(t){this._$EO?.forEach((t=>t.hostUpdated?.())),this.hasUpdated||(this.hasUpdated=true,this.firstUpdated(t)),this.updated(t);}_$EM(){this._$AL=new Map,this.isUpdatePending=false;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return true}update(t){this._$Eq&&=this._$Eq.forEach((t=>this._$ET(t,this[t]))),this._$EM();}updated(t){}firstUpdated(t){}};y$1.elementStyles=[],y$1.shadowRootOptions={mode:"open"},y$1[d$1("elementProperties")]=new Map,y$1[d$1("finalized")]=new Map,p$1?.({ReactiveElement:y$1}),(a$1.reactiveElementVersions??=[]).push("2.1.1");
13
+
14
+ /**
15
+ * @license
16
+ * Copyright 2017 Google LLC
17
+ * SPDX-License-Identifier: BSD-3-Clause
18
+ */
19
+ const t$1=globalThis,i$1=t$1.trustedTypes,s$1=i$1?i$1.createPolicy("lit-html",{createHTML:t=>t}):void 0,e$1="$lit$",h=`lit$${Math.random().toFixed(9).slice(2)}$`,o$2="?"+h,n$1=`<${o$2}>`,r$1=document,l=()=>r$1.createComment(""),c=t=>null===t||"object"!=typeof t&&"function"!=typeof t,a=Array.isArray,u=t=>a(t)||"function"==typeof t?.[Symbol.iterator],d="[ \t\n\f\r]",f=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,v=/-->/g,_=/>/g,m=RegExp(`>|${d}(?:([^\\s"'>=/]+)(${d}*=${d}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),p=/'/g,g=/"/g,$=/^(?:script|style|textarea|title)$/i,y=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),x=y(1),T=Symbol.for("lit-noChange"),E=Symbol.for("lit-nothing"),A=new WeakMap,C=r$1.createTreeWalker(r$1,129);function P(t,i){if(!a(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==s$1?s$1.createHTML(i):i}const V=(t,i)=>{const s=t.length-1,o=[];let r,l=2===i?"<svg>":3===i?"<math>":"",c=f;for(let i=0;i<s;i++){const s=t[i];let a,u,d=-1,y=0;for(;y<s.length&&(c.lastIndex=y,u=c.exec(s),null!==u);)y=c.lastIndex,c===f?"!--"===u[1]?c=v:void 0!==u[1]?c=_:void 0!==u[2]?($.test(u[2])&&(r=RegExp("</"+u[2],"g")),c=m):void 0!==u[3]&&(c=m):c===m?">"===u[0]?(c=r??f,d=-1):void 0===u[1]?d=-2:(d=c.lastIndex-u[2].length,a=u[1],c=void 0===u[3]?m:'"'===u[3]?g:p):c===g||c===p?c=m:c===v||c===_?c=f:(c=m,r=void 0);const x=c===m&&t[i+1].startsWith("/>")?" ":"";l+=c===f?s+n$1:d>=0?(o.push(a),s.slice(0,d)+e$1+s.slice(d)+h+x):s+h+(-2===d?i:x);}return [P(t,l+(t[s]||"<?>")+(2===i?"</svg>":3===i?"</math>":"")),o]};class N{constructor({strings:t,_$litType$:s},n){let r;this.parts=[];let c=0,a=0;const u=t.length-1,d=this.parts,[f,v]=V(t,s);if(this.el=N.createElement(f,n),C.currentNode=this.el.content,2===s||3===s){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes);}for(;null!==(r=C.nextNode())&&d.length<u;){if(1===r.nodeType){if(r.hasAttributes())for(const t of r.getAttributeNames())if(t.endsWith(e$1)){const i=v[a++],s=r.getAttribute(t).split(h),e=/([.?@])?(.*)/.exec(i);d.push({type:1,index:c,name:e[2],strings:s,ctor:"."===e[1]?H:"?"===e[1]?I:"@"===e[1]?L:k}),r.removeAttribute(t);}else t.startsWith(h)&&(d.push({type:6,index:c}),r.removeAttribute(t));if($.test(r.tagName)){const t=r.textContent.split(h),s=t.length-1;if(s>0){r.textContent=i$1?i$1.emptyScript:"";for(let i=0;i<s;i++)r.append(t[i],l()),C.nextNode(),d.push({type:2,index:++c});r.append(t[s],l());}}}else if(8===r.nodeType)if(r.data===o$2)d.push({type:2,index:c});else {let t=-1;for(;-1!==(t=r.data.indexOf(h,t+1));)d.push({type:7,index:c}),t+=h.length-1;}c++;}}static createElement(t,i){const s=r$1.createElement("template");return s.innerHTML=t,s}}function S(t,i,s=t,e){if(i===T)return i;let h=void 0!==e?s._$Co?.[e]:s._$Cl;const o=c(i)?void 0:i._$litDirective$;return h?.constructor!==o&&(h?._$AO?.(false),void 0===o?h=void 0:(h=new o(t),h._$AT(t,s,e)),void 0!==e?(s._$Co??=[])[e]=h:s._$Cl=h),void 0!==h&&(i=S(t,h._$AS(t,i.values),h,e)),i}class M{constructor(t,i){this._$AV=[],this._$AN=void 0,this._$AD=t,this._$AM=i;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(t){const{el:{content:i},parts:s}=this._$AD,e=(t?.creationScope??r$1).importNode(i,true);C.currentNode=e;let h=C.nextNode(),o=0,n=0,l=s[0];for(;void 0!==l;){if(o===l.index){let i;2===l.type?i=new R(h,h.nextSibling,this,t):1===l.type?i=new l.ctor(h,l.name,l.strings,this,t):6===l.type&&(i=new z(h,this,t)),this._$AV.push(i),l=s[++n];}o!==l?.index&&(h=C.nextNode(),o++);}return C.currentNode=r$1,e}p(t){let i=0;for(const s of this._$AV) void 0!==s&&(void 0!==s.strings?(s._$AI(t,s,i),i+=s.strings.length-2):s._$AI(t[i])),i++;}}class R{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(t,i,s,e){this.type=2,this._$AH=E,this._$AN=void 0,this._$AA=t,this._$AB=i,this._$AM=s,this.options=e,this._$Cv=e?.isConnected??true;}get parentNode(){let t=this._$AA.parentNode;const i=this._$AM;return void 0!==i&&11===t?.nodeType&&(t=i.parentNode),t}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(t,i=this){t=S(this,t,i),c(t)?t===E||null==t||""===t?(this._$AH!==E&&this._$AR(),this._$AH=E):t!==this._$AH&&t!==T&&this._(t):void 0!==t._$litType$?this.$(t):void 0!==t.nodeType?this.T(t):u(t)?this.k(t):this._(t);}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t));}_(t){this._$AH!==E&&c(this._$AH)?this._$AA.nextSibling.data=t:this.T(r$1.createTextNode(t)),this._$AH=t;}$(t){const{values:i,_$litType$:s}=t,e="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=N.createElement(P(s.h,s.h[0]),this.options)),s);if(this._$AH?._$AD===e)this._$AH.p(i);else {const t=new M(e,this),s=t.u(this.options);t.p(i),this.T(s),this._$AH=t;}}_$AC(t){let i=A.get(t.strings);return void 0===i&&A.set(t.strings,i=new N(t)),i}k(t){a(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,e=0;for(const h of t)e===i.length?i.push(s=new R(this.O(l()),this.O(l()),this,this.options)):s=i[e],s._$AI(h),e++;e<i.length&&(this._$AR(s&&s._$AB.nextSibling,e),i.length=e);}_$AR(t=this._$AA.nextSibling,i){for(this._$AP?.(false,true,i);t!==this._$AB;){const i=t.nextSibling;t.remove(),t=i;}}setConnected(t){ void 0===this._$AM&&(this._$Cv=t,this._$AP?.(t));}}class k{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(t,i,s,e,h){this.type=1,this._$AH=E,this._$AN=void 0,this.element=t,this.name=i,this._$AM=e,this.options=h,s.length>2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=E;}_$AI(t,i=this,s,e){const h=this.strings;let o=false;if(void 0===h)t=S(this,t,i,0),o=!c(t)||t!==this._$AH&&t!==T,o&&(this._$AH=t);else {const e=t;let n,r;for(t=h[0],n=0;n<h.length-1;n++)r=S(this,e[s+n],i,n),r===T&&(r=this._$AH[n]),o||=!c(r)||r!==this._$AH[n],r===E?t=E:t!==E&&(t+=(r??"")+h[n+1]),this._$AH[n]=r;}o&&!e&&this.j(t);}j(t){t===E?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,t??"");}}class H extends k{constructor(){super(...arguments),this.type=3;}j(t){this.element[this.name]=t===E?void 0:t;}}class I extends k{constructor(){super(...arguments),this.type=4;}j(t){this.element.toggleAttribute(this.name,!!t&&t!==E);}}class L extends k{constructor(t,i,s,e,h){super(t,i,s,e,h),this.type=5;}_$AI(t,i=this){if((t=S(this,t,i,0)??E)===T)return;const s=this._$AH,e=t===E&&s!==E||t.capture!==s.capture||t.once!==s.once||t.passive!==s.passive,h=t!==E&&(s===E||e);e&&this.element.removeEventListener(this.name,this,s),h&&this.element.addEventListener(this.name,this,t),this._$AH=t;}handleEvent(t){"function"==typeof this._$AH?this._$AH.call(this.options?.host??this.element,t):this._$AH.handleEvent(t);}}class z{constructor(t,i,s){this.element=t,this.type=6,this._$AN=void 0,this._$AM=i,this.options=s;}get _$AU(){return this._$AM._$AU}_$AI(t){S(this,t);}}const j=t$1.litHtmlPolyfillSupport;j?.(N,R),(t$1.litHtmlVersions??=[]).push("3.3.1");const B=(t,i,s)=>{const e=s?.renderBefore??i;let h=e._$litPart$;if(void 0===h){const t=s?.renderBefore??null;e._$litPart$=h=new R(i.insertBefore(l(),t),t,void 0,s??{});}return h._$AI(t),h};
20
+
21
+ /**
22
+ * @license
23
+ * Copyright 2017 Google LLC
24
+ * SPDX-License-Identifier: BSD-3-Clause
25
+ */const s=globalThis;class i extends y$1{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const r=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=B(r,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return T}}i._$litElement$=true,i["finalized"]=true,s.litElementHydrateSupport?.({LitElement:i});const o$1=s.litElementPolyfillSupport;o$1?.({LitElement:i});(s.litElementVersions??=[]).push("4.2.1");
26
+
27
+ function e(e){this.message=e;}e.prototype=new Error,e.prototype.name="InvalidCharacterError";var r="undefined"!=typeof window&&window.atob&&window.atob.bind(window)||function(r){var t=String(r).replace(/=+$/,"");if(t.length%4==1)throw new e("'atob' failed: The string to be decoded is not correctly encoded.");for(var n,o,a=0,i=0,c="";o=t.charAt(i++);~o&&(n=a%4?64*n+o:o,a++%4)?c+=String.fromCharCode(255&n>>(-2*a&6)):0)o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(o);return c};function t(e){var t=e.replace(/-/g,"+").replace(/_/g,"/");switch(t.length%4){case 0:break;case 2:t+="==";break;case 3:t+="=";break;default:throw "Illegal base64url string!"}try{return function(e){return decodeURIComponent(r(e).replace(/(.)/g,(function(e,r){var t=r.charCodeAt(0).toString(16).toUpperCase();return t.length<2&&(t="0"+t),"%"+t})))}(t)}catch(e){return r(t)}}function n(e){this.message=e;}function o(e,r){if("string"!=typeof e)throw new n("Invalid token specified");var o=true===(r=r||{}).header?0:1;try{return JSON.parse(t(e.split(".")[o]))}catch(e){throw new n("Invalid token specified: "+e.message)}}n.prototype=new Error,n.prototype.name="InvalidTokenError";
28
+
29
+ function delay(ms) {
30
+ return new Promise(resolve => setTimeout(resolve, ms));
31
+ }
32
+
33
+ /**
34
+ * Dynamic Form Builder Web Component
35
+ * Fetches JSON schema and form data, then renders a dynamic form
36
+ *
37
+ * @element form-builder
38
+ *
39
+ * @attr {string} fbms-base-url - Base URL of the form builder microservice
40
+ * @attr {string} fbms-form-fname - Form name to fetch
41
+ * @attr {string} oidc-url - OpenID Connect URL for authentication
42
+ * @attr {string} styles - Optional custom CSS styles
43
+ */
44
+ class FormBuilder extends i {
45
+ static properties = {
46
+ fbmsBaseUrl: { type: String, attribute: 'fbms-base-url' },
47
+ fbmsFormFname: { type: String, attribute: 'fbms-form-fname' },
48
+ oidcUrl: { type: String, attribute: 'oidc-url' },
49
+ customStyles: { type: String, attribute: 'styles' },
50
+
51
+ // Internal state
52
+ schema: { type: Object, state: true },
53
+ formData: { type: Object, state: true },
54
+ uiSchema: { type: Object, state: true },
55
+ fbmsFormVersion: { type: String, state: true },
56
+ loading: { type: Boolean, state: true },
57
+ submitting: { type: Boolean, state: true },
58
+ error: { type: String, state: true },
59
+ token: { type: String, state: true },
60
+ decoded: { type: Object, state: true },
61
+ };
62
+
63
+ static styles = i$3`
64
+ :host {
65
+ display: block;
66
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
67
+ }
68
+
69
+ .container {
70
+ max-width: 800px;
71
+ margin: 0 auto;
72
+ padding: 20px;
73
+ }
74
+
75
+ .loading {
76
+ text-align: center;
77
+ padding: 40px;
78
+ color: #666;
79
+ }
80
+
81
+ .error {
82
+ background-color: #fee;
83
+ border: 1px solid #fcc;
84
+ border-radius: 4px;
85
+ padding: 15px;
86
+ margin: 20px 0;
87
+ color: #c00;
88
+ }
89
+
90
+ form {
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: 20px;
94
+ }
95
+
96
+ .form-group {
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: 8px;
100
+ }
101
+
102
+ label {
103
+ font-weight: 500;
104
+ color: #333;
105
+ }
106
+
107
+ .required::after {
108
+ content: ' *';
109
+ color: #c00;
110
+ }
111
+
112
+ .description {
113
+ font-size: 0.875rem;
114
+ color: #666;
115
+ margin-top: 4px;
116
+ }
117
+
118
+ input[type="text"],
119
+ input[type="email"],
120
+ input[type="number"],
121
+ input[type="date"],
122
+ input[type="tel"],
123
+ textarea,
124
+ select {
125
+ padding: 8px 12px;
126
+ border: 1px solid #ccc;
127
+ border-radius: 4px;
128
+ font-size: 1rem;
129
+ font-family: inherit;
130
+ width: 100%;
131
+ box-sizing: border-box;
132
+ }
133
+
134
+ input:focus,
135
+ textarea:focus,
136
+ select:focus {
137
+ outline: none;
138
+ border-color: #0066cc;
139
+ box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
140
+ }
141
+
142
+ textarea {
143
+ min-height: 100px;
144
+ resize: vertical;
145
+ }
146
+
147
+ input[type="checkbox"],
148
+ input[type="radio"] {
149
+ margin-right: 8px;
150
+ }
151
+
152
+ .checkbox-group,
153
+ .radio-group {
154
+ display: flex;
155
+ flex-direction: column;
156
+ gap: 8px;
157
+ }
158
+
159
+ .checkbox-item,
160
+ .radio-item {
161
+ display: flex;
162
+ align-items: center;
163
+ }
164
+
165
+ .error-message {
166
+ color: #c00;
167
+ font-size: 0.875rem;
168
+ margin-top: 4px;
169
+ }
170
+
171
+ .buttons {
172
+ display: flex;
173
+ gap: 12px;
174
+ margin-top: 20px;
175
+ }
176
+
177
+ button {
178
+ padding: 10px 20px;
179
+ border: none;
180
+ border-radius: 4px;
181
+ font-size: 1rem;
182
+ cursor: pointer;
183
+ font-family: inherit;
184
+ transition: background-color 0.2s;
185
+ }
186
+
187
+ button[type="submit"] {
188
+ background-color: #0066cc;
189
+ color: white;
190
+ }
191
+
192
+ button[type="submit"]:hover {
193
+ background-color: #0052a3;
194
+ }
195
+
196
+ button[type="button"] {
197
+ background-color: #c0c0c0;
198
+ color: #333;
199
+ }
200
+
201
+ button[type="button"]:hover {
202
+ background-color: #e0e0e0;
203
+ }
204
+
205
+ button:disabled {
206
+ opacity: 0.5;
207
+ cursor: not-allowed;
208
+ }
209
+
210
+ .spinner {
211
+ display: inline-block;
212
+ width: 1em;
213
+ height: 1em;
214
+ border: 2px solid rgba(255, 255, 255, 0.3);
215
+ border-radius: 50%;
216
+ border-top-color: white;
217
+ animation: spin 0.8s linear infinite;
218
+ margin-right: 8px;
219
+ vertical-align: middle;
220
+ }
221
+
222
+ @keyframes spin {
223
+ to { transform: rotate(360deg); }
224
+ }
225
+
226
+ .button-content {
227
+ display: inline-flex;
228
+ align-items: center;
229
+ justify-content: center;
230
+ }
231
+ `;
232
+
233
+ constructor() {
234
+ super();
235
+ this.loading = true;
236
+ this.submitting = false;
237
+ this.error = null;
238
+ this.schema = null;
239
+ this.formData = {};
240
+ this.uiSchema = null;
241
+ this.fbmsFormVersion = null;
242
+ this.token = null;
243
+ this.decoded = {sub: 'unknown'};
244
+ this.fieldErrors = {};
245
+ }
246
+
247
+ async connectedCallback() {
248
+ super.connectedCallback();
249
+ await this.initialize();
250
+ }
251
+
252
+ async initialize() {
253
+ try {
254
+ this.loading = true;
255
+ this.error = null;
256
+
257
+ // Fetch OIDC token if URL provided
258
+ if (this.oidcUrl) {
259
+ await this.fetchToken();
260
+ }
261
+
262
+ // Fetch form schema and data
263
+ await Promise.all([
264
+ this.fetchSchema(),
265
+ this.fetchFormData(),
266
+ ]);
267
+
268
+ this.loading = false;
269
+ } catch (err) {
270
+ this.error = err.message || 'Failed to initialize form';
271
+ this.loading = false;
272
+ }
273
+ }
274
+
275
+ async fetchToken() {
276
+ try {
277
+ const response = await fetch(this.oidcUrl, {
278
+ credentials: 'include',
279
+ });
280
+
281
+ if (!response.ok) {
282
+ throw new Error('Failed to authenticate');
283
+ }
284
+
285
+ const data = await response.text();
286
+ this.token = data;
287
+ this.decoded = o(this.token);
288
+ } catch (err) {
289
+ console.error('Token fetch error:', err);
290
+ throw new Error('Authentication failed');
291
+ }
292
+ }
293
+
294
+ async fetchSchema() {
295
+ const url = `${this.fbmsBaseUrl}/api/v1/forms/${this.fbmsFormFname}`;
296
+ const headers = {
297
+ 'content-type': 'application/jwt',
298
+ };
299
+
300
+ if (this.token) {
301
+ headers['Authorization'] = `Bearer ${this.token}`;
302
+ }
303
+
304
+ const response = await fetch(url, {
305
+ credentials: 'same-origin',
306
+ headers,
307
+ });
308
+
309
+ if (!response.ok) {
310
+ throw new Error(`Failed to fetch schema: ${response.statusText}`);
311
+ }
312
+
313
+ const data = await response.json();
314
+ this.fbmsFormVersion = data.version;
315
+ this.schema = data.schema || data;
316
+ this.uiSchema = data.metadata;
317
+ }
318
+
319
+ async fetchFormData() {
320
+ const url = `${this.fbmsBaseUrl}/api/v1/submissions/${this.fbmsFormFname}?safarifix=${Math.random()}`;
321
+ const headers = {
322
+ 'content-type': 'application/jwt',
323
+ };
324
+
325
+ if (this.token) {
326
+ headers['Authorization'] = `Bearer ${this.token}`;
327
+ }
328
+
329
+ try {
330
+ const response = await fetch(url, {
331
+ credentials: 'same-origin',
332
+ headers,
333
+ });
334
+
335
+ if (response.ok) {
336
+ const payload = await response.json();
337
+ this.formData = payload.answers;
338
+ } else {
339
+ // It's OK if there's no existing data
340
+ this.formData = {};
341
+ }
342
+ } catch (err) {
343
+ // Non-critical error
344
+ console.warn('Could not fetch form data:', err);
345
+ this.formData = {};
346
+ }
347
+ }
348
+
349
+ handleInputChange(fieldName, event) {
350
+ const { type, value, checked } = event.target;
351
+
352
+ this.formData = {
353
+ ...this.formData,
354
+ [fieldName]: type === 'checkbox' ? checked : value,
355
+ };
356
+
357
+ // Clear field error on change
358
+ if (this.fieldErrors[fieldName]) {
359
+ this.fieldErrors = { ...this.fieldErrors };
360
+ delete this.fieldErrors[fieldName];
361
+ }
362
+ }
363
+
364
+ handleArrayChange(fieldName, index, event) {
365
+ const currentArray = this.formData[fieldName] || [];
366
+ const newArray = [...currentArray];
367
+ newArray[index] = event.target.value;
368
+
369
+ this.formData = {
370
+ ...this.formData,
371
+ [fieldName]: newArray,
372
+ };
373
+ }
374
+
375
+ validateForm() {
376
+ const errors = {};
377
+ const { properties = {}, required = [] } = this.schema;
378
+
379
+ // Check required fields
380
+ required.forEach(fieldName => {
381
+ const value = this.formData[fieldName];
382
+ if (value === undefined || value === null || value === '') {
383
+ errors[fieldName] = 'This field is required';
384
+ }
385
+ });
386
+
387
+ // Type validation
388
+ Object.entries(properties).forEach(([fieldName, fieldSchema]) => {
389
+ const value = this.formData[fieldName];
390
+
391
+ if (value !== undefined && value !== null && value !== '') {
392
+ // Email validation
393
+ if (fieldSchema.format === 'email') {
394
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
395
+ if (!emailRegex.test(value)) {
396
+ errors[fieldName] = 'Invalid email address';
397
+ }
398
+ }
399
+
400
+ // Number validation
401
+ if (fieldSchema.type === 'number' || fieldSchema.type === 'integer') {
402
+ const num = Number(value);
403
+ if (isNaN(num)) {
404
+ errors[fieldName] = 'Must be a number';
405
+ } else {
406
+ if (fieldSchema.minimum !== undefined && num < fieldSchema.minimum) {
407
+ errors[fieldName] = `Must be at least ${fieldSchema.minimum}`;
408
+ }
409
+ if (fieldSchema.maximum !== undefined && num > fieldSchema.maximum) {
410
+ errors[fieldName] = `Must be at most ${fieldSchema.maximum}`;
411
+ }
412
+ }
413
+ }
414
+
415
+ // String length validation
416
+ if (fieldSchema.type === 'string') {
417
+ if (fieldSchema.minLength && value.length < fieldSchema.minLength) {
418
+ errors[fieldName] = `Must be at least ${fieldSchema.minLength} characters`;
419
+ }
420
+ if (fieldSchema.maxLength && value.length > fieldSchema.maxLength) {
421
+ errors[fieldName] = `Must be at most ${fieldSchema.maxLength} characters`;
422
+ }
423
+ }
424
+ }
425
+ });
426
+
427
+ this.fieldErrors = errors;
428
+ return Object.keys(errors).length === 0;
429
+ }
430
+
431
+ async handleSubmit(event) {
432
+ event.preventDefault();
433
+
434
+ if (!this.validateForm()) {
435
+ this.requestUpdate();
436
+ return;
437
+ }
438
+
439
+ // Prevent double-submission
440
+ if (this.submitting) {
441
+ return;
442
+ }
443
+
444
+ try {
445
+ this.submitting = true;
446
+ this.error = null;
447
+ const body = {
448
+ username: this.decoded.sub,
449
+ formFname: this.fbmsFormFname,
450
+ formVersion: this.fbmsFormVersion,
451
+ timestamp: Date.now(),
452
+ answers: this.formData
453
+ };
454
+ await delay(300);
455
+
456
+ const url = `${this.fbmsBaseUrl}/api/v1/submissions/${this.fbmsFormFname}`;
457
+ const headers = {
458
+ 'content-type': 'application/json',
459
+ };
460
+
461
+ if (this.token) {
462
+ headers['Authorization'] = `Bearer ${this.token}`;
463
+ }
464
+
465
+ const response = await fetch(url, {
466
+ method: 'POST',
467
+ credentials: 'same-origin',
468
+ headers,
469
+ body: JSON.stringify(body),
470
+ });
471
+
472
+ if (!response.ok) {
473
+ throw new Error(`Failed to submit form: ${response.statusText}`);
474
+ }
475
+
476
+ // Dispatch success event
477
+ this.dispatchEvent(new CustomEvent('form-submit-success', {
478
+ detail: { data: body },
479
+ bubbles: true,
480
+ composed: true,
481
+ }));
482
+
483
+ // Optional: Reset or show success message
484
+ this.error = null;
485
+ } catch (err) {
486
+ this.error = err.message || 'Failed to submit form';
487
+
488
+ this.dispatchEvent(new CustomEvent('form-submit-error', {
489
+ detail: { error: err.message },
490
+ bubbles: true,
491
+ composed: true,
492
+ }));
493
+ } finally {
494
+ this.submitting = false;
495
+ }
496
+ }
497
+
498
+ handleReset() {
499
+ this.formData = {};
500
+ this.fieldErrors = {};
501
+ this.requestUpdate();
502
+ }
503
+
504
+ renderField(fieldName, fieldSchema) {
505
+ const value = this.formData[fieldName];
506
+ const error = this.fieldErrors[fieldName];
507
+ const required = this.schema.required?.includes(fieldName);
508
+ const uiOptions = this.uiSchema?.[fieldName] || {};
509
+
510
+ return x`
511
+ <div class="form-group">
512
+ <label class="${required ? 'required' : ''}" for="${fieldName}">
513
+ ${fieldSchema.title || fieldName}
514
+ </label>
515
+
516
+ ${fieldSchema.description ? x`
517
+ <span class="description">${fieldSchema.description}</span>
518
+ ` : ''}
519
+
520
+ ${this.renderInput(fieldName, fieldSchema, value, uiOptions)}
521
+
522
+ ${error ? x`
523
+ <span class="error-message">${error}</span>
524
+ ` : ''}
525
+ </div>
526
+ `;
527
+ }
528
+
529
+ renderInput(fieldName, fieldSchema, value, uiOptions) {
530
+ const { type, enum: enumValues, format } = fieldSchema;
531
+
532
+ // Enum - render as select
533
+ if (enumValues) {
534
+ return x`
535
+ <select
536
+ id="${fieldName}"
537
+ name="${fieldName}"
538
+ .value="${value || ''}"
539
+ @change="${(e) => this.handleInputChange(fieldName, e)}"
540
+ >
541
+ <option value="">-- Select --</option>
542
+ ${enumValues.map(opt => x`
543
+ <option value="${opt}" ?selected="${value === opt}">
544
+ ${opt}
545
+ </option>
546
+ `)}
547
+ </select>
548
+ `;
549
+ }
550
+
551
+ // Boolean - render as checkbox
552
+ if (type === 'boolean') {
553
+ return x`
554
+ <div class="checkbox-item">
555
+ <input
556
+ type="checkbox"
557
+ id="${fieldName}"
558
+ name="${fieldName}"
559
+ .checked="${!!value}"
560
+ @change="${(e) => this.handleInputChange(fieldName, e)}"
561
+ />
562
+ <label for="${fieldName}">${fieldSchema.title || fieldName}</label>
563
+ </div>
564
+ `;
565
+ }
566
+
567
+ // String with format
568
+ if (type === 'string') {
569
+ if (format === 'email') {
570
+ return x`
571
+ <input
572
+ type="email"
573
+ id="${fieldName}"
574
+ name="${fieldName}"
575
+ .value="${value || ''}"
576
+ @input="${(e) => this.handleInputChange(fieldName, e)}"
577
+ />
578
+ `;
579
+ }
580
+
581
+ if (format === 'date') {
582
+ return x`
583
+ <input
584
+ type="date"
585
+ id="${fieldName}"
586
+ name="${fieldName}"
587
+ .value="${value || ''}"
588
+ @input="${(e) => this.handleInputChange(fieldName, e)}"
589
+ />
590
+ `;
591
+ }
592
+
593
+ if (uiOptions['ui:widget'] === 'textarea') {
594
+ return x`
595
+ <textarea
596
+ id="${fieldName}"
597
+ name="${fieldName}"
598
+ .value="${value || ''}"
599
+ @input="${(e) => this.handleInputChange(fieldName, e)}"
600
+ ></textarea>
601
+ `;
602
+ }
603
+
604
+ // Default text input
605
+ return x`
606
+ <input
607
+ type="text"
608
+ id="${fieldName}"
609
+ name="${fieldName}"
610
+ .value="${value || ''}"
611
+ @input="${(e) => this.handleInputChange(fieldName, e)}"
612
+ />
613
+ `;
614
+ }
615
+
616
+ // Number
617
+ if (type === 'number' || type === 'integer') {
618
+ return x`
619
+ <input
620
+ type="number"
621
+ id="${fieldName}"
622
+ name="${fieldName}"
623
+ .value="${value || ''}"
624
+ step="${type === 'integer' ? '1' : 'any'}"
625
+ @input="${(e) => this.handleInputChange(fieldName, e)}"
626
+ />
627
+ `;
628
+ }
629
+
630
+ // Fallback
631
+ return x`
632
+ <input
633
+ type="text"
634
+ id="${fieldName}"
635
+ name="${fieldName}"
636
+ .value="${value || ''}"
637
+ @input="${(e) => this.handleInputChange(fieldName, e)}"
638
+ />
639
+ `;
640
+ }
641
+
642
+ render() {
643
+ if (this.loading) {
644
+ return x`
645
+ <div class="container">
646
+ <div class="loading">Loading form...</div>
647
+ </div>
648
+ `;
649
+ }
650
+
651
+ if (this.error) {
652
+ return x`
653
+ <div class="container">
654
+ <div class="error">
655
+ <strong>Error:</strong> ${this.error}
656
+ </div>
657
+ </div>
658
+ `;
659
+ }
660
+
661
+ if (!this.schema || !this.schema.properties) {
662
+ return x`
663
+ <div class="container">
664
+ <div class="error">Invalid form schema</div>
665
+ </div>
666
+ `;
667
+ }
668
+
669
+ return x`
670
+ ${this.customStyles ? x`<style>${this.customStyles}</style>` : ''}
671
+
672
+ <div class="container">
673
+ <form @submit="${this.handleSubmit}">
674
+ ${this.schema.title ? x`<h2>${this.schema.title}</h2>` : ''}
675
+ ${this.schema.description ? x`<p>${this.schema.description}</p>` : ''}
676
+
677
+ ${Object.entries(this.schema.properties).map(([fieldName, fieldSchema]) =>
678
+ this.renderField(fieldName, fieldSchema)
679
+ )}
680
+
681
+ <div class="buttons">
682
+ <button type="submit" ?disabled="${this.submitting}">
683
+ <span class="button-content">
684
+ ${this.submitting ? x`<span class="spinner"></span>` : ''}
685
+ ${this.submitting ? 'Submitting...' : 'Submit'}
686
+ </span>
687
+ </button>
688
+ <button type="button" @click="${this.handleReset}" ?disabled="${this.submitting}">
689
+ Reset
690
+ </button>
691
+ </div>
692
+ </form>
693
+ </div>
694
+ `;
695
+ }
696
+ }
697
+
698
+ customElements.define('form-builder', FormBuilder);
699
+ //# sourceMappingURL=form-builder.js.map