ngx-t-forms 2.0.29 → 2.0.31

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 (161) hide show
  1. package/fesm2022/ngx-t-forms-auto-complete-input-element.component-DCKuXHAW.mjs +104 -0
  2. package/fesm2022/ngx-t-forms-auto-complete-input-element.component-DCKuXHAW.mjs.map +1 -0
  3. package/fesm2022/ngx-t-forms-basic-input-input-element.component-Ce4ipSUc.mjs +85 -0
  4. package/fesm2022/ngx-t-forms-basic-input-input-element.component-Ce4ipSUc.mjs.map +1 -0
  5. package/fesm2022/ngx-t-forms-calculated-field-rules.component-C5TPddVe.mjs +643 -0
  6. package/fesm2022/ngx-t-forms-calculated-field-rules.component-C5TPddVe.mjs.map +1 -0
  7. package/fesm2022/ngx-t-forms-chip-options-creator-editor.component-CICQaqz6.mjs +97 -0
  8. package/fesm2022/ngx-t-forms-chip-options-creator-editor.component-CICQaqz6.mjs.map +1 -0
  9. package/fesm2022/ngx-t-forms-config-mscoa-additional-inputs.component-CzisLSIP.mjs +195 -0
  10. package/fesm2022/ngx-t-forms-config-mscoa-additional-inputs.component-CzisLSIP.mjs.map +1 -0
  11. package/fesm2022/ngx-t-forms-data-source-picker.component-Dzz_o6fJ.mjs +261 -0
  12. package/fesm2022/ngx-t-forms-data-source-picker.component-Dzz_o6fJ.mjs.map +1 -0
  13. package/fesm2022/ngx-t-forms-date-picker-input-element.component-CYUbVyzP.mjs +85 -0
  14. package/fesm2022/ngx-t-forms-date-picker-input-element.component-CYUbVyzP.mjs.map +1 -0
  15. package/fesm2022/ngx-t-forms-date-range-picker-input-element.component-CmoquQGV.mjs +156 -0
  16. package/fesm2022/ngx-t-forms-date-range-picker-input-element.component-CmoquQGV.mjs.map +1 -0
  17. package/fesm2022/ngx-t-forms-document-list-label-config-editor.component-CLUOXreG.mjs +368 -0
  18. package/fesm2022/ngx-t-forms-document-list-label-config-editor.component-CLUOXreG.mjs.map +1 -0
  19. package/fesm2022/ngx-t-forms-document-picker.component-qObjcqhE.mjs +704 -0
  20. package/fesm2022/ngx-t-forms-document-picker.component-qObjcqhE.mjs.map +1 -0
  21. package/fesm2022/ngx-t-forms-editor-input-element.component-BLXlfb6F.mjs +294 -0
  22. package/fesm2022/ngx-t-forms-editor-input-element.component-BLXlfb6F.mjs.map +1 -0
  23. package/fesm2022/ngx-t-forms-editor-js-input.component-BQL0AH7H.mjs +240 -0
  24. package/fesm2022/ngx-t-forms-editor-js-input.component-BQL0AH7H.mjs.map +1 -0
  25. package/fesm2022/ngx-t-forms-file-upload-input-element.component-C7mMeEjF.mjs +205 -0
  26. package/fesm2022/ngx-t-forms-file-upload-input-element.component-C7mMeEjF.mjs.map +1 -0
  27. package/fesm2022/ngx-t-forms-form-input-selector.component-C9u8zq9B.mjs +86 -0
  28. package/fesm2022/ngx-t-forms-form-input-selector.component-C9u8zq9B.mjs.map +1 -0
  29. package/fesm2022/ngx-t-forms-form-json-view.component-856Hx1Bg.mjs +22 -0
  30. package/fesm2022/ngx-t-forms-form-json-view.component-856Hx1Bg.mjs.map +1 -0
  31. package/fesm2022/ngx-t-forms-form-payload-projection.component-CDkTuX9S.mjs +179 -0
  32. package/fesm2022/ngx-t-forms-form-payload-projection.component-CDkTuX9S.mjs.map +1 -0
  33. package/fesm2022/ngx-t-forms-form-section-stepper.component-Bs50-nEB.mjs +319 -0
  34. package/fesm2022/ngx-t-forms-form-section-stepper.component-Bs50-nEB.mjs.map +1 -0
  35. package/fesm2022/ngx-t-forms-forms-builder-menu.component-qrhM0jGL.mjs +379 -0
  36. package/fesm2022/ngx-t-forms-forms-builder-menu.component-qrhM0jGL.mjs.map +1 -0
  37. package/fesm2022/ngx-t-forms-geo-location.component-Bosp1UzR.mjs +124 -0
  38. package/fesm2022/ngx-t-forms-geo-location.component-Bosp1UzR.mjs.map +1 -0
  39. package/fesm2022/ngx-t-forms-getInputIcon-B4ADgevZ.mjs +31 -0
  40. package/fesm2022/ngx-t-forms-getInputIcon-B4ADgevZ.mjs.map +1 -0
  41. package/fesm2022/ngx-t-forms-image-capture-input-element.component-C1g7Z0cK.mjs +180 -0
  42. package/fesm2022/ngx-t-forms-image-capture-input-element.component-C1g7Z0cK.mjs.map +1 -0
  43. package/fesm2022/ngx-t-forms-index-dDSobs6A.mjs +2 -0
  44. package/fesm2022/ngx-t-forms-index-dDSobs6A.mjs.map +1 -0
  45. package/fesm2022/ngx-t-forms-input-custom.component-BkbHFAyR.mjs +105 -0
  46. package/fesm2022/ngx-t-forms-input-custom.component-BkbHFAyR.mjs.map +1 -0
  47. package/fesm2022/ngx-t-forms-input-editor.component-BPUOM9kQ.mjs +181 -0
  48. package/fesm2022/ngx-t-forms-input-editor.component-BPUOM9kQ.mjs.map +1 -0
  49. package/fesm2022/{ngx-t-forms-map-mat-options-keys-CbdW82su.mjs → ngx-t-forms-map-mat-options-keys-B6hJ7Io5.mjs} +12 -14
  50. package/fesm2022/ngx-t-forms-map-mat-options-keys-B6hJ7Io5.mjs.map +1 -0
  51. package/fesm2022/ngx-t-forms-mat-chip-list-editor.component-c7uZT1sr.mjs +66 -0
  52. package/fesm2022/ngx-t-forms-mat-chip-list-editor.component-c7uZT1sr.mjs.map +1 -0
  53. package/fesm2022/ngx-t-forms-mat-slider-editor.component-CTSBrM-j.mjs +211 -0
  54. package/fesm2022/ngx-t-forms-mat-slider-editor.component-CTSBrM-j.mjs.map +1 -0
  55. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-CcYiwx-8.mjs +165 -0
  56. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-CcYiwx-8.mjs.map +1 -0
  57. package/fesm2022/ngx-t-forms-missing-form-configs.component-DrnH8qdG.mjs +38 -0
  58. package/fesm2022/ngx-t-forms-missing-form-configs.component-DrnH8qdG.mjs.map +1 -0
  59. package/fesm2022/ngx-t-forms-mscoa-chart-toolbar.component-C_abEBQ5.mjs +38 -0
  60. package/fesm2022/ngx-t-forms-mscoa-chart-toolbar.component-C_abEBQ5.mjs.map +1 -0
  61. package/fesm2022/ngx-t-forms-mscoa-error-display.component-99DpVSy7.mjs +126 -0
  62. package/fesm2022/ngx-t-forms-mscoa-error-display.component-99DpVSy7.mjs.map +1 -0
  63. package/fesm2022/ngx-t-forms-mscoa-segment-config.component-C0qsMfsq.mjs +336 -0
  64. package/fesm2022/ngx-t-forms-mscoa-segment-config.component-C0qsMfsq.mjs.map +1 -0
  65. package/fesm2022/ngx-t-forms-mscoa-temporary-hint.component-B1Z-IXSL.mjs +74 -0
  66. package/fesm2022/ngx-t-forms-mscoa-temporary-hint.component-B1Z-IXSL.mjs.map +1 -0
  67. package/fesm2022/ngx-t-forms-multiple-input-input-element.component-C7y1OGPx.mjs +905 -0
  68. package/fesm2022/ngx-t-forms-multiple-input-input-element.component-C7y1OGPx.mjs.map +1 -0
  69. package/fesm2022/ngx-t-forms-ngx-t-forms-u_kigDid.mjs +19461 -0
  70. package/fesm2022/ngx-t-forms-ngx-t-forms-u_kigDid.mjs.map +1 -0
  71. package/fesm2022/ngx-t-forms-paginated-selection-table-AQZSMmhr.mjs +555 -0
  72. package/fesm2022/ngx-t-forms-paginated-selection-table-AQZSMmhr.mjs.map +1 -0
  73. package/fesm2022/ngx-t-forms-pipeline-generator.component-DmNSc5aw.mjs +748 -0
  74. package/fesm2022/ngx-t-forms-pipeline-generator.component-DmNSc5aw.mjs.map +1 -0
  75. package/fesm2022/ngx-t-forms-record-list-manager.component-CUMMvMch.mjs +358 -0
  76. package/fesm2022/ngx-t-forms-record-list-manager.component-CUMMvMch.mjs.map +1 -0
  77. package/fesm2022/ngx-t-forms-required-inputs.component-Ch2yNcIS.mjs +272 -0
  78. package/fesm2022/ngx-t-forms-required-inputs.component-Ch2yNcIS.mjs.map +1 -0
  79. package/fesm2022/ngx-t-forms-rest-api-call-setup.component-C_aFtdvW.mjs +398 -0
  80. package/fesm2022/ngx-t-forms-rest-api-call-setup.component-C_aFtdvW.mjs.map +1 -0
  81. package/fesm2022/ngx-t-forms-search-field.component-B2ZO7lqO.mjs +38 -0
  82. package/fesm2022/ngx-t-forms-search-field.component-B2ZO7lqO.mjs.map +1 -0
  83. package/fesm2022/ngx-t-forms-section-report.component-BxOhR6C0.mjs +98 -0
  84. package/fesm2022/ngx-t-forms-section-report.component-BxOhR6C0.mjs.map +1 -0
  85. package/fesm2022/ngx-t-forms-select-input-element.component-DbgZdNoe.mjs +150 -0
  86. package/fesm2022/ngx-t-forms-select-input-element.component-DbgZdNoe.mjs.map +1 -0
  87. package/fesm2022/ngx-t-forms-selection-options-editor.component-Dhln81DL.mjs +169 -0
  88. package/fesm2022/ngx-t-forms-selection-options-editor.component-Dhln81DL.mjs.map +1 -0
  89. package/fesm2022/ngx-t-forms-t-workflow-picker.component-leBokXvM.mjs +204 -0
  90. package/fesm2022/ngx-t-forms-t-workflow-picker.component-leBokXvM.mjs.map +1 -0
  91. package/fesm2022/ngx-t-forms-textarea-input-element.component-BEbXJjFA.mjs +95 -0
  92. package/fesm2022/ngx-t-forms-textarea-input-element.component-BEbXJjFA.mjs.map +1 -0
  93. package/fesm2022/ngx-t-forms-toggle-input-element.component-DDErRUJd.mjs +82 -0
  94. package/fesm2022/ngx-t-forms-toggle-input-element.component-DDErRUJd.mjs.map +1 -0
  95. package/fesm2022/ngx-t-forms-validators-config.component-oGjQVGE2.mjs +733 -0
  96. package/fesm2022/ngx-t-forms-validators-config.component-oGjQVGE2.mjs.map +1 -0
  97. package/fesm2022/ngx-t-forms-workflow-adjudication.component-CtU8dECN.mjs +1303 -0
  98. package/fesm2022/ngx-t-forms-workflow-adjudication.component-CtU8dECN.mjs.map +1 -0
  99. package/fesm2022/ngx-t-forms.mjs +2 -1
  100. package/fesm2022/ngx-t-forms.mjs.map +1 -1
  101. package/package.json +20 -18
  102. package/styles/_editor-mixins.scss +62 -0
  103. package/styles/_json-editor-syntax.scss +26 -0
  104. package/styles/_signature-pad.scss +26 -0
  105. package/styles/_tokens.scss +148 -0
  106. package/types/ngx-t-forms.d.ts +1767 -621
  107. package/fesm2022/ngx-t-forms-calculated-field-rules.component-D-SBMdYg.mjs +0 -313
  108. package/fesm2022/ngx-t-forms-calculated-field-rules.component-D-SBMdYg.mjs.map +0 -1
  109. package/fesm2022/ngx-t-forms-chip-options-creator-editor.component-1cpszpPN.mjs +0 -191
  110. package/fesm2022/ngx-t-forms-chip-options-creator-editor.component-1cpszpPN.mjs.map +0 -1
  111. package/fesm2022/ngx-t-forms-config-mscoa-additional-inputs.component-DFdAVWTg.mjs +0 -207
  112. package/fesm2022/ngx-t-forms-config-mscoa-additional-inputs.component-DFdAVWTg.mjs.map +0 -1
  113. package/fesm2022/ngx-t-forms-data-source-picker.component-DxORinAD.mjs +0 -204
  114. package/fesm2022/ngx-t-forms-data-source-picker.component-DxORinAD.mjs.map +0 -1
  115. package/fesm2022/ngx-t-forms-document-list-label-config-editor.component-DcWS1txl.mjs +0 -289
  116. package/fesm2022/ngx-t-forms-document-list-label-config-editor.component-DcWS1txl.mjs.map +0 -1
  117. package/fesm2022/ngx-t-forms-form-input-selector.component-B2QEnvkq.mjs +0 -134
  118. package/fesm2022/ngx-t-forms-form-input-selector.component-B2QEnvkq.mjs.map +0 -1
  119. package/fesm2022/ngx-t-forms-form-json-view.component-DePf44w6.mjs +0 -22
  120. package/fesm2022/ngx-t-forms-form-json-view.component-DePf44w6.mjs.map +0 -1
  121. package/fesm2022/ngx-t-forms-form-section-stepper.component-BTkcSjg7.mjs +0 -270
  122. package/fesm2022/ngx-t-forms-form-section-stepper.component-BTkcSjg7.mjs.map +0 -1
  123. package/fesm2022/ngx-t-forms-forms-builder-menu.component-Wamzf_sq.mjs +0 -345
  124. package/fesm2022/ngx-t-forms-forms-builder-menu.component-Wamzf_sq.mjs.map +0 -1
  125. package/fesm2022/ngx-t-forms-input-editor.component-D4xHO76K.mjs +0 -147
  126. package/fesm2022/ngx-t-forms-input-editor.component-D4xHO76K.mjs.map +0 -1
  127. package/fesm2022/ngx-t-forms-map-mat-options-keys-CbdW82su.mjs.map +0 -1
  128. package/fesm2022/ngx-t-forms-mat-chip-list-editor.component-DmTyO9Wi.mjs +0 -105
  129. package/fesm2022/ngx-t-forms-mat-chip-list-editor.component-DmTyO9Wi.mjs.map +0 -1
  130. package/fesm2022/ngx-t-forms-mat-slider-editor.component-DZ4TenrI.mjs +0 -109
  131. package/fesm2022/ngx-t-forms-mat-slider-editor.component-DZ4TenrI.mjs.map +0 -1
  132. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-DPyBYE4p.mjs +0 -155
  133. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-DPyBYE4p.mjs.map +0 -1
  134. package/fesm2022/ngx-t-forms-missing-form-configs.component-BRmnwAK6.mjs +0 -28
  135. package/fesm2022/ngx-t-forms-missing-form-configs.component-BRmnwAK6.mjs.map +0 -1
  136. package/fesm2022/ngx-t-forms-mscoa-chart-toolbar.component-D_umeAPL.mjs +0 -43
  137. package/fesm2022/ngx-t-forms-mscoa-chart-toolbar.component-D_umeAPL.mjs.map +0 -1
  138. package/fesm2022/ngx-t-forms-mscoa-error-display.component-CSX2NCNU.mjs +0 -116
  139. package/fesm2022/ngx-t-forms-mscoa-error-display.component-CSX2NCNU.mjs.map +0 -1
  140. package/fesm2022/ngx-t-forms-mscoa-segment-config.component-B6IF8kGg.mjs +0 -296
  141. package/fesm2022/ngx-t-forms-mscoa-segment-config.component-B6IF8kGg.mjs.map +0 -1
  142. package/fesm2022/ngx-t-forms-mscoa-temporary-hint.component-BPkjsRmH.mjs +0 -83
  143. package/fesm2022/ngx-t-forms-mscoa-temporary-hint.component-BPkjsRmH.mjs.map +0 -1
  144. package/fesm2022/ngx-t-forms-ngx-t-forms-D9qmig6g.mjs +0 -16844
  145. package/fesm2022/ngx-t-forms-ngx-t-forms-D9qmig6g.mjs.map +0 -1
  146. package/fesm2022/ngx-t-forms-pipeline-generator.component-DBJEyCbd.mjs +0 -613
  147. package/fesm2022/ngx-t-forms-pipeline-generator.component-DBJEyCbd.mjs.map +0 -1
  148. package/fesm2022/ngx-t-forms-record-list-manager.component-Dgs9lNSr.mjs +0 -269
  149. package/fesm2022/ngx-t-forms-record-list-manager.component-Dgs9lNSr.mjs.map +0 -1
  150. package/fesm2022/ngx-t-forms-required-inputs.component-CSIJvSHq.mjs +0 -190
  151. package/fesm2022/ngx-t-forms-required-inputs.component-CSIJvSHq.mjs.map +0 -1
  152. package/fesm2022/ngx-t-forms-rest-api-call-setup.component-CY-JSkGs.mjs +0 -291
  153. package/fesm2022/ngx-t-forms-rest-api-call-setup.component-CY-JSkGs.mjs.map +0 -1
  154. package/fesm2022/ngx-t-forms-section-report.component-12-KdKT6.mjs +0 -156
  155. package/fesm2022/ngx-t-forms-section-report.component-12-KdKT6.mjs.map +0 -1
  156. package/fesm2022/ngx-t-forms-selection-options-editor.component-Be3QAG_L.mjs +0 -186
  157. package/fesm2022/ngx-t-forms-selection-options-editor.component-Be3QAG_L.mjs.map +0 -1
  158. package/fesm2022/ngx-t-forms-t-workflow-picker.component-a4f1r8gH.mjs +0 -187
  159. package/fesm2022/ngx-t-forms-t-workflow-picker.component-a4f1r8gH.mjs.map +0 -1
  160. package/fesm2022/ngx-t-forms-validators-config.component-B3j9Dmgu.mjs +0 -215
  161. package/fesm2022/ngx-t-forms-validators-config.component-B3j9Dmgu.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngx-t-forms-file-upload-input-element.component-C7mMeEjF.mjs","sources":["../../../projects/ngx-t-forms/src/lib/components/t-form-input/elements/file-upload-input-element/core/file-uploader/file-uploader.component.ts","../../../projects/ngx-t-forms/src/lib/components/t-form-input/elements/file-upload-input-element/core/file-uploader/file-uploader.component.html","../../../projects/ngx-t-forms/src/lib/components/t-form-input/elements/file-upload-input-element/file-upload-input-element.component.ts","../../../projects/ngx-t-forms/src/lib/components/t-form-input/elements/file-upload-input-element/file-upload-input-element.component.html"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n PLATFORM_ID,\r\n computed,\r\n inject,\r\n input,\r\n viewChild,\r\n} from '@angular/core';\r\nimport { isPlatformBrowser } from '@angular/common';\r\nimport { NgControl } from '@angular/forms';\r\nimport { MatFormFieldControl } from '@angular/material/form-field';\r\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\r\nimport { MatIconModule } from '@angular/material/icon';\r\nimport { MatToolbarModule } from '@angular/material/toolbar';\r\nimport { MatButtonModule } from '@angular/material/button';\r\n\r\nimport {\r\n AllDocumentFileExtensions,\r\n AllImageFileExtensions,\r\n FileUploadInputValueInterface,\r\n IFileUploadInput,\r\n ITowerStepColumn,\r\n InputFileType,\r\n UploadTypes,\r\n} from 'ngx-t-forms-types';\r\n\r\nimport { BaseCustomInput, BaseCustomInputConfig } from '../../../../../../services/core/t-input-controller/functions/baseCustomInput';\r\n\r\nconst customInputConfig: BaseCustomInputConfig = {\r\n controlType: 'app-file-uploader',\r\n nextId: 0,\r\n};\r\n\r\n@Component({\r\n selector: 'lib-file-uploader',\r\n templateUrl: './file-uploader.component.html',\r\n styleUrls: ['./file-uploader.component.css'],\r\n imports: [\r\n MatProgressBarModule,\r\n MatIconModule,\r\n MatToolbarModule,\r\n MatButtonModule,\r\n ],\r\n host: {\r\n '[class.floating]': 'shouldLabelFloat',\r\n '[id]': 'id',\r\n },\r\n providers: [{ provide: MatFormFieldControl, useExisting: FileUploaderComponent }],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class FileUploaderComponent extends BaseCustomInput<FileUploadInputValueInterface | object | null> {\r\n readonly inputConfig = input.required<ITowerStepColumn>();\r\n\r\n protected readonly uploader = viewChild.required<ElementRef<HTMLInputElement>>('uploader');\r\n\r\n protected readonly uploadAccept = computed<string>(() => {\r\n const cfg = this.inputConfig() as IFileUploadInput | undefined;\r\n const fileType = cfg?.fileType;\r\n if (!fileType) {\r\n return InputFileType.file;\r\n }\r\n return (UploadTypes as Record<string, string>)[String(fileType)] ?? InputFileType.file;\r\n });\r\n\r\n protected readonly previewSrc = computed(() => {\r\n const cfg = this.inputConfig() as IFileUploadInput | undefined;\r\n const fileType = cfg?.fileType;\r\n switch (fileType) {\r\n case InputFileType.file:\r\n return 'https://res.cloudinary.com/afrob/image/upload/v1676973154/Flows/Icons/iPhone_14_-_1_tx1scw.png';\r\n default:\r\n return (this.value as FileUploadInputValueInterface | null)?.base64 ?? undefined;\r\n }\r\n });\r\n\r\n protected fileName: string | null = null;\r\n protected loading: number | null = null;\r\n protected dragOver = false;\r\n\r\n readonly #platformId = inject(PLATFORM_ID);\r\n\r\n constructor() {\r\n super(\r\n inject(NgControl, { optional: true, self: true }) as NgControl,\r\n inject<ElementRef<HTMLElement>>(ElementRef),\r\n customInputConfig,\r\n );\r\n }\r\n\r\n override get value(): FileUploadInputValueInterface | object | null {\r\n return this._value;\r\n }\r\n override set value(file: FileUploadInputValueInterface | object | null) {\r\n const fileType = this.inputConfig()?.fileType;\r\n if (!file || typeof file !== 'object') {\r\n this._value = file;\r\n this.stateChanges.next();\r\n return;\r\n }\r\n\r\n const { base64, fileExtension } = file as FileUploadInputValueInterface;\r\n\r\n if (!base64) {\r\n this._value = file;\r\n this.stateChanges.next();\r\n return;\r\n }\r\n\r\n const allDocExtensions = Object.values(AllDocumentFileExtensions);\r\n const allImageExtensions = Object.values(AllImageFileExtensions);\r\n const isFile = allDocExtensions.includes(fileExtension as AllDocumentFileExtensions);\r\n const isImage = allImageExtensions.includes(fileExtension as AllImageFileExtensions);\r\n if (\r\n (fileType === InputFileType.file && !isFile) ||\r\n ((fileType === InputFileType.Image || fileType === InputFileType.CaptureImage) && !isImage)\r\n ) {\r\n this._value = null;\r\n this.temporaryError(`Invalid file type. Expected ${fileType} file`);\r\n this.stateChanges.next();\r\n return;\r\n } else {\r\n this._value = file;\r\n this.stateChanges.next();\r\n return;\r\n }\r\n }\r\n\r\n override get empty(): boolean {\r\n return !this.value;\r\n }\r\n\r\n override get shouldLabelFloat(): boolean {\r\n return this.focused || !this.empty;\r\n }\r\n\r\n override onContainerClick(event: MouseEvent): void {\r\n this.uploader().nativeElement.click();\r\n if (event.target !== this._elementRef.nativeElement) {\r\n this.markAsTouched();\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n protected triggerUploader(): void {\r\n this.uploader().nativeElement.click();\r\n }\r\n\r\n protected onFileSelected(event: Event): void {\r\n const files = (event.target as HTMLInputElement).files;\r\n if (files && files.length > 0) {\r\n const file = files[0];\r\n if (!file) return;\r\n this.assignFile(file);\r\n }\r\n }\r\n\r\n protected assignFile(file: File): void {\r\n // FileReader is a browser-only API; gate by isPlatformBrowser to keep SSR-safe.\r\n if (!isPlatformBrowser(this.#platformId)) {\r\n return;\r\n }\r\n const reader = new FileReader();\r\n reader.onload = () => {\r\n this.fileName = file.name;\r\n const nameSplit = file.name.split('.');\r\n const fileExtension = nameSplit[nameSplit.length - 1];\r\n this.value = {\r\n fileName: file.name,\r\n base64: reader.result as string,\r\n type: this.inputConfig()?.fileType,\r\n blob: file,\r\n fileExtension,\r\n };\r\n this.onChange(this.value);\r\n };\r\n reader.readAsDataURL(file);\r\n }\r\n\r\n protected onDragOver(event: DragEvent): void {\r\n this.dragOver = true;\r\n event.preventDefault();\r\n event.stopPropagation();\r\n }\r\n\r\n protected onDrop(event: DragEvent): void {\r\n this.dragOver = false;\r\n event.preventDefault();\r\n event.stopPropagation();\r\n const files = event.dataTransfer?.files;\r\n if (files && files.length > 0) {\r\n const file = files[0];\r\n if (!file) return;\r\n this.assignFile(file);\r\n }\r\n }\r\n\r\n protected onDragLeave(event: DragEvent): void {\r\n this.dragOver = false;\r\n event.preventDefault();\r\n event.stopPropagation();\r\n }\r\n\r\n protected clearFile(event: MouseEvent): void {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n this.value = null;\r\n this.stateChanges.next();\r\n }\r\n}\r\n","@if (loading) {\r\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\r\n}\r\n\r\n@if (!shouldLabelFloat) {\r\n <section>\r\n <br>\r\n <div\r\n [style.border-color]=\"dragOver ? ' var(--mat-stepper-header-selected-state-icon-background-color)' : '#ccc;'\"\r\n (dragover)=\"onDragOver($event)\"\r\n (drop)=\"onDrop($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n class=\"drop-zone\">\r\n <div>\r\n <mat-icon>\r\n cloud_upload\r\n </mat-icon>\r\n </div>\r\n <div>\r\n @if (!!errorMessage) {\r\n <span class=\"mat-mdc-form-field-error\">\r\n {{ errorMessage }}\r\n </span>\r\n } @else {\r\n Drag and drop files or click to upload\r\n }\r\n </div>\r\n </div>\r\n </section>\r\n}\r\n\r\n<input\r\n style=\"display: none\"\r\n hidden\r\n [readonly]=\"!!inputConfig().readonly\"\r\n [type]=\"inputConfig().type || 'file'\"\r\n [accept]=\"uploadAccept()\"\r\n (change)=\"onFileSelected($event)\"\r\n #uploader\r\n/>\r\n\r\n@if (!!shouldLabelFloat) {\r\n <div\r\n [style.border-color]=\"dragOver ? 'var(--mat-stepper-header-selected-state-icon-background-color)' : '#ccc;'\"\r\n class=\"drop-zone\"\r\n (dragover)=\"onDragOver($event)\"\r\n (drop)=\"onDrop($event)\"\r\n (dragleave)=\"onDragLeave($event)\">\r\n <img style=\"width: 100%;\" [src]=\"previewSrc()\" alt=\"\">\r\n <mat-toolbar style=\" height: min-content !important;\">\r\n <mat-icon\r\n [style.color]=\"dragOver ? 'var(--mat-stepper-header-selected-state-icon-background-color)' : ''\">\r\n cloud_upload\r\n </mat-icon>\r\n <span class=\"spacer\"></span>\r\n <div style=\"padding: 8px;\">\r\n @if (!!fileName) {\r\n <div style=\"\r\n line-height: normal;width: 100%;\r\n white-space:normal;overflow: hidden;text-overflow: ellipsis;\r\n font-size: 0.75em;color:var(--mat-stepper-header-selected-state-icon-background-color);\">\r\n {{ fileName }}\r\n </div>\r\n }\r\n <div style=\"font-size: 0.875em;\">\r\n Drag and drop files or click to upload\r\n </div>\r\n </div>\r\n\r\n <span class=\"spacer\"></span>\r\n <button (click)=\"clearFile($event)\" mat-icon-button>\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </mat-toolbar>\r\n </div>\r\n}\r\n","import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\r\nimport { FormGroup, ReactiveFormsModule, ValidationErrors } from '@angular/forms';\r\nimport { MatFormFieldModule } from '@angular/material/form-field';\r\nimport { MatIconModule } from '@angular/material/icon';\r\nimport { MatButtonModule } from '@angular/material/button';\r\nimport { MatTooltipModule } from '@angular/material/tooltip';\r\nimport type { ITowerStepColumn } from 'ngx-t-forms-types';\r\nimport { TFormInputStatusComponent } from '../../../t-form-input-status/t-form-input-status.component';\r\nimport { FileUploaderComponent } from './core/file-uploader/file-uploader.component';\r\nimport { getInputErrorMessage } from '../../../../services/core/t-input-controller/functions/inputErrorMessage';\r\n\r\n/** Wraps a reactive file-uploader control inside a `mat-form-field`. */\r\n@Component({\r\n selector: 'lib-file-upload-input-element',\r\n templateUrl: './file-upload-input-element.component.html',\r\n styleUrl: './file-upload-input-element.component.css',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [\r\n ReactiveFormsModule,\r\n MatFormFieldModule,\r\n MatIconModule,\r\n MatButtonModule,\r\n MatTooltipModule,\r\n FileUploaderComponent,\r\n TFormInputStatusComponent,\r\n ],\r\n})\r\nexport class FileUploadInputElementComponent {\r\n readonly inputConfig = input.required<ITowerStepColumn>();\r\n readonly formGroup = input.required<FormGroup>();\r\n readonly reload = output<void>();\r\n\r\n protected readonly errorMessage = computed(() =>\r\n getInputErrorMessage(this.inputConfig(), this.formGroup()),\r\n );\r\n\r\n protected getErrors(): ValidationErrors | null | undefined {\r\n return this.formGroup().controls[this.inputConfig().id]?.errors;\r\n }\r\n}\r\n","<form [formGroup]=\"formGroup()\">\r\n @if (inputConfig(); as inputConfig) {\r\n <mat-form-field [appearance]=\"inputConfig.appearance || 'fill'\" subscriptSizing=\"dynamic\">\r\n <mat-label>\r\n {{ inputConfig.label }}\r\n <lib-t-form-input-status [inputConfig]=\"inputConfig\"></lib-t-form-input-status>\r\n </mat-label>\r\n\r\n <lib-file-uploader\r\n [inputConfig]=\"inputConfig\"\r\n [formControlName]=\"inputConfig.id\"\r\n [required]=\"inputConfig.required\"\r\n ></lib-file-uploader>\r\n\r\n @if (inputConfig.hintLabel || inputConfig.temporaryHint) {\r\n <mat-hint class=\"inputHint\">\r\n {{ inputConfig.temporaryHint || inputConfig.hintLabel }}\r\n </mat-hint>\r\n }\r\n\r\n @if (!!errorMessage()) {\r\n <mat-error class=\"oneLineTextEllipsis\" matTooltipClass=\"errorToolTip\">{{ errorMessage() }}</mat-error>\r\n }\r\n\r\n @if (inputConfig.prefixIcon) {\r\n <mat-icon matPrefix>{{ inputConfig.prefixIcon }}</mat-icon>\r\n }\r\n\r\n @if (inputConfig.canReload?.canReload) {\r\n <button\r\n mat-icon-button\r\n style=\"margin-right: 4px;\"\r\n matTooltip=\"Click to refresh this field.\"\r\n class=\"input-sync-button\"\r\n (click)=\"inputConfig.canReload.emit()\"\r\n matSuffix\r\n >\r\n <mat-icon style=\"color: var(--mat-sys-primary)\">sync</mat-icon>\r\n </button>\r\n }\r\n\r\n @if (inputConfig.suffixIcon && inputConfig.id !== 'password') {\r\n <mat-icon matSuffix>{{ inputConfig.suffixIcon }}</mat-icon>\r\n }\r\n\r\n @if (inputConfig.prefixText) {\r\n <span matPrefix style=\"top: 0\">{{ inputConfig.prefixText }}</span>\r\n }\r\n @if (inputConfig.suffixText) {\r\n <span matSuffix style=\"padding-left: 5px\">{{ inputConfig.suffixText }}</span>\r\n }\r\n\r\n @if (inputConfig.maxLength && formGroup()) {\r\n <mat-hint align=\"end\">\r\n {{ (formGroup().controls[inputConfig.id]?.value?.length || 0) + '/' + inputConfig.maxLength }}\r\n </mat-hint>\r\n }\r\n </mat-form-field>\r\n }\r\n</form>\r\n"],"names":["i2","i3","i4","i5"],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import * as i3 from '@angular/material/icon';
4
+ import { MatIconModule } from '@angular/material/icon';
5
+ import * as i4 from '@angular/material/list';
6
+ import { MatListModule } from '@angular/material/list';
7
+ import * as i7 from '@angular/material/core';
8
+ import { MatRippleModule } from '@angular/material/core';
9
+ import { g as getInputIcon } from './ngx-t-forms-getInputIcon-B4ADgevZ.mjs';
10
+ import { E as EmptyStateComponent } from './ngx-t-forms-ngx-t-forms-u_kigDid.mjs';
11
+
12
+ /**
13
+ * Single-select list that lets a user pick one form input by id. Child of
14
+ * `t-dynamic-data-edit`, which supplies the field label, hint, and validation
15
+ * errors — so this component owns only the selectable list content.
16
+ *
17
+ * Selecting an already-selected input deselects it (emits `undefined`). While a
18
+ * value is set the list collapses to the selected-only view.
19
+ */
20
+ class FormInputSelectorComponent {
21
+ constructor() {
22
+ /**
23
+ * Validation errors passed down by the parent editor. Retained on the contract
24
+ * (the parent binds `[errors]`) but not rendered here — `t-dynamic-data-edit`
25
+ * owns error display, so rendering them again would double them.
26
+ */
27
+ this.errors = input([], ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
28
+ /** Currently selected input id, or `undefined` when nothing is selected. */
29
+ this.value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
30
+ /** Available form inputs to choose from. */
31
+ this.formInputs = input([], ...(ngDevMode ? [{ debugName: "formInputs" }] : /* istanbul ignore next */ []));
32
+ /** Disable user interaction. */
33
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
34
+ /** Optional id attribute supplied by the parent editor. */
35
+ this.id = input(undefined, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
36
+ /** Emits the chosen input id, or `undefined` when the selection is cleared. */
37
+ this.change = output();
38
+ /**
39
+ * The list to render: the selected-only view once a value is set, otherwise
40
+ * every available input. Each entry carries its derived icon, sub-item flag,
41
+ * and accessible label.
42
+ */
43
+ this.options = computed(() => {
44
+ const value = this.value();
45
+ const inputs = this.formInputs();
46
+ const selected = value === undefined ? undefined : inputs.find((item) => item.id === value);
47
+ const source = selected ? [selected] : inputs;
48
+ return source.map((item) => this.#toOption(item, item.id === value));
49
+ }, ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
50
+ }
51
+ /** Toggle selection: re-selecting the current value clears it. */
52
+ select(id) {
53
+ if (this.disabled()) {
54
+ return;
55
+ }
56
+ this.change.emit(this.value() === id ? undefined : id);
57
+ }
58
+ #toOption(item, selected) {
59
+ const isSubItem = Boolean(item.multipleInputInEditId);
60
+ const dataType = item.dataType ?? '';
61
+ const kind = isSubItem ? 'Sub-item input' : 'Primary input';
62
+ const dataTypeLabel = dataType ? `, ${dataType}` : '';
63
+ return {
64
+ input: item,
65
+ id: item.id,
66
+ label: item.label,
67
+ icon: getInputIcon(item.element),
68
+ dataType,
69
+ isSubItem,
70
+ selected,
71
+ ariaLabel: `${item.label}. ${kind}${dataTypeLabel}.${selected ? ' Selected.' : ''}`,
72
+ };
73
+ }
74
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: FormInputSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
75
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: FormInputSelectorComponent, isStandalone: true, selector: "lib-form-input-selector", inputs: { errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, formInputs: { classPropertyName: "formInputs", publicName: "formInputs", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { change: "change" }, host: { properties: { "attr.id": "id()" }, classAttribute: "lib-form-input-selector" }, ngImport: i0, template: "@if (options().length) {\n<mat-nav-list class=\"lib-form-input-selector__list\" role=\"listbox\" aria-label=\"Form input selector\">\n @for (option of options(); track option.id) {\n <mat-list-item\n class=\"lib-form-input-selector__item\"\n matRipple\n [disabled]=\"disabled()\"\n [activated]=\"option.selected\"\n [attr.aria-selected]=\"option.selected\"\n [attr.aria-label]=\"option.ariaLabel\"\n (click)=\"select(option.id)\">\n <mat-icon matListItemAvatar class=\"lib-form-input-selector__avatar\" aria-hidden=\"true\">\n {{ option.icon }}\n </mat-icon>\n <span matListItemTitle class=\"lib-form-input-selector__title\">{{ option.label }}</span>\n <span matListItemLine class=\"lib-form-input-selector__meta-text\">\n @if (option.isSubItem) {\n <span>Sub-item</span>\n }\n @if (option.dataType) {\n <span>{{ option.dataType }}</span>\n }\n @if (option.selected) {\n <span class=\"lib-form-input-selector__selected-hint\">Selected</span>\n }\n </span>\n <mat-icon\n matListItemMeta\n class=\"lib-form-input-selector__indicator\"\n [class.lib-form-input-selector__indicator--on]=\"option.selected\"\n aria-hidden=\"true\">\n {{ option.selected ? 'check_circle' : 'radio_button_unchecked' }}\n </mat-icon>\n </mat-list-item>\n }\n</mat-nav-list>\n} @else {\n<lib-empty-state icon=\"search_off\" message=\"No form inputs found\" />\n}\n", styles: [":host{display:block;color:var(--lib-forms-on-surface)}.lib-form-input-selector__list{width:100%;box-sizing:border-box;max-height:65vh;overflow:auto;padding:.5rem;background:var(--lib-forms-surface);border:1px solid color-mix(in srgb,var(--lib-forms-outline) 12%,transparent);border-radius:var(--lib-forms-radius-lg, 12px)}.lib-form-input-selector__item{min-height:3rem;height:fit-content;margin-bottom:.25rem;border-radius:var(--lib-forms-radius-sm, 8px);cursor:pointer;transition:background-color var(--lib-forms-duration-hover, .3s) var(--lib-forms-easing)}.lib-form-input-selector__item:last-child{margin-bottom:0}.lib-form-input-selector__item:hover{background:color-mix(in srgb,var(--lib-forms-primary) 6%,transparent)}.lib-form-input-selector__item:focus-visible{outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--lib-forms-primary) 25%,transparent)}.lib-form-input-selector__item.mdc-list-item--activated{background:color-mix(in srgb,var(--lib-forms-primary) 12%,transparent)}mat-icon.lib-form-input-selector__avatar{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border-radius:var(--lib-forms-radius-sm, 8px);font-size:1.5rem;line-height:1;background:var(--lib-forms-surface-container-high);color:var(--lib-forms-on-surface-variant)}.lib-form-input-selector__title{font-size:.9375rem;font-weight:600}.lib-form-input-selector__meta-text{display:flex;flex-wrap:wrap;gap:.5rem;font-size:.75rem;color:var(--lib-forms-on-surface-variant)}.lib-form-input-selector__selected-hint{font-size:.625rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--lib-forms-primary)}.lib-form-input-selector__indicator{color:var(--lib-forms-on-surface-variant)}.lib-form-input-selector__indicator--on{color:var(--lib-forms-primary)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i4.MatNavList, selector: "mat-nav-list", exportAs: ["matNavList"] }, { kind: "component", type: i4.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i4.MatListItemAvatar, selector: "[matListItemAvatar]" }, { kind: "directive", type: i4.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i4.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "directive", type: i4.MatListItemMeta, selector: "[matListItemMeta]" }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i7.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "component", type: EmptyStateComponent, selector: "lib-empty-state", inputs: ["icon", "message"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
76
+ }
77
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: FormInputSelectorComponent, decorators: [{
78
+ type: Component,
79
+ args: [{ selector: 'lib-form-input-selector', imports: [MatIconModule, MatListModule, MatRippleModule, EmptyStateComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
80
+ 'class': 'lib-form-input-selector',
81
+ '[attr.id]': 'id()',
82
+ }, template: "@if (options().length) {\n<mat-nav-list class=\"lib-form-input-selector__list\" role=\"listbox\" aria-label=\"Form input selector\">\n @for (option of options(); track option.id) {\n <mat-list-item\n class=\"lib-form-input-selector__item\"\n matRipple\n [disabled]=\"disabled()\"\n [activated]=\"option.selected\"\n [attr.aria-selected]=\"option.selected\"\n [attr.aria-label]=\"option.ariaLabel\"\n (click)=\"select(option.id)\">\n <mat-icon matListItemAvatar class=\"lib-form-input-selector__avatar\" aria-hidden=\"true\">\n {{ option.icon }}\n </mat-icon>\n <span matListItemTitle class=\"lib-form-input-selector__title\">{{ option.label }}</span>\n <span matListItemLine class=\"lib-form-input-selector__meta-text\">\n @if (option.isSubItem) {\n <span>Sub-item</span>\n }\n @if (option.dataType) {\n <span>{{ option.dataType }}</span>\n }\n @if (option.selected) {\n <span class=\"lib-form-input-selector__selected-hint\">Selected</span>\n }\n </span>\n <mat-icon\n matListItemMeta\n class=\"lib-form-input-selector__indicator\"\n [class.lib-form-input-selector__indicator--on]=\"option.selected\"\n aria-hidden=\"true\">\n {{ option.selected ? 'check_circle' : 'radio_button_unchecked' }}\n </mat-icon>\n </mat-list-item>\n }\n</mat-nav-list>\n} @else {\n<lib-empty-state icon=\"search_off\" message=\"No form inputs found\" />\n}\n", styles: [":host{display:block;color:var(--lib-forms-on-surface)}.lib-form-input-selector__list{width:100%;box-sizing:border-box;max-height:65vh;overflow:auto;padding:.5rem;background:var(--lib-forms-surface);border:1px solid color-mix(in srgb,var(--lib-forms-outline) 12%,transparent);border-radius:var(--lib-forms-radius-lg, 12px)}.lib-form-input-selector__item{min-height:3rem;height:fit-content;margin-bottom:.25rem;border-radius:var(--lib-forms-radius-sm, 8px);cursor:pointer;transition:background-color var(--lib-forms-duration-hover, .3s) var(--lib-forms-easing)}.lib-form-input-selector__item:last-child{margin-bottom:0}.lib-form-input-selector__item:hover{background:color-mix(in srgb,var(--lib-forms-primary) 6%,transparent)}.lib-form-input-selector__item:focus-visible{outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--lib-forms-primary) 25%,transparent)}.lib-form-input-selector__item.mdc-list-item--activated{background:color-mix(in srgb,var(--lib-forms-primary) 12%,transparent)}mat-icon.lib-form-input-selector__avatar{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;border-radius:var(--lib-forms-radius-sm, 8px);font-size:1.5rem;line-height:1;background:var(--lib-forms-surface-container-high);color:var(--lib-forms-on-surface-variant)}.lib-form-input-selector__title{font-size:.9375rem;font-weight:600}.lib-form-input-selector__meta-text{display:flex;flex-wrap:wrap;gap:.5rem;font-size:.75rem;color:var(--lib-forms-on-surface-variant)}.lib-form-input-selector__selected-hint{font-size:.625rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--lib-forms-primary)}.lib-form-input-selector__indicator{color:var(--lib-forms-on-surface-variant)}.lib-form-input-selector__indicator--on{color:var(--lib-forms-primary)}\n"] }]
83
+ }], propDecorators: { errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], formInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "formInputs", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], change: [{ type: i0.Output, args: ["change"] }] } });
84
+
85
+ export { FormInputSelectorComponent };
86
+ //# sourceMappingURL=ngx-t-forms-form-input-selector.component-C9u8zq9B.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngx-t-forms-form-input-selector.component-C9u8zq9B.mjs","sources":["../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/form-input-selector/form-input-selector.component.ts","../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/form-input-selector/form-input-selector.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n output,\n} from '@angular/core';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatListModule } from '@angular/material/list';\nimport { MatRippleModule } from '@angular/material/core';\nimport type { FormColumnInputs } from 'ngx-t-forms-types';\nimport { getInputIcon } from '../../../../shared/functions/getInputIcon';\nimport type { IConfigElementError } from '../../t-dynamic-data-edit.component';\nimport { EmptyStateComponent } from '../_shared/empty-state/empty-state.component';\n\n/** A form input decorated with the derived view-model fields the list renders. */\ninterface FormInputOption {\n readonly input: FormColumnInputs;\n readonly id: string;\n readonly label: string;\n readonly icon: string;\n readonly dataType: string;\n readonly isSubItem: boolean;\n readonly selected: boolean;\n readonly ariaLabel: string;\n}\n\n/**\n * Single-select list that lets a user pick one form input by id. Child of\n * `t-dynamic-data-edit`, which supplies the field label, hint, and validation\n * errors — so this component owns only the selectable list content.\n *\n * Selecting an already-selected input deselects it (emits `undefined`). While a\n * value is set the list collapses to the selected-only view.\n */\n@Component({\n selector: 'lib-form-input-selector',\n imports: [MatIconModule, MatListModule, MatRippleModule, EmptyStateComponent],\n templateUrl: './form-input-selector.component.html',\n styleUrl: './form-input-selector.component.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n 'class': 'lib-form-input-selector',\n '[attr.id]': 'id()',\n },\n})\nexport class FormInputSelectorComponent {\n /**\n * Validation errors passed down by the parent editor. Retained on the contract\n * (the parent binds `[errors]`) but not rendered here — `t-dynamic-data-edit`\n * owns error display, so rendering them again would double them.\n */\n readonly errors = input<IConfigElementError[] | undefined>([]);\n\n /** Currently selected input id, or `undefined` when nothing is selected. */\n readonly value = input<string | undefined>(undefined);\n\n /** Available form inputs to choose from. */\n readonly formInputs = input<readonly FormColumnInputs[]>([]);\n\n /** Disable user interaction. */\n readonly disabled = input<boolean>(false);\n\n /** Optional id attribute supplied by the parent editor. */\n readonly id = input<string | undefined>(undefined);\n\n /** Emits the chosen input id, or `undefined` when the selection is cleared. */\n readonly change = output<string | undefined>();\n\n /**\n * The list to render: the selected-only view once a value is set, otherwise\n * every available input. Each entry carries its derived icon, sub-item flag,\n * and accessible label.\n */\n protected readonly options = computed<readonly FormInputOption[]>(() => {\n const value = this.value();\n const inputs = this.formInputs();\n const selected = value === undefined ? undefined : inputs.find((item) => item.id === value);\n const source = selected ? [selected] : inputs;\n return source.map((item) => this.#toOption(item, item.id === value));\n });\n\n /** Toggle selection: re-selecting the current value clears it. */\n protected select(id: string): void {\n if (this.disabled()) {\n return;\n }\n this.change.emit(this.value() === id ? undefined : id);\n }\n\n #toOption(item: FormColumnInputs, selected: boolean): FormInputOption {\n const isSubItem = Boolean(item.multipleInputInEditId);\n const dataType = item.dataType ?? '';\n const kind = isSubItem ? 'Sub-item input' : 'Primary input';\n const dataTypeLabel = dataType ? `, ${dataType}` : '';\n return {\n input: item,\n id: item.id,\n label: item.label,\n icon: getInputIcon(item.element),\n dataType,\n isSubItem,\n selected,\n ariaLabel: `${item.label}. ${kind}${dataTypeLabel}.${selected ? ' Selected.' : ''}`,\n };\n }\n}\n","@if (options().length) {\n<mat-nav-list class=\"lib-form-input-selector__list\" role=\"listbox\" aria-label=\"Form input selector\">\n @for (option of options(); track option.id) {\n <mat-list-item\n class=\"lib-form-input-selector__item\"\n matRipple\n [disabled]=\"disabled()\"\n [activated]=\"option.selected\"\n [attr.aria-selected]=\"option.selected\"\n [attr.aria-label]=\"option.ariaLabel\"\n (click)=\"select(option.id)\">\n <mat-icon matListItemAvatar class=\"lib-form-input-selector__avatar\" aria-hidden=\"true\">\n {{ option.icon }}\n </mat-icon>\n <span matListItemTitle class=\"lib-form-input-selector__title\">{{ option.label }}</span>\n <span matListItemLine class=\"lib-form-input-selector__meta-text\">\n @if (option.isSubItem) {\n <span>Sub-item</span>\n }\n @if (option.dataType) {\n <span>{{ option.dataType }}</span>\n }\n @if (option.selected) {\n <span class=\"lib-form-input-selector__selected-hint\">Selected</span>\n }\n </span>\n <mat-icon\n matListItemMeta\n class=\"lib-form-input-selector__indicator\"\n [class.lib-form-input-selector__indicator--on]=\"option.selected\"\n aria-hidden=\"true\">\n {{ option.selected ? 'check_circle' : 'radio_button_unchecked' }}\n </mat-icon>\n </mat-list-item>\n }\n</mat-nav-list>\n} @else {\n<lib-empty-state icon=\"search_off\" message=\"No form inputs found\" />\n}\n"],"names":["i2","i3"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { AsyncPipe } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { F as FormsStoreService, b as TDynamicDataViewComponent } from './ngx-t-forms-ngx-t-forms-u_kigDid.mjs';
5
+ import { map } from 'rxjs';
6
+
7
+ class FormJsonViewComponent {
8
+ constructor() {
9
+ this.#store = inject(FormsStoreService);
10
+ this.form$ = this.#store.selectors.selectFormInEdit$.pipe(map((form) => form?.form));
11
+ }
12
+ #store;
13
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: FormJsonViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
14
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.12", type: FormJsonViewComponent, isStandalone: true, selector: "lib-form-json-view", ngImport: i0, template: "\r\n<lib-t-dynamic-data-view [data]=\"form$|async\"></lib-t-dynamic-data-view>", styles: ["pre{margin:0;border:1px solid var(--lib-forms-outline-variant);background-color:var(--lib-forms-on-surface);padding:10px}code{color:#b0e0e6;font-size:.875em}.token.punctuation{color:#999}.token.property{color:#905}.token.string{color:var(--lib-forms-code-string)}.token.number{color:var(--lib-forms-code-number)}.token.boolean{color:var(--lib-forms-code-boolean)}.token.keyword{color:#07a}\n"], dependencies: [{ kind: "component", type: TDynamicDataViewComponent, selector: "lib-t-dynamic-data-view", inputs: ["data"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
15
+ }
16
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: FormJsonViewComponent, decorators: [{
17
+ type: Component,
18
+ args: [{ selector: 'lib-form-json-view', imports: [AsyncPipe, TDynamicDataViewComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "\r\n<lib-t-dynamic-data-view [data]=\"form$|async\"></lib-t-dynamic-data-view>", styles: ["pre{margin:0;border:1px solid var(--lib-forms-outline-variant);background-color:var(--lib-forms-on-surface);padding:10px}code{color:#b0e0e6;font-size:.875em}.token.punctuation{color:#999}.token.property{color:#905}.token.string{color:var(--lib-forms-code-string)}.token.number{color:var(--lib-forms-code-number)}.token.boolean{color:var(--lib-forms-code-boolean)}.token.keyword{color:#07a}\n"] }]
19
+ }] });
20
+
21
+ export { FormJsonViewComponent };
22
+ //# sourceMappingURL=ngx-t-forms-form-json-view.component-856Hx1Bg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngx-t-forms-form-json-view.component-856Hx1Bg.mjs","sources":["../../../projects/ngx-t-forms/src/lib/components/form-builder/elements/form-json-view/form-json-view.component.ts","../../../projects/ngx-t-forms/src/lib/components/form-builder/elements/form-json-view/form-json-view.component.html"],"sourcesContent":["import { AsyncPipe } from '@angular/common';\r\nimport { ChangeDetectionStrategy, Component, inject } from '@angular/core';\r\n\r\nimport { FormsStoreService } from '../../../forms/store/forms-store.service';\r\nimport { TDynamicDataViewComponent } from '../../../t-dynamic-data-view/t-dynamic-data-view.component';\r\n\r\nimport { map } from 'rxjs';\r\n\r\n@Component({\r\n selector: 'lib-form-json-view',\r\n imports: [AsyncPipe, TDynamicDataViewComponent],\r\n templateUrl: './form-json-view.component.html',\r\n styleUrl: './form-json-view.component.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class FormJsonViewComponent {\r\n readonly #store = inject(FormsStoreService);\r\n\r\n protected readonly form$ = this.#store.selectors.selectFormInEdit$.pipe(\r\n map((form) => form?.form),\r\n );\r\n}\r\n","\r\n<lib-t-dynamic-data-view [data]=\"form$|async\"></lib-t-dynamic-data-view>"],"names":[],"mappings":";;;;;;MAea,qBAAqB,CAAA;AAPlC,IAAA,WAAA,GAAA;AAQW,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC;QAExB,IAAA,CAAA,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,iBAAiB,CAAC,IAAI,CACrE,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,IAAI,CAAC,CAC1B;AACF,IAAA;AALU,IAAA,MAAM;+GADJ,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAArB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECflC,gFACwE,EAAA,MAAA,EAAA,CAAA,yYAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDSjD,yBAAyB,iFAApC,SAAS,EAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FAKR,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBAPjC,SAAS;+BACE,oBAAoB,EAAA,OAAA,EACrB,CAAC,SAAS,EAAE,yBAAyB,CAAC,EAAA,eAAA,EAG9B,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,gFAAA,EAAA,MAAA,EAAA,CAAA,yYAAA,CAAA,EAAA;;;;;"}
@@ -0,0 +1,179 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, signal, computed, effect, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { JsonPipe } from '@angular/common';
4
+ import { MatButtonModule } from '@angular/material/button';
5
+ import * as i2 from '@angular/material/button-toggle';
6
+ import { MatButtonToggleModule } from '@angular/material/button-toggle';
7
+ import * as i3 from '@angular/material/icon';
8
+ import { MatIconModule } from '@angular/material/icon';
9
+ import * as i5 from '@angular/material/tooltip';
10
+ import { MatTooltipModule } from '@angular/material/tooltip';
11
+
12
+ /**
13
+ * Parser/serialiser for the `projectFormData` submission expression.
14
+ *
15
+ * At runtime the expression is consumed by `destructureObject({ $formValue: form }, expr)`,
16
+ * so the only token available is `$formValue` — the entire submitted form. The
17
+ * sole authoring choice is therefore whether to send the form fields flat or to
18
+ * nest the whole payload under one or more named keys. This module bridges that
19
+ * raw string and the guided builder's structured model.
20
+ */
21
+ /** Token that resolves to the entire submitted form payload at runtime. */
22
+ const FORM_VALUE_TOKEN = '$formValue';
23
+ /** Default envelope: the whole form nested under a `data` key. */
24
+ const DEFAULT_PROJECTION = `data:{${FORM_VALUE_TOKEN}}`;
25
+ /** Matches a single `alias:{token}` envelope entry. */
26
+ const ENVELOPE = /^([^:]+):\{(.+)\}$/;
27
+ /**
28
+ * Parses a `projectFormData` expression into the guided model (best-effort).
29
+ * Unrecognised entries are kept as literal keys so nothing is silently dropped.
30
+ */
31
+ function parseProjection(expression) {
32
+ if (!expression)
33
+ return { keys: [] };
34
+ const keys = expression
35
+ .split(',')
36
+ .map((entry) => entry.trim())
37
+ .filter(Boolean)
38
+ .map((entry) => {
39
+ const match = ENVELOPE.exec(entry);
40
+ // `alias:{$formValue}` ⇒ alias; a bare token (or any other entry) ⇒ literal key.
41
+ return (match?.[1] ?? entry).trim();
42
+ })
43
+ .filter(Boolean);
44
+ return { keys };
45
+ }
46
+ /** Serialises the guided model back to a `projectFormData` expression. */
47
+ function serializeProjection(model) {
48
+ return model.keys
49
+ .map((key) => key.trim())
50
+ .filter(Boolean)
51
+ .map((key) => `${key}:{${FORM_VALUE_TOKEN}}`)
52
+ .join(', ');
53
+ }
54
+
55
+ /**
56
+ * Guided editor for a form's `projectFormData` submission expression.
57
+ *
58
+ * Replaces the raw `data:{$formValue}` text field with a no-code chooser: send
59
+ * the form fields flat, or wrap the whole payload under one or more named keys —
60
+ * with a live preview of the resulting JSON shape. The runtime engine only
61
+ * exposes the whole form (`$formValue`), so the model is intentionally a simple
62
+ * list of envelope keys (see {@link parseProjection}).
63
+ *
64
+ * @example
65
+ * <lib-form-payload-projection [value]="expr()" [formInputs]="inputs()"
66
+ * (valueChanged)="onExpr($event)" />
67
+ */
68
+ class FormPayloadProjectionComponent {
69
+ constructor() {
70
+ /** Current `projectFormData` expression. */
71
+ this.value = input('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
72
+ /** Whether the editor is read-only. */
73
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
74
+ /** The form's inputs — used to sketch the payload shape in the preview. */
75
+ this.formInputs = input([], ...(ngDevMode ? [{ debugName: "formInputs" }] : /* istanbul ignore next */ []));
76
+ /** Validation errors surfaced by the parent for display. */
77
+ this.errors = input([], ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
78
+ /** Emits the serialised expression whenever the user changes the mapping. */
79
+ this.valueChanged = output();
80
+ /** Envelope keys the whole form is nested under. Empty ⇒ sent flat. */
81
+ this.keys = signal([], ...(ngDevMode ? [{ debugName: "keys" }] : /* istanbul ignore next */ []));
82
+ /** 'flat' = top-level fields; 'wrap' = nested under key(s). */
83
+ this.mode = computed(() => (this.keys().length > 0 ? 'wrap' : 'flat'), ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
84
+ /**
85
+ * The last expression we synced from / emitted. Guards the input bridge so our
86
+ * own round-tripped emissions don't clobber in-progress edits (the parent
87
+ * debounces and echoes the value back through `value`).
88
+ */
89
+ this.#lastSynced = undefined;
90
+ this.valueBridge = effect(() => {
91
+ const incoming = this.value() ?? '';
92
+ if (incoming === this.#lastSynced)
93
+ return;
94
+ this.#lastSynced = incoming;
95
+ this.keys.set([...parseProjection(incoming).keys]);
96
+ }, ...(ngDevMode ? [{ debugName: "valueBridge" }] : /* istanbul ignore next */ []));
97
+ /** A sample of the form payload (field name → placeholder) for the preview. */
98
+ this.#sampleForm = computed(() => {
99
+ const sample = {};
100
+ for (const column of this.formInputs()) {
101
+ if (column.multipleInputInEditId || !column.formControlName)
102
+ continue;
103
+ sample[column.formControlName] = '…';
104
+ if (Object.keys(sample).length >= 8)
105
+ break;
106
+ }
107
+ if (Object.keys(sample).length === 0)
108
+ sample['field'] = '…';
109
+ return sample;
110
+ }, ...(ngDevMode ? [{ debugName: "#sampleForm" }] : /* istanbul ignore next */ []));
111
+ /** Live preview of the JSON shape sent to the endpoint. */
112
+ this.preview = computed(() => {
113
+ const keys = this.keys().map((k) => k.trim()).filter(Boolean);
114
+ const sample = this.#sampleForm();
115
+ if (keys.length === 0)
116
+ return sample;
117
+ const out = {};
118
+ for (const key of keys)
119
+ out[key] = sample;
120
+ return out;
121
+ }, ...(ngDevMode ? [{ debugName: "preview" }] : /* istanbul ignore next */ []));
122
+ }
123
+ /**
124
+ * The last expression we synced from / emitted. Guards the input bridge so our
125
+ * own round-tripped emissions don't clobber in-progress edits (the parent
126
+ * debounces and echoes the value back through `value`).
127
+ */
128
+ #lastSynced;
129
+ /** A sample of the form payload (field name → placeholder) for the preview. */
130
+ #sampleForm;
131
+ /** Switches between flat and wrapped modes (seeding a default key on first wrap). */
132
+ setMode(mode) {
133
+ if (mode === 'flat') {
134
+ this.#apply([]);
135
+ return;
136
+ }
137
+ if (this.keys().length === 0)
138
+ this.#apply(['data']);
139
+ }
140
+ addKey() {
141
+ this.#apply([...this.keys(), '']);
142
+ }
143
+ updateKey(index, key) {
144
+ this.#apply(this.keys().map((k, i) => (i === index ? key : k)));
145
+ }
146
+ removeKey(index) {
147
+ this.#apply(this.keys().filter((_, i) => i !== index));
148
+ }
149
+ /** Reads a native input's value from a DOM event (keeps the template `any`-free). */
150
+ inputValue(event) {
151
+ return event.target.value;
152
+ }
153
+ trackByIndex(index) {
154
+ return index;
155
+ }
156
+ /** Reset to the recommended default (`data:{$formValue}`). */
157
+ resetToDefault() {
158
+ this.#apply([...parseProjection(DEFAULT_PROJECTION).keys]);
159
+ }
160
+ #apply(keys) {
161
+ if (this.disabled())
162
+ return;
163
+ this.keys.set(keys);
164
+ const serialized = serializeProjection({ keys });
165
+ // Pre-record so the echoed value (see valueBridge) is ignored, preserving
166
+ // empty in-progress key rows that serialise away.
167
+ this.#lastSynced = serialized;
168
+ this.valueChanged.emit(serialized);
169
+ }
170
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: FormPayloadProjectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
171
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: FormPayloadProjectionComponent, isStandalone: true, selector: "lib-form-payload-projection", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, formInputs: { classPropertyName: "formInputs", publicName: "formInputs", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChanged: "valueChanged" }, host: { classAttribute: "lib-form-payload-projection" }, ngImport: i0, template: "<div class=\"projection\">\n <p class=\"hint-line\">\n <mat-icon class=\"info-icon\">help_outline</mat-icon>\n <span>Choose how the submitted form is shaped before it is sent to the endpoint.</span>\n </p>\n\n <mat-button-toggle-group class=\"mode-toggle\" [value]=\"mode()\" hideSingleSelectionIndicator\n [disabled]=\"disabled()\" (change)=\"setMode($event.value)\" aria-label=\"Payload shape\">\n <mat-button-toggle value=\"flat\" matTooltip=\"Send each field at the top level\">\n <mat-icon>list</mat-icon> Top level\n </mat-button-toggle>\n <mat-button-toggle value=\"wrap\" matTooltip=\"Nest the whole form under a key\">\n <mat-icon>data_object</mat-icon> Wrap under key\n </mat-button-toggle>\n </mat-button-toggle-group>\n\n @if (mode() === 'wrap') {\n <div class=\"keys\">\n <span class=\"field-label\">Wrap the form under</span>\n @for (key of keys(); track trackByIndex($index); let i = $index) {\n <div class=\"key-row\">\n <span class=\"key-prefix\" aria-hidden=\"true\">&#123;</span>\n <input class=\"key-input\" [value]=\"key\" [disabled]=\"disabled()\"\n placeholder=\"e.g. data, payload\" [attr.aria-label]=\"'Wrapper key ' + (i + 1)\"\n (input)=\"updateKey(i, inputValue($event))\">\n <span class=\"key-suffix\" aria-hidden=\"true\">: form&#125;</span>\n <button type=\"button\" class=\"key-remove\" [disabled]=\"disabled()\" matTooltip=\"Remove key\"\n [attr.aria-label]=\"'Remove wrapper key ' + (i + 1)\" (click)=\"removeKey(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n }\n <button type=\"button\" class=\"link-btn\" [disabled]=\"disabled()\" (click)=\"addKey()\">\n <mat-icon>add</mat-icon> Add another key\n </button>\n </div>\n } @else {\n <p class=\"hint-line subtle\">\n <mat-icon class=\"info-icon\">info</mat-icon>\n <span>Every field is sent at the top level of the request body.</span>\n </p>\n }\n\n <div class=\"preview\">\n <div class=\"preview-head\">\n <span class=\"field-label\">Sent to the endpoint</span>\n <button type=\"button\" class=\"link-btn\" [disabled]=\"disabled()\" matTooltip=\"Reset to the recommended shape\"\n (click)=\"resetToDefault()\">\n <mat-icon>restart_alt</mat-icon> Reset\n </button>\n </div>\n <pre class=\"preview-json\">{{ preview() | json }}</pre>\n </div>\n\n @for (error of errors(); track error.key) {\n <p class=\"error-line\">{{ error.message }}</p>\n }\n</div>\n", styles: [":host{display:block}.projection{display:flex;flex-direction:column;gap:1.5rem}.hint-line{display:flex;align-items:center;gap:.5rem;margin:0;font-size:.875rem;color:var(--lib-forms-on-surface-variant)}.hint-line.subtle{opacity:.85}.info-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;color:var(--lib-forms-primary)}.field-label{font-size:.625rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--lib-forms-on-surface-variant)}.mode-toggle{align-self:flex-start;border-radius:8px}.keys{display:flex;flex-direction:column;gap:.75rem}.key-row{display:flex;align-items:center;gap:.5rem}.key-prefix,.key-suffix{font-family:var(--lib-forms-font-mono, monospace);font-size:.8125rem;color:var(--lib-forms-on-surface-variant);flex-shrink:0}.key-input{flex:1;min-width:0;font:inherit;font-size:.9375rem;padding:.5rem .75rem;color:var(--lib-forms-on-surface);background:var(--lib-forms-surface-container-highest);border:1px solid color-mix(in srgb,var(--lib-forms-outline) 30%,transparent);border-radius:8px;transition:border-color .3s cubic-bezier(.16,1,.3,1)}.key-input:focus{outline:none;border-color:var(--lib-forms-primary)}.key-input:disabled{opacity:.6;cursor:not-allowed}.key-remove{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;flex-shrink:0;border:none;background:transparent;border-radius:8px;color:var(--lib-forms-on-surface-variant);cursor:pointer;transition:color .3s cubic-bezier(.16,1,.3,1)}.key-remove:hover:not(:disabled){color:var(--lib-forms-error)}.key-remove:disabled{opacity:.5;cursor:not-allowed}.key-remove mat-icon{font-size:1.125rem;width:1.125rem;height:1.125rem}.link-btn{display:inline-flex;align-items:center;gap:.375rem;align-self:flex-start;padding:.25rem 0;border:none;background:transparent;color:var(--lib-forms-primary);font:inherit;font-size:.8125rem;font-weight:500;cursor:pointer}.link-btn:disabled{opacity:.5;cursor:not-allowed}.link-btn mat-icon{font-size:1.125rem;width:1.125rem;height:1.125rem}.preview{display:flex;flex-direction:column;gap:.5rem}.preview-head{display:flex;align-items:center;justify-content:space-between}.preview-json{margin:0;padding:1rem;font-family:var(--lib-forms-font-mono, monospace);font-size:.8125rem;line-height:1.5;color:var(--lib-forms-on-surface);background:var(--lib-forms-surface-container-highest);border-radius:8px;overflow-x:auto;white-space:pre}.error-line{margin:0;font-size:.75rem;color:var(--lib-forms-error)}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i2.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i2.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
172
+ }
173
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: FormPayloadProjectionComponent, decorators: [{
174
+ type: Component,
175
+ args: [{ selector: 'lib-form-payload-projection', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: { class: 'lib-form-payload-projection' }, imports: [JsonPipe, MatButtonModule, MatButtonToggleModule, MatIconModule, MatTooltipModule], template: "<div class=\"projection\">\n <p class=\"hint-line\">\n <mat-icon class=\"info-icon\">help_outline</mat-icon>\n <span>Choose how the submitted form is shaped before it is sent to the endpoint.</span>\n </p>\n\n <mat-button-toggle-group class=\"mode-toggle\" [value]=\"mode()\" hideSingleSelectionIndicator\n [disabled]=\"disabled()\" (change)=\"setMode($event.value)\" aria-label=\"Payload shape\">\n <mat-button-toggle value=\"flat\" matTooltip=\"Send each field at the top level\">\n <mat-icon>list</mat-icon> Top level\n </mat-button-toggle>\n <mat-button-toggle value=\"wrap\" matTooltip=\"Nest the whole form under a key\">\n <mat-icon>data_object</mat-icon> Wrap under key\n </mat-button-toggle>\n </mat-button-toggle-group>\n\n @if (mode() === 'wrap') {\n <div class=\"keys\">\n <span class=\"field-label\">Wrap the form under</span>\n @for (key of keys(); track trackByIndex($index); let i = $index) {\n <div class=\"key-row\">\n <span class=\"key-prefix\" aria-hidden=\"true\">&#123;</span>\n <input class=\"key-input\" [value]=\"key\" [disabled]=\"disabled()\"\n placeholder=\"e.g. data, payload\" [attr.aria-label]=\"'Wrapper key ' + (i + 1)\"\n (input)=\"updateKey(i, inputValue($event))\">\n <span class=\"key-suffix\" aria-hidden=\"true\">: form&#125;</span>\n <button type=\"button\" class=\"key-remove\" [disabled]=\"disabled()\" matTooltip=\"Remove key\"\n [attr.aria-label]=\"'Remove wrapper key ' + (i + 1)\" (click)=\"removeKey(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n }\n <button type=\"button\" class=\"link-btn\" [disabled]=\"disabled()\" (click)=\"addKey()\">\n <mat-icon>add</mat-icon> Add another key\n </button>\n </div>\n } @else {\n <p class=\"hint-line subtle\">\n <mat-icon class=\"info-icon\">info</mat-icon>\n <span>Every field is sent at the top level of the request body.</span>\n </p>\n }\n\n <div class=\"preview\">\n <div class=\"preview-head\">\n <span class=\"field-label\">Sent to the endpoint</span>\n <button type=\"button\" class=\"link-btn\" [disabled]=\"disabled()\" matTooltip=\"Reset to the recommended shape\"\n (click)=\"resetToDefault()\">\n <mat-icon>restart_alt</mat-icon> Reset\n </button>\n </div>\n <pre class=\"preview-json\">{{ preview() | json }}</pre>\n </div>\n\n @for (error of errors(); track error.key) {\n <p class=\"error-line\">{{ error.message }}</p>\n }\n</div>\n", styles: [":host{display:block}.projection{display:flex;flex-direction:column;gap:1.5rem}.hint-line{display:flex;align-items:center;gap:.5rem;margin:0;font-size:.875rem;color:var(--lib-forms-on-surface-variant)}.hint-line.subtle{opacity:.85}.info-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;color:var(--lib-forms-primary)}.field-label{font-size:.625rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--lib-forms-on-surface-variant)}.mode-toggle{align-self:flex-start;border-radius:8px}.keys{display:flex;flex-direction:column;gap:.75rem}.key-row{display:flex;align-items:center;gap:.5rem}.key-prefix,.key-suffix{font-family:var(--lib-forms-font-mono, monospace);font-size:.8125rem;color:var(--lib-forms-on-surface-variant);flex-shrink:0}.key-input{flex:1;min-width:0;font:inherit;font-size:.9375rem;padding:.5rem .75rem;color:var(--lib-forms-on-surface);background:var(--lib-forms-surface-container-highest);border:1px solid color-mix(in srgb,var(--lib-forms-outline) 30%,transparent);border-radius:8px;transition:border-color .3s cubic-bezier(.16,1,.3,1)}.key-input:focus{outline:none;border-color:var(--lib-forms-primary)}.key-input:disabled{opacity:.6;cursor:not-allowed}.key-remove{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;flex-shrink:0;border:none;background:transparent;border-radius:8px;color:var(--lib-forms-on-surface-variant);cursor:pointer;transition:color .3s cubic-bezier(.16,1,.3,1)}.key-remove:hover:not(:disabled){color:var(--lib-forms-error)}.key-remove:disabled{opacity:.5;cursor:not-allowed}.key-remove mat-icon{font-size:1.125rem;width:1.125rem;height:1.125rem}.link-btn{display:inline-flex;align-items:center;gap:.375rem;align-self:flex-start;padding:.25rem 0;border:none;background:transparent;color:var(--lib-forms-primary);font:inherit;font-size:.8125rem;font-weight:500;cursor:pointer}.link-btn:disabled{opacity:.5;cursor:not-allowed}.link-btn mat-icon{font-size:1.125rem;width:1.125rem;height:1.125rem}.preview{display:flex;flex-direction:column;gap:.5rem}.preview-head{display:flex;align-items:center;justify-content:space-between}.preview-json{margin:0;padding:1rem;font-family:var(--lib-forms-font-mono, monospace);font-size:.8125rem;line-height:1.5;color:var(--lib-forms-on-surface);background:var(--lib-forms-surface-container-highest);border-radius:8px;overflow-x:auto;white-space:pre}.error-line{margin:0;font-size:.75rem;color:var(--lib-forms-error)}\n"] }]
176
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], formInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "formInputs", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], valueChanged: [{ type: i0.Output, args: ["valueChanged"] }] } });
177
+
178
+ export { FormPayloadProjectionComponent };
179
+ //# sourceMappingURL=ngx-t-forms-form-payload-projection.component-CDkTuX9S.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngx-t-forms-form-payload-projection.component-CDkTuX9S.mjs","sources":["../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/form-payload-projection/projection-expression.ts","../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/form-payload-projection/form-payload-projection.component.ts","../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/form-payload-projection/form-payload-projection.component.html"],"sourcesContent":["/**\n * Parser/serialiser for the `projectFormData` submission expression.\n *\n * At runtime the expression is consumed by `destructureObject({ $formValue: form }, expr)`,\n * so the only token available is `$formValue` — the entire submitted form. The\n * sole authoring choice is therefore whether to send the form fields flat or to\n * nest the whole payload under one or more named keys. This module bridges that\n * raw string and the guided builder's structured model.\n */\n\n/** Token that resolves to the entire submitted form payload at runtime. */\nexport const FORM_VALUE_TOKEN = '$formValue';\n\n/** Default envelope: the whole form nested under a `data` key. */\nexport const DEFAULT_PROJECTION = `data:{${FORM_VALUE_TOKEN}}`;\n\n/**\n * Structured model behind the guided \"Project form data\" editor.\n *\n * @remarks `keys` empty ⇒ send fields at the top level; each key nests the whole\n * form under that name (`{ key: { ...form } }`).\n */\nexport interface ProjectionModel {\n /** Keys to nest the whole form under. Empty ⇒ send fields at the top level. */\n readonly keys: readonly string[];\n}\n\n/** Matches a single `alias:{token}` envelope entry. */\nconst ENVELOPE = /^([^:]+):\\{(.+)\\}$/;\n\n/**\n * Parses a `projectFormData` expression into the guided model (best-effort).\n * Unrecognised entries are kept as literal keys so nothing is silently dropped.\n */\nexport function parseProjection(expression: string | null | undefined): ProjectionModel {\n if (!expression) return { keys: [] };\n const keys = expression\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n .map((entry) => {\n const match = ENVELOPE.exec(entry);\n // `alias:{$formValue}` ⇒ alias; a bare token (or any other entry) ⇒ literal key.\n return (match?.[1] ?? entry).trim();\n })\n .filter(Boolean);\n return { keys };\n}\n\n/** Serialises the guided model back to a `projectFormData` expression. */\nexport function serializeProjection(model: ProjectionModel): string {\n return model.keys\n .map((key) => key.trim())\n .filter(Boolean)\n .map((key) => `${key}:{${FORM_VALUE_TOKEN}}`)\n .join(', ');\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n effect,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { JsonPipe } from '@angular/common';\n\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatButtonToggleModule } from '@angular/material/button-toggle';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatTooltipModule } from '@angular/material/tooltip';\n\nimport { FormColumnInputs } from 'ngx-t-forms-types';\n\nimport {\n DEFAULT_PROJECTION,\n parseProjection,\n serializeProjection,\n} from './projection-expression';\n\n/** Validation error surfaced by the parent editor for display. */\ninterface ProjectionError {\n readonly key: string;\n readonly message: string;\n}\n\n/**\n * Guided editor for a form's `projectFormData` submission expression.\n *\n * Replaces the raw `data:{$formValue}` text field with a no-code chooser: send\n * the form fields flat, or wrap the whole payload under one or more named keys —\n * with a live preview of the resulting JSON shape. The runtime engine only\n * exposes the whole form (`$formValue`), so the model is intentionally a simple\n * list of envelope keys (see {@link parseProjection}).\n *\n * @example\n * <lib-form-payload-projection [value]=\"expr()\" [formInputs]=\"inputs()\"\n * (valueChanged)=\"onExpr($event)\" />\n */\n@Component({\n selector: 'lib-form-payload-projection',\n templateUrl: './form-payload-projection.component.html',\n styleUrl: './form-payload-projection.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: { class: 'lib-form-payload-projection' },\n imports: [JsonPipe, MatButtonModule, MatButtonToggleModule, MatIconModule, MatTooltipModule],\n})\nexport class FormPayloadProjectionComponent {\n /** Current `projectFormData` expression. */\n readonly value = input<string>('');\n\n /** Whether the editor is read-only. */\n readonly disabled = input<boolean>(false);\n\n /** The form's inputs — used to sketch the payload shape in the preview. */\n readonly formInputs = input<FormColumnInputs[]>([]);\n\n /** Validation errors surfaced by the parent for display. */\n readonly errors = input<ProjectionError[]>([]);\n\n /** Emits the serialised expression whenever the user changes the mapping. */\n readonly valueChanged = output<string>();\n\n /** Envelope keys the whole form is nested under. Empty ⇒ sent flat. */\n protected readonly keys = signal<string[]>([]);\n\n /** 'flat' = top-level fields; 'wrap' = nested under key(s). */\n protected readonly mode = computed<'flat' | 'wrap'>(() => (this.keys().length > 0 ? 'wrap' : 'flat'));\n\n /**\n * The last expression we synced from / emitted. Guards the input bridge so our\n * own round-tripped emissions don't clobber in-progress edits (the parent\n * debounces and echoes the value back through `value`).\n */\n #lastSynced: string | undefined = undefined;\n\n protected readonly valueBridge = effect(() => {\n const incoming = this.value() ?? '';\n if (incoming === this.#lastSynced) return;\n this.#lastSynced = incoming;\n this.keys.set([...parseProjection(incoming).keys]);\n });\n\n /** A sample of the form payload (field name → placeholder) for the preview. */\n readonly #sampleForm = computed<Record<string, unknown>>(() => {\n const sample: Record<string, unknown> = {};\n for (const column of this.formInputs()) {\n if (column.multipleInputInEditId || !column.formControlName) continue;\n sample[column.formControlName] = '…';\n if (Object.keys(sample).length >= 8) break;\n }\n if (Object.keys(sample).length === 0) sample['field'] = '…';\n return sample;\n });\n\n /** Live preview of the JSON shape sent to the endpoint. */\n protected readonly preview = computed<unknown>(() => {\n const keys = this.keys().map((k) => k.trim()).filter(Boolean);\n const sample = this.#sampleForm();\n if (keys.length === 0) return sample;\n const out: Record<string, unknown> = {};\n for (const key of keys) out[key] = sample;\n return out;\n });\n\n /** Switches between flat and wrapped modes (seeding a default key on first wrap). */\n protected setMode(mode: 'flat' | 'wrap'): void {\n if (mode === 'flat') {\n this.#apply([]);\n return;\n }\n if (this.keys().length === 0) this.#apply(['data']);\n }\n\n protected addKey(): void {\n this.#apply([...this.keys(), '']);\n }\n\n protected updateKey(index: number, key: string): void {\n this.#apply(this.keys().map((k, i) => (i === index ? key : k)));\n }\n\n protected removeKey(index: number): void {\n this.#apply(this.keys().filter((_, i) => i !== index));\n }\n\n /** Reads a native input's value from a DOM event (keeps the template `any`-free). */\n protected inputValue(event: Event): string {\n return (event.target as HTMLInputElement).value;\n }\n\n protected trackByIndex(index: number): number {\n return index;\n }\n\n /** Reset to the recommended default (`data:{$formValue}`). */\n protected resetToDefault(): void {\n this.#apply([...parseProjection(DEFAULT_PROJECTION).keys]);\n }\n\n #apply(keys: string[]): void {\n if (this.disabled()) return;\n this.keys.set(keys);\n const serialized = serializeProjection({ keys });\n // Pre-record so the echoed value (see valueBridge) is ignored, preserving\n // empty in-progress key rows that serialise away.\n this.#lastSynced = serialized;\n this.valueChanged.emit(serialized);\n }\n}\n","<div class=\"projection\">\n <p class=\"hint-line\">\n <mat-icon class=\"info-icon\">help_outline</mat-icon>\n <span>Choose how the submitted form is shaped before it is sent to the endpoint.</span>\n </p>\n\n <mat-button-toggle-group class=\"mode-toggle\" [value]=\"mode()\" hideSingleSelectionIndicator\n [disabled]=\"disabled()\" (change)=\"setMode($event.value)\" aria-label=\"Payload shape\">\n <mat-button-toggle value=\"flat\" matTooltip=\"Send each field at the top level\">\n <mat-icon>list</mat-icon> Top level\n </mat-button-toggle>\n <mat-button-toggle value=\"wrap\" matTooltip=\"Nest the whole form under a key\">\n <mat-icon>data_object</mat-icon> Wrap under key\n </mat-button-toggle>\n </mat-button-toggle-group>\n\n @if (mode() === 'wrap') {\n <div class=\"keys\">\n <span class=\"field-label\">Wrap the form under</span>\n @for (key of keys(); track trackByIndex($index); let i = $index) {\n <div class=\"key-row\">\n <span class=\"key-prefix\" aria-hidden=\"true\">&#123;</span>\n <input class=\"key-input\" [value]=\"key\" [disabled]=\"disabled()\"\n placeholder=\"e.g. data, payload\" [attr.aria-label]=\"'Wrapper key ' + (i + 1)\"\n (input)=\"updateKey(i, inputValue($event))\">\n <span class=\"key-suffix\" aria-hidden=\"true\">: form&#125;</span>\n <button type=\"button\" class=\"key-remove\" [disabled]=\"disabled()\" matTooltip=\"Remove key\"\n [attr.aria-label]=\"'Remove wrapper key ' + (i + 1)\" (click)=\"removeKey(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n }\n <button type=\"button\" class=\"link-btn\" [disabled]=\"disabled()\" (click)=\"addKey()\">\n <mat-icon>add</mat-icon> Add another key\n </button>\n </div>\n } @else {\n <p class=\"hint-line subtle\">\n <mat-icon class=\"info-icon\">info</mat-icon>\n <span>Every field is sent at the top level of the request body.</span>\n </p>\n }\n\n <div class=\"preview\">\n <div class=\"preview-head\">\n <span class=\"field-label\">Sent to the endpoint</span>\n <button type=\"button\" class=\"link-btn\" [disabled]=\"disabled()\" matTooltip=\"Reset to the recommended shape\"\n (click)=\"resetToDefault()\">\n <mat-icon>restart_alt</mat-icon> Reset\n </button>\n </div>\n <pre class=\"preview-json\">{{ preview() | json }}</pre>\n </div>\n\n @for (error of errors(); track error.key) {\n <p class=\"error-line\">{{ error.message }}</p>\n }\n</div>\n"],"names":["i1","i2","i3"],"mappings":"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"}