@overmap-ai/forms 1.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.
Files changed (64) hide show
  1. package/README.md +12 -0
  2. package/dist/builder/DropDispatch.d.ts +27 -0
  3. package/dist/builder/FieldActions.d.ts +11 -0
  4. package/dist/builder/FieldBuilder.d.ts +22 -0
  5. package/dist/builder/FieldSectionWithActions.d.ts +10 -0
  6. package/dist/builder/FieldWithActions.d.ts +10 -0
  7. package/dist/builder/FieldsEditor.d.ts +2 -0
  8. package/dist/builder/FormBuilder.d.ts +15 -0
  9. package/dist/builder/constants.d.ts +1 -0
  10. package/dist/builder/index.d.ts +2 -0
  11. package/dist/builder/typings.d.ts +11 -0
  12. package/dist/builder/utils.d.ts +11 -0
  13. package/dist/fields/BaseField/BaseField.d.ts +32 -0
  14. package/dist/fields/BaseField/hooks.d.ts +374 -0
  15. package/dist/fields/BaseField/index.d.ts +4 -0
  16. package/dist/fields/BaseField/layouts.d.ts +19 -0
  17. package/dist/fields/BaseField/typings.d.ts +7 -0
  18. package/dist/fields/BooleanField/BooleanInput.d.ts +4 -0
  19. package/dist/fields/BooleanField/index.d.ts +2 -0
  20. package/dist/fields/CustomField/CustomField.d.ts +19 -0
  21. package/dist/fields/CustomField/FieldInputClonerField/FieldInputCloner.d.ts +8 -0
  22. package/dist/fields/CustomField/FieldInputClonerField/FieldInputClonerField.d.ts +18 -0
  23. package/dist/fields/CustomField/FieldInputClonerField/index.d.ts +3 -0
  24. package/dist/fields/CustomField/FieldInputClonerField/typings.d.ts +5 -0
  25. package/dist/fields/CustomField/index.d.ts +1 -0
  26. package/dist/fields/DateField/DateInput.d.ts +4 -0
  27. package/dist/fields/DateField/index.d.ts +2 -0
  28. package/dist/fields/FieldSection/FieldSection.d.ts +29 -0
  29. package/dist/fields/FieldSection/FieldSectionLayout.d.ts +7 -0
  30. package/dist/fields/FieldSection/index.d.ts +1 -0
  31. package/dist/fields/MultiStringField/MultiStringInput.d.ts +8 -0
  32. package/dist/fields/MultiStringField/index.d.ts +2 -0
  33. package/dist/fields/NumberField/NumberInput.d.ts +4 -0
  34. package/dist/fields/NumberField/index.d.ts +2 -0
  35. package/dist/fields/SelectField/BaseSelectField.d.ts +26 -0
  36. package/dist/fields/SelectField/MultiSelectInput.d.ts +4 -0
  37. package/dist/fields/SelectField/SelectInput.d.ts +4 -0
  38. package/dist/fields/SelectField/index.d.ts +4 -0
  39. package/dist/fields/StringOrTextFields/StringField/StringInput.d.ts +4 -0
  40. package/dist/fields/StringOrTextFields/StringField/index.d.ts +2 -0
  41. package/dist/fields/StringOrTextFields/StringOrTextField.d.ts +29 -0
  42. package/dist/fields/StringOrTextFields/TextField/TextInput.d.ts +4 -0
  43. package/dist/fields/StringOrTextFields/TextField/index.d.ts +2 -0
  44. package/dist/fields/StringOrTextFields/index.d.ts +2 -0
  45. package/dist/fields/constants.d.ts +18 -0
  46. package/dist/fields/hooks.d.ts +6 -0
  47. package/dist/fields/index.d.ts +11 -0
  48. package/dist/fields/typings.d.ts +24 -0
  49. package/dist/fields/utils.d.ts +12 -0
  50. package/dist/forms.js +1786 -0
  51. package/dist/forms.umd.cjs +1 -0
  52. package/dist/index.d.ts +3 -0
  53. package/dist/renderer/FormBrowser/FormBrowser.d.ts +11 -0
  54. package/dist/renderer/FormRenderer/FormRenderer.d.ts +28 -0
  55. package/dist/renderer/FormSubmissionBrowser/FormSubmissionBrowser.d.ts +28 -0
  56. package/dist/renderer/FormSubmissionViewer/FormSubmissionViewer.d.ts +17 -0
  57. package/dist/renderer/PatchForm/Field.d.ts +15 -0
  58. package/dist/renderer/PatchForm/Provider.d.ts +24 -0
  59. package/dist/renderer/PatchForm/index.d.ts +2 -0
  60. package/dist/renderer/index.d.ts +5 -0
  61. package/dist/style.css +1 -0
  62. package/dist/typings.d.ts +5 -0
  63. package/dist/utils.d.ts +7 -0
  64. package/package.json +88 -0
@@ -0,0 +1 @@
1
+ (function(y,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("react/jsx-runtime"),require("@overmap-ai/blocks"),require("formik"),require("react"),require("@hello-pangea/dnd"),require("@overmap-ai/core"),require("lodash.get"),require("lodash.set")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","@overmap-ai/blocks","formik","react","@hello-pangea/dnd","@overmap-ai/core","lodash.get","lodash.set"],n):(y=typeof globalThis<"u"?globalThis:y||self,n(y.forms={},y.jsxRuntime,y.blocks,y.formik,y.React,y.dnd,y.core,y.get,y.set))})(this,function(y,n,s,A,c,N,C,Q,Fe){"use strict";var _t=Object.defineProperty;var qt=(y,n,s)=>n in y?_t(y,n,{enumerable:!0,configurable:!0,writable:!0,value:s}):y[n]=s;var g=(y,n,s)=>(qt(y,typeof n!="symbol"?n+"":n,s),s);class Ve{constructor(i){g(this,"type");g(this,"identifier");g(this,"description");const{description:e=null,identifier:t,type:r}=i;this.identifier=t,this.description=e,this.type=r}getId(){return this.identifier}static deserialize(i){throw new Error(`${this.name} must implement deserialize.`)}_serialize(){if(!this.identifier)throw new Error("Field identifier must be set before serializing.");return{type:this.type,identifier:this.identifier,description:this.description}}}class $ extends Ve{constructor(e){const{label:t,required:r,fieldValidators:o=[],formValidators:d=[],...a}=e;super(a);g(this,"required");g(this,"formValidators");g(this,"fieldValidators");g(this,"label");this.label=t,this.required=r,this.fieldValidators=o,this.formValidators=d}static getFieldCreationSchema(){return[]}isBlank(e){return e==null||e===""}getError(e,t){if(this.required&&this.isBlank(e))return"This field is required.";for(const r of this.getFieldValidators()){const o=r(e);if(o)return o}if(t)for(const r of this.getFormValidators()){const o=r(e,t);if(o)return o}}_serialize(){return{...super._serialize(),label:this.label,required:this.required}}getFieldValidators(){return[...this.fieldValidators]}getFormValidators(){return[...this.formValidators]}}g($,"fieldTypeName"),g($,"fieldTypeDescription");const ue={description:"_description_17zed_1"},H=l=>{const{label:i,children:e,severity:t,inputId:r,labelId:o,flexProps:d}=l;return n.jsx(s.Flex,{direction:"column",gap:"1",asChild:!0,...d,children:n.jsxs("label",{htmlFor:r,children:[n.jsx(s.Text,{severity:t,id:o,children:i}),e]})})},j=l=>{const{helpText:i,children:e,severity:t}=l;return n.jsxs(s.Flex,{direction:"column",gap:"1",children:[e,n.jsx(s.Flex,{direction:"column",children:n.jsx(s.Text,{size:"1",severity:t,className:ue.description,children:i})})]})},W=l=>{const{id:i,field:e,formId:t,...r}=l,[o,d,a]=A.useField(e.getId()),{touched:u}=d,p=d.error??e.description,h=d.error?"danger":void 0,f=i??`${t}-${e.getId()}-input`,m=`${f}-label`,v=e.required?`${e.label} *`:e.label,x=c.useMemo(()=>({...o,onChange:I=>{a.setValue(I.target.value,!1).then(),u&&a.setError(e.getError(I.target.value))},onBlur:I=>{a.setTouched(!0,!1).then(),a.setError(e.getError(I.target.value))}}),[e,o,a,u]);return[{helpText:p,severity:h,inputId:f,labelId:m,label:v,fieldProps:x,helpers:a,field:e},{...r,"aria-labelledby":m}]},nt=[!0,"true"],Be=c.memo(function(i){const[{inputId:e,labelId:t,severity:r,helpText:o,field:d,label:a,fieldProps:u,helpers:p},h]=W(i),f=s.useSeverityColor(r),{setValue:m,setTouched:v,setError:x}=p,w=nt.includes(u.value),F=c.useCallback(I=>{if(I==="indeterminate")throw new Error("indeterminate CheckedState value is not supported");m(I,!1).then(),v(!0,!1).then(),x(d.getError(I))},[m,v,x,d]);return n.jsx(j,{helpText:o,severity:r,children:n.jsx(H,{severity:r,inputId:e,labelId:t,label:a,flexProps:{direction:"row-reverse",justify:"end",align:"center",gap:"2"},children:n.jsx(s.Checkbox,{...h,...u,id:e,color:f,value:w.toString(),checked:w,onCheckedChange:F,onChange:void 0,onBlur:void 0})})})}),k=class k extends ${constructor(i){super({...i,type:"boolean"})}isBlank(i){return this.required&&!i}serialize(){return super._serialize()}static deserialize(i){if(i.type!=="boolean")throw new Error("Type mismatch.");return new k(i)}getInput(i){return n.jsx(Be,{...i,field:this})}};g(k,"fieldTypeName","Checkbox"),g(k,"fieldTypeDescription","Perfect for both optional and required yes/no questions."),g(k,"Icon",s.CheckCircledIcon);let K=k;const $e=c.memo(function(i){const[{inputId:e,labelId:t,severity:r,helpText:o,label:d,fieldProps:a,field:u},p]=W(i),h=s.useSeverityColor(r);return n.jsx(j,{helpText:o,severity:r,children:n.jsx(H,{severity:r,inputId:e,labelId:t,label:d,children:n.jsx(s.TextField.Input,{...p,...a,type:"number",id:e,min:u.minimum,max:u.maximum,step:u.integers?1:.1,color:h})})})}),_=class _ extends ${constructor(e){const{minimum:t=Number.MIN_SAFE_INTEGER,maximum:r=Number.MAX_SAFE_INTEGER,integers:o=!1,...d}=e;super({...d,type:"number"});g(this,"minimum");g(this,"maximum");g(this,"integers");this.minimum=t,this.maximum=r,this.integers=o}static getFieldCreationSchema(){return[new _({label:"Minimum",description:"Minimum value",integers:!0,required:!1,identifier:"minimum",formValidators:[this._validateMin]}),new _({label:"Maximum",description:"Maximum value",integers:!0,required:!1,identifier:"maximum",formValidators:[this._validateMax]}),new K({label:"Integers",description:"Whole numbers only",required:!1,identifier:"integers"})]}getFieldValidators(){const e=super.getFieldValidators(),t=this.minimum,r=this.maximum;return typeof t=="number"&&e.push(o=>{if(typeof o=="number"&&o<t)return`Must be at least ${this.minimum}.`}),typeof r=="number"&&e.push(o=>{if(typeof o=="number"&&o>r)return`Must be at most ${this.maximum}.`}),this.integers&&e.push(o=>{if(typeof o=="number"&&!Number.isInteger(o))return"Must be a whole number."}),e}serialize(){return{...super._serialize(),minimum:this.minimum,maximum:this.maximum,integers:this.integers}}static deserialize(e){if(e.type!=="number")throw new Error("Type mismatch.");return new _(e)}getInput(e){return n.jsx($e,{field:this,...e})}};g(_,"fieldTypeName","Number"),g(_,"fieldTypeDescription","Allows specifying a number within a given range."),g(_,"Icon",s.FontFamilyIcon),g(_,"_validateMin",(e,t)=>typeof t.maximum=="number"&&typeof e=="number"&&t.maximum<e?"Minimum cannot be greater than minimum.":null),g(_,"_validateMax",(e,t)=>typeof t.minimum=="number"&&typeof e=="number"&&t.minimum>e?"Maximum cannot be less than minimum.":null);let X=_;const _e=c.memo(function(i){const[{inputId:e,labelId:t,severity:r,helpText:o,label:d,field:a,fieldProps:u,helpers:p},h]=W(i),f=s.useSeverityColor(r),{setValue:m,setError:v}=p,x=u.value?u.value.split("T")[0]:"",w=c.useCallback(F=>{const I=new Date(F.target.value).toISOString();m(I,!1).then(),v(a.getError(I))},[a,v,m]);return n.jsx(j,{helpText:o,severity:r,children:n.jsx(H,{severity:r,inputId:e,labelId:t,label:d,children:n.jsx(s.TextField.Input,{...h,...u,type:"date",id:e,color:f,value:x,onChange:w})})})}),R=class R extends ${constructor(i){super({...i,type:"date"})}serialize(){return super._serialize()}static deserialize(i){if(i.type!=="date")throw new Error("Type mismatch.");return new R(i)}getInput(i){return n.jsx(_e,{field:this,...i})}};g(R,"fieldTypeName","Date"),g(R,"fieldTypeDescription","Allows specifying a date."),g(R,"Icon",s.CalendarIcon);let fe=R;class he extends ${constructor(e){const{minLength:t,maxLength:r=5e3,...o}=e;super(o);g(this,"minLength");g(this,"maxLength");this.minLength=t?Math.max(t,0):void 0,this.maxLength=r?Math.max(r,0):5e3}static getFieldCreationSchema(){return[new X({label:"Minimum length",description:"Minimum number of characters",required:!1,identifier:"minimum_length",minimum:0,maximum:100,formValidators:[this._validateMin],integers:!0}),new X({label:"Maximum length",description:"Maximum number of characters",required:!1,identifier:"maximum_length",minimum:1,maximum:5e3,formValidators:[this._validateMax],integers:!0})]}getFieldValidators(){const e=super.getFieldValidators();return this.minLength&&e.push(t=>{if(this.minLength&&(!t||t.length<this.minLength))return!this.required&&!t?null:`Minimum ${this.minLength} character(s).`}),this.maxLength&&e.push(t=>{if(typeof t=="string"&&this.maxLength&&t.length>this.maxLength)return`Maximum ${this.maxLength} character(s).`}),e}_serialize(){if(!this.identifier)throw new Error("Field identifier must be set before serializing.");return{...super._serialize(),minimum_length:this.minLength,maximum_length:this.maxLength}}}g(he,"_validateMin",(e,t)=>typeof t.maximum_length=="number"&&typeof e=="number"&&t.maximum_length<e?"Minimum cannot be greater than maximum.":null),g(he,"_validateMax",(e,t)=>{if(typeof e!="number")return null;const{minimum_length:r}=t;return typeof r!="number"?null:r>e?"Maximum cannot be less than minimum.":null});const qe=c.memo(function(i){const[{inputId:e,labelId:t,severity:r,helpText:o,label:d,fieldProps:a,field:u},p]=W(i),h=s.useSeverityColor(r);return n.jsx(j,{helpText:o,severity:r,children:n.jsx(H,{severity:r,inputId:e,labelId:t,label:d,children:n.jsx(s.TextField.Input,{...p,...a,type:u.inputType,id:e,color:h})})})}),ee=class ee extends he{constructor(e){const{inputType:t="text",...r}=e,o=e.maxLength?Math.min(500,e.maxLength):500,d=e.minLength?Math.min(e.minLength,o):void 0;super({...r,maxLength:o,minLength:d,type:"string"});g(this,"inputType");this.inputType=t}serialize(){return{...super._serialize(),input_type:this.inputType}}static deserialize(e){if(e.type!=="string")throw new Error("Type mismatch.");const{maximum_length:t,minimum_length:r,input_type:o,...d}=e;return new ee({...d,maxLength:t,minLength:r,inputType:o})}getInput(e){return n.jsx(qe,{field:this,...e})}};g(ee,"fieldTypeName","Short Text"),g(ee,"fieldTypeDescription","Short text fields can hold up to 500 characters on a single line."),g(ee,"Icon",s.InputIcon);let J=ee;const Ne=c.memo(function(i){const[{inputId:e,labelId:t,severity:r,helpText:o,label:d,fieldProps:a},u]=W(i);return n.jsx(j,{helpText:o,severity:r,children:n.jsx(H,{severity:r,inputId:e,labelId:t,label:d,children:n.jsx(s.TextArea,{...u,...a,resize:"vertical",id:e,severity:r})})})}),te=class te extends he{constructor(i){const e=i.maxLength?Math.min(5e3,i.maxLength):5e3,t=i.minLength?Math.min(i.minLength,e):void 0;super({...i,maxLength:e,minLength:t,type:"text"})}serialize(){return super._serialize()}static deserialize(i){if(i.type!=="text")throw new Error("Type mismatch.");const{maximum_length:e,minimum_length:t,...r}=i;return new te({...r,maxLength:e,minLength:t})}getInput(i){return n.jsx(Ne,{field:this,...i})}};g(te,"fieldTypeName","Paragraph"),g(te,"fieldTypeDescription","Paragraph fields can hold up to 5000 characters and can have multiple lines."),g(te,"Icon",s.RowsIcon);let Z=te;const He=c.memo(function(i){const[{inputId:e,labelId:t,severity:r,helpText:o,label:d,fieldProps:a,field:u,helpers:p},h]=W(i),{setValue:f,setTouched:m,setError:v}=p,x=c.useMemo(()=>u.options.map(F=>({value:F.value,itemContent:F.label})),[u.options]),w=c.useCallback(F=>{f(F,!1).then(),m(!0,!1).then(),v(u.getError(F))},[m,f,v,u]);return n.jsx(j,{helpText:o,severity:r,children:n.jsx(H,{severity:r,inputId:e,labelId:t,label:d,children:n.jsx(s.Select,{items:x,...a,onValueChange:w,placeholder:"Select one...",id:e,severity:r,...h})})})}),pe=(l="",i=[])=>({type:"section",fields:i,identifier:l,label:null,condition:null,conditional:!1}),rt=l=>{if(!l)return;const i=l.fields;let e=[];const t=[];for(const r of i)r.type==="section"?(e.length>0&&(t.push(pe(`AUTO_section-${i.indexOf(r)}`,e)),e=[]),t.push(r)):e.push(r);return e.length>0&&t.push(pe("AUTO_section-last",e)),{...l,fields:t}};function Ce(l,i,e){const t=Array.from(l),[r]=t.splice(i,1);if(!r)throw new Error("Could not find field to reorder.");return t.splice(e,0,r),t}function st(l,i,e){const t=Array.from(l);return t[i]=e,t}function je(l,i,e){const t=Array.from(l??[]);return t.splice(i,0,e),t}function me(l,i){const e=Array.from(l);return e.splice(i,1),e}const ot=(l,i)=>typeof l=="string"&&l.length>0?l:C.slugify(i),We=(l,i)=>{if(!i)return null;for(const e of l)if(e.type==="section"){const t=We(e.fields,i);if(t)return t}else if(e.identifier===i)return e;return null},be=(l,i)=>l.filter((e,t)=>t<i).flatMap(e=>e.fields),Ue=c.memo(function(i){const[{inputId:e,labelId:t,severity:r,helpText:o,label:d,fieldProps:a,field:u,helpers:p},h]=W(i),f=s.useSeverityColor(r),m=c.useMemo(()=>Array.isArray(a.value)?a.value:[],[a.value]),{setValue:v,setTouched:x,setError:w}=p,F=`${e}-droppable`,{disabled:I}=h,[b,z]=c.useState(""),[P,M]=c.useState(""),O=P||o,G=P?"red":f,V=c.useCallback(E=>{v(E,!1).then(),x(!0,!1).then(),w(u.getError(E))},[v,x,w,u]),S=c.useCallback(E=>{m.findIndex(B=>B.value===E.target.value.trim())>=0?M("All options must be unique"):E.target.value?M(""):M("Option cannot be empty"),z(E.target.value)},[z,m]),T=c.useCallback(()=>{if(P)return;if(!b.trim())return M("Option cannot be empty");const E=b.trim();V([...m,{value:E,label:E}]),z("")},[b,P,V,m]),D=c.useCallback(E=>{E.key==="Enter"&&(E.preventDefault(),T())},[T]),L=c.useCallback(E=>{V(me(m,E))},[m,V]),q=c.useCallback(E=>{if(!E.destination)return;const B=E.source.index,Y=E.destination.index;V(Ce(m,B,Y))},[V,m]);return n.jsx(N.DragDropContext,{onDragEnd:q,children:n.jsxs(s.Flex,{direction:"column",gap:"2",children:[n.jsx(j,{helpText:O,severity:r,children:n.jsx(H,{severity:r,inputId:e,labelId:t,label:d,children:(!I||m.length===0)&&n.jsxs(s.Flex,{gap:"2",children:[n.jsx(s.Box,{grow:"1",children:n.jsx(s.TextField.Input,{placeholder:"Press enter to add a new option",...h,...a,value:b,onChange:S,onKeyDown:D,id:e,color:G,onBlur:void 0})}),n.jsx(s.IconButton,{type:"button","aria-label":"Add option",disabled:!!P||I,onClick:T,children:n.jsx(s.PlusIcon,{})})]})})}),n.jsx(N.Droppable,{droppableId:F,children:E=>n.jsxs(s.Flex,{...E.droppableProps,ref:E.innerRef,direction:"column",children:[m.map((B,Y)=>n.jsx(N.Draggable,{draggableId:`${B.value}-draggable`,index:Y,isDragDisabled:I,children:({draggableProps:oe,dragHandleProps:Oe,innerRef:$t})=>n.jsx(s.Flex,{...Oe,...oe,ref:$t,gap:"2",align:"center",justify:"between",mb:"1",asChild:!0,children:n.jsxs(s.Badge,{color:"gray",size:"2",children:[n.jsx("span",{children:B.label}),n.jsx(s.IconButton,{size:"small",variant:"ghost",type:"button","aria-label":"Delete option",severity:"info",disabled:I,onClick:()=>L(Y),children:n.jsx(s.Cross1Icon,{})})]})})},B.value)),E.placeholder]})})]})})}),ie=class ie extends ${constructor(e){const{minimum_length:t,maximum_length:r,...o}=e;super({...o,type:"multi-string"});g(this,"minLength");g(this,"maxLength");this.minLength=t??0,this.maxLength=r??1/0}getInput(e){return n.jsx(Ue,{field:this,...e})}serialize(){return{...super._serialize(),minimum_length:this.minLength,maximum_length:this.maxLength}}isBlank(e){return super.isBlank(e)||e.length===0}getFieldValidators(){const e=super.getFieldValidators();return e.push(t=>{if(Array.isArray(t)&&t.length<this.minLength)return`Must have at least ${this.minLength} options.`}),e.push(t=>{if(Array.isArray(t)&&t.length>this.maxLength)return`Must have at most ${this.maxLength} options.`}),e}static deserialize(e){if(e.type!=="multi-string")throw new Error("Type mismatch.");return new ie(e)}};g(ie,"fieldTypeName","Multi-string"),g(ie,"fieldTypeDescription","Allows the user to provide multiple unique strings."),g(ie,"Icon",s.ListBulletIcon);let le=ie;class Ge extends ${constructor(e){super(e);g(this,"options");const t=new Set;this.options=e.options.map(r=>(typeof r=="string"&&(r={label:r,value:r}),t.add(r.label),r)),t.size!==e.options.length&&console.error(`${e.options.length-t.size} duplicate identifiers found in options. This may cause unexpected behavior. Options:`,e.options)}_serialize(){return{...super._serialize(),options:this.options}}static getFieldCreationSchema(){return[new le({label:"Options",description:"List possible options for the user to select from.",required:!0,identifier:"options",minimum_length:2,maximum_length:20})]}}const ne=class ne extends Ge{constructor(i){super({...i,type:"select"})}serialize(){return super._serialize()}static deserialize(i){if(i.type!=="select")throw new Error("Type mismatch.");return new ne(i)}getInput(i){return n.jsx(He,{field:this,...i})}};g(ne,"fieldTypeName","Dropdown"),g(ne,"fieldTypeDescription","Allows the user to select a single option from a list of options."),g(ne,"Icon",s.DropdownMenuIcon);let ae=ne;const Ke=c.memo(function(i){const[{inputId:e,labelId:t,severity:r,helpText:o,label:d,fieldProps:a,field:u,helpers:p},h]=W(i),{setValue:f,setTouched:m,setError:v}=p,x=c.useMemo(()=>Array.isArray(a.value)?a.value:[],[a.value]),w=c.useCallback(F=>{f(F,!1).then(),m(!0,!1).then(),v(u.getError(F))},[m,f,v,u]);return n.jsx(j,{helpText:o,severity:r,children:n.jsx(H,{severity:r,inputId:e,labelId:t,label:d,children:n.jsx(s.MultiSelect,{value:x,onValueChange:w,options:u.options,name:a.name,placeholder:"Select one or more...",id:e,severity:r,...h})})})}),re=class re extends Ge{constructor(i){super({...i,type:"multi-select"})}isBlank(i){return super.isBlank(i)||i.length===0}serialize(){return super._serialize()}static deserialize(i){if(i.type!=="multi-select")throw new Error("Type mismatch.");return new re(i)}getInput(i){return n.jsx(Ke,{field:this,...i})}};g(re,"fieldTypeName","Multi-select"),g(re,"fieldTypeDescription","Allows the user to select a multiple options from a list of options."),g(re,"Icon",s.CheckboxIcon);let ge=re;const lt=c.memo(function({field:i,...e}){const[{value:t}]=A.useField(i.options.clonedFieldIdentifier),r=c.useMemo(()=>{const o=i.options.getFieldToClone(t);return o?de(o):null},[i.options,t]);return ce(r,e)});class ye extends ${constructor(e,t){super({...e,type:"custom"});g(this,"Component");g(this,"options");this.options=e,this.Component=t}serialize(){throw new Error("Serializing only supported for public input types.")}getInput(e){const t=this.Component;return n.jsx(t,{field:this,...e})}}g(ye,"fieldTypeName","Custom"),g(ye,"fieldTypeDescription","Allows re-rendering of field already in the form");class at extends ye{constructor(i){super(i,lt)}}const dt=c.memo(function(i){const{field:e,...t}=i,{label:r,description:o,fields:d,condition:a}=e,{values:u,setFieldValue:p}=A.useFormikContext(),h=a!=null&&a.identifier?Q(u,a.identifier):void 0,f=c.useMemo(()=>Me(a,h),[a,h]);c.useEffect(()=>{if(!f)for(const v of d)p(v.getId(),"").then()},[f,d,p]);const m=ze(d,t);return f?r?n.jsx(s.Card,{children:n.jsxs(s.Flex,{direction:"column",gap:"3",children:[n.jsxs(s.Flex,{direction:"column",children:[n.jsx(s.Heading,{as:"h3",size:"3",children:r}),n.jsx(s.Text,{className:ue.description,children:o})]}),m]})}):m:null}),se=class se extends Ve{constructor(e){const{label:t=null,fields:r,condition:o=null,conditional:d,...a}=e;super({...a,type:"section"});g(this,"label");g(this,"fields");g(this,"condition");this.fields=r,this.condition=o,this.label=t,d===!1&&(this.condition=null)}static getFieldCreationSchema(e){return e.length===0?[]:[new K({label:"Conditional",description:"Conditionally show or hide this section.",identifier:"conditional",required:!1}),new se({label:"Conditional settings",identifier:"conditional-settings",condition:{identifier:"conditional",value:!0},fields:[new ae({label:"Field",description:"The field to use for the condition.",options:e.map(t=>t.label?{label:t.label,value:t.identifier}:null).filter(t=>!!t),identifier:"condition.identifier",required:!0}),new at({label:"Value",identifier:"condition.value",required:!0,clonedFieldIdentifier:"condition.identifier",getFieldToClone(t){if(!t)return null;const r=e.find(o=>o.identifier===t);return r?{...r,label:"Value",identifier:"condition.value",description:"The value to compare against.",required:r.type!=="boolean"}:(console.error("Could not find field with identifier",t),null)}})]})]}static deserialize(e){var r;if(e.type!=="section")throw new Error("Invalid type");const t=((r=e.fields)==null?void 0:r.map(Se))??[];return new se({...e,fields:t})}conditional(){return this.condition!==null}serialize(){return{...super._serialize(),label:this.label,condition:this.condition,conditional:this.conditional(),fields:this.fields.map(e=>e.serialize())}}getErrors(e){const t={};for(const r of this.fields){const o=r.getId(),d=r.getError(Q(e,o),e);d&&Fe(t,r.getId(),d)}return t}getInput(e){return n.jsx(dt,{field:this,...e})}};g(se,"fieldTypeName","Section"),g(se,"fieldTypeDescription","Sections can be useful for grouping fields together. They can also be conditionally shown or hidden.");let U=se;const Te={date:fe,number:X,boolean:K,select:ae,string:J,text:Z,custom:ye,"multi-string":le,"multi-select":ge},Se=l=>{const i=l.type;return Te[i].deserialize(l)},de=l=>l.type==="section"?U.deserialize(l):Se(l);function Ee(l,i={}){const{readonly:e=!1}=i;return{title:l.title,description:l.description,fields:l.fields.map(t=>de(t)),meta:{readonly:e}}}function Me(l,i){if(!l)return!0;const e=Array.isArray(i)?i.map(r=>typeof r=="string"?r:r.value):i,t=Array.isArray(l.value)?l.value.map(r=>typeof r=="string"?r:r.value):l.value;if(Array.isArray(t)&&Array.isArray(e)){for(const r of t)if(!e.includes(r))return!1;return!0}return t===i}const ce=(l,i)=>c.useMemo(()=>!i||!l?null:l.getInput(i),[l,i]),ze=(l,i)=>{const e=c.useMemo(()=>l.map(t=>n.jsx("div",{children:t.getInput(i)},t.getId())),[l,i]);return n.jsx(s.Flex,{direction:"column",gap:"2",children:e})},ve=l=>Object.keys(l).length>0,Ye=async(l,i)=>{const e={};for(const t of l.fields)if(t instanceof U){if(t.condition){const{identifier:r}=t.condition;if(!Me(t.condition,Q(i,r)))continue}Object.assign(e,t.getErrors(i))}else{if(!(t instanceof $))throw new Error("Invalid field type");const r=t.getId(),o=t.getError(Q(i,r),i);o&&Fe(e,r,o)}if(ve(e))return e},ct=[null,void 0],Pe=(l,i)=>l.reduce((e,t)=>t instanceof U?{...e,...Pe(t.fields,i)}:(ct.includes(Q(e,t.getId()))&&Fe(e,t.getId(),""),e),i),ut=()=>{throw new Error("onSubmit must be provided if form is not readonly.")},xe=c.memo(c.forwardRef((l,i)=>{const{schema:e,values:t={},onSubmit:r=ut,submitText:o="Submit",cancelText:d,onCancel:a,onDirty:u,hideTitle:p=!e.title,hideDescription:h,className:f}=l,{readonly:m}=e.meta,v=c.useMemo(()=>crypto.randomUUID(),[]),x=A.useFormik({initialValues:Pe(e.fields,t),onSubmit:r,validate:z=>Ye(e,z),validateOnBlur:!1,validateOnChange:!1}),{dirty:w}=x,F=c.useMemo(()=>typeof e.title=="string"?n.jsx(s.Heading,{children:e.title}):e.title,[e.title]),I=c.useMemo(()=>typeof e.description=="string"?n.jsx(s.Text,{className:ue.description,children:e.description}):e.description,[e.description]),b=ze(e.fields,{formId:v,disabled:m});return c.useEffect(()=>{w&&u&&u()},[w,u]),n.jsx(A.FormikProvider,{value:x,children:n.jsx(s.Flex,{ref:i,direction:"column",gap:"2",className:f,asChild:!0,children:n.jsxs("form",{id:v,onSubmit:x.handleSubmit,children:[!p&&n.jsx(s.Card,{children:n.jsxs(s.Flex,{direction:"column",gap:"1",children:[F,!h&&I]})}),b,!m&&n.jsxs(s.Flex,{justify:"end",gap:"2",children:[d&&n.jsx(s.Button,{type:"button",variant:"soft",onClick:a,children:d}),n.jsx(s.Button,{type:"submit",disabled:!x.isValid,children:o})]})]})})})})),ft=c.memo(c.forwardRef((l,i)=>{const{submission:e,showFormDescription:t=!1,showFormTitle:r=!0}=l,o=C.useAppSelector(C.selectFormRevision(e.form_revision));if(!o)throw new Error(`Could not find revision ${e.form_revision} for submission ${e.offline_id}.`);const d=c.useMemo(()=>Ee(o,{readonly:!0}),[o]);return n.jsx(xe,{ref:i,schema:d,values:e.values,hideDescription:!t,hideTitle:!r})})),Qe={favoriteIcon:"_favoriteIcon_1bixi_1",regularIcon:"_regularIcon_1bixi_9"},Ae="organization:",Le="user:",ht=c.memo(c.forwardRef((l,i)=>{const{maxResults:e=20,...t}=l,[r,o]=c.useState(""),[d,a]=c.useState(""),{sdk:u}=C.useSDK(),p=c.useMemo(()=>{const b={maxResults:e,searchTerm:r};return d&&(d.startsWith(Ae)?b.owner_organization=parseInt(d.slice(Ae.length)):d.startsWith(Le)&&(b.owner_user=parseInt(d.slice(Le.length)))),b},[r,e,d]),h=C.useAppSelector(C.selectFilteredUserForms(p))??[],f=C.useAppSelector(C.selectUserFormMapping),m=c.useCallback(b=>{b.favorite?u.userForms.unfavorite(b.offline_id).then():u.userForms.favorite(b.offline_id).then()},[u]),v=c.useMemo(()=>{const b=u.store.getState(),z={};for(const P of Object.values(f)){const M=C.selectOrganization(P.owner_organization||-1)(b);M&&(z[`${Ae}${M.id}`]=M.name);const O=C.selectUser(P.owner_user||-1)(b);O&&(z[`${Le}${O.id}`]=O.username)}return Object.entries(z).map(([P,M])=>({itemContent:M,value:P}))},[f,u.store]),x=c.useCallback(b=>{o(b.currentTarget.value)},[]),F=(C.useAppSelector(C.selectNumberOfUserForms)||0)-h.length,I=h.length==e&&F>0?`Only the first ${e} results are shown (${F} hidden)`:F>0&&`${F} hidden forms`;return n.jsxs(s.Flex,{ref:i,direction:"column",gap:"2",children:[n.jsxs(s.Flex,{gap:"2",grow:"1",children:[n.jsx(s.Box,{grow:"1",asChild:!0,children:n.jsx(s.TextField.Root,{size:"3",children:n.jsx(s.TextField.Input,{placeholder:"Filter",value:r,onChange:x})})}),n.jsx(s.Select,{items:v,value:d,onValueChange:a,placeholder:"Owner",size:"large"})]}),h.length>0&&n.jsx(s.ButtonList.Root,{children:h.map(b=>n.jsx(pt,{...t,form:b,handleToggleFavorite:()=>m(b)},b.offline_id))}),n.jsx(s.Box,{px:"3",children:n.jsx(s.Text,{size:"2",severity:"info",children:I})})]})})),pt=l=>{var m;const{form:i,onSelectForm:e,isFavoriteEditable:t,handleToggleFavorite:r}=l,o=(m=C.useAppSelector(C.selectOrganization(i.owner_organization||-1)))==null?void 0:m.name,d=C.useAppSelector(C.selectUser(i.owner_user||-1)),a=C.useAppSelector(C.selectCurrentUser).id,u=!!d&&d.id===a,p=o??(u?"You":d==null?void 0:d.username)??"Unknown",h=c.useCallback(v=>{v.stopPropagation(),r()},[r]),f=n.jsx(s.ButtonList.Item,{onClick:()=>e(i),asChild:!0,children:n.jsxs(s.Flex,{justify:"between",gap:"2",py:"2",px:"3",...s.divButtonProps,children:[n.jsxs(s.Flex,{grow:"1",align:"center",gap:"2",children:[n.jsx(s.IconButton,{className:C.classNames(i.favorite?Qe.favoriteIcon:Qe.regularIcon),variant:"ghost",onClick:h,"aria-label":i.favorite?"Favorite form":"Standard form",disabled:!t,children:i.favorite?n.jsx(s.StarFilledIcon,{}):n.jsx(s.StarIcon,{})}),n.jsx(s.Text,{noWrap:!0,children:i.latestRevision.title}),i.latestRevision.description&&n.jsx(s.QuestionMarkCircledIcon,{})]}),p&&n.jsxs(s.Flex,{align:"center",gap:"2",children:[n.jsx(s.PersonIcon,{})," ",p]})]})});return i.latestRevision.description?n.jsx(s.Tooltip,{content:i.latestRevision.description,children:f},i.offline_id):f},Xe={submissionsContainer:"_submissionsContainer_9iirt_1",stopHorizontalOverflow:"_stopHorizontalOverflow_9iirt_6"},mt=c.memo(function(i){var b;const{submission:e,onSubmissionClick:t,compact:r,labelType:o,rowDecorator:d}=i,a=C.useAppSelector(C.selectCurrentUser),u=C.useAppSelector(C.selectUser("created_by"in e?e.created_by:a.id)),p=De(e),h=C.isToday(p)?p.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}):C.getLocalDateString(p),f=C.useAppSelector(C.selectFormRevision(e.form_revision));if(!f)throw new Error(`Could not find revision ${e.form_revision} for submission ${e.offline_id}.`);const m=(b=C.useAppSelector(C.selectLatestFormRevision(f.form)))==null?void 0:b.revision,v=C.useFileSrc({file:(u==null?void 0:u.profile.file)??null,fileSha1:(u==null?void 0:u.profile.file_sha1)??null}),x=(u==null?void 0:u.username.charAt(0).toUpperCase())??"?",w=f.revision===m,F=c.useCallback(()=>{t&&t({submission:e})},[e,t]),I=n.jsx(s.ButtonList.Item,{onClick:F,asChild:!0,children:n.jsxs(s.Flex,{grow:"1",width:"100%",p:"2",gap:"2",justify:"between",children:[n.jsxs(s.Flex,{gap:"2",align:"center",className:Xe.stopHorizontalOverflow,children:[n.jsx(s.Avatar,{src:v,size:"1",fallback:x}),n.jsx(s.Text,{size:"2",noWrap:!0,children:o==="creator"?(u||a).username:f.title})]}),n.jsxs(s.Flex,{gap:"2",align:"center",children:[!r&&(f.revision?n.jsx(s.Badge,{variant:"soft",severity:w?"primary":"info",children:r?f.revision.toString():`Revision #${f.revision}`}):!!m&&n.jsx(s.Badge,{children:"Original"})),n.jsx(s.Text,{size:"2",noWrap:!0,children:h})]})]})});return d?d(e,I):I}),De=l=>{const i="created_at"in l?l.created_at:l.submitted_at;return new Date(i)},gt=c.memo(function(i){const{formId:e,submissions:t,compact:r=!1,className:o,after:d,variant:a="outline",...u}=i;if(!!e==!!t)throw new Error("Either formId or submissions must be provided, but not both.");const p=C.useAppSelector(t?()=>t:C.selectSubmissionsForForm(e)),h=c.useMemo(()=>p==null?void 0:p.sort((f,m)=>De(m).getTime()-De(f).getTime()),[p]);return n.jsx(s.ButtonList.Root,{className:C.classNames(Xe.submissionsContainer,o),size:"small",variant:a,before:!r&&n.jsxs(s.Text,{severity:"info",children:["There are ",((p==null?void 0:p.length)||0).toString()," submissions of this form."]}),after:d,children:h==null?void 0:h.map((f,m)=>n.jsx(mt,{submission:f,compact:r,...u},m))})}),yt=c.memo(function(i){const{name:e,render:t}=i,{submitForm:r}=A.useFormikContext(),[o,d,a]=A.useField(e);return c.useMemo(()=>{const u=p=>a.setValue(p,!1);return t({value:o.value,setValue:u,patchValue:r})},[r,a,o.value,t])}),vt=c.memo(c.forwardRef((l,i)=>{const{children:e,schema:t,values:r,onPatch:o,onError:d,...a}=l,u=c.useMemo(()=>Pe(t.fields,r),[t.fields,r]),p=c.useCallback(x=>{const w={};for(const F in x){const I=x[F];I!==u[F]&&I!==void 0&&(w[F]=I)}ve(w)&&o(w)},[u,o]),h=c.useCallback(async x=>{const w=await Ye(t,x);return w&&d(w),w},[t,d]),f=A.useFormik({initialValues:u,onSubmit:p,validate:h,validateOnBlur:!1,validateOnChange:!1}),{errors:m,resetForm:v}=f;return c.useEffect(()=>{ve(m)&&v({values:u,errors:{}})},[m,u,v]),n.jsx(A.FormikProvider,{value:f,children:n.jsx("form",{...a,ref:i,onSubmit:f.handleSubmit,children:e})})})),Je={...Te,section:U},xt=c.memo(function(i){const{field:e,setFieldType:t}=i,r=e.fieldTypeName,o=e.fieldTypeDescription,d=e.Icon;return n.jsxs(s.Flex,{gap:"4",align:"center",children:[n.jsx(s.Button,{type:"button",variant:"surface",onClick:t,style:{width:"135px"},children:n.jsxs(s.Flex,{gap:"3",align:"center",grow:"1",children:[n.jsx(d,{}),r]})}),n.jsx(s.Text,{children:o})]})}),Ze=[["string","text"],["select","multi-select"],["boolean","date","number","multi-string"]],wt=Ze.length-1,It=c.memo(function(i){const{setFieldType:e}=i;return n.jsx(s.Flex,{direction:"column",gap:"3",children:Ze.map((t,r)=>n.jsxs(s.Flex,{direction:"column",gap:"3",children:[n.jsx(s.Flex,{direction:"column",gap:"2",children:t.map(o=>n.jsx(xt,{field:Te[o],setFieldType:()=>e(o)},o))}),r<wt&&n.jsx(s.Separator,{size:"4"})]},r))})}),Ft=l=>i=>{if(!i||typeof i!="string")return;const e=C.slugify(i);if(l.includes(e))return"This name is already taken."},Ct=(l,i)=>{const e=[new J({label:"Label",required:!0,maxLength:100,identifier:"label",fieldValidators:[Ft(l)]}),new Z({label:"Description",required:!1,maxLength:1e3,identifier:"description"})];return i==="section"?e:[...e,new K({label:"Required",description:null,required:!1,identifier:"required"})]},bt=c.memo(function(i){const{fieldType:e,handleCancel:t,handleCreateField:r,defaultField:o,conditionalSourceFields:d}=i,a=Je[e],u=A.useFormikContext(),p=c.useMemo(()=>{const h=u.values.fields.flatMap(m=>m.type==="section"?[...m.fields.map(v=>v.identifier),m.identifier]:m.identifier).filter(m=>m!==(o==null?void 0:o.identifier));let f=Ct(h,e);if(a===U){if(d===void 0)throw new Error("Conditional source fields must be provided when changing sections.");f=f.concat(a.getFieldCreationSchema(d))}else{if(!(a.prototype instanceof $))throw new Error(`Field must be an instance of BaseField. Got ${a}.`);f=[...f,...a.getFieldCreationSchema()]}return{fields:f,meta:{readonly:!1},title:null}},[u.values.fields,e,a,o==null?void 0:o.identifier,d]);return n.jsx(xe,{schema:p,values:o,onSubmit:r,cancelText:o?void 0:"Back",onCancel:t})}),we=c.memo(function(i){const{parentPath:e,index:t,children:r,initial:o,editing:d,conditionalSourceFields:a}=i,[u,p]=c.useState(),h=(o==null?void 0:o.type)??u,f=h?Je[h].fieldTypeName:void 0,{setFieldValue:m,values:v}=A.useFormikContext();if(d&&!o)throw new Error("Initial field must be provided if editing is true.");const x=!h&&!d&&!o,w=x?"Choose a field type":`${f} settings`,F=x?"Select a field type to add to this section.":(f==null?void 0:f.toLowerCase())==="section"?"Customize your section":`Customize your ${f==null?void 0:f.toLowerCase()} field.`,I=c.useCallback(()=>p(void 0),[]),b=c.useCallback(M=>{p(void 0),M()},[]),z=c.useCallback((M,O)=>{const{label:G}=M;if(!h)throw new Error("Field type must be selected before creating a field.");if(!G||typeof G!="string")throw new Error("Label must be provided before creating a field.");const V=de({type:h,...M,identifier:ot(M.identifier,G)}).serialize(),S=Q(v,e);if(S===void 0)throw new Error("Parent path must point to an existing field.");let T;if(!Array.isArray(S))throw new Error("Parent path must point to an array.");d?T=st(S,t,V):T=je(S,t,V),m(e,T).then(),O()},[d,h,v,e,m,t]),P=c.useCallback(M=>x?n.jsx(It,{setFieldType:p}):n.jsx(bt,{conditionalSourceFields:a,handleCancel:I,handleCreateField:O=>z(O,M),fieldType:h,defaultField:o}),[a,I,z,o,x,h]);return n.jsx(s.Dialog,{onCloseInterrupt:b,title:w,description:F,content:P,children:r})}),ke=({children:l})=>n.jsx(n.Fragment,{children:l}),Re=(l,i)=>({initial:l?i:"none",sm:l?"none":i}),et=c.memo(function(i){const{remove:e,dragHandleProps:t,editProps:r,insertAfterProps:o}=i,d=c.useMemo(()=>[{Wrapper:we,wrapperProps:r,Icon:s.Pencil1Icon,text:"Edit"},{Icon:s.TrashIcon,buttonProps:{onClick:e},text:"Delete"},{Wrapper:we,wrapperProps:o,Icon:s.PlusIcon,text:"Add after"},{Icon:a=>n.jsx("div",{...a,children:n.jsx(s.DragHandleDots2Icon,{})}),text:"Reorder",disableOnMobile:!0,buttonProps:{...t,asChild:!0}}],[t,r,o,e]);return n.jsxs(n.Fragment,{children:[n.jsx(s.Flex,{gap:"4",display:Re(!1,"flex"),children:d.map(a=>{const u=a.Wrapper??ke;return n.jsx(u,{...a.wrapperProps,children:n.jsx(s.IconButton,{type:"button",variant:"ghost","aria-label":a.text,...a.buttonProps,children:n.jsx(a.Icon,{})})},a.text)})}),n.jsx(s.Box,{display:Re(!0,"block"),children:n.jsx(s.DropdownMenu,{trigger:n.jsx(s.IconButton,{variant:"ghost","aria-label":"Actions menu",children:n.jsx(s.DotsVerticalIcon,{})}),closeOnSelect:!1,items:d.map(a=>{var p;if(a.disableOnMobile)return null;const u=a.Wrapper??ke;return{...a.buttonProps,onSelect:(p=a.buttonProps)==null?void 0:p.onClick,content:n.jsx(u,{...a.wrapperProps,children:n.jsxs(s.Flex,{gap:"2",align:"center",children:[n.jsx(a.Icon,{}),a.text]})})}}).filter(a=>a!==null)})})]})}),Ie="form-builder",Tt=c.memo(function(i){const{field:e,index:t,sectionIndex:r,remove:o}=i,d=c.useMemo(()=>de(e),[e]),a=ce(d,{formId:Ie,disabled:!0}),u=c.useMemo(()=>({index:t,parentPath:`fields.${r}.fields`,initial:e,editing:!0}),[e,t,r]),p=c.useMemo(()=>({parentPath:`fields.${r}.fields`,index:t+1,initial:void 0}),[t,r]);return n.jsx(N.Draggable,{draggableId:e.identifier,index:t,children:h=>n.jsx(s.Card,{ref:h.innerRef,...h.draggableProps,...h.dragHandleProps,mb:"4",children:n.jsxs(s.Flex,{gap:"4",justify:"between",align:"center",children:[a,n.jsx(et,{remove:o,editProps:u,insertAfterProps:p,dragHandleProps:h.dragHandleProps})]})})})}),St=c.memo(function(i){var z,P,M,O,G,V;const{field:e,index:t,dropState:r}=i,o=(z=r[e.identifier])==null?void 0:z.disabled,{setFieldValue:d,values:a}=A.useFormikContext(),u=s.useAlertDialog(),p=c.useCallback((S,T)=>{for(const D of S){const L=T.indexOf(D);d(`fields.${L}.condition`,null).then(),d(`fields.${L}.conditional`,!1).then()}},[d]),h=c.useCallback(S=>{var L;const T=e.fields[S];if(!T)throw new Error("Could not find field to remove.");const D=[];for(const q of a.fields)((L=q.condition)==null?void 0:L.identifier)===T.identifier&&D.push(q);return{removing:T,affectedSections:D,action:()=>d(`fields.${t}.fields`,me(e.fields,S))}},[e.fields,a.fields,d,t]),f=c.useCallback(S=>{const{affectedSections:T,action:D,removing:L}=h(S),q=()=>{D().then(),p(T,a.fields)};if(T.length>0){const E=T.map(B=>B.label).join(", ");return u({title:"Remove condition?",description:`${L.label} is being used as a condition, deleting it will remove the condition from the ${E} section(s).`,severity:"danger",actionText:"Remove",onAction:q})}q()},[h,p,a.fields,u]),m=c.useCallback(()=>{const T=e.fields.map((oe,Oe)=>h(Oe)).flatMap(oe=>oe.affectedSections),D=T.length?"Remove fields and conditions?":"Remove fields?",L=e.fields.length,q=T.map(oe=>oe.label).join(", "),E=T.length?`Deleting this section will remove the ${L} field(s) it contains and will remove the conditions from following sections: ${q}`:`Deleting this section will remove the ${L} field(s) it contains.`,B=me(a.fields,t),Y=()=>d("fields",B);if(T.length>0)return u({title:D,description:E,severity:"danger",actionText:"Remove",onAction:()=>{Y().then(()=>{p(T,B)})}});Y().then()},[e.fields,a.fields,t,h,d,u,p]),v=c.useMemo(()=>({index:t,parentPath:"fields",initial:e,editing:!0,conditionalSourceFields:be(a.fields,t)}),[e,t,a.fields]),x=c.useMemo(()=>({index:t+1,parentPath:"fields",initial:pe(),conditionalSourceFields:be(a.fields,t+1)}),[t,a.fields]),w=c.useMemo(()=>({parentPath:`fields.${t}.fields`,index:e.fields.length,initial:void 0}),[e.fields.length,t]),F=c.useMemo(()=>{var S,T;return(T=We(a.fields,(S=e.condition)==null?void 0:S.identifier))==null?void 0:T.label},[(P=e.condition)==null?void 0:P.identifier,a.fields]),I=Array.isArray((M=e.condition)==null?void 0:M.value)?"contains all of":"equals",b=Array.isArray((O=e.condition)==null?void 0:O.value)?(G=e.condition)==null?void 0:G.value.map(S=>typeof S=="string"?S:S.label).join(", "):(V=e.condition)==null?void 0:V.value.toString();return n.jsx(N.Draggable,{draggableId:e.identifier,index:t,children:S=>n.jsx(s.Card,{ref:S.innerRef,...S.draggableProps,...S.dragHandleProps,mb:"4",children:n.jsxs(s.Flex,{gap:"3",justify:"between",align:"center",children:[n.jsxs(s.Flex,{direction:"column",gap:"2",grow:"1",children:[n.jsxs(s.Flex,{direction:"column",children:[n.jsx(s.Heading,{as:"h3",size:"3",children:e.label}),n.jsx(s.Text,{className:ue.description,children:e.description})]}),e.condition&&n.jsx(s.Text,{size:"1",children:n.jsxs(s.Em,{children:["Display only if ",n.jsx(s.Strong,{children:F})," ",I," ",n.jsx(s.Strong,{children:b})]})}),n.jsx(N.Droppable,{droppableId:e.identifier,type:"SECTION",isDropDisabled:o,children:T=>n.jsxs(s.Flex,{ref:T.innerRef,...T.droppableProps,direction:"column",gap:"0",children:[e.fields.map((D,L)=>n.jsx(Tt,{field:D,index:L,sectionIndex:t,remove:()=>f(L)},D.identifier)),T.placeholder,n.jsx(we,{...w,children:n.jsxs(s.Button,{type:"button",variant:"outline",children:[n.jsx(s.PlusIcon,{})," Add a field"]})})]})})]}),n.jsx(et,{remove:m,insertAfterProps:x,dragHandleProps:S.dragHandleProps,editProps:v})]})})})}),Et=(l,i)=>{var t;const e={...l};switch(i.type){case"release":for(const r in e)e[r].disabled=!1;return e;case"hold":for(const r in e)(t=e[r])!=null&&t.conditionFields.has(i.fieldId)&&(e[r].disabled=!0);return e;case"update":return i.state}},Mt=(l,i)=>{if(i)for(let e=0;e<l.length;e++){const t=l[e];if(t){for(const r of t.fields)if(r.identifier===i)return e}}},tt=l=>{var e,t,r;const i={};for(let o=0;o<l.length;o++){const d=l[o];if(!d)throw new Error("Field is undefined.");const a=o>0?(e=i[l[o-1].identifier])==null?void 0:e.conditionFields:void 0,u=new Set(a);(t=d.condition)!=null&&t.identifier&&u.add(d.condition.identifier),i[d.identifier]={disabled:!1,conditionFields:u,conditionIndex:Mt(l,(r=d.condition)==null?void 0:r.identifier),index:o,label:d.label}}return i},it=(l,i)=>{for(const[e,t]of Object.entries(l))if(t.identifier===i)return[t,e]},zt=c.memo(function(){const{values:i,setFieldValue:e}=A.useFormikContext(),[t,r]=c.useReducer(Et,i.fields,tt),{showInfo:o}=s.useToast();c.useEffect(()=>{r({type:"update",state:tt(i.fields)})},[r,i.fields]);const d=c.useCallback(p=>{p.type==="SECTION"&&r({type:"hold",fieldId:p.draggableId})},[]),a=c.useCallback(p=>{const{source:h,destination:f,type:m,reason:v,draggableId:x}=p;if(r({type:"release"}),!f||v==="CANCEL")return;if(m==="ROOT"){const z=t[x];if(!z)throw new Error("Could not find section context.");let P=typeof z.conditionIndex<"u"?Math.max(z.conditionIndex+1,f.index):f.index;for(const M of Object.values(t))M.conditionIndex===h.index&&(P=Math.min(P,M.index-1));return P!=f.index&&o({title:"Reordered sections",description:"Sections with conditions must be below the fields they reference."}),e("fields",Ce(i.fields,h.index,P))}if(m!=="SECTION")throw new Error("Unexpected droppable type.");const[w,F]=it(i.fields,h.droppableId)??[],[I,b]=it(i.fields,f.droppableId)??[];if(!(w!=null&&w.fields)||!I)throw new Error("Could not find section with fields.");if(w.identifier===I.identifier)e(`fields.${F}.fields`,Ce(w.fields,h.index,f.index)).then();else{const z=w.fields[h.index];if(!z)throw new Error("Could not find field to reorder.");e(`fields.${F}.fields`,me(w.fields,h.index)).then(),e(`fields.${b}.fields`,je(I.fields,f.index,z)).then()}},[i.fields,e,t,o]),u=c.useMemo(()=>({index:i.fields.length,parentPath:"fields",initial:pe(),conditionalSourceFields:be(i.fields,i.fields.length)}),[i.fields]);return n.jsx(N.DragDropContext,{onDragStart:d,onDragEnd:a,children:n.jsx(N.Droppable,{droppableId:"droppable",type:"ROOT",children:p=>n.jsxs(s.Flex,{ref:p.innerRef,...p.droppableProps,direction:"column",gap:"0",children:[i.fields.map((h,f)=>n.jsx(St,{field:h,index:f,dropState:t},h.label)),p.placeholder,n.jsx(we,{...u,children:n.jsxs(s.Button,{type:"button",variant:"outline",children:[n.jsx(s.PlusIcon,{})," Add a section"]})})]})})})}),Pt={title:"",description:"",fields:[]},At=new J({label:"Title",minLength:0,maxLength:100,required:!0,identifier:"title"}),Lt={formId:Ie,placeholder:"Give your form a title."},Dt=new Z({label:"Description",minLength:0,maxLength:1e3,required:!1,identifier:"description"}),Ot={formId:Ie,placeholder:"Explain the purpose of this form."},Vt=()=>{alert("This is a form preview, your data will not be saved.")},Bt=c.memo(c.forwardRef((l,i)=>{const{onCancel:e,onSave:t,revision:r}=l,o=r?"Edit form":"Create a new form",{heading:d=o}=l,a=c.useCallback(v=>{const x={};if(v.title||(x.title="Title is required."),(!v.fields||v.fields.length===0)&&(x.fields="At least one field is required."),ve(x))return x},[]),u=A.useFormik({initialValues:rt(r)??Pt,validate:a,onSubmit:v=>t(v),validateOnChange:!1,validateOnBlur:!1}),p=c.useMemo(()=>Ee(u.values),[u.values]),h=ce(At,Lt),f=ce(Dt,Ot),m=c.useMemo(()=>typeof d=="object"?d:n.jsx(s.Heading,{children:d}),[d]);return n.jsx(s.Tabs.Root,{ref:i,defaultValue:"edit",children:n.jsxs(s.Flex,{direction:"column",gap:"2",children:[n.jsxs(s.Tabs.List,{children:[n.jsx(s.Tabs.Trigger,{value:"edit",children:"Edit"}),n.jsx(s.Tabs.Trigger,{value:"preview",children:"Preview"})]}),n.jsxs(s.Tabs.Content,{value:"edit",children:[m,n.jsxs(s.Text,{children:["Add a new form field by clicking a + button. Specify options for each field, then drag and drop to rearrange them. You can see what a submitted form might look like in the"," ",n.jsx("em",{children:"Preview"})," tab, but"," ",n.jsx("strong",{children:"field values entered on this page will not be saved."})]}),n.jsx(s.Flex,{asChild:!0,direction:"column",gap:"2",mt:"3",children:n.jsxs("form",{id:Ie,onSubmit:u.handleSubmit,children:[n.jsxs(A.FormikProvider,{value:u,children:[h,f,n.jsx(zt,{}),n.jsx(s.Text,{severity:"danger",size:"1",children:typeof u.errors.fields=="string"&&u.errors.fields})]}),n.jsxs(s.Flex,{justify:"end",gap:"2",children:[n.jsx(s.Button,{type:"button",variant:"soft",onClick:e,children:"Cancel"}),n.jsx(s.Button,{type:"submit",disabled:!u.isValid,children:"Save"})]})]})})]}),n.jsx(s.Tabs.Content,{value:"preview",children:n.jsx(xe,{schema:p,onSubmit:Vt})})]})})}));y.BooleanField=K,y.BooleanInput=Be,y.DateField=fe,y.DateInput=_e,y.FieldSection=U,y.FormBrowser=ht,y.FormBuilder=Bt,y.FormRenderer=xe,y.FormSubmissionBrowser=gt,y.FormSubmissionViewer=ft,y.MultiSelectField=ge,y.MultiSelectInput=Ke,y.MultiStringField=le,y.MultiStringInput=Ue,y.NumberField=X,y.NumberInput=$e,y.PatchField=yt,y.PatchFormProvider=vt,y.SelectField=ae,y.SelectInput=He,y.StringField=J,y.StringInput=qe,y.TextField=Z,y.TextInput=Ne,y.deserialize=de,y.deserializeField=Se,y.formRevisionToSchema=Ee,y.isConditionMet=Me,y.useFieldInput=ce,y.useFieldInputs=ze,Object.defineProperty(y,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1,3 @@
1
+ export * from "./builder";
2
+ export * from "./fields";
3
+ export * from "./renderer";
@@ -0,0 +1,11 @@
1
+ /// <reference types="react" />
2
+ import { CachedUserForm } from "@overmap-ai/core";
3
+ interface FormBrowserProps {
4
+ /** If `true`, the user can toggle the favorite state of the forms they have access to */
5
+ isFavoriteEditable?: boolean;
6
+ onSelectForm: (form: CachedUserForm) => void;
7
+ /** @default 20 */
8
+ maxResults?: number;
9
+ }
10
+ export declare const FormBrowser: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<FormBrowserProps & import("react").RefAttributes<HTMLDivElement>>>;
11
+ export {};
@@ -0,0 +1,28 @@
1
+ /// <reference types="react" />
2
+ import { ISchema } from '../../fields/typings';
3
+ import { Form } from '../../typings';
4
+ interface FormRendererProps {
5
+ /** The schema of the form the render */
6
+ schema: ISchema;
7
+ /** Initial values of the form */
8
+ values?: Form;
9
+ onSubmit?: (values: Form) => Promise<void> | void;
10
+ /** @default "Submit" */
11
+ submitText?: string;
12
+ /** The text of the cancel button (hidden by default)
13
+ * @default null
14
+ */
15
+ cancelText?: string;
16
+ onCancel?: () => void;
17
+ onDirty?: () => void;
18
+ /** Hide the form description
19
+ * @default false */
20
+ hideDescription?: boolean;
21
+ /** Hide the title (and description)
22
+ * @default false
23
+ */
24
+ hideTitle?: boolean;
25
+ className?: string;
26
+ }
27
+ export declare const FormRenderer: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<FormRendererProps & import("react").RefAttributes<HTMLDivElement>>>;
28
+ export {};
@@ -0,0 +1,28 @@
1
+ import React, { ReactNode } from "react";
2
+ import { Offline, UserFormSubmission, UserFormSubmissionPayload } from "@overmap-ai/core";
3
+ export type FormSubmissionClickEvent = {
4
+ submission: Offline<UserFormSubmissionPayload>;
5
+ };
6
+ interface UserFormSubmissionBrowserEntryProps {
7
+ submission: Offline<UserFormSubmissionPayload> | UserFormSubmission;
8
+ onSubmissionClick?: (e: FormSubmissionClickEvent) => void;
9
+ rowDecorator?: (submission: Offline<UserFormSubmissionPayload> | UserFormSubmission, Row: ReactNode) => ReactNode;
10
+ compact: boolean;
11
+ labelType: "creator" | "formName";
12
+ }
13
+ interface FormSubmissionBrowserProps {
14
+ formId?: string;
15
+ submissions?: (Offline<UserFormSubmissionPayload> | UserFormSubmission)[];
16
+ onSubmissionClick?: (e: FormSubmissionClickEvent) => void;
17
+ rowDecorator?: UserFormSubmissionBrowserEntryProps["rowDecorator"];
18
+ /** @default false */
19
+ compact?: boolean;
20
+ labelType: "creator" | "formName";
21
+ className?: string;
22
+ /** @default outline */
23
+ variant?: "ghost" | "outline";
24
+ /** Content displayed within the container but after the list */
25
+ after?: ReactNode;
26
+ }
27
+ export declare const FormSubmissionBrowser: React.NamedExoticComponent<FormSubmissionBrowserProps>;
28
+ export {};
@@ -0,0 +1,17 @@
1
+ /// <reference types="react" />
2
+ import { Offline, UserFormSubmissionPayload } from "@overmap-ai/core";
3
+ interface FormSubmissionViewerProps {
4
+ /** The submission to render for viewing */
5
+ submission: Offline<UserFormSubmissionPayload>;
6
+ /** @default false */
7
+ showFormDescription?: boolean;
8
+ /** @default true */
9
+ showFormTitle?: boolean;
10
+ }
11
+ /**
12
+ * Wrapper for `FormRenderer` that takes a `UserFormSubmissionPayload` instead of an `ISchema`.
13
+ * Also, unlike `FormRenderer`, this component depends on the redux store.
14
+ * @see FormRenderer
15
+ */
16
+ export declare const FormSubmissionViewer: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<FormSubmissionViewerProps & import("react").RefAttributes<HTMLDivElement>>>;
17
+ export {};
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from "react";
2
+ import { FieldValue } from "@overmap-ai/core";
3
+ interface RenderArgs {
4
+ value: FieldValue;
5
+ /** Intermediate changes to the field value */
6
+ setValue: (value: FieldValue) => void;
7
+ /** EX: when the onBlur event is fired */
8
+ patchValue: () => void;
9
+ }
10
+ interface PatchFieldProps {
11
+ name: string;
12
+ render: (args: RenderArgs) => ReactNode;
13
+ }
14
+ export declare const PatchField: import("react").NamedExoticComponent<PatchFieldProps>;
15
+ export {};
@@ -0,0 +1,24 @@
1
+ /// <reference types="react" />
2
+ import { ISchema } from '../../fields';
3
+ import { FormikErrors } from "formik";
4
+ import { Form } from '../../typings';
5
+ interface PatchFormProviderProps {
6
+ children: React.ReactNode;
7
+ schema: ISchema;
8
+ values: Form;
9
+ /** Called when `patchValue` is called on a child `PatchField` and the form is valid.
10
+ * @example ```js
11
+ * {"field name": "field value"}
12
+ * ``` */
13
+ onPatch: (values: Form) => void;
14
+ /** Called when `patchValue` is called on a child `PatchField` and the form is not valid.
15
+ * After this event is fired, the form is reset to the initial values.
16
+ * @example ```js
17
+ * {"field name": "error message"}
18
+ * ``` */
19
+ onError: (error: FormikErrors<Form>) => void;
20
+ className?: string;
21
+ }
22
+ /** Use PatchForms to create patch edits to existing forms rather than editing the entire form. */
23
+ export declare const PatchFormProvider: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<PatchFormProviderProps & import("react").RefAttributes<HTMLFormElement>>>;
24
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from "./Field";
2
+ export * from "./Provider";
@@ -0,0 +1,5 @@
1
+ export * from "./FormRenderer/FormRenderer";
2
+ export * from "./FormSubmissionViewer/FormSubmissionViewer";
3
+ export * from "./FormBrowser/FormBrowser";
4
+ export * from "./FormSubmissionBrowser/FormSubmissionBrowser";
5
+ export * from "./PatchForm";
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ ._description_17zed_1{white-space:pre-line}._favoriteIcon_1bixi_1._favoriteIcon_1bixi_1{color:var(--yellow-a9)}._favoriteIcon_1bixi_1._favoriteIcon_1bixi_1:hover:not(:disabled){background-color:transparent;color:var(--gray-a9)}._regularIcon_1bixi_9._regularIcon_1bixi_9:hover:not(:disabled){color:var(--yellow-a9);background-color:transparent}._submissionsContainer_9iirt_1{overflow-y:auto;max-height:min(369px,100vh - 200px)}._stopHorizontalOverflow_9iirt_6{min-width:0}
@@ -0,0 +1,5 @@
1
+ import { FieldValue } from "@overmap-ai/core";
2
+ import { AnyField, BaseField } from "./fields";
3
+ export type Form = Record<string, FieldValue>;
4
+ /** Helper type that extracts the TValue type from a BaseField. */
5
+ export type ValueOfField<Type extends AnyField> = Type extends BaseField<infer TValue> ? TValue : never;
@@ -0,0 +1,7 @@
1
+ import { ISchema } from './fields';
2
+ import { BaseFormElement } from './fields/BaseField';
3
+ import { FormikErrors } from "formik";
4
+ import { Form } from './typings';
5
+ export declare const hasKeys: (errors: object) => boolean;
6
+ export declare const validateForm: (schema: ISchema, form: Form) => Promise<FormikErrors<Form> | undefined>;
7
+ export declare const initialFormValues: (fields: BaseFormElement[], values: Form) => Form;
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@overmap-ai/forms",
3
+ "description": "Form functionality for Overmap",
4
+ "author": "Wôrdn Inc.",
5
+ "license": "UNLICENSED",
6
+ "version": "1.0.0",
7
+ "type": "module",
8
+ "main": "dist/forms.umd.cjs",
9
+ "module": "dist/forms.js",
10
+ "types": "dist/index.d.ts",
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "dev": "yarn storybook",
16
+ "storybook": "storybook dev -p 6006",
17
+ "build": "tsc --noEmit && vite build",
18
+ "lint": "eslint --report-unused-disable-directives --max-warnings 0 \"src/**/*.{tsx,ts}\"",
19
+ "lint:fix": "yarn lint --fix",
20
+ "preview": "vite preview",
21
+ "prepare": "husky install",
22
+ "build-storybook": "storybook build"
23
+ },
24
+ "dependencies": {
25
+ "@hello-pangea/dnd": "^16.3.0",
26
+ "formik": "^2.4.5",
27
+ "lodash.get": "^4.4.2",
28
+ "lodash.set": "^4.3.2"
29
+ },
30
+ "peerDependencies": {
31
+ "@overmap-ai/blocks": "^1.0.1",
32
+ "@overmap-ai/core": "^1.0.1",
33
+ "react": "^18.2.0",
34
+ "react-dom": "^18.2.0"
35
+ },
36
+ "devDependencies": {
37
+ "@overmap-ai/blocks": "^1.0.1",
38
+ "@overmap-ai/core": "^1.0.1",
39
+ "@rollup/plugin-commonjs": "^25.0.4",
40
+ "@storybook/addon-a11y": "^7.4.5",
41
+ "@storybook/addon-essentials": "^7.4.5",
42
+ "@storybook/addon-interactions": "^7.4.5",
43
+ "@storybook/addon-links": "^7.4.5",
44
+ "@storybook/addon-themes": "^7.4.5",
45
+ "@storybook/blocks": "^7.4.5",
46
+ "@storybook/react": "^7.4.5",
47
+ "@storybook/react-vite": "^7.4.5",
48
+ "@storybook/testing-library": "^0.2.1",
49
+ "@types/lodash.get": "^4.4.7",
50
+ "@types/lodash.set": "^4.3.7",
51
+ "@types/node": "^20.6.2",
52
+ "@types/react": "^18.2.15",
53
+ "@types/react-dom": "^18.2.7",
54
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
55
+ "@typescript-eslint/parser": "^6.0.0",
56
+ "@vitejs/plugin-react-swc": "^3.3.2",
57
+ "eslint": "^8.45.0",
58
+ "eslint-config-prettier": "^9.0.0",
59
+ "eslint-plugin-prettier": "^5.0.0",
60
+ "eslint-plugin-react": "^7.33.2",
61
+ "eslint-plugin-react-hooks": "^4.6.0",
62
+ "eslint-plugin-react-refresh": "^0.4.3",
63
+ "eslint-plugin-storybook": "^0.6.14",
64
+ "husky": "^8.0.3",
65
+ "lint-staged": "^14.0.1",
66
+ "prettier": "^3.0.3",
67
+ "react": "^18.2.0",
68
+ "react-dom": "^18.2.0",
69
+ "react-redux": "^8.1.3",
70
+ "redux": "^4.2.1",
71
+ "rollup-plugin-polyfill-node": "^0.12.0",
72
+ "sass": "^1.67.0",
73
+ "storybook": "^7.4.5",
74
+ "typescript": "^5.0.2",
75
+ "vite": "^4.4.5",
76
+ "vite-plugin-dts": "^3.5.3",
77
+ "vite-plugin-externalize-deps": "^0.7.0"
78
+ },
79
+ "lint-staged": {
80
+ "*.{ts,tsx}": [
81
+ "eslint --fix --report-unused-disable-directives --max-warnings 0"
82
+ ]
83
+ },
84
+ "resolutions": {
85
+ "jackspeak": "2.1.1",
86
+ "@types/react": "^18.2.15"
87
+ }
88
+ }